Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpCannyEdgeDetection.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 *****************************************************************************/
31 
32 #include <visp3/core/vpCannyEdgeDetection.h>
33 
34 #include <visp3/core/vpImageConvert.h>
35 
36 #if (VISP_CXX_STANDARD == VISP_CXX_STANDARD_98) // Check if cxx98
37 namespace
38 {
39 // Helper to apply the scale to the raw values of the filters
40 template <typename FilterType>
41 static void scaleFilter(
42 #ifdef ENABLE_VISP_NAMESPACE
43  visp::
44 #endif
45  vpArray2D<FilterType> &filter, const float &scale)
46 {
47  const unsigned int nbRows = filter.getRows();
48  const unsigned int nbCols = filter.getCols();
49  for (unsigned int r = 0; r < nbRows; ++r) {
50  for (unsigned int c = 0; c < nbCols; ++c) {
51  filter[r][c] = filter[r][c] * scale;
52  }
53  }
54 }
55 };
56 #endif
57 
59 #ifdef VISP_HAVE_NLOHMANN_JSON
60 void from_json(const nlohmann::json &j, vpCannyEdgeDetection &detector)
61 {
62  std::string filteringAndGradientName = vpImageFilter::vpCannyFiltAndGradTypeToStr(detector.m_filteringAndGradientType);
63  filteringAndGradientName = j.value("filteringAndGradientType", filteringAndGradientName);
64  detector.m_filteringAndGradientType = vpImageFilter::vpCannyFiltAndGradTypeFromStr(filteringAndGradientName);
65  detector.m_gaussianKernelSize = j.value("gaussianSize", detector.m_gaussianKernelSize);
66  detector.m_gaussianStdev = j.value("gaussianStdev", detector.m_gaussianStdev);
67  detector.m_lowerThreshold = j.value("lowerThreshold", detector.m_lowerThreshold);
68  detector.m_lowerThresholdRatio = j.value("lowerThresholdRatio", detector.m_lowerThresholdRatio);
69  detector.m_gradientFilterKernelSize = j.value("gradientFilterKernelSize", detector.m_gradientFilterKernelSize);
70  detector.m_upperThreshold = j.value("upperThreshold", detector.m_upperThreshold);
71  detector.m_upperThresholdRatio = j.value("upperThresholdRatio", detector.m_upperThresholdRatio);
72 }
73 
74 void to_json(nlohmann::json &j, const vpCannyEdgeDetection &detector)
75 {
76  std::string filteringAndGradientName = vpImageFilter::vpCannyFiltAndGradTypeToStr(detector.m_filteringAndGradientType);
77  j = nlohmann::json {
78  {"filteringAndGradientType", filteringAndGradientName},
79  {"gaussianSize", detector.m_gaussianKernelSize},
80  {"gaussianStdev", detector.m_gaussianStdev},
81  {"lowerThreshold", detector.m_lowerThreshold},
82  {"lowerThresholdRatio", detector.m_lowerThresholdRatio},
83  {"gradientFilterKernelSize", detector.m_gradientFilterKernelSize},
84  {"upperThreshold", detector.m_upperThreshold},
85  {"upperThresholdRatio", detector.m_upperThresholdRatio}
86  };
87 }
88 #endif
89 
90 // // Initialization methods
91 
93  : m_filteringAndGradientType(vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING)
94  , m_gaussianKernelSize(3)
95  , m_gaussianStdev(1.f)
96  , m_areGradientAvailable(false)
97  , m_gradientFilterKernelSize(3)
98  , m_lowerThreshold(-1.f)
99  , m_lowerThresholdRatio(0.6f)
100  , m_upperThreshold(-1.f)
101  , m_upperThresholdRatio(0.8f)
102  , mp_mask(nullptr)
103 {
104  initGaussianFilters();
105  initGradientFilters();
106 }
107 
108 vpCannyEdgeDetection::vpCannyEdgeDetection(const int &gaussianKernelSize, const float &gaussianStdev
109  , const unsigned int &sobelAperture, const float &lowerThreshold, const float &upperThreshold
110  , const float &lowerThresholdRatio, const float &upperThresholdRatio
112 )
113  : m_filteringAndGradientType(filteringType)
114  , m_gaussianKernelSize(gaussianKernelSize)
115  , m_gaussianStdev(gaussianStdev)
116  , m_areGradientAvailable(false)
117  , m_gradientFilterKernelSize(sobelAperture)
118  , m_lowerThreshold(lowerThreshold)
119  , m_lowerThresholdRatio(lowerThresholdRatio)
120  , m_upperThreshold(upperThreshold)
121  , m_upperThresholdRatio(upperThresholdRatio)
122  , mp_mask(nullptr)
123 {
124  initGaussianFilters();
125  initGradientFilters();
126 }
127 
128 #ifdef VISP_HAVE_NLOHMANN_JSON
129 
130 using json = nlohmann::json;
131 
132 vpCannyEdgeDetection::vpCannyEdgeDetection(const std::string &jsonPath)
133 {
134  initFromJSON(jsonPath);
135 }
136 
137 void
138 vpCannyEdgeDetection::initFromJSON(const std::string &jsonPath)
139 {
140  std::ifstream file(jsonPath);
141  if (!file.good()) {
142  std::stringstream ss;
143  ss << "Problem opening file " << jsonPath << ". Make sure it exists and is readable" << std::endl;
144  throw vpException(vpException::ioError, ss.str());
145  }
146  json j;
147  try {
148  j = json::parse(file);
149  }
150  catch (json::parse_error &e) {
151  std::stringstream msg;
152  msg << "Could not parse JSON file : \n";
153  msg << e.what() << std::endl;
154  msg << "Byte position of error: " << e.byte;
155  throw vpException(vpException::ioError, msg.str());
156  }
157  from_json(j, *this);
158  file.close();
159  initGaussianFilters();
160  initGradientFilters();
161 }
162 #endif
163 
164 void
165 vpCannyEdgeDetection::initGaussianFilters()
166 {
167  if ((m_gaussianKernelSize % 2) == 0) {
168  throw(vpException(vpException::badValue, "The Gaussian kernel size should be odd"));
169  }
170  m_fg.resize(1, (m_gaussianKernelSize + 1) / 2);
171  vpImageFilter::getGaussianKernel(m_fg.data, m_gaussianKernelSize, m_gaussianStdev, true);
172 }
173 
174 void
175 vpCannyEdgeDetection::initGradientFilters()
176 {
177  if ((m_gradientFilterKernelSize % 2) != 1) {
178  throw vpException(vpException::badValue, "Gradient filters kernel size should be odd.");
179  }
180  m_gradientFilterX.resize(m_gradientFilterKernelSize, m_gradientFilterKernelSize);
181  m_gradientFilterY.resize(m_gradientFilterKernelSize, m_gradientFilterKernelSize);
182 
183 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
184  auto scaleFilter = [](vpArray2D<float> &filter, const float &scale) {
185  unsigned int filter_rows = filter.getRows();
186  unsigned int filter_col = filter.getCols();
187  for (unsigned int r = 0; r < filter_rows; ++r) {
188  for (unsigned int c = 0; c < filter_col; ++c) {
189  filter[r][c] = filter[r][c] * scale;
190  }
191  }
192  };
193 #endif
194 
195  float scaleX = 1.f;
196  float scaleY = 1.f;
197 
198  if (m_filteringAndGradientType == vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING) {
199  scaleX = vpImageFilter::getSobelKernelX(m_gradientFilterX.data, (m_gradientFilterKernelSize - 1) / 2);
200  scaleY = vpImageFilter::getSobelKernelY(m_gradientFilterY.data, (m_gradientFilterKernelSize - 1) / 2);
201  }
202  else if (m_filteringAndGradientType == vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING) {
203  // Compute the Scharr filters
204  scaleX = vpImageFilter::getScharrKernelX(m_gradientFilterX.data, (m_gradientFilterKernelSize - 1) / 2);
205  scaleY = vpImageFilter::getScharrKernelY(m_gradientFilterY.data, (m_gradientFilterKernelSize - 1) / 2);
206  }
207  else {
208  std::string errMsg = "[vpCannyEdgeDetection::initGradientFilters] Error: gradient filtering method \"";
209  errMsg += vpImageFilter::vpCannyFiltAndGradTypeToStr(m_filteringAndGradientType);
210  errMsg += "\" has not been implemented yet\n";
212  }
213 
214  scaleFilter(m_gradientFilterX, scaleX);
215  scaleFilter(m_gradientFilterY, scaleY);
216 }
217 
218 // // Detection methods
219 #ifdef HAVE_OPENCV_CORE
221 vpCannyEdgeDetection::detect(const cv::Mat &cv_I)
222 {
223  vpImage<unsigned char> I_gray;
224  vpImageConvert::convert(cv_I, I_gray);
225  return detect(I_gray);
226 }
227 #endif
228 
231 {
232  vpImage<unsigned char> I_gray;
233  vpImageConvert::convert(I_color, I_gray);
234  return detect(I_gray);
235 }
236 
239 {
240  // // Clearing the previous results
241  m_edgeMap.resize(I.getHeight(), I.getWidth(), 0);
242  m_edgeCandidateAndGradient.clear();
243  m_edgePointsCandidates.clear();
244 
245  // // Step 1 and 2: filter the image and compute the gradient, if not given by the user
246  if (!m_areGradientAvailable) {
247  computeFilteringAndGradient(I);
248  }
249  m_areGradientAvailable = false; // Reset for next call
250 
251  // // Step 3: edge thining
252  float upperThreshold = m_upperThreshold;
253  float lowerThreshold = m_lowerThreshold;
254  if (upperThreshold < 0) {
255  upperThreshold = vpImageFilter::computeCannyThreshold(I, lowerThreshold, &m_dIx, &m_dIy, m_gaussianKernelSize,
256  m_gaussianStdev, m_gradientFilterKernelSize, m_lowerThresholdRatio,
257  m_upperThresholdRatio, m_filteringAndGradientType, mp_mask);
258  }
259  else if (m_lowerThreshold < 0) {
260  // Applying Canny recommendation to have the upper threshold 3 times greater than the lower threshold.
261  lowerThreshold = m_upperThreshold / 3.f;
262  }
263  // To ensure that if lowerThreshold = 0, we reject null gradient points
264  lowerThreshold = std::max<float>(lowerThreshold, std::numeric_limits<float>::epsilon());
265  performEdgeThinning(lowerThreshold);
266 
267  // // Step 4: hysteresis thresholding
268  performHysteresisThresholding(lowerThreshold, upperThreshold);
269 
270  // // Step 5: edge tracking
271  performEdgeTracking();
272  return m_edgeMap;
273 }
274 
275 void
276 vpCannyEdgeDetection::computeFilteringAndGradient(const vpImage<unsigned char> &I)
277 {
278  if ((m_filteringAndGradientType == vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING)
279  || (m_filteringAndGradientType == vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING)) {
280  // Computing the Gaussian blur
281  vpImage<float> Iblur;
282  vpImage<float> GIx;
283  vpImageFilter::filterX<unsigned char, float>(I, GIx, m_fg.data, m_gaussianKernelSize, mp_mask);
284  vpImageFilter::filterY<float, float>(GIx, Iblur, m_fg.data, m_gaussianKernelSize, mp_mask);
285 
286  // Computing the gradients
287  vpImageFilter::filter(Iblur, m_dIx, m_gradientFilterX, true, mp_mask);
288  vpImageFilter::filter(Iblur, m_dIy, m_gradientFilterY, true, mp_mask);
289  }
290  else {
291  std::string errmsg("Currently, the filtering operation \"");
292  errmsg += vpImageFilter::vpCannyFiltAndGradTypeToStr(m_filteringAndGradientType);
293  errmsg += "\" is not handled.";
295  }
296 }
297 
310 void
311 getInterpolWeightsAndOffsets(const float &gradientOrientation,
312  float &alpha, float &beta,
313  int &dRowGradAlpha, int &dRowGradBeta,
314  int &dColGradAlpha, int &dColGradBeta
315 )
316 {
317  float thetaMin = 0.f;
318  if (gradientOrientation < M_PI_4_FLOAT) {
319  // Angles between 0 and 45 deg rely on the horizontal and diagonal points
320  dColGradAlpha = 1;
321  dColGradBeta = 1;
322  dRowGradAlpha = 0;
323  dRowGradBeta = -1;
324  }
325  else if ((gradientOrientation >= M_PI_4_FLOAT) && (gradientOrientation < M_PI_2_FLOAT)) {
326  // Angles between 45 and 90 deg rely on the diagonal and vertical points
327  thetaMin = M_PI_4_FLOAT;
328  dColGradAlpha = 1;
329  dColGradBeta = 0;
330  dRowGradAlpha = -1;
331  dRowGradBeta = -1;
332  }
333  else if ((gradientOrientation >= M_PI_2_FLOAT) && (gradientOrientation < (3.f * M_PI_4_FLOAT))) {
334  // Angles between 90 and 135 deg rely on the vertical and diagonal points
335  thetaMin = M_PI_2_FLOAT;
336  dColGradAlpha = 0;
337  dColGradBeta = -1;
338  dRowGradAlpha = -1;
339  dRowGradBeta = -1;
340  }
341  else if ((gradientOrientation >= (3.f * M_PI_4_FLOAT)) && (gradientOrientation < M_PI_FLOAT)) {
342  // Angles between 135 and 180 deg rely on the vertical and diagonal points
343  thetaMin = 3.f * M_PI_4_FLOAT;
344  dColGradAlpha = -1;
345  dColGradBeta = -1;
346  dRowGradAlpha = -1;
347  dRowGradBeta = 0;
348  }
349  beta = (gradientOrientation - thetaMin) / M_PI_4_FLOAT;
350  alpha = 1.f - beta;
351 }
352 
362 float
363 getManhattanGradient(const vpImage<float> &dIx, const vpImage<float> &dIy, const int &row, const int &col)
364 {
365  float grad = 0.;
366  int nbRows = dIx.getRows();
367  int nbCols = dIx.getCols();
368  if ((row >= 0)
369  && (row < nbRows)
370  && (col >= 0)
371  && (col < nbCols)
372  ) {
373  float dx = dIx[row][col];
374  float dy = dIy[row][col];
375  grad = std::abs(dx) + std::abs(dy);
376  }
377  return grad;
378 }
379 
391 float
392 getGradientOrientation(const vpImage<float> &dIx, const vpImage<float> &dIy, const int &row, const int &col)
393 {
394  float gradientOrientation = 0.f;
395  float dx = dIx[row][col];
396  float dy = dIy[row][col];
397 
398  if (std::abs(dx) < std::numeric_limits<float>::epsilon()) {
399  gradientOrientation = M_PI_2_FLOAT;
400  }
401  else {
402  // -dy because the y-axis of the image is oriented towards the bottom of the screen
403  // while we later work with a y-axis oriented towards the top when getting the theta quadrant.
404  gradientOrientation = static_cast<float>(std::atan2(-dy, dx));
405  if (gradientOrientation < 0.f) {
406  gradientOrientation += M_PI_FLOAT; // + M_PI in order to be between 0 and M_PI_FLOAT
407  }
408  }
409  return gradientOrientation;
410 }
411 
412 void
413 vpCannyEdgeDetection::performEdgeThinning(const float &lowerThreshold)
414 {
415  int nbRows = m_dIx.getRows();
416  int nbCols = m_dIx.getCols();
417 
418  bool ignore_current_pixel = false;
419  bool grad_lower_threshold = false;
420  for (int row = 0; row < nbRows; ++row) {
421  for (int col = 0; col < nbCols; ++col) {
422  // reset the checks
423  ignore_current_pixel = false;
424  grad_lower_threshold = false;
425 
426  if (mp_mask != nullptr) {
427  if (!(*mp_mask)[row][col]) {
428  // The mask tells us to ignore the current pixel
429  ignore_current_pixel = true;
430  // continue
431  }
432  }
433  // continue if the mask does not tell us to ignore the current pixel
434  if (ignore_current_pixel == false) {
435 
436  // Computing the gradient orientation and magnitude
437  float grad = getManhattanGradient(m_dIx, m_dIy, row, col);
438 
439  if (grad < lowerThreshold) {
440  // The gradient is lower than minimum threshold => ignoring the point
441  grad_lower_threshold = true;
442  // continue
443  }
444  if (grad_lower_threshold == false) {
445  //
446  // Getting the offset along the horizontal and vertical axes
447  // depending on the gradient orientation
448  int dRowAlphaPlus = 0, dRowBetaPlus = 0;
449  int dColAphaPlus = 0, dColBetaPlus = 0;
450  float gradientOrientation = getGradientOrientation(m_dIx, m_dIy, row, col);
451  float alpha = 0.f, beta = 0.f;
452  getInterpolWeightsAndOffsets(gradientOrientation, alpha, beta, dRowAlphaPlus, dRowBetaPlus, dColAphaPlus, dColBetaPlus);
453  int dRowAlphaMinus = -dRowAlphaPlus, dRowBetaMinus = -dRowBetaPlus;
454  int dColAphaMinus = -dColAphaPlus, dColBetaMinus = -dColBetaPlus;
455  float gradAlphaPlus = getManhattanGradient(m_dIx, m_dIy, row + dRowAlphaPlus, col + dColAphaPlus);
456  float gradBetaPlus = getManhattanGradient(m_dIx, m_dIy, row + dRowBetaPlus, col + dColBetaPlus);
457  float gradAlphaMinus = getManhattanGradient(m_dIx, m_dIy, row + dRowAlphaMinus, col + dColAphaMinus);
458  float gradBetaMinus = getManhattanGradient(m_dIx, m_dIy, row + dRowBetaMinus, col + dColBetaMinus);
459  float gradPlus = (alpha * gradAlphaPlus) + (beta * gradBetaPlus);
460  float gradMinus = (alpha * gradAlphaMinus) + (beta * gradBetaMinus);
461 
462  if ((grad >= gradPlus) && (grad >= gradMinus)) {
463  // Keeping the edge point that has the highest gradient
464  std::pair<unsigned int, unsigned int> bestPixel(row, col);
465  m_edgeCandidateAndGradient[bestPixel] = grad;
466  }
467  }
468  }
469  }
470  }
471 }
472 
473 void
474 vpCannyEdgeDetection::performHysteresisThresholding(const float &lowerThreshold, const float &upperThreshold)
475 {
476  std::map<std::pair<unsigned int, unsigned int>, float>::iterator it;
477  std::map<std::pair<unsigned int, unsigned int>, float>::iterator m_edgeCandidateAndGradient_end = m_edgeCandidateAndGradient.end();
478  for (it = m_edgeCandidateAndGradient.begin(); it != m_edgeCandidateAndGradient_end; ++it) {
479  if (it->second >= upperThreshold) {
480  m_edgePointsCandidates[it->first] = STRONG_EDGE;
481  }
482  else if ((it->second >= lowerThreshold) && (it->second < upperThreshold)) {
483  m_edgePointsCandidates[it->first] = WEAK_EDGE;
484  }
485  }
486 }
487 
488 void
489 vpCannyEdgeDetection::performEdgeTracking()
490 {
491  std::map<std::pair<unsigned int, unsigned int>, EdgeType>::iterator it;
492  std::map<std::pair<unsigned int, unsigned int>, EdgeType>::iterator m_edgePointsCandidates_end = m_edgePointsCandidates.end();
493  for (it = m_edgePointsCandidates.begin(); it != m_edgePointsCandidates_end; ++it) {
494  if (it->second == STRONG_EDGE) {
495  m_edgeMap[it->first.first][it->first.second] = 255;
496  }
497  else if (it->second == WEAK_EDGE) {
498  if (recursiveSearchForStrongEdge(it->first)) {
499  m_edgeMap[it->first.first][it->first.second] = 255;
500  }
501  }
502  }
503 }
504 
505 bool
506 vpCannyEdgeDetection::recursiveSearchForStrongEdge(const std::pair<unsigned int, unsigned int> &coordinates)
507 {
508  bool hasFoundStrongEdge = false;
509  int nbRows = m_dIx.getRows();
510  int nbCols = m_dIx.getCols();
511  m_edgePointsCandidates[coordinates] = ON_CHECK;
512  bool test_row = false;
513  bool test_col = false;
514  bool test_drdc = false;
515  bool edge_in_image_limit = false;
516  int dr = -1;
517  while ((dr <= 1) && (!hasFoundStrongEdge)) {
518  int dc = -1;
519  while ((dc <= 1) && (!hasFoundStrongEdge)) {
520  // reset the check for the edge on image limit
521  edge_in_image_limit = false;
522 
523  int idRow = dr + static_cast<int>(coordinates.first);
524  idRow = std::max<int>(idRow, 0); // Avoid getting negative pixel ID
525  int idCol = dc + static_cast<int>(coordinates.second);
526  idCol = std::max<int>(idCol, 0); // Avoid getting negative pixel ID
527 
528  // Checking if we are still looking for an edge in the limit of the image
529  test_row = (idRow < 0) || (idRow >= nbRows);
530  test_col = (idCol < 0) || (idCol >= nbCols);
531  test_drdc = (dr == 0) && (dc == 0);
532  if (test_row || test_col || test_drdc) {
533  edge_in_image_limit = true;
534  // the continue is replaced by the test
535  }
536  if (edge_in_image_limit == false) {
537 
538  try {
539  std::pair<unsigned int, unsigned int> key_candidate(idRow, idCol);
540  // Checking if the 8-neighbor point is in the list of edge candidates
541  EdgeType type_candidate = m_edgePointsCandidates.at(key_candidate);
542  if (type_candidate == STRONG_EDGE) {
543  // The 8-neighbor point is a strong edge => the weak edge becomes a strong edge
544  hasFoundStrongEdge = true;
545  }
546  else if (type_candidate == WEAK_EDGE) {
547  // Checking if the WEAK_EDGE neighbor has a STRONG_EDGE neighbor
548  hasFoundStrongEdge = recursiveSearchForStrongEdge(key_candidate);
549  }
550  }
551  catch (...) {
552  // continue - nothing to do
553  }
554  }
555  ++dc;
556  }
557  ++dr;
558  }
559  if (hasFoundStrongEdge) {
560  m_edgePointsCandidates[coordinates] = STRONG_EDGE;
561  m_edgeMap[coordinates.first][coordinates.second] = 255;
562  }
563  return hasFoundStrongEdge;
564 }
565 END_VISP_NAMESPACE
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition: vpArray2D.h:145
unsigned int getCols() const
Definition: vpArray2D.h:337
Type * data
Address of the first element of the data array.
Definition: vpArray2D.h:148
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition: vpArray2D.h:362
unsigned int getRows() const
Definition: vpArray2D.h:347
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.
friend void from_json(const nlohmann::json &j, vpCannyEdgeDetection &detector)
Read the detector configuration from JSON. All values are optional and if an argument is not present,...
void initFromJSON(const std::string &jsonPath)
Initialize all the algorithm parameters using the JSON file whose path is jsonPath....
vpCannyEdgeDetection()
Default constructor of the vpCannyEdgeDetection class. The thresholds used during the hysteresis thre...
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ ioError
I/O error.
Definition: vpException.h:67
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
@ notImplementedError
Not implemented.
Definition: vpException.h:69
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Various image filter, convolution, etc...
Definition: vpImageFilter.h:71
static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
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
static void filter(const vpImage< ImageType > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false, const vpImage< bool > *p_mask=nullptr)
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 void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
unsigned int getWidth() const
Definition: vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:538
unsigned int getCols() const
Definition: vpImage.h:171
unsigned int getHeight() const
Definition: vpImage.h:181
unsigned int getRows() const
Definition: vpImage.h:212
Definition: vpIoTools.h:61