diff --git a/modules/python/bindings/include/core.hpp b/modules/python/bindings/include/core.hpp index 5e7f5caefd..99c8b7e8ee 100644 --- a/modules/python/bindings/include/core.hpp +++ b/modules/python/bindings/include/core.hpp @@ -38,5 +38,7 @@ #include "core/arrays.hpp" #include "core/images.hpp" #include "core/pixel_meter.hpp" +#include "core/image_conversions.hpp" + #endif diff --git a/modules/python/bindings/include/core/image_conversions.hpp b/modules/python/bindings/include/core/image_conversions.hpp new file mode 100644 index 0000000000..f826b44709 --- /dev/null +++ b/modules/python/bindings/include/core/image_conversions.hpp @@ -0,0 +1,205 @@ +/* + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2023 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See https://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Python bindings. + */ + +#ifndef VISP_PYTHON_CORE_IMAGE_CONVERT_HPP +#define VISP_PYTHON_CORE_IMAGE_CONVERT_HPP + +#include +#include +#include + +#include + +namespace +{ +using ConversionFunction1D = void(*)(unsigned char *, unsigned char *, unsigned int); +using ConversionFunction2D = void(*)(unsigned char *, unsigned char *, unsigned int, unsigned int); +using ComputeBytesFunction = unsigned(*)(unsigned int, unsigned int); + +void call_conversion_fn(ConversionFunction2D fn, unsigned char *src, unsigned char *dest, unsigned int h, unsigned int w) +{ + fn(src, dest, h, w); +} +void call_conversion_fn(ConversionFunction1D fn, unsigned char *src, unsigned char *dest, unsigned int h, unsigned int w) +{ + fn(src, dest, h * w); +} + +template +struct SimpleConversionStruct +{ + SimpleConversionStruct(const std::string &name, ConversionFn fn, unsigned int srcBytesPerPixel, unsigned int destBytesPerPixel) : + name(name), fn(fn), srcBytesPerPixel(srcBytesPerPixel), destBytesPerPixel(destBytesPerPixel) + { } + std::string name; + ConversionFn fn; + unsigned int srcBytesPerPixel; + unsigned int destBytesPerPixel; + + void add_conversion_binding(py::class_ &pyImageConvert) + { + pyImageConvert.def_static(name.c_str(), [this](py::array_t &src, + py::array_t &dest) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + if (bufsrc.ndim < 2 || bufdest.ndim < 2) { + throw std::runtime_error("Expected to have src and dest arrays with at least two dimensions."); + } + if (bufsrc.shape[0] != bufdest.shape[0] || bufsrc.shape[1] != bufdest.shape[1]) { + std::stringstream ss; + ss << "src and dest must have the same number of pixels, but got src = " << shape_to_string(bufsrc.shape); + ss << "and dest = " << shape_to_string(bufdest.shape); + throw std::runtime_error(ss.str()); + } + if (srcBytesPerPixel > 1 && (bufsrc.ndim != 3 || bufsrc.shape[2] != srcBytesPerPixel)) { + std::stringstream ss; + ss << "Source array should be a 3D array of shape (H, W, " << srcBytesPerPixel << ")"; + throw std::runtime_error(ss.str()); + } + else if (srcBytesPerPixel == 1 && bufsrc.ndim == 3 && bufsrc.shape[2] > 1) { + throw std::runtime_error("Source array should be a either a 2D array of shape H x W or a 3D array of shape (H, W, 1)"); + } + if (destBytesPerPixel > 1 && (bufdest.ndim != 3 || bufdest.shape[2] != destBytesPerPixel)) { + std::stringstream ss; + ss << "Destination array should be a 3D array of shape (H, W, " << destBytesPerPixel << ")"; + throw std::runtime_error(ss.str()); + } + else if (destBytesPerPixel == 1 && bufdest.ndim == 3 && bufdest.shape[2] > 1) { + throw std::runtime_error("Destination should be a either a 2D array of shape H x W or a 3D array of shape (H, W, 1)"); + } + + + unsigned char *src_ptr = static_cast(bufsrc.ptr); + unsigned char *dest_ptr = static_cast(bufdest.ptr); + call_conversion_fn(fn, src_ptr, dest_ptr, bufsrc.shape[0], bufsrc.shape[1]); + }, py::arg("src"), py::arg("dest")); + } + +}; + +struct ConversionFromYUVLike +{ + ConversionFromYUVLike(const std::string &name, ConversionFunction2D fn, ComputeBytesFunction sourceBytesFn, unsigned int destBytesPerPixel) : + name(name), fn(fn), , destBytesPerPixel(destBytesPerPixel) + { } + std::string name; + ConversionFunction2D fn; + ComputeBytesFunction sourceBytesFn; + + unsigned int destBytesPerPixel; + + void add_conversion_binding(py::class_ &pyImageConvert) + { + pyImageConvert.def_static(name.c_str(), [this](py::array_t &src, + py::array_t &dest) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + if (bufdest.ndim < 2) { + throw std::runtime_error("Expected to have dest array with at least two dimensions."); + } + + unsigned int height = bufdest.shape[0], width = bufdest.shape[1]; + + unsigned expectedSourceBytes = sourceBytesFn(height, width); + + unsigned actualBytes = 1; + for (unsigned int i = 0; i < bufsrc.ndim; ++i) { + actualBytes *= bufsrc.shape[i]; + } + + if (actualBytes != expectedSourceBytes) { + std::stringstream ss; + ss << "Expected to have " << expectedSourceBytes << " bytes in the input array, but got " << actualBytes << " elements."; + throw std::runtime_error(ss.str()); + } + + if (destBytesPerPixel > 1 && (bufdest.ndim != 3 || bufdest.shape[2] != destBytesPerPixel)) { + std::stringstream ss; + ss << "Destination array should be a 3D array of shape (H, W, " << destBytesPerPixel << ")"; + throw std::runtime_error(ss.str()); + } + else if (destBytesPerPixel == 1 && bufdest.ndim == 3 && bufdest.shape[2] > 1) { + throw std::runtime_error("Destination should be a either a 2D array of shape H x W or a 3D array of shape (H, W, 1)"); + } + + + unsigned char *src_ptr = static_cast(bufsrc.ptr); + unsigned char *dest_ptr = static_cast(bufdest.ptr); + call_conversion_fn(fn, src_ptr, dest_ptr, bufdest.shape[0], bufdest.shape[1]); + }, py::arg("src"), py::arg("dest")); + } + +}; + + + +} + + + +void bindings_vpImageConvert(py::class_ &pyImageConvert) +{ + // Simple conversions where the size input is a single argument + { + std::vector> conversions = { + SimpleConversionStruct("YUV444ToGrey", &vpImageConvert::YUV444ToGrey, 3, 1), + SimpleConversionStruct("YUV444ToRGB", &vpImageConvert::YUV444ToRGB, 3, 3), + SimpleConversionStruct("YUV444ToRGBa", &vpImageConvert::YUV444ToRGBa, 3, 4), + SimpleConversionStruct("RGBToRGBa", static_cast(&vpImageConvert::RGBToRGBa), 3, 4), + SimpleConversionStruct("RGBaToRGB", &vpImageConvert::RGBaToRGB, 4, 3), + SimpleConversionStruct("GreyToRGB", &vpImageConvert::GreyToRGB, 1, 3), + SimpleConversionStruct("GreyToRGBa", static_cast(&vpImageConvert::GreyToRGBa), 1, 4) + }; + for (auto &conversion: conversions) { + conversion.add_conversion_binding(pyImageConvert); + } + } + //Simple conversions where the size input are height and width + { + std::vector> conversions = { + }; + for (auto &conversion: conversions) { + conversion.add_conversion_binding(pyImageConvert); + } + } + //YUV conversions + { + std::vector> conversions = { + }; + for (auto &conversion: conversions) { + conversion.add_conversion_binding(pyImageConvert); + } + } + + +} + +#endif diff --git a/modules/python/config/core.json b/modules/python/config/core.json index e6dbb6324e..44b7ea4bed 100644 --- a/modules/python/config/core.json +++ b/modules/python/config/core.json @@ -313,7 +313,116 @@ } ] }, - + "vpImageConvert": { + "additional_bindings": "bindings_vpImageConvert", + "methods": + [ + { + "static": true, + "signature": "void RGBaToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true + }, + { + "static": true, + "signature": "void RGBToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true + }, + { + "static": true, + "signature": "void RGBToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int, bool)", + "ignore": true + }, + { + "static": true, + "signature": "void YUV444ToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true + }, + { + "static": true, + "signature": "void YUV444ToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true + }, + { + "static": true, + "signature": "void YUV444ToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true + }, + { + "static": true, + "signature": "void GreyToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true + }, + { + "static": true, + "signature": "void GreyToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true + }, + { + "static": true, + "signature": "void GreyToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true + } + ] + }, + "vpConvert": { + "methods": [ + { + "static": true, + "signature": "void convertToOpenCV(const std::vector&, std::vector&, bool)", + "ignore" :true + }, + { + "static": true, + "signature": "void convertToOpenCV(const std::vector&, std::vector&, bool)", + "ignore" :true + }, + { + "static": true, + "signature": "void convertToOpenCV(const std::vector&, std::vector&)", + "ignore" :true + }, + { + "static": true, + "signature": "void convertToOpenCV(const std::vector&, std::vector&)", + "ignore": true + }, + { + "static": true, + "signature": "void convertFromOpenCV(const std::vector&, std::vector&)", + "ignore": true + }, + { + "static": true, + "signature": "void convertFromOpenCV(const std::vector&, std::vector&)", + "ignore": true + }, + { + "static": true, + "signature": "void convertFromOpenCV(const std::vector&, std::vector&, bool)", + "ignore": true + }, + { + "static": true, + "signature": "void convertFromOpenCV(const std::vector&, std::vector&, bool)", + "ignore": true + }, + { + "static": true, + "signature": "void convertFromOpenCV(const std::vector&, std::vector&)", + "ignore": true + }, + { + "static": true, + "signature": "void convertFromOpenCV(const std::vector&, std::vector&)", + "ignore": true + }, + { + "static": true, + "signature": "void convertFromOpenCV(const std::vector&, std::vector&)", + "ignore": true + } + ] + }, "vpDisplay": { "methods": [ @@ -361,7 +470,19 @@ }, "vpMomentDatabase": { "methods": [ - + { + "static": false, + "signature": "const vpMoment& get(const std::string&, bool&)", + "use_default_param_policy": false, + "param_is_input": [ + true, + false + ], + "param_is_output": [ + false, + true + ] + } ] }, "vpPixelMeterConversion": { @@ -369,6 +490,17 @@ }, "vpMeterPixelConversion": { "additional_bindings": "bindings_vpMeterPixelConversion" + }, + "vpCircle": { + "methods": [ + { + "static": true, + "signature": "void computeIntersectionPoint(const vpCircle&, const vpCameraParameters&, const double&, const double&, double&, double&)", + "use_default_param_policy": false, + "param_is_input": [true, true, true, true, false, false], + "param_is_output": [false, false, false, false, true, true] + } + ] } }