Visual Servoing Platform  version 3.6.1 under development (2023-11-30)
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 < vpCannyBackendType::CANNY_COUNT_BACKEND - 1; i++) {
53  list += vpCannyBackendTypeToString(type);
54  list += sep;
55  }
56  vpCannyBackendType type = (vpCannyBackendType)(vpCannyBackendType::CANNY_COUNT_BACKEND - 1);
57  list += vpCannyBackendTypeToString(type);
58  list += suf;
59  return list;
60 }
61 
69 {
70  std::string name;
71  switch (type) {
72  case vpCannyBackendType::CANNY_OPENCV_BACKEND:
73  name = "opencv-backend";
74  break;
75  case vpCannyBackendType::CANNY_VISP_BACKEND:
76  name = "visp-backend";
77  break;
78  case vpCannyBackendType::CANNY_COUNT_BACKEND:
79  default:
80  return "unknown-backend";
81  }
82  return name;
83 }
84 
92 {
93  vpCannyBackendType type(vpCannyBackendType::CANNY_COUNT_BACKEND);
94  std::string nameLowerCase = vpIoTools::toLowerCase(name);
95  unsigned int count = (unsigned int)vpCannyBackendType::CANNY_COUNT_BACKEND;
96  bool found = false;
97  for (unsigned int i = 0; i < count && !found; i++) {
99  if (nameLowerCase == vpCannyBackendTypeToString(temp)) {
100  type = temp;
101  found = true;
102  }
103  }
104  return type;
105 }
106 
115 std::string vpImageFilter::vpCannyFilteringAndGradientTypeList(const std::string &pref, const std::string &sep,
116  const std::string &suf)
117 {
118  std::string list(pref);
119  for (unsigned int i = 0; i < vpCannyFilteringAndGradientType::CANNY_COUNT_FILTERING - 1; i++) {
122  list += sep;
123  }
126  list += suf;
127  return list;
128 }
129 
137 {
138  std::string name;
139  switch (type) {
140  case vpCannyFilteringAndGradientType::CANNY_GBLUR_SOBEL_FILTERING:
141  name = "gaussianblur+sobel-filtering";
142  break;
143  case vpCannyFilteringAndGradientType::CANNY_GBLUR_SCHARR_FILTERING:
144  name = "gaussianblur+scharr-filtering";
145  break;
146  case vpCannyFilteringAndGradientType::CANNY_COUNT_FILTERING:
147  default:
148  return "unknown-filtering";
149  }
150  return name;
151 }
152 
160 {
161  vpCannyFilteringAndGradientType type(vpCannyFilteringAndGradientType::CANNY_COUNT_FILTERING);
162  std::string nameLowerCase = vpIoTools::toLowerCase(name);
163  unsigned int count = (unsigned int)vpCannyFilteringAndGradientType::CANNY_COUNT_FILTERING;
164  bool found = false;
165  for (unsigned int i = 0; i < count && !found; i++) {
167  if (nameLowerCase == vpCannyFilteringAndGradientTypeToString(temp)) {
168  type = temp;
169  found = true;
170  }
171  }
172  return type;
173 }
174 
178 template
179 void vpImageFilter::filter<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &If, const vpArray2D<float> &M, bool convolve);
180 
181 template
182 void vpImageFilter::filter<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &If, const vpArray2D<double> &M, bool convolve);
183 
184 template
185 void vpImageFilter::filter<float, float>(const vpImage<float> &I, vpImage<float> &Iu, vpImage<float> &Iv, const vpArray2D<float> &M,
186  bool convolve);
187 
188 template
189 void vpImageFilter::filter<double, double>(const vpImage<double> &I, vpImage<double> &Iu, vpImage<double> &Iv, const vpArray2D<double> &M,
190  bool convolve);
191 
192 template
193 void vpImageFilter::filter<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &GI, const float *filter,
194  unsigned int size);
195 
196 template
197 void vpImageFilter::filter<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &GI, const double *filter,
198  unsigned int size);
199 
200 template
201 void vpImageFilter::filter<float, float>(const vpImage<float> &I, vpImage<float> &GI, const float *filter, unsigned int size);
202 
203 template
204 void vpImageFilter::filter<double, double>(const vpImage<double> &I, vpImage<double> &GI, const double *filter, unsigned int size);
261  const vpColVector &kernelV)
262 {
263  unsigned int size = kernelH.size();
264  unsigned int half_size = size / 2;
265 
266  If.resize(I.getHeight(), I.getWidth(), 0.0);
267  vpImage<double> I_filter(I.getHeight(), I.getWidth(), 0.0);
268 
269  for (unsigned int i = 0; i < I.getHeight(); i++) {
270  for (unsigned int j = half_size; j < I.getWidth() - half_size; j++) {
271  double conv = 0.0;
272  for (unsigned int a = 0; a < kernelH.size(); a++) {
273  conv += kernelH[a] * I[i][j + half_size - a];
274  }
275 
276  I_filter[i][j] = conv;
277  }
278  }
279 
280  for (unsigned int i = half_size; i < I.getHeight() - half_size; i++) {
281  for (unsigned int j = 0; j < I.getWidth(); j++) {
282  double conv = 0.0;
283  for (unsigned int a = 0; a < kernelV.size(); a++) {
284  conv += kernelV[a] * I_filter[i + half_size - a][j];
285  }
286 
287  If[i][j] = conv;
288  }
289  }
290 }
291 
295 template
296 void vpImageFilter::filterX<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIx, const float *filter,
297  unsigned int size);
298 
299 template
300 void vpImageFilter::filterX<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIx, const double *filter,
301  unsigned int size);
302 
303 template
304 void vpImageFilter::filterX<float, float>(const vpImage<float> &I, vpImage<float> &dIx, const float *filter, unsigned int size);
305 
306 template
307 void vpImageFilter::filterX<double, double>(const vpImage<double> &I, vpImage<double> &dIx, const double *filter, unsigned int size);
312 void vpImageFilter::filterX(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size)
313 {
314  dIx.resize(I.getHeight(), I.getWidth());
315  for (unsigned int i = 0; i < I.getHeight(); i++) {
316  for (unsigned int j = 0; j < (size - 1) / 2; j++) {
317  dIx[i][j].R = static_cast<unsigned char>(vpImageFilter::filterXLeftBorderR(I, i, j, filter, size));
318  dIx[i][j].G = static_cast<unsigned char>(vpImageFilter::filterXLeftBorderG(I, i, j, filter, size));
319  dIx[i][j].B = static_cast<unsigned char>(vpImageFilter::filterXLeftBorderB(I, i, j, filter, size));
320  }
321  for (unsigned int j = (size - 1) / 2; j < I.getWidth() - (size - 1) / 2; j++) {
322  dIx[i][j].R = static_cast<unsigned char>(vpImageFilter::filterXR(I, i, j, filter, size));
323  dIx[i][j].G = static_cast<unsigned char>(vpImageFilter::filterXG(I, i, j, filter, size));
324  dIx[i][j].B = static_cast<unsigned char>(vpImageFilter::filterXB(I, i, j, filter, size));
325  }
326  for (unsigned int j = I.getWidth() - (size - 1) / 2; j < I.getWidth(); j++) {
327  dIx[i][j].R = static_cast<unsigned char>(vpImageFilter::filterXRightBorderR(I, i, j, filter, size));
328  dIx[i][j].G = static_cast<unsigned char>(vpImageFilter::filterXRightBorderG(I, i, j, filter, size));
329  dIx[i][j].B = static_cast<unsigned char>(vpImageFilter::filterXRightBorderB(I, i, j, filter, size));
330  }
331  }
332 }
333 
337 template
338 void vpImageFilter::filterY<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIy, const float *filter,
339  unsigned int size);
340 
341 template
342 void vpImageFilter::filterY<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIy, const double *filter,
343  unsigned int size);
344 
345 template
346 void vpImageFilter::filterY<float, float>(const vpImage<float> &I, vpImage<float> &dIy, const float *filter, unsigned int size);
347 
348 template
349 void vpImageFilter::filterY<double, double>(const vpImage<double> &I, vpImage<double> &dIy, const double *filter, unsigned int size);
354 void vpImageFilter::filterY(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIy, const double *filter, unsigned int size)
355 {
356  dIy.resize(I.getHeight(), I.getWidth());
357  for (unsigned int i = 0; i < (size - 1) / 2; i++) {
358  for (unsigned int j = 0; j < I.getWidth(); j++) {
359  dIy[i][j].R = static_cast<unsigned char>(vpImageFilter::filterYTopBorderR(I, i, j, filter, size));
360  dIy[i][j].G = static_cast<unsigned char>(vpImageFilter::filterYTopBorderG(I, i, j, filter, size));
361  dIy[i][j].B = static_cast<unsigned char>(vpImageFilter::filterYTopBorderB(I, i, j, filter, size));
362  }
363  }
364  for (unsigned int i = (size - 1) / 2; i < I.getHeight() - (size - 1) / 2; i++) {
365  for (unsigned int j = 0; j < I.getWidth(); j++) {
366  dIy[i][j].R = static_cast<unsigned char>(vpImageFilter::filterYR(I, i, j, filter, size));
367  dIy[i][j].G = static_cast<unsigned char>(vpImageFilter::filterYG(I, i, j, filter, size));
368  dIy[i][j].B = static_cast<unsigned char>(vpImageFilter::filterYB(I, i, j, filter, size));
369  }
370  }
371  for (unsigned int i = I.getHeight() - (size - 1) / 2; i < I.getHeight(); i++) {
372  for (unsigned int j = 0; j < I.getWidth(); j++) {
373  dIy[i][j].R = static_cast<unsigned char>(vpImageFilter::filterYBottomBorderR(I, i, j, filter, size));
374  dIy[i][j].G = static_cast<unsigned char>(vpImageFilter::filterYBottomBorderG(I, i, j, filter, size));
375  dIy[i][j].B = static_cast<unsigned char>(vpImageFilter::filterYBottomBorderB(I, i, j, filter, size));
376  }
377  }
378 }
379 
383 template
384 void vpImageFilter::gaussianBlur<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &GI, unsigned int size, float sigma, bool normalize);
385 
386 template
387 void vpImageFilter::gaussianBlur<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &GI, unsigned int size, double sigma, bool normalize);
388 
389 template
390 void vpImageFilter::gaussianBlur<float, float>(const vpImage<float> &I, vpImage<float> &GI, unsigned int size, float sigma, bool normalize);
391 
392 template
393 void vpImageFilter::gaussianBlur<double, double>(const vpImage<double> &I, vpImage<double> &GI, unsigned int size, double sigma, bool normalize);
409 void vpImageFilter::gaussianBlur(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &GI, unsigned int size, double sigma, bool normalize)
410 {
411  double *fg = new double[(size + 1) / 2];
412  vpImageFilter::getGaussianKernel(fg, size, sigma, normalize);
413  vpImage<vpRGBa> GIx;
414  vpImageFilter::filterX(I, GIx, fg, size);
415  vpImageFilter::filterY(GIx, GI, fg, size);
416  GIx.destroy();
417  delete[] fg;
418 }
419 
423 template
424 void vpImageFilter::getGaussianKernel<float>(float *filter, unsigned int size, float sigma, bool normalize);
425 
426 template
427 void vpImageFilter::getGaussianDerivativeKernel<float>(float *filter, unsigned int size, float sigma, bool normalize);
428 
429 template
430 void vpImageFilter::getGaussianDerivativeKernel<double>(double *filter, unsigned int size, double sigma, bool normalize);
431 
432 template
433 void vpImageFilter::getGradX<float>(const vpImage<unsigned char> &I, vpImage<float> &dIx);
434 
435 template
436 void vpImageFilter::getGradX<double>(const vpImage<unsigned char> &I, vpImage<double> &dIx);
437 
438 template
439 void vpImageFilter::getGradY<float>(const vpImage<unsigned char> &I, vpImage<float> &dIy);
440 
441 template
442 void vpImageFilter::getGradY<double>(const vpImage<unsigned char> &I, vpImage<double> &dIy);
443 
444 template
445 void vpImageFilter::getGradX<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIx, const float *filter, unsigned int size);
446 
447 template
448 void vpImageFilter::getGradX<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIx, const double *filter, unsigned int size);
449 
450 template
451 void vpImageFilter::getGradX<float, float>(const vpImage<float> &I, vpImage<float> &dIx, const float *filter, unsigned int size);
452 
453 template
454 void vpImageFilter::getGradX<double, double>(const vpImage<double> &I, vpImage<double> &dIx, const double *filter, unsigned int size);
455 
456 template
457 void vpImageFilter::getGradY<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIy, const float *filter, unsigned int size);
458 
459 template
460 void vpImageFilter::getGradY<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIy, const double *filter, unsigned int size);
461 
462 template
463 void vpImageFilter::getGradY<float, float>(const vpImage<float> &I, vpImage<float> &dIy, const float *filter, unsigned int size);
464 
465 template
466 void vpImageFilter::getGradY<double, double>(const vpImage<double> &I, vpImage<double> &dIy, const double *filter, unsigned int size);
467 
468 template
469 void vpImageFilter::getGradXGauss2D<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIx, const float *gaussianKernel,
470  const float *gaussianDerivativeKernel, unsigned int size);
471 
472 template
473 void vpImageFilter::getGradXGauss2D<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIx, const double *gaussianKernel,
474  const double *gaussianDerivativeKernel, unsigned int size);
475 
476 template
477 void vpImageFilter::getGradXGauss2D<float, float>(const vpImage<float> &I, vpImage<float> &dIx, const float *gaussianKernel,
478  const float *gaussianDerivativeKernel, unsigned int size);
479 
480 template
481 void vpImageFilter::getGradXGauss2D<double, double>(const vpImage<double> &I, vpImage<double> &dIx, const double *gaussianKernel,
482  const double *gaussianDerivativeKernel, unsigned int size);
483 
484 template
485 void vpImageFilter::getGradYGauss2D<unsigned char, float>(const vpImage<unsigned char> &I, vpImage<float> &dIy, const float *gaussianKernel,
486  const float *gaussianDerivativeKernel, unsigned int size);
487 
488 template
489 void vpImageFilter::getGradYGauss2D<unsigned char, double>(const vpImage<unsigned char> &I, vpImage<double> &dIy, const double *gaussianKernel,
490  const double *gaussianDerivativeKernel, unsigned int size);
491 
492 template
493 void vpImageFilter::getGradYGauss2D<float, float>(const vpImage<float> &I, vpImage<float> &dIy, const float *gaussianKernel,
494  const float *gaussianDerivativeKernel, unsigned int size);
495 
496 template
497 void vpImageFilter::getGradYGauss2D<double, double>(const vpImage<double> &I, vpImage<double> &dIy, const double *gaussianKernel,
498  const double *gaussianDerivativeKernel, unsigned int size);
503 // Operation for Gaussian pyramid
505 {
507 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
508 #if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
509  cv::Mat imgsrc, imgdest;
510  vpImageConvert::convert(I, imgsrc);
511  cv::pyrDown(imgsrc, imgdest, cv::Size((int)I.getWidth() / 2, (int)I.getHeight() / 2));
512  vpImageConvert::convert(imgdest, GI);
513 #else
514  cv::Mat imgsrc, imgdest;
515  vpImageConvert::convert(I, imgsrc);
516  cv::pyrDown(imgsrc, imgdest, cvSize((int)I.getWidth() / 2, (int)I.getHeight() / 2));
517  vpImageConvert::convert(imgdest, GI);
518 #endif
519 #else
522 #endif
523 }
524 
526 {
527  unsigned int w = I.getWidth() / 2;
528 
529  GI.resize(I.getHeight(), w);
530  for (unsigned int i = 0; i < I.getHeight(); i++) {
531  GI[i][0] = I[i][0];
532  for (unsigned int j = 1; j < w - 1; j++) {
533  GI[i][j] = vpImageFilter::filterGaussXPyramidal(I, i, 2 * j);
534  }
535  GI[i][w - 1] = I[i][2 * w - 1];
536  }
537 }
538 
540 {
541  unsigned int h = I.getHeight() / 2;
542 
543  GI.resize(h, I.getWidth());
544  for (unsigned int j = 0; j < I.getWidth(); j++) {
545  GI[0][j] = I[0][j];
546  for (unsigned int i = 1; i < h - 1; i++) {
547  GI[i][j] = vpImageFilter::filterGaussYPyramidal(I, 2 * i, j);
548  }
549  GI[h - 1][j] = I[2 * h - 1][j];
550  }
551 }
552 
556 template
557 double vpImageFilter::getSobelKernelX<double>(double *filter, unsigned int size);
558 
559 template
560 float vpImageFilter::getSobelKernelX<float>(float *filter, unsigned int size);
561 
562 template
563 double vpImageFilter::getSobelKernelY<double>(double *filter, unsigned int size);
564 
565 template
566 float vpImageFilter::getSobelKernelY<float>(float *filter, unsigned int size);
572 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
578 float vpImageFilter::median(const cv::Mat &channel)
579 {
580  float m = (channel.rows * channel.cols) / 2.f;
581  int bin = 0;
582  float med = -1.0f;
583 
584  int histSize = 256;
585  float range[] = { 0, 256 };
586  const float *histRange = { range };
587  bool uniform = true;
588  bool accumulate = false;
589  cv::Mat hist;
590  cv::calcHist(&channel, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
591 
592  for (int i = 0; i < histSize && med < 0.0; ++i) {
593  bin += cvRound(hist.at<float>(i));
594  if (bin > m && med < 0.0)
595  med = static_cast<float>(i);
596  }
597 
598  return med;
599 }
600 
609 {
610  cv::Mat cv_I;
611  vpImageConvert::convert(Isrc, cv_I);
612  return median(cv_I);
613 }
614 
623 std::vector<float> vpImageFilter::median(const vpImage<vpRGBa> &Isrc)
624 {
625  cv::Mat cv_I_bgr;
626  vpImageConvert::convert(Isrc, cv_I_bgr);
627  std::vector<cv::Mat> channels;
628  cv::split(cv_I_bgr, channels);
629  std::vector<float> meds(3);
630  const int orderMeds[] = { 2, 1, 0 }; // To keep the order R, G, B
631  const int orderCvChannels[] = { 0, 1, 2 }; // Because the order of the cv::Mat is B, G, R
632  for (unsigned int i = 0; i < 3; i++) {
633  meds[orderMeds[i]] = median(channels[orderCvChannels[i]]);
634  }
635  return meds;
636 }
637 
656 float vpImageFilter::computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy,
657  float &lowerThresh, const unsigned int &gaussianKernelSize,
658  const float &gaussianStdev, const unsigned int &apertureGradient,
659  const float &lowerThresholdRatio, const float &upperThresholdRatio,
661 {
662  double w = cv_I.cols;
663  double h = cv_I.rows;
664  int bins = 256;
665  cv::Mat dI, dIx, dIy, dIx_abs, dIy_abs;
666 
667  if (p_cv_dIx == nullptr || p_cv_dIy == nullptr) {
668  computePartialDerivatives(cv_I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev, apertureGradient,
669  filteringType);
670  }
671  else {
672  dIx = *p_cv_dIx;
673  dIy = *p_cv_dIy;
674  }
675 
676  // Compute the absolute gradient of the blurred image G = |dIx| + |dIy|
677  cv::convertScaleAbs(dIx, dIx_abs);
678  cv::convertScaleAbs(dIy, dIy_abs);
679  cv::addWeighted(dIx_abs, 1, dIy_abs, 1, 0, dI);
680  dI.convertTo(dI, CV_8U);
681 
682  // Compute the upper threshold from the equalized histogram
683  cv::Mat hist;
684  const float range[] = { 0.f, 256.f }; // The upper boundary is exclusive
685  const float *ranges[] = { range };
686  int channels[] = { 0 };
687  bool dims = 1; // The number of dimensions of the histogram
688  int histSize[] = { bins };
689  bool uniform = true;
690  bool accumulate = false; // Clear the histogram at the beginning of calcHist if false, does not clear it otherwise
691  cv::calcHist(&dI, 1, channels, cv::Mat(), hist, dims, histSize, ranges, uniform, accumulate);
692  float accu = 0;
693  float t = (float)(upperThresholdRatio * w * h);
694  float bon = 0;
695  for (int i = 0; i < bins; i++) {
696  float tf = hist.at<float>(i);
697  accu = accu + tf;
698  if (accu > t) {
699  bon = (float)i;
700  break;
701  }
702  }
703  float upperThresh = std::max(bon, 1.f);
704  lowerThresh = lowerThresholdRatio * bon;
705  return upperThresh;
706 }
707 
725  cv::Mat &cv_dIx, cv::Mat &cv_dIy,
726  const bool &computeDx, const bool &computeDy, const bool &normalize,
727  const unsigned int &gaussianKernelSize, const float &gaussianStdev,
728  const unsigned int &apertureGradient,
730 {
732  || filteringType == vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING) {
733  cv::Mat img_blur;
734  // Apply Gaussian blur to the image
735  cv::Size gsz(gaussianKernelSize, gaussianKernelSize);
736  cv::GaussianBlur(cv_I, img_blur, gsz, gaussianStdev);
737 
738  // Compute the gradient of the blurred image
739  if (filteringType == vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING) {
740  double scale = 1.;
741  if (normalize) {
742  scale = 1. / 8.;
743  if (apertureGradient > 3) {
744  scale *= std::pow(1./16., ((apertureGradient -1.)/2.) - 1.);
745  }
746  }
747  if (computeDx) {
748  cv::Sobel(img_blur, cv_dIx, CV_16S, 1, 0, apertureGradient, 1, 0, scale);
749  }
750  if (computeDy) {
751  cv::Sobel(img_blur, cv_dIy, CV_16S, 0, 1, apertureGradient, 1, 0, scale);
752  }
753  }
754  else if (filteringType == vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING) {
755  double scale = 1.;
756  if (normalize) {
757  scale = 1. / 32.;
758  }
759  if (computeDx) {
760  cv::Scharr(img_blur, cv_dIx, CV_16S, 1, 0, scale);
761  }
762  if (computeDy) {
763  cv::Scharr(img_blur, cv_dIy, CV_16S, 0, 1, scale);
764  }
765  }
766  }
767 }
768 #endif
769 
773 template
774 void vpImageFilter::computePartialDerivatives<unsigned char, float>(const vpImage<unsigned char> &I,
775  vpImage<float> &dIx, vpImage<float> &dIy,
776  const bool &computeDx, const bool &computeDy, const bool &normalize,
777  const unsigned int &gaussianKernelSize, const float &gaussianStdev,
778  const unsigned int &apertureGradient,
779  const vpCannyFilteringAndGradientType &filteringType,
780  const vpCannyBackendType &backend);
781 
782 template
783 void vpImageFilter::computePartialDerivatives<unsigned char, double>(const vpImage<unsigned char> &I,
784  vpImage<double> &dIx, vpImage<double> &dIy,
785  const bool &computeDx, const bool &computeDy, const bool &normalize,
786  const unsigned int &gaussianKernelSize, const double &gaussianStdev,
787  const unsigned int &apertureGradient,
788  const vpCannyFilteringAndGradientType &filteringType,
789  const vpCannyBackendType &backend);
790 
791 template
792 void vpImageFilter::computePartialDerivatives<float, float>(const vpImage<float> &I,
793  vpImage<float> &dIx, vpImage<float> &dIy,
794  const bool &computeDx, const bool &computeDy, const bool &normalize,
795  const unsigned int &gaussianKernelSize, const float &gaussianStdev,
796  const unsigned int &apertureGradient,
797  const vpCannyFilteringAndGradientType &filteringType,
798  const vpCannyBackendType &backend);
799 
800 template
801 void vpImageFilter::computePartialDerivatives<float, double>(const vpImage<float> &I,
802  vpImage<double> &dIx, vpImage<double> &dIy,
803  const bool &computeDx, const bool &computeDy, const bool &normalize,
804  const unsigned int &gaussianKernelSize, const double &gaussianStdev,
805  const unsigned int &apertureGradient,
806  const vpCannyFilteringAndGradientType &filteringType,
807  const vpCannyBackendType &backend);
808 
809 template
810 void vpImageFilter::computePartialDerivatives<double, float>(const vpImage<double> &I,
811  vpImage<float> &dIx, vpImage<float> &dIy,
812  const bool &computeDx, const bool &computeDy, const bool &normalize,
813  const unsigned int &gaussianKernelSize, const float &gaussianStdev,
814  const unsigned int &apertureGradient,
815  const vpCannyFilteringAndGradientType &filteringType,
816  const vpCannyBackendType &backend);
817 
818 template
819 void vpImageFilter::computePartialDerivatives<double, double>(const vpImage<double> &I,
820  vpImage<double> &dIx, vpImage<double> &dIy,
821  const bool &computeDx, const bool &computeDy, const bool &normalize,
822  const unsigned int &gaussianKernelSize, const double &gaussianStdev,
823  const unsigned int &apertureGradient,
824  const vpCannyFilteringAndGradientType &filteringType,
825  const vpCannyBackendType &backend);
826 
827 template
828 float vpImageFilter::computeCannyThreshold<double>(const vpImage<unsigned char> &I, float &lowerThresh,
829  const vpImage<double> *p_dIx, const vpImage<double> *p_dIy,
830  const unsigned int &gaussianKernelSize,
831  const double &gaussianStdev, const unsigned int &apertureGradient,
832  const float &lowerThresholdRatio, const float &upperThresholdRatio,
834 
835 template
836 float vpImageFilter::computeCannyThreshold<float>(const vpImage<unsigned char> &I, float &lowerThresh,
837  const vpImage<float> *p_dIx, const vpImage<float> *p_dIy,
838  const unsigned int &gaussianKernelSize,
839  const float &gaussianStdev, const unsigned int &apertureGradient,
840  const float &lowerThresholdRatio, const float &upperThresholdRatio,
886  const unsigned int &gaussianFilterSize, const float &thresholdCanny,
887  const unsigned int &apertureSobel)
888 {
889  vpImageFilter::canny(Isrc, Ires, gaussianFilterSize, thresholdCanny / 3.f, thresholdCanny, apertureSobel);
890 }
891 
936  const unsigned int &gaussianFilterSize,
937  const float &lowerThreshold, const float &upperThreshold,
938  const unsigned int &apertureSobel)
939 {
940  const float gaussianStdev = 2.f;
941  const float upperThresholdRatio = 0.8f;
942  const float lowerThresholdRatio = 0.6f;
943 #if defined(HAVE_OPENCV_IMGPROC)
944  const vpCannyBackendType cannyBackend = CANNY_OPENCV_BACKEND;
945 #else
946  const vpCannyBackendType cannyBackend = CANNY_VISP_BACKEND;
947 #endif
949  canny(Isrc, Ires, gaussianFilterSize, lowerThreshold, upperThreshold, apertureSobel,
950  gaussianStdev, lowerThresholdRatio, upperThresholdRatio, false, cannyBackend, cannyFilteringSteps);
951 }
952 
1017  const unsigned int &gaussianFilterSize,
1018  const float &lowerThreshold, const float &upperThreshold, const unsigned int &apertureGradient,
1019  const float &gaussianStdev, const float &lowerThresholdRatio, const float &upperThresholdRatio,
1020  const bool &normalizeGradients,
1021  const vpCannyBackendType &cannyBackend, const vpCannyFilteringAndGradientType &cannyFilteringSteps)
1022 {
1023  if (cannyBackend == CANNY_OPENCV_BACKEND) {
1024 #if defined(HAVE_OPENCV_IMGPROC)
1025  cv::Mat img_cvmat, cv_dx, cv_dy, edges_cvmat;
1026  vpImageConvert::convert(Isrc, img_cvmat);
1027  computePartialDerivatives(img_cvmat, cv_dx, cv_dy, true, true, normalizeGradients, gaussianFilterSize,
1028  gaussianStdev, apertureGradient, cannyFilteringSteps);
1029  float upperCannyThresh = upperThreshold;
1030  float lowerCannyThresh = lowerThreshold;
1031  if (upperCannyThresh < 0) {
1032  upperCannyThresh = computeCannyThreshold(img_cvmat, &cv_dx, &cv_dy, lowerCannyThresh, gaussianFilterSize,
1033  gaussianStdev, apertureGradient, lowerThresholdRatio, upperThresholdRatio,
1034  cannyFilteringSteps);
1035  }
1036  else if (lowerCannyThresh < 0) {
1037  lowerCannyThresh = upperCannyThresh / 3.f;
1038  }
1039 #if (VISP_HAVE_OPENCV_VERSION >= 0x030200)
1040  cv::Canny(cv_dx, cv_dy, edges_cvmat, lowerCannyThresh, upperCannyThresh, false);
1041 #else
1042  cv::GaussianBlur(img_cvmat, img_cvmat, cv::Size((int)gaussianFilterSize, (int)gaussianFilterSize),
1043  gaussianStdev, gaussianStdev);
1044  cv::Canny(img_cvmat, edges_cvmat, lowerCannyThresh, upperCannyThresh);
1045 #endif
1046  vpImageConvert::convert(edges_cvmat, Ires);
1047 #else
1048  std::string errMsg("[vpImageFilter::canny]You asked for CANNY_OPENCV_BACKEND but ViSP has not been compiled with OpenCV");
1049  throw(vpException(vpException::badValue, errMsg));
1050 #endif
1051  }
1052  else if (cannyBackend == CANNY_VISP_BACKEND) {
1053  float upperCannyThresh = upperThreshold;
1054  float lowerCannyThresh = lowerThreshold;
1055 
1056  vpImage<float> dIx, dIy;
1057  computePartialDerivatives(Isrc, dIx, dIy, true, true, normalizeGradients, gaussianFilterSize,
1058  gaussianStdev, apertureGradient, cannyFilteringSteps, cannyBackend);
1059 
1060  if (upperCannyThresh < 0) {
1061  upperCannyThresh = computeCannyThreshold(Isrc, lowerCannyThresh, &dIx, &dIy, gaussianFilterSize, gaussianStdev,
1062  apertureGradient, lowerThresholdRatio, upperThresholdRatio,
1063  cannyFilteringSteps);
1064  }
1065  else if (lowerCannyThresh < 0) {
1066  lowerCannyThresh = upperCannyThresh / 3.;
1067  }
1068  vpCannyEdgeDetection edgeDetector(gaussianFilterSize, gaussianStdev, apertureGradient, lowerCannyThresh, upperCannyThresh,
1069  lowerThresholdRatio, upperThresholdRatio, cannyFilteringSteps);
1070  edgeDetector.setGradients(dIx, dIy);
1071  Ires = edgeDetector.detect(Isrc);
1072  }
1073 }
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:269
json namespace shortcut
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 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 double filterYTopBorderG(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static void filterYB(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
static double filterYBottomBorderB(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static double filterXRightBorderB(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static void filter(const vpImage< ImageType > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false)
static double filterYBottomBorderR(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static double filterYTopBorderB(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static void filterXB(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
static double filterXLeftBorderB(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static void filterYR(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.
Definition: vpImageFilter.h:88
@ CANNY_GBLUR_SOBEL_FILTERING
Apply Gaussian blur + Sobel operator on the input image.
Definition: vpImageFilter.h:89
@ CANNY_GBLUR_SCHARR_FILTERING
Apply Gaussian blur + Scharr operator on the input image.
Definition: vpImageFilter.h:90
static vpCannyFilteringAndGradientType vpCannyFilteringAndGradientTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyFilteringAndGradientType.
static void filterY(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
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.
Definition: vpImageFilter.h:73
@ CANNY_VISP_BACKEND
Use ViSP.
Definition: vpImageFilter.h:75
@ CANNY_OPENCV_BACKEND
Use OpenCV.
Definition: vpImageFilter.h:74
static double filterXLeftBorderR(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static std::string vpCannyBackendTypeList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyBackendType.
static double filterXLeftBorderG(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static double filterXRightBorderR(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
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 double filterYTopBorderR(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static void filterYG(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size)
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 double filterXRightBorderG(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static void filterX(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *filter, unsigned int size)
static std::string vpCannyFilteringAndGradientTypeList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpCannyFilteringAndGradientType.
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 double filterYBottomBorderG(const vpImage< vpRGBa > &I, unsigned int r, unsigned int c, const double *filter, unsigned int size)
static unsigned char filterGaussYPyramidal(const vpImage< unsigned char > &I, unsigned int i, unsigned int j)
static void gaussianBlur(const vpImage< ImageType > &I, vpImage< FilterType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true)
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:822
unsigned int getWidth() const
Definition: vpImage.h:240
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:793
unsigned int getHeight() const
Definition: vpImage.h:182
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:1694