Visual Servoing Platform  version 3.6.1 under development (2024-10-15)
vpImageFilter.h
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Various image tools, convolution, ...
32  */
33 
39 #ifndef VP_IMAGE_FILTER_H
40 #define VP_IMAGE_FILTER_H
41 
42 #include <fstream>
43 #include <iostream>
44 #include <math.h>
45 #include <string.h>
46 
47 #include <visp3/core/vpConfig.h>
48 #include <visp3/core/vpException.h>
49 #include <visp3/core/vpHistogram.h>
50 #include <visp3/core/vpImage.h>
51 #include <visp3/core/vpImageConvert.h>
52 #include <visp3/core/vpImageException.h>
53 #include <visp3/core/vpMath.h>
54 #include <visp3/core/vpMatrix.h>
55 #include <visp3/core/vpRGBa.h>
56 
57 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
58 #include <opencv2/imgproc/imgproc.hpp>
59 #include <opencv2/imgproc/imgproc_c.h>
60 #endif
61 
62 BEGIN_VISP_NAMESPACE
70 class VISP_EXPORT vpImageFilter
71 {
72 public:
74  typedef enum vpCannyBackendType
75  {
76  CANNY_OPENCV_BACKEND = 0,
77  CANNY_VISP_BACKEND = 1,
78  CANNY_COUNT_BACKEND = 2
79  } vpCannyBackendType;
80 
81  static std::string vpCannyBackendTypeList(const std::string &pref = "<", const std::string &sep = " , ",
82  const std::string &suf = ">");
83 
84  static std::string vpCannyBackendTypeToString(const vpCannyBackendType &type);
85 
86  static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name);
87 
90  {
91  CANNY_GBLUR_SOBEL_FILTERING = 0,
92  CANNY_GBLUR_SCHARR_FILTERING = 1,
93  CANNY_COUNT_FILTERING = 2
94  } vpCannyFilteringAndGradientType;
95 
96  static std::string vpGetCannyFiltAndGradTypes(const std::string &pref = "<", const std::string &sep = " , ",
97  const std::string &suf = ">");
98 
99  static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type);
100 
101  static vpCannyFilteringAndGradientType vpCannyFiltAndGradTypeFromStr(const std::string &name);
102 
103  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
104  const float &thresholdCanny, const unsigned int &apertureSobel);
105 
106  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
107  const float &lowerThresholdCanny, const float &higherThresholdCanny,
108  const unsigned int &apertureSobel);
109 
110  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
111  const float &lowerThresholdCanny, const float &higherThresholdCanny,
112  const unsigned int &apertureSobel, const float &gaussianStdev, const float &lowerThresholdRatio,
113  const float &upperThresholdRatio, const bool &normalizeGradients,
114  const vpCannyBackendType &cannyBackend, const vpCannyFilteringAndGradientType &cannyFilteringSteps,
115  const vpImage<bool> *p_mask = nullptr);
116 
117 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
118  static float computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy,
119  float &lowerThresh, const unsigned int &gaussianKernelSize = 5,
120  const float &gaussianStdev = 2.f, const unsigned int &apertureGradient = 3,
121  const float &lowerThresholdRatio = 0.6, const float &upperThresholdRatio = 0.8,
122  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
123 
124  static void computePartialDerivatives(const cv::Mat &cv_I,
125  cv::Mat &cv_dIx, cv::Mat &cv_dIy,
126  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
127  const unsigned int &gaussianKernelSize = 5, const float &gaussianStdev = 2.f,
128  const unsigned int &apertureGradient = 3,
129  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
130 #endif
131 
152  template <typename ImageType, typename FilterType>
153  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
155  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
156  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
157  const unsigned int &apertureGradient = 3,
158  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
159  const vpCannyBackendType &backend = CANNY_VISP_BACKEND,
160  const vpImage<bool> *p_mask = nullptr)
161  {
162  if (backend == CANNY_OPENCV_BACKEND) {
163 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
164  cv::Mat cv_I, cv_dIx, cv_dIy;
165  vpImageConvert::convert(I, cv_I);
166  computePartialDerivatives(cv_I, cv_dIx, cv_dIy, computeDx, computeDy, normalize, gaussianKernelSize,
167  static_cast<float>(gaussianStdev), apertureGradient, filteringType);
168  if (computeDx) {
169  vpImageConvert::convert(cv_dIx, dIx);
170  }
171  if (computeDy) {
172  vpImageConvert::convert(cv_dIy, dIy);
173  }
174 #else
175  throw(vpException(vpException::badValue, "You need to compile ViSP with OpenCV to use CANNY_OPENCV_BACKEND"));
176 #endif
177  }
178  else {
179  if ((filteringType == CANNY_GBLUR_SCHARR_FILTERING) || (filteringType == CANNY_GBLUR_SOBEL_FILTERING)) {
180  dIx.resize(I.getHeight(), I.getWidth());
181  dIy.resize(I.getHeight(), I.getWidth());
182 
183  // Computing the Gaussian blur + gradients of the image
184  vpImage<FilterType> Iblur;
185  vpImageFilter::gaussianBlur(I, Iblur, gaussianKernelSize, gaussianStdev, true, p_mask);
186 
187  vpArray2D<FilterType> gradientFilterX(apertureGradient, apertureGradient); // Gradient filter along the X-axis
188  vpArray2D<FilterType> gradientFilterY(apertureGradient, apertureGradient); // Gradient filter along the Y-axis
189 
190 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
191  // Helper to apply the scale to the raw values of the filters
192  auto scaleFilter = [](vpArray2D<FilterType> &filter, const float &scale) {
193  const unsigned int nbRows = filter.getRows();
194  const unsigned int nbCols = filter.getCols();
195  for (unsigned int r = 0; r < nbRows; ++r) {
196  for (unsigned int c = 0; c < nbCols; ++c) {
197  filter[r][c] = filter[r][c] * scale;
198  }
199  }
200  };
201 #endif
202 
203  // Scales to apply to the filters to get a normalized gradient filter that gives a gradient
204  // between 0 and 255 for an vpImage<uchar>
205  float scaleX = 1.f;
206  float scaleY = 1.f;
207  const unsigned int val2 = 2U;
208 
209  if (filteringType == CANNY_GBLUR_SOBEL_FILTERING) {
210  if (computeDx) {
211  scaleX = static_cast<float>(vpImageFilter::getSobelKernelX(gradientFilterX.data, (apertureGradient - 1) / val2));
212  }
213  if (computeDy) {
214  scaleY = static_cast<float>(vpImageFilter::getSobelKernelY(gradientFilterY.data, (apertureGradient - 1) / val2));
215  }
216  }
217  else if (filteringType == CANNY_GBLUR_SCHARR_FILTERING) {
218  if (computeDx) {
219  scaleX = static_cast<float>(vpImageFilter::getScharrKernelX(gradientFilterX.data, (apertureGradient - 1) / val2));
220  }
221  if (computeDy) {
222  scaleY = static_cast<float>(vpImageFilter::getScharrKernelY(gradientFilterY.data, (apertureGradient - 1) / val2));
223  }
224  }
225 
226  // Scale the gradient filters to have a normalized gradient filter
227  if (normalize) {
228  if (computeDx) {
229  scaleFilter(gradientFilterX, scaleX);
230  }
231  if (computeDy) {
232  scaleFilter(gradientFilterY, scaleY);
233  }
234  }
235 
236  // Apply the gradient filters to get the gradients
237  if (computeDx) {
238  vpImageFilter::filter(Iblur, dIx, gradientFilterX, true, p_mask);
239  }
240 
241  if (computeDy) {
242  vpImageFilter::filter(Iblur, dIy, gradientFilterY, true, p_mask);
243  }
244  }
245  else {
246  std::string errMsg = "[vpImageFilter::computePartialDerivatives] Filtering + gradient method \"";
247  errMsg += vpCannyFiltAndGradTypeToStr(filteringType);
248  errMsg += "\" is not implemented yet\n";
250  }
251  }
252  }
253 
254 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
255  template <typename FilterType>
256  inline static void computePartialDerivatives(const vpImage<vpRGBa> &I,
258  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
259  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
260  const unsigned int &apertureGradient = 3,
261  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
262  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
263 
264  template <typename ImageType>
265  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
267  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
268  const unsigned int &gaussianKernelSize = 5, const unsigned char &gaussianStdev = 2.f,
269  const unsigned int &apertureGradient = 3,
270  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
271  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
272 
273  template <typename ImageType>
274  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
275  vpImage<vpRGBa> &dIx, vpImage<vpRGBa> &dIy,
276  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
277  const unsigned int gaussianKernelSize = 5, const vpRGBa gaussianStdev = vpRGBa(),
278  const unsigned int apertureGradient = 3,
279  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
280  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
281 #else
282  template <typename FilterType>
283  inline static void computePartialDerivatives(const vpImage<vpRGBa> &I,
285  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
286  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
287  const unsigned int &apertureGradient = 3,
288  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
289  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
290 
291  template <typename ImageType>
292  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
294  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
295  const unsigned int &gaussianKernelSize = 5, const unsigned char &gaussianStdev = 2.f,
296  const unsigned int &apertureGradient = 3,
297  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
298  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
299 
300  template <typename ImageType>
301  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
302  vpImage<vpRGBa> &dIx, vpImage<vpRGBa> &dIy,
303  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
304  const unsigned int gaussianKernelSize = 5, const vpRGBa gaussianStdev = vpRGBa(),
305  const unsigned int apertureGradient = 3,
306  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
307  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
308 #endif
309 
330  template<typename OutType>
331  inline static float computeCannyThreshold(const vpImage<unsigned char> &I, float &lowerThresh,
332  const vpImage<OutType> *p_dIx = nullptr, const vpImage<OutType> *p_dIy = nullptr,
333  const unsigned int &gaussianKernelSize = 5,
334  const OutType &gaussianStdev = 2.f, const unsigned int &apertureGradient = 3,
335  const float &lowerThresholdRatio = 0.6, const float &upperThresholdRatio = 0.8,
336  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
337  const vpImage<bool> *p_mask = nullptr)
338  {
339  const unsigned int w = I.getWidth();
340  const unsigned int h = I.getHeight();
341 
342  vpImage<unsigned char> dI(h, w);
343  vpImage<OutType> dIx(h, w), dIy(h, w);
344  if ((p_dIx != nullptr) && (p_dIy != nullptr)) {
345  dIx = *p_dIx;
346  dIy = *p_dIy;
347  }
348  else {
349  computePartialDerivatives(I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev,
350  apertureGradient, filteringType, vpImageFilter::CANNY_VISP_BACKEND, p_mask);
351  }
352 
353  // Computing the absolute gradient of the image G = |dIx| + |dIy|
354  for (unsigned int r = 0; r < h; ++r) {
355  for (unsigned int c = 0; c < w; ++c) {
356  // We have to compute the value for each pixel if we don't have a mask or for
357  // pixels for which the mask is true otherwise
358  bool computeVal = checkBooleanMask(p_mask, r, c);
359 
360  if (computeVal) {
361  float dx = static_cast<float>(dIx[r][c]);
362  float dy = static_cast<float>(dIy[r][c]);
363  float gradient = std::abs(dx) + std::abs(dy);
364  float gradientClamped = std::min<float>(gradient, static_cast<float>(std::numeric_limits<unsigned char>::max()));
365  dI[r][c] = static_cast<unsigned char>(gradientClamped);
366  }
367  }
368  }
369 
370  // Compute the histogram
371  vpHistogram hist;
372  hist.setMask(p_mask);
373  const unsigned int nbBins = 256;
374  hist.calculate(dI, nbBins);
375  float totalNbPixels = static_cast<float>(hist.getTotal());
376  float accu = 0;
377  float t = upperThresholdRatio * totalNbPixels;
378  float bon = 0;
379  unsigned int i = 0;
380  bool notFound = true;
381  while ((i < nbBins) && notFound) {
382  float tf = static_cast<float>(hist[i]);
383  accu = accu + tf;
384  if (accu > t) {
385  bon = static_cast<float>(i);
386  notFound = false;
387  }
388  ++i;
389  }
390  float upperThresh = std::max<float>(bon, 1.f);
391  lowerThresh = lowerThresholdRatio * bon;
392  return upperThresh;
393  }
394 
402  template <class ImageType> static double derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
403  {
404  const int val1 = 1, val2 = 2, val3 = 3;
405  return ((2047.0 * static_cast<double>(I[r][c + val1] - I[r][c - val1])) + (913.0 * static_cast<double>(I[r][c + val2] - I[r][c - val2])) +
406  (112.0 * static_cast<double>(I[r][c + val3] - I[r][c - val3]))) / 8418.0;
407  }
408 
416  template <class ImageType> static double derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
417  {
418  const int val1 = 1, val2 = 2, val3 = 3;
419  return ((2047.0 * static_cast<double>(I[r + val1][c] - I[r - val1][c])) + (913.0 * static_cast<double>(I[r + val2][c] - I[r - val2][c])) +
420  (112.0 * static_cast<double>(I[r + val3][c] - I[r - val3][c]))) / 8418.0;
421  }
422 
436  template <class ImageType, typename FilterType>
437  static FilterType derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
438  {
439  const unsigned int stop = (size - 1) / 2;
440  unsigned int i;
441  FilterType result;
442 
443  result = 0;
444 
445  for (i = 1; i <= stop; ++i) {
446  result += filter[i] * static_cast<FilterType>(I[r][c + i] - I[r][c - i]);
447  }
448  return result;
449  }
450 
464  template <class ImageType, typename FilterType>
465  static FilterType derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
466  {
467  const unsigned int stop = (size - 1) / 2;
468  unsigned int i;
469  FilterType result;
470 
471  result = 0;
472 
473  for (i = 1; i <= stop; ++i) {
474  result += filter[i] * static_cast<FilterType>(I[r + i][c] - I[r - i][c]);
475  }
476  return result;
477  }
478 
508  template <typename ImageType, typename FilterType>
509  static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false,
510  const vpImage<bool> *p_mask = nullptr)
511  {
512  const unsigned int size_y = M.getRows(), size_x = M.getCols();
513  const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
514 
515  const unsigned int inputHeight = I.getHeight(), inputWidth = I.getWidth();
516  If.resize(inputHeight, inputWidth, 0.0);
517 
518  if (convolve) {
519  const unsigned int stopHeight = inputHeight - half_size_y;
520  const unsigned int stopWidth = inputWidth - half_size_x;
521  for (unsigned int i = half_size_y; i < stopHeight; ++i) {
522  for (unsigned int j = half_size_x; j < stopWidth; ++j) {
523  // We have to compute the value for each pixel if we don't have a mask or for
524  // pixels for which the mask is true otherwise
525  bool computeVal = checkBooleanMask(p_mask, i, j);
526  if (computeVal) {
527  FilterType conv = 0;
528 
529  for (unsigned int a = 0; a < size_y; ++a) {
530  for (unsigned int b = 0; b < size_x; ++b) {
531  FilterType val = static_cast<FilterType>(I[(i + half_size_y) - a][(j + half_size_x) - b]); // Convolution
532  conv += M[a][b] * val;
533  }
534  }
535  If[i][j] = conv;
536  }
537  }
538  }
539  }
540  else {
541  const unsigned int stopHeight = inputHeight - half_size_y;
542  const unsigned int stopWidth = inputWidth - half_size_x;
543  for (unsigned int i = half_size_y; i < stopHeight; ++i) {
544  for (unsigned int j = half_size_x; j < stopWidth; ++j) {
545  // We have to compute the value for each pixel if we don't have a mask or for
546  // pixels for which the mask is true otherwise
547  bool computeVal = checkBooleanMask(p_mask, i, j);
548  if (computeVal) {
549  FilterType corr = 0;
550 
551  for (unsigned int a = 0; a < size_y; ++a) {
552  for (unsigned int b = 0; b < size_x; ++b) {
553  FilterType val = static_cast<FilterType>(I[(i - half_size_y) + a][(j - half_size_x) + b]); // Correlation
554  corr += M[a][b] * val;
555  }
556  }
557  If[i][j] = corr;
558  }
559  }
560  }
561  }
562  }
563 
573  template <typename FilterType>
574  static FilterType filter(const vpImage<FilterType> &I, const vpArray2D<FilterType> &M, unsigned int row, unsigned int col)
575  {
576  const unsigned int size_y = M.getRows(), size_x = M.getCols();
577  const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
578  FilterType corr = 0;
579 
580  for (unsigned int a = 0; a < size_y; ++a) {
581  for (unsigned int b = 0; b < size_x; ++b) {
582  FilterType val = static_cast<FilterType>(I[row - half_size_y + a][col - half_size_x + b]); // Correlation
583  corr += M[a][b] * val;
584  }
585  }
586  return corr;
587  }
588 
589 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
590  template <typename FilterType>
591  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false) = delete;
592 #else
593  template <typename FilterType>
594  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false);
595 #endif
596 
610  template <typename ImageType, typename FilterType>
612  bool convolve = false, const vpImage<bool> *p_mask = nullptr)
613  {
614  const unsigned int size = M.getRows();
615  const unsigned int half_size = size / 2;
616  const unsigned int height = I.getHeight(), width = I.getWidth();
617  const unsigned int stopV = height - half_size;
618  const unsigned int stopU = width - half_size;
619 
620  Iu.resize(height, width, 0.0);
621  Iv.resize(height, width, 0.0);
622 
623  if (convolve) {
624  for (unsigned int v = half_size; v < stopV; ++v) {
625  for (unsigned int u = half_size; u < stopU; ++u) {
626  // We have to compute the value for each pixel if we don't have a mask or for
627  // pixels for which the mask is true otherwise
628  bool computeVal = checkBooleanMask(p_mask, v, u);
629  if (computeVal) {
630  FilterType conv_u = 0;
631  FilterType conv_v = 0;
632 
633  for (unsigned int a = 0; a < size; ++a) {
634  for (unsigned int b = 0; b < size; ++b) {
635  FilterType val = static_cast<FilterType>(I[(v + half_size) - a][(u + half_size) - b]); // Convolution
636  conv_u += M[a][b] * val;
637  conv_v += M[b][a] * val;
638  }
639  }
640  Iu[v][u] = conv_u;
641  Iv[v][u] = conv_v;
642  }
643  }
644  }
645  }
646  else {
647  for (unsigned int v = half_size; v < stopV; ++v) {
648  for (unsigned int u = half_size; u < stopU; ++u) {
649  // We have to compute the value for each pixel if we don't have a mask or for
650  // pixels for which the mask is true otherwise
651  bool computeVal = checkBooleanMask(p_mask, v, u);
652 
653  if (computeVal) {
654  FilterType conv_u = 0;
655  FilterType conv_v = 0;
656 
657  for (unsigned int a = 0; a < size; ++a) {
658  for (unsigned int b = 0; b < size; ++b) {
659  FilterType val = static_cast<FilterType>(I[(v - half_size) + a][(u - half_size) + b]); // Correlation
660  conv_u += M[a][b] * val;
661  conv_v += M[b][a] * val;
662  }
663  }
664  Iu[v][u] = conv_u;
665  Iv[v][u] = conv_v;
666  }
667  }
668  }
669  }
670  }
671 
672 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
673  template<typename FilterType>
674  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve) = delete;
675 
676  template<typename ImageType>
677  static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve) = delete;
678 #else
679  template<typename FilterType>
680  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve);
681 
682  template<typename ImageType>
683  static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve);
684 #endif
685 
686  static void sepFilter(const vpImage<unsigned char> &I, vpImage<double> &If, const vpColVector &kernelH, const vpColVector &kernelV);
687 
697  template <typename ImageType, typename FilterType>
698  static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &GI, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
699  {
701  filterX<ImageType, FilterType>(I, GIx, filter, size, p_mask);
702  filterY<FilterType, FilterType>(GIx, GI, filter, size, p_mask);
703  GIx.destroy();
704  }
705 
706  static inline unsigned char filterGaussXPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
707  {
708  const int val2 = 2;
709  return static_cast<unsigned char>(((1. * I[i][j - val2]) + (4. * I[i][j - 1]) + (6. * I[i][j]) + (4. * I[i][j + 1]) + (1. * I[i][j + val2])) / 16.);
710  }
711  static inline unsigned char filterGaussYPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
712  {
713  const int val2 = 2;
714  return static_cast<unsigned char>(((1. * I[i - val2][j]) + (4. * I[i - 1][j]) + (6. * I[i][j]) + (4. * I[i + 1][j]) + (1. * I[i + val2][j])) / 16.);
715  }
716 
717  template <typename ImageType, typename FilterType>
718  static void filterX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size,
719  const vpImage<bool> *p_mask = nullptr)
720  {
721  const unsigned int height = I.getHeight();
722  const unsigned int width = I.getWidth();
723  const unsigned int stop1J = (size - 1) / 2;
724  const unsigned int stop2J = width - ((size - 1) / 2);
725  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
726 
727  for (unsigned int i = 0; i < height; ++i) {
728  for (unsigned int j = 0; j < stop1J; ++j) {
729  // We have to compute the value for each pixel if we don't have a mask or for
730  // pixels for which the mask is true otherwise
731  bool computeVal = checkBooleanMask(p_mask, i, j);
732  if (computeVal) {
733  dIx[i][j] = vpImageFilter::filterXLeftBorder<ImageType, FilterType>(I, i, j, filter, size);
734  }
735  }
736  for (unsigned int j = stop1J; j < stop2J; ++j) {
737  // We have to compute the value for each pixel if we don't have a mask or for
738  // pixels for which the mask is true otherwise
739  bool computeVal = checkBooleanMask(p_mask, i, j);
740  if (computeVal) {
741  dIx[i][j] = vpImageFilter::filterX<ImageType, FilterType>(I, i, j, filter, size);
742  }
743  }
744  for (unsigned int j = stop2J; j < width; ++j) {
745  // We have to compute the value for each pixel if we don't have a mask or for
746  // pixels for which the mask is true otherwise
747  bool computeVal = checkBooleanMask(p_mask, i, j);
748  if (computeVal) {
749  dIx[i][j] = vpImageFilter::filterXRightBorder<ImageType, FilterType>(I, i, j, filter, size);
750  }
751  }
752  }
753  }
754 
755  static void filterX(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
756 
757  template<typename ImageType, typename FilterType>
758  static inline FilterType filterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
759  {
760  const unsigned int stop = (size - 1) / 2;
761  FilterType result = static_cast<FilterType>(0.);
762 
763  for (unsigned int i = 1; i <= stop; ++i) {
764  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
765  }
766  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
767  }
768 
769 #ifndef DOXYGEN_SHOULD_SKIP_THIS
770  static void filterXR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
771  static void filterXG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
772  static void filterXB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
773 
774  static double filterXR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
775  static double filterXG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
776  static double filterXB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
777 
778  static double filterXLeftBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
779  static double filterXLeftBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
780  static double filterXLeftBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
781  static double filterXRightBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
782  static double filterXRightBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
783  static double filterXRightBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
784 
785  template <typename ImageType, typename FilterType>
786  static inline FilterType filterXLeftBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
787  const FilterType *filter, unsigned int size)
788  {
789  const unsigned int stop = (size - 1) / 2;
790  FilterType result = static_cast<FilterType>(0.);
791 
792  for (unsigned int i = 1; i <= stop; ++i) {
793  if (c > i) {
794  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
795  }
796  else {
797  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][i - c]);
798  }
799  }
800  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
801  }
802 
803  template <typename ImageType, typename FilterType>
804  static inline FilterType filterXRightBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
805  const FilterType *filter, unsigned int size)
806  {
807  const unsigned int stop = (size - 1) / 2;
808  const unsigned int width = I.getWidth();
809  FilterType result = static_cast<FilterType>(0.);
810  const unsigned int twice = 2;
811 
812  for (unsigned int i = 1; i <= stop; ++i) {
813  if ((c + i) < width) {
814  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
815  }
816  else {
817  result += filter[i] * static_cast<FilterType>(I[r][((twice * width) - c) - i - 1] + I[r][c - i]);
818  }
819  }
820  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
821  }
822 #endif
823 
824  static void filterY(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
825 
826  template<typename ImageType, typename FilterType>
827  static void filterY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size,
828  const vpImage<bool> *p_mask = nullptr)
829  {
830  const unsigned int height = I.getHeight(), width = I.getWidth();
831  const unsigned int stop1I = (size - 1) / 2;
832  const unsigned int stop2I = height - ((size - 1) / 2);
833  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
834 
835  for (unsigned int i = 0; i < stop1I; ++i) {
836  for (unsigned int j = 0; j < width; ++j) {
837  // We have to compute the value for each pixel if we don't have a mask or for
838  // pixels for which the mask is true otherwise
839  bool computeVal = checkBooleanMask(p_mask, i, j);
840  if (computeVal) {
841  dIy[i][j] = vpImageFilter::filterYTopBorder<ImageType, FilterType>(I, i, j, filter, size);
842  }
843  }
844  }
845  for (unsigned int i = stop1I; i < stop2I; ++i) {
846  for (unsigned int j = 0; j < width; ++j) {
847  // We have to compute the value for each pixel if we don't have a mask or for
848  // pixels for which the mask is true otherwise
849  bool computeVal = checkBooleanMask(p_mask, i, j);
850  if (computeVal) {
851  dIy[i][j] = vpImageFilter::filterY<ImageType, FilterType>(I, i, j, filter, size);
852  }
853  }
854  }
855  for (unsigned int i = stop2I; i < height; ++i) {
856  for (unsigned int j = 0; j < width; ++j) {
857  // We have to compute the value for each pixel if we don't have a mask or for
858  // pixels for which the mask is true otherwise
859  bool computeVal = checkBooleanMask(p_mask, i, j);
860  if (computeVal) {
861  dIy[i][j] = vpImageFilter::filterYBottomBorder<ImageType, FilterType>(I, i, j, filter, size);
862  }
863  }
864  }
865  }
866 
867  template<typename ImageType, typename FilterType>
868  static inline FilterType filterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
869  {
870  const unsigned int stop = (size - 1) / 2;
871  FilterType result = static_cast<FilterType>(0.);
872 
873  for (unsigned int i = 1; i <= stop; ++i) {
874  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
875  }
876  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
877  }
878 #ifndef DOXYGEN_SHOULD_SKIP_THIS
879  static void filterYR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
880  static void filterYG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
881  static void filterYB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
882 
883  static double filterYR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
884  static double filterYG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
885  static double filterYB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
886 
887  static double filterYTopBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
888  static double filterYTopBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
889  static double filterYTopBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
890  static double filterYBottomBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
891  static double filterYBottomBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
892  static double filterYBottomBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
893 
894  template<typename ImageType, typename FilterType>
895  static inline FilterType filterYTopBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
896  const FilterType *filter, unsigned int size)
897  {
898  const unsigned int stop = (size - 1) / 2;
899  FilterType result = static_cast<FilterType>(0.);
900 
901  for (unsigned int i = 1; i <= stop; ++i) {
902  if (r > i) {
903  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
904  }
905  else {
906  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[i - r][c]);
907  }
908  }
909  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
910  }
911 
912  template<typename ImageType, typename FilterType>
913  static inline FilterType filterYBottomBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
914  const FilterType *filter, unsigned int size)
915  {
916  const unsigned int height = I.getHeight();
917  const unsigned int stop = (size - 1) / 2;
918  FilterType result = static_cast<FilterType>(0.);
919  const unsigned int twiceHeight = 2 * height;
920  for (unsigned int i = 1; i <= stop; ++i) {
921  if ((r + i) < height) {
922  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
923  }
924  else {
925  result += filter[i] * static_cast<FilterType>(I[(twiceHeight - r) - i - 1][c] + I[r - i][c]);
926  }
927  }
928  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
929  }
930 #endif
931 
932 
946  template <typename ImageType, typename FilterType>
947  static void gaussianBlur(const vpImage<ImageType> &I, vpImage<FilterType> &GI, unsigned int size = 7, FilterType sigma = 0., bool normalize = true,
948  const vpImage<bool> *p_mask = nullptr)
949  {
950  FilterType *fg = new FilterType[(size + 1) / 2];
951  vpImageFilter::getGaussianKernel<FilterType>(fg, size, sigma, normalize);
953  vpImageFilter::filterX<ImageType, FilterType>(I, GIx, fg, size, p_mask);
954  vpImageFilter::filterY<FilterType, FilterType>(GIx, GI, fg, size, p_mask);
955  GIx.destroy();
956  delete[] fg;
957  }
958 
959  static void gaussianBlur(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &GI, unsigned int size = 7, double sigma = 0., bool normalize = true,
960  const vpImage<bool> *p_mask = nullptr);
961 
969  template <class T> static double gaussianFilter(const vpImage<T> &fr, unsigned int r, unsigned int c)
970  {
971  const int val2 = 2;
972  return ((15.0 * fr[r][c]) +
973  (12.0 * (fr[r - 1][c] + fr[r][c - 1] + fr[r + 1][c] + fr[r][c + 1])) +
974  (9.0 * (fr[r - 1][c - 1] + fr[r + 1][c - 1] + fr[r - 1][c + 1] + fr[r + 1][c + 1])) +
975  (5.0 * (fr[r - val2][c] + fr[r][c - val2] + fr[r + val2][c] + fr[r][c + val2])) +
976  (4.0 * (fr[r - val2][c + 1] + fr[r - val2][c - 1] + fr[r - 1][c - val2] + fr[r + 1][c - val2] + fr[r + val2][c - 1] +
977  fr[r + val2][c + 1] + fr[r - 1][c + val2] + fr[r + 1][c + val2])) +
978  (2.0 * (fr[r - val2][c - val2] + fr[r + val2][c - val2] + fr[r - val2][c + val2] + fr[r + val2][c + val2]))) / 159.0;
979  }
980  // Gaussian pyramid operation
981  static void getGaussPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
982  static void getGaussXPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
983  static void getGaussYPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
984 
1001  template<typename FilterType>
1002  static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1003  {
1004  const unsigned int mod2 = 2;
1005  if ((size % mod2) != 1) {
1006  throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1007  }
1008 
1009  if (sigma <= 0) {
1010  sigma = static_cast<FilterType>((size - 1) / 6.0);
1011  }
1012 
1013  int middle = (static_cast<int>(size) - 1) / 2;
1014  FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(sigma));
1015  FilterType coef1 = static_cast<FilterType>(1. / (sigma * sqrt(2. * M_PI)));
1016  FilterType v_2_sigma2 = static_cast<FilterType>(2. * sigma2);
1017  for (int i = 0; i <= middle; ++i) {
1018  filter[i] = coef1 * static_cast<FilterType>(exp(-(i * i) / v_2_sigma2));
1019  }
1020  if (normalize) {
1021  // renormalization
1022  FilterType sum = 0;
1023  const unsigned int val2 = 2U;
1024  for (int i = 1; i <= middle; ++i) {
1025  sum += val2 * filter[i];
1026  }
1027  sum += filter[0];
1028 
1029  for (int i = 0; i <= middle; ++i) {
1030  filter[i] = filter[i] / sum;
1031  }
1032  }
1033  }
1034 
1049  template <typename FilterType>
1050  static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1051  {
1052  const unsigned int mod2 = 2;
1053  if ((size % mod2) != 1) {
1054  throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1055  }
1056 
1057  if (sigma <= 0) {
1058  sigma = static_cast<FilterType>((size - 1) / 6.0);
1059  }
1060 
1061  const int half = 2;
1062  int middle = (static_cast<int>(size) - 1) / half;
1063  FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(sigma));
1064  FilterType coef_1 = static_cast<FilterType>(1. / (sigma * sqrt(2. * M_PI)));
1065  FilterType coef_1_over_2 = coef_1 / static_cast<FilterType>(2.);
1066  FilterType v_2_coef_1 = static_cast<FilterType>(2.) * coef_1;
1067  FilterType v_2_sigma2 = static_cast<FilterType>(2. * sigma2);
1068  filter[0] = 0.;
1069  for (int i = 1; i <= middle; ++i) {
1070  filter[i] = -coef_1_over_2 * (static_cast<FilterType>(exp(-((i + 1) * (i + 1)) / v_2_sigma2)) - static_cast<FilterType>(exp(-((i - 1) * (i - 1)) / v_2_sigma2)));
1071  }
1072 
1073  if (normalize) {
1074  FilterType sum = 0;
1075  for (int i = 1; i <= middle; ++i) {
1076  sum += v_2_coef_1 * static_cast<FilterType>(exp(-(i * i) / v_2_sigma2));
1077  }
1078  sum += coef_1;
1079 
1080  for (int i = 1; i <= middle; ++i) {
1081  filter[i] = filter[i] / sum;
1082  }
1083  }
1084  }
1085 
1086  // Gradient along X
1087  template<typename FilterType>
1088  static void getGradX(const vpImage<unsigned char> &I, vpImage<FilterType> &dIx, const vpImage<bool> *p_mask = nullptr)
1089  {
1090  const unsigned int height = I.getHeight(), width = I.getWidth();
1091  const unsigned int stopJ = width - 3;
1092  const unsigned int val_3 = 3;
1093  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1094 
1095  for (unsigned int i = 0; i < height; ++i) {
1096  for (unsigned int j = 0; j < val_3; ++j) {
1097  // If a mask is used, the image is already initialized with 0s
1098  bool computeVal = (p_mask == nullptr);
1099  if (computeVal) {
1100  dIx[i][j] = static_cast<FilterType>(0);
1101  }
1102  }
1103  for (unsigned int j = 3; j < stopJ; ++j) {
1104  // We have to compute the value for each pixel if we don't have a mask or for
1105  // pixels for which the mask is true otherwise
1106  bool computeVal = checkBooleanMask(p_mask, i, j);
1107  if (computeVal) {
1108  dIx[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterX(I, i, j));
1109  }
1110  }
1111  for (unsigned int j = stopJ; j < width; ++j) {
1112  // If a mask is used, the image is already initialized with 0s
1113  bool computeVal = (p_mask == nullptr);
1114  if (computeVal) {
1115  dIx[i][j] = static_cast<FilterType>(0);
1116  }
1117  }
1118  }
1119  }
1120 
1121  template <typename ImageType, typename FilterType>
1122  static void getGradX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1123  {
1124  const unsigned int height = I.getHeight(), width = I.getWidth();
1125  const unsigned int stop1J = (size - 1) / 2;
1126  const unsigned int stop2J = width - ((size - 1) / 2);
1127  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1128 
1129  for (unsigned int i = 0; i < height; ++i) {
1130  for (unsigned int j = 0; j < stop1J; ++j) {
1131  // If a mask is used, the image is already initialized with 0s
1132  bool computeVal = (p_mask == nullptr);
1133  if (computeVal) {
1134  dIx[i][j] = static_cast<FilterType>(0);
1135  }
1136  }
1137  for (unsigned int j = stop1J; j < stop2J; ++j) {
1138  // We have to compute the value for each pixel if we don't have a mask or for
1139  // pixels for which the mask is true otherwise
1140  bool computeVal = checkBooleanMask(p_mask, i, j);
1141  if (computeVal) {
1142  dIx[i][j] = vpImageFilter::derivativeFilterX<ImageType, FilterType>(I, i, j, filter, size);
1143  }
1144  }
1145  for (unsigned int j = stop2J; j < width; ++j) {
1146  // If a mask is used, the image is already initialized with 0s
1147  bool computeVal = (p_mask == nullptr);
1148  if (computeVal) {
1149  dIx[i][j] = static_cast<FilterType>(0);
1150  }
1151  }
1152  }
1153  }
1154 
1166  template <typename ImageType, typename FilterType>
1167  static void getGradXGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *gaussianKernel,
1168  const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1169  {
1170  vpImage<FilterType> GIy;
1171  vpImageFilter::filterY<ImageType, FilterType>(I, GIy, gaussianKernel, size, p_mask);
1172  vpImageFilter::getGradX<FilterType, FilterType>(GIy, dIx, gaussianDerivativeKernel, size, p_mask);
1173  }
1174 
1175  // Gradient along Y
1176  template <typename FilterType>
1177  static void getGradY(const vpImage<unsigned char> &I, vpImage<FilterType> &dIy, const vpImage<bool> *p_mask = nullptr)
1178  {
1179  const unsigned int height = I.getHeight(), width = I.getWidth();
1180  const unsigned int stopI = height - 3;
1181  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1182  const unsigned int val_3 = 3;
1183  for (unsigned int i = 0; i < val_3; ++i) {
1184  for (unsigned int j = 0; j < width; ++j) {
1185  // We have to compute the value for each pixel if we don't have a mask or for
1186  // pixels for which the mask is true otherwise
1187  bool computeVal = checkBooleanMask(p_mask, i, j);
1188  if (computeVal) {
1189  dIy[i][j] = static_cast<FilterType>(0);
1190  }
1191  }
1192  }
1193  for (unsigned int i = 3; i < stopI; ++i) {
1194  for (unsigned int j = 0; j < width; ++j) {
1195  // We have to compute the value for each pixel if we don't have a mask or for
1196  // pixels for which the mask is true otherwise
1197  bool computeVal = checkBooleanMask(p_mask, i, j);
1198  if (computeVal) {
1199  dIy[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterY(I, i, j));
1200  }
1201  }
1202  }
1203  for (unsigned int i = stopI; i < height; ++i) {
1204  for (unsigned int j = 0; j < width; ++j) {
1205  // We have to compute the value for each pixel if we don't have a mask or for
1206  // pixels for which the mask is true otherwise
1207  bool computeVal = checkBooleanMask(p_mask, i, j);
1208  if (computeVal) {
1209  dIy[i][j] = static_cast<FilterType>(0);
1210  }
1211  }
1212  }
1213  }
1214 
1215  template <typename ImageType, typename FilterType>
1216  static void getGradY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1217  {
1218  const unsigned int height = I.getHeight(), width = I.getWidth();
1219  const unsigned int stop1I = (size - 1) / 2;
1220  const unsigned int stop2I = height - ((size - 1) / 2);
1221  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1222 
1223  for (unsigned int i = 0; i < stop1I; ++i) {
1224  for (unsigned int j = 0; j < width; ++j) {
1225  // We have to compute the value for each pixel if we don't have a mask or for
1226  // pixels for which the mask is true otherwise
1227  bool computeVal = checkBooleanMask(p_mask, i, j);
1228  if (computeVal) {
1229  dIy[i][j] = static_cast<FilterType>(0);
1230  }
1231  }
1232  }
1233  for (unsigned int i = stop1I; i < stop2I; ++i) {
1234  for (unsigned int j = 0; j < width; ++j) {
1235  // We have to compute the value for each pixel if we don't have a mask or for
1236  // pixels for which the mask is true otherwise
1237  bool computeVal = checkBooleanMask(p_mask, i, j);
1238  if (computeVal) {
1239  dIy[i][j] = vpImageFilter::derivativeFilterY<ImageType, FilterType>(I, i, j, filter, size);
1240  }
1241  }
1242  }
1243  for (unsigned int i = stop2I; i < height; ++i) {
1244  for (unsigned int j = 0; j < width; ++j) {
1245  // We have to compute the value for each pixel if we don't have a mask or for
1246  // pixels for which the mask is true otherwise
1247  bool computeVal = checkBooleanMask(p_mask, i, j);
1248  if (computeVal) {
1249  dIy[i][j] = static_cast<FilterType>(0);
1250  }
1251  }
1252  }
1253  }
1254 
1266  template <typename ImageType, typename FilterType>
1267  static void getGradYGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *gaussianKernel,
1268  const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1269  {
1270  vpImage<FilterType> GIx;
1271  vpImageFilter::filterX<ImageType, FilterType>(I, GIx, gaussianKernel, size, p_mask);
1272  vpImageFilter::getGradY<FilterType, FilterType>(GIx, dIy, gaussianDerivativeKernel, size, p_mask);
1273  }
1274 
1282  template <typename FilterType>
1283  inline static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
1284  {
1285  const unsigned int actualKernelSize = (size * 2) + 1;
1286  if (size != 1) {
1287  // Size = 1 => kernel_size = 2*1 + 1 = 3
1288  std::stringstream errMsg;
1289  errMsg << "Cannot get Scharr kernel of size " << actualKernelSize << " != 3";
1290  throw vpException(vpException::dimensionError, errMsg.str());
1291  }
1292 
1293  vpArray2D<FilterType> ScharrY(actualKernelSize, actualKernelSize);
1294  FilterType norm = getScharrKernelY<FilterType>(ScharrY.data, size);
1295  memcpy(filter, ScharrY.t().data, ScharrY.getRows() * ScharrY.getCols() * sizeof(FilterType));
1296  return norm;
1297  }
1298 
1306  template <typename FilterType>
1307  inline static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
1308  {
1309  // Scharr kernel pre-computed for the usual size
1310  static const FilterType ScharrY3x3[9] = { -3.0, -10.0, -3.0, 0.0, 0.0, 0.0, 3.0, 10.0, 3.0 };
1311 
1312  if (size != 1) {
1313  // Size = 1 => kernel_size = 2*1 + 1 = 3
1314  std::stringstream errMsg;
1315  errMsg << "Cannot get Scharr kernel of size " << ((size * 2) + 1) << " != 3";
1316  throw vpException(vpException::dimensionError, errMsg.str());
1317  }
1318 
1319  const unsigned int kernel_size = (size * 2) + 1;
1320  const unsigned int kernel3 = 3;
1321  if (kernel_size == kernel3) {
1322  memcpy(filter, ScharrY3x3, kernel_size * kernel_size * sizeof(FilterType));
1323  return static_cast<FilterType>(1.0 / 32.0);
1324  }
1325 
1326  return static_cast<FilterType>(0.);
1327  }
1328 
1336  template <typename FilterType>
1337  inline static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
1338  {
1339  const unsigned int maxSize = 20;
1340  if (size == 0) {
1341  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
1342  }
1343  if (size > maxSize) {
1344  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
1345  }
1346 
1347  const unsigned int kernel_size = (size * 2) + 1;
1348  vpArray2D<FilterType> SobelY(kernel_size, kernel_size);
1349  FilterType norm = getSobelKernelY<FilterType>(SobelY.data, size);
1350  memcpy(filter, SobelY.t().data, SobelY.getRows() * SobelY.getCols() * sizeof(FilterType));
1351  return norm;
1352  }
1353 
1361  template <typename FilterType>
1362  inline static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
1363  {
1364  // Sobel kernel pre-computed for the usual size
1365  static const FilterType SobelY3x3[9] = { -1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0 };
1366  static const FilterType SobelY5x5[25] = { -1.0, -4.0, -6.0, -4.0, -1.0, -2.0, -8.0, -12.0, -8.0, -2.0, 0.0, 0.0, 0.0,
1367  0.0, 0.0, 2.0, 8.0, 12.0, 8.0, 2.0, 1.0, 4.0, 6.0, 4.0, 1.0 };
1368  static const FilterType SobelY7x7[49] = { -1, -6, -15, -20, -15, -6, -1, -4, -24, -60, -80, -60, -24, -4, -5, -30, -75,
1369  -100, -75, -30, -5, 0, 0, 0, 0, 0, 0, 0, 5, 30, 75, 100, 75, 30,
1370  5, 4, 24, 60, 80, 60, 24, 4, 1, 6, 15, 20, 15, 6, 1 };
1371  const vpArray2D<FilterType> smoothingKernel(3, 3);
1372  const unsigned int index_0 = 0;
1373  const unsigned int index_1 = 1;
1374  const unsigned int index_2 = 2;
1375  smoothingKernel[index_0][index_0] = 1.0;
1376  smoothingKernel[index_0][index_1] = 2.0;
1377  smoothingKernel[index_0][index_2] = 1.0;
1378  smoothingKernel[index_1][index_0] = 2.0;
1379  smoothingKernel[index_1][index_1] = 4.0;
1380  smoothingKernel[index_1][index_2] = 2.0;
1381  smoothingKernel[index_2][index_0] = 1.0;
1382  smoothingKernel[index_2][index_1] = 2.0;
1383  smoothingKernel[index_2][index_2] = 1.0;
1384 
1385  const unsigned int maxSize = 20;
1386  if (size == 0) {
1387  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
1388  }
1389  if (size > maxSize) {
1390  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
1391  }
1392 
1393  const unsigned int kernel_size = (size * 2) + 1;
1394  FilterType scale = static_cast<FilterType>(1. / 8.); // Scale to normalize Sobel3x3
1395  const unsigned int kernel3 = 3, kernel5 = 5, kernel7 = 7;
1396  if (kernel_size == kernel3) {
1397  memcpy(filter, SobelY3x3, kernel_size * kernel_size * sizeof(FilterType));
1398  return scale;
1399  }
1400  scale *= static_cast<FilterType>(1. / 16.); // Sobel5x5 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel3x3
1401  if (kernel_size == kernel5) {
1402  memcpy(filter, SobelY5x5, kernel_size * kernel_size * sizeof(FilterType));
1403  return scale;
1404  }
1405  scale *= static_cast<FilterType>(1. / 16.); // Sobel7x7 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel5x5
1406  if (kernel_size == kernel7) {
1407  memcpy(filter, SobelY7x7, kernel_size * kernel_size * sizeof(FilterType));
1408  return scale;
1409  }
1410 
1411  vpArray2D<FilterType> sobelY(7, 7);
1412  memcpy(sobelY.data, SobelY7x7, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
1413  for (unsigned int i = 4; i <= size; ++i) {
1414  sobelY = vpArray2D<FilterType>::conv2(sobelY, smoothingKernel, "full");
1415  // Sobel(N+1)x(N+1) is the convolution of smoothingKernel, which needs 1/16 scale factor, with SobelNxN
1416  scale *= static_cast<FilterType>(1. / 16.);
1417  }
1418 
1419  memcpy(filter, sobelY.data, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
1420 
1421  return scale;
1422  }
1423 
1424 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
1425  static float median(const cv::Mat &cv_I);
1426  static float median(const vpImage<unsigned char> &Isrc);
1427  static std::vector<float> median(const vpImage<vpRGBa> &Isrc);
1428 #endif
1429 
1430 private:
1441  template<typename ImageType>
1442  static void resizeAndInitializeIfNeeded(const vpImage<bool> *p_mask, const unsigned int height, const unsigned int width, vpImage<ImageType> &I)
1443  {
1444  if (p_mask == nullptr) {
1445  // Just need to resize the output image, values will be computed and overwrite what is inside the image
1446  I.resize(height, width);
1447  }
1448  else {
1449  // Need to reset the image because some points will not be computed
1450  I.resize(height, width, static_cast<ImageType>(0));
1451  }
1452  }
1453 
1463  static bool checkBooleanMask(const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c)
1464  {
1465  bool computeVal = true;
1466 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
1467  if (p_mask != nullptr)
1468 #else
1469  if (p_mask != NULL)
1470 #endif
1471  {
1472  computeVal = (*p_mask)[r][c];
1473  }
1474  return computeVal;
1475  }
1476 
1477 // Note that on ubuntu 12.04 __cplusplus is equal to 1 that's why in the next line we consider __cplusplus <= 199711L
1478 // and not __cplusplus == 199711L
1479 #if ((__cplusplus <= 199711L) || (defined(_MSVC_LANG) && (_MSVC_LANG == 199711L))) // Check if cxx98
1480  // Helper to apply the scale to the raw values of the filters
1481  template <typename FilterType>
1482  static void scaleFilter(vpArray2D<FilterType> &filter, const float &scale)
1483  {
1484  const unsigned int nbRows = filter.getRows();
1485  const unsigned int nbCols = filter.getCols();
1486  for (unsigned int r = 0; r < nbRows; ++r) {
1487  for (unsigned int c = 0; c < nbCols; ++c) {
1488  filter[r][c] = filter[r][c] * scale;
1489  }
1490  }
1491  }
1492 #endif
1493 
1494 };
1495 END_VISP_NAMESPACE
1496 #endif
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
static vpArray2D< Type > conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, const std::string &mode)
Definition: vpArray2D.h:1174
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition: vpArray2D.h:1163
unsigned int getRows() const
Definition: vpArray2D.h:347
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
@ dimensionError
Bad dimension.
Definition: vpException.h:71
@ notImplementedError
Not implemented.
Definition: vpException.h:69
Class to compute a gray level image histogram.
Definition: vpHistogram.h:106
unsigned int getTotal()
Get the total number of pixels in the input image.
Definition: vpHistogram.h:308
void calculate(const vpImage< unsigned char > &I, unsigned int nbins=256, unsigned int nbThreads=1)
void setMask(const vpImage< bool > *p_mask)
Set a mask to ignore pixels for which the mask is false.
Definition: vpHistogram.h:246
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Error that can be emitted by the vpImage class and its derivatives.
@ incorrectInitializationError
Wrong image initialization.
Various image filter, convolution, etc...
Definition: vpImageFilter.h:71
static void filterY(const vpImage< ImageType > &I, vpImage< FilterType > &dIy, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static unsigned char filterGaussXPyramidal(const vpImage< unsigned char > &I, unsigned int i, unsigned int j)
static void getGradY(const vpImage< ImageType > &I, vpImage< FilterType > &dIy, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static FilterType filterX(const vpImage< ImageType > &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
static double derivativeFilterX(const vpImage< ImageType > &I, unsigned int r, unsigned int c)
static void computePartialDerivatives(const vpImage< vpRGBa > &I, vpImage< FilterType > &dIx, vpImage< FilterType > &dIy, const bool &computeDx=true, const bool &computeDy=true, const bool &normalize=true, const unsigned int &gaussianKernelSize=5, const FilterType &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING, const vpCannyBackendType &backend=CANNY_VISP_BACKEND, const vpImage< bool > *p_mask=nullptr)
static void getGradX(const vpImage< unsigned char > &I, vpImage< FilterType > &dIx, const vpImage< bool > *p_mask=nullptr)
static float computeCannyThreshold(const vpImage< unsigned char > &I, float &lowerThresh, const vpImage< OutType > *p_dIx=nullptr, const vpImage< OutType > *p_dIy=nullptr, const unsigned int &gaussianKernelSize=5, const OutType &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, const vpImage< bool > *p_mask=nullptr)
Compute the upper Canny edge filter threshold, using Gaussian blur + Sobel or + Scharr operators to c...
static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
static void getGradXGauss2D(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *gaussianKernel, const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static FilterType filterY(const vpImage< ImageType > &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
vpCannyFilteringAndGradientType
Canny filter and gradient operators to apply on the image before the edge detection stage.
Definition: vpImageFilter.h:90
static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
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)
vpCannyBackendType
Canny filter backends for the edge detection operations.
Definition: vpImageFilter.h:75
@ CANNY_VISP_BACKEND
Use ViSP.
Definition: vpImageFilter.h:77
static void getGradX(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static FilterType derivativeFilterY(const vpImage< ImageType > &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
static double derivativeFilterY(const vpImage< ImageType > &I, unsigned int r, unsigned int c)
static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static void computePartialDerivatives(const vpImage< ImageType > &I, vpImage< unsigned char > &dIx, vpImage< unsigned char > &dIy, const bool &computeDx=true, const bool &computeDy=true, const bool &normalize=true, const unsigned int &gaussianKernelSize=5, const unsigned char &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING, const vpCannyBackendType &backend=CANNY_VISP_BACKEND, const vpImage< bool > *p_mask=nullptr)
static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
static void filter(const vpImage< vpRGBa > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false)
static void computePartialDerivatives(const vpImage< ImageType > &I, vpImage< vpRGBa > &dIx, vpImage< vpRGBa > &dIy, const bool &computeDx=true, const bool &computeDy=true, const bool &normalize=true, const unsigned int gaussianKernelSize=5, const vpRGBa gaussianStdev=vpRGBa(), const unsigned int apertureGradient=3, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING, const vpCannyBackendType &backend=CANNY_VISP_BACKEND, const vpImage< bool > *p_mask=nullptr)
static FilterType filter(const vpImage< FilterType > &I, const vpArray2D< FilterType > &M, unsigned int row, unsigned int col)
Apply a filter at a given image location.
static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
static void filter(const vpImage< vpRGBa > &I, vpImage< FilterType > &Iu, vpImage< FilterType > &Iv, const vpArray2D< FilterType > &M, bool convolve)
static void getGradYGauss2D(const vpImage< ImageType > &I, vpImage< FilterType > &dIy, const FilterType *gaussianKernel, const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
static void filter(const vpImage< ImageType > &I, vpImage< FilterType > &GI, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static void getGradY(const vpImage< unsigned char > &I, vpImage< FilterType > &dIy, const vpImage< bool > *p_mask=nullptr)
static void filter(const vpImage< ImageType > &I, vpImage< ImageType > &Iu, vpImage< ImageType > &Iv, const vpArray2D< vpRGBa > &M, bool convolve)
static void filterX(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static void computePartialDerivatives(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, vpImage< FilterType > &dIy, const bool &computeDx=true, const bool &computeDy=true, const bool &normalize=true, const unsigned int &gaussianKernelSize=5, const FilterType &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING, const vpCannyBackendType &backend=CANNY_VISP_BACKEND, const vpImage< bool > *p_mask=nullptr)
Compute the partial derivatives (i.e. horizontal and vertical gradients) of the input image.
static unsigned char filterGaussYPyramidal(const vpImage< unsigned char > &I, unsigned int i, unsigned int j)
static void filter(const vpImage< ImageType > &I, vpImage< FilterType > &Iu, vpImage< FilterType > &Iv, const vpArray2D< FilterType > &M, bool convolve=false, const vpImage< bool > *p_mask=nullptr)
static double gaussianFilter(const vpImage< T > &fr, unsigned int r, unsigned int c)
static FilterType derivativeFilterX(const vpImage< ImageType > &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
void destroy()
Destructor : Memory de-allocation.
Definition: vpImage.h:571
unsigned int getWidth() const
Definition: vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:542
unsigned int getHeight() const
Definition: vpImage.h:181
static double sqr(double x)
Definition: vpMath.h:203
Definition: vpRGBa.h:65