Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
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 
70 class VISP_EXPORT vpImageFilter
71 {
72 public:
74  typedef enum vpCannyBackendType
75  {
76  CANNY_OPENCV_BACKEND = 0,
77  CANNY_VISP_BACKEND = 1,
78  CANNY_COUNT_BACKEND = 2
79  } vpCannyBackendType;
80 
81  static std::string vpCannyBackendTypeList(const std::string &pref = "<", const std::string &sep = " , ",
82  const std::string &suf = ">");
83 
84  static std::string vpCannyBackendTypeToString(const vpCannyBackendType &type);
85 
86  static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name);
87 
90  {
91  CANNY_GBLUR_SOBEL_FILTERING = 0,
92  CANNY_GBLUR_SCHARR_FILTERING = 1,
93  CANNY_COUNT_FILTERING = 2
94  } vpCannyFilteringAndGradientType;
95 
96  static std::string vpGetCannyFiltAndGradTypes(const std::string &pref = "<", const std::string &sep = " , ",
97  const std::string &suf = ">");
98 
99  static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type);
100 
101  static vpCannyFilteringAndGradientType vpCannyFiltAndGradTypeFromStr(const std::string &name);
102 
103  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
104  const float &thresholdCanny, const unsigned int &apertureSobel);
105 
106  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
107  const float &lowerThresholdCanny, const float &higherThresholdCanny,
108  const unsigned int &apertureSobel);
109 
110  static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
111  const float &lowerThresholdCanny, const float &higherThresholdCanny,
112  const unsigned int &apertureSobel, const float &gaussianStdev, const float &lowerThresholdRatio,
113  const float &upperThresholdRatio, const bool &normalizeGradients,
114  const vpCannyBackendType &cannyBackend, const vpCannyFilteringAndGradientType &cannyFilteringSteps,
115  const vpImage<bool> *p_mask = nullptr);
116 
117 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
118  static float computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy,
119  float &lowerThresh, const unsigned int &gaussianKernelSize = 5,
120  const float &gaussianStdev = 2.f, const unsigned int &apertureGradient = 3,
121  const float &lowerThresholdRatio = 0.6, const float &upperThresholdRatio = 0.8,
122  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
123 
124  static void computePartialDerivatives(const cv::Mat &cv_I,
125  cv::Mat &cv_dIx, cv::Mat &cv_dIy,
126  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
127  const unsigned int &gaussianKernelSize = 5, const float &gaussianStdev = 2.f,
128  const unsigned int &apertureGradient = 3,
129  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
130 #endif
131 
152  template <typename ImageType, typename FilterType>
153  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
155  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
156  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
157  const unsigned int &apertureGradient = 3,
158  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
159  const vpCannyBackendType &backend = CANNY_VISP_BACKEND,
160  const vpImage<bool> *p_mask = nullptr)
161  {
162  if (backend == CANNY_OPENCV_BACKEND) {
163 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
164  cv::Mat cv_I, cv_dIx, cv_dIy;
165  vpImageConvert::convert(I, cv_I);
166  computePartialDerivatives(cv_I, cv_dIx, cv_dIy, computeDx, computeDy, normalize, gaussianKernelSize,
167  static_cast<float>(gaussianStdev), apertureGradient, filteringType);
168  if (computeDx) {
169  vpImageConvert::convert(cv_dIx, dIx);
170  }
171  if (computeDy) {
172  vpImageConvert::convert(cv_dIy, dIy);
173  }
174 #else
175  throw(vpException(vpException::badValue, "You need to compile ViSP with OpenCV to use CANNY_OPENCV_BACKEND"));
176 #endif
177  }
178  else {
179  if ((filteringType == CANNY_GBLUR_SCHARR_FILTERING) || (filteringType == CANNY_GBLUR_SOBEL_FILTERING)) {
180  dIx.resize(I.getHeight(), I.getWidth());
181  dIy.resize(I.getHeight(), I.getWidth());
182 
183  // Computing the Gaussian blur + gradients of the image
184  vpImage<FilterType> Iblur;
185  vpImageFilter::gaussianBlur(I, Iblur, gaussianKernelSize, gaussianStdev, true, p_mask);
186 
187  vpArray2D<FilterType> gradientFilterX(apertureGradient, apertureGradient); // Gradient filter along the X-axis
188  vpArray2D<FilterType> gradientFilterY(apertureGradient, apertureGradient); // Gradient filter along the Y-axis
189 
190 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
191  // Helper to apply the scale to the raw values of the filters
192  auto scaleFilter = [](vpArray2D<FilterType> &filter, const float &scale) {
193  const unsigned int nbRows = filter.getRows();
194  const unsigned int nbCols = filter.getCols();
195  for (unsigned int r = 0; r < nbRows; ++r) {
196  for (unsigned int c = 0; c < nbCols; ++c) {
197  filter[r][c] = filter[r][c] * scale;
198  }
199  }
200  };
201 #endif
202 
203  // Scales to apply to the filters to get a normalized gradient filter that gives a gradient
204  // between 0 and 255 for an vpImage<uchar>
205  float scaleX = 1.f;
206  float scaleY = 1.f;
207  const unsigned int val2 = 2U;
208 
209  if (filteringType == CANNY_GBLUR_SOBEL_FILTERING) {
210  if (computeDx) {
211  scaleX = static_cast<float>(vpImageFilter::getSobelKernelX(gradientFilterX.data, (apertureGradient - 1) / val2));
212  }
213  if (computeDy) {
214  scaleY = static_cast<float>(vpImageFilter::getSobelKernelY(gradientFilterY.data, (apertureGradient - 1) / val2));
215  }
216  }
217  else if (filteringType == CANNY_GBLUR_SCHARR_FILTERING) {
218  if (computeDx) {
219  scaleX = static_cast<float>(vpImageFilter::getScharrKernelX(gradientFilterX.data, (apertureGradient - 1) / val2));
220  }
221  if (computeDy) {
222  scaleY = static_cast<float>(vpImageFilter::getScharrKernelY(gradientFilterY.data, (apertureGradient - 1) / val2));
223  }
224  }
225 
226  // Scale the gradient filters to have a normalized gradient filter
227  if (normalize) {
228  if (computeDx) {
229  scaleFilter(gradientFilterX, scaleX);
230  }
231  if (computeDy) {
232  scaleFilter(gradientFilterY, scaleY);
233  }
234  }
235 
236  // Apply the gradient filters to get the gradients
237  if (computeDx) {
238  vpImageFilter::filter(Iblur, dIx, gradientFilterX, true, p_mask);
239  }
240 
241  if (computeDy) {
242  vpImageFilter::filter(Iblur, dIy, gradientFilterY, true, p_mask);
243  }
244  }
245  else {
246  std::string errMsg = "[vpImageFilter::computePartialDerivatives] Filtering + gradient method \"";
247  errMsg += vpCannyFiltAndGradTypeToStr(filteringType);
248  errMsg += "\" is not implemented yet\n";
250  }
251  }
252  }
253 
254 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
255  template <typename FilterType>
256  inline static void computePartialDerivatives(const vpImage<vpRGBa> &I,
258  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
259  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
260  const unsigned int &apertureGradient = 3,
261  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
262  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
263 
264  template <typename ImageType>
265  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
267  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
268  const unsigned int &gaussianKernelSize = 5, const unsigned char &gaussianStdev = 2.f,
269  const unsigned int &apertureGradient = 3,
270  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
271  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
272 
273  template <typename ImageType>
274  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
275  vpImage<vpRGBa> &dIx, vpImage<vpRGBa> &dIy,
276  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
277  const unsigned int gaussianKernelSize = 5, const vpRGBa gaussianStdev = vpRGBa(),
278  const unsigned int apertureGradient = 3,
279  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
280  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
281 #else
282  template <typename FilterType>
283  inline static void computePartialDerivatives(const vpImage<vpRGBa> &I,
285  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
286  const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
287  const unsigned int &apertureGradient = 3,
288  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
289  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
290 
291  template <typename ImageType>
292  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
294  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
295  const unsigned int &gaussianKernelSize = 5, const unsigned char &gaussianStdev = 2.f,
296  const unsigned int &apertureGradient = 3,
297  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
298  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
299 
300  template <typename ImageType>
301  inline static void computePartialDerivatives(const vpImage<ImageType> &I,
302  vpImage<vpRGBa> &dIx, vpImage<vpRGBa> &dIy,
303  const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
304  const unsigned int gaussianKernelSize = 5, const vpRGBa gaussianStdev = vpRGBa(),
305  const unsigned int apertureGradient = 3,
306  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
307  const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
308 #endif
309 
330  template<typename OutType>
331  inline static float computeCannyThreshold(const vpImage<unsigned char> &I, float &lowerThresh,
332  const vpImage<OutType> *p_dIx = nullptr, const vpImage<OutType> *p_dIy = nullptr,
333  const unsigned int &gaussianKernelSize = 5,
334  const OutType &gaussianStdev = 2.f, const unsigned int &apertureGradient = 3,
335  const float &lowerThresholdRatio = 0.6, const float &upperThresholdRatio = 0.8,
336  const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING,
337  const vpImage<bool> *p_mask = nullptr)
338  {
339  const unsigned int w = I.getWidth();
340  const unsigned int h = I.getHeight();
341 
342  vpImage<unsigned char> dI(h, w);
343  vpImage<OutType> dIx(h, w), dIy(h, w);
344  if ((p_dIx != nullptr) && (p_dIy != nullptr)) {
345  dIx = *p_dIx;
346  dIy = *p_dIy;
347  }
348  else {
349  computePartialDerivatives(I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev,
350  apertureGradient, filteringType, vpImageFilter::CANNY_VISP_BACKEND, p_mask);
351  }
352 
353  // Computing the absolute gradient of the image G = |dIx| + |dIy|
354  for (unsigned int r = 0; r < h; ++r) {
355  for (unsigned int c = 0; c < w; ++c) {
356  // We have to compute the value for each pixel if we don't have a mask or for
357  // pixels for which the mask is true otherwise
358  bool computeVal = checkBooleanMask(p_mask, r, c);
359 
360  if (computeVal) {
361  float dx = static_cast<float>(dIx[r][c]);
362  float dy = static_cast<float>(dIy[r][c]);
363  float gradient = std::abs(dx) + std::abs(dy);
364  float gradientClamped = std::min<float>(gradient, static_cast<float>(std::numeric_limits<unsigned char>::max()));
365  dI[r][c] = static_cast<unsigned char>(gradientClamped);
366  }
367  }
368  }
369 
370  // Compute the histogram
371  vpHistogram hist;
372  hist.setMask(p_mask);
373  const unsigned int nbBins = 256;
374  hist.calculate(dI, nbBins);
375  float totalNbPixels = static_cast<float>(hist.getTotal());
376  float accu = 0;
377  float t = upperThresholdRatio * totalNbPixels;
378  float bon = 0;
379  unsigned int i = 0;
380  bool notFound = true;
381  while ((i < nbBins) && notFound) {
382  float tf = static_cast<float>(hist[i]);
383  accu = accu + tf;
384  if (accu > t) {
385  bon = static_cast<float>(i);
386  notFound = false;
387  }
388  ++i;
389  }
390  float upperThresh = std::max<float>(bon, 1.f);
391  lowerThresh = lowerThresholdRatio * bon;
392  return upperThresh;
393  }
394 
402  template <class ImageType> static double derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
403  {
404  const int val1 = 1, val2 = 2, val3 = 3;
405  return ((2047.0 * static_cast<double>(I[r][c + val1] - I[r][c - val1])) + (913.0 * static_cast<double>(I[r][c + val2] - I[r][c - val2])) +
406  (112.0 * static_cast<double>(I[r][c + val3] - I[r][c - val3]))) / 8418.0;
407  }
408 
416  template <class ImageType> static double derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
417  {
418  const int val1 = 1, val2 = 2, val3 = 3;
419  return ((2047.0 * static_cast<double>(I[r + val1][c] - I[r - val1][c])) + (913.0 * static_cast<double>(I[r + val2][c] - I[r - val2][c])) +
420  (112.0 * static_cast<double>(I[r + val3][c] - I[r - val3][c]))) / 8418.0;
421  }
422 
436  template <class ImageType, typename FilterType>
437  static FilterType derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
438  {
439  const unsigned int stop = (size - 1) / 2;
440  unsigned int i;
441  FilterType result;
442 
443  result = 0;
444 
445  for (i = 1; i <= stop; ++i) {
446  result += filter[i] * static_cast<FilterType>(I[r][c + i] - I[r][c - i]);
447  }
448  return result;
449  }
450 
464  template <class ImageType, typename FilterType>
465  static FilterType derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
466  {
467  const unsigned int stop = (size - 1) / 2;
468  unsigned int i;
469  FilterType result;
470 
471  result = 0;
472 
473  for (i = 1; i <= stop; ++i) {
474  result += filter[i] * static_cast<FilterType>(I[r + i][c] - I[r - i][c]);
475  }
476  return result;
477  }
478 
508  template <typename ImageType, typename FilterType>
509  static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false,
510  const vpImage<bool> *p_mask = nullptr)
511  {
512  const unsigned int size_y = M.getRows(), size_x = M.getCols();
513  const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
514 
515  const unsigned int inputHeight = I.getHeight(), inputWidth = I.getWidth();
516  If.resize(inputHeight, inputWidth, 0.0);
517 
518  if (convolve) {
519  const unsigned int stopHeight = inputHeight - half_size_y;
520  const unsigned int stopWidth = inputWidth - half_size_x;
521  for (unsigned int i = half_size_y; i < stopHeight; ++i) {
522  for (unsigned int j = half_size_x; j < stopWidth; ++j) {
523  // We have to compute the value for each pixel if we don't have a mask or for
524  // pixels for which the mask is true otherwise
525  bool computeVal = checkBooleanMask(p_mask, i, j);
526  if (computeVal) {
527  FilterType conv = 0;
528 
529  for (unsigned int a = 0; a < size_y; ++a) {
530  for (unsigned int b = 0; b < size_x; ++b) {
531  FilterType val = static_cast<FilterType>(I[(i + half_size_y) - a][(j + half_size_x) - b]); // Convolution
532  conv += M[a][b] * val;
533  }
534  }
535  If[i][j] = conv;
536  }
537  }
538  }
539  }
540  else {
541  const unsigned int stopHeight = inputHeight - half_size_y;
542  const unsigned int stopWidth = inputWidth - half_size_x;
543  for (unsigned int i = half_size_y; i < stopHeight; ++i) {
544  for (unsigned int j = half_size_x; j < stopWidth; ++j) {
545  // We have to compute the value for each pixel if we don't have a mask or for
546  // pixels for which the mask is true otherwise
547  bool computeVal = checkBooleanMask(p_mask, i, j);
548  if (computeVal) {
549  FilterType corr = 0;
550 
551  for (unsigned int a = 0; a < size_y; ++a) {
552  for (unsigned int b = 0; b < size_x; ++b) {
553  FilterType val = static_cast<FilterType>(I[(i - half_size_y) + a][(j - half_size_x) + b]); // Correlation
554  corr += M[a][b] * val;
555  }
556  }
557  If[i][j] = corr;
558  }
559  }
560  }
561  }
562  }
563 
564 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
565  template <typename FilterType>
566  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false) = delete;
567 #else
568  template <typename FilterType>
569  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false);
570 #endif
571 
585  template <typename ImageType, typename FilterType>
587  bool convolve = false, const vpImage<bool> *p_mask = nullptr)
588  {
589  const unsigned int size = M.getRows();
590  const unsigned int half_size = size / 2;
591  const unsigned int height = I.getHeight(), width = I.getWidth();
592  const unsigned int stopV = height - half_size;
593  const unsigned int stopU = width - half_size;
594 
595  Iu.resize(height, width, 0.0);
596  Iv.resize(height, width, 0.0);
597 
598  if (convolve) {
599  for (unsigned int v = half_size; v < stopV; ++v) {
600  for (unsigned int u = half_size; u < stopU; ++u) {
601  // We have to compute the value for each pixel if we don't have a mask or for
602  // pixels for which the mask is true otherwise
603  bool computeVal = checkBooleanMask(p_mask, v, u);
604  if (computeVal) {
605  FilterType conv_u = 0;
606  FilterType conv_v = 0;
607 
608  for (unsigned int a = 0; a < size; ++a) {
609  for (unsigned int b = 0; b < size; ++b) {
610  FilterType val = static_cast<FilterType>(I[(v + half_size) - a][(u + half_size) - b]); // Convolution
611  conv_u += M[a][b] * val;
612  conv_v += M[b][a] * val;
613  }
614  }
615  Iu[v][u] = conv_u;
616  Iv[v][u] = conv_v;
617  }
618  }
619  }
620  }
621  else {
622  for (unsigned int v = half_size; v < stopV; ++v) {
623  for (unsigned int u = half_size; u < stopU; ++u) {
624  // We have to compute the value for each pixel if we don't have a mask or for
625  // pixels for which the mask is true otherwise
626  bool computeVal = checkBooleanMask(p_mask, v, u);
627 
628  if (computeVal) {
629  FilterType conv_u = 0;
630  FilterType conv_v = 0;
631 
632  for (unsigned int a = 0; a < size; ++a) {
633  for (unsigned int b = 0; b < size; ++b) {
634  FilterType val = static_cast<FilterType>(I[(v - half_size) + a][(u - half_size) + b]); // Correlation
635  conv_u += M[a][b] * val;
636  conv_v += M[b][a] * val;
637  }
638  }
639  Iu[v][u] = conv_u;
640  Iv[v][u] = conv_v;
641  }
642  }
643  }
644  }
645  }
646 
647 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
648  template<typename FilterType>
649  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve) = delete;
650 
651  template<typename ImageType>
652  static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve) = delete;
653 #else
654  template<typename FilterType>
655  static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve);
656 
657  template<typename ImageType>
658  static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve);
659 #endif
660 
661  static void sepFilter(const vpImage<unsigned char> &I, vpImage<double> &If, const vpColVector &kernelH, const vpColVector &kernelV);
662 
672  template <typename ImageType, typename FilterType>
673  static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &GI, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
674  {
676  filterX<ImageType, FilterType>(I, GIx, filter, size, p_mask);
677  filterY<FilterType, FilterType>(GIx, GI, filter, size, p_mask);
678  GIx.destroy();
679  }
680 
681  static inline unsigned char filterGaussXPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
682  {
683  const int val2 = 2;
684  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.);
685  }
686  static inline unsigned char filterGaussYPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
687  {
688  const int val2 = 2;
689  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.);
690  }
691 
692  template <typename ImageType, typename FilterType>
693  static void filterX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size,
694  const vpImage<bool> *p_mask = nullptr)
695  {
696  const unsigned int height = I.getHeight();
697  const unsigned int width = I.getWidth();
698  const unsigned int stop1J = (size - 1) / 2;
699  const unsigned int stop2J = width - ((size - 1) / 2);
700  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
701 
702  for (unsigned int i = 0; i < height; ++i) {
703  for (unsigned int j = 0; j < stop1J; ++j) {
704  // We have to compute the value for each pixel if we don't have a mask or for
705  // pixels for which the mask is true otherwise
706  bool computeVal = checkBooleanMask(p_mask, i, j);
707  if (computeVal) {
708  dIx[i][j] = vpImageFilter::filterXLeftBorder<ImageType, FilterType>(I, i, j, filter, size);
709  }
710  }
711  for (unsigned int j = stop1J; j < stop2J; ++j) {
712  // We have to compute the value for each pixel if we don't have a mask or for
713  // pixels for which the mask is true otherwise
714  bool computeVal = checkBooleanMask(p_mask, i, j);
715  if (computeVal) {
716  dIx[i][j] = vpImageFilter::filterX<ImageType, FilterType>(I, i, j, filter, size);
717  }
718  }
719  for (unsigned int j = stop2J; j < width; ++j) {
720  // We have to compute the value for each pixel if we don't have a mask or for
721  // pixels for which the mask is true otherwise
722  bool computeVal = checkBooleanMask(p_mask, i, j);
723  if (computeVal) {
724  dIx[i][j] = vpImageFilter::filterXRightBorder<ImageType, FilterType>(I, i, j, filter, size);
725  }
726  }
727  }
728  }
729 
730  static void filterX(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
731 
732  template<typename ImageType, typename FilterType>
733  static inline FilterType filterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
734  {
735  const unsigned int stop = (size - 1) / 2;
736  FilterType result = static_cast<FilterType>(0.);
737 
738  for (unsigned int i = 1; i <= stop; ++i) {
739  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
740  }
741  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
742  }
743 
744 #ifndef DOXYGEN_SHOULD_SKIP_THIS
745  static void filterXR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
746  static void filterXG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
747  static void filterXB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
748 
749  static double filterXR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
750  static double filterXG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
751  static double filterXB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
752 
753  static double filterXLeftBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
754  static double filterXLeftBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
755  static double filterXLeftBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
756  static double filterXRightBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
757  static double filterXRightBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
758  static double filterXRightBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
759 
760  template <typename ImageType, typename FilterType>
761  static inline FilterType filterXLeftBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
762  const FilterType *filter, unsigned int size)
763  {
764  const unsigned int stop = (size - 1) / 2;
765  FilterType result = static_cast<FilterType>(0.);
766 
767  for (unsigned int i = 1; i <= stop; ++i) {
768  if (c > i) {
769  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
770  }
771  else {
772  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][i - c]);
773  }
774  }
775  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
776  }
777 
778  template <typename ImageType, typename FilterType>
779  static inline FilterType filterXRightBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
780  const FilterType *filter, unsigned int size)
781  {
782  const unsigned int stop = (size - 1) / 2;
783  const unsigned int width = I.getWidth();
784  FilterType result = static_cast<FilterType>(0.);
785  const unsigned int twice = 2;
786 
787  for (unsigned int i = 1; i <= stop; ++i) {
788  if ((c + i) < width) {
789  result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
790  }
791  else {
792  result += filter[i] * static_cast<FilterType>(I[r][((twice * width) - c) - i - 1] + I[r][c - i]);
793  }
794  }
795  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
796  }
797 #endif
798 
799  static void filterY(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
800 
801  template<typename ImageType, typename FilterType>
802  static void filterY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size,
803  const vpImage<bool> *p_mask = nullptr)
804  {
805  const unsigned int height = I.getHeight(), width = I.getWidth();
806  const unsigned int stop1I = (size - 1) / 2;
807  const unsigned int stop2I = height - ((size - 1) / 2);
808  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
809 
810  for (unsigned int i = 0; i < stop1I; ++i) {
811  for (unsigned int j = 0; j < width; ++j) {
812  // We have to compute the value for each pixel if we don't have a mask or for
813  // pixels for which the mask is true otherwise
814  bool computeVal = checkBooleanMask(p_mask, i, j);
815  if (computeVal) {
816  dIy[i][j] = vpImageFilter::filterYTopBorder<ImageType, FilterType>(I, i, j, filter, size);
817  }
818  }
819  }
820  for (unsigned int i = stop1I; i < stop2I; ++i) {
821  for (unsigned int j = 0; j < width; ++j) {
822  // We have to compute the value for each pixel if we don't have a mask or for
823  // pixels for which the mask is true otherwise
824  bool computeVal = checkBooleanMask(p_mask, i, j);
825  if (computeVal) {
826  dIy[i][j] = vpImageFilter::filterY<ImageType, FilterType>(I, i, j, filter, size);
827  }
828  }
829  }
830  for (unsigned int i = stop2I; i < height; ++i) {
831  for (unsigned int j = 0; j < width; ++j) {
832  // We have to compute the value for each pixel if we don't have a mask or for
833  // pixels for which the mask is true otherwise
834  bool computeVal = checkBooleanMask(p_mask, i, j);
835  if (computeVal) {
836  dIy[i][j] = vpImageFilter::filterYBottomBorder<ImageType, FilterType>(I, i, j, filter, size);
837  }
838  }
839  }
840  }
841 
842  template<typename ImageType, typename FilterType>
843  static inline FilterType filterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
844  {
845  const unsigned int stop = (size - 1) / 2;
846  FilterType result = static_cast<FilterType>(0.);
847 
848  for (unsigned int i = 1; i <= stop; ++i) {
849  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
850  }
851  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
852  }
853 #ifndef DOXYGEN_SHOULD_SKIP_THIS
854  static void filterYR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
855  static void filterYG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
856  static void filterYB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
857 
858  static double filterYR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
859  static double filterYG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
860  static double filterYB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
861 
862  static double filterYTopBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
863  static double filterYTopBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
864  static double filterYTopBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
865  static double filterYBottomBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
866  static double filterYBottomBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
867  static double filterYBottomBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
868 
869  template<typename ImageType, typename FilterType>
870  static inline FilterType filterYTopBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
871  const FilterType *filter, unsigned int size)
872  {
873  const unsigned int stop = (size - 1) / 2;
874  FilterType result = static_cast<FilterType>(0.);
875 
876  for (unsigned int i = 1; i <= stop; ++i) {
877  if (r > i) {
878  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
879  }
880  else {
881  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[i - r][c]);
882  }
883  }
884  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
885  }
886 
887  template<typename ImageType, typename FilterType>
888  static inline FilterType filterYBottomBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
889  const FilterType *filter, unsigned int size)
890  {
891  const unsigned int height = I.getHeight();
892  const unsigned int stop = (size - 1) / 2;
893  FilterType result = static_cast<FilterType>(0.);
894  const unsigned int twiceHeight = 2 * height;
895  for (unsigned int i = 1; i <= stop; ++i) {
896  if ((r + i) < height) {
897  result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
898  }
899  else {
900  result += filter[i] * static_cast<FilterType>(I[(twiceHeight - r) - i - 1][c] + I[r - i][c]);
901  }
902  }
903  return result + (filter[0] * static_cast<FilterType>(I[r][c]));
904  }
905 #endif
906 
907 
921  template <typename ImageType, typename FilterType>
922  static void gaussianBlur(const vpImage<ImageType> &I, vpImage<FilterType> &GI, unsigned int size = 7, FilterType sigma = 0., bool normalize = true,
923  const vpImage<bool> *p_mask = nullptr)
924  {
925  FilterType *fg = new FilterType[(size + 1) / 2];
926  vpImageFilter::getGaussianKernel<FilterType>(fg, size, sigma, normalize);
928  vpImageFilter::filterX<ImageType, FilterType>(I, GIx, fg, size, p_mask);
929  vpImageFilter::filterY<FilterType, FilterType>(GIx, GI, fg, size, p_mask);
930  GIx.destroy();
931  delete[] fg;
932  }
933 
934  static void gaussianBlur(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &GI, unsigned int size = 7, double sigma = 0., bool normalize = true,
935  const vpImage<bool> *p_mask = nullptr);
936 
944  template <class T> static double gaussianFilter(const vpImage<T> &fr, unsigned int r, unsigned int c)
945  {
946  const int val2 = 2;
947  return ((15.0 * fr[r][c]) +
948  (12.0 * (fr[r - 1][c] + fr[r][c - 1] + fr[r + 1][c] + fr[r][c + 1])) +
949  (9.0 * (fr[r - 1][c - 1] + fr[r + 1][c - 1] + fr[r - 1][c + 1] + fr[r + 1][c + 1])) +
950  (5.0 * (fr[r - val2][c] + fr[r][c - val2] + fr[r + val2][c] + fr[r][c + val2])) +
951  (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] +
952  fr[r + val2][c + 1] + fr[r - 1][c + val2] + fr[r + 1][c + val2])) +
953  (2.0 * (fr[r - val2][c - val2] + fr[r + val2][c - val2] + fr[r - val2][c + val2] + fr[r + val2][c + val2]))) / 159.0;
954  }
955  // Gaussian pyramid operation
956  static void getGaussPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
957  static void getGaussXPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
958  static void getGaussYPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
959 
976  template<typename FilterType>
977  static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
978  {
979  const unsigned int mod2 = 2;
980  if ((size % mod2) != 1) {
981  throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
982  }
983 
984  if (sigma <= 0) {
985  sigma = static_cast<FilterType>((size - 1) / 6.0);
986  }
987 
988  int middle = (static_cast<int>(size) - 1) / 2;
989  FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(sigma));
990  FilterType coef1 = static_cast<FilterType>(1. / (sigma * sqrt(2. * M_PI)));
991  FilterType v_2_sigma2 = static_cast<FilterType>(2. * sigma2);
992  for (int i = 0; i <= middle; ++i) {
993  filter[i] = coef1 * static_cast<FilterType>(exp(-(i * i) / v_2_sigma2));
994  }
995  if (normalize) {
996  // renormalization
997  FilterType sum = 0;
998  const unsigned int val2 = 2U;
999  for (int i = 1; i <= middle; ++i) {
1000  sum += val2 * filter[i];
1001  }
1002  sum += filter[0];
1003 
1004  for (int i = 0; i <= middle; ++i) {
1005  filter[i] = filter[i] / sum;
1006  }
1007  }
1008  }
1009 
1024  template <typename FilterType>
1025  static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1026  {
1027  const unsigned int mod2 = 2;
1028  if ((size % mod2) != 1) {
1029  throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1030  }
1031 
1032  if (sigma <= 0) {
1033  sigma = static_cast<FilterType>((size - 1) / 6.0);
1034  }
1035 
1036  const int half = 2;
1037  int middle = (static_cast<int>(size) - 1) / half;
1038  FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(sigma));
1039  FilterType coef_1 = static_cast<FilterType>(1. / (sigma * sqrt(2. * M_PI)));
1040  FilterType coef_1_over_2 = coef_1 / static_cast<FilterType>(2.);
1041  FilterType v_2_coef_1 = static_cast<FilterType>(2.) * coef_1;
1042  FilterType v_2_sigma2 = static_cast<FilterType>(2. * sigma2);
1043  filter[0] = 0.;
1044  for (int i = 1; i <= middle; ++i) {
1045  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)));
1046  }
1047 
1048  if (normalize) {
1049  FilterType sum = 0;
1050  for (int i = 1; i <= middle; ++i) {
1051  sum += v_2_coef_1 * static_cast<FilterType>(exp(-(i * i) / v_2_sigma2));
1052  }
1053  sum += coef_1;
1054 
1055  for (int i = 1; i <= middle; ++i) {
1056  filter[i] = filter[i] / sum;
1057  }
1058  }
1059  }
1060 
1061  // Gradient along X
1062  template<typename FilterType>
1063  static void getGradX(const vpImage<unsigned char> &I, vpImage<FilterType> &dIx, const vpImage<bool> *p_mask = nullptr)
1064  {
1065  const unsigned int height = I.getHeight(), width = I.getWidth();
1066  const unsigned int stopJ = width - 3;
1067  const unsigned int val_3 = 3;
1068  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1069 
1070  for (unsigned int i = 0; i < height; ++i) {
1071  for (unsigned int j = 0; j < val_3; ++j) {
1072  // If a mask is used, the image is already initialized with 0s
1073  bool computeVal = (p_mask == nullptr);
1074  if (computeVal) {
1075  dIx[i][j] = static_cast<FilterType>(0);
1076  }
1077  }
1078  for (unsigned int j = 3; j < stopJ; ++j) {
1079  // We have to compute the value for each pixel if we don't have a mask or for
1080  // pixels for which the mask is true otherwise
1081  bool computeVal = checkBooleanMask(p_mask, i, j);
1082  if (computeVal) {
1083  dIx[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterX(I, i, j));
1084  }
1085  }
1086  for (unsigned int j = stopJ; j < width; ++j) {
1087  // If a mask is used, the image is already initialized with 0s
1088  bool computeVal = (p_mask == nullptr);
1089  if (computeVal) {
1090  dIx[i][j] = static_cast<FilterType>(0);
1091  }
1092  }
1093  }
1094  }
1095 
1096  template <typename ImageType, typename FilterType>
1097  static void getGradX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1098  {
1099  const unsigned int height = I.getHeight(), width = I.getWidth();
1100  const unsigned int stop1J = (size - 1) / 2;
1101  const unsigned int stop2J = width - ((size - 1) / 2);
1102  resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1103 
1104  for (unsigned int i = 0; i < height; ++i) {
1105  for (unsigned int j = 0; j < stop1J; ++j) {
1106  // If a mask is used, the image is already initialized with 0s
1107  bool computeVal = (p_mask == nullptr);
1108  if (computeVal) {
1109  dIx[i][j] = static_cast<FilterType>(0);
1110  }
1111  }
1112  for (unsigned int j = stop1J; j < stop2J; ++j) {
1113  // We have to compute the value for each pixel if we don't have a mask or for
1114  // pixels for which the mask is true otherwise
1115  bool computeVal = checkBooleanMask(p_mask, i, j);
1116  if (computeVal) {
1117  dIx[i][j] = vpImageFilter::derivativeFilterX<ImageType, FilterType>(I, i, j, filter, size);
1118  }
1119  }
1120  for (unsigned int j = stop2J; j < width; ++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  }
1128  }
1129 
1141  template <typename ImageType, typename FilterType>
1142  static void getGradXGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *gaussianKernel,
1143  const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1144  {
1145  vpImage<FilterType> GIy;
1146  vpImageFilter::filterY<ImageType, FilterType>(I, GIy, gaussianKernel, size, p_mask);
1147  vpImageFilter::getGradX<FilterType, FilterType>(GIy, dIx, gaussianDerivativeKernel, size, p_mask);
1148  }
1149 
1150  // Gradient along Y
1151  template <typename FilterType>
1152  static void getGradY(const vpImage<unsigned char> &I, vpImage<FilterType> &dIy, const vpImage<bool> *p_mask = nullptr)
1153  {
1154  const unsigned int height = I.getHeight(), width = I.getWidth();
1155  const unsigned int stopI = height - 3;
1156  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1157  const unsigned int val_3 = 3;
1158  for (unsigned int i = 0; i < val_3; ++i) {
1159  for (unsigned int j = 0; j < width; ++j) {
1160  // We have to compute the value for each pixel if we don't have a mask or for
1161  // pixels for which the mask is true otherwise
1162  bool computeVal = checkBooleanMask(p_mask, i, j);
1163  if (computeVal) {
1164  dIy[i][j] = static_cast<FilterType>(0);
1165  }
1166  }
1167  }
1168  for (unsigned int i = 3; i < stopI; ++i) {
1169  for (unsigned int j = 0; j < width; ++j) {
1170  // We have to compute the value for each pixel if we don't have a mask or for
1171  // pixels for which the mask is true otherwise
1172  bool computeVal = checkBooleanMask(p_mask, i, j);
1173  if (computeVal) {
1174  dIy[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterY(I, i, j));
1175  }
1176  }
1177  }
1178  for (unsigned int i = stopI; i < height; ++i) {
1179  for (unsigned int j = 0; j < width; ++j) {
1180  // We have to compute the value for each pixel if we don't have a mask or for
1181  // pixels for which the mask is true otherwise
1182  bool computeVal = checkBooleanMask(p_mask, i, j);
1183  if (computeVal) {
1184  dIy[i][j] = static_cast<FilterType>(0);
1185  }
1186  }
1187  }
1188  }
1189 
1190  template <typename ImageType, typename FilterType>
1191  static void getGradY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1192  {
1193  const unsigned int height = I.getHeight(), width = I.getWidth();
1194  const unsigned int stop1I = (size - 1) / 2;
1195  const unsigned int stop2I = height - ((size - 1) / 2);
1196  resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1197 
1198  for (unsigned int i = 0; i < stop1I; ++i) {
1199  for (unsigned int j = 0; j < width; ++j) {
1200  // We have to compute the value for each pixel if we don't have a mask or for
1201  // pixels for which the mask is true otherwise
1202  bool computeVal = checkBooleanMask(p_mask, i, j);
1203  if (computeVal) {
1204  dIy[i][j] = static_cast<FilterType>(0);
1205  }
1206  }
1207  }
1208  for (unsigned int i = stop1I; i < stop2I; ++i) {
1209  for (unsigned int j = 0; j < width; ++j) {
1210  // We have to compute the value for each pixel if we don't have a mask or for
1211  // pixels for which the mask is true otherwise
1212  bool computeVal = checkBooleanMask(p_mask, i, j);
1213  if (computeVal) {
1214  dIy[i][j] = vpImageFilter::derivativeFilterY<ImageType, FilterType>(I, i, j, filter, size);
1215  }
1216  }
1217  }
1218  for (unsigned int i = stop2I; i < height; ++i) {
1219  for (unsigned int j = 0; j < width; ++j) {
1220  // We have to compute the value for each pixel if we don't have a mask or for
1221  // pixels for which the mask is true otherwise
1222  bool computeVal = checkBooleanMask(p_mask, i, j);
1223  if (computeVal) {
1224  dIy[i][j] = static_cast<FilterType>(0);
1225  }
1226  }
1227  }
1228  }
1229 
1241  template <typename ImageType, typename FilterType>
1242  static void getGradYGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *gaussianKernel,
1243  const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1244  {
1245  vpImage<FilterType> GIx;
1246  vpImageFilter::filterX<ImageType, FilterType>(I, GIx, gaussianKernel, size, p_mask);
1247  vpImageFilter::getGradY<FilterType, FilterType>(GIx, dIy, gaussianDerivativeKernel, size, p_mask);
1248  }
1249 
1257  template <typename FilterType>
1258  inline static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
1259  {
1260  const unsigned int actualKernelSize = (size * 2) + 1;
1261  if (size != 1) {
1262  // Size = 1 => kernel_size = 2*1 + 1 = 3
1263  std::stringstream errMsg;
1264  errMsg << "Cannot get Scharr kernel of size " << actualKernelSize << " != 3";
1265  throw vpException(vpException::dimensionError, errMsg.str());
1266  }
1267 
1268  vpArray2D<FilterType> ScharrY(actualKernelSize, actualKernelSize);
1269  FilterType norm = getScharrKernelY<FilterType>(ScharrY.data, size);
1270  memcpy(filter, ScharrY.t().data, ScharrY.getRows() * ScharrY.getCols() * sizeof(FilterType));
1271  return norm;
1272  }
1273 
1281  template <typename FilterType>
1282  inline static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
1283  {
1284  // Scharr kernel pre-computed for the usual size
1285  static const FilterType ScharrY3x3[9] = { -3.0, -10.0, -3.0, 0.0, 0.0, 0.0, 3.0, 10.0, 3.0 };
1286 
1287  if (size != 1) {
1288  // Size = 1 => kernel_size = 2*1 + 1 = 3
1289  std::stringstream errMsg;
1290  errMsg << "Cannot get Scharr kernel of size " << ((size * 2) + 1) << " != 3";
1291  throw vpException(vpException::dimensionError, errMsg.str());
1292  }
1293 
1294  const unsigned int kernel_size = (size * 2) + 1;
1295  const unsigned int kernel3 = 3;
1296  if (kernel_size == kernel3) {
1297  memcpy(filter, ScharrY3x3, kernel_size * kernel_size * sizeof(FilterType));
1298  return static_cast<FilterType>(1.0 / 32.0);
1299  }
1300 
1301  return static_cast<FilterType>(0.);
1302  }
1303 
1311  template <typename FilterType>
1312  inline static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
1313  {
1314  const unsigned int maxSize = 20;
1315  if (size == 0) {
1316  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
1317  }
1318  if (size > maxSize) {
1319  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
1320  }
1321 
1322  const unsigned int kernel_size = (size * 2) + 1;
1323  vpArray2D<FilterType> SobelY(kernel_size, kernel_size);
1324  FilterType norm = getSobelKernelY<FilterType>(SobelY.data, size);
1325  memcpy(filter, SobelY.t().data, SobelY.getRows() * SobelY.getCols() * sizeof(FilterType));
1326  return norm;
1327  }
1328 
1336  template <typename FilterType>
1337  inline static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
1338  {
1339  // Sobel kernel pre-computed for the usual size
1340  static const FilterType SobelY3x3[9] = { -1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0 };
1341  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,
1342  0.0, 0.0, 2.0, 8.0, 12.0, 8.0, 2.0, 1.0, 4.0, 6.0, 4.0, 1.0 };
1343  static const FilterType SobelY7x7[49] = { -1, -6, -15, -20, -15, -6, -1, -4, -24, -60, -80, -60, -24, -4, -5, -30, -75,
1344  -100, -75, -30, -5, 0, 0, 0, 0, 0, 0, 0, 5, 30, 75, 100, 75, 30,
1345  5, 4, 24, 60, 80, 60, 24, 4, 1, 6, 15, 20, 15, 6, 1 };
1346  const vpArray2D<FilterType> smoothingKernel(3, 3);
1347  const unsigned int index_0 = 0;
1348  const unsigned int index_1 = 1;
1349  const unsigned int index_2 = 2;
1350  smoothingKernel[index_0][index_0] = 1.0;
1351  smoothingKernel[index_0][index_1] = 2.0;
1352  smoothingKernel[index_0][index_2] = 1.0;
1353  smoothingKernel[index_1][index_0] = 2.0;
1354  smoothingKernel[index_1][index_1] = 4.0;
1355  smoothingKernel[index_1][index_2] = 2.0;
1356  smoothingKernel[index_2][index_0] = 1.0;
1357  smoothingKernel[index_2][index_1] = 2.0;
1358  smoothingKernel[index_2][index_2] = 1.0;
1359 
1360  const unsigned int maxSize = 20;
1361  if (size == 0) {
1362  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
1363  }
1364  if (size > maxSize) {
1365  throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
1366  }
1367 
1368  const unsigned int kernel_size = (size * 2) + 1;
1369  FilterType scale = static_cast<FilterType>(1. / 8.); // Scale to normalize Sobel3x3
1370  const unsigned int kernel3 = 3, kernel5 = 5, kernel7 = 7;
1371  if (kernel_size == kernel3) {
1372  memcpy(filter, SobelY3x3, kernel_size * kernel_size * sizeof(FilterType));
1373  return scale;
1374  }
1375  scale *= static_cast<FilterType>(1. / 16.); // Sobel5x5 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel3x3
1376  if (kernel_size == kernel5) {
1377  memcpy(filter, SobelY5x5, kernel_size * kernel_size * sizeof(FilterType));
1378  return scale;
1379  }
1380  scale *= static_cast<FilterType>(1. / 16.); // Sobel7x7 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel5x5
1381  if (kernel_size == kernel7) {
1382  memcpy(filter, SobelY7x7, kernel_size * kernel_size * sizeof(FilterType));
1383  return scale;
1384  }
1385 
1386  vpArray2D<FilterType> sobelY(7, 7);
1387  memcpy(sobelY.data, SobelY7x7, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
1388  for (unsigned int i = 4; i <= size; ++i) {
1389  sobelY = vpArray2D<FilterType>::conv2(sobelY, smoothingKernel, "full");
1390  // Sobel(N+1)x(N+1) is the convolution of smoothingKernel, which needs 1/16 scale factor, with SobelNxN
1391  scale *= static_cast<FilterType>(1. / 16.);
1392  }
1393 
1394  memcpy(filter, sobelY.data, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
1395 
1396  return scale;
1397  }
1398 
1399 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
1400  static float median(const cv::Mat &cv_I);
1401  static float median(const vpImage<unsigned char> &Isrc);
1402  static std::vector<float> median(const vpImage<vpRGBa> &Isrc);
1403 #endif
1404 
1405 private:
1416  template<typename ImageType>
1417  static void resizeAndInitializeIfNeeded(const vpImage<bool> *p_mask, const unsigned int height, const unsigned int width, vpImage<ImageType> &I)
1418  {
1419  if (p_mask == nullptr) {
1420  // Just need to resize the output image, values will be computed and overwrite what is inside the image
1421  I.resize(height, width);
1422  }
1423  else {
1424  // Need to reset the image because some points will not be computed
1425  I.resize(height, width, static_cast<ImageType>(0));
1426  }
1427  }
1428 
1438  static bool checkBooleanMask(const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c)
1439  {
1440  bool computeVal = true;
1441 #if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
1442  if (p_mask != nullptr)
1443 #else
1444  if (p_mask != NULL)
1445 #endif
1446  {
1447  computeVal = (*p_mask)[r][c];
1448  }
1449  return computeVal;
1450  }
1451 
1452 // Note that on ubuntu 12.04 __cplusplus is equal to 1 that's why in the next line we consider __cplusplus <= 199711L
1453 // and not __cplusplus == 199711L
1454 #if ((__cplusplus <= 199711L) || (defined(_MSVC_LANG) && (_MSVC_LANG == 199711L))) // Check if cxx98
1455  // Helper to apply the scale to the raw values of the filters
1456  template <typename FilterType>
1457  static void scaleFilter(vpArray2D<FilterType> &filter, const float &scale)
1458  {
1459  const unsigned int nbRows = filter.getRows();
1460  const unsigned int nbCols = filter.getCols();
1461  for (unsigned int r = 0; r < nbRows; ++r) {
1462  for (unsigned int c = 0; c < nbCols; ++c) {
1463  filter[r][c] = filter[r][c] * scale;
1464  }
1465  }
1466  }
1467 #endif
1468 
1469 };
1470 END_VISP_NAMESPACE
1471 #endif
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition: vpArray2D.h:145
unsigned int getCols() const
Definition: vpArray2D.h:337
Type * data
Address of the first element of the data array.
Definition: vpArray2D.h:148
static vpArray2D< Type > conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, const std::string &mode)
Definition: vpArray2D.h:1174
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition: vpArray2D.h:1163
unsigned int getRows() const
Definition: vpArray2D.h:347
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
@ dimensionError
Bad dimension.
Definition: vpException.h:71
@ notImplementedError
Not implemented.
Definition: vpException.h:69
Class to compute a gray level image histogram.
Definition: vpHistogram.h:106
unsigned int getTotal()
Get the total number of pixels in the input image.
Definition: vpHistogram.h:308
void calculate(const vpImage< unsigned char > &I, unsigned int nbins=256, unsigned int nbThreads=1)
void setMask(const vpImage< bool > *p_mask)
Set a mask to ignore pixels for which the mask is false.
Definition: vpHistogram.h:246
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Error that can be emitted by the vpImage class and its derivatives.
@ incorrectInitializationError
Wrong image initialization.
Various image filter, convolution, etc...
Definition: vpImageFilter.h:71
static void filterY(const vpImage< ImageType > &I, vpImage< FilterType > &dIy, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static unsigned char filterGaussXPyramidal(const vpImage< unsigned char > &I, unsigned int i, unsigned int j)
static void getGradY(const vpImage< ImageType > &I, vpImage< FilterType > &dIy, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static FilterType filterX(const vpImage< ImageType > &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
static double derivativeFilterX(const vpImage< ImageType > &I, unsigned int r, unsigned int c)
static void getGradX(const vpImage< unsigned char > &I, vpImage< FilterType > &dIx, const vpImage< bool > *p_mask=nullptr)
static float computeCannyThreshold(const vpImage< unsigned char > &I, float &lowerThresh, const vpImage< OutType > *p_dIx=nullptr, const vpImage< OutType > *p_dIy=nullptr, const unsigned int &gaussianKernelSize=5, const OutType &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const float &lowerThresholdRatio=0.6, const float &upperThresholdRatio=0.8, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING, const vpImage< bool > *p_mask=nullptr)
Compute the upper Canny edge filter threshold, using Gaussian blur + Sobel or + Scharr operators to c...
static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
static void getGradXGauss2D(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *gaussianKernel, const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static FilterType filterY(const vpImage< ImageType > &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
static void filter(const vpImage< ImageType > &I, vpImage< ImageType > &Iu, vpImage< ImageType > &Iv, const vpArray2D< vpRGBa > &M, bool convolve)=delete
static void filter(const vpImage< vpRGBa > &I, vpImage< FilterType > &Iu, vpImage< FilterType > &Iv, const vpArray2D< FilterType > &M, bool convolve)=delete
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 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)=delete
static double derivativeFilterY(const vpImage< ImageType > &I, unsigned int r, unsigned int c)
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)=delete
static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
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 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)=delete
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 void filter(const vpImage< vpRGBa > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false)=delete
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:567
unsigned int getWidth() const
Definition: vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:538
unsigned int getHeight() const
Definition: vpImage.h:181
static double sqr(double x)
Definition: vpMath.h:203
Definition: vpRGBa.h:65