Visual Servoing Platform  version 3.6.1 under development (2025-02-03)
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 #if (VISP_HAVE_OPENCV_VERSION < 0x050000)
60 #include <opencv2/imgproc/imgproc_c.h>
61 #endif
62 #endif
63 
64 BEGIN_VISP_NAMESPACE
72 class VISP_EXPORT vpImageFilter
73 {
74 public:
76  typedef enum vpCannyBackendType
77  {
78  CANNY_OPENCV_BACKEND = 0,
79  CANNY_VISP_BACKEND = 1,
80  CANNY_COUNT_BACKEND = 2
81  } vpCannyBackendType;
82 
83  static std::string vpCannyBackendTypeList(const std::string &pref = "<", const std::string &sep = " , ",
84  const std::string &suf = ">");
85 
86  static std::string vpCannyBackendTypeToString(const vpCannyBackendType &type);
87 
88  static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name);
89 
92  {
93  CANNY_GBLUR_SOBEL_FILTERING = 0,
94  CANNY_GBLUR_SCHARR_FILTERING = 1,
95  CANNY_COUNT_FILTERING = 2
96  } vpCannyFilteringAndGradientType;
97 
98  static std::string vpGetCannyFiltAndGradTypes(const std::string &pref = "<", const std::string &sep = " , ",
99  const std::string &suf = ">");
100 
101  static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type);
102 
103  static vpCannyFilteringAndGradientType vpCannyFiltAndGradTypeFromStr(const std::string &name);
104 
105  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
106  const float &thresholdCanny, const unsigned int &apertureSobel);
107 
108  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
109  const float &lowerThresholdCanny, const float &higherThresholdCanny,
110  const unsigned int &apertureSobel);
111 
112  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
113  const float &lowerThresholdCanny, const float &higherThresholdCanny,
114  const unsigned int &apertureSobel, const float &gaussianStdev, const float &lowerThresholdRatio,
115  const float &upperThresholdRatio, const bool &normalizeGradients,
116  const vpCannyBackendType &cannyBackend, const vpCannyFilteringAndGradientType &cannyFilteringSteps,
117  const vpImage<bool> *p_mask = nullptr);
118 
119 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
120  static float computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy,
121  float &lowerThresh, const unsigned int &gaussianKernelSize = 5,
122  const float &gaussianStdev = 2.f, const unsigned int &apertureGradient = 3,
123  const float &lowerThresholdRatio = 0.6, const float &upperThresholdRatio = 0.8,
124  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
125 
126  static void computePartialDerivatives(const cv::Mat &cv_I,
127  cv::Mat &cv_dIx, cv::Mat &cv_dIy,
128  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
129  const unsigned int &gaussianKernelSize = 5, const float &gaussianStdev = 2.f,
130  const unsigned int &apertureGradient = 3,
131  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
132 #endif
133 
154  template <typename ImageType, typename FilterType>
155  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
157  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
158  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
159  const unsigned int &apertureGradient = 3,
160  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
161  const vpCannyBackendType &backend = CANNY_VISP_BACKEND,
162  const vpImage<bool> *p_mask = nullptr)
163  {
164  if (backend == CANNY_OPENCV_BACKEND) {
165 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
166  cv::Mat cv_I, cv_dIx, cv_dIy;
167  vpImageConvert::convert(I, cv_I);
168  computePartialDerivatives(cv_I, cv_dIx, cv_dIy, computeDx, computeDy, normalize, gaussianKernelSize,
169  static_cast<float>(gaussianStdev), apertureGradient, filteringType);
170  if (computeDx) {
171  vpImageConvert::convert(cv_dIx, dIx);
172  }
173  if (computeDy) {
174  vpImageConvert::convert(cv_dIy, dIy);
175  }
176 #else
177  throw(vpException(vpException::badValue, "You need to compile ViSP with OpenCV to use CANNY_OPENCV_BACKEND"));
178 #endif
179  }
180  else {
181  if ((filteringType == CANNY_GBLUR_SCHARR_FILTERING) || (filteringType == CANNY_GBLUR_SOBEL_FILTERING)) {
182  dIx.resize(I.getHeight(), I.getWidth());
183  dIy.resize(I.getHeight(), I.getWidth());
184 
185  // Computing the Gaussian blur + gradients of the image
186  vpImage<FilterType> Iblur;
187  vpImageFilter::gaussianBlur(I, Iblur, gaussianKernelSize, gaussianStdev, true, p_mask);
188 
189  vpArray2D<FilterType> gradientFilterX(apertureGradient, apertureGradient); // Gradient filter along the X-axis
190  vpArray2D<FilterType> gradientFilterY(apertureGradient, apertureGradient); // Gradient filter along the Y-axis
191 
192 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
193  // Helper to apply the scale to the raw values of the filters
194  auto scaleFilter = [](vpArray2D<FilterType> &filter, const float &scale) {
195  const unsigned int nbRows = filter.getRows();
196  const unsigned int nbCols = filter.getCols();
197  for (unsigned int r = 0; r < nbRows; ++r) {
198  for (unsigned int c = 0; c < nbCols; ++c) {
199  filter[r][c] = filter[r][c] * scale;
200  }
201  }
202  };
203 #endif
204 
205  // Scales to apply to the filters to get a normalized gradient filter that gives a gradient
206  // between 0 and 255 for an vpImage<uchar>
207  float scaleX = 1.f;
208  float scaleY = 1.f;
209  const unsigned int val2 = 2U;
210 
211  if (filteringType == CANNY_GBLUR_SOBEL_FILTERING) {
212  if (computeDx) {
213  scaleX = static_cast<float>(vpImageFilter::getSobelKernelX(gradientFilterX.data, (apertureGradient - 1) / val2));
214  }
215  if (computeDy) {
216  scaleY = static_cast<float>(vpImageFilter::getSobelKernelY(gradientFilterY.data, (apertureGradient - 1) / val2));
217  }
218  }
219  else if (filteringType == CANNY_GBLUR_SCHARR_FILTERING) {
220  if (computeDx) {
221  scaleX = static_cast<float>(vpImageFilter::getScharrKernelX(gradientFilterX.data, (apertureGradient - 1) / val2));
222  }
223  if (computeDy) {
224  scaleY = static_cast<float>(vpImageFilter::getScharrKernelY(gradientFilterY.data, (apertureGradient - 1) / val2));
225  }
226  }
227 
228  // Scale the gradient filters to have a normalized gradient filter
229  if (normalize) {
230  if (computeDx) {
231  scaleFilter(gradientFilterX, scaleX);
232  }
233  if (computeDy) {
234  scaleFilter(gradientFilterY, scaleY);
235  }
236  }
237 
238  // Apply the gradient filters to get the gradients
239  if (computeDx) {
240  vpImageFilter::filter(Iblur, dIx, gradientFilterX, true, p_mask);
241  }
242 
243  if (computeDy) {
244  vpImageFilter::filter(Iblur, dIy, gradientFilterY, true, p_mask);
245  }
246  }
247  else {
248  std::string errMsg = "[vpImageFilter::computePartialDerivatives] Filtering + gradient method \"";
249  errMsg += vpCannyFiltAndGradTypeToStr(filteringType);
250  errMsg += "\" is not implemented yet\n";
252  }
253  }
254  }
255 
256 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
257  template <typename FilterType>
258  inline static void computePartialDerivatives(const vpImage<vpRGBa> &I,
260  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
261  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
262  const unsigned int &apertureGradient = 3,
263  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
264  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
265 
266  template <typename ImageType>
267  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
269  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
270  const unsigned int &gaussianKernelSize = 5, const unsigned char &gaussianStdev = 2.f,
271  const unsigned int &apertureGradient = 3,
272  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
273  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
274 
275  template <typename ImageType>
276  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
277  vpImage<vpRGBa> &dIx, vpImage<vpRGBa> &dIy,
278  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
279  const unsigned int gaussianKernelSize = 5, const vpRGBa gaussianStdev = vpRGBa(),
280  const unsigned int apertureGradient = 3,
281  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
282  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
283 #else
284  template <typename FilterType>
285  inline static void computePartialDerivatives(const vpImage<vpRGBa> &I,
287  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
288  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
289  const unsigned int &apertureGradient = 3,
290  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
291  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
292 
293  template <typename ImageType>
294  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
296  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
297  const unsigned int &gaussianKernelSize = 5, const unsigned char &gaussianStdev = 2.f,
298  const unsigned int &apertureGradient = 3,
299  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
300  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
301 
302  template <typename ImageType>
303  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
304  vpImage<vpRGBa> &dIx, vpImage<vpRGBa> &dIy,
305  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
306  const unsigned int gaussianKernelSize = 5, const vpRGBa gaussianStdev = vpRGBa(),
307  const unsigned int apertureGradient = 3,
308  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
309  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
310 #endif
311 
332  template<typename OutType>
333  inline static float computeCannyThreshold(const vpImage<unsigned char> &I, float &lowerThresh,
334  const vpImage<OutType> *p_dIx = nullptr, const vpImage<OutType> *p_dIy = nullptr,
335  const unsigned int &gaussianKernelSize = 5,
336  const OutType &gaussianStdev = 2.f, const unsigned int &apertureGradient = 3,
337  const float &lowerThresholdRatio = 0.6f, const float &upperThresholdRatio = 0.8f,
338  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
339  const vpImage<bool> *p_mask = nullptr)
340  {
341  const unsigned int w = I.getWidth();
342  const unsigned int h = I.getHeight();
343 
344  if ((lowerThresholdRatio <= 0.f) || (lowerThresholdRatio >= 1.f)) {
345  std::stringstream errMsg;
346  errMsg << "Lower ratio (" << lowerThresholdRatio << ") " << (lowerThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
347  throw(vpException(vpException::fatalError, errMsg.str()));
348  }
349 
350  if ((upperThresholdRatio <= 0.f) || (upperThresholdRatio >= 1.f)) {
351  std::stringstream errMsg;
352  errMsg << "Upper ratio (" << upperThresholdRatio << ") " << (upperThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
353  throw(vpException(vpException::fatalError, errMsg.str()));
354  }
355 
356  if (lowerThresholdRatio >= upperThresholdRatio) {
357  std::stringstream errMsg;
358  errMsg << "Lower ratio (" << lowerThresholdRatio << ") should be lower than the upper ratio (" << upperThresholdRatio << ")";
359  throw(vpException(vpException::fatalError, errMsg.str()));
360  }
361 
362  vpImage<unsigned char> dI(h, w);
363  vpImage<OutType> dIx(h, w), dIy(h, w);
364  if ((p_dIx != nullptr) && (p_dIy != nullptr)) {
365  dIx = *p_dIx;
366  dIy = *p_dIy;
367  }
368  else {
369  computePartialDerivatives(I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev,
370  apertureGradient, filteringType, vpImageFilter::CANNY_VISP_BACKEND, p_mask);
371  }
372 
373  // Computing the absolute gradient of the image G = |dIx| + |dIy|
374  for (unsigned int r = 0; r < h; ++r) {
375  for (unsigned int c = 0; c < w; ++c) {
376  // We have to compute the value for each pixel if we don't have a mask or for
377  // pixels for which the mask is true otherwise
378  bool computeVal = checkBooleanMask(p_mask, r, c);
379 
380  if (computeVal) {
381  float dx = static_cast<float>(dIx[r][c]);
382  float dy = static_cast<float>(dIy[r][c]);
383  float gradient = std::abs(dx) + std::abs(dy);
384  float gradientClamped = std::min<float>(gradient, static_cast<float>(std::numeric_limits<unsigned char>::max()));
385  dI[r][c] = static_cast<unsigned char>(gradientClamped);
386  }
387  }
388  }
389 
390  // Compute the histogram
391  vpHistogram hist;
392  hist.setMask(p_mask);
393  const unsigned int nbBins = 256;
394  hist.calculate(dI, nbBins);
395  float totalNbPixels = static_cast<float>(hist.getTotal());
396  float accu = 0;
397  float t = upperThresholdRatio * totalNbPixels;
398  float bon = 0;
399  unsigned int i = 0;
400  bool notFound = true;
401  while ((i < nbBins) && notFound) {
402  float tf = static_cast<float>(hist[i]);
403  accu = accu + tf;
404  if (accu > t) {
405  bon = static_cast<float>(i);
406  notFound = false;
407  }
408  ++i;
409  }
410  if (notFound) {
411  std::stringstream errMsg;
412  errMsg << "Could not find a bin for which " << upperThresholdRatio * 100.f << " percents of the pixels had a gradient lower than the upper threshold.";
413  throw(vpException(vpException::fatalError, errMsg.str()));
414  }
415  float upperThresh = std::max<float>(bon, 1.f);
416  lowerThresh = lowerThresholdRatio * bon;
417  lowerThresh = std::max<float>(lowerThresh, std::numeric_limits<float>::epsilon());
418  return upperThresh;
419  }
420 
428  template <class ImageType> static double derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
429  {
430  const int val1 = 1, val2 = 2, val3 = 3;
431  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])) +
432  (112.0 * static_cast<double>(I[r][c + val3] - I[r][c - val3]))) / 8418.0;
433  }
434 
442  template <class ImageType> static double derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
443  {
444  const int val1 = 1, val2 = 2, val3 = 3;
445  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])) +
446  (112.0 * static_cast<double>(I[r + val3][c] - I[r - val3][c]))) / 8418.0;
447  }
448 
462  template <class ImageType, typename FilterType>
463  static FilterType derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
464  {
465  const unsigned int stop = (size - 1) / 2;
466  unsigned int i;
467  FilterType result;
468 
469  result = 0;
470 
471  for (i = 1; i <= stop; ++i) {
472  result += filter[i] * static_cast<FilterType>(I[r][c + i] - I[r][c - i]);
473  }
474  return result;
475  }
476 
490  template <class ImageType, typename FilterType>
491  static FilterType derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
492  {
493  const unsigned int stop = (size - 1) / 2;
494  unsigned int i;
495  FilterType result;
496 
497  result = 0;
498 
499  for (i = 1; i <= stop; ++i) {
500  result += filter[i] * static_cast<FilterType>(I[r + i][c] - I[r - i][c]);
501  }
502  return result;
503  }
504 
534  template <typename ImageType, typename FilterType>
535  static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false,
536  const vpImage<bool> *p_mask = nullptr)
537  {
538  const unsigned int size_y = M.getRows(), size_x = M.getCols();
539  const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
540 
541  const unsigned int inputHeight = I.getHeight(), inputWidth = I.getWidth();
542  If.resize(inputHeight, inputWidth, 0.0);
543 
544  if (convolve) {
545  const unsigned int stopHeight = inputHeight - half_size_y;
546  const unsigned int stopWidth = inputWidth - half_size_x;
547  for (unsigned int i = half_size_y; i < stopHeight; ++i) {
548  for (unsigned int j = half_size_x; j < stopWidth; ++j) {
549  // We have to compute the value for each pixel if we don't have a mask or for
550  // pixels for which the mask is true otherwise
551  bool computeVal = checkBooleanMask(p_mask, i, j);
552  if (computeVal) {
553  FilterType conv = 0;
554 
555  for (unsigned int a = 0; a < size_y; ++a) {
556  for (unsigned int b = 0; b < size_x; ++b) {
557  FilterType val = static_cast<FilterType>(I[(i + half_size_y) - a][(j + half_size_x) - b]); // Convolution
558  conv += M[a][b] * val;
559  }
560  }
561  If[i][j] = conv;
562  }
563  }
564  }
565  }
566  else {
567  const unsigned int stopHeight = inputHeight - half_size_y;
568  const unsigned int stopWidth = inputWidth - half_size_x;
569  for (unsigned int i = half_size_y; i < stopHeight; ++i) {
570  for (unsigned int j = half_size_x; j < stopWidth; ++j) {
571  // We have to compute the value for each pixel if we don't have a mask or for
572  // pixels for which the mask is true otherwise
573  bool computeVal = checkBooleanMask(p_mask, i, j);
574  if (computeVal) {
575  FilterType corr = 0;
576 
577  for (unsigned int a = 0; a < size_y; ++a) {
578  for (unsigned int b = 0; b < size_x; ++b) {
579  FilterType val = static_cast<FilterType>(I[(i - half_size_y) + a][(j - half_size_x) + b]); // Correlation
580  corr += M[a][b] * val;
581  }
582  }
583  If[i][j] = corr;
584  }
585  }
586  }
587  }
588  }
589 
599  template <typename FilterType>
600  static FilterType filter(const vpImage<FilterType> &I, const vpArray2D<FilterType> &M, unsigned int row, unsigned int col)
601  {
602  const unsigned int size_y = M.getRows(), size_x = M.getCols();
603  const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
604  FilterType corr = 0;
605 
606  for (unsigned int a = 0; a < size_y; ++a) {
607  for (unsigned int b = 0; b < size_x; ++b) {
608  FilterType val = static_cast<FilterType>(I[row - half_size_y + a][col - half_size_x + b]); // Correlation
609  corr += M[a][b] * val;
610  }
611  }
612  return corr;
613  }
614 
615 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
616  template <typename FilterType>
617  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false) = delete;
618 #else
619  template <typename FilterType>
620  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false);
621 #endif
622 
636  template <typename ImageType, typename FilterType>
638  bool convolve = false, const vpImage<bool> *p_mask = nullptr)
639  {
640  const unsigned int size = M.getRows();
641  const unsigned int half_size = size / 2;
642  const unsigned int height = I.getHeight(), width = I.getWidth();
643  const unsigned int stopV = height - half_size;
644  const unsigned int stopU = width - half_size;
645 
646  Iu.resize(height, width, 0.0);
647  Iv.resize(height, width, 0.0);
648 
649  if (convolve) {
650  for (unsigned int v = half_size; v < stopV; ++v) {
651  for (unsigned int u = half_size; u < stopU; ++u) {
652  // We have to compute the value for each pixel if we don't have a mask or for
653  // pixels for which the mask is true otherwise
654  bool computeVal = checkBooleanMask(p_mask, v, u);
655  if (computeVal) {
656  FilterType conv_u = 0;
657  FilterType conv_v = 0;
658 
659  for (unsigned int a = 0; a < size; ++a) {
660  for (unsigned int b = 0; b < size; ++b) {
661  FilterType val = static_cast<FilterType>(I[(v + half_size) - a][(u + half_size) - b]); // Convolution
662  conv_u += M[a][b] * val;
663  conv_v += M[b][a] * val;
664  }
665  }
666  Iu[v][u] = conv_u;
667  Iv[v][u] = conv_v;
668  }
669  }
670  }
671  }
672  else {
673  for (unsigned int v = half_size; v < stopV; ++v) {
674  for (unsigned int u = half_size; u < stopU; ++u) {
675  // We have to compute the value for each pixel if we don't have a mask or for
676  // pixels for which the mask is true otherwise
677  bool computeVal = checkBooleanMask(p_mask, v, u);
678 
679  if (computeVal) {
680  FilterType conv_u = 0;
681  FilterType conv_v = 0;
682 
683  for (unsigned int a = 0; a < size; ++a) {
684  for (unsigned int b = 0; b < size; ++b) {
685  FilterType val = static_cast<FilterType>(I[(v - half_size) + a][(u - half_size) + b]); // Correlation
686  conv_u += M[a][b] * val;
687  conv_v += M[b][a] * val;
688  }
689  }
690  Iu[v][u] = conv_u;
691  Iv[v][u] = conv_v;
692  }
693  }
694  }
695  }
696  }
697 
698 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
699  template<typename FilterType>
700  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve) = delete;
701 
702  template<typename ImageType>
703  static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve) = delete;
704 #else
705  template<typename FilterType>
706  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve);
707 
708  template<typename ImageType>
709  static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve);
710 #endif
711 
712  static void sepFilter(const vpImage<unsigned char> &I, vpImage<double> &If, const vpColVector &kernelH, const vpColVector &kernelV);
713 
723  template <typename ImageType, typename FilterType>
724  static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &GI, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
725  {
727  filterX<ImageType, FilterType>(I, GIx, filter, size, p_mask);
728  filterY<FilterType, FilterType>(GIx, GI, filter, size, p_mask);
729  GIx.destroy();
730  }
731 
732  static inline unsigned char filterGaussXPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
733  {
734  const int val2 = 2;
735  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.);
736  }
737  static inline unsigned char filterGaussYPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
738  {
739  const int val2 = 2;
740  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.);
741  }
742 
743  template <typename ImageType, typename FilterType>
744  static void filterX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size,
745  const vpImage<bool> *p_mask = nullptr)
746  {
747  const unsigned int height = I.getHeight();
748  const unsigned int width = I.getWidth();
749  const unsigned int stop1J = (size - 1) / 2;
750  const unsigned int stop2J = width - ((size - 1) / 2);
751  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
752 
753  for (unsigned int i = 0; i < height; ++i) {
754  for (unsigned int j = 0; j < stop1J; ++j) {
755  // We have to compute the value for each pixel if we don't have a mask or for
756  // pixels for which the mask is true otherwise
757  bool computeVal = checkBooleanMask(p_mask, i, j);
758  if (computeVal) {
759  dIx[i][j] = vpImageFilter::filterXLeftBorder<ImageType, FilterType>(I, i, j, filter, size);
760  }
761  }
762  for (unsigned int j = stop1J; j < stop2J; ++j) {
763  // We have to compute the value for each pixel if we don't have a mask or for
764  // pixels for which the mask is true otherwise
765  bool computeVal = checkBooleanMask(p_mask, i, j);
766  if (computeVal) {
767  dIx[i][j] = vpImageFilter::filterX<ImageType, FilterType>(I, i, j, filter, size);
768  }
769  }
770  for (unsigned int j = stop2J; j < width; ++j) {
771  // We have to compute the value for each pixel if we don't have a mask or for
772  // pixels for which the mask is true otherwise
773  bool computeVal = checkBooleanMask(p_mask, i, j);
774  if (computeVal) {
775  dIx[i][j] = vpImageFilter::filterXRightBorder<ImageType, FilterType>(I, i, j, filter, size);
776  }
777  }
778  }
779  }
780 
781  static void filterX(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
782 
783  template<typename ImageType, typename FilterType>
784  static inline FilterType filterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
785  {
786  const unsigned int stop = (size - 1) / 2;
787  FilterType result = static_cast<FilterType>(0.);
788 
789  for (unsigned int i = 1; i <= stop; ++i) {
790  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
791  }
792  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
793  }
794 
795 #ifndef DOXYGEN_SHOULD_SKIP_THIS
796  static void filterXR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
797  static void filterXG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
798  static void filterXB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
799 
800  static double filterXR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
801  static double filterXG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
802  static double filterXB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
803 
804  static double filterXLeftBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
805  static double filterXLeftBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
806  static double filterXLeftBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
807  static double filterXRightBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
808  static double filterXRightBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
809  static double filterXRightBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
810 
811  template <typename ImageType, typename FilterType>
812  static inline FilterType filterXLeftBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
813  const FilterType *filter, unsigned int size)
814  {
815  const unsigned int stop = (size - 1) / 2;
816  FilterType result = static_cast<FilterType>(0.);
817 
818  for (unsigned int i = 1; i <= stop; ++i) {
819  if (c > i) {
820  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
821  }
822  else {
823  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][i - c]);
824  }
825  }
826  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
827  }
828 
829  template <typename ImageType, typename FilterType>
830  static inline FilterType filterXRightBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
831  const FilterType *filter, unsigned int size)
832  {
833  const unsigned int stop = (size - 1) / 2;
834  const unsigned int width = I.getWidth();
835  FilterType result = static_cast<FilterType>(0.);
836  const unsigned int twice = 2;
837 
838  for (unsigned int i = 1; i <= stop; ++i) {
839  if ((c + i) < width) {
840  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
841  }
842  else {
843  result += filter[i] * static_cast<FilterType>(I[r][((twice * width) - c) - i - 1] + I[r][c - i]);
844  }
845  }
846  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
847  }
848 #endif
849 
850  static void filterY(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
851 
852  template<typename ImageType, typename FilterType>
853  static void filterY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size,
854  const vpImage<bool> *p_mask = nullptr)
855  {
856  const unsigned int height = I.getHeight(), width = I.getWidth();
857  const unsigned int stop1I = (size - 1) / 2;
858  const unsigned int stop2I = height - ((size - 1) / 2);
859  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
860 
861  for (unsigned int i = 0; i < stop1I; ++i) {
862  for (unsigned int j = 0; j < width; ++j) {
863  // We have to compute the value for each pixel if we don't have a mask or for
864  // pixels for which the mask is true otherwise
865  bool computeVal = checkBooleanMask(p_mask, i, j);
866  if (computeVal) {
867  dIy[i][j] = vpImageFilter::filterYTopBorder<ImageType, FilterType>(I, i, j, filter, size);
868  }
869  }
870  }
871  for (unsigned int i = stop1I; i < stop2I; ++i) {
872  for (unsigned int j = 0; j < width; ++j) {
873  // We have to compute the value for each pixel if we don't have a mask or for
874  // pixels for which the mask is true otherwise
875  bool computeVal = checkBooleanMask(p_mask, i, j);
876  if (computeVal) {
877  dIy[i][j] = vpImageFilter::filterY<ImageType, FilterType>(I, i, j, filter, size);
878  }
879  }
880  }
881  for (unsigned int i = stop2I; i < height; ++i) {
882  for (unsigned int j = 0; j < width; ++j) {
883  // We have to compute the value for each pixel if we don't have a mask or for
884  // pixels for which the mask is true otherwise
885  bool computeVal = checkBooleanMask(p_mask, i, j);
886  if (computeVal) {
887  dIy[i][j] = vpImageFilter::filterYBottomBorder<ImageType, FilterType>(I, i, j, filter, size);
888  }
889  }
890  }
891  }
892 
893  template<typename ImageType, typename FilterType>
894  static inline FilterType filterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
895  {
896  const unsigned int stop = (size - 1) / 2;
897  FilterType result = static_cast<FilterType>(0.);
898 
899  for (unsigned int i = 1; i <= stop; ++i) {
900  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
901  }
902  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
903  }
904 #ifndef DOXYGEN_SHOULD_SKIP_THIS
905  static void filterYR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
906  static void filterYG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
907  static void filterYB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
908 
909  static double filterYR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
910  static double filterYG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
911  static double filterYB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
912 
913  static double filterYTopBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
914  static double filterYTopBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
915  static double filterYTopBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
916  static double filterYBottomBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
917  static double filterYBottomBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
918  static double filterYBottomBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
919 
920  template<typename ImageType, typename FilterType>
921  static inline FilterType filterYTopBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
922  const FilterType *filter, unsigned int size)
923  {
924  const unsigned int stop = (size - 1) / 2;
925  FilterType result = static_cast<FilterType>(0.);
926 
927  for (unsigned int i = 1; i <= stop; ++i) {
928  if (r > i) {
929  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
930  }
931  else {
932  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[i - r][c]);
933  }
934  }
935  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
936  }
937 
938  template<typename ImageType, typename FilterType>
939  static inline FilterType filterYBottomBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
940  const FilterType *filter, unsigned int size)
941  {
942  const unsigned int height = I.getHeight();
943  const unsigned int stop = (size - 1) / 2;
944  FilterType result = static_cast<FilterType>(0.);
945  const unsigned int twiceHeight = 2 * height;
946  for (unsigned int i = 1; i <= stop; ++i) {
947  if ((r + i) < height) {
948  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
949  }
950  else {
951  result += filter[i] * static_cast<FilterType>(I[(twiceHeight - r) - i - 1][c] + I[r - i][c]);
952  }
953  }
954  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
955  }
956 #endif
957 
958 
972  template <typename ImageType, typename FilterType>
973  static void gaussianBlur(const vpImage<ImageType> &I, vpImage<FilterType> &GI, unsigned int size = 7, FilterType sigma = 0., bool normalize = true,
974  const vpImage<bool> *p_mask = nullptr)
975  {
976  FilterType *fg = new FilterType[(size + 1) / 2];
977  vpImageFilter::getGaussianKernel<FilterType>(fg, size, sigma, normalize);
979  vpImageFilter::filterX<ImageType, FilterType>(I, GIx, fg, size, p_mask);
980  vpImageFilter::filterY<FilterType, FilterType>(GIx, GI, fg, size, p_mask);
981  GIx.destroy();
982  delete[] fg;
983  }
984 
985  static void gaussianBlur(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &GI, unsigned int size = 7, double sigma = 0., bool normalize = true,
986  const vpImage<bool> *p_mask = nullptr);
987 
995  template <class T> static double gaussianFilter(const vpImage<T> &fr, unsigned int r, unsigned int c)
996  {
997  const int val2 = 2;
998  return ((15.0 * fr[r][c]) +
999  (12.0 * (fr[r - 1][c] + fr[r][c - 1] + fr[r + 1][c] + fr[r][c + 1])) +
1000  (9.0 * (fr[r - 1][c - 1] + fr[r + 1][c - 1] + fr[r - 1][c + 1] + fr[r + 1][c + 1])) +
1001  (5.0 * (fr[r - val2][c] + fr[r][c - val2] + fr[r + val2][c] + fr[r][c + val2])) +
1002  (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] +
1003  fr[r + val2][c + 1] + fr[r - 1][c + val2] + fr[r + 1][c + val2])) +
1004  (2.0 * (fr[r - val2][c - val2] + fr[r + val2][c - val2] + fr[r - val2][c + val2] + fr[r + val2][c + val2]))) / 159.0;
1005  }
1006  // Gaussian pyramid operation
1007  static void getGaussPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1008  static void getGaussXPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1009  static void getGaussYPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1010 
1027  template<typename FilterType>
1028  static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1029  {
1030  const unsigned int mod2 = 2;
1031  if ((size % mod2) != 1) {
1032  throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1033  }
1034 
1035  if (sigma <= 0) {
1036  sigma = static_cast<FilterType>((size - 1) / 6.0);
1037  }
1038 
1039  int middle = (static_cast<int>(size) - 1) / 2;
1040  FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(sigma));
1041  FilterType coef1 = static_cast<FilterType>(1. / (sigma * sqrt(2. * M_PI)));
1042  FilterType v_2_sigma2 = static_cast<FilterType>(2. * sigma2);
1043  for (int i = 0; i <= middle; ++i) {
1044  filter[i] = coef1 * static_cast<FilterType>(exp(-(i * i) / v_2_sigma2));
1045  }
1046  if (normalize) {
1047  // renormalization
1048  FilterType sum = 0;
1049  const unsigned int val2 = 2U;
1050  for (int i = 1; i <= middle; ++i) {
1051  sum += val2 * filter[i];
1052  }
1053  sum += filter[0];
1054 
1055  for (int i = 0; i <= middle; ++i) {
1056  filter[i] = filter[i] / sum;
1057  }
1058  }
1059  }
1060 
1075  template <typename FilterType>
1076  static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1077  {
1078  const unsigned int mod2 = 2;
1079  if ((size % mod2) != 1) {
1080  throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1081  }
1082 
1083  if (sigma <= 0) {
1084  sigma = static_cast<FilterType>((size - 1) / 6.0);
1085  }
1086 
1087  const int half = 2;
1088  int middle = (static_cast<int>(size) - 1) / half;
1089  FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(sigma));
1090  FilterType coef_1 = static_cast<FilterType>(1. / (sigma * sqrt(2. * M_PI)));
1091  FilterType coef_1_over_2 = coef_1 / static_cast<FilterType>(2.);
1092  FilterType v_2_coef_1 = static_cast<FilterType>(2.) * coef_1;
1093  FilterType v_2_sigma2 = static_cast<FilterType>(2. * sigma2);
1094  filter[0] = 0.;
1095  for (int i = 1; i <= middle; ++i) {
1096  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)));
1097  }
1098 
1099  if (normalize) {
1100  FilterType sum = 0;
1101  for (int i = 1; i <= middle; ++i) {
1102  sum += v_2_coef_1 * static_cast<FilterType>(exp(-(i * i) / v_2_sigma2));
1103  }
1104  sum += coef_1;
1105 
1106  for (int i = 1; i <= middle; ++i) {
1107  filter[i] = filter[i] / sum;
1108  }
1109  }
1110  }
1111 
1112  // Gradient along X
1113  template<typename FilterType>
1114  static void getGradX(const vpImage<unsigned char> &I, vpImage<FilterType> &dIx, const vpImage<bool> *p_mask = nullptr)
1115  {
1116  const unsigned int height = I.getHeight(), width = I.getWidth();
1117  const unsigned int stopJ = width - 3;
1118  const unsigned int val_3 = 3;
1119  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1120 
1121  for (unsigned int i = 0; i < height; ++i) {
1122  for (unsigned int j = 0; j < val_3; ++j) {
1123  // If a mask is used, the image is already initialized with 0s
1124  bool computeVal = (p_mask == nullptr);
1125  if (computeVal) {
1126  dIx[i][j] = static_cast<FilterType>(0);
1127  }
1128  }
1129  for (unsigned int j = 3; j < stopJ; ++j) {
1130  // We have to compute the value for each pixel if we don't have a mask or for
1131  // pixels for which the mask is true otherwise
1132  bool computeVal = checkBooleanMask(p_mask, i, j);
1133  if (computeVal) {
1134  dIx[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterX(I, i, j));
1135  }
1136  }
1137  for (unsigned int j = stopJ; j < width; ++j) {
1138  // If a mask is used, the image is already initialized with 0s
1139  bool computeVal = (p_mask == nullptr);
1140  if (computeVal) {
1141  dIx[i][j] = static_cast<FilterType>(0);
1142  }
1143  }
1144  }
1145  }
1146 
1147  template <typename ImageType, typename FilterType>
1148  static void getGradX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1149  {
1150  const unsigned int height = I.getHeight(), width = I.getWidth();
1151  const unsigned int stop1J = (size - 1) / 2;
1152  const unsigned int stop2J = width - ((size - 1) / 2);
1153  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1154 
1155  for (unsigned int i = 0; i < height; ++i) {
1156  for (unsigned int j = 0; j < stop1J; ++j) {
1157  // If a mask is used, the image is already initialized with 0s
1158  bool computeVal = (p_mask == nullptr);
1159  if (computeVal) {
1160  dIx[i][j] = static_cast<FilterType>(0);
1161  }
1162  }
1163  for (unsigned int j = stop1J; j < stop2J; ++j) {
1164  // We have to compute the value for each pixel if we don't have a mask or for
1165  // pixels for which the mask is true otherwise
1166  bool computeVal = checkBooleanMask(p_mask, i, j);
1167  if (computeVal) {
1168  dIx[i][j] = vpImageFilter::derivativeFilterX<ImageType, FilterType>(I, i, j, filter, size);
1169  }
1170  }
1171  for (unsigned int j = stop2J; j < width; ++j) {
1172  // If a mask is used, the image is already initialized with 0s
1173  bool computeVal = (p_mask == nullptr);
1174  if (computeVal) {
1175  dIx[i][j] = static_cast<FilterType>(0);
1176  }
1177  }
1178  }
1179  }
1180 
1192  template <typename ImageType, typename FilterType>
1193  static void getGradXGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *gaussianKernel,
1194  const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1195  {
1196  vpImage<FilterType> GIy;
1197  vpImageFilter::filterY<ImageType, FilterType>(I, GIy, gaussianKernel, size, p_mask);
1198  vpImageFilter::getGradX<FilterType, FilterType>(GIy, dIx, gaussianDerivativeKernel, size, p_mask);
1199  }
1200 
1201  // Gradient along Y
1202  template <typename FilterType>
1203  static void getGradY(const vpImage<unsigned char> &I, vpImage<FilterType> &dIy, const vpImage<bool> *p_mask = nullptr)
1204  {
1205  const unsigned int height = I.getHeight(), width = I.getWidth();
1206  const unsigned int stopI = height - 3;
1207  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1208  const unsigned int val_3 = 3;
1209  for (unsigned int i = 0; i < val_3; ++i) {
1210  for (unsigned int j = 0; j < width; ++j) {
1211  // We have to compute the value for each pixel if we don't have a mask or for
1212  // pixels for which the mask is true otherwise
1213  bool computeVal = checkBooleanMask(p_mask, i, j);
1214  if (computeVal) {
1215  dIy[i][j] = static_cast<FilterType>(0);
1216  }
1217  }
1218  }
1219  for (unsigned int i = 3; i < stopI; ++i) {
1220  for (unsigned int j = 0; j < width; ++j) {
1221  // We have to compute the value for each pixel if we don't have a mask or for
1222  // pixels for which the mask is true otherwise
1223  bool computeVal = checkBooleanMask(p_mask, i, j);
1224  if (computeVal) {
1225  dIy[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterY(I, i, j));
1226  }
1227  }
1228  }
1229  for (unsigned int i = stopI; i < height; ++i) {
1230  for (unsigned int j = 0; j < width; ++j) {
1231  // We have to compute the value for each pixel if we don't have a mask or for
1232  // pixels for which the mask is true otherwise
1233  bool computeVal = checkBooleanMask(p_mask, i, j);
1234  if (computeVal) {
1235  dIy[i][j] = static_cast<FilterType>(0);
1236  }
1237  }
1238  }
1239  }
1240 
1241  template <typename ImageType, typename FilterType>
1242  static void getGradY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1243  {
1244  const unsigned int height = I.getHeight(), width = I.getWidth();
1245  const unsigned int stop1I = (size - 1) / 2;
1246  const unsigned int stop2I = height - ((size - 1) / 2);
1247  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1248 
1249  for (unsigned int i = 0; i < stop1I; ++i) {
1250  for (unsigned int j = 0; j < width; ++j) {
1251  // We have to compute the value for each pixel if we don't have a mask or for
1252  // pixels for which the mask is true otherwise
1253  bool computeVal = checkBooleanMask(p_mask, i, j);
1254  if (computeVal) {
1255  dIy[i][j] = static_cast<FilterType>(0);
1256  }
1257  }
1258  }
1259  for (unsigned int i = stop1I; i < stop2I; ++i) {
1260  for (unsigned int j = 0; j < width; ++j) {
1261  // We have to compute the value for each pixel if we don't have a mask or for
1262  // pixels for which the mask is true otherwise
1263  bool computeVal = checkBooleanMask(p_mask, i, j);
1264  if (computeVal) {
1265  dIy[i][j] = vpImageFilter::derivativeFilterY<ImageType, FilterType>(I, i, j, filter, size);
1266  }
1267  }
1268  }
1269  for (unsigned int i = stop2I; i < height; ++i) {
1270  for (unsigned int j = 0; j < width; ++j) {
1271  // We have to compute the value for each pixel if we don't have a mask or for
1272  // pixels for which the mask is true otherwise
1273  bool computeVal = checkBooleanMask(p_mask, i, j);
1274  if (computeVal) {
1275  dIy[i][j] = static_cast<FilterType>(0);
1276  }
1277  }
1278  }
1279  }
1280 
1292  template <typename ImageType, typename FilterType>
1293  static void getGradYGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *gaussianKernel,
1294  const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1295  {
1296  vpImage<FilterType> GIx;
1297  vpImageFilter::filterX<ImageType, FilterType>(I, GIx, gaussianKernel, size, p_mask);
1298  vpImageFilter::getGradY<FilterType, FilterType>(GIx, dIy, gaussianDerivativeKernel, size, p_mask);
1299  }
1300 
1308  template <typename FilterType>
1309  inline static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
1310  {
1311  const unsigned int actualKernelSize = (size * 2) + 1;
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 " << actualKernelSize << " != 3";
1316  throw vpException(vpException::dimensionError, errMsg.str());
1317  }
1318 
1319  vpArray2D<FilterType> ScharrY(actualKernelSize, actualKernelSize);
1320  FilterType norm = getScharrKernelY<FilterType>(ScharrY.data, size);
1321  memcpy(filter, ScharrY.t().data, ScharrY.getRows() * ScharrY.getCols() * sizeof(FilterType));
1322  return norm;
1323  }
1324 
1332  template <typename FilterType>
1333  inline static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
1334  {
1335  // Scharr kernel pre-computed for the usual size
1336  static const FilterType ScharrY3x3[9] = { -3.0, -10.0, -3.0, 0.0, 0.0, 0.0, 3.0, 10.0, 3.0 };
1337 
1338  if (size != 1) {
1339  // Size = 1 => kernel_size = 2*1 + 1 = 3
1340  std::stringstream errMsg;
1341  errMsg << "Cannot get Scharr kernel of size " << ((size * 2) + 1) << " != 3";
1342  throw vpException(vpException::dimensionError, errMsg.str());
1343  }
1344 
1345  const unsigned int kernel_size = (size * 2) + 1;
1346  const unsigned int kernel3 = 3;
1347  if (kernel_size == kernel3) {
1348  memcpy(filter, ScharrY3x3, kernel_size * kernel_size * sizeof(FilterType));
1349  return static_cast<FilterType>(1.0 / 32.0);
1350  }
1351 
1352  return static_cast<FilterType>(0.);
1353  }
1354 
1362  template <typename FilterType>
1363  inline static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
1364  {
1365  const unsigned int maxSize = 20;
1366  if (size == 0) {
1367  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
1368  }
1369  if (size > maxSize) {
1370  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
1371  }
1372 
1373  const unsigned int kernel_size = (size * 2) + 1;
1374  vpArray2D<FilterType> SobelY(kernel_size, kernel_size);
1375  FilterType norm = getSobelKernelY<FilterType>(SobelY.data, size);
1376  memcpy(filter, SobelY.t().data, SobelY.getRows() * SobelY.getCols() * sizeof(FilterType));
1377  return norm;
1378  }
1379 
1387  template <typename FilterType>
1388  inline static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
1389  {
1390  // Sobel kernel pre-computed for the usual size
1391  static const FilterType SobelY3x3[9] = { -1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0 };
1392  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,
1393  0.0, 0.0, 2.0, 8.0, 12.0, 8.0, 2.0, 1.0, 4.0, 6.0, 4.0, 1.0 };
1394  static const FilterType SobelY7x7[49] = { -1, -6, -15, -20, -15, -6, -1, -4, -24, -60, -80, -60, -24, -4, -5, -30, -75,
1395  -100, -75, -30, -5, 0, 0, 0, 0, 0, 0, 0, 5, 30, 75, 100, 75, 30,
1396  5, 4, 24, 60, 80, 60, 24, 4, 1, 6, 15, 20, 15, 6, 1 };
1397  const vpArray2D<FilterType> smoothingKernel(3, 3);
1398  const unsigned int index_0 = 0;
1399  const unsigned int index_1 = 1;
1400  const unsigned int index_2 = 2;
1401  smoothingKernel[index_0][index_0] = 1.0;
1402  smoothingKernel[index_0][index_1] = 2.0;
1403  smoothingKernel[index_0][index_2] = 1.0;
1404  smoothingKernel[index_1][index_0] = 2.0;
1405  smoothingKernel[index_1][index_1] = 4.0;
1406  smoothingKernel[index_1][index_2] = 2.0;
1407  smoothingKernel[index_2][index_0] = 1.0;
1408  smoothingKernel[index_2][index_1] = 2.0;
1409  smoothingKernel[index_2][index_2] = 1.0;
1410 
1411  const unsigned int maxSize = 20;
1412  if (size == 0) {
1413  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
1414  }
1415  if (size > maxSize) {
1416  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
1417  }
1418 
1419  const unsigned int kernel_size = (size * 2) + 1;
1420  FilterType scale = static_cast<FilterType>(1. / 8.); // Scale to normalize Sobel3x3
1421  const unsigned int kernel3 = 3, kernel5 = 5, kernel7 = 7;
1422  if (kernel_size == kernel3) {
1423  memcpy(filter, SobelY3x3, kernel_size * kernel_size * sizeof(FilterType));
1424  return scale;
1425  }
1426  scale *= static_cast<FilterType>(1. / 16.); // Sobel5x5 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel3x3
1427  if (kernel_size == kernel5) {
1428  memcpy(filter, SobelY5x5, kernel_size * kernel_size * sizeof(FilterType));
1429  return scale;
1430  }
1431  scale *= static_cast<FilterType>(1. / 16.); // Sobel7x7 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel5x5
1432  if (kernel_size == kernel7) {
1433  memcpy(filter, SobelY7x7, kernel_size * kernel_size * sizeof(FilterType));
1434  return scale;
1435  }
1436 
1437  vpArray2D<FilterType> sobelY(7, 7);
1438  memcpy(sobelY.data, SobelY7x7, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
1439  for (unsigned int i = 4; i <= size; ++i) {
1440  sobelY = vpArray2D<FilterType>::conv2(sobelY, smoothingKernel, "full");
1441  // Sobel(N+1)x(N+1) is the convolution of smoothingKernel, which needs 1/16 scale factor, with SobelNxN
1442  scale *= static_cast<FilterType>(1. / 16.);
1443  }
1444 
1445  memcpy(filter, sobelY.data, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
1446 
1447  return scale;
1448  }
1449 
1450 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
1451  static float median(const cv::Mat &cv_I);
1452  static float median(const vpImage<unsigned char> &Isrc);
1453  static std::vector<float> median(const vpImage<vpRGBa> &Isrc);
1454 #endif
1455 
1456 private:
1467  template<typename ImageType>
1468  static void resizeAndInitializeIfNeeded(const vpImage<bool> *p_mask, const unsigned int height, const unsigned int width, vpImage<ImageType> &I)
1469  {
1470  if (p_mask == nullptr) {
1471  // Just need to resize the output image, values will be computed and overwrite what is inside the image
1472  I.resize(height, width);
1473  }
1474  else {
1475  // Need to reset the image because some points will not be computed
1476  I.resize(height, width, static_cast<ImageType>(0));
1477  }
1478  }
1479 
1489  static bool checkBooleanMask(const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c)
1490  {
1491  bool computeVal = true;
1492 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
1493  if (p_mask != nullptr)
1494 #else
1495  if (p_mask != NULL)
1496 #endif
1497  {
1498  computeVal = (*p_mask)[r][c];
1499  }
1500  return computeVal;
1501  }
1502 
1503 // Note that on ubuntu 12.04 __cplusplus is equal to 1 that's why in the next line we consider __cplusplus <= 199711L
1504 // and not __cplusplus == 199711L
1505 #if ((__cplusplus <= 199711L) || (defined(_MSVC_LANG) && (_MSVC_LANG == 199711L))) // Check if cxx98
1506  // Helper to apply the scale to the raw values of the filters
1507  template <typename FilterType>
1508  static void scaleFilter(vpArray2D<FilterType> &filter, const float &scale)
1509  {
1510  const unsigned int nbRows = filter.getRows();
1511  const unsigned int nbCols = filter.getCols();
1512  for (unsigned int r = 0; r < nbRows; ++r) {
1513  for (unsigned int c = 0; c < nbCols; ++c) {
1514  filter[r][c] = filter[r][c] * scale;
1515  }
1516  }
1517  }
1518 #endif
1519 
1520 };
1521 END_VISP_NAMESPACE
1522 #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:1177
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition: vpArray2D.h:1166
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
@ fatalError
Fatal error.
Definition: vpException.h:72
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:73
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 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 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.6f, const float &upperThresholdRatio=0.8f, 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 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:92
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:77
@ CANNY_VISP_BACKEND
Use ViSP.
Definition: vpImageFilter.h:79
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:573
unsigned int getWidth() const
Definition: vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:544
unsigned int getHeight() const
Definition: vpImage.h:181
static double sqr(double x)
Definition: vpMath.h:203
Definition: vpRGBa.h:70