Visual Servoing Platform  version 3.6.1 under development (2024-10-18)
vpImageFilter_canny.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 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 
39 BEGIN_VISP_NAMESPACE
40 
49 std::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;
77  case CANNY_VISP_BACKEND:
78  name = "visp-backend";
79  break;
81  default:
82  return "unknown-backend";
83  }
84  return name;
85 }
86 
94 {
96  std::string nameLowerCase = vpIoTools::toLowerCase(name);
97  unsigned int count = static_cast<unsigned int>(CANNY_COUNT_BACKEND);
98  bool notFound = true;
99  unsigned int i = 0;
100  while ((i < count) && notFound) {
101  vpCannyBackendType temp = static_cast<vpCannyBackendType>(i);
102  if (nameLowerCase == vpCannyBackendTypeToString(temp)) {
103  type = temp;
104  notFound = false;
105  }
106  ++i;
107  }
108  return type;
109 }
110 
119 std::string vpImageFilter::vpGetCannyFiltAndGradTypes(const std::string &pref, const std::string &sep,
120  const std::string &suf)
121 {
122  std::string list(pref);
123  for (unsigned int i = 0; i < (CANNY_COUNT_FILTERING - 1); ++i) {
125  list += vpCannyFiltAndGradTypeToStr(type);
126  list += sep;
127  }
129  list += vpCannyFiltAndGradTypeToStr(type);
130  list += suf;
131  return list;
132 }
133 
141 {
142  std::string name;
143  switch (type) {
145  name = "gaussianblur+sobel-filtering";
146  break;
148  name = "gaussianblur+scharr-filtering";
149  break;
151  default:
152  return "unknown-filtering";
153  }
154  return name;
155 }
156 
164 {
166  std::string nameLowerCase = vpIoTools::toLowerCase(name);
167  unsigned int count = static_cast<unsigned int>(CANNY_COUNT_FILTERING);
168  bool notFound = true;
169  unsigned int i = 0;
170  while ((i < count) && notFound) {
172  if (nameLowerCase == vpCannyFiltAndGradTypeToStr(temp)) {
173  type = temp;
174  notFound = false;
175  }
176  ++i;
177  }
178  return type;
179 }
180 
181 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
200 float vpImageFilter::computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy,
201  float &lowerThresh, const unsigned int &gaussianKernelSize,
202  const float &gaussianStdev, const unsigned int &apertureGradient,
203  const float &lowerThresholdRatio, const float &upperThresholdRatio,
205 {
206  double w = cv_I.cols;
207  double h = cv_I.rows;
208  int bins = 256;
209  cv::Mat dI, dIx, dIy, dIx_abs, dIy_abs;
210 
211  if ((p_cv_dIx == nullptr) || (p_cv_dIy == nullptr)) {
212  computePartialDerivatives(cv_I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev, apertureGradient,
213  filteringType);
214  }
215  else {
216  dIx = *p_cv_dIx;
217  dIy = *p_cv_dIy;
218  }
219 
220  // Compute the absolute gradient of the blurred image G = |dIx| + |dIy|
221  cv::convertScaleAbs(dIx, dIx_abs);
222  cv::convertScaleAbs(dIy, dIy_abs);
223  cv::addWeighted(dIx_abs, 1, dIy_abs, 1, 0, dI);
224  dI.convertTo(dI, CV_8U);
225 
226  // Compute the upper threshold from the equalized histogram
227  cv::Mat hist;
228  const float range[] = { 0.f, 256.f }; // The upper boundary is exclusive
229  const float *ranges[] = { range };
230  int channels[] = { 0 };
231  int dims = 1; // The number of dimensions of the histogram
232  int histSize[] = { bins };
233  bool uniform = true;
234  bool accumulate = false; // Clear the histogram at the beginning of calcHist if false, does not clear it otherwise
235  cv::calcHist(&dI, 1, channels, cv::Mat(), hist, dims, histSize, ranges, uniform, accumulate);
236  float accu = 0;
237  float t = static_cast<float>(upperThresholdRatio * w * h);
238  float bon = 0;
239  for (int i = 0; i < bins; ++i) {
240  float tf = hist.at<float>(i);
241  accu = accu + tf;
242  if (accu > t) {
243  bon = static_cast<float>(i);
244  break;
245  }
246  }
247  float upperThresh = std::max<float>(bon, 1.f);
248  lowerThresh = lowerThresholdRatio * bon;
249  return upperThresh;
250 }
251 #endif
252 
297  const unsigned int &gaussianFilterSize, const float &thresholdCanny,
298  const unsigned int &apertureSobel)
299 {
300  vpImageFilter::canny(Isrc, Ires, gaussianFilterSize, thresholdCanny / 3.f, thresholdCanny, apertureSobel);
301 }
302 
351  const unsigned int &gaussianFilterSize,
352  const float &lowerThreshold, const float &upperThreshold,
353  const unsigned int &apertureSobel)
354 {
355  const float gaussianStdev = 2.f;
356  const float upperThresholdRatio = 0.8f;
357  const float lowerThresholdRatio = 0.6f;
358 #if defined(HAVE_OPENCV_IMGPROC)
359  const vpCannyBackendType cannyBackend = CANNY_OPENCV_BACKEND;
360 #else
361  const vpCannyBackendType cannyBackend = CANNY_VISP_BACKEND;
362 #endif
364  canny(Isrc, Ires, gaussianFilterSize, lowerThreshold, upperThreshold, apertureSobel,
365  gaussianStdev, lowerThresholdRatio, upperThresholdRatio, false, cannyBackend, cannyFilteringSteps);
366 }
367 
437  const unsigned int &gaussianFilterSize,
438  const float &lowerThreshold, const float &upperThreshold, const unsigned int &apertureGradient,
439  const float &gaussianStdev, const float &lowerThresholdRatio, const float &upperThresholdRatio,
440  const bool &normalizeGradients,
441  const vpCannyBackendType &cannyBackend, const vpCannyFilteringAndGradientType &cannyFilteringSteps,
442  const vpImage<bool> *p_mask)
443 {
444  if (cannyBackend == CANNY_OPENCV_BACKEND) {
445 #if defined(HAVE_OPENCV_IMGPROC)
446  cv::Mat img_cvmat, cv_dx, cv_dy, edges_cvmat;
447  vpImageConvert::convert(Isrc, img_cvmat);
448  computePartialDerivatives(img_cvmat, cv_dx, cv_dy, true, true, normalizeGradients, gaussianFilterSize,
449  gaussianStdev, apertureGradient, cannyFilteringSteps);
450  float upperCannyThresh = upperThreshold;
451  float lowerCannyThresh = lowerThreshold;
452  if (upperCannyThresh < 0.f) {
453  upperCannyThresh = computeCannyThreshold(img_cvmat, &cv_dx, &cv_dy, lowerCannyThresh, gaussianFilterSize,
454  gaussianStdev, apertureGradient, lowerThresholdRatio, upperThresholdRatio,
455  cannyFilteringSteps);
456  }
457  else if (lowerCannyThresh < 0.f) {
458  lowerCannyThresh = upperCannyThresh / 3.f;
459  }
460 #if (VISP_HAVE_OPENCV_VERSION >= 0x030200)
461  cv::Canny(cv_dx, cv_dy, edges_cvmat, lowerCannyThresh, upperCannyThresh, false);
462 #else
463  cv::GaussianBlur(img_cvmat, img_cvmat, cv::Size((int)gaussianFilterSize, (int)gaussianFilterSize),
464  gaussianStdev, gaussianStdev);
465  cv::Canny(img_cvmat, edges_cvmat, lowerCannyThresh, upperCannyThresh);
466 #endif
467  vpImageConvert::convert(edges_cvmat, Ires);
468 #else
469  std::string errMsg("[vpImageFilter::canny]You asked for CANNY_OPENCV_BACKEND but ViSP has not been compiled with OpenCV");
470  throw(vpException(vpException::badValue, errMsg));
471 #endif
472  }
473  else if (cannyBackend == CANNY_VISP_BACKEND) {
474  float upperCannyThresh = upperThreshold;
475  float lowerCannyThresh = lowerThreshold;
476 
477  vpImage<float> dIx, dIy;
478  computePartialDerivatives(Isrc, dIx, dIy, true, true, normalizeGradients, gaussianFilterSize,
479  gaussianStdev, apertureGradient, cannyFilteringSteps, cannyBackend, p_mask);
480 
481  if (upperCannyThresh < 0.f) {
482  upperCannyThresh = computeCannyThreshold(Isrc, lowerCannyThresh, &dIx, &dIy, gaussianFilterSize, gaussianStdev,
483  apertureGradient, lowerThresholdRatio, upperThresholdRatio,
484  cannyFilteringSteps, p_mask);
485  }
486  else if (lowerCannyThresh < 0.f) {
487  lowerCannyThresh = upperCannyThresh / 3.f;
488  }
489  vpCannyEdgeDetection edgeDetector(gaussianFilterSize, gaussianStdev, apertureGradient, lowerCannyThresh, upperCannyThresh,
490  lowerThresholdRatio, upperThresholdRatio, cannyFilteringSteps);
491  edgeDetector.setGradients(dIx, dIy);
492  edgeDetector.setMask(p_mask);
493  Ires = edgeDetector.detect(Isrc);
494  }
495 }
496 
497 
498 END_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
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 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.
Definition: vpImageFilter.h:90
@ CANNY_GBLUR_SOBEL_FILTERING
Apply Gaussian blur + Sobel operator on the input image.
Definition: vpImageFilter.h:91
@ CANNY_GBLUR_SCHARR_FILTERING
Apply Gaussian blur + Scharr operator on the input image.
Definition: vpImageFilter.h:92
vpCannyBackendType
Canny filter backends for the edge detection operations.
Definition: vpImageFilter.h:75
@ CANNY_VISP_BACKEND
Use ViSP.
Definition: vpImageFilter.h:77
@ CANNY_OPENCV_BACKEND
Use OpenCV.
Definition: vpImageFilter.h:76
static vpCannyFilteringAndGradientType vpCannyFiltAndGradTypeFromStr(const std::string &name)
Cast a string into a vpImageFilter::vpCannyFilteringAndGradientType.
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.6, const float &upperThresholdRatio=0.8, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING)
Compute the upper Canny edge filter threshold, using Gaussian blur + Sobel or + Scharr operators to c...
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.
static std::string toLowerCase(const std::string &input)
Return a lower-case version of the string input . Numbers and special characters stay the same.
Definition: vpIoTools.cpp:1339