Visual Servoing Platform  version 3.6.1 under development (2024-12-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 #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.6f, const float &upperThresholdRatio = 0.8f,
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  if ((lowerThresholdRatio <= 0.f) || (lowerThresholdRatio >= 1.f)) {
343  std::stringstream errMsg;
344  errMsg << "Lower ratio (" << lowerThresholdRatio << ") " << (lowerThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
345  throw(vpException(vpException::fatalError, errMsg.str()));
346  }
347 
348  if ((upperThresholdRatio <= 0.f) || (upperThresholdRatio >= 1.f)) {
349  std::stringstream errMsg;
350  errMsg << "Upper ratio (" << upperThresholdRatio << ") " << (upperThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
351  throw(vpException(vpException::fatalError, errMsg.str()));
352  }
353 
354  if (lowerThresholdRatio >= upperThresholdRatio) {
355  std::stringstream errMsg;
356  errMsg << "Lower ratio (" << lowerThresholdRatio << ") should be lower than the upper ratio (" << upperThresholdRatio << ")";
357  throw(vpException(vpException::fatalError, errMsg.str()));
358  }
359 
360  vpImage<unsigned char> dI(h, w);
361  vpImage<OutType> dIx(h, w), dIy(h, w);
362  if ((p_dIx != nullptr) && (p_dIy != nullptr)) {
363  dIx = *p_dIx;
364  dIy = *p_dIy;
365  }
366  else {
367  computePartialDerivatives(I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev,
368  apertureGradient, filteringType, vpImageFilter::CANNY_VISP_BACKEND, p_mask);
369  }
370 
371  // Computing the absolute gradient of the image G = |dIx| + |dIy|
372  for (unsigned int r = 0; r < h; ++r) {
373  for (unsigned int c = 0; c < w; ++c) {
374  // We have to compute the value for each pixel if we don't have a mask or for
375  // pixels for which the mask is true otherwise
376  bool computeVal = checkBooleanMask(p_mask, r, c);
377 
378  if (computeVal) {
379  float dx = static_cast<float>(dIx[r][c]);
380  float dy = static_cast<float>(dIy[r][c]);
381  float gradient = std::abs(dx) + std::abs(dy);
382  float gradientClamped = std::min<float>(gradient, static_cast<float>(std::numeric_limits<unsigned char>::max()));
383  dI[r][c] = static_cast<unsigned char>(gradientClamped);
384  }
385  }
386  }
387 
388  // Compute the histogram
389  vpHistogram hist;
390  hist.setMask(p_mask);
391  const unsigned int nbBins = 256;
392  hist.calculate(dI, nbBins);
393  float totalNbPixels = static_cast<float>(hist.getTotal());
394  float accu = 0;
395  float t = upperThresholdRatio * totalNbPixels;
396  float bon = 0;
397  unsigned int i = 0;
398  bool notFound = true;
399  while ((i < nbBins) && notFound) {
400  float tf = static_cast<float>(hist[i]);
401  accu = accu + tf;
402  if (accu > t) {
403  bon = static_cast<float>(i);
404  notFound = false;
405  }
406  ++i;
407  }
408  if (notFound) {
409  std::stringstream errMsg;
410  errMsg << "Could not find a bin for which " << upperThresholdRatio * 100.f << " percents of the pixels had a gradient lower than the upper threshold.";
411  throw(vpException(vpException::fatalError, errMsg.str()));
412  }
413  float upperThresh = std::max<float>(bon, 1.f);
414  lowerThresh = lowerThresholdRatio * bon;
415  lowerThresh = std::max<float>(lowerThresh, std::numeric_limits<float>::epsilon());
416  return upperThresh;
417  }
418 
426  template <class ImageType> static double derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
427  {
428  const int val1 = 1, val2 = 2, val3 = 3;
429  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])) +
430  (112.0 * static_cast<double>(I[r][c + val3] - I[r][c - val3]))) / 8418.0;
431  }
432 
440  template <class ImageType> static double derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
441  {
442  const int val1 = 1, val2 = 2, val3 = 3;
443  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])) +
444  (112.0 * static_cast<double>(I[r + val3][c] - I[r - val3][c]))) / 8418.0;
445  }
446 
460  template <class ImageType, typename FilterType>
461  static FilterType derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
462  {
463  const unsigned int stop = (size - 1) / 2;
464  unsigned int i;
465  FilterType result;
466 
467  result = 0;
468 
469  for (i = 1; i <= stop; ++i) {
470  result += filter[i] * static_cast<FilterType>(I[r][c + i] - I[r][c - i]);
471  }
472  return result;
473  }
474 
488  template <class ImageType, typename FilterType>
489  static FilterType derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
490  {
491  const unsigned int stop = (size - 1) / 2;
492  unsigned int i;
493  FilterType result;
494 
495  result = 0;
496 
497  for (i = 1; i <= stop; ++i) {
498  result += filter[i] * static_cast<FilterType>(I[r + i][c] - I[r - i][c]);
499  }
500  return result;
501  }
502 
532  template <typename ImageType, typename FilterType>
533  static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false,
534  const vpImage<bool> *p_mask = nullptr)
535  {
536  const unsigned int size_y = M.getRows(), size_x = M.getCols();
537  const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
538 
539  const unsigned int inputHeight = I.getHeight(), inputWidth = I.getWidth();
540  If.resize(inputHeight, inputWidth, 0.0);
541 
542  if (convolve) {
543  const unsigned int stopHeight = inputHeight - half_size_y;
544  const unsigned int stopWidth = inputWidth - half_size_x;
545  for (unsigned int i = half_size_y; i < stopHeight; ++i) {
546  for (unsigned int j = half_size_x; j < stopWidth; ++j) {
547  // We have to compute the value for each pixel if we don't have a mask or for
548  // pixels for which the mask is true otherwise
549  bool computeVal = checkBooleanMask(p_mask, i, j);
550  if (computeVal) {
551  FilterType conv = 0;
552 
553  for (unsigned int a = 0; a < size_y; ++a) {
554  for (unsigned int b = 0; b < size_x; ++b) {
555  FilterType val = static_cast<FilterType>(I[(i + half_size_y) - a][(j + half_size_x) - b]); // Convolution
556  conv += M[a][b] * val;
557  }
558  }
559  If[i][j] = conv;
560  }
561  }
562  }
563  }
564  else {
565  const unsigned int stopHeight = inputHeight - half_size_y;
566  const unsigned int stopWidth = inputWidth - half_size_x;
567  for (unsigned int i = half_size_y; i < stopHeight; ++i) {
568  for (unsigned int j = half_size_x; j < stopWidth; ++j) {
569  // We have to compute the value for each pixel if we don't have a mask or for
570  // pixels for which the mask is true otherwise
571  bool computeVal = checkBooleanMask(p_mask, i, j);
572  if (computeVal) {
573  FilterType corr = 0;
574 
575  for (unsigned int a = 0; a < size_y; ++a) {
576  for (unsigned int b = 0; b < size_x; ++b) {
577  FilterType val = static_cast<FilterType>(I[(i - half_size_y) + a][(j - half_size_x) + b]); // Correlation
578  corr += M[a][b] * val;
579  }
580  }
581  If[i][j] = corr;
582  }
583  }
584  }
585  }
586  }
587 
597  template <typename FilterType>
598  static FilterType filter(const vpImage<FilterType> &I, const vpArray2D<FilterType> &M, unsigned int row, unsigned int col)
599  {
600  const unsigned int size_y = M.getRows(), size_x = M.getCols();
601  const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
602  FilterType corr = 0;
603 
604  for (unsigned int a = 0; a < size_y; ++a) {
605  for (unsigned int b = 0; b < size_x; ++b) {
606  FilterType val = static_cast<FilterType>(I[row - half_size_y + a][col - half_size_x + b]); // Correlation
607  corr += M[a][b] * val;
608  }
609  }
610  return corr;
611  }
612 
613 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
614  template <typename FilterType>
615  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false) = delete;
616 #else
617  template <typename FilterType>
618  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false);
619 #endif
620 
634  template <typename ImageType, typename FilterType>
636  bool convolve = false, const vpImage<bool> *p_mask = nullptr)
637  {
638  const unsigned int size = M.getRows();
639  const unsigned int half_size = size / 2;
640  const unsigned int height = I.getHeight(), width = I.getWidth();
641  const unsigned int stopV = height - half_size;
642  const unsigned int stopU = width - half_size;
643 
644  Iu.resize(height, width, 0.0);
645  Iv.resize(height, width, 0.0);
646 
647  if (convolve) {
648  for (unsigned int v = half_size; v < stopV; ++v) {
649  for (unsigned int u = half_size; u < stopU; ++u) {
650  // We have to compute the value for each pixel if we don't have a mask or for
651  // pixels for which the mask is true otherwise
652  bool computeVal = checkBooleanMask(p_mask, v, u);
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]); // Convolution
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  else {
671  for (unsigned int v = half_size; v < stopV; ++v) {
672  for (unsigned int u = half_size; u < stopU; ++u) {
673  // We have to compute the value for each pixel if we don't have a mask or for
674  // pixels for which the mask is true otherwise
675  bool computeVal = checkBooleanMask(p_mask, v, u);
676 
677  if (computeVal) {
678  FilterType conv_u = 0;
679  FilterType conv_v = 0;
680 
681  for (unsigned int a = 0; a < size; ++a) {
682  for (unsigned int b = 0; b < size; ++b) {
683  FilterType val = static_cast<FilterType>(I[(v - half_size) + a][(u - half_size) + b]); // Correlation
684  conv_u += M[a][b] * val;
685  conv_v += M[b][a] * val;
686  }
687  }
688  Iu[v][u] = conv_u;
689  Iv[v][u] = conv_v;
690  }
691  }
692  }
693  }
694  }
695 
696 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
697  template<typename FilterType>
698  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve) = delete;
699 
700  template<typename ImageType>
701  static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve) = delete;
702 #else
703  template<typename FilterType>
704  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve);
705 
706  template<typename ImageType>
707  static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve);
708 #endif
709 
710  static void sepFilter(const vpImage<unsigned char> &I, vpImage<double> &If, const vpColVector &kernelH, const vpColVector &kernelV);
711 
721  template <typename ImageType, typename FilterType>
722  static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &GI, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
723  {
725  filterX<ImageType, FilterType>(I, GIx, filter, size, p_mask);
726  filterY<FilterType, FilterType>(GIx, GI, filter, size, p_mask);
727  GIx.destroy();
728  }
729 
730  static inline unsigned char filterGaussXPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
731  {
732  const int val2 = 2;
733  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.);
734  }
735  static inline unsigned char filterGaussYPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
736  {
737  const int val2 = 2;
738  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.);
739  }
740 
741  template <typename ImageType, typename FilterType>
742  static void filterX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size,
743  const vpImage<bool> *p_mask = nullptr)
744  {
745  const unsigned int height = I.getHeight();
746  const unsigned int width = I.getWidth();
747  const unsigned int stop1J = (size - 1) / 2;
748  const unsigned int stop2J = width - ((size - 1) / 2);
749  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
750 
751  for (unsigned int i = 0; i < height; ++i) {
752  for (unsigned int j = 0; j < stop1J; ++j) {
753  // We have to compute the value for each pixel if we don't have a mask or for
754  // pixels for which the mask is true otherwise
755  bool computeVal = checkBooleanMask(p_mask, i, j);
756  if (computeVal) {
757  dIx[i][j] = vpImageFilter::filterXLeftBorder<ImageType, FilterType>(I, i, j, filter, size);
758  }
759  }
760  for (unsigned int j = stop1J; j < stop2J; ++j) {
761  // We have to compute the value for each pixel if we don't have a mask or for
762  // pixels for which the mask is true otherwise
763  bool computeVal = checkBooleanMask(p_mask, i, j);
764  if (computeVal) {
765  dIx[i][j] = vpImageFilter::filterX<ImageType, FilterType>(I, i, j, filter, size);
766  }
767  }
768  for (unsigned int j = stop2J; j < width; ++j) {
769  // We have to compute the value for each pixel if we don't have a mask or for
770  // pixels for which the mask is true otherwise
771  bool computeVal = checkBooleanMask(p_mask, i, j);
772  if (computeVal) {
773  dIx[i][j] = vpImageFilter::filterXRightBorder<ImageType, FilterType>(I, i, j, filter, size);
774  }
775  }
776  }
777  }
778 
779  static void filterX(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
780 
781  template<typename ImageType, typename FilterType>
782  static inline FilterType filterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
783  {
784  const unsigned int stop = (size - 1) / 2;
785  FilterType result = static_cast<FilterType>(0.);
786 
787  for (unsigned int i = 1; i <= stop; ++i) {
788  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
789  }
790  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
791  }
792 
793 #ifndef DOXYGEN_SHOULD_SKIP_THIS
794  static void filterXR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
795  static void filterXG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
796  static void filterXB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
797 
798  static double filterXR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
799  static double filterXG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
800  static double filterXB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
801 
802  static double filterXLeftBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
803  static double filterXLeftBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
804  static double filterXLeftBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
805  static double filterXRightBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
806  static double filterXRightBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
807  static double filterXRightBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
808 
809  template <typename ImageType, typename FilterType>
810  static inline FilterType filterXLeftBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
811  const FilterType *filter, unsigned int size)
812  {
813  const unsigned int stop = (size - 1) / 2;
814  FilterType result = static_cast<FilterType>(0.);
815 
816  for (unsigned int i = 1; i <= stop; ++i) {
817  if (c > i) {
818  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
819  }
820  else {
821  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][i - c]);
822  }
823  }
824  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
825  }
826 
827  template <typename ImageType, typename FilterType>
828  static inline FilterType filterXRightBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
829  const FilterType *filter, unsigned int size)
830  {
831  const unsigned int stop = (size - 1) / 2;
832  const unsigned int width = I.getWidth();
833  FilterType result = static_cast<FilterType>(0.);
834  const unsigned int twice = 2;
835 
836  for (unsigned int i = 1; i <= stop; ++i) {
837  if ((c + i) < width) {
838  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
839  }
840  else {
841  result += filter[i] * static_cast<FilterType>(I[r][((twice * width) - c) - i - 1] + I[r][c - i]);
842  }
843  }
844  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
845  }
846 #endif
847 
848  static void filterY(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
849 
850  template<typename ImageType, typename FilterType>
851  static void filterY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size,
852  const vpImage<bool> *p_mask = nullptr)
853  {
854  const unsigned int height = I.getHeight(), width = I.getWidth();
855  const unsigned int stop1I = (size - 1) / 2;
856  const unsigned int stop2I = height - ((size - 1) / 2);
857  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
858 
859  for (unsigned int i = 0; i < stop1I; ++i) {
860  for (unsigned int j = 0; j < width; ++j) {
861  // We have to compute the value for each pixel if we don't have a mask or for
862  // pixels for which the mask is true otherwise
863  bool computeVal = checkBooleanMask(p_mask, i, j);
864  if (computeVal) {
865  dIy[i][j] = vpImageFilter::filterYTopBorder<ImageType, FilterType>(I, i, j, filter, size);
866  }
867  }
868  }
869  for (unsigned int i = stop1I; i < stop2I; ++i) {
870  for (unsigned int j = 0; j < width; ++j) {
871  // We have to compute the value for each pixel if we don't have a mask or for
872  // pixels for which the mask is true otherwise
873  bool computeVal = checkBooleanMask(p_mask, i, j);
874  if (computeVal) {
875  dIy[i][j] = vpImageFilter::filterY<ImageType, FilterType>(I, i, j, filter, size);
876  }
877  }
878  }
879  for (unsigned int i = stop2I; i < height; ++i) {
880  for (unsigned int j = 0; j < width; ++j) {
881  // We have to compute the value for each pixel if we don't have a mask or for
882  // pixels for which the mask is true otherwise
883  bool computeVal = checkBooleanMask(p_mask, i, j);
884  if (computeVal) {
885  dIy[i][j] = vpImageFilter::filterYBottomBorder<ImageType, FilterType>(I, i, j, filter, size);
886  }
887  }
888  }
889  }
890 
891  template<typename ImageType, typename FilterType>
892  static inline FilterType filterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
893  {
894  const unsigned int stop = (size - 1) / 2;
895  FilterType result = static_cast<FilterType>(0.);
896 
897  for (unsigned int i = 1; i <= stop; ++i) {
898  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
899  }
900  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
901  }
902 #ifndef DOXYGEN_SHOULD_SKIP_THIS
903  static void filterYR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
904  static void filterYG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
905  static void filterYB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
906 
907  static double filterYR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
908  static double filterYG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
909  static double filterYB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
910 
911  static double filterYTopBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
912  static double filterYTopBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
913  static double filterYTopBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
914  static double filterYBottomBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
915  static double filterYBottomBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
916  static double filterYBottomBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
917 
918  template<typename ImageType, typename FilterType>
919  static inline FilterType filterYTopBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
920  const FilterType *filter, unsigned int size)
921  {
922  const unsigned int stop = (size - 1) / 2;
923  FilterType result = static_cast<FilterType>(0.);
924 
925  for (unsigned int i = 1; i <= stop; ++i) {
926  if (r > i) {
927  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
928  }
929  else {
930  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[i - r][c]);
931  }
932  }
933  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
934  }
935 
936  template<typename ImageType, typename FilterType>
937  static inline FilterType filterYBottomBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
938  const FilterType *filter, unsigned int size)
939  {
940  const unsigned int height = I.getHeight();
941  const unsigned int stop = (size - 1) / 2;
942  FilterType result = static_cast<FilterType>(0.);
943  const unsigned int twiceHeight = 2 * height;
944  for (unsigned int i = 1; i <= stop; ++i) {
945  if ((r + i) < height) {
946  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
947  }
948  else {
949  result += filter[i] * static_cast<FilterType>(I[(twiceHeight - r) - i - 1][c] + I[r - i][c]);
950  }
951  }
952  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
953  }
954 #endif
955 
956 
970  template <typename ImageType, typename FilterType>
971  static void gaussianBlur(const vpImage<ImageType> &I, vpImage<FilterType> &GI, unsigned int size = 7, FilterType sigma = 0., bool normalize = true,
972  const vpImage<bool> *p_mask = nullptr)
973  {
974  FilterType *fg = new FilterType[(size + 1) / 2];
975  vpImageFilter::getGaussianKernel<FilterType>(fg, size, sigma, normalize);
977  vpImageFilter::filterX<ImageType, FilterType>(I, GIx, fg, size, p_mask);
978  vpImageFilter::filterY<FilterType, FilterType>(GIx, GI, fg, size, p_mask);
979  GIx.destroy();
980  delete[] fg;
981  }
982 
983  static void gaussianBlur(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &GI, unsigned int size = 7, double sigma = 0., bool normalize = true,
984  const vpImage<bool> *p_mask = nullptr);
985 
993  template <class T> static double gaussianFilter(const vpImage<T> &fr, unsigned int r, unsigned int c)
994  {
995  const int val2 = 2;
996  return ((15.0 * fr[r][c]) +
997  (12.0 * (fr[r - 1][c] + fr[r][c - 1] + fr[r + 1][c] + fr[r][c + 1])) +
998  (9.0 * (fr[r - 1][c - 1] + fr[r + 1][c - 1] + fr[r - 1][c + 1] + fr[r + 1][c + 1])) +
999  (5.0 * (fr[r - val2][c] + fr[r][c - val2] + fr[r + val2][c] + fr[r][c + val2])) +
1000  (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] +
1001  fr[r + val2][c + 1] + fr[r - 1][c + val2] + fr[r + 1][c + val2])) +
1002  (2.0 * (fr[r - val2][c - val2] + fr[r + val2][c - val2] + fr[r - val2][c + val2] + fr[r + val2][c + val2]))) / 159.0;
1003  }
1004  // Gaussian pyramid operation
1005  static void getGaussPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1006  static void getGaussXPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1007  static void getGaussYPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1008 
1025  template<typename FilterType>
1026  static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1027  {
1028  const unsigned int mod2 = 2;
1029  if ((size % mod2) != 1) {
1030  throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1031  }
1032 
1033  if (sigma <= 0) {
1034  sigma = static_cast<FilterType>((size - 1) / 6.0);
1035  }
1036 
1037  int middle = (static_cast<int>(size) - 1) / 2;
1038  FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(sigma));
1039  FilterType coef1 = static_cast<FilterType>(1. / (sigma * sqrt(2. * M_PI)));
1040  FilterType v_2_sigma2 = static_cast<FilterType>(2. * sigma2);
1041  for (int i = 0; i <= middle; ++i) {
1042  filter[i] = coef1 * static_cast<FilterType>(exp(-(i * i) / v_2_sigma2));
1043  }
1044  if (normalize) {
1045  // renormalization
1046  FilterType sum = 0;
1047  const unsigned int val2 = 2U;
1048  for (int i = 1; i <= middle; ++i) {
1049  sum += val2 * filter[i];
1050  }
1051  sum += filter[0];
1052 
1053  for (int i = 0; i <= middle; ++i) {
1054  filter[i] = filter[i] / sum;
1055  }
1056  }
1057  }
1058 
1073  template <typename FilterType>
1074  static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1075  {
1076  const unsigned int mod2 = 2;
1077  if ((size % mod2) != 1) {
1078  throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1079  }
1080 
1081  if (sigma <= 0) {
1082  sigma = static_cast<FilterType>((size - 1) / 6.0);
1083  }
1084 
1085  const int half = 2;
1086  int middle = (static_cast<int>(size) - 1) / half;
1087  FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(sigma));
1088  FilterType coef_1 = static_cast<FilterType>(1. / (sigma * sqrt(2. * M_PI)));
1089  FilterType coef_1_over_2 = coef_1 / static_cast<FilterType>(2.);
1090  FilterType v_2_coef_1 = static_cast<FilterType>(2.) * coef_1;
1091  FilterType v_2_sigma2 = static_cast<FilterType>(2. * sigma2);
1092  filter[0] = 0.;
1093  for (int i = 1; i <= middle; ++i) {
1094  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)));
1095  }
1096 
1097  if (normalize) {
1098  FilterType sum = 0;
1099  for (int i = 1; i <= middle; ++i) {
1100  sum += v_2_coef_1 * static_cast<FilterType>(exp(-(i * i) / v_2_sigma2));
1101  }
1102  sum += coef_1;
1103 
1104  for (int i = 1; i <= middle; ++i) {
1105  filter[i] = filter[i] / sum;
1106  }
1107  }
1108  }
1109 
1110  // Gradient along X
1111  template<typename FilterType>
1112  static void getGradX(const vpImage<unsigned char> &I, vpImage<FilterType> &dIx, const vpImage<bool> *p_mask = nullptr)
1113  {
1114  const unsigned int height = I.getHeight(), width = I.getWidth();
1115  const unsigned int stopJ = width - 3;
1116  const unsigned int val_3 = 3;
1117  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1118 
1119  for (unsigned int i = 0; i < height; ++i) {
1120  for (unsigned int j = 0; j < val_3; ++j) {
1121  // If a mask is used, the image is already initialized with 0s
1122  bool computeVal = (p_mask == nullptr);
1123  if (computeVal) {
1124  dIx[i][j] = static_cast<FilterType>(0);
1125  }
1126  }
1127  for (unsigned int j = 3; j < stopJ; ++j) {
1128  // We have to compute the value for each pixel if we don't have a mask or for
1129  // pixels for which the mask is true otherwise
1130  bool computeVal = checkBooleanMask(p_mask, i, j);
1131  if (computeVal) {
1132  dIx[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterX(I, i, j));
1133  }
1134  }
1135  for (unsigned int j = stopJ; j < width; ++j) {
1136  // If a mask is used, the image is already initialized with 0s
1137  bool computeVal = (p_mask == nullptr);
1138  if (computeVal) {
1139  dIx[i][j] = static_cast<FilterType>(0);
1140  }
1141  }
1142  }
1143  }
1144 
1145  template <typename ImageType, typename FilterType>
1146  static void getGradX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1147  {
1148  const unsigned int height = I.getHeight(), width = I.getWidth();
1149  const unsigned int stop1J = (size - 1) / 2;
1150  const unsigned int stop2J = width - ((size - 1) / 2);
1151  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1152 
1153  for (unsigned int i = 0; i < height; ++i) {
1154  for (unsigned int j = 0; j < stop1J; ++j) {
1155  // If a mask is used, the image is already initialized with 0s
1156  bool computeVal = (p_mask == nullptr);
1157  if (computeVal) {
1158  dIx[i][j] = static_cast<FilterType>(0);
1159  }
1160  }
1161  for (unsigned int j = stop1J; j < stop2J; ++j) {
1162  // We have to compute the value for each pixel if we don't have a mask or for
1163  // pixels for which the mask is true otherwise
1164  bool computeVal = checkBooleanMask(p_mask, i, j);
1165  if (computeVal) {
1166  dIx[i][j] = vpImageFilter::derivativeFilterX<ImageType, FilterType>(I, i, j, filter, size);
1167  }
1168  }
1169  for (unsigned int j = stop2J; j < width; ++j) {
1170  // If a mask is used, the image is already initialized with 0s
1171  bool computeVal = (p_mask == nullptr);
1172  if (computeVal) {
1173  dIx[i][j] = static_cast<FilterType>(0);
1174  }
1175  }
1176  }
1177  }
1178 
1190  template <typename ImageType, typename FilterType>
1191  static void getGradXGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *gaussianKernel,
1192  const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1193  {
1194  vpImage<FilterType> GIy;
1195  vpImageFilter::filterY<ImageType, FilterType>(I, GIy, gaussianKernel, size, p_mask);
1196  vpImageFilter::getGradX<FilterType, FilterType>(GIy, dIx, gaussianDerivativeKernel, size, p_mask);
1197  }
1198 
1199  // Gradient along Y
1200  template <typename FilterType>
1201  static void getGradY(const vpImage<unsigned char> &I, vpImage<FilterType> &dIy, const vpImage<bool> *p_mask = nullptr)
1202  {
1203  const unsigned int height = I.getHeight(), width = I.getWidth();
1204  const unsigned int stopI = height - 3;
1205  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1206  const unsigned int val_3 = 3;
1207  for (unsigned int i = 0; i < val_3; ++i) {
1208  for (unsigned int j = 0; j < width; ++j) {
1209  // We have to compute the value for each pixel if we don't have a mask or for
1210  // pixels for which the mask is true otherwise
1211  bool computeVal = checkBooleanMask(p_mask, i, j);
1212  if (computeVal) {
1213  dIy[i][j] = static_cast<FilterType>(0);
1214  }
1215  }
1216  }
1217  for (unsigned int i = 3; i < stopI; ++i) {
1218  for (unsigned int j = 0; j < width; ++j) {
1219  // We have to compute the value for each pixel if we don't have a mask or for
1220  // pixels for which the mask is true otherwise
1221  bool computeVal = checkBooleanMask(p_mask, i, j);
1222  if (computeVal) {
1223  dIy[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterY(I, i, j));
1224  }
1225  }
1226  }
1227  for (unsigned int i = stopI; i < height; ++i) {
1228  for (unsigned int j = 0; j < width; ++j) {
1229  // We have to compute the value for each pixel if we don't have a mask or for
1230  // pixels for which the mask is true otherwise
1231  bool computeVal = checkBooleanMask(p_mask, i, j);
1232  if (computeVal) {
1233  dIy[i][j] = static_cast<FilterType>(0);
1234  }
1235  }
1236  }
1237  }
1238 
1239  template <typename ImageType, typename FilterType>
1240  static void getGradY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1241  {
1242  const unsigned int height = I.getHeight(), width = I.getWidth();
1243  const unsigned int stop1I = (size - 1) / 2;
1244  const unsigned int stop2I = height - ((size - 1) / 2);
1245  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1246 
1247  for (unsigned int i = 0; i < stop1I; ++i) {
1248  for (unsigned int j = 0; j < width; ++j) {
1249  // We have to compute the value for each pixel if we don't have a mask or for
1250  // pixels for which the mask is true otherwise
1251  bool computeVal = checkBooleanMask(p_mask, i, j);
1252  if (computeVal) {
1253  dIy[i][j] = static_cast<FilterType>(0);
1254  }
1255  }
1256  }
1257  for (unsigned int i = stop1I; i < stop2I; ++i) {
1258  for (unsigned int j = 0; j < width; ++j) {
1259  // We have to compute the value for each pixel if we don't have a mask or for
1260  // pixels for which the mask is true otherwise
1261  bool computeVal = checkBooleanMask(p_mask, i, j);
1262  if (computeVal) {
1263  dIy[i][j] = vpImageFilter::derivativeFilterY<ImageType, FilterType>(I, i, j, filter, size);
1264  }
1265  }
1266  }
1267  for (unsigned int i = stop2I; i < height; ++i) {
1268  for (unsigned int j = 0; j < width; ++j) {
1269  // We have to compute the value for each pixel if we don't have a mask or for
1270  // pixels for which the mask is true otherwise
1271  bool computeVal = checkBooleanMask(p_mask, i, j);
1272  if (computeVal) {
1273  dIy[i][j] = static_cast<FilterType>(0);
1274  }
1275  }
1276  }
1277  }
1278 
1290  template <typename ImageType, typename FilterType>
1291  static void getGradYGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *gaussianKernel,
1292  const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1293  {
1294  vpImage<FilterType> GIx;
1295  vpImageFilter::filterX<ImageType, FilterType>(I, GIx, gaussianKernel, size, p_mask);
1296  vpImageFilter::getGradY<FilterType, FilterType>(GIx, dIy, gaussianDerivativeKernel, size, p_mask);
1297  }
1298 
1306  template <typename FilterType>
1307  inline static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
1308  {
1309  const unsigned int actualKernelSize = (size * 2) + 1;
1310  if (size != 1) {
1311  // Size = 1 => kernel_size = 2*1 + 1 = 3
1312  std::stringstream errMsg;
1313  errMsg << "Cannot get Scharr kernel of size " << actualKernelSize << " != 3";
1314  throw vpException(vpException::dimensionError, errMsg.str());
1315  }
1316 
1317  vpArray2D<FilterType> ScharrY(actualKernelSize, actualKernelSize);
1318  FilterType norm = getScharrKernelY<FilterType>(ScharrY.data, size);
1319  memcpy(filter, ScharrY.t().data, ScharrY.getRows() * ScharrY.getCols() * sizeof(FilterType));
1320  return norm;
1321  }
1322 
1330  template <typename FilterType>
1331  inline static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
1332  {
1333  // Scharr kernel pre-computed for the usual size
1334  static const FilterType ScharrY3x3[9] = { -3.0, -10.0, -3.0, 0.0, 0.0, 0.0, 3.0, 10.0, 3.0 };
1335 
1336  if (size != 1) {
1337  // Size = 1 => kernel_size = 2*1 + 1 = 3
1338  std::stringstream errMsg;
1339  errMsg << "Cannot get Scharr kernel of size " << ((size * 2) + 1) << " != 3";
1340  throw vpException(vpException::dimensionError, errMsg.str());
1341  }
1342 
1343  const unsigned int kernel_size = (size * 2) + 1;
1344  const unsigned int kernel3 = 3;
1345  if (kernel_size == kernel3) {
1346  memcpy(filter, ScharrY3x3, kernel_size * kernel_size * sizeof(FilterType));
1347  return static_cast<FilterType>(1.0 / 32.0);
1348  }
1349 
1350  return static_cast<FilterType>(0.);
1351  }
1352 
1360  template <typename FilterType>
1361  inline static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
1362  {
1363  const unsigned int maxSize = 20;
1364  if (size == 0) {
1365  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
1366  }
1367  if (size > maxSize) {
1368  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
1369  }
1370 
1371  const unsigned int kernel_size = (size * 2) + 1;
1372  vpArray2D<FilterType> SobelY(kernel_size, kernel_size);
1373  FilterType norm = getSobelKernelY<FilterType>(SobelY.data, size);
1374  memcpy(filter, SobelY.t().data, SobelY.getRows() * SobelY.getCols() * sizeof(FilterType));
1375  return norm;
1376  }
1377 
1385  template <typename FilterType>
1386  inline static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
1387  {
1388  // Sobel kernel pre-computed for the usual size
1389  static const FilterType SobelY3x3[9] = { -1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0 };
1390  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,
1391  0.0, 0.0, 2.0, 8.0, 12.0, 8.0, 2.0, 1.0, 4.0, 6.0, 4.0, 1.0 };
1392  static const FilterType SobelY7x7[49] = { -1, -6, -15, -20, -15, -6, -1, -4, -24, -60, -80, -60, -24, -4, -5, -30, -75,
1393  -100, -75, -30, -5, 0, 0, 0, 0, 0, 0, 0, 5, 30, 75, 100, 75, 30,
1394  5, 4, 24, 60, 80, 60, 24, 4, 1, 6, 15, 20, 15, 6, 1 };
1395  const vpArray2D<FilterType> smoothingKernel(3, 3);
1396  const unsigned int index_0 = 0;
1397  const unsigned int index_1 = 1;
1398  const unsigned int index_2 = 2;
1399  smoothingKernel[index_0][index_0] = 1.0;
1400  smoothingKernel[index_0][index_1] = 2.0;
1401  smoothingKernel[index_0][index_2] = 1.0;
1402  smoothingKernel[index_1][index_0] = 2.0;
1403  smoothingKernel[index_1][index_1] = 4.0;
1404  smoothingKernel[index_1][index_2] = 2.0;
1405  smoothingKernel[index_2][index_0] = 1.0;
1406  smoothingKernel[index_2][index_1] = 2.0;
1407  smoothingKernel[index_2][index_2] = 1.0;
1408 
1409  const unsigned int maxSize = 20;
1410  if (size == 0) {
1411  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
1412  }
1413  if (size > maxSize) {
1414  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
1415  }
1416 
1417  const unsigned int kernel_size = (size * 2) + 1;
1418  FilterType scale = static_cast<FilterType>(1. / 8.); // Scale to normalize Sobel3x3
1419  const unsigned int kernel3 = 3, kernel5 = 5, kernel7 = 7;
1420  if (kernel_size == kernel3) {
1421  memcpy(filter, SobelY3x3, kernel_size * kernel_size * sizeof(FilterType));
1422  return scale;
1423  }
1424  scale *= static_cast<FilterType>(1. / 16.); // Sobel5x5 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel3x3
1425  if (kernel_size == kernel5) {
1426  memcpy(filter, SobelY5x5, kernel_size * kernel_size * sizeof(FilterType));
1427  return scale;
1428  }
1429  scale *= static_cast<FilterType>(1. / 16.); // Sobel7x7 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel5x5
1430  if (kernel_size == kernel7) {
1431  memcpy(filter, SobelY7x7, kernel_size * kernel_size * sizeof(FilterType));
1432  return scale;
1433  }
1434 
1435  vpArray2D<FilterType> sobelY(7, 7);
1436  memcpy(sobelY.data, SobelY7x7, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
1437  for (unsigned int i = 4; i <= size; ++i) {
1438  sobelY = vpArray2D<FilterType>::conv2(sobelY, smoothingKernel, "full");
1439  // Sobel(N+1)x(N+1) is the convolution of smoothingKernel, which needs 1/16 scale factor, with SobelNxN
1440  scale *= static_cast<FilterType>(1. / 16.);
1441  }
1442 
1443  memcpy(filter, sobelY.data, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
1444 
1445  return scale;
1446  }
1447 
1448 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
1449  static float median(const cv::Mat &cv_I);
1450  static float median(const vpImage<unsigned char> &Isrc);
1451  static std::vector<float> median(const vpImage<vpRGBa> &Isrc);
1452 #endif
1453 
1454 private:
1465  template<typename ImageType>
1466  static void resizeAndInitializeIfNeeded(const vpImage<bool> *p_mask, const unsigned int height, const unsigned int width, vpImage<ImageType> &I)
1467  {
1468  if (p_mask == nullptr) {
1469  // Just need to resize the output image, values will be computed and overwrite what is inside the image
1470  I.resize(height, width);
1471  }
1472  else {
1473  // Need to reset the image because some points will not be computed
1474  I.resize(height, width, static_cast<ImageType>(0));
1475  }
1476  }
1477 
1487  static bool checkBooleanMask(const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c)
1488  {
1489  bool computeVal = true;
1490 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
1491  if (p_mask != nullptr)
1492 #else
1493  if (p_mask != NULL)
1494 #endif
1495  {
1496  computeVal = (*p_mask)[r][c];
1497  }
1498  return computeVal;
1499  }
1500 
1501 // Note that on ubuntu 12.04 __cplusplus is equal to 1 that's why in the next line we consider __cplusplus <= 199711L
1502 // and not __cplusplus == 199711L
1503 #if ((__cplusplus <= 199711L) || (defined(_MSVC_LANG) && (_MSVC_LANG == 199711L))) // Check if cxx98
1504  // Helper to apply the scale to the raw values of the filters
1505  template <typename FilterType>
1506  static void scaleFilter(vpArray2D<FilterType> &filter, const float &scale)
1507  {
1508  const unsigned int nbRows = filter.getRows();
1509  const unsigned int nbCols = filter.getCols();
1510  for (unsigned int r = 0; r < nbRows; ++r) {
1511  for (unsigned int c = 0; c < nbCols; ++c) {
1512  filter[r][c] = filter[r][c] * scale;
1513  }
1514  }
1515  }
1516 #endif
1517 
1518 };
1519 END_VISP_NAMESPACE
1520 #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: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 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: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: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:65