Visual Servoing Platform  version 3.6.1 under development (2024-04-23)
vpImageFilter.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 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  * Various image tools, convolution, ...
32  */
33 
34 #include <visp3/core/vpCannyEdgeDetection.h>
35 #include <visp3/core/vpImageFilter.h>
36 #include <visp3/core/vpIoTools.h>
37 #include <visp3/core/vpRGBa.h>
38 
47 std::string vpImageFilter::vpCannyBackendTypeList(const std::string &pref, const std::string &sep,
48  const std::string &suf)
49 {
50  std::string list(pref);
51  for (unsigned int i = 0; i < (CANNY_COUNT_BACKEND - 1); ++i) {
52  vpCannyBackendType type = static_cast<vpCannyBackendType>(i);
53  list += vpCannyBackendTypeToString(type);
54  list += sep;
55  }
57  list += vpCannyBackendTypeToString(type);
58  list += suf;
59  return list;
60 }
61 
69 {
70  std::string name;
71  switch (type) {
73  name = "opencv-backend";
74  break;
75  case CANNY_VISP_BACKEND:
76  name = "visp-backend";
77  break;
79  default:
80  return "unknown-backend";
81  }
82  return name;
83 }
84 
92 {
94  std::string nameLowerCase = vpIoTools::toLowerCase(name);
95  unsigned int count = static_cast<unsigned int>(CANNY_COUNT_BACKEND);
96  bool notFound = true;
97  unsigned int i = 0;
98  while ((i < count) && notFound) {
99  vpCannyBackendType temp = static_cast<vpCannyBackendType>(i);
100  if (nameLowerCase == vpCannyBackendTypeToString(temp)) {
101  type = temp;
102  notFound = false;
103  }
104  ++i;
105  }
106  return type;
107 }
108 
117 std::string vpImageFilter::vpCannyFilteringAndGradientTypeList(const std::string &pref, const std::string &sep,
118  const std::string &suf)
119 {
120  std::string list(pref);
121  for (unsigned int i = 0; i < (CANNY_COUNT_FILTERING - 1); ++i) {
124  list += sep;
125  }
128  list += suf;
129  return list;
130 }
131 
139 {
140  std::string name;
141  switch (type) {
143  name = "gaussianblur+sobel-filtering";
144  break;
146  name = "gaussianblur+scharr-filtering";
147  break;
149  default:
150  return "unknown-filtering";
151  }
152  return name;
153 }
154 
162 {
164  std::string nameLowerCase = vpIoTools::toLowerCase(name);
165  unsigned int count = static_cast<unsigned int>(CANNY_COUNT_FILTERING);
166  bool notFound = true;
167  unsigned int i = 0;
168  while ((i < count) && notFound) {
170  if (nameLowerCase == vpCannyFilteringAndGradientTypeToString(temp)) {
171  type = temp;
172  notFound = false;
173  }
174  ++i;
175  }
176  return type;
177 }
178 
182 template
183 void vpImageFilter::filter<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &If, const vpArray2D<float> &M, bool convolve, const vpImage<bool> *p_mask);
184 
185 template
186 void vpImageFilter::filter<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &If, const vpArray2D<double> &M, bool convolve, const vpImage<bool> *p_mask);
187 
188 template
189 void vpImageFilter::filter<float, float>(const vpImage<float> &I, vpImage<float> &Iu, vpImage<float> &Iv, const vpArray2D<float> &M,
190  bool convolve, const vpImage<bool> *p_mask);
191 
192 template
193 void vpImageFilter::filter<double, double>(const vpImage<double> &I, vpImage<double> &Iu, vpImage<double> &Iv, const vpArray2D<double> &M,
194  bool convolve, const vpImage<bool> *p_mask);
195 
196 template
197 void vpImageFilter::filter<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &GI, const float *filter,
198  unsigned int size, const vpImage<bool> *p_mask);
199 
200 template
201 void vpImageFilter::filter<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &GI, const double *filter,
202  unsigned int size, const vpImage<bool> *p_mask);
203 
204 template
205 void vpImageFilter::filter<float, float>(const vpImage<float> &I, vpImage<float> &GI, const float *filter, unsigned int size, const vpImage<bool> *p_mask);
206 
207 template
208 void vpImageFilter::filter<double, double>(const vpImage<double> &I, vpImage<double> &GI, const double *filter, unsigned int size, const vpImage<bool> *p_mask);
265  const vpColVector &kernelV)
266 {
267  const unsigned int size = kernelH.size(), sizeV = kernelV.size();
268  const unsigned int widthI = I.getWidth(), heightI = I.getHeight();
269  const unsigned int half_size = size / 2;
270 
271  If.resize(heightI, widthI, 0.0);
272  vpImage<double> I_filter(heightI, widthI, 0.0);
273 
274  for (unsigned int i = 0; i < heightI; ++i) {
275  for (unsigned int j = half_size; j < (widthI - half_size); ++j) {
276  double conv = 0.0;
277  for (unsigned int a = 0; a < size; ++a) {
278  conv += kernelH[a] * static_cast<double>(I[i][(j + half_size) - a]);
279  }
280 
281  I_filter[i][j] = conv;
282  }
283  }
284 
285  for (unsigned int i = half_size; i < (heightI - half_size); ++i) {
286  for (unsigned int j = 0; j < widthI; ++j) {
287  double conv = 0.0;
288  for (unsigned int a = 0; a < sizeV; ++a) {
289  conv += kernelV[a] * I_filter[(i + half_size) - a][j];
290  }
291 
292  If[i][j] = conv;
293  }
294  }
295 }
296 
300 template
301 void vpImageFilter::filterX<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIx, const float *filter,
302  unsigned int size, const vpImage<bool> *p_mask);
303 
304 template
305 void vpImageFilter::filterX<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIx, const double *filter,
306  unsigned int size, const vpImage<bool> *p_mask);
307 
308 template
309 void vpImageFilter::filterX<float, float>(const vpImage<float> &I, vpImage<float> &dIx, const float *filter, unsigned int size, const vpImage<bool> *p_mask);
310 
311 template
312 void vpImageFilter::filterX<double, double>(const vpImage<double> &I, vpImage<double> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask);
317 void vpImageFilter::filterX(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size,
318  const vpImage<bool> *p_mask)
319 {
320  const unsigned int heightI = I.getHeight(), widthI = I.getWidth();
321  const unsigned int stop1J = (size - 1) / 2;
322  const unsigned int stop2J = widthI - ((size - 1) / 2);
323  resizeAndInitializeIfNeeded(p_mask, heightI, widthI, dIx);
324 
325  for (unsigned int i = 0; i < heightI; ++i) {
326  for (unsigned int j = 0; j < stop1J; ++j) {
327  // We have to compute the value for each pixel if we don't have a mask or for
328  // pixels for which the mask is true otherwise
329  bool computeVal = checkBooleanMask(p_mask, i, j);
330  if (computeVal) {
331  dIx[i][j].R = static_cast<unsigned char>(vpImageFilter::filterXLeftBorderR(I, i, j, filter, size));
332  dIx[i][j].G = static_cast<unsigned char>(vpImageFilter::filterXLeftBorderG(I, i, j, filter, size));
333  dIx[i][j].B = static_cast<unsigned char>(vpImageFilter::filterXLeftBorderB(I, i, j, filter, size));
334  }
335  }
336  for (unsigned int j = stop1J; j < stop2J; ++j) {
337  // We have to compute the value for each pixel if we don't have a mask or for
338  // pixels for which the mask is true otherwise
339  bool computeVal = checkBooleanMask(p_mask, i, j);
340  if (computeVal) {
341  dIx[i][j].R = static_cast<unsigned char>(vpImageFilter::filterXR(I, i, j, filter, size));
342  dIx[i][j].G = static_cast<unsigned char>(vpImageFilter::filterXG(I, i, j, filter, size));
343  dIx[i][j].B = static_cast<unsigned char>(vpImageFilter::filterXB(I, i, j, filter, size));
344  }
345  }
346  for (unsigned int j = stop2J; j < widthI; ++j) {
347  // We have to compute the value for each pixel if we don't have a mask or for
348  // pixels for which the mask is true otherwise
349  bool computeVal = checkBooleanMask(p_mask, i, j);
350  if (computeVal) {
351  dIx[i][j].R = static_cast<unsigned char>(vpImageFilter::filterXRightBorderR(I, i, j, filter, size));
352  dIx[i][j].G = static_cast<unsigned char>(vpImageFilter::filterXRightBorderG(I, i, j, filter, size));
353  dIx[i][j].B = static_cast<unsigned char>(vpImageFilter::filterXRightBorderB(I, i, j, filter, size));
354  }
355  }
356  }
357 }
358 
362 template
363 void vpImageFilter::filterY<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIy, const float *filter,
364  unsigned int size, const vpImage<bool> *p_mask);
365 
366 template
367 void vpImageFilter::filterY<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIy, const double *filter,
368  unsigned int size, const vpImage<bool> *p_mask);
369 
370 template
371 void vpImageFilter::filterY<float, float>(const vpImage<float> &I, vpImage<float> &dIy, const float *filter, unsigned int size, const vpImage<bool> *p_mask);
372 
373 template
374 void vpImageFilter::filterY<double, double>(const vpImage<double> &I, vpImage<double> &dIy, const double *filter, unsigned int size, const vpImage<bool> *p_mask);
379 void vpImageFilter::filterY(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIy, const double *filter, unsigned int size,
380  const vpImage<bool> *p_mask)
381 {
382  const unsigned int heightI = I.getHeight(), widthI = I.getWidth();
383  const unsigned int stop1I = (size - 1) / 2;
384  const unsigned int stop2I = heightI - ((size - 1) / 2);
385  resizeAndInitializeIfNeeded(p_mask, heightI, widthI, dIy);
386 
387  for (unsigned int i = 0; i < stop1I; ++i) {
388  for (unsigned int j = 0; j < widthI; ++j) {
389  // We have to compute the value for each pixel if we don't have a mask or for
390  // pixels for which the mask is true otherwise
391  bool computeVal = checkBooleanMask(p_mask, i, j);
392  if (computeVal) {
393  dIy[i][j].R = static_cast<unsigned char>(vpImageFilter::filterYTopBorderR(I, i, j, filter, size));
394  dIy[i][j].G = static_cast<unsigned char>(vpImageFilter::filterYTopBorderG(I, i, j, filter, size));
395  dIy[i][j].B = static_cast<unsigned char>(vpImageFilter::filterYTopBorderB(I, i, j, filter, size));
396  }
397  }
398  }
399  for (unsigned int i = stop1I; i < stop2I; ++i) {
400  for (unsigned int j = 0; j < widthI; ++j) {
401  // We have to compute the value for each pixel if we don't have a mask or for
402  // pixels for which the mask is true otherwise
403  bool computeVal = checkBooleanMask(p_mask, i, j);
404  if (computeVal) {
405  dIy[i][j].R = static_cast<unsigned char>(vpImageFilter::filterYR(I, i, j, filter, size));
406  dIy[i][j].G = static_cast<unsigned char>(vpImageFilter::filterYG(I, i, j, filter, size));
407  dIy[i][j].B = static_cast<unsigned char>(vpImageFilter::filterYB(I, i, j, filter, size));
408  }
409  }
410  }
411  for (unsigned int i = stop2I; i < heightI; ++i) {
412  for (unsigned int j = 0; j < widthI; ++j) {
413  // We have to compute the value for each pixel if we don't have a mask or for
414  // pixels for which the mask is true otherwise
415  bool computeVal = checkBooleanMask(p_mask, i, j);
416  if (computeVal) {
417  dIy[i][j].R = static_cast<unsigned char>(vpImageFilter::filterYBottomBorderR(I, i, j, filter, size));
418  dIy[i][j].G = static_cast<unsigned char>(vpImageFilter::filterYBottomBorderG(I, i, j, filter, size));
419  dIy[i][j].B = static_cast<unsigned char>(vpImageFilter::filterYBottomBorderB(I, i, j, filter, size));
420  }
421  }
422  }
423 }
424 
428 template
429 void vpImageFilter::gaussianBlur<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &GI, unsigned int size, float sigma, bool normalize, const vpImage<bool> *p_mask);
430 
431 template
432 void vpImageFilter::gaussianBlur<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &GI, unsigned int size, double sigma, bool normalize, const vpImage<bool> *p_mask);
433 
434 template
435 void vpImageFilter::gaussianBlur<float, float>(const vpImage<float> &I, vpImage<float> &GI, unsigned int size, float sigma, bool normalize, const vpImage<bool> *p_mask);
436 
437 template
438 void vpImageFilter::gaussianBlur<double, double>(const vpImage<double> &I, vpImage<double> &GI, unsigned int size, double sigma, bool normalize, const vpImage<bool> *p_mask);
455 void vpImageFilter::gaussianBlur(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &GI, unsigned int size, double sigma, bool normalize,
456  const vpImage<bool> *p_mask)
457 {
458  double *fg = new double[(size + 1) / 2];
459  vpImageFilter::getGaussianKernel(fg, size, sigma, normalize);
460  vpImage<vpRGBa> GIx;
461  vpImageFilter::filterX(I, GIx, fg, size, p_mask);
462  vpImageFilter::filterY(GIx, GI, fg, size, p_mask);
463  GIx.destroy();
464  delete[] fg;
465 }
466 
470 template
471 void vpImageFilter::getGaussianKernel<float>(float *filter, unsigned int size, float sigma, bool normalize);
472 
473 template
474 void vpImageFilter::getGaussianDerivativeKernel<float>(float *filter, unsigned int size, float sigma, bool normalize);
475 
476 template
477 void vpImageFilter::getGaussianDerivativeKernel<double>(double *filter, unsigned int size, double sigma, bool normalize);
478 
479 template
480 void vpImageFilter::getGradX<float>(const vpImage<unsigned char> &I, vpImage<float> &dIx, const vpImage<bool> *p_mask);
481 
482 template
483 void vpImageFilter::getGradX<double>(const vpImage<unsigned char> &I, vpImage<double> &dIx, const vpImage<bool> *p_mask);
484 
485 template
486 void vpImageFilter::getGradY<float>(const vpImage<unsigned char> &I, vpImage<float> &dIy, const vpImage<bool> *p_mask);
487 
488 template
489 void vpImageFilter::getGradY<double>(const vpImage<unsigned char> &I, vpImage<double> &dIy, const vpImage<bool> *p_mask);
490 
491 template
492 void vpImageFilter::getGradX<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIx, const float *filter, unsigned int size, const vpImage<bool> *p_mask);
493 
494 template
495 void vpImageFilter::getGradX<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask);
496 
497 template
498 void vpImageFilter::getGradX<float, float>(const vpImage<float> &I, vpImage<float> &dIx, const float *filter, unsigned int size, const vpImage<bool> *p_mask);
499 
500 template
501 void vpImageFilter::getGradX<double, double>(const vpImage<double> &I, vpImage<double> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask);
502 
503 template
504 void vpImageFilter::getGradY<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIy, const float *filter, unsigned int size, const vpImage<bool> *p_mask);
505 
506 template
507 void vpImageFilter::getGradY<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIy, const double *filter, unsigned int size, const vpImage<bool> *p_mask);
508 
509 template
510 void vpImageFilter::getGradY<float, float>(const vpImage<float> &I, vpImage<float> &dIy, const float *filter, unsigned int size, const vpImage<bool> *p_mask);
511 
512 template
513 void vpImageFilter::getGradY<double, double>(const vpImage<double> &I, vpImage<double> &dIy, const double *filter, unsigned int size, const vpImage<bool> *p_mask);
514 
515 template
516 void vpImageFilter::getGradXGauss2D<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIx, const float *gaussianKernel,
517  const float *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask);
518 
519 template
520 void vpImageFilter::getGradXGauss2D<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIx, const double *gaussianKernel,
521  const double *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask);
522 
523 template
524 void vpImageFilter::getGradXGauss2D<float, float>(const vpImage<float> &I, vpImage<float> &dIx, const float *gaussianKernel,
525  const float *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask);
526 
527 template
528 void vpImageFilter::getGradXGauss2D<double, double>(const vpImage<double> &I, vpImage<double> &dIx, const double *gaussianKernel,
529  const double *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask);
530 
531 template
532 void vpImageFilter::getGradYGauss2D<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIy, const float *gaussianKernel,
533  const float *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask);
534 
535 template
536 void vpImageFilter::getGradYGauss2D<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIy, const double *gaussianKernel,
537  const double *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask);
538 
539 template
540 void vpImageFilter::getGradYGauss2D<float, float>(const vpImage<float> &I, vpImage<float> &dIy, const float *gaussianKernel,
541  const float *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask);
542 
543 template
544 void vpImageFilter::getGradYGauss2D<double, double>(const vpImage<double> &I, vpImage<double> &dIy, const double *gaussianKernel,
545  const double *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask);
550  // Operation for Gaussian pyramid
552 {
554 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
555 #if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
556  cv::Mat imgsrc, imgdest;
557  vpImageConvert::convert(I, imgsrc);
558  cv::pyrDown(imgsrc, imgdest, cv::Size((int)I.getWidth() / 2, (int)I.getHeight() / 2));
559  vpImageConvert::convert(imgdest, GI);
560 #else
561  cv::Mat imgsrc, imgdest;
562  vpImageConvert::convert(I, imgsrc);
563  cv::pyrDown(imgsrc, imgdest, cvSize((int)I.getWidth() / 2, (int)I.getHeight() / 2));
564  vpImageConvert::convert(imgdest, GI);
565 #endif
566 #else
569 #endif
570 }
571 
573 {
574  const unsigned int w = I.getWidth() / 2;
575  const unsigned int height = I.getHeight();
576 
577  GI.resize(height, w);
578  for (unsigned int i = 0; i < height; ++i) {
579  GI[i][0] = I[i][0];
580  for (unsigned int j = 1; j < (w - 1); ++j) {
581  GI[i][j] = vpImageFilter::filterGaussXPyramidal(I, i, 2 * j);
582  }
583  GI[i][w - 1] = I[i][(2 * w) - 1];
584  }
585 }
586 
588 {
589  const unsigned int h = I.getHeight() / 2;
590  const unsigned int width = I.getWidth();
591 
592  GI.resize(h, width);
593  for (unsigned int j = 0; j < width; ++j) {
594  GI[0][j] = I[0][j];
595  for (unsigned int i = 1; i < (h - 1); ++i) {
596  GI[i][j] = vpImageFilter::filterGaussYPyramidal(I, 2 * i, j);
597  }
598  GI[h - 1][j] = I[(2 * h) - 1][j];
599  }
600 }
601 
605 template
606 double vpImageFilter::getSobelKernelX<double>(double *filter, unsigned int size);
607 
608 template
609 float vpImageFilter::getSobelKernelX<float>(float *filter, unsigned int size);
610 
611 template
612 double vpImageFilter::getSobelKernelY<double>(double *filter, unsigned int size);
613 
614 template
615 float vpImageFilter::getSobelKernelY<float>(float *filter, unsigned int size);
621 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
627 float vpImageFilter::median(const cv::Mat &channel)
628 {
629  float m = (channel.rows * channel.cols) / 2.f;
630  int bin = 0;
631  float med = -1.0f;
632 
633  int histSize = 256;
634  float range[] = { 0, 256 };
635  const float *histRange = { range };
636  bool uniform = true;
637  bool accumulate = false;
638  cv::Mat hist;
639  cv::calcHist(&channel, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
640 
641  int i = 0;
642  while ((i < histSize) && (med < 0.0f)) {
643  bin += cvRound(hist.at<float>(i));
644  if ((bin > m) && (med < 0.0f)) {
645  med = static_cast<float>(i);
646  }
647  ++i;
648  }
649 
650  return med;
651 }
652 
661 {
662  cv::Mat cv_I;
663  vpImageConvert::convert(Isrc, cv_I);
664  return median(cv_I);
665 }
666 
675 std::vector<float> vpImageFilter::median(const vpImage<vpRGBa> &Isrc)
676 {
677  cv::Mat cv_I_bgr;
678  vpImageConvert::convert(Isrc, cv_I_bgr);
679  std::vector<cv::Mat> channels;
680  cv::split(cv_I_bgr, channels);
681  std::vector<float> meds(3);
682  const int orderMeds[] = { 2, 1, 0 }; // To keep the order R, G, B
683  const int orderCvChannels[] = { 0, 1, 2 }; // Because the order of the cv::Mat is B, G, R
684  for (unsigned int i = 0; i < 3; ++i) {
685  meds[orderMeds[i]] = median(channels[orderCvChannels[i]]);
686  }
687  return meds;
688 }
689 
708 float vpImageFilter::computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy,
709  float &lowerThresh, const unsigned int &gaussianKernelSize,
710  const float &gaussianStdev, const unsigned int &apertureGradient,
711  const float &lowerThresholdRatio, const float &upperThresholdRatio,
713 {
714  double w = cv_I.cols;
715  double h = cv_I.rows;
716  int bins = 256;
717  cv::Mat dI, dIx, dIy, dIx_abs, dIy_abs;
718 
719  if ((p_cv_dIx == nullptr) || (p_cv_dIy == nullptr)) {
720  computePartialDerivatives(cv_I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev, apertureGradient,
721  filteringType);
722  }
723  else {
724  dIx = *p_cv_dIx;
725  dIy = *p_cv_dIy;
726  }
727 
728  // Compute the absolute gradient of the blurred image G = |dIx| + |dIy|
729  cv::convertScaleAbs(dIx, dIx_abs);
730  cv::convertScaleAbs(dIy, dIy_abs);
731  cv::addWeighted(dIx_abs, 1, dIy_abs, 1, 0, dI);
732  dI.convertTo(dI, CV_8U);
733 
734  // Compute the upper threshold from the equalized histogram
735  cv::Mat hist;
736  const float range[] = { 0.f, 256.f }; // The upper boundary is exclusive
737  const float *ranges[] = { range };
738  int channels[] = { 0 };
739  int dims = 1; // The number of dimensions of the histogram
740  int histSize[] = { bins };
741  bool uniform = true;
742  bool accumulate = false; // Clear the histogram at the beginning of calcHist if false, does not clear it otherwise
743  cv::calcHist(&dI, 1, channels, cv::Mat(), hist, dims, histSize, ranges, uniform, accumulate);
744  float accu = 0;
745  float t = static_cast<float>(upperThresholdRatio * w * h);
746  float bon = 0;
747  for (int i = 0; i < bins; ++i) {
748  float tf = hist.at<float>(i);
749  accu = accu + tf;
750  if (accu > t) {
751  bon = static_cast<float>(i);
752  break;
753  }
754  }
755  float upperThresh = std::max<float>(bon, 1.f);
756  lowerThresh = lowerThresholdRatio * bon;
757  return upperThresh;
758 }
759 
777  cv::Mat &cv_dIx, cv::Mat &cv_dIy,
778  const bool &computeDx, const bool &computeDy, const bool &normalize,
779  const unsigned int &gaussianKernelSize, const float &gaussianStdev,
780  const unsigned int &apertureGradient,
782 {
783  if ((filteringType == vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING)
784  || (filteringType == vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING)) {
785  cv::Mat img_blur;
786  // Apply Gaussian blur to the image
787  cv::Size gsz(gaussianKernelSize, gaussianKernelSize);
788  cv::GaussianBlur(cv_I, img_blur, gsz, gaussianStdev);
789 
790  // Compute the gradient of the blurred image
791  if (filteringType == vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING) {
792  double scale = 1.;
793  if (normalize) {
794  scale = 1. / 8.;
795  if (apertureGradient > 3) {
796  scale *= std::pow(1. / 2., (static_cast<double>(apertureGradient) * 2. - 3.)); // 1 / 2^(2 x ksize - dx - dy -2) with ksize =apertureGradient and dx xor dy = 1
797  }
798  }
799  if (computeDx) {
800  cv::Sobel(img_blur, cv_dIx, CV_16S, 1, 0, apertureGradient, scale, 0., cv::BORDER_REPLICATE);
801  }
802  if (computeDy) {
803  cv::Sobel(img_blur, cv_dIy, CV_16S, 0, 1, apertureGradient, scale, 0., cv::BORDER_REPLICATE);
804  }
805  }
806  else if (filteringType == vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING) {
807  double scale = 1.;
808  if (normalize) {
809  scale = 1. / 32.;
810  }
811  if (computeDx) {
812  cv::Scharr(img_blur, cv_dIx, CV_16S, 1, 0, scale);
813  }
814  if (computeDy) {
815  cv::Scharr(img_blur, cv_dIy, CV_16S, 0, 1, scale);
816  }
817  }
818  }
819 }
820 #endif
821 
825 template
826 void vpImageFilter::computePartialDerivatives<unsigned char, float>(const vpImage<unsigned char> &I,
827  vpImage<float> &dIx, vpImage<float> &dIy,
828  const bool &computeDx, const bool &computeDy, const bool &normalize,
829  const unsigned int &gaussianKernelSize, const float &gaussianStdev,
830  const unsigned int &apertureGradient,
831  const vpCannyFilteringAndGradientType &filteringType,
832  const vpCannyBackendType &backend, const vpImage<bool> *p_mask);
833 
834 template
835 void vpImageFilter::computePartialDerivatives<unsigned char, double>(const vpImage<unsigned char> &I,
836  vpImage<double> &dIx, vpImage<double> &dIy,
837  const bool &computeDx, const bool &computeDy, const bool &normalize,
838  const unsigned int &gaussianKernelSize, const double &gaussianStdev,
839  const unsigned int &apertureGradient,
840  const vpCannyFilteringAndGradientType &filteringType,
841  const vpCannyBackendType &backend, const vpImage<bool> *p_mask);
842 
843 template
844 void vpImageFilter::computePartialDerivatives<float, float>(const vpImage<float> &I,
845  vpImage<float> &dIx, vpImage<float> &dIy,
846  const bool &computeDx, const bool &computeDy, const bool &normalize,
847  const unsigned int &gaussianKernelSize, const float &gaussianStdev,
848  const unsigned int &apertureGradient,
849  const vpCannyFilteringAndGradientType &filteringType,
850  const vpCannyBackendType &backend, const vpImage<bool> *p_mask);
851 
852 template
853 void vpImageFilter::computePartialDerivatives<float, double>(const vpImage<float> &I,
854  vpImage<double> &dIx, vpImage<double> &dIy,
855  const bool &computeDx, const bool &computeDy, const bool &normalize,
856  const unsigned int &gaussianKernelSize, const double &gaussianStdev,
857  const unsigned int &apertureGradient,
858  const vpCannyFilteringAndGradientType &filteringType,
859  const vpCannyBackendType &backend, const vpImage<bool> *p_mask);
860 
861 template
862 void vpImageFilter::computePartialDerivatives<double, float>(const vpImage<double> &I,
863  vpImage<float> &dIx, vpImage<float> &dIy,
864  const bool &computeDx, const bool &computeDy, const bool &normalize,
865  const unsigned int &gaussianKernelSize, const float &gaussianStdev,
866  const unsigned int &apertureGradient,
867  const vpCannyFilteringAndGradientType &filteringType,
868  const vpCannyBackendType &backend, const vpImage<bool> *p_mask);
869 
870 template
871 void vpImageFilter::computePartialDerivatives<double, double>(const vpImage<double> &I,
872  vpImage<double> &dIx, vpImage<double> &dIy,
873  const bool &computeDx, const bool &computeDy, const bool &normalize,
874  const unsigned int &gaussianKernelSize, const double &gaussianStdev,
875  const unsigned int &apertureGradient,
876  const vpCannyFilteringAndGradientType &filteringType,
877  const vpCannyBackendType &backend, const vpImage<bool> *p_mask);
878 
879 template
880 float vpImageFilter::computeCannyThreshold<double>(const vpImage<unsigned char> &I, float &lowerThresh,
881  const vpImage<double> *p_dIx, const vpImage<double> *p_dIy,
882  const unsigned int &gaussianKernelSize,
883  const double &gaussianStdev, const unsigned int &apertureGradient,
884  const float &lowerThresholdRatio, const float &upperThresholdRatio,
886  const vpImage<bool> *p_mask);
887 
888 template
889 float vpImageFilter::computeCannyThreshold<float>(const vpImage<unsigned char> &I, float &lowerThresh,
890  const vpImage<float> *p_dIx, const vpImage<float> *p_dIy,
891  const unsigned int &gaussianKernelSize,
892  const float &gaussianStdev, const unsigned int &apertureGradient,
893  const float &lowerThresholdRatio, const float &upperThresholdRatio,
895  const vpImage<bool> *p_mask);
940  const unsigned int &gaussianFilterSize, const float &thresholdCanny,
941  const unsigned int &apertureSobel)
942 {
943  vpImageFilter::canny(Isrc, Ires, gaussianFilterSize, thresholdCanny / 3.f, thresholdCanny, apertureSobel);
944 }
945 
990  const unsigned int &gaussianFilterSize,
991  const float &lowerThreshold, const float &upperThreshold,
992  const unsigned int &apertureSobel)
993 {
994  const float gaussianStdev = 2.f;
995  const float upperThresholdRatio = 0.8f;
996  const float lowerThresholdRatio = 0.6f;
997 #if defined(HAVE_OPENCV_IMGPROC)
998  const vpCannyBackendType cannyBackend = CANNY_OPENCV_BACKEND;
999 #else
1000  const vpCannyBackendType cannyBackend = CANNY_VISP_BACKEND;
1001 #endif
1003  canny(Isrc, Ires, gaussianFilterSize, lowerThreshold, upperThreshold, apertureSobel,
1004  gaussianStdev, lowerThresholdRatio, upperThresholdRatio, false, cannyBackend, cannyFilteringSteps);
1005 }
1006 
1072  const unsigned int &gaussianFilterSize,
1073  const float &lowerThreshold, const float &upperThreshold, const unsigned int &apertureGradient,
1074  const float &gaussianStdev, const float &lowerThresholdRatio, const float &upperThresholdRatio,
1075  const bool &normalizeGradients,
1076  const vpCannyBackendType &cannyBackend, const vpCannyFilteringAndGradientType &cannyFilteringSteps,
1077  const vpImage<bool> *p_mask)
1078 {
1079  if (cannyBackend == CANNY_OPENCV_BACKEND) {
1080 #if defined(HAVE_OPENCV_IMGPROC)
1081  cv::Mat img_cvmat, cv_dx, cv_dy, edges_cvmat;
1082  vpImageConvert::convert(Isrc, img_cvmat);
1083  computePartialDerivatives(img_cvmat, cv_dx, cv_dy, true, true, normalizeGradients, gaussianFilterSize,
1084  gaussianStdev, apertureGradient, cannyFilteringSteps);
1085  float upperCannyThresh = upperThreshold;
1086  float lowerCannyThresh = lowerThreshold;
1087  if (upperCannyThresh < 0.f) {
1088  upperCannyThresh = computeCannyThreshold(img_cvmat, &cv_dx, &cv_dy, lowerCannyThresh, gaussianFilterSize,
1089  gaussianStdev, apertureGradient, lowerThresholdRatio, upperThresholdRatio,
1090  cannyFilteringSteps);
1091  }
1092  else if (lowerCannyThresh < 0.f) {
1093  lowerCannyThresh = upperCannyThresh / 3.f;
1094  }
1095 #if (VISP_HAVE_OPENCV_VERSION >= 0x030200)
1096  cv::Canny(cv_dx, cv_dy, edges_cvmat, lowerCannyThresh, upperCannyThresh, false);
1097 #else
1098  cv::GaussianBlur(img_cvmat, img_cvmat, cv::Size((int)gaussianFilterSize, (int)gaussianFilterSize),
1099  gaussianStdev, gaussianStdev);
1100  cv::Canny(img_cvmat, edges_cvmat, lowerCannyThresh, upperCannyThresh);
1101 #endif
1102  vpImageConvert::convert(edges_cvmat, Ires);
1103 #else
1104  std::string errMsg("[vpImageFilter::canny]You asked for CANNY_OPENCV_BACKEND but ViSP has not been compiled with OpenCV");
1105  throw(vpException(vpException::badValue, errMsg));
1106 #endif
1107  }
1108  else if (cannyBackend == CANNY_VISP_BACKEND) {
1109  float upperCannyThresh = upperThreshold;
1110  float lowerCannyThresh = lowerThreshold;
1111 
1112  vpImage<float> dIx, dIy;
1113  computePartialDerivatives(Isrc, dIx, dIy, true, true, normalizeGradients, gaussianFilterSize,
1114  gaussianStdev, apertureGradient, cannyFilteringSteps, cannyBackend, p_mask);
1115 
1116  if (upperCannyThresh < 0.f) {
1117  upperCannyThresh = computeCannyThreshold(Isrc, lowerCannyThresh, &dIx, &dIy, gaussianFilterSize, gaussianStdev,
1118  apertureGradient, lowerThresholdRatio, upperThresholdRatio,
1119  cannyFilteringSteps, p_mask);
1120  }
1121  else if (lowerCannyThresh < 0.f) {
1122  lowerCannyThresh = upperCannyThresh / 3.f;
1123  }
1124  vpCannyEdgeDetection edgeDetector(gaussianFilterSize, gaussianStdev, apertureGradient, lowerCannyThresh, upperCannyThresh,
1125  lowerThresholdRatio, upperThresholdRatio, cannyFilteringSteps);
1126  edgeDetector.setGradients(dIx, dIy);
1127  edgeDetector.setMask(p_mask);
1128  Ires = edgeDetector.detect(Isrc);
1129  }
1130 }
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:339
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.
Implementation of column vector and the associated operations.
Definition: vpColVector.h:163
error that can be emitted by ViSP classes.
Definition: vpException.h:59
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:85
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 unsigned char filterGaussXPyramidal(const vpImage< unsigned char > &I, unsigned int i, unsigned int j)
static void filterXB(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
static void sepFilter(const vpImage< unsigned char > &I, vpImage< double > &If, const vpColVector &kernelH, const vpColVector &kernelV)
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.
static vpCannyFilteringAndGradientType vpCannyFilteringAndGradientTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyFilteringAndGradientType.
static void gaussianBlur(const vpImage< ImageType > &I, vpImage< FilterType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true, const vpImage< bool > *p_mask=nullptr)
static void filter(const vpImage< ImageType > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false, const vpImage< bool > *p_mask=nullptr)
static std::string vpCannyFilteringAndGradientTypeToString(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
vpCannyBackendType
Canny filter backends for the edge detection operations.
@ CANNY_VISP_BACKEND
Use ViSP.
@ CANNY_OPENCV_BACKEND
Use OpenCV.
static std::string vpCannyBackendTypeList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyBackendType.
static void filterXR(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
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 getGaussXPyramidal(const vpImage< unsigned char > &I, vpImage< unsigned char > &GI)
static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static void getGaussYPyramidal(const vpImage< unsigned char > &I, vpImage< unsigned char > &GI)
static float median(const cv::Mat &cv_I)
Calculates the median value of a single channel. The algorithm is based on based on https://github....
static std::string vpCannyFilteringAndGradientTypeList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyFilteringAndGradientType.
static void filterY(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyBackendTypeToString.
static void filterXG(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
static void filterX(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static unsigned char filterGaussYPyramidal(const vpImage< unsigned char > &I, unsigned int i, unsigned int j)
static void getGaussPyramidal(const vpImage< unsigned char > &I, vpImage< unsigned char > &GI)
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.
void destroy()
Destructor : Memory de-allocation.
Definition: vpImage.h:812
unsigned int getWidth() const
Definition: vpImage.h:245
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:783
unsigned int getHeight() const
Definition: vpImage.h:184
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:2111