diff --git a/modules/python/bindings/include/core/image_filter.hpp b/modules/python/bindings/include/core/image_filter.hpp index 9f2af62dfa..f395f55e4b 100644 --- a/modules/python/bindings/include/core/image_filter.hpp +++ b/modules/python/bindings/include/core/image_filter.hpp @@ -1,6 +1,6 @@ /* * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2025 by Inria. All rights reserved. + * Copyright (C) 2005 - 2026 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 @@ -414,6 +414,185 @@ Example usage: )doc", py::arg("input"), py::arg("output"), py::arg("filter"), py::arg("size"), py::arg("mask").none(true) = static_cast>>(std::nullopt)); } + + +template +void define_gaussianFilter(py::class_> &pyClass) +{ +#ifdef ENABLE_VISP_NAMESPACE + using namespace VISP_NAMESPACE_NAME; +#endif + + pyClass.def_static( + "gaussianFilter", + [](const vpImage &input, + unsigned int r, + unsigned int c) -> double { + return vpImageFilter::gaussianFilter(input, r, c); + }, R"doc( +Apply a 5x5 Gaussian filter to one image pixel. + +:param input: The image to filter. +:param r: Row coordinate of the pixel. +:param c: Column coordinate of the pixel. + +:return: The filtered pixel value. + +Example usage: + +.. testcode:: + + from visp.core import ImageGray, ImageFilter + + Iin = ImageGray(100, 100, 0) + Iin[25:75, 25:75] = 50 + + filtered_value = ImageFilter.gaussianFilter(Iin, 50, 50) + assert isinstance(filtered_value, float) +)doc", + py::arg("input"), + py::arg("r"), + py::arg("c")); +} + +template +void define_gaussianBlur( + py::class_> &pyClass) +{ +#ifdef ENABLE_VISP_NAMESPACE + using namespace VISP_NAMESPACE_NAME; +#endif + + pyClass.def_static( + "gaussianBlur", + [](const vpImage &input, + vpImage &output, + unsigned int size, + double sigma, + bool normalize, + const std::optional> &mask) -> void { + if (!mask) { + vpImageFilter::gaussianBlur(input, output, size, sigma, normalize, nullptr); + } + else { + vpImageFilter::gaussianBlur(input, output, size, sigma, normalize, &(mask.value())); + } + }, + R"doc( +Apply a Gaussian blur to an image. + +:param input: The input image. +:param output: The resulting blurred image. +:param size: Filter size. This value should be odd. +:param sigma: Gaussian standard deviation. If it is zero or negative, it is computed from the filter size as ``(size - 1) / 6``. +:param normalize: If True, normalize the Gaussian filter coefficients. +:param mask: Optional mask indicating which pixels to consider. True means that the pixel is considered, while False means that it is ignored. + +Example usage: + +.. testcode:: + + from visp.core import ImageGray, ImageDouble, ImageBool, ImageFilter + + Iin = ImageGray(100, 100, 0) + Iin[25:75, 25:75] = 50 + + Iout = ImageDouble() + ImageFilter.gaussianBlur(Iin, Iout) + + assert Iout.getRows() == Iin.getRows() + assert Iout.getCols() == Iin.getCols() + + mask = ImageBool(100, 100, True) + + Iout = ImageDouble() + ImageFilter.gaussianBlur( + Iin, + Iout, + size=7, + sigma=0.0, + normalize=True, + mask=mask + ) + + assert Iout.getRows() == Iin.getRows() + assert Iout.getCols() == Iin.getCols() +)doc", + py::arg("input"), + py::arg("output"), + py::arg("size") = 7, + py::arg("sigma") = 0., + py::arg("normalize") = true, + py::arg("mask").none(true) = static_cast>>(std::nullopt)); +} + +void define_canny( + py::class_> &pyClass) +{ +#ifdef ENABLE_VISP_NAMESPACE + using namespace VISP_NAMESPACE_NAME; +#endif + + pyClass.def_static( + "canny", + [](const vpImage &input, + vpImage &output, + unsigned int gaussianFilterSize, + float lowerThreshold, + float upperThreshold, + unsigned int apertureGradient, + float gaussianStdev, + float lowerThresholdRatio, + float upperThresholdRatio, + bool normalizeGradients, + vpImageFilter::vpCannyBackendType cannyBackend, + vpImageFilter::vpCannyFilteringAndGradientType cannyFilteringSteps, + const std::optional> &mask) -> void + { + if (!mask) { + vpImageFilter::canny(input, output, gaussianFilterSize, lowerThreshold, upperThreshold, apertureGradient, gaussianStdev, + lowerThresholdRatio, upperThresholdRatio, normalizeGradients, cannyBackend, cannyFilteringSteps, nullptr); + } + else { + vpImageFilter::canny(input, output, gaussianFilterSize, lowerThreshold, upperThreshold, apertureGradient, gaussianStdev, + lowerThresholdRatio, upperThresholdRatio, normalizeGradients, cannyBackend, cannyFilteringSteps, &(mask.value())); + } + }, + R"doc( +Apply the Canny edge detector to a grayscale image. + +:param Isrc : The input grayscale image. +:param Ires : The resulting edge image. Edge pixels have value 255, non-edge pixels have value 0. +:param gaussianFilterSize : Size of the Gaussian filter. Must be odd. +:param lowerThreshold : The lower threshold for the Canny operator. Values lower than this value are rejected. If negative, it will be set to one third of the thresholdCanny. +:param upperThreshold : The upper threshold for the Canny operator. Only value greater than this value are marked as an edge. If negative, it will be automatically computed, along with the lower threshold. Otherwise, the lower threshold will be set to one third of the upper threshold. +:param apertureGradient : Size of the Sobel or Scharr gradient mask. Must be odd. +:param gaussianStdev : The standard deviation of the Gaussian filter to apply. If it is non-positive, it is computed from kernel size (gaussianKernelSize parameter) as σ=0.3∗((gaussianKernelSize−1)∗0.5−1)+0.8. +:param lowerThresholdRatio : The ratio of the upper threshold the lower threshold must be equal to. It is used only if the user asks to compute the Canny thresholds. +:param upperThresholdRatio : The ratio of pixels whose absolute gradient is lower or equal to define the upper threshold. It is used only if the user asks to compute the Canny thresholds. +:param normalizeGradients : Needs to be true if asking to compute the upperThreshold, otherwise it depends on the user application and user-defined thresholds. +:param cannyBackend : The backend to use to perform the Canny edge filtering. +:param cannyFilteringSteps : The filtering + gradient operators to apply to compute the gradient in the early stage of the Canny algorithm. +:param mask : Optional mask. True pixels are processed and False pixels are ignored. +)doc", + py::arg("input"), + py::arg("output"), + py::arg("gaussianFilterSize"), + py::arg("lowerThreshold"), + py::arg("upperThreshold"), + py::arg("apertureGradient"), + py::arg("gaussianStdev"), + py::arg("lowerThresholdRatio"), + py::arg("upperThresholdRatio"), + py::arg("normalizeGradients"), + py::arg("cannyBackend"), + py::arg("cannyFilteringSteps"), + py::arg("mask").none(true) = static_cast>>(std::nullopt) + ); +} + + + /* * vpImageFilter */ @@ -431,5 +610,16 @@ bindings_vpImageFilter(py::class_(pyImageFilter); define_complex_getGradXY(pyImageFilter); define_complex_getGradXY(pyImageFilter); + + define_gaussianFilter(pyImageFilter); + define_gaussianFilter(pyImageFilter); + define_gaussianFilter(pyImageFilter); + + define_gaussianBlur(pyImageFilter); + define_gaussianBlur(pyImageFilter); + define_gaussianBlur(pyImageFilter); + define_gaussianBlur(pyImageFilter); + + define_canny(pyImageFilter); } #endif diff --git a/modules/python/config/core.json b/modules/python/config/core.json index 29f1be308a..7b54de10b4 100644 --- a/modules/python/config/core.json +++ b/modules/python/config/core.json @@ -1092,6 +1092,44 @@ "TypeFilterable" ] ] + }, + { + "static": true, + "signature": "void canny(const vpImage< unsigned char > &, vpImage< unsigned char > &, const unsigned int &, const float &, const float &, const unsigned int &)", + "param_is_input": [ + true, + false, + true, + true, + true, + true + ], + "param_is_output": [ + false, + true, + false, + false, + false, + false + ] + }, + { + "static": true, + "signature": "void canny(const vpImage< unsigned char > &, vpImage< unsigned char > &, const unsigned int &, const float &, const unsigned int &)", + "param_is_input": [ + true, + false, + true, + true, + true + ], + "param_is_output": [ + false, + true, + false, + false, + false + ] } ] },