Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpImageFilter.h
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 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
38
39#ifndef VP_IMAGE_FILTER_H
40#define VP_IMAGE_FILTER_H
41
42#include <cstring>
43#include <fstream>
44#include <iostream>
45#include <math.h>
46#include <string.h>
47
48#include <visp3/core/vpConfig.h>
49#include <visp3/core/vpColorGetter.h>
50#include <visp3/core/vpException.h>
51#include <visp3/core/vpHistogram.h>
52#include <visp3/core/vpImage.h>
53#include <visp3/core/vpImageConvert.h>
54#include <visp3/core/vpImageException.h>
55#include <visp3/core/vpMath.h>
56#include <visp3/core/vpMatrix.h>
57#include <visp3/core/vpRGBa.h>
58
59#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
60#include <opencv2/imgproc/imgproc.hpp>
61#if (VISP_HAVE_OPENCV_VERSION < 0x050000)
62#include <opencv2/imgproc/imgproc_c.h>
63#endif
64#endif
65
66#if defined(__clang__)
67# pragma clang diagnostic push
68# pragma clang diagnostic ignored "-Wdocumentation"
69#endif
70
71#ifdef VISP_HAVE_OPENMP
72#include <omp.h>
73#endif
74
88class VISP_EXPORT vpImageFilter
89{
90public:
98
99 static std::string vpCannyBackendTypeList(const std::string &pref = "<", const std::string &sep = " , ",
100 const std::string &suf = ">");
101
102 static std::string vpCannyBackendTypeToString(const vpCannyBackendType &type);
103
104 static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name);
105
113
114 static std::string vpGetCannyFiltAndGradTypes(const std::string &pref = "<", const std::string &sep = " , ",
115 const std::string &suf = ">");
116
117 static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type);
118
119 static vpCannyFilteringAndGradientType vpCannyFiltAndGradTypeFromStr(const std::string &name);
120
121 static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
122 const float &thresholdCanny, const unsigned int &apertureSobel);
123
124 static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
125 const float &lowerThresholdCanny, const float &higherThresholdCanny,
126 const unsigned int &apertureSobel);
127
128 static void canny(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ic, const unsigned int &gaussianFilterSize,
129 const float &lowerThresholdCanny, const float &higherThresholdCanny,
130 const unsigned int &apertureSobel, const float &gaussianStdev, const float &lowerThresholdRatio,
131 const float &upperThresholdRatio, const bool &normalizeGradients,
132 const vpCannyBackendType &cannyBackend, const vpCannyFilteringAndGradientType &cannyFilteringSteps,
133 const vpImage<bool> *p_mask = nullptr);
134
135#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
136 static float computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_dIx, const cv::Mat *p_cv_dIy,
137 float &lowerThresh, const unsigned int &gaussianKernelSize = 5,
138 const float &gaussianStdev = 2.f, const unsigned int &apertureGradient = 3,
139 const float &lowerThresholdRatio = 0.6f, const float &upperThresholdRatio = 0.8f,
140 const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
141
142 static void computePartialDerivatives(const cv::Mat &cv_I,
143 cv::Mat &cv_dIx, cv::Mat &cv_dIy,
144 const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
145 const unsigned int &gaussianKernelSize = 5, const float &gaussianStdev = 2.f,
146 const unsigned int &apertureGradient = 3,
147 const vpCannyFilteringAndGradientType &filteringType = CANNY_GBLUR_SOBEL_FILTERING);
148#endif
149
170 template <typename ImageType, typename FilterType>
173 const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
174 const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
175 const unsigned int &apertureGradient = 3,
177 const vpCannyBackendType &backend = CANNY_VISP_BACKEND,
178 const vpImage<bool> *p_mask = nullptr)
179 {
180 if (backend == CANNY_OPENCV_BACKEND) {
181#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
182 cv::Mat cv_I, cv_dIx, cv_dIy;
184 computePartialDerivatives(cv_I, cv_dIx, cv_dIy, computeDx, computeDy, normalize, gaussianKernelSize,
185 static_cast<float>(gaussianStdev), apertureGradient, filteringType);
186 if (computeDx) {
187 vpImageConvert::convert(cv_dIx, dIx);
188 }
189 if (computeDy) {
190 vpImageConvert::convert(cv_dIy, dIy);
191 }
192#else
193 throw(vpException(vpException::badValue, "You need to compile ViSP with OpenCV to use CANNY_OPENCV_BACKEND"));
194#endif
195 }
196 else {
197 if ((filteringType == CANNY_GBLUR_SCHARR_FILTERING) || (filteringType == CANNY_GBLUR_SOBEL_FILTERING)) {
198 dIx.resize(I.getHeight(), I.getWidth());
199 dIy.resize(I.getHeight(), I.getWidth());
200
201 // Computing the Gaussian blur + gradients of the image
203 vpImageFilter::gaussianBlur(I, Iblur, gaussianKernelSize, gaussianStdev, true, p_mask);
204
205 vpArray2D<FilterType> gradientFilterX(apertureGradient, apertureGradient); // Gradient filter along the X-axis
206 vpArray2D<FilterType> gradientFilterY(apertureGradient, apertureGradient); // Gradient filter along the Y-axis
207
208#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
209 // Helper to apply the scale to the raw values of the filters
210 auto scaleFilter = [](vpArray2D<FilterType> &filter, const FilterType &scale) {
211 const unsigned int nbRows = filter.getRows();
212 const unsigned int nbCols = filter.getCols();
213 for (unsigned int r = 0; r < nbRows; ++r) {
214 for (unsigned int c = 0; c < nbCols; ++c) {
215 filter[r][c] = filter[r][c] * scale;
216 }
217 }
218 };
219#endif
220
221 // Scales to apply to the filters to get a normalized gradient filter that gives a gradient
222 // between 0 and 255 for an vpImage<uchar>
223 FilterType scaleX = 1.0;
224 FilterType scaleY = 1.0;
225 const unsigned int val2 = 2U;
226
227 if (filteringType == CANNY_GBLUR_SOBEL_FILTERING) {
228 if (computeDx) {
229 scaleX = static_cast<FilterType>(vpImageFilter::getSobelKernelX(gradientFilterX.data, (apertureGradient - 1) / val2));
230 }
231 if (computeDy) {
232 scaleY = static_cast<FilterType>(vpImageFilter::getSobelKernelY(gradientFilterY.data, (apertureGradient - 1) / val2));
233 }
234 }
235 else if (filteringType == CANNY_GBLUR_SCHARR_FILTERING) {
236 if (computeDx) {
237 scaleX = static_cast<FilterType>(vpImageFilter::getScharrKernelX(gradientFilterX.data, (apertureGradient - 1) / val2));
238 }
239 if (computeDy) {
240 scaleY = static_cast<FilterType>(vpImageFilter::getScharrKernelY(gradientFilterY.data, (apertureGradient - 1) / val2));
241 }
242 }
243
244 // Scale the gradient filters to have a normalized gradient filter
245 if (normalize) {
246 if (computeDx) {
247 scaleFilter(gradientFilterX, scaleX);
248 }
249 if (computeDy) {
250 scaleFilter(gradientFilterY, scaleY);
251 }
252 }
253
254 // Apply the gradient filters to get the gradients
255 if (computeDx) {
256 vpImageFilter::filter(Iblur, dIx, gradientFilterX, true, p_mask);
257 }
258
259 if (computeDy) {
260 vpImageFilter::filter(Iblur, dIy, gradientFilterY, true, p_mask);
261 }
262 }
263 else {
264 std::string errMsg = "[vpImageFilter::computePartialDerivatives] Filtering + gradient method \"";
265 errMsg += vpCannyFiltAndGradTypeToStr(filteringType);
266 errMsg += "\" is not implemented yet\n";
268 }
269 }
270 }
271
272#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
273 template <typename FilterType>
274 inline static void computePartialDerivatives(const vpImage<vpRGBa> &I,
276 const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
277 const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
278 const unsigned int &apertureGradient = 3,
280 const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
281
282 template <typename ImageType>
285 const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
286 const unsigned int &gaussianKernelSize = 5, const unsigned char &gaussianStdev = 2.f,
287 const unsigned int &apertureGradient = 3,
289 const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
290
291 template <typename ImageType>
294 const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
295 const unsigned int gaussianKernelSize = 5, const vpRGBa gaussianStdev = vpRGBa(),
296 const unsigned int apertureGradient = 3,
298 const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr) = delete;
299#else
300 template <typename FilterType>
301 inline static void computePartialDerivatives(const vpImage<vpRGBa> &I,
303 const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
304 const unsigned int &gaussianKernelSize = 5, const FilterType &gaussianStdev = 2.f,
305 const unsigned int &apertureGradient = 3,
307 const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
308
309 template <typename ImageType>
310 inline static void computePartialDerivatives(const vpImage<ImageType> &I,
312 const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
313 const unsigned int &gaussianKernelSize = 5, const unsigned char &gaussianStdev = 2.f,
314 const unsigned int &apertureGradient = 3,
316 const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
317
318 template <typename ImageType>
319 inline static void computePartialDerivatives(const vpImage<ImageType> &I,
321 const bool &computeDx = true, const bool &computeDy = true, const bool &normalize = true,
322 const unsigned int gaussianKernelSize = 5, const vpRGBa gaussianStdev = vpRGBa(),
323 const unsigned int apertureGradient = 3,
325 const vpCannyBackendType &backend = CANNY_VISP_BACKEND, const vpImage<bool> *p_mask = nullptr);
326#endif
327
348 template<typename OutType>
349 inline static float computeCannyThreshold(const vpImage<unsigned char> &I, float &lowerThresh,
350 const vpImage<OutType> *p_dIx = nullptr, const vpImage<OutType> *p_dIy = nullptr,
351 const unsigned int &gaussianKernelSize = 5,
352 const OutType &gaussianStdev = 2.f, const unsigned int &apertureGradient = 3,
353 const float &lowerThresholdRatio = 0.6f, const float &upperThresholdRatio = 0.8f,
355 const vpImage<bool> *p_mask = nullptr)
356 {
357 const unsigned int w = I.getWidth();
358 const unsigned int h = I.getHeight();
359 const int size = static_cast<int>(I.getSize());
360
361 if ((lowerThresholdRatio <= 0.f) || (lowerThresholdRatio >= 1.f)) {
362 std::stringstream errMsg;
363 errMsg << "Lower ratio (" << lowerThresholdRatio << ") " << (lowerThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
364 throw(vpException(vpException::fatalError, errMsg.str()));
365 }
366
367 if ((upperThresholdRatio <= 0.f) || (upperThresholdRatio >= 1.f)) {
368 std::stringstream errMsg;
369 errMsg << "Upper ratio (" << upperThresholdRatio << ") " << (upperThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
370 throw(vpException(vpException::fatalError, errMsg.str()));
371 }
372
373 if (lowerThresholdRatio >= upperThresholdRatio) {
374 std::stringstream errMsg;
375 errMsg << "Lower ratio (" << lowerThresholdRatio << ") should be lower than the upper ratio (" << upperThresholdRatio << ")";
376 throw(vpException(vpException::fatalError, errMsg.str()));
377 }
378
379 vpImage<unsigned char> dI(h, w);
380 vpImage<OutType> dIx(h, w), dIy(h, w);
381 if ((p_dIx != nullptr) && (p_dIy != nullptr)) {
382 dIx = *p_dIx;
383 dIy = *p_dIy;
384 }
385 else {
386 computePartialDerivatives(I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev,
387 apertureGradient, filteringType, vpImageFilter::CANNY_VISP_BACKEND, p_mask);
388 }
389
390 // Computing the absolute gradient of the image G = |dIx| + |dIy|
391#ifdef VISP_HAVE_OPENMP
392#pragma omp parallel for
393#endif
394 for (int iter = 0; iter < size; ++iter) {
395 // We have to compute the value for each pixel if we don't have a mask or for
396 // pixels for which the mask is true otherwise
397 bool computeVal = checkBooleanMask(p_mask, iter);
398
399 if (computeVal) {
400 float dx = static_cast<float>(dIx.bitmap[iter]);
401 float dy = static_cast<float>(dIy.bitmap[iter]);
402 float gradient = std::abs(dx) + std::abs(dy);
403 float gradientClamped = std::min<float>(gradient, static_cast<float>(std::numeric_limits<unsigned char>::max()));
404 dI.bitmap[iter] = static_cast<unsigned char>(gradientClamped);
405 }
406 }
407
408 // Compute the histogram
409 vpHistogram hist;
410 hist.setMask(p_mask);
411 const unsigned int nbBins = 256;
412 hist.calculate(dI, nbBins);
413 float totalNbPixels = static_cast<float>(hist.getTotal());
414 float accu = 0;
415 float t = upperThresholdRatio * totalNbPixels;
416 float tLow = lowerThresholdRatio * totalNbPixels;
417 float bon = 0;
418 unsigned int i = 0;
419 bool notFound = true, notFoundLower = true;
420 while ((i < nbBins) && notFound) {
421 float tf = static_cast<float>(hist[static_cast<unsigned char>(i)]);
422 accu = accu + tf;
423 if ((accu > tLow) && notFoundLower) {
424 lowerThresh = static_cast<float>(i);
425 notFoundLower = false;
426 }
427 if (accu > t) {
428 bon = static_cast<float>(i);
429 notFound = false;
430 }
431 ++i;
432 }
433 if (notFound) {
434 std::stringstream errMsg;
435 errMsg << "Could not find a bin for which " << upperThresholdRatio * 100.f << " percents of the pixels had a gradient lower than the upper threshold.";
436 throw(vpException(vpException::fatalError, errMsg.str()));
437 }
438
439 float upperThresh = std::max<float>(bon, 1.f);
440 lowerThresh = std::max<float>(lowerThresh, std::numeric_limits<float>::epsilon());
441 return upperThresh;
442 }
443
444#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11) // Check if cxx11 or higher
465 template<typename ArithmeticType, bool useFullScale, typename OutType>
466 inline static float computeCannyThreshold(const vpImage<vpHSV<ArithmeticType, useFullScale>> &I, float &lowerThresh,
467 const vpImage<OutType> *p_dIx = nullptr, const vpImage<OutType> *p_dIy = nullptr,
468 const unsigned int &gaussianKernelSize = 5,
469 const OutType &gaussianStdev = 2.f,
470 const float &lowerThresholdRatio = 0.6f, const float &upperThresholdRatio = 0.8f,
472 const vpImage<bool> *p_mask = nullptr)
473 {
474 const unsigned int w = I.getWidth();
475 const unsigned int h = I.getHeight();
476 const int size = I.getSize();
477
478 if ((lowerThresholdRatio <= 0.f) || (lowerThresholdRatio >= 1.f)) {
479 std::stringstream errMsg;
480 errMsg << "Lower ratio (" << lowerThresholdRatio << ") " << (lowerThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
481 throw(vpException(vpException::fatalError, errMsg.str()));
482 }
483
484 if ((upperThresholdRatio <= 0.f) || (upperThresholdRatio >= 1.f)) {
485 std::stringstream errMsg;
486 errMsg << "Upper ratio (" << upperThresholdRatio << ") " << (upperThresholdRatio < 0.f ? "should be greater than 0 !" : "should be lower than 1 !");
487 throw(vpException(vpException::fatalError, errMsg.str()));
488 }
489
490 if (lowerThresholdRatio >= upperThresholdRatio) {
491 std::stringstream errMsg;
492 errMsg << "Lower ratio (" << lowerThresholdRatio << ") should be lower than the upper ratio (" << upperThresholdRatio << ")";
493 throw(vpException(vpException::fatalError, errMsg.str()));
494 }
495
496 int nbThread = 1;
497#ifdef VISP_HAVE_OPENMP
498 nbThread = omp_get_max_threads();
499#endif
500
501 vpImage<OutType> dI(h, w);
502 vpImage<OutType> dIx(h, w), dIy(h, w);
503 if ((p_dIx != nullptr) && (p_dIy != nullptr)) {
504 dIx = *p_dIx;
505 dIy = *p_dIy;
506 }
507 else {
509 gaussianBlur(I, Iblur, gaussianKernelSize, gaussianStdev, true, p_mask);
510 gradientFilter(Iblur, dIx, dIy, nbThread, p_mask, filteringType);
511 }
512
513 // Computing the absolute gradient of the image G = |dIx| + |dIy|
514 float dIMax = -1.; // dI is the absolute gradient => positive
515 float dIMin = std::numeric_limits<OutType>::max();
516 int iter, istart = 0, istop = size;
517#ifdef VISP_HAVE_OPENMP
518 int iam, nt, ipoints, npoints(size);
519#pragma omp parallel default(shared) private(iter, iam, nt, ipoints, istart, istop)
520 {
521 iam = omp_get_thread_num();
522 nt = omp_get_num_threads();
523 ipoints = npoints / nt;
524 // size of partition
525 istart = iam * ipoints; // starting array index
526 if (iam == nt-1) {
527 // last thread may do more
528 ipoints = npoints - istart;
529 }
530 istop = istart + ipoints;
531#endif
532 float localdImin = std::numeric_limits<OutType>::max(), localdImax = -1.;
533 for (iter = istart; iter < istop; ++iter) {
534 // We have to compute the value for each pixel if we don't have a mask or for
535 // pixels for which the mask is true otherwise
536 bool computeVal = checkBooleanMask(p_mask, iter);
537
538 if (computeVal) {
539 float dx = static_cast<float>(dIx.bitmap[iter]);
540 float dy = static_cast<float>(dIy.bitmap[iter]);
541 float gradient = std::abs(dx) + std::abs(dy);
542 localdImax = std::max(localdImax, gradient);
543 localdImin = std::min(localdImin, gradient);
544 dI.bitmap[iter] = gradient;
545 }
546 }
547#ifdef VISP_HAVE_OPENMP
548#pragma omp critical
549 {
550 dIMin = std::min(dIMin, localdImin);
551 dIMax = std::max(dIMax, localdImax);
552 }
553#else
554 dIMin = localdImin;
555 dIMax = localdImax;
556#endif
557#ifdef VISP_HAVE_OPENMP
558 }
559#endif
560
561 // Compute the histogram
562#ifdef VISP_HAVE_THREADS
563 nbThread = std::max(static_cast<int>(std::thread::hardware_concurrency()), 1); // The method can return 0 when it is not able to detect the supported number of threads.
564#endif
565 const unsigned int nbBins = 1024;
566 vpHistogram hist(nbBins);
567 hist.setMask(p_mask);
568 OutType step = 0.;
569 hist.calculate<OutType>(dI, dIMin, dIMax, step, nbBins, nbThread);
570 float totalNbPixels = static_cast<float>(hist.getTotal());
571 float accu = 0;
572 float t = upperThresholdRatio * totalNbPixels;
573 float tLow = lowerThresholdRatio * totalNbPixels;
574 float bon = 0;
575 unsigned int i = 0;
576 bool notFound = true, notFoundLower = true;
577 while ((i < nbBins) && notFound) {
578 float tf = static_cast<float>(hist[i]);
579 accu = accu + tf;
580 if ((accu > tLow) && notFoundLower) {
581 lowerThresh = static_cast<float>(i) * static_cast<float>(step) + dIMin;
582 notFoundLower = false;
583 }
584 if (accu > t) {
585 bon = static_cast<float>(i);
586 notFound = false;
587 }
588 ++i;
589 }
590 if (notFound) {
591 std::stringstream errMsg;
592 errMsg << "Could not find a bin for which " << upperThresholdRatio * 100.f << " percents of the pixels had a gradient lower than the upper threshold.";
593 throw(vpException(vpException::fatalError, errMsg.str()));
594 }
595 float upperThresh = bon * static_cast<float>(step) + dIMin;
596 lowerThresh = std::max<float>(lowerThresh, std::numeric_limits<float>::epsilon());
597 return upperThresh;
598 }
599#endif
600
608 template <class ImageType> static double derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
609 {
610 const int val1 = 1, val2 = 2, val3 = 3;
611 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])) +
612 (112.0 * static_cast<double>(I[r][c + val3] - I[r][c - val3]))) / 8418.0;
613 }
614
622 template <class ImageType> static double derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c)
623 {
624 const int val1 = 1, val2 = 2, val3 = 3;
625 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])) +
626 (112.0 * static_cast<double>(I[r + val3][c] - I[r - val3][c]))) / 8418.0;
627 }
628
642 template <class ImageType, typename FilterType>
643 static FilterType derivativeFilterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
644 {
645 const unsigned int stop = (size - 1) / 2;
646 unsigned int i;
647 FilterType result;
648
649 result = 0;
650
651 for (i = 1; i <= stop; ++i) {
652 result += filter[i] * static_cast<FilterType>(I[r][c + i] - I[r][c - i]);
653 }
654 return result;
655 }
656
670 template <class ImageType, typename FilterType>
671 static FilterType derivativeFilterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
672 {
673 const unsigned int stop = (size - 1) / 2;
674 unsigned int i;
675 FilterType result;
676
677 result = 0;
678
679 for (i = 1; i <= stop; ++i) {
680 result += filter[i] * static_cast<FilterType>(I[r + i][c] - I[r - i][c]);
681 }
682 return result;
683 }
684
714 template <typename ImageType, typename FilterType>
715 static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false,
716 const vpImage<bool> *p_mask = nullptr)
717 {
718 const unsigned int size_y = M.getRows(), size_x = M.getCols();
719 const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
720
721 const unsigned int inputHeight = I.getHeight(), inputWidth = I.getWidth();
722 If.resize(inputHeight, inputWidth, 0.0);
723
724 if (convolve) {
725 const unsigned int stopHeight = inputHeight - half_size_y;
726 const unsigned int stopWidth = inputWidth - half_size_x;
727 for (unsigned int i = half_size_y; i < stopHeight; ++i) {
728 for (unsigned int j = half_size_x; j < stopWidth; ++j) {
729 // We have to compute the value for each pixel if we don't have a mask or for
730 // pixels for which the mask is true otherwise
731 bool computeVal = checkBooleanMask(p_mask, i, j);
732 if (computeVal) {
733 FilterType conv = 0;
734
735 for (unsigned int a = 0; a < size_y; ++a) {
736 for (unsigned int b = 0; b < size_x; ++b) {
737 FilterType val = static_cast<FilterType>(I[(i + half_size_y) - a][(j + half_size_x) - b]); // Convolution
738 conv += M[a][b] * val;
739 }
740 }
741 If[i][j] = conv;
742 }
743 }
744 }
745 }
746 else {
747 const unsigned int stopHeight = inputHeight - half_size_y;
748 const unsigned int stopWidth = inputWidth - half_size_x;
749 for (unsigned int i = half_size_y; i < stopHeight; ++i) {
750 for (unsigned int j = half_size_x; j < stopWidth; ++j) {
751 // We have to compute the value for each pixel if we don't have a mask or for
752 // pixels for which the mask is true otherwise
753 bool computeVal = checkBooleanMask(p_mask, i, j);
754 if (computeVal) {
755 FilterType corr = 0;
756
757 for (unsigned int a = 0; a < size_y; ++a) {
758 for (unsigned int b = 0; b < size_x; ++b) {
759 FilterType val = static_cast<FilterType>(I[(i - half_size_y) + a][(j - half_size_x) + b]); // Correlation
760 corr += M[a][b] * val;
761 }
762 }
763 If[i][j] = corr;
764 }
765 }
766 }
767 }
768 }
769
779 template <typename FilterType>
780 static FilterType filter(const vpImage<FilterType> &I, const vpArray2D<FilterType> &M, unsigned int row, unsigned int col)
781 {
782 const unsigned int size_y = M.getRows(), size_x = M.getCols();
783 const unsigned int half_size_y = size_y / 2, half_size_x = size_x / 2;
784 FilterType corr = 0;
785
786 for (unsigned int a = 0; a < size_y; ++a) {
787 for (unsigned int b = 0; b < size_x; ++b) {
788 FilterType val = static_cast<FilterType>(I[row - half_size_y + a][col - half_size_x + b]); // Correlation
789 corr += M[a][b] * val;
790 }
791 }
792 return corr;
793 }
794
795#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
796 template <typename FilterType>
797 static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false) = delete;
798#else
799 template <typename FilterType>
800 static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &If, const vpArray2D<FilterType> &M, bool convolve = false);
801#endif
802
816 template <typename ImageType, typename FilterType>
818 bool convolve = false, const vpImage<bool> *p_mask = nullptr)
819 {
820 const unsigned int size = M.getRows();
821 const unsigned int half_size = size / 2;
822 const unsigned int height = I.getHeight(), width = I.getWidth();
823 const unsigned int stopV = height - half_size;
824 const unsigned int stopU = width - half_size;
825
826 Iu.resize(height, width, 0.0);
827 Iv.resize(height, width, 0.0);
828
829 if (convolve) {
830 for (unsigned int v = half_size; v < stopV; ++v) {
831 for (unsigned int u = half_size; u < stopU; ++u) {
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, v, u);
835 if (computeVal) {
836 FilterType conv_u = 0;
837 FilterType conv_v = 0;
838
839 for (unsigned int a = 0; a < size; ++a) {
840 for (unsigned int b = 0; b < size; ++b) {
841 FilterType val = static_cast<FilterType>(I[(v + half_size) - a][(u + half_size) - b]); // Convolution
842 conv_u += M[a][b] * val;
843 conv_v += M[b][a] * val;
844 }
845 }
846 Iu[v][u] = conv_u;
847 Iv[v][u] = conv_v;
848 }
849 }
850 }
851 }
852 else {
853 for (unsigned int v = half_size; v < stopV; ++v) {
854 for (unsigned int u = half_size; u < stopU; ++u) {
855 // We have to compute the value for each pixel if we don't have a mask or for
856 // pixels for which the mask is true otherwise
857 bool computeVal = checkBooleanMask(p_mask, v, u);
858
859 if (computeVal) {
860 FilterType conv_u = 0;
861 FilterType conv_v = 0;
862
863 for (unsigned int a = 0; a < size; ++a) {
864 for (unsigned int b = 0; b < size; ++b) {
865 FilterType val = static_cast<FilterType>(I[(v - half_size) + a][(u - half_size) + b]); // Correlation
866 conv_u += M[a][b] * val;
867 conv_v += M[b][a] * val;
868 }
869 }
870 Iu[v][u] = conv_u;
871 Iv[v][u] = conv_v;
872 }
873 }
874 }
875 }
876 }
877
878#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
879 template<typename FilterType>
880 static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve) = delete;
881
882 template<typename ImageType>
883 static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve) = delete;
884#else
885 template<typename FilterType>
886 static void filter(const vpImage<vpRGBa> &I, vpImage<FilterType> &Iu, vpImage<FilterType> &Iv, const vpArray2D<FilterType> &M, bool convolve);
887
888 template<typename ImageType>
889 static void filter(const vpImage<ImageType> &I, vpImage<ImageType> &Iu, vpImage<ImageType> &Iv, const vpArray2D<vpRGBa> &M, bool convolve);
890#endif
891
892 static void sepFilter(const vpImage<unsigned char> &I, vpImage<double> &If, const vpColVector &kernelH, const vpColVector &kernelV);
893
903 template <typename ImageType, typename FilterType>
904 static void filter(const vpImage<ImageType> &I, vpImage<FilterType> &GI, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
905 {
907 filterX<ImageType, FilterType>(I, GIx, filter, size, p_mask);
908 filterY<FilterType, FilterType>(GIx, GI, filter, size, p_mask);
909 GIx.destroy();
910 }
911
912 static inline unsigned char filterGaussXPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
913 {
914 const int val2 = 2;
915 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.);
916 }
917 static inline unsigned char filterGaussYPyramidal(const vpImage<unsigned char> &I, unsigned int i, unsigned int j)
918 {
919 const int val2 = 2;
920 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.);
921 }
922
923#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
924 template <typename ImageType, typename FilterType>
925 static void filterX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size,
926 const vpImage<bool> *p_mask = nullptr)
927 {
928 const unsigned int height = I.getHeight();
929 const unsigned int width = I.getWidth();
930 const unsigned int stop1J = (size - 1) / 2;
931 const unsigned int stop2J = width - ((size - 1) / 2);
932 resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
933
934 for (unsigned int i = 0; i < height; ++i) {
935 for (unsigned int j = 0; j < stop1J; ++j) {
936 // We have to compute the value for each pixel if we don't have a mask or for
937 // pixels for which the mask is true otherwise
938 bool computeVal = checkBooleanMask(p_mask, i, j);
939 if (computeVal) {
940 dIx[i][j] = vpImageFilter::filterXLeftBorder<ImageType, FilterType>(I, i, j, filter, size);
941 }
942 }
943 for (unsigned int j = stop1J; j < stop2J; ++j) {
944 // We have to compute the value for each pixel if we don't have a mask or for
945 // pixels for which the mask is true otherwise
946 bool computeVal = checkBooleanMask(p_mask, i, j);
947 if (computeVal) {
948 dIx[i][j] = vpImageFilter::filterX<ImageType, FilterType>(I, i, j, filter, size);
949 }
950 }
951 for (unsigned int j = stop2J; j < width; ++j) {
952 // We have to compute the value for each pixel if we don't have a mask or for
953 // pixels for which the mask is true otherwise
954 bool computeVal = checkBooleanMask(p_mask, i, j);
955 if (computeVal) {
956 dIx[i][j] = vpImageFilter::filterXRightBorder<ImageType, FilterType>(I, i, j, filter, size);
957 }
958 }
959 }
960 }
961
962 static void filterX(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
963
964 template<typename ImageType, typename FilterType>
965 static inline FilterType filterX(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
966 {
967 const unsigned int stop = (size - 1) / 2;
968 FilterType result = static_cast<FilterType>(0.);
969
970 for (unsigned int i = 1; i <= stop; ++i) {
971 result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
972 }
973 return result + (filter[0] * static_cast<FilterType>(I[r][c]));
974 }
975
976#ifndef DOXYGEN_SHOULD_SKIP_THIS
977 static void filterXR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
978 static void filterXG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
979 static void filterXB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
980
981 static double filterXR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
982 static double filterXG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
983 static double filterXB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
984
985 static double filterXLeftBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
986 static double filterXLeftBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
987 static double filterXLeftBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
988 static double filterXRightBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
989 static double filterXRightBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
990 static double filterXRightBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
991
992 template <typename ImageType, typename FilterType>
993 static inline FilterType filterXLeftBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
994 const FilterType *filter, unsigned int size)
995 {
996 const unsigned int stop = (size - 1) / 2;
997 FilterType result = static_cast<FilterType>(0.);
998
999 for (unsigned int i = 1; i <= stop; ++i) {
1000 if (c > i) {
1001 result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
1002 }
1003 else {
1004 result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][i - c]);
1005 }
1006 }
1007 return result + (filter[0] * static_cast<FilterType>(I[r][c]));
1008 }
1009
1010 template <typename ImageType, typename FilterType>
1011 static inline FilterType filterXRightBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
1012 const FilterType *filter, unsigned int size)
1013 {
1014 const unsigned int stop = (size - 1) / 2;
1015 const unsigned int width = I.getWidth();
1016 FilterType result = static_cast<FilterType>(0.);
1017 const unsigned int twice = 2;
1018
1019 for (unsigned int i = 1; i <= stop; ++i) {
1020 if ((c + i) < width) {
1021 result += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
1022 }
1023 else {
1024 result += filter[i] * static_cast<FilterType>(I[r][((twice * width) - c) - i - 1] + I[r][c - i]);
1025 }
1026 }
1027 return result + (filter[0] * static_cast<FilterType>(I[r][c]));
1028 }
1029#endif
1030#else
1031#ifndef DOXYGEN_SHOULD_SKIP_THIS
1041 template<class Color, typename FilterType>
1042 static inline typename std::enable_if<!std::is_same<Color, vpRGBa>::value, void>::type
1043 filterChannel(const Color &in, vpColVector &out, const FilterType &coeff)
1044 {
1045 out[0] = coeff * vpColorGetter<0>::get(in);
1046 out[1] = coeff * vpColorGetter<1>::get(in);
1047 out[2] = coeff * vpColorGetter<2>::get(in);
1048 }
1049
1058 template<class Color, typename FilterType>
1059 static inline typename std::enable_if<std::is_same<Color, vpRGBa>::value, void>::type
1060 filterChannel(const Color &in, vpColVector &out, const FilterType &coeff)
1061 {
1062 out[0] = coeff * vpColorGetter<0>::get(in);
1063 out[1] = coeff * vpColorGetter<1>::get(in);
1064 out[2] = coeff * vpColorGetter<2>::get(in);
1065 out[3] = vpRGBa::alpha_default;
1066 }
1067
1078 template<class Color, typename FilterType>
1079 inline static void
1080 filterChannel(const Color &in1, const Color &in2, vpColVector &out, const FilterType &coeff)
1081 {
1082 out[0] += coeff * (vpColorGetter<0>::get(in1) + vpColorGetter<0>::get(in2));
1083 out[1] += coeff * (vpColorGetter<1>::get(in1) + vpColorGetter<1>::get(in2));
1084 out[2] += coeff * (vpColorGetter<2>::get(in1) + vpColorGetter<2>::get(in2));
1085 }
1086#endif
1087
1101 template<typename ImageType, typename OutputType, typename FilterType>
1102 static void filterX(const vpImage<ImageType> &I, vpImage<OutputType> &dIx, const FilterType *filter, unsigned int size,
1103 const vpImage<bool> *p_mask = nullptr)
1104 {
1105 const int height = static_cast<int>(I.getHeight());
1106 const int width = static_cast<int>(I.getWidth());
1107 const int stop1J = static_cast<int>((size - 1) / 2);
1108 const int stop2J = static_cast<int>(width - ((size - 1) / 2));
1109 resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1110
1111 int istart = 0;
1112 int istop = height;
1113#ifdef VISP_HAVE_OPENMP
1114 int iam, nt, ipoints, npoints(height);
1115#pragma omp parallel default(shared) private(iam, nt, ipoints, istart, istop)
1116 {
1117 iam = omp_get_thread_num();
1118 nt = omp_get_num_threads();
1119 ipoints = npoints / nt;
1120 // size of partition
1121 istart = iam * ipoints; // starting array index
1122 if (iam == nt-1) {
1123 // last thread may do more
1124 ipoints = npoints - istart;
1125 }
1126 istop = istart + ipoints;
1127#endif
1128 for (int i = istart; i < istop; ++i) {
1129 for (int j = 0; j < stop1J; ++j) {
1130 // We have to compute the value for each pixel if we don't have a mask or for
1131 // pixels for which the mask is true otherwise
1132 bool computeVal = checkBooleanMask(p_mask, i, j);
1133 if (computeVal) {
1134 vpImageFilter::filterXLeftBorder(I, dIx[i][j], i, j, filter, size);
1135 }
1136 }
1137
1138 for (int j = stop1J; j < stop2J; ++j) {
1139 // We have to compute the value for each pixel if we don't have a mask or for
1140 // pixels for which the mask is true otherwise
1141 bool computeVal = checkBooleanMask(p_mask, i, j);
1142 if (computeVal) {
1143 vpImageFilter::filterX(I, dIx[i][j], i, j, filter, size);
1144 }
1145 }
1146 for (int j = stop2J; j < width; ++j) {
1147 // We have to compute the value for each pixel if we don't have a mask or for
1148 // pixels for which the mask is true otherwise
1149 bool computeVal = checkBooleanMask(p_mask, i, j);
1150 if (computeVal) {
1151 vpImageFilter::filterXRightBorder(I, dIx[i][j], i, j, filter, size);
1152 }
1153 }
1154 }
1155#ifdef VISP_HAVE_OPENMP
1156 }
1157#endif
1158 }
1159
1174 template<typename ImageType, typename OutputType, typename FilterType>
1175 static inline typename std::enable_if<std::is_arithmetic<ImageType>::value, void>::type
1176 filterX(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1177 {
1178 const unsigned int stop = (size - 1) / 2;
1179 FilterType res = filter[0] * static_cast<FilterType>(I[r][c]);
1180
1181 for (unsigned int i = 1; i <= stop; ++i) {
1182 res += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
1183 }
1184 result = static_cast<FilterType>(res);
1185 }
1186
1201 template<typename ImageType, typename OutputType, typename FilterType>
1202 static inline typename std::enable_if<!std::is_arithmetic<ImageType>::value, void>::type
1203 filterX(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1204 {
1205 const unsigned int stop = (size - 1) / 2;
1206#ifdef VISP_HAVE_OPENMP
1207 vpColVector res(ImageType::nbChannels);
1208#else
1209 static vpColVector res(ImageType::nbChannels);
1210#endif
1211 filterChannel(I[r][c], res, filter[0]);
1212
1213 for (unsigned int i = 1; i <= stop; ++i) {
1214 filterChannel(I[r][c + i], I[r][c - i], res, filter[i]);
1215 }
1216 result = OutputType(res);
1217 }
1218
1219#ifndef DOXYGEN_SHOULD_SKIP_THIS
1235 template<typename ImageType, typename OutputType, typename FilterType>
1236 static inline typename std::enable_if<std::is_arithmetic<ImageType>::value, void>::type
1237 filterXLeftBorder(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1238 {
1239 const unsigned int stop = (size - 1) / 2;
1240 FilterType res = filter[0] * static_cast<FilterType>(I[r][c]);
1241
1242 for (unsigned int i = 1; i <= stop; ++i) {
1243 if (c > i) {
1244 res += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
1245 }
1246 else {
1247 res += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][i - c]);
1248 }
1249 }
1250 result = static_cast<OutputType>(res);
1251 }
1252
1268 template<typename ImageType, typename OutputType, typename FilterType>
1269 static inline typename std::enable_if<!std::is_arithmetic<ImageType>::value, void>::type
1270 filterXLeftBorder(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1271 {
1272 const unsigned int stop = (size - 1) / 2;
1273#ifdef VISP_HAVE_OPENMP
1274 vpColVector res(ImageType::nbChannels);
1275#else
1276 static vpColVector res(ImageType::nbChannels);
1277#endif
1278 filterChannel(I[r][c], res, filter[0]);
1279
1280 for (unsigned int i = 1; i <= stop; ++i) {
1281 if (c > i) {
1282 filterChannel(I[r][c + i], I[r][c - i], res, filter[i]);
1283 }
1284 else {
1285 filterChannel(I[r][c + i], I[r][i - c], res, filter[i]);
1286 }
1287 }
1288 result = OutputType(res);
1289 }
1290
1306 template<typename ImageType, typename OutputType, typename FilterType>
1307 static inline typename std::enable_if<std::is_arithmetic<ImageType>::value, void>::type
1308 filterXRightBorder(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1309 {
1310 const unsigned int stop = (size - 1) / 2;
1311 const unsigned int width = I.getWidth();
1312 const unsigned int twice = 2;
1313 FilterType res = filter[0] * static_cast<FilterType>(I[r][c]);
1314
1315 for (unsigned int i = 1; i <= stop; ++i) {
1316 if ((c + i) < width) {
1317 res += filter[i] * static_cast<FilterType>(I[r][c + i] + I[r][c - i]);
1318 }
1319 else {
1320 res += filter[i] * static_cast<FilterType>(I[r][((twice * width) - c) - i - 1] + I[r][c - i]);
1321 }
1322 }
1323 result = static_cast<FilterType>(res);
1324 }
1325
1341 template<typename ImageType, typename OutputType, typename FilterType>
1342 static inline typename std::enable_if<!std::is_arithmetic<ImageType>::value, void>::type
1343 filterXRightBorder(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1344 {
1345 const unsigned int stop = (size - 1) / 2;
1346 const unsigned int width = I.getWidth();
1347 const unsigned int twice = 2;
1348#ifdef VISP_HAVE_OPENMP
1349 vpColVector res(ImageType::nbChannels);
1350#else
1351 static vpColVector res(ImageType::nbChannels);
1352#endif
1353 filterChannel(I[r][c], res, filter[0]);
1354
1355 for (unsigned int i = 1; i <= stop; ++i) {
1356 if ((c + i) < width) {
1357 filterChannel(I[r][c + i], I[r][c - i], res, filter[i]);
1358 }
1359 else {
1360 filterChannel(I[r][((twice * width) - c) - i - 1], I[r][c - i], res, filter[i]);
1361 }
1362 }
1363 result = OutputType(res);
1364 }
1365#endif
1366#endif
1367
1368
1369#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
1370 static void filterY(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr);
1371
1372 template<typename ImageType, typename FilterType>
1373 static void filterY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size,
1374 const vpImage<bool> *p_mask = nullptr)
1375 {
1376 const unsigned int height = I.getHeight(), width = I.getWidth();
1377 const unsigned int stop1I = (size - 1) / 2;
1378 const unsigned int stop2I = height - ((size - 1) / 2);
1379 resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1380
1381 for (unsigned int i = 0; i < stop1I; ++i) {
1382 for (unsigned int j = 0; j < width; ++j) {
1383 // We have to compute the value for each pixel if we don't have a mask or for
1384 // pixels for which the mask is true otherwise
1385 bool computeVal = checkBooleanMask(p_mask, i, j);
1386 if (computeVal) {
1387 dIy[i][j] = vpImageFilter::filterYTopBorder<ImageType, FilterType>(I, i, j, filter, size);
1388 }
1389 }
1390 }
1391 for (unsigned int i = stop1I; i < stop2I; ++i) {
1392 for (unsigned int j = 0; j < width; ++j) {
1393 // We have to compute the value for each pixel if we don't have a mask or for
1394 // pixels for which the mask is true otherwise
1395 bool computeVal = checkBooleanMask(p_mask, i, j);
1396 if (computeVal) {
1397 dIy[i][j] = vpImageFilter::filterY<ImageType, FilterType>(I, i, j, filter, size);
1398 }
1399 }
1400 }
1401 for (unsigned int i = stop2I; i < height; ++i) {
1402 for (unsigned int j = 0; j < width; ++j) {
1403 // We have to compute the value for each pixel if we don't have a mask or for
1404 // pixels for which the mask is true otherwise
1405 bool computeVal = checkBooleanMask(p_mask, i, j);
1406 if (computeVal) {
1407 dIy[i][j] = vpImageFilter::filterYBottomBorder<ImageType, FilterType>(I, i, j, filter, size);
1408 }
1409 }
1410 }
1411 }
1412
1413 template<typename ImageType, typename FilterType>
1414 static inline FilterType filterY(const vpImage<ImageType> &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1415 {
1416 const unsigned int stop = (size - 1) / 2;
1417 FilterType result = static_cast<FilterType>(0.);
1418
1419 for (unsigned int i = 1; i <= stop; ++i) {
1420 result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
1421 }
1422 return result + (filter[0] * static_cast<FilterType>(I[r][c]));
1423 }
1424#ifndef DOXYGEN_SHOULD_SKIP_THIS
1425 static void filterYR(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
1426 static void filterYG(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
1427 static void filterYB(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &dIx, const double *filter, unsigned int size);
1428
1429 static double filterYR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1430 static double filterYG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1431 static double filterYB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1432
1433 static double filterYTopBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1434 static double filterYTopBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1435 static double filterYTopBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1436 static double filterYBottomBorderR(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1437 static double filterYBottomBorderG(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1438 static double filterYBottomBorderB(const vpImage<vpRGBa> &I, unsigned int r, unsigned int c, const double *filter, unsigned int size);
1439
1440 template<typename ImageType, typename FilterType>
1441 static inline FilterType filterYTopBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
1442 const FilterType *filter, unsigned int size)
1443 {
1444 const unsigned int stop = (size - 1) / 2;
1445 FilterType result = static_cast<FilterType>(0.);
1446
1447 for (unsigned int i = 1; i <= stop; ++i) {
1448 if (r > i) {
1449 result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
1450 }
1451 else {
1452 result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[i - r][c]);
1453 }
1454 }
1455 return result + (filter[0] * static_cast<FilterType>(I[r][c]));
1456 }
1457
1458 template<typename ImageType, typename FilterType>
1459 static inline FilterType filterYBottomBorder(const vpImage<ImageType> &I, unsigned int r, unsigned int c,
1460 const FilterType *filter, unsigned int size)
1461 {
1462 const unsigned int height = I.getHeight();
1463 const unsigned int stop = (size - 1) / 2;
1464 FilterType result = static_cast<FilterType>(0.);
1465 const unsigned int twiceHeight = 2 * height;
1466 for (unsigned int i = 1; i <= stop; ++i) {
1467 if ((r + i) < height) {
1468 result += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
1469 }
1470 else {
1471 result += filter[i] * static_cast<FilterType>(I[(twiceHeight - r) - i - 1][c] + I[r - i][c]);
1472 }
1473 }
1474 return result + (filter[0] * static_cast<FilterType>(I[r][c]));
1475 }
1476#endif
1477#else
1478
1492 template<typename ImageType, typename OutputType, typename FilterType>
1493 static void filterY(const vpImage<ImageType> &I, vpImage<OutputType> &dIy, const FilterType *filter, unsigned int size,
1494 const vpImage<bool> *p_mask = nullptr)
1495 {
1496 const unsigned int height = I.getHeight(), width = I.getWidth();
1497 const unsigned int stop1I = (size - 1) / 2;
1498 const unsigned int stop2I = height - ((size - 1) / 2);
1499 resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
1500
1501 unsigned int jstart = 0;
1502 unsigned int jstop = width;
1503#ifdef VISP_HAVE_OPENMP
1504 unsigned int iam, nt, jpoints, npoints(width);
1505#pragma omp parallel default(shared) private(iam, nt, jpoints, jstart, jstop)
1506 {
1507 iam = omp_get_thread_num();
1508 nt = omp_get_num_threads();
1509 jpoints = npoints / nt;
1510 // size of partition
1511 jstart = iam * jpoints; // starting array index
1512 if (iam == nt-1) {
1513 // last thread may do more
1514 jpoints = npoints - jstart;
1515 }
1516 jstop = jstart + jpoints;
1517#endif
1518 for (unsigned int i = 0; i < stop1I; ++i) {
1519 for (unsigned int j = jstart; j < jstop; ++j) {
1520 // We have to compute the value for each pixel if we don't have a mask or for
1521 // pixels for which the mask is true otherwise
1522 bool computeVal = checkBooleanMask(p_mask, i, j);
1523 if (computeVal) {
1524 vpImageFilter::filterYTopBorder(I, dIy[i][j], i, j, filter, size);
1525 }
1526 }
1527 }
1528 for (unsigned int i = stop1I; i < stop2I; ++i) {
1529 for (unsigned int j = jstart; j < jstop; ++j) {
1530 // We have to compute the value for each pixel if we don't have a mask or for
1531 // pixels for which the mask is true otherwise
1532 bool computeVal = checkBooleanMask(p_mask, i, j);
1533 if (computeVal) {
1534 vpImageFilter::filterY(I, dIy[i][j], i, j, filter, size);
1535 }
1536 }
1537 }
1538 for (unsigned int i = stop2I; i < height; ++i) {
1539 for (unsigned int j = jstart; j < jstop; ++j) {
1540 // We have to compute the value for each pixel if we don't have a mask or for
1541 // pixels for which the mask is true otherwise
1542 bool computeVal = checkBooleanMask(p_mask, i, j);
1543 if (computeVal) {
1544 vpImageFilter::filterYBottomBorder(I, dIy[i][j], i, j, filter, size);
1545 }
1546 }
1547#ifdef VISP_HAVE_OPENMP
1548 }
1549#endif
1550 }
1551 }
1552
1567 template<typename ImageType, typename OutputType, typename FilterType>
1568 static inline typename std::enable_if<std::is_arithmetic<ImageType>::value, void>::type
1569 filterY(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1570 {
1571 const unsigned int stop = (size - 1) / 2;
1572 FilterType res = (filter[0] * static_cast<FilterType>(I[r][c]));
1573
1574 for (unsigned int i = 1; i <= stop; ++i) {
1575 res += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
1576 }
1577 result = static_cast<OutputType>(res);
1578 }
1579
1594 template<typename ImageType, typename OutputType, typename FilterType>
1595 static inline typename std::enable_if<!std::is_arithmetic<ImageType>::value, void>::type
1596 filterY(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1597 {
1598 const unsigned int stop = (size - 1) / 2;
1599#ifdef VISP_HAVE_OPENMP
1600 vpColVector res(ImageType::nbChannels);
1601#else
1602 static vpColVector res(ImageType::nbChannels);
1603#endif
1604 filterChannel(I[r][c], res, filter[0]);
1605
1606 for (unsigned int i = 1; i <= stop; ++i) {
1607 filterChannel(I[r + i][c], I[r - i][c], res, filter[i]);
1608 }
1609 result = OutputType(res);
1610 }
1611
1612#ifndef DOXYGEN_SHOULD_SKIP_THIS
1628 template<typename ImageType, typename OutputType, typename FilterType>
1629 static inline typename std::enable_if<std::is_arithmetic<ImageType>::value, void>::type
1630 filterYTopBorder(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1631 {
1632 const unsigned int stop = (size - 1) / 2;
1633 FilterType res = (filter[0] * static_cast<FilterType>(I[r][c]));
1634
1635 for (unsigned int i = 1; i <= stop; ++i) {
1636 if (r > i) {
1637 res += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
1638 }
1639 else {
1640 res += filter[i] * static_cast<FilterType>(I[r + i][c] + I[i - r][c]);
1641 }
1642 }
1643 result = static_cast<OutputType>(res);
1644 }
1645
1661 template<typename ImageType, typename OutputType, typename FilterType>
1662 static inline typename std::enable_if<!std::is_arithmetic<ImageType>::value, void>::type filterYTopBorder(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1663 {
1664 const unsigned int stop = (size - 1) / 2;
1665#ifdef VISP_HAVE_OPENMP
1666 vpColVector res(ImageType::nbChannels);
1667#else
1668 static vpColVector res(ImageType::nbChannels);
1669#endif
1670 filterChannel(I[r][c], res, filter[0]);
1671
1672 for (unsigned int i = 1; i <= stop; ++i) {
1673 if (r > i) {
1674 filterChannel(I[r + i][c], I[r - i][c], res, filter[i]);
1675 }
1676 else {
1677 filterChannel(I[r + i][c], I[i - r][c], res, filter[i]);
1678 }
1679 }
1680 result = OutputType(res);
1681 }
1682
1698 template<typename ImageType, typename OutputType, typename FilterType>
1699 static inline typename std::enable_if<std::is_arithmetic<ImageType>::value, void>::type filterYBottomBorder(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c,
1700 const FilterType *filter, unsigned int size)
1701 {
1702 const unsigned int height = I.getHeight();
1703 const unsigned int stop = (size - 1) / 2;
1704 const unsigned int twiceHeight = 2 * height;
1705 FilterType res = (filter[0] * static_cast<FilterType>(I[r][c]));
1706 for (unsigned int i = 1; i <= stop; ++i) {
1707 if ((r + i) < height) {
1708 res += filter[i] * static_cast<FilterType>(I[r + i][c] + I[r - i][c]);
1709 }
1710 else {
1711 res += filter[i] * static_cast<FilterType>(I[(twiceHeight - r) - i - 1][c] + I[r - i][c]);
1712 }
1713 }
1714 result = static_cast<OutputType>(res);
1715 }
1716
1732 template<typename ImageType, typename OutputType, typename FilterType>
1733 static inline typename std::enable_if<!std::is_arithmetic<ImageType>::value, void>::type filterYBottomBorder(const vpImage<ImageType> &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
1734 {
1735 const unsigned int stop = (size - 1) / 2;
1736 const unsigned int height = I.getHeight();
1737 const unsigned int twiceHeight = 2 * height;
1738#ifdef VISP_HAVE_OPENMP
1739 vpColVector res(ImageType::nbChannels);
1740#else
1741 static vpColVector res(ImageType::nbChannels);
1742#endif
1743 filterChannel(I[r][c], res, filter[0]);
1744
1745 for (unsigned int i = 1; i <= stop; ++i) {
1746 if ((r + i) < height) {
1747 filterChannel(I[r + i][c], I[r - i][c], res, filter[i]);
1748 }
1749 else {
1750 filterChannel(I[(twiceHeight - r) - i - 1][c], I[r - i][c], res, filter[i]);
1751 }
1752 }
1753 result = OutputType(res);
1754 }
1755#endif
1756#endif
1757
1773#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
1774 template <typename ImageType, typename OutputType, typename FilterType = float>
1775 static inline void
1776 gaussianBlur(const vpImage<ImageType> &I, vpImage<OutputType> &GI, unsigned int size = 7, FilterType sigma = 0., bool normalize = true,
1777 const vpImage<bool> *p_mask = nullptr)
1778 {
1779 if (size == 0 || size-1 > I.getWidth() || size-1 > I.getHeight()) {
1780 std::ostringstream oss;
1781 oss << "Image size (" << I.getWidth() << "x" << I.getHeight() << ") is too small for the Gaussian kernel ("
1782 << "size=" << size << "), min size is " << (size-1);
1783 throw vpException(vpException::dimensionError, oss.str());
1784 }
1785
1786 FilterType *fg = new FilterType[(size + 1) / 2];
1787 vpImageFilter::getGaussianKernel<FilterType>(fg, size, sigma, normalize);
1789 vpImageFilter::filterX<ImageType, OutputType>(I, GIx, fg, size, p_mask);
1790 vpImageFilter::filterY<OutputType, OutputType>(GIx, GI, fg, size, p_mask);
1791 GIx.destroy();
1792 delete[] fg;
1793 }
1794#else
1795 template <typename ImageType, typename OutputType>
1796 static inline void
1797 gaussianBlur(const vpImage<ImageType> &I, vpImage<OutputType> &GI, unsigned int size, float sigma, bool normalize = true,
1798 const vpImage<bool> *p_mask = nullptr)
1799 {
1800 gaussianBlur<ImageType, OutputType, float>(I, GI, size, sigma, normalize, p_mask);
1801 }
1802
1803 template <typename ImageType, typename OutputType, typename FilterType >
1804 static inline void
1805 gaussianBlur(const vpImage<ImageType> &I, vpImage<OutputType> &GI, unsigned int size = 7, FilterType sigma = 0., bool normalize = true,
1806 const vpImage<bool> *p_mask = nullptr)
1807 {
1808 if (size == 0 || size-1 > I.getWidth() || size-1 > I.getHeight()) {
1809 std::ostringstream oss;
1810 oss << "Image size (" << I.getWidth() << "x" << I.getHeight() << ") is too small for the Gaussian kernel ("
1811 << "size=" << size << "), min size is " << (size-1);
1812 throw vpException(vpException::dimensionError, oss.str());
1813 }
1814
1815 FilterType *fg = new FilterType[(size + 1) / 2];
1816 vpImageFilter::getGaussianKernel<FilterType>(fg, size, sigma, normalize);
1817 vpImage<OutputType> GIx;
1818 vpImageFilter::filterX<ImageType, OutputType>(I, GIx, fg, size, p_mask);
1819 vpImageFilter::filterY<OutputType, OutputType>(GIx, GI, fg, size, p_mask);
1820 GIx.destroy();
1821 delete[] fg;
1822 }
1823#endif
1824
1825#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
1826 static void gaussianBlur(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &GI, unsigned int size = 7, double sigma = 0., bool normalize = true,
1827 const vpImage<bool> *p_mask = nullptr);
1828#endif
1829
1837 template <class T> static double gaussianFilter(const vpImage<T> &fr, unsigned int r, unsigned int c)
1838 {
1839 const int val2 = 2;
1840 return ((15.0 * fr[r][c]) +
1841 (12.0 * (fr[r - 1][c] + fr[r][c - 1] + fr[r + 1][c] + fr[r][c + 1])) +
1842 (9.0 * (fr[r - 1][c - 1] + fr[r + 1][c - 1] + fr[r - 1][c + 1] + fr[r + 1][c + 1])) +
1843 (5.0 * (fr[r - val2][c] + fr[r][c - val2] + fr[r + val2][c] + fr[r][c + val2])) +
1844 (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] +
1845 fr[r + val2][c + 1] + fr[r - 1][c + val2] + fr[r + 1][c + val2])) +
1846 (2.0 * (fr[r - val2][c - val2] + fr[r + val2][c - val2] + fr[r - val2][c + val2] + fr[r + val2][c + val2]))) / 159.0;
1847 }
1848 // Gaussian pyramid operation
1849 static void getGaussPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1850 static void getGaussXPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1851 static void getGaussYPyramidal(const vpImage<unsigned char> &I, vpImage<unsigned char> &GI);
1852
1869 template<typename FilterType>
1870 static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1871 {
1872 const unsigned int mod2 = 2;
1873 if ((size % mod2) != 1) {
1874 throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1875 }
1876
1877 if (sigma <= 0) {
1878 sigma = static_cast<FilterType>((size - 1) / 6.0);
1879 }
1880
1881 int middle = (static_cast<int>(size) - 1) / 2;
1882 FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(static_cast<double>(sigma)));
1883 FilterType coef1 = static_cast<FilterType>(1. / (static_cast<double>(sigma) * sqrt(2. * M_PI)));
1884 FilterType v_2_sigma2 = static_cast<FilterType>(2. * static_cast<double>(sigma2));
1885 for (int i = 0; i <= middle; ++i) {
1886 filter[i] = coef1 * static_cast<FilterType>(exp(static_cast<double>(-static_cast<FilterType>(i * i) / v_2_sigma2)));
1887 }
1888 if (normalize) {
1889 // renormalization
1890 FilterType sum = 0;
1891 const unsigned int val2 = 2U;
1892 for (int i = 1; i <= middle; ++i) {
1893 sum += val2 * filter[i];
1894 }
1895 sum += filter[0];
1896
1897 for (int i = 0; i <= middle; ++i) {
1898 filter[i] = filter[i] / sum;
1899 }
1900 }
1901 }
1902
1917 template <typename FilterType>
1918 static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma = 0., bool normalize = true)
1919 {
1920 const unsigned int mod2 = 2;
1921 if ((size % mod2) != 1) {
1922 throw(vpImageException(vpImageException::incorrectInitializationError, "Bad Gaussian filter size"));
1923 }
1924
1925 if (sigma <= 0) {
1926 sigma = static_cast<FilterType>((size - 1) / 6.0);
1927 }
1928
1929 const int half = 2;
1930 int middle = (static_cast<int>(size) - 1) / half;
1931 FilterType sigma2 = static_cast<FilterType>(vpMath::sqr(static_cast<double>(sigma)));
1932 FilterType coef_1 = static_cast<FilterType>(1. / (static_cast<double>(sigma) * sqrt(2. * M_PI)));
1933 FilterType coef_1_over_2 = coef_1 / static_cast<FilterType>(2.);
1934 FilterType v_2_coef_1 = static_cast<FilterType>(2.) * coef_1;
1935 FilterType v_2_sigma2 = static_cast<FilterType>(2.) * sigma2;
1936 filter[0] = 0.;
1937 for (int i = 1; i <= middle; ++i) {
1938 FilterType i_plus_1 = static_cast<FilterType>(i + 1);
1939 FilterType i_minus_1 = static_cast<FilterType>(i - 1);
1940 filter[i] = -coef_1_over_2 * (static_cast<FilterType>(exp(-static_cast<double>(i_plus_1 * i_plus_1 / v_2_sigma2))) - static_cast<FilterType>(exp(-static_cast<double>(i_minus_1 * i_minus_1 / v_2_sigma2))));
1941 }
1942
1943 if (normalize) {
1944 FilterType sum = static_cast<FilterType>(0);
1945 for (int i = 1; i <= middle; ++i) {
1946 FilterType i_ = static_cast<FilterType>(i);
1947 sum += v_2_coef_1 * static_cast<FilterType>(exp(-static_cast<double>(i_ * i_ / v_2_sigma2)));
1948 }
1949 sum += coef_1;
1950
1951 for (int i = 1; i <= middle; ++i) {
1952 filter[i] = filter[i] / sum;
1953 }
1954 }
1955 }
1956
1957 // Gradient along X
1958 template<typename FilterType>
1959 static void getGradX(const vpImage<unsigned char> &I, vpImage<FilterType> &dIx, const vpImage<bool> *p_mask = nullptr)
1960 {
1961 const unsigned int height = I.getHeight(), width = I.getWidth();
1962 const unsigned int stopJ = width - 3;
1963 const unsigned int val_3 = 3;
1964 resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1965
1966 for (unsigned int i = 0; i < height; ++i) {
1967 for (unsigned int j = 0; j < val_3; ++j) {
1968 // If a mask is used, the image is already initialized with 0s
1969 bool computeVal = (p_mask == nullptr);
1970 if (computeVal) {
1971 dIx[i][j] = static_cast<FilterType>(0);
1972 }
1973 }
1974 for (unsigned int j = 3; j < stopJ; ++j) {
1975 // We have to compute the value for each pixel if we don't have a mask or for
1976 // pixels for which the mask is true otherwise
1977 bool computeVal = checkBooleanMask(p_mask, i, j);
1978 if (computeVal) {
1979 dIx[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterX(I, i, j));
1980 }
1981 }
1982 for (unsigned int j = stopJ; j < width; ++j) {
1983 // If a mask is used, the image is already initialized with 0s
1984 bool computeVal = (p_mask == nullptr);
1985 if (computeVal) {
1986 dIx[i][j] = static_cast<FilterType>(0);
1987 }
1988 }
1989 }
1990 }
1991
1992 template <typename ImageType, typename FilterType>
1993 static void getGradX(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
1994 {
1995 const unsigned int height = I.getHeight(), width = I.getWidth();
1996 const unsigned int stop1J = (size - 1) / 2;
1997 const unsigned int stop2J = width - ((size - 1) / 2);
1998 resizeAndInitializeIfNeeded(p_mask, height, width, dIx);
1999
2000 for (unsigned int i = 0; i < height; ++i) {
2001 for (unsigned int j = 0; j < stop1J; ++j) {
2002 // If a mask is used, the image is already initialized with 0s
2003 bool computeVal = (p_mask == nullptr);
2004 if (computeVal) {
2005 dIx[i][j] = static_cast<FilterType>(0);
2006 }
2007 }
2008 for (unsigned int j = stop1J; j < stop2J; ++j) {
2009 // We have to compute the value for each pixel if we don't have a mask or for
2010 // pixels for which the mask is true otherwise
2011 bool computeVal = checkBooleanMask(p_mask, i, j);
2012 if (computeVal) {
2014 }
2015 }
2016 for (unsigned int j = stop2J; j < width; ++j) {
2017 // If a mask is used, the image is already initialized with 0s
2018 bool computeVal = (p_mask == nullptr);
2019 if (computeVal) {
2020 dIx[i][j] = static_cast<FilterType>(0);
2021 }
2022 }
2023 }
2024 }
2025
2037 template <typename ImageType, typename FilterType>
2038 static void getGradXGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIx, const FilterType *gaussianKernel,
2039 const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
2040 {
2042 vpImageFilter::filterY<ImageType, FilterType>(I, GIy, gaussianKernel, size, p_mask);
2043 vpImageFilter::getGradX<FilterType, FilterType>(GIy, dIx, gaussianDerivativeKernel, size, p_mask);
2044 }
2045
2046#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
2059 template <typename ArithmeticType, typename FilterType, bool useFullScale>
2061 {
2062 const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
2063 GIx.resize(nbRows, nbCols, 0.);
2064 std::vector<FilterType> filter(3);
2065 FilterType scale;
2066 switch (type) {
2068 filter = { 1., 2., 1. };
2069 scale = 8.;
2070 break;
2072 filter = { 3., 10., 3. };
2073 scale = 32.;
2074 break;
2075 default:
2076 throw(vpException(vpException::badValue, "Wrong type of filtering"));
2077 }
2078
2079 for (unsigned char i = 0; i < 3; ++i) {
2080 filter[i] = filter[i] / scale;
2081 }
2082
2083#ifdef VISP_HAVE_OPENMP
2084 if (nbThread == 1) {
2085 gradientFilterXMonothread(I, GIx, filter, p_mask);
2086 }
2087 else {
2088 gradientFilterXMultithread(I, GIx, filter, nbThread, p_mask);
2089 }
2090#else
2091 (void)nbThread; // Unused paramter when OpenMP is unavailable
2092 gradientFilterXMonothread(I, GIx, filter, p_mask);
2093#endif
2094 }
2095#endif
2096
2097 // Gradient along Y
2098 template <typename FilterType>
2099 static void getGradY(const vpImage<unsigned char> &I, vpImage<FilterType> &dIy, const vpImage<bool> *p_mask = nullptr)
2100 {
2101 const unsigned int height = I.getHeight(), width = I.getWidth();
2102 const unsigned int stopI = height - 3;
2103 resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
2104 const unsigned int val_3 = 3;
2105 for (unsigned int i = 0; i < val_3; ++i) {
2106 for (unsigned int j = 0; j < width; ++j) {
2107 // We have to compute the value for each pixel if we don't have a mask or for
2108 // pixels for which the mask is true otherwise
2109 bool computeVal = checkBooleanMask(p_mask, i, j);
2110 if (computeVal) {
2111 dIy[i][j] = static_cast<FilterType>(0);
2112 }
2113 }
2114 }
2115 for (unsigned int i = 3; i < stopI; ++i) {
2116 for (unsigned int j = 0; j < width; ++j) {
2117 // We have to compute the value for each pixel if we don't have a mask or for
2118 // pixels for which the mask is true otherwise
2119 bool computeVal = checkBooleanMask(p_mask, i, j);
2120 if (computeVal) {
2121 dIy[i][j] = static_cast<FilterType>(vpImageFilter::derivativeFilterY(I, i, j));
2122 }
2123 }
2124 }
2125 for (unsigned int i = stopI; i < height; ++i) {
2126 for (unsigned int j = 0; j < width; ++j) {
2127 // We have to compute the value for each pixel if we don't have a mask or for
2128 // pixels for which the mask is true otherwise
2129 bool computeVal = checkBooleanMask(p_mask, i, j);
2130 if (computeVal) {
2131 dIy[i][j] = static_cast<FilterType>(0);
2132 }
2133 }
2134 }
2135 }
2136
2137 template <typename ImageType, typename FilterType>
2138 static void getGradY(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *filter, unsigned int size, const vpImage<bool> *p_mask = nullptr)
2139 {
2140 const unsigned int height = I.getHeight(), width = I.getWidth();
2141 const unsigned int stop1I = (size - 1) / 2;
2142 const unsigned int stop2I = height - ((size - 1) / 2);
2143 resizeAndInitializeIfNeeded(p_mask, height, width, dIy);
2144
2145 for (unsigned int i = 0; i < stop1I; ++i) {
2146 for (unsigned int j = 0; j < width; ++j) {
2147 // We have to compute the value for each pixel if we don't have a mask or for
2148 // pixels for which the mask is true otherwise
2149 bool computeVal = checkBooleanMask(p_mask, i, j);
2150 if (computeVal) {
2151 dIy[i][j] = static_cast<FilterType>(0);
2152 }
2153 }
2154 }
2155 for (unsigned int i = stop1I; i < stop2I; ++i) {
2156 for (unsigned int j = 0; j < width; ++j) {
2157 // We have to compute the value for each pixel if we don't have a mask or for
2158 // pixels for which the mask is true otherwise
2159 bool computeVal = checkBooleanMask(p_mask, i, j);
2160 if (computeVal) {
2162 }
2163 }
2164 }
2165 for (unsigned int i = stop2I; i < height; ++i) {
2166 for (unsigned int j = 0; j < width; ++j) {
2167 // We have to compute the value for each pixel if we don't have a mask or for
2168 // pixels for which the mask is true otherwise
2169 bool computeVal = checkBooleanMask(p_mask, i, j);
2170 if (computeVal) {
2171 dIy[i][j] = static_cast<FilterType>(0);
2172 }
2173 }
2174 }
2175 }
2176
2188 template <typename ImageType, typename FilterType>
2189 static void getGradYGauss2D(const vpImage<ImageType> &I, vpImage<FilterType> &dIy, const FilterType *gaussianKernel,
2190 const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage<bool> *p_mask = nullptr)
2191 {
2193 vpImageFilter::filterX<ImageType, FilterType>(I, GIx, gaussianKernel, size, p_mask);
2194 vpImageFilter::getGradY<FilterType, FilterType>(GIx, dIy, gaussianDerivativeKernel, size, p_mask);
2195 }
2196
2197#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
2210 template <typename ArithmeticType, typename FilterType, bool useFullScale>
2212 {
2213 const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
2214 GIy.resize(nbRows, nbCols);
2215 std::vector<FilterType> filter(3);
2216 FilterType scale;
2217 switch (type) {
2219 // Prewitt case
2220 filter = { 1., 1., 1. };
2221 scale = 6.;
2222 break;
2224 filter = { 1., 2., 1. };
2225 scale = 8.;
2226 break;
2228 filter = { 3., 10., 3. };
2229 scale = 32.;
2230 break;
2231 default:
2232 throw(vpException(vpException::badValue, "Wrong type of filtering"));
2233 }
2234 for (unsigned char i = 0; i < 3; ++i) {
2235 filter[i] = filter[i] / scale;
2236 }
2237
2238#ifdef VISP_HAVE_OPENMP
2239 if (nbThread == 1) {
2240 gradientFilterYMonothread(I, GIy, filter, p_mask);
2241 }
2242 else {
2243 gradientFilterYMultithread(I, GIy, filter, nbThread, p_mask);
2244 }
2245#else
2246 (void)nbThread; // Unused paramter when OpenMP is unavailable
2247 gradientFilterYMonothread(I, GIy, filter, p_mask);
2248#endif
2249 }
2250#endif
2251
2259 template <typename FilterType>
2260 inline static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
2261 {
2262 const unsigned int actualKernelSize = (size * 2) + 1;
2263 if (size != 1) {
2264 // Size = 1 => kernel_size = 2*1 + 1 = 3
2265 std::stringstream errMsg;
2266 errMsg << "Cannot get Scharr kernel of size " << actualKernelSize << " != 3";
2267 throw vpException(vpException::dimensionError, errMsg.str());
2268 }
2269
2270 vpArray2D<FilterType> ScharrY(actualKernelSize, actualKernelSize);
2271 FilterType norm = getScharrKernelY<FilterType>(ScharrY.data, size);
2272 memcpy(filter, ScharrY.t().data, ScharrY.getRows() * ScharrY.getCols() * sizeof(FilterType));
2273 return norm;
2274 }
2275
2283 template <typename FilterType>
2284 inline static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
2285 {
2286 // Scharr kernel pre-computed for the usual size
2287 static const FilterType ScharrY3x3[9] = { -3.0, -10.0, -3.0, 0.0, 0.0, 0.0, 3.0, 10.0, 3.0 };
2288
2289 if (size != 1) {
2290 // Size = 1 => kernel_size = 2*1 + 1 = 3
2291 std::stringstream errMsg;
2292 errMsg << "Cannot get Scharr kernel of size " << ((size * 2) + 1) << " != 3";
2293 throw vpException(vpException::dimensionError, errMsg.str());
2294 }
2295
2296 const unsigned int kernel_size = (size * 2) + 1;
2297 const unsigned int kernel3 = 3;
2298 if (kernel_size == kernel3) {
2299 memcpy(filter, ScharrY3x3, kernel_size * kernel_size * sizeof(FilterType));
2300 return static_cast<FilterType>(1.0 / 32.0);
2301 }
2302
2303 return static_cast<FilterType>(0.);
2304 }
2305
2313 template <typename FilterType>
2314 inline static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
2315 {
2316 const unsigned int maxSize = 20;
2317 if (size == 0) {
2318 throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
2319 }
2320 if (size > maxSize) {
2321 throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
2322 }
2323
2324 const unsigned int kernel_size = (size * 2) + 1;
2325 vpArray2D<FilterType> SobelY(kernel_size, kernel_size);
2326 FilterType norm = getSobelKernelY<FilterType>(SobelY.data, size);
2327 memcpy(filter, SobelY.t().data, SobelY.getRows() * SobelY.getCols() * sizeof(FilterType));
2328 return norm;
2329 }
2330
2338 template <typename FilterType>
2339 inline static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
2340 {
2341 // Sobel kernel pre-computed for the usual size
2342 static const FilterType SobelY3x3[9] = { -1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0 };
2343 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,
2344 0.0, 0.0, 2.0, 8.0, 12.0, 8.0, 2.0, 1.0, 4.0, 6.0, 4.0, 1.0 };
2345 static const FilterType SobelY7x7[49] = { -1, -6, -15, -20, -15, -6, -1, -4, -24, -60, -80, -60, -24, -4, -5, -30, -75,
2346 -100, -75, -30, -5, 0, 0, 0, 0, 0, 0, 0, 5, 30, 75, 100, 75, 30,
2347 5, 4, 24, 60, 80, 60, 24, 4, 1, 6, 15, 20, 15, 6, 1 };
2348 const vpArray2D<FilterType> smoothingKernel(3, 3);
2349 const unsigned int index_0 = 0;
2350 const unsigned int index_1 = 1;
2351 const unsigned int index_2 = 2;
2352 smoothingKernel[index_0][index_0] = 1.0;
2353 smoothingKernel[index_0][index_1] = 2.0;
2354 smoothingKernel[index_0][index_2] = 1.0;
2355 smoothingKernel[index_1][index_0] = 2.0;
2356 smoothingKernel[index_1][index_1] = 4.0;
2357 smoothingKernel[index_1][index_2] = 2.0;
2358 smoothingKernel[index_2][index_0] = 1.0;
2359 smoothingKernel[index_2][index_1] = 2.0;
2360 smoothingKernel[index_2][index_2] = 1.0;
2361
2362 const unsigned int maxSize = 20;
2363 if (size == 0) {
2364 throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size 0!");
2365 }
2366 if (size > maxSize) {
2367 throw vpException(vpException::dimensionError, "Cannot get Sobel kernel of size > 20!");
2368 }
2369
2370 const unsigned int kernel_size = (size * 2) + 1;
2371 FilterType scale = static_cast<FilterType>(1. / 8.); // Scale to normalize Sobel3x3
2372 const unsigned int kernel3 = 3, kernel5 = 5, kernel7 = 7;
2373 if (kernel_size == kernel3) {
2374 memcpy(filter, SobelY3x3, kernel_size * kernel_size * sizeof(FilterType));
2375 return scale;
2376 }
2377 scale *= static_cast<FilterType>(1. / 16.); // Sobel5x5 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel3x3
2378 if (kernel_size == kernel5) {
2379 memcpy(filter, SobelY5x5, kernel_size * kernel_size * sizeof(FilterType));
2380 return scale;
2381 }
2382 scale *= static_cast<FilterType>(1. / 16.); // Sobel7x7 is the convolution of smoothingKernel, which needs 1/16 scale factor, with Sobel5x5
2383 if (kernel_size == kernel7) {
2384 memcpy(filter, SobelY7x7, kernel_size * kernel_size * sizeof(FilterType));
2385 return scale;
2386 }
2387
2388 vpArray2D<FilterType> sobelY(7, 7);
2389 memcpy(sobelY.data, SobelY7x7, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
2390 for (unsigned int i = 4; i <= size; ++i) {
2391 sobelY = vpArray2D<FilterType>::conv2(sobelY, smoothingKernel, "full");
2392 // Sobel(N+1)x(N+1) is the convolution of smoothingKernel, which needs 1/16 scale factor, with SobelNxN
2393 scale *= static_cast<FilterType>(1. / 16.);
2394 }
2395
2396 memcpy(filter, sobelY.data, sobelY.getRows() * sobelY.getCols() * sizeof(FilterType));
2397
2398 return scale;
2399 }
2400
2401#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
2402 static float median(const cv::Mat &cv_I);
2403 static float median(const vpImage<unsigned char> &Isrc);
2404 static std::vector<float> median(const vpImage<vpRGBa> &Isrc);
2405#endif
2406
2407#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
2421 template <typename ArithmeticType, typename FilterType, bool useFullScale>
2423 {
2424 const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
2425 GIx.resize(nbRows, nbCols, 0.);
2426 GIy.resize(nbRows, nbCols, 0.);
2427 gradientFilterX(I, GIx, nbThread, p_mask, type);
2428 gradientFilterY(I, GIy, nbThread, p_mask, type);
2429 }
2430#endif
2431
2432private:
2443 template<typename ImageType>
2444 static void resizeAndInitializeIfNeeded(const vpImage<bool> *p_mask, const unsigned int height, const unsigned int width, vpImage<ImageType> &I)
2445 {
2446 if (p_mask == nullptr) {
2447 // Just need to resize the output image, values will be computed and overwrite what is inside the image
2448 I.resize(height, width);
2449 }
2450 else {
2451 // Need to reset the image because some points will not be computed
2452 I.resize(height, width, static_cast<ImageType>(0));
2453 }
2454 }
2455
2465 static bool checkBooleanMask(const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c)
2466 {
2467 bool computeVal = true;
2468#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
2469 if (p_mask != nullptr)
2470#else
2471 if (p_mask != NULL)
2472#endif
2473 {
2474 computeVal = (*p_mask)[r][c];
2475 }
2476 return computeVal;
2477 }
2478
2487 static bool checkBooleanMask(const vpImage<bool> *p_mask, const int &id)
2488 {
2489 bool computeVal = true;
2490#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
2491 if (p_mask != nullptr)
2492#else
2493 if (p_mask != NULL)
2494#endif
2495 {
2496 computeVal = p_mask->bitmap[id];
2497 }
2498 return computeVal;
2499 }
2500
2501 // Note that on ubuntu 12.04 __cplusplus is equal to 1 that's why in the next line we consider __cplusplus <= 199711L
2502 // and not __cplusplus == 199711L
2503#if ((__cplusplus <= 199711L) || (defined(_MSVC_LANG) && (_MSVC_LANG == 199711L))) // Check if cxx98
2504 // Helper to apply the scale to the raw values of the filters
2505 template <typename FilterType>
2506 static void scaleFilter(vpArray2D<FilterType> &filter, const float &scale)
2507 {
2508 const unsigned int nbRows = filter.getRows();
2509 const unsigned int nbCols = filter.getCols();
2510 for (unsigned int r = 0; r < nbRows; ++r) {
2511 for (unsigned int c = 0; c < nbCols; ++c) {
2512 filter[r][c] = filter[r][c] * scale;
2513 }
2514 }
2515 }
2516#endif
2517
2518#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
2533 static bool checkBooleanPatch(const vpImage<bool> *p_mask, const int &iter, const int &c,
2534 const int &h, const int &w,
2535 const bool &isGradientX)
2536 {
2537 if (!p_mask) {
2538 return true;
2539 }
2540 const int maxIter = (h - 1) * w;
2541 const int minIter = w;
2542 bool hasToCompute = p_mask->bitmap[iter];
2543 if (c < w - 1) { // We do not compute gradient on the last column
2544 hasToCompute |= p_mask->bitmap[iter + 1]; // I[r][c + 1];
2545 if (iter < maxIter) { // We do not compute gradient on the last row
2546 hasToCompute |= p_mask->bitmap[iter + w + 1]; // I[r + 1][c + 1];
2547 }
2548 }
2549
2550 if (iter < maxIter) { // We do not compute gradient on the last row
2551 hasToCompute |= p_mask->bitmap[iter + w]; // I[r + 1][c];
2552 }
2553
2554 if (isGradientX) {
2555 if (iter > minIter) { // We do not compute gradient on the first row
2556 hasToCompute |= p_mask->bitmap[iter - w]; // I[r - 1][c];
2557 if (c < w - 1) { // We do not compute gradient on the last column
2558 hasToCompute |= p_mask->bitmap[iter - w + 1]; // I[r - 1][c + 1];
2559 }
2560 }
2561 }
2562 else {
2563 if (c > 1) { // We do not compute gradient on the first column
2564 hasToCompute |= p_mask->bitmap[iter - 1]; // Checking mask[r][c - 1]
2565 if (iter < maxIter) { // We do not compute gradient on the last row
2566 hasToCompute |= p_mask->bitmap[iter + w - 1]; // Checking mask[r + 1][c - 1]
2567 }
2568 }
2569 }
2570 return hasToCompute;
2571 }
2572
2573
2574 template <typename HSVType, bool useFullScale, typename OutputType>
2575 static typename std::enable_if<std::is_arithmetic<OutputType>::value, void>::type initGradientFilterDifferenceImageX(
2576 const vpImage<vpHSV<HSVType, useFullScale>> &I, std::vector<OutputType> &Idiff
2577 )
2578 {
2579 const unsigned int nbCols = I.getCols();
2580
2581 Idiff[0] = static_cast<OutputType>(I.bitmap[1].V - I.bitmap[0].V);
2582
2583 // Computing the difference and sign for row 1 column 0
2584 Idiff[nbCols] = static_cast<OutputType>(I.bitmap[nbCols + 1].V - I.bitmap[nbCols].V);
2585
2586 for (unsigned int iter = 1; iter < nbCols - 1; ++iter) {
2587 // Computing the difference and sign for row 0
2588 OutputType distanceRow0 = static_cast<OutputType>(I.bitmap[iter + 1].V - I.bitmap[iter].V);
2589 Idiff[iter] = distanceRow0;
2590
2591 // Computing the difference and sign for row 1
2592 OutputType distanceRow1 = static_cast<OutputType>(I.bitmap[nbCols + iter + 1].V - I.bitmap[nbCols + iter].V);
2593 Idiff[nbCols + iter] = distanceRow1;
2594 }
2595 }
2596
2597 template <typename HSVType, bool useFullScale, typename OutputType>
2598 static typename std::enable_if<std::is_arithmetic<OutputType>::value, void>::type gradientFilterXMonothread(
2599 const vpImage<vpHSV<HSVType, useFullScale>> &I, vpImage<OutputType> &GI, const std::vector<OutputType> &filter,
2600 const vpImage<bool> *p_mask = nullptr
2601 )
2602 {
2603 const int nbRows = I.getRows(), nbCols = I.getCols();
2604 const int size = I.getSize();
2605 const int offsetIdiff = nbCols;
2606
2607 std::vector<OutputType> Idiff(size);
2608 initGradientFilterDifferenceImageX(I, Idiff);
2609 const int resetCounter = nbCols - 1;
2610 const int stopIter = size - (nbCols + 1);
2611 int counter = resetCounter, idCol = 0;
2612 for (int iter = nbCols; iter < stopIter; ++iter) {
2613 if (counter) {
2614 // Computing the amplitude of the difference
2615 OutputType futureDiff = 0.;
2616 if (checkBooleanPatch(p_mask, iter + offsetIdiff, idCol, nbRows, nbCols, true)) {
2617 futureDiff = static_cast<OutputType>(I.bitmap[iter + nbCols +1].V - I.bitmap[iter + offsetIdiff].V);
2618 Idiff[iter + offsetIdiff] = futureDiff;
2619 }
2620 }
2621 if (counter) {
2622 if ((counter != resetCounter)) {
2623 if (checkBooleanMask(p_mask, iter)) {
2624 OutputType gradient = 0.;
2625 int offset = iter - nbCols; // Looking in the row above first
2626 for (int i = -1; i <= 1; ++i) {
2627 // Kind of +/- (I[r + i][c + 1] - I[r + i][c]) +/- (I[r + i][c] - I[r + i][c - 1])
2628 gradient += filter[i + 1] * (Idiff[offset] + Idiff[offset - 1]);
2629 offset += nbCols; // Preparing to look in the next row
2630 }
2631 GI.bitmap[iter] = gradient;
2632 }
2633 }
2634 --counter;
2635 }
2636 else {
2637 counter = resetCounter;
2638 }
2639
2640 if (idCol < resetCounter) {
2641 ++idCol;
2642 }
2643 else {
2644 idCol = 0;
2645 }
2646 }
2647 }
2648
2649 template <typename HSVType, bool useFullScale, typename OutputType>
2650 static typename std::enable_if<std::is_arithmetic<OutputType>::value, void>::type initGradientFilterDifferenceImageY(
2651 const vpImage<vpHSV<HSVType, useFullScale>> &I, std::vector<OutputType> &Idiff
2652 )
2653 {
2654 const unsigned int nbCols = I.getCols();
2655 // Computing the sign and distance for the first row
2656 for (unsigned int iter = 0; iter < nbCols; ++iter) {
2657 OutputType distance = static_cast<OutputType>(I.bitmap[iter + nbCols].V - I.bitmap[iter].V);
2658 Idiff[iter] = distance;
2659 }
2660 // Computing the distance and sign for I[1][0]
2661 OutputType distance = static_cast<OutputType>(I.bitmap[nbCols + nbCols].V - I.bitmap[nbCols].V);
2662 Idiff[nbCols] = distance;
2663 }
2664
2665 template <typename HSVType, bool useFullScale, typename OutputType>
2666 static typename std::enable_if<std::is_arithmetic<OutputType>::value, void>::type gradientFilterYMonothread(
2667 const vpImage<vpHSV<HSVType, useFullScale>> &I, vpImage<OutputType> &GI, const std::vector<OutputType> &filter,
2668 const vpImage<bool> *p_mask = nullptr
2669 )
2670 {
2671 const int nbRows = I.getRows(), nbCols = I.getCols();
2672 const int size = I.getSize();
2673 const int offsetIdiff = 1;
2674
2675 std::vector<OutputType> Idiff(size);
2676 initGradientFilterDifferenceImageY(I, Idiff);
2677 const int resetCounter = nbCols - 1;
2678 const int stopIter = size - (nbCols + 1);
2679 int counter = resetCounter, iterSign = offsetIdiff;
2680 for (int iter = nbCols; iter < stopIter; ++iter) {
2681 // Computing the amplitude of the difference
2682 OutputType futureDiff = 0.;
2683
2684 if (checkBooleanPatch(p_mask, iter + offsetIdiff, iterSign, nbRows, nbCols, false)) {
2685 futureDiff = static_cast<OutputType>(I.bitmap[iter + nbCols +1].V - I.bitmap[iter + offsetIdiff].V);
2686 Idiff[iter + offsetIdiff] = futureDiff;
2687 }
2688
2689 if (counter) {
2690 if ((counter != resetCounter)) {
2691 if (checkBooleanMask(p_mask, iter)) {
2692 OutputType gradient = 0.;
2693 for (int i = -1; i <= 1; ++i) {
2694 // Kind of +/- (I[r + 1][c + i] - I[r][c + 1]) +/- (I[r][c + i] - I[r - 1][c + 1])
2695 gradient += filter[i + 1] * (Idiff[iter + i] + Idiff[iter - nbCols + i]);
2696 }
2697 GI.bitmap[iter] = gradient;
2698 }
2699 }
2700 --counter;
2701 }
2702 else {
2703 counter = resetCounter;
2704 }
2705 if (iterSign < resetCounter) {
2706 ++iterSign;
2707 }
2708 else {
2709 iterSign = 0;
2710 }
2711 }
2712 }
2713
2714#ifdef VISP_HAVE_OPENMP
2715 template <typename HSVType, bool useFullScale, typename OutputType>
2716 static typename std::enable_if<std::is_arithmetic<OutputType>::value, void>::type initGradientFilterDifferenceImageX(
2717 const vpImage<vpHSV<HSVType, useFullScale>> &I, std::vector<OutputType> &Idiff,
2718 const int &istart, const int &iam
2719 )
2720 {
2721 const int nbCols = I.getCols();
2722
2723 if (iam > 0) {
2724 Idiff[0] = static_cast<OutputType>(I.bitmap[istart - nbCols + 1].V - I.bitmap[istart - nbCols].V);
2725 }
2726
2727 // Computing the difference and sign for row 1 column 0, which corresponds to the current row of the image
2728 Idiff[nbCols] = static_cast<OutputType>(I.bitmap[istart + 1].V - I.bitmap[istart].V);
2729
2730 for (int iter = 1; iter < nbCols - 1; ++iter) {
2731 if (iam > 0) {
2732 // Computing the difference and sign for row 0, which corresponds to the previous row of the image
2733 OutputType distanceRow0 = static_cast<OutputType>(I.bitmap[istart - nbCols + iter + 1].V - I.bitmap[istart - nbCols + iter].V);
2734 Idiff[iter] = distanceRow0;
2735 }
2736
2737 // Computing the difference and sign for row 1, which corresponds to the current row of the image
2738 OutputType distanceRow1 = static_cast<OutputType>(I.bitmap[istart + iter + 1].V - I.bitmap[istart + iter].V);
2739 Idiff[nbCols + iter] = distanceRow1;
2740 }
2741 }
2742
2743 template <typename HSVType, bool useFullScale, typename OutputType>
2744 static typename std::enable_if<std::is_arithmetic<OutputType>::value, void>::type gradientFilterXMultithread(
2745 const vpImage<vpHSV<HSVType, useFullScale>> &I, vpImage<OutputType> &GI, const std::vector<OutputType> &filter,
2746 const int &maxNbThread, const vpImage<bool> *p_mask = nullptr)
2747 {
2748 const int nbRows = I.getRows(), nbCols = I.getCols();
2749 const int offsetIdiff = nbCols;
2750 const int resetCounter = nbCols - 1;
2751 const int nrows(nbRows - 1);
2752
2753 int nbThread = maxNbThread;
2754 if (nbThread < 0) {
2755 nbThread = omp_get_max_threads();
2756 }
2757
2758 if (static_cast<int>(nbRows) < (4 * nbThread)) {
2759 gradientFilterXMonothread(I, GI, filter, p_mask);
2760 }
2761
2762 int iam, nt, irows, rstart, istart, istop;
2763
2764#pragma omp parallel default(shared) private(iam, nt, irows, rstart, istart, istop) num_threads(nbThread)
2765 {
2766 iam = omp_get_thread_num();
2767 nt = omp_get_num_threads();
2768 irows = nrows / nt;
2769 // size of partition
2770 rstart = irows * iam;
2771 istart = rstart * nbCols; // starting array index
2772 if (iam == nt-1) {
2773 // last thread may do more
2774 irows = nrows - rstart;
2775 }
2776 istop = istart + irows * nbCols;
2777
2778 std::vector<OutputType> Idiff((irows + 2) * nbCols);
2779 std::vector<OutputType> GItemp(irows * nbCols);
2780 initGradientFilterDifferenceImageX(I, Idiff, istart, iam);
2781
2782 int counter = resetCounter, idCol = 0;
2783 int iterStart = (iam != 0 ? istart : istart + nbCols);
2784 for (int iter = iterStart; iter < istop; ++iter) {
2785 if (counter) {
2786 // Computing the amplitude of the difference
2787 OutputType futureDiff = 0.;
2788 if (checkBooleanPatch(p_mask, iter + offsetIdiff, idCol, nbRows, nbCols, true)) {
2789 futureDiff = static_cast<OutputType>(I.bitmap[iter + nbCols +1].V - I.bitmap[iter + offsetIdiff].V);
2790 Idiff[iter + 2 * offsetIdiff - istart] = futureDiff;
2791 }
2792 }
2793 if (counter) {
2794 if ((counter != resetCounter)) {
2795 if (checkBooleanMask(p_mask, iter)) {
2796 OutputType gradient = 0.;
2797 int offset = iter - istart; // Looking in the row above first
2798 for (int i = -1; i <= 1; ++i) {
2799 // Kind of +/- (I[r + i][c + 1] - I[r + i][c]) +/- (I[r + i][c] - I[r + i][c - 1])
2800 gradient += filter[i + 1] * (Idiff[offset] + Idiff[offset - 1]);
2801 offset += nbCols; // Preparing to look in the next row
2802 }
2803 GItemp[iter - istart] = gradient;
2804 }
2805 }
2806 --counter;
2807 }
2808 else {
2809 counter = resetCounter;
2810 }
2811
2812 if (idCol < resetCounter) {
2813 ++idCol;
2814 }
2815 else {
2816 idCol = 0;
2817 }
2818 }
2819#pragma omp critical
2820 {
2821 std::memcpy(GI.bitmap + istart, GItemp.data(), GItemp.size() * sizeof(OutputType));
2822 }
2823 }
2824 }
2825
2826 template <typename HSVType, bool useFullScale, typename OutputType>
2827 static typename std::enable_if<std::is_arithmetic<OutputType>::value, void>::type initGradientFilterDifferenceImageY(
2828 const vpImage<vpHSV<HSVType, useFullScale>> &I, std::vector<OutputType> &Idiff,
2829 const int &istart
2830 )
2831 {
2832 const int nbCols = I.getCols();
2833 // Computing the sign and distance for the first row, which corresponds to the row above the beginning of the gradient computation in the thread
2834 int idDiff = 0;
2835 for (int iter = istart - nbCols; iter < istart; ++iter) {
2836 OutputType distance = static_cast<OutputType>(I.bitmap[iter + nbCols].V - I.bitmap[iter].V);
2837 Idiff[idDiff] = distance;
2838 ++idDiff;
2839 }
2840 // Computing the distance and sign for I[1][0]
2841 OutputType distance = static_cast<OutputType>(I.bitmap[nbCols + nbCols].V - I.bitmap[nbCols].V);
2842 Idiff[nbCols] = distance;
2843 }
2844
2845 template <typename HSVType, bool useFullScale, typename OutputType>
2846 static typename std::enable_if<std::is_arithmetic<OutputType>::value, void>::type gradientFilterYMultithread(
2847 const vpImage<vpHSV<HSVType, useFullScale>> &I, vpImage<OutputType> &GI, const std::vector<OutputType> &filter,
2848 const int &maxNbThread, const vpImage<bool> *p_mask = nullptr)
2849 {
2850 const int nbRows = I.getRows(), nbCols = I.getCols();
2851 const int offsetIdiff = 1;
2852 const int resetCounter = nbCols - 1;
2853 const int nrows(nbRows - 1);
2854
2855 int nbThread = maxNbThread;
2856 if (nbThread < 0) {
2857 nbThread = omp_get_max_threads();
2858 }
2859
2860 if (static_cast<int>(nbRows) < (4 * nbThread)) {
2861 gradientFilterXMonothread(I, GI, filter, p_mask);
2862 }
2863
2864 int iam, nt, irows, rstart, istart, istop;
2865
2866#pragma omp parallel default(shared) private(iam, nt, irows, rstart, istart, istop) num_threads(nbThread)
2867 {
2868 iam = omp_get_thread_num();
2869 nt = omp_get_num_threads();
2870 irows = nrows / nt;
2871 // size of partition
2872 rstart = irows * iam;
2873 istart = rstart * nbCols; // starting array index
2874 if (iam == nt-1) {
2875 // last thread may do more
2876 irows = nrows - rstart;
2877 }
2878 istop = istart + irows * nbCols;
2879
2880 std::vector<OutputType> Idiff((irows + 2) * nbCols);
2881 std::vector<OutputType> GItemp(irows * nbCols);
2882
2883 if (iam == 0) {
2884 initGradientFilterDifferenceImageY(I, Idiff);
2885 }
2886 else {
2887 initGradientFilterDifferenceImageY(I, Idiff, istart);
2888 }
2889
2890 int counter = resetCounter, iterSign = offsetIdiff;
2891 int iterStart = (iam != 0 ? istart : istart + nbCols);
2892 for (int iter = iterStart; iter < istop; ++iter) {
2893 // Computing the amplitude of the difference
2894 OutputType futureDiff = 0.;
2895
2896 if (checkBooleanPatch(p_mask, iter + offsetIdiff, iterSign, nbRows, nbCols, false)) {
2897 futureDiff = static_cast<OutputType>(I.bitmap[iter + nbCols +1].V - I.bitmap[iter + offsetIdiff].V);
2898 Idiff[iter - istart + nbCols + offsetIdiff] = futureDiff;
2899 }
2900
2901 if (counter) {
2902 if ((counter != resetCounter)) {
2903 if (checkBooleanMask(p_mask, iter)) {
2904 OutputType gradient = 0.;
2905 for (int i = -1; i <= 1; ++i) {
2906 // Kind of +/- (I[r + 1][c + i] - I[r][c + 1]) +/- (I[r][c + i] - I[r - 1][c + 1])
2907 gradient += filter[i + 1] * (Idiff[iter - istart + nbCols + i] + Idiff[iter - istart + i]);
2908 }
2909 GItemp[iter - istart] = gradient;
2910 }
2911 }
2912 --counter;
2913 }
2914 else {
2915 counter = resetCounter;
2916 }
2917 if (iterSign < resetCounter) {
2918 ++iterSign;
2919 }
2920 else {
2921 iterSign = 0;
2922 }
2923 }
2924#pragma omp critical
2925 {
2926 std::memcpy(GI.bitmap + istart, GItemp.data(), GItemp.size() * sizeof(OutputType));
2927 }
2928 }
2929 }
2930#endif
2931#endif
2932};
2933#if defined(__clang__)
2934# pragma clang diagnostic pop
2935#endif
2936END_VISP_NAMESPACE
2937#endif
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition vpArray2D.h:146
unsigned int getCols() const
Definition vpArray2D.h:423
Type * data
Address of the first element of the data array.
Definition vpArray2D.h:149
static vpArray2D< Type > conv2(const vpArray2D< Type > &M, const vpArray2D< Type > &kernel, const std::string &mode)
Definition vpArray2D.h:1284
vpArray2D< Type > t() const
Compute the transpose of the array.
Definition vpArray2D.h:1273
unsigned int getRows() const
Definition vpArray2D.h:433
Implementation of column vector and the associated operations.
static ArithmeticType & get(vpHSV< ArithmeticType, useFullScale > &col)
Setter for a channel of a HSV pixel.
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 implementing the HSV pixel format.
Definition vpHSV.h:135
Class to compute a gray level image histogram.
unsigned int getTotal()
Get the total number of pixels in the input image.
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.
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...
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 void gradientFilterX(const vpImage< vpHSV< ArithmeticType, useFullScale > > &I, vpImage< FilterType > &GIx, const int &nbThread, const vpImage< bool > *p_mask, const vpImageFilter::vpCannyFilteringAndGradientType &type)
Gradient filter along the X-axis for HSV images.
static double derivativeFilterX(const vpImage< ImageType > &I, unsigned int r, unsigned int c)
static void filterY(const vpImage< ImageType > &I, vpImage< OutputType > &dIy, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
Filter along the vertical direction.
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 void filterX(const vpImage< ImageType > &I, vpImage< OutputType > &dIx, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
Filter along the horizontal direction.
static std::string vpCannyFiltAndGradTypeToStr(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
static void gradientFilter(const vpImage< vpHSV< ArithmeticType, useFullScale > > &I, vpImage< FilterType > &GIx, vpImage< FilterType > &GIy, const int &nbThread=-1, const vpImage< bool > *p_mask=nullptr, const vpImageFilter::vpCannyFilteringAndGradientType &type=CANNY_GBLUR_SCHARR_FILTERING)
Compute the horizontal and vertical gradients for HSV images.
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.
@ CANNY_GBLUR_SOBEL_FILTERING
Apply Gaussian blur + Sobel operator on the input image.
@ CANNY_GBLUR_SCHARR_FILTERING
Apply Gaussian blur + Scharr operator on the input image.
static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static void filter(const vpImage< ImageType > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false, const vpImage< bool > *p_mask=nullptr)
static std::enable_if<!std::is_arithmetic< ImageType >::value, void >::type filterY(const vpImage< ImageType > &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
Filter along the vertical direction "in the middle" of the image (i.e not on a border).
vpCannyBackendType
Canny filter backends for the edge detection operations.
@ CANNY_VISP_BACKEND
Use ViSP.
@ CANNY_OPENCV_BACKEND
Use OpenCV.
static void getGradX(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static void sepFilter(const vpImage< unsigned char > &I, vpImage< double > &If, const vpColVector &kernelH, const vpColVector &kernelV)
static FilterType derivativeFilterY(const vpImage< ImageType > &I, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
static void gaussianBlur(const vpImage< ImageType > &I, vpImage< OutputType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true, const vpImage< bool > *p_mask=nullptr)
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 std::enable_if< std::is_arithmetic< ImageType >::value, void >::type filterY(const vpImage< ImageType > &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
Filter along the vertical direction "in the middle" of the image (i.e not on a border).
static double derivativeFilterY(const vpImage< ImageType > &I, unsigned int r, unsigned int c)
static void gradientFilterY(const vpImage< vpHSV< ArithmeticType, useFullScale > > &I, vpImage< FilterType > &GIy, const int &nbThread, const vpImage< bool > *p_mask, const vpImageFilter::vpCannyFilteringAndGradientType &type)
Gradient filter along the Y-axis for HSV images.
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 std::enable_if<!std::is_arithmetic< ImageType >::value, void >::type filterX(const vpImage< ImageType > &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
Filter along the horizontal direction "in the middle" of the image (i.e not on a border).
static std::enable_if< std::is_arithmetic< ImageType >::value, void >::type filterX(const vpImage< ImageType > &I, OutputType &result, unsigned int r, unsigned int c, const FilterType *filter, unsigned int size)
Filter along the horizontal direction "in the middle" of the image (i.e not on a border).
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 getGradYGauss2D(const vpImage< ImageType > &I, vpImage< FilterType > &dIy, const FilterType *gaussianKernel, const FilterType *gaussianDerivativeKernel, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static float computeCannyThreshold(const vpImage< vpHSV< ArithmeticType, useFullScale > > &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 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 for a HSV image.
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 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 computePartialDerivatives(const cv::Mat &cv_I, cv::Mat &cv_dIx, cv::Mat &cv_dIy, const bool &computeDx=true, const bool &computeDy=true, const bool &normalize=true, const unsigned int &gaussianKernelSize=5, const float &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING)
Compute the partial derivatives (i.e. horizontal and vertical gradients) of the input image.
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)
Definition of the vpImage class member functions.
Definition vpImage.h:131
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 getSize() const
Definition vpImage.h:221
unsigned int getCols() const
Definition vpImage.h:171
Type * bitmap
points toward the bitmap
Definition vpImage.h:135
unsigned int getHeight() const
Definition vpImage.h:181
unsigned int getRows() const
Definition vpImage.h:212
static double sqr(double x)
Definition vpMath.h:203
@ alpha_default
Definition vpRGBa.h:76