diff --git a/modules/python/bindings/include/core/image_conversions.hpp b/modules/python/bindings/include/core/image_conversions.hpp index 5a8d3b9deb..91d8045157 100644 --- a/modules/python/bindings/include/core/image_conversions.hpp +++ b/modules/python/bindings/include/core/image_conversions.hpp @@ -64,9 +64,6 @@ void call_conversion_fn(ConversionFunction1D fn, unsigned char *src, unsigned ch fn(src, dest, h * w); } - - - template struct SimpleConversionStruct { @@ -229,72 +226,98 @@ unsigned size411(unsigned h, unsigned w) return h * w + ((h / 4) * (w / 4)) * 2; } -template -void add_hsv_to_rgb_or_rgba_binding(py::class_ &pyImageConvert, - void (*fn)(const T *, const T *, const T *, unsigned char *, unsigned int), const char *name, const unsigned destBytes) +void rgb_or_rgba_to_hsv_verification(const py::buffer_info &bufsrc, const py::buffer_info &bufdest, const unsigned destBytes, const unsigned height, const unsigned width) { - pyImageConvert.def_static(name, [fn, destBytes](py::array_t &src, - py::array_t &dest) { - py::buffer_info bufsrc = src.request(), bufdest = dest.request(); if (bufsrc.ndim != 3 || bufdest.ndim != 3) { throw std::runtime_error("Expected to have src and dest arrays with at least two dimensions."); } - if (bufsrc.shape[0] != 3) { + if (bufdest.shape[0] != 3) { throw std::runtime_error("Source array should be a 3D array of shape (3, H, W) "); } - if (bufdest.shape[2] != destBytes) { + if (bufsrc.shape[2] != destBytes) { std::stringstream ss; ss << "Target array should be a 3D array of shape (H, W, " << destBytes << ")"; throw std::runtime_error(ss.str()); } - const unsigned height = bufsrc.shape[1]; - const unsigned width = bufsrc.shape[2]; - if (bufdest.shape[0] != height || bufdest.shape[1] != width) { + if (bufsrc.shape[0] != height || bufsrc.shape[1] != width) { std::stringstream ss; ss << "src and dest must have the same number of pixels, but got HSV planes with dimensions (" << height << ", " << width << ")"; ss << "and RGB array with dimensions (" << bufdest.shape[0] << ", " << bufdest.shape[1] << ")"; throw std::runtime_error(ss.str()); } +} + +void add_hsv_double_to_rgb_or_rgba_binding(py::class_ &pyImageConvert, + void (*fn)(const double *, const double *, const double *, unsigned char *, unsigned int), const char *name, const unsigned destBytes) +{ + pyImageConvert.def_static(name, [fn, destBytes](py::array_t &src, + py::array_t &dest) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + const unsigned height = bufsrc.shape[1]; + const unsigned width = bufsrc.shape[2]; + rgb_or_rgba_to_hsv_verification(bufdest, bufsrc, destBytes, height, width); - const T *h = static_cast(bufsrc.ptr); - const T *s = h + (height * width); - const T *v = s + (height * width); + const double *h = static_cast(bufsrc.ptr); + const double *s = h + (height * width); + const double *v = s + (height * width); unsigned char *dest_ptr = static_cast(bufdest.ptr); fn(h, s, v, dest_ptr, height * width); }, "Convert from HSV Planes (as a 3 x H x W array) to a an RGB/RGBA array (as an H x W x 3 or H x W x 4 array)", py::arg("hsv"), py::arg("rgb")); } -template -void add_rgb_or_rgba_to_hsv_binding(py::class_ &pyImageConvert, - void (*fn)(const unsigned char *, T *, T *, T *, unsigned int), const char *name, const unsigned destBytes) +void add_hsv_uchar_to_rgb_or_rgba_binding(py::class_ &pyImageConvert, + void (*fn)(const unsigned char *, const unsigned char *, const unsigned char *, unsigned char *, unsigned int, bool), const char *name, const unsigned destBytes) { pyImageConvert.def_static(name, [fn, destBytes](py::array_t &src, - py::array_t &dest) { + py::array_t &dest, bool h_full) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + const unsigned height = bufsrc.shape[1]; + const unsigned width = bufsrc.shape[2]; + rgb_or_rgba_to_hsv_verification(bufdest, bufsrc, destBytes, height, width); + + const unsigned char *h = static_cast(bufsrc.ptr); + const unsigned char *s = h + (height * width); + const unsigned char *v = s + (height * width); + unsigned char *dest_ptr = static_cast(bufdest.ptr); + fn(h, s, v, dest_ptr, height * width, h_full); + + }, "Convert from HSV Planes (as a 3 x H x W array) to a an RGB/RGBA array (as an H x W x 3 or H x W x 4 array)", py::arg("hsv"), py::arg("rgb"), py::arg("h_full")=true); +} + +void add_rgb_or_rgba_uchar_to_hsv_binding(py::class_ &pyImageConvert, + void (*fn)(const unsigned char *, unsigned char *, unsigned char *, unsigned char *, unsigned int, bool), const char *name, const unsigned destBytes) +{ + pyImageConvert.def_static(name, [fn, destBytes](py::array_t &src, + py::array_t &dest, + bool h_full) { py::buffer_info bufsrc = src.request(), bufdest = dest.request(); - if (bufsrc.ndim != 3 || bufdest.ndim != 3) { - throw std::runtime_error("Expected to have src and dest arrays with at least two dimensions."); - } - if (bufdest.shape[0] != 3) { - throw std::runtime_error("Source array should be a 3D array of shape (3, H, W) "); - } - if (bufsrc.shape[2] != destBytes) { - std::stringstream ss; - ss << "Target array should be a 3D array of shape (H, W, " << destBytes << ")"; - throw std::runtime_error(ss.str()); - } const unsigned height = bufdest.shape[1]; const unsigned width = bufdest.shape[2]; - if (bufsrc.shape[0] != height || bufsrc.shape[1] != width) { - std::stringstream ss; - ss << "src and dest must have the same number of pixels, but got HSV planes with dimensions (" << height << ", " << width << ")"; - ss << "and RGB array with dimensions (" << bufdest.shape[0] << ", " << bufdest.shape[1] << ")"; - throw std::runtime_error(ss.str()); - } + rgb_or_rgba_to_hsv_verification(bufsrc, bufdest, destBytes, height, width); + + unsigned char *h = static_cast(bufdest.ptr); + unsigned char *s = h + (height * width); + unsigned char *v = s + (height * width); + const unsigned char *rgb = static_cast(bufsrc.ptr); + fn(rgb, h, s, v, height * width, h_full); + + }, "Convert from HSV Planes (as a 3 x H x W array) to a an RGB/RGBA array (as an H x W x 3 or H x W x 4 array)", py::arg("rgb"), py::arg("hsv"), py::arg("h_full")=true); +} + +void add_rgb_or_rgba_double_to_hsv_binding(py::class_ &pyImageConvert, + void (*fn)(const unsigned char *, double *, double *, double *, unsigned int), const char *name, const unsigned destBytes) +{ + pyImageConvert.def_static(name, [fn, destBytes](py::array_t &src, + py::array_t &dest) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + const unsigned height = bufdest.shape[1]; + const unsigned width = bufdest.shape[2]; + rgb_or_rgba_to_hsv_verification(bufsrc, bufdest, destBytes, height, width); - T *h = static_cast(bufdest.ptr); - T *s = h + (height * width); - T *v = s + (height * width); + double *h = static_cast(bufdest.ptr); + double *s = h + (height * width); + double *v = s + (height * width); const unsigned char *rgb = static_cast(bufsrc.ptr); fn(rgb, h, s, v, height * width); @@ -412,15 +435,15 @@ void bindings_vpImageConvert(py::class_ &pyImageConvert) } // HSV <-> RGB/a - add_hsv_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGB, "HSVToRGB", 3); - add_hsv_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGB, "HSVToRGB", 3); - add_hsv_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGBa, "HSVToRGBa", 4); - add_hsv_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGBa, "HSVToRGBa", 4); - - add_rgb_or_rgba_to_hsv_binding(pyImageConvert, vpImageConvert::RGBToHSV, "RGBToHSV", 3); - add_rgb_or_rgba_to_hsv_binding(pyImageConvert, vpImageConvert::RGBToHSV, "RGBToHSV", 3); - add_rgb_or_rgba_to_hsv_binding(pyImageConvert, vpImageConvert::RGBaToHSV, "RGBaToHSV", 4); - add_rgb_or_rgba_to_hsv_binding(pyImageConvert, vpImageConvert::RGBaToHSV, "RGBaToHSV", 4); + add_hsv_uchar_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGB, "HSVToRGB", 3); + add_hsv_double_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGB, "HSVToRGB", 3); + add_hsv_uchar_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGBa, "HSVToRGBa", 4); + add_hsv_double_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGBa, "HSVToRGBa", 4); + + add_rgb_or_rgba_uchar_to_hsv_binding(pyImageConvert, vpImageConvert::RGBToHSV, "RGBToHSV", 3); + add_rgb_or_rgba_double_to_hsv_binding(pyImageConvert, vpImageConvert::RGBToHSV, "RGBToHSV", 3); + add_rgb_or_rgba_uchar_to_hsv_binding(pyImageConvert, vpImageConvert::RGBaToHSV, "RGBaToHSV", 4); + add_rgb_or_rgba_double_to_hsv_binding(pyImageConvert, vpImageConvert::RGBaToHSV, "RGBaToHSV", 4); // uint8_t implems diff --git a/modules/python/config/core_image.json b/modules/python/config/core_image.json index 11369876c3..f93a66ff60 100644 --- a/modules/python/config/core_image.json +++ b/modules/python/config/core_image.json @@ -348,7 +348,7 @@ }, { "static": true, - "signature": "void RGBaToHSV(const unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned int)", + "signature": "void RGBaToHSV(const unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned int, bool)", "ignore": true, "custom_implem": true }, @@ -360,13 +360,13 @@ }, { "static": true, - "signature": "void HSVToRGB(const unsigned char*, const unsigned char*, const unsigned char*, unsigned char*, unsigned int)", + "signature": "void HSVToRGB(const unsigned char*, const unsigned char*, const unsigned char*, unsigned char*, unsigned int, bool)", "ignore": true, "custom_implem": true }, { "static": true, - "signature": "void HSVToRGBa(const unsigned char*, const unsigned char*, const unsigned char*, unsigned char*, unsigned int)", + "signature": "void HSVToRGBa(const unsigned char*, const unsigned char*, const unsigned char*, unsigned char*, unsigned int, bool)", "ignore": true, "custom_implem": true }, @@ -384,7 +384,7 @@ }, { "static": true, - "signature": "void RGBToHSV(const unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned int)", + "signature": "void RGBToHSV(const unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned int, bool)", "ignore": true, "custom_implem": true }, diff --git a/modules/python/doc/conf.py.in b/modules/python/doc/conf.py.in index 5f0df5e14b..c4e8ea6c2c 100644 --- a/modules/python/doc/conf.py.in +++ b/modules/python/doc/conf.py.in @@ -148,6 +148,7 @@ html_theme = 'sphinx_immaterial' # html_theme_options = {} html_theme_options = { "toc_title_is_page_title": True, + "font": False, "repo_url": "https://github.com/lagadic/visp", "repo_name": "visp", "features": [ diff --git a/modules/python/doc/rst/dev/dev.rst b/modules/python/doc/rst/dev/dev.rst index e41f275ae0..ee075d4113 100644 --- a/modules/python/doc/rst/dev/dev.rst +++ b/modules/python/doc/rst/dev/dev.rst @@ -75,7 +75,7 @@ If you encounter a compilation error, make sure to first try rebuilding after cl Pybind did generate problems (an error at the pybind include line) that were solved like this. Static and member methods have the same name -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------------------------- If, when importing visp in python, you encounter this message: @@ -86,7 +86,7 @@ If, when importing visp in python, you encounter this message: Then it means that a class has both a static method and a member method with the same name. You should :ref:`rename either one through the config files `. Abstract class not detected -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------------------------- If you have this error: @@ -103,7 +103,8 @@ This error occurs because some methods are defined as pure virtual in a parent c Template errors -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------------------------- + If you have an issue that looks like: diff --git a/modules/python/doc/rst/python_api/python_specific_fns.rst b/modules/python/doc/rst/python_api/python_specific_fns.rst index 9ddba3a843..502c800b2c 100644 --- a/modules/python/doc/rst/python_api/python_specific_fns.rst +++ b/modules/python/doc/rst/python_api/python_specific_fns.rst @@ -10,11 +10,11 @@ Core module ---------------------- * :py:class:`~visp.core.PixelMeterConversion` and :py:class:`~visp.core.MeterPixelConversion` both -have a vectorised implementation of :code:`convertPoint`, called :code:`convertPoints`, accepting NumPy arrays + have a vectorised implementation of :code:`convertPoint`, called :code:`convertPoints`, accepting NumPy arrays MBT module ----------------------- * :py:class:`~visp.mbt.MbGenericTracker` as a reworked version of :py:meth:`visp.mbt.MbGenericTracker.track`, taking as inputs -maps of color images and of numpy representations (of shape H x W x 3) of the point clouds. + maps of color images and of numpy representations (of shape H x W x 3) of the point clouds. diff --git a/modules/python/generator/visp_python_bindgen/doc_parser.py b/modules/python/generator/visp_python_bindgen/doc_parser.py index 2e4fc4eafa..ccae166814 100644 --- a/modules/python/generator/visp_python_bindgen/doc_parser.py +++ b/modules/python/generator/visp_python_bindgen/doc_parser.py @@ -89,10 +89,8 @@ def to_cstring(s: str) -> str: current_char = 0 result = '' while current_char < len(s): - result += f'''R"doc( -{s[current_char: min((current_char + per_string_limit), len(s))]})doc" - -''' + result += f'''R"doc({s[current_char: min((current_char + per_string_limit), len(s))]})doc" + ''' current_char += per_string_limit return result diff --git a/modules/python/generator/visp_python_bindgen/methods.py b/modules/python/generator/visp_python_bindgen/methods.py index 61376860da..913f7e9b28 100644 --- a/modules/python/generator/visp_python_bindgen/methods.py +++ b/modules/python/generator/visp_python_bindgen/methods.py @@ -230,9 +230,14 @@ def get_py_args(parameters: List[types.Parameter], specs, env_mapping) -> List[s def make_arg(name: str) -> str: return f'py::arg("{name}")' py_args = [] + arg_index = 0 for parameter in parameters: + parameter_name = parameter.name + if parameter_name is None: + parameter_name = f'arg{arg_index}' + if parameter.default is None or not parameter_can_have_default_value(parameter, specs, env_mapping): - py_args.append(make_arg(parameter.name)) + py_args.append(make_arg(parameter_name)) else: t = parameter.type gt = lambda typename: get_typename(typename, specs, env_mapping) @@ -260,7 +265,8 @@ def make_arg(name: str) -> str: default_value_rep = default_value_rep.replace('"', '\"') # Escape inner quotes in std::string args like std::string("hello"). This would break parsing at compile time default_value = env_mapping.get(default_value) or default_value - py_args.append(f'py::arg_v("{parameter.name}", {default_value}, "{default_value_rep}")') + py_args.append(f'py::arg_v("{parameter_name}", {default_value}, "{default_value_rep}")') + arg_index += 1 return py_args