Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpImageFilter_canny.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Image Canny filtering.
32 */
33
34#include <visp3/core/vpConfig.h>
35#include <visp3/core/vpImageFilter.h>
36#include <visp3/core/vpIoTools.h>
37#include <visp3/core/vpCannyEdgeDetection.h>
38
40
49std::string vpImageFilter::vpCannyBackendTypeList(const std::string &pref, const std::string &sep,
50 const std::string &suf)
51{
52 std::string list(pref);
53 for (unsigned int i = 0; i < (CANNY_COUNT_BACKEND - 1); ++i) {
54 vpCannyBackendType type = static_cast<vpCannyBackendType>(i);
55 list += vpCannyBackendTypeToString(type);
56 list += sep;
57 }
59 list += vpCannyBackendTypeToString(type);
60 list += suf;
61 return list;
62}
63
71{
72 std::string name;
73 switch (type) {
75 name = "opencv-backend";
76 break;
78 name = "visp-backend";
79 break;
81 return "unknown-backend";
82 default: {
83 throw(vpException(vpException::fatalError, "Unsupported canny backend type in vpImageFilter::vpCannyBackendTypeToString()"));
84 }
85 }
86 return name;
87}
88
96{
98 std::string nameLowerCase = vpIoTools::toLowerCase(name);
99 unsigned int count = static_cast<unsigned int>(CANNY_COUNT_BACKEND);
100 bool notFound = true;
101 unsigned int i = 0;
102 while ((i < count) && notFound) {
103 vpCannyBackendType temp = static_cast<vpCannyBackendType>(i);
104 if (nameLowerCase == vpCannyBackendTypeToString(temp)) {
105 type = temp;
106 notFound = false;
107 }
108 ++i;
109 }
110 return type;
111}
112
121std::string vpImageFilter::vpGetCannyFiltAndGradTypes(const std::string &pref, const std::string &sep,
122 const std::string &suf)
123{
124 std::string list(pref);
125 for (unsigned int i = 0; i < (CANNY_COUNT_FILTERING - 1); ++i) {
127 list += vpCannyFiltAndGradTypeToStr(type);
128 list += sep;
129 }
131 list += vpCannyFiltAndGradTypeToStr(type);
132 list += suf;
133 return list;
134}
135
143{
144 std::string name;
145 switch (type) {
147 name = "gaussianblur+sobel-filtering";
148 break;
150 name = "gaussianblur+scharr-filtering";
151 break;
153 return "unknown-filtering";
154 default: {
155 throw(vpException(vpException::fatalError, "Unsupported canny backend type in vpImageFilter::vpCannyFiltAndGradTypeToStr()"));
156 }
157 }
158 return name;
159}
160
168{
170 std::string nameLowerCase = vpIoTools::toLowerCase(name);
171 unsigned int count = static_cast<unsigned int>(CANNY_COUNT_FILTERING);
172 bool notFound = true;
173 unsigned int i = 0;
174 while ((i < count) && notFound) {
176 if (nameLowerCase == vpCannyFiltAndGradTypeToStr(temp)) {
177 type = temp;
178 notFound = false;
179 }
180 ++i;
181 }
182 return type;
183}
184
185#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
204float vpImageFilter::computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy,
205 float &lowerThresh, const unsigned int &gaussianKernelSize,
206 const float &gaussianStdev, const unsigned int &apertureGradient,
207 const float &lowerThresholdRatio, const float &upperThresholdRatio,
209{
210 if ((lowerThresholdRatio <= 0.f) || (lowerThresholdRatio >= 1.f)) {
211 std::stringstream errMsg;
212 errMsg << "Lower ratio (" << lowerThresholdRatio << ") " << (lowerThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
213 throw(vpException(vpException::fatalError, errMsg.str()));
214 }
215
216 if ((upperThresholdRatio <= 0.f) || (upperThresholdRatio >= 1.f)) {
217 std::stringstream errMsg;
218 errMsg << "Upper ratio (" << upperThresholdRatio << ") " << (upperThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
219 throw(vpException(vpException::fatalError, errMsg.str()));
220 }
221
222 if (lowerThresholdRatio >= upperThresholdRatio) {
223 std::stringstream errMsg;
224 errMsg << "Lower ratio (" << lowerThresholdRatio << ") should be lower than the upper ratio (" << upperThresholdRatio << ")";
225 throw(vpException(vpException::fatalError, errMsg.str()));
226 }
227
228 double w = cv_I.cols;
229 double h = cv_I.rows;
230 int bins = 256;
231 cv::Mat dI, dIx, dIy, dIx_abs, dIy_abs;
232
233 if ((p_cv_dIx == nullptr) || (p_cv_dIy == nullptr)) {
234 computePartialDerivatives(cv_I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev, apertureGradient,
235 filteringType);
236 }
237 else {
238 dIx = *p_cv_dIx;
239 dIy = *p_cv_dIy;
240 }
241
242 // Compute the absolute gradient of the blurred image G = |dIx| + |dIy|
243 cv::convertScaleAbs(dIx, dIx_abs);
244 cv::convertScaleAbs(dIy, dIy_abs);
245 cv::addWeighted(dIx_abs, 1, dIy_abs, 1, 0, dI);
246 dI.convertTo(dI, CV_8U);
247
248 // Compute the upper threshold from the equalized histogram
249 cv::Mat hist;
250 const float range[] = { 0.f, 256.f }; // The upper boundary is exclusive
251 const float *ranges[] = { range };
252 int channels[] = { 0 };
253 int dims = 1; // The number of dimensions of the histogram
254 int histSize[] = { bins };
255 bool uniform = true;
256 bool accumulate = false; // Clear the histogram at the beginning of calcHist if false, does not clear it otherwise
257 cv::calcHist(&dI, 1, channels, cv::Mat(), hist, dims, histSize, ranges, uniform, accumulate);
258 float accu = 0;
259 float t = static_cast<float>(upperThresholdRatio * w * h);
260 float tLow = static_cast<float>(lowerThresholdRatio * w * h);
261 float bon = 0;
262 int i = 0;
263 bool notFound = true, notFoundLower = true;
264 while ((i < bins) && notFound) {
265 float tf = hist.at<float>(i);
266 accu = accu + tf;
267 if ((accu > tLow) && notFoundLower) {
268 lowerThresh = static_cast<float>(i);
269 notFoundLower = false;
270 }
271 if (accu > t) {
272 bon = static_cast<float>(i);
273 notFound = false;
274 }
275 ++i;
276 }
277 if (notFound) {
278 std::stringstream errMsg;
279 errMsg << "Could not find a bin for which " << upperThresholdRatio * 100.f << " percents of the pixels had a gradient lower than the upper threshold.";
280 throw(vpException(vpException::fatalError, errMsg.str()));
281 }
282 float upperThresh = std::max<float>(bon, 1.f);
283 return upperThresh;
284}
285#endif
286
331 const unsigned int &gaussianFilterSize, const float &thresholdCanny,
332 const unsigned int &apertureSobel)
333{
334 vpImageFilter::canny(Isrc, Ires, gaussianFilterSize, thresholdCanny / 3.f, thresholdCanny, apertureSobel);
335}
336
385 const unsigned int &gaussianFilterSize,
386 const float &lowerThreshold, const float &upperThreshold,
387 const unsigned int &apertureSobel)
388{
389 const float gaussianStdev = 2.f;
390 const float upperThresholdRatio = 0.8f;
391 const float lowerThresholdRatio = 0.6f;
392#if defined(HAVE_OPENCV_IMGPROC)
393 const vpCannyBackendType cannyBackend = CANNY_OPENCV_BACKEND;
394#else
395 const vpCannyBackendType cannyBackend = CANNY_VISP_BACKEND;
396#endif
398 canny(Isrc, Ires, gaussianFilterSize, lowerThreshold, upperThreshold, apertureSobel,
399 gaussianStdev, lowerThresholdRatio, upperThresholdRatio, false, cannyBackend, cannyFilteringSteps);
400}
401
471 const unsigned int &gaussianFilterSize,
472 const float &lowerThreshold, const float &upperThreshold, const unsigned int &apertureGradient,
473 const float &gaussianStdev, const float &lowerThresholdRatio, const float &upperThresholdRatio,
474 const bool &normalizeGradients,
475 const vpCannyBackendType &cannyBackend, const vpCannyFilteringAndGradientType &cannyFilteringSteps,
476 const vpImage<bool> *p_mask)
477{
478 if (cannyBackend == CANNY_OPENCV_BACKEND) {
479#if defined(HAVE_OPENCV_IMGPROC)
480 cv::Mat img_cvmat, cv_dx, cv_dy, edges_cvmat;
481 vpImageConvert::convert(Isrc, img_cvmat);
482 computePartialDerivatives(img_cvmat, cv_dx, cv_dy, true, true, normalizeGradients, gaussianFilterSize,
483 gaussianStdev, apertureGradient, cannyFilteringSteps);
484 float upperCannyThresh = upperThreshold;
485 float lowerCannyThresh = lowerThreshold;
486 if (upperCannyThresh < 0.f) {
487 upperCannyThresh = computeCannyThreshold(img_cvmat, &cv_dx, &cv_dy, lowerCannyThresh, gaussianFilterSize,
488 gaussianStdev, apertureGradient, lowerThresholdRatio, upperThresholdRatio,
489 cannyFilteringSteps);
490 }
491 else if (lowerCannyThresh < 0.f) {
492 lowerCannyThresh = upperCannyThresh / 3.f;
493 }
494#if (VISP_HAVE_OPENCV_VERSION >= 0x030200)
495 cv::Canny(cv_dx, cv_dy, edges_cvmat, lowerCannyThresh, upperCannyThresh, false);
496#else
497 cv::GaussianBlur(img_cvmat, img_cvmat, cv::Size(static_cast<int>(gaussianFilterSize), static_cast<int>(gaussianFilterSize)),
498 gaussianStdev, gaussianStdev);
499 cv::Canny(img_cvmat, edges_cvmat, lowerCannyThresh, upperCannyThresh);
500#endif
501 vpImageConvert::convert(edges_cvmat, Ires);
502#else
503 std::string errMsg("[vpImageFilter::canny]You asked for CANNY_OPENCV_BACKEND but ViSP has not been compiled with OpenCV");
504 throw(vpException(vpException::badValue, errMsg));
505#endif
506 }
507 else if (cannyBackend == CANNY_VISP_BACKEND) {
508 float upperCannyThresh = upperThreshold;
509 float lowerCannyThresh = lowerThreshold;
510
511 vpImage<float> dIx, dIy;
512 computePartialDerivatives(Isrc, dIx, dIy, true, true, normalizeGradients, gaussianFilterSize,
513 gaussianStdev, apertureGradient, cannyFilteringSteps, cannyBackend, p_mask);
514
515 if (upperCannyThresh < 0.f) {
516 upperCannyThresh = computeCannyThreshold(Isrc, lowerCannyThresh, &dIx, &dIy, gaussianFilterSize, gaussianStdev,
517 apertureGradient, lowerThresholdRatio, upperThresholdRatio,
518 cannyFilteringSteps, p_mask);
519 }
520 else if (lowerCannyThresh < 0.f) {
521 lowerCannyThresh = upperCannyThresh / 3.f;
522 }
523 vpCannyEdgeDetection edgeDetector(gaussianFilterSize, gaussianStdev, apertureGradient, lowerCannyThresh, upperCannyThresh,
524 lowerThresholdRatio, upperThresholdRatio, cannyFilteringSteps);
525 edgeDetector.setGradients(dIx, dIy);
526 edgeDetector.setMask(p_mask);
527 Ires = edgeDetector.detect(Isrc);
528 }
529}
530
531END_VISP_NAMESPACE
Class that implements the Canny's edge detector. It is possible to use a boolean mask to ignore some ...
vpImage< unsigned char > detect(const vpImage< vpRGBa > &I_color)
Detect the edges in an image. Convert the color image into a gray-scale image.
void setMask(const vpImage< bool > *p_mask)
Set a mask to ignore pixels for which the mask is false.
void setGradients(const vpImage< float > &dIx, const vpImage< float > &dIy)
Set the Gradients of the image that will be processed.
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73
@ fatalError
Fatal error.
Definition vpException.h:72
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static std::string vpCannyBackendTypeToString(const vpCannyBackendType &type)
Cast a vpImageFilter::vpCannyBackendTypeToString into a string, to know its name.
static void canny(const vpImage< unsigned char > &I, vpImage< unsigned char > &Ic, const unsigned int &gaussianFilterSize, const float &thresholdCanny, const unsigned int &apertureSobel)
static std::string vpCannyBackendTypeList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyBackendType.
static float computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy, float &lowerThresh, const unsigned int &gaussianKernelSize=5, const float &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const float &lowerThresholdRatio=0.6f, const float &upperThresholdRatio=0.8f, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING)
Compute the upper Canny edge filter threshold, using Gaussian blur + Sobel or + Scharr operators to c...
static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
vpCannyFilteringAndGradientType
Canny filter and gradient operators to apply on the image before the edge detection stage.
@ CANNY_GBLUR_SOBEL_FILTERING
Apply Gaussian blur + Sobel operator on the input image.
@ CANNY_GBLUR_SCHARR_FILTERING
Apply Gaussian blur + Scharr operator on the input image.
vpCannyBackendType
Canny filter backends for the edge detection operations.
@ CANNY_VISP_BACKEND
Use ViSP.
@ CANNY_OPENCV_BACKEND
Use OpenCV.
static vpCannyFilteringAndGradientType vpCannyFiltAndGradTypeFromStr(const std::string &name)
Cast a string into a vpImageFilter::vpCannyFilteringAndGradientType.
static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyBackendTypeToString.
static std::string vpGetCannyFiltAndGradTypes(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyFilteringAndGradientType.
static void computePartialDerivatives(const cv::Mat &cv_I, cv::Mat &cv_dIx, cv::Mat &cv_dIy, const bool &computeDx=true, const bool &computeDy=true, const bool &normalize=true, const unsigned int &gaussianKernelSize=5, const float &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING)
Compute the partial derivatives (i.e. horizontal and vertical gradients) of the input image.
Definition of the vpImage class member functions.
Definition vpImage.h:131
static std::string toLowerCase(const std::string &input)
Return a lower-case version of the string input . Numbers and special characters stay the same.