Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
testHSVGradient.cpp
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 * Test vpImageFilter::gaussianBlur() new implementation and compare it to the old one.
32 */
38
39#include <iostream>
40#include <limits>
41
42#include <visp3/core/vpRGBa.h>
43#include <visp3/core/vpHSV.h>
44#include <visp3/core/vpImage.h>
45#include <visp3/core/vpImageFilter.h>
46
47#include "hsvUtils.h"
48
49#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
50#include <type_traits>
51
52#ifdef ENABLE_VISP_NAMESPACE
53using namespace VISP_NAMESPACE_NAME;
54#endif
55
56#ifndef DOXYGEN_SHOULD_SKIP_THIS
57void gradientFilter(const vpImage<unsigned char> &I, const vpImage<double> &filter, vpImage<double> &GIy)
58{
59 const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
60 for (unsigned int r = 1; r < nbRows - 1; ++r) {
61 for (unsigned int c = 1; c < nbCols - 1; ++c) {
62 for (unsigned int i = 0; i <= 2; ++i) {
63 for (unsigned int j = 0; j <= 2; ++j) {
64 GIy[r][c] += filter[i][j] * I[r-1 + i][c-1 + j];
65 }
66 }
67 }
68 }
69}
70
71void gradientFilter(const vpImage<unsigned char> &I, const vpImage<double> &filterX, const vpImage<double> &filterY, vpImage<double> &GIx, vpImage<double> &GIy)
72{
73 const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
74 GIx.resize(nbRows, nbCols, 0.);
75 GIy.resize(nbRows, nbCols, 0.);
76 gradientFilter(I, filterX, GIx);
77 gradientFilter(I, filterY, GIy);
78}
79
80static bool checkBooleanMask(const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c)
81{
82 bool computeVal = true;
83 if (p_mask != nullptr) {
84 computeVal = (*p_mask)[r][c];
85 }
86 return computeVal;
87}
88
89template <typename ArithmeticType, typename FilterType, bool useFullScale>
91{
92 const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
93 GIx.resize(nbRows, nbCols, 0.);
94 std::vector<FilterType> filter(3);
95 FilterType scale;
96 std::string name;
97 switch (type) {
99 filter = { 1., 2., 1. };
100 scale = 8.;
101 name = "Sobel";
102 break;
104 filter = { 3., 10., 3. };
105 scale = 32.;
106 name = "Scharr";
107 break;
108 default:
109 throw(vpException(vpException::badValue, "Wrong type of filtering"));
110 }
111 // std::cout << "Using " << name << " filter" << std::endl;
112 for (unsigned char i = 0; i < 3; ++i) {
113 filter[i] = filter[i] / scale;
114 }
115
116 auto checkBooleanPatch = [](const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c, const unsigned int &h, const unsigned int &w)
117 {
118 if (!p_mask) {
119 return true;
120 }
121 bool hasToCompute = (*p_mask)[r][c];
122
123 if (c < w - 1) { // We do not compute gradient on the last column
124 hasToCompute |= (*p_mask)[r][c + 1];
125 if (r < h - 1) { // We do not compute gradient on the last row
126 hasToCompute |= (*p_mask)[r + 1][c + 1];
127 }
128 }
129
130 if (r < h - 1) { // We do not compute gradient on the last row
131 hasToCompute |= (*p_mask)[r + 1][c];
132 }
133
134 if (r > 1) { // We do not compute gradient on the first row
135 hasToCompute |= (*p_mask)[r - 1][c];
136 if (c < w - 1) { // We do not compute gradient on the last column
137 hasToCompute |= (*p_mask)[r - 1][c + 1];
138 }
139 }
140 return hasToCompute;
141 };
142
143 const unsigned int rStop = nbRows - 1, cStop = nbCols - 1;
144 vpImage<double> IabsDiff(nbRows, nbCols);
145
146 // Computation of the rest of the image
147 for (unsigned int r = 1; r < rStop; ++r) {
148 // Computation for I[r][0]
149 if (checkBooleanPatch(p_mask, r, 0, nbRows, nbCols)) {
150 IabsDiff[r][0] = I[r][1].V - I[r][0].V;
151 }
152
153 // Computation for all the other columns
154 for (unsigned int c = 1; c < cStop; ++c) {
155 if (checkBooleanPatch(p_mask, r, c, nbRows, nbCols)) {
156 // Of the absolute value of the distance
157 IabsDiff[r][c] = I[r][c + 1].V - I[r][c].V;
158 }
159 }
160 }
161
162 for (unsigned int r = 1; r < rStop; ++r) {
163 for (unsigned int c = 1; c < cStop; ++c) {
164 if (checkBooleanMask(p_mask, r, c)) {
165 GIx[r][c] = 0.;
166 for (int dr = -1; dr <= 1; ++dr) {
167 GIx[r][c] += filter[dr + 1] * (IabsDiff[r + dr][c - 1] + IabsDiff[r + dr][c]);
168 }
169 }
170 }
171 }
172}
173
174template <typename ArithmeticType, typename FilterType, bool useFullScale>
176{
177 const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
178 std::vector<FilterType> filter(3);
179 FilterType scale;
180 switch (type) {
182 filter = { 1., 2., 1. };
183 scale = 8.;
184 break;
186 filter = { 3., 10., 3. };
187 scale = 32.;
188 break;
189 default:
190 throw(vpException(vpException::badValue, "Wrong type of filtering"));
191 }
192 for (unsigned char i = 0; i < 3; ++i) {
193 filter[i] = filter[i] / scale;
194 }
195
196 const unsigned int rStop = nbRows - 1, cStop = nbCols - 1;
197 vpImage<double> IabsDiff(nbRows, nbCols);
198
199 auto checkBooleanPatch = [](const vpImage<bool> *p_mask, const unsigned int &r, const unsigned int &c, const unsigned int &h, const unsigned int &w)
200 {
201 if (!p_mask) {
202 return true;
203 }
204
205 bool hasToCompute = (*p_mask)[r][c];
206 if (c < w - 1) { // We do not compute gradient on the last column
207 hasToCompute |= (*p_mask)[r][c + 1];
208 if (r < h - 1) { // We do not compute gradient on the last row
209 hasToCompute |= (*p_mask)[r + 1][c + 1];
210 }
211 }
212
213 if (r < h - 1) { // We do not compute gradient on the last row
214 hasToCompute |= (*p_mask)[r + 1][c];
215 }
216
217 if (c > 1) { // We do not compute gradient on the first column
218 hasToCompute |= (*p_mask)[r][c - 1];
219 if (r < h - 1) { // We do not compute gradient on the last row
220 hasToCompute |= (*p_mask)[r + 1][c - 1];
221 }
222 }
223 return hasToCompute;
224 };
225
226 // Computation for the first row
227 for (unsigned int c = 0; c < nbCols; ++c) {
228 if (checkBooleanPatch(p_mask, 0, c, nbRows, nbCols)) {
229 IabsDiff[0][c] = I[1][c].V - I[0][c].V;
230 }
231 }
232
233 // Computation for the rest of the image of d and sign
234 for (unsigned int r = 1; r < rStop; ++r) {
235 for (unsigned int c = 0; c < nbCols; ++c) {
236 // Of the absolute value of the distance
237 if (checkBooleanPatch(p_mask, r, c, nbRows, nbCols)) {
238 IabsDiff[r][c] = I[r + 1][c].V - I[r][c].V;
239 }
240 }
241 }
242
243 // Computation of the gradient
244 for (unsigned int r = 1; r < rStop; ++r) {
245 for (unsigned int c = 1; c < cStop; ++c) {
246 if (checkBooleanMask(p_mask, r, c)) {
247 GIy[r][c] = 0.;
248 for (int dc = -1; dc <= 1; ++dc) {
249 GIy[r][c] += filter[dc + 1] * (IabsDiff[r - 1][c + dc] + IabsDiff[r][c + dc]);
250 }
251 }
252 }
253 }
254}
255
256template <typename ArithmeticType, bool useFullScale>
258{
259 const unsigned int nbRows = I.getRows(), nbCols = I.getCols();
260 GIx.resize(nbRows, nbCols, 0.);
261 GIy.resize(nbRows, nbCols, 0.);
262 gradientFilterX(I, GIx, p_mask, type);
263 gradientFilterY(I, GIy, p_mask, type);
264}
265#endif
266
267int main()
268{
269 bool isSuccess = true;
270
271 // Inputs
272 vpHSVTests::vpInputDataset dataset;
273 bool useSobel = false;
274 vpImage<double> filterX(3, 3);
275 filterX[0][0] = -1.; filterX[0][1] = 0.; filterX[0][2] = 1.;
276 filterX[1][0] = (useSobel ? -2. : -1.); filterX[1][1] = 0.; filterX[1][2] = (useSobel ? 2. : 1.);
277 filterX[2][0] = -1.; filterX[2][1] = 0.; filterX[2][2] = 1.;
278
279 vpImage<double> filterY(3, 3);
280 filterY[0][0] = -1.; filterY[0][1] = (useSobel ? -2. : -1.); filterY[0][2] = -1.;
281 filterY[1][0] = 0.; filterY[1][1] = 0.; filterY[1][2] = 0.;
282 filterY[2][0] = 1.; filterY[2][1] = (useSobel ? 2. : 1.); filterY[2][2] = 1.;
283
284 // Outputs
285 vpImage<double> GIx, GIx_ref, GIy, GIy_ref;
286
287 std::vector<vpImageFilter::vpCannyFilteringAndGradientType> types = {
290 };
291
292 std::vector<int> nbThreads = { 1, 2 };
293
294 vpImage<bool> *p_mask = nullptr;
295 for (unsigned int i = 0; i < 2; ++i) {
296 if (i == 1) {
297 p_mask = &dataset.m_Imask;
298 }
299 for (auto type: types) {
300 for (auto nbThread: nbThreads) {
301 for (auto input: dataset.m_hsvUCtrue) {
302 vpImageFilter::gradientFilter(input.second.m_I, GIx, GIy, nbThread, p_mask, type);
303 gradientFilter(input.second.m_I, GIx_ref, GIy_ref, p_mask, type);
304 bool isSuccessGIx = vpHSVTests::areAlmostEqual(GIx, "GIx", GIx_ref, "GIx_ref");
305 bool isSuccessGIy = vpHSVTests::areAlmostEqual(GIy, "GIy", GIy_ref, "GIy_ref");
306 isSuccess = isSuccess && isSuccessGIx && isSuccessGIy;
307 if (!isSuccessGIx) {
308 std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along X on HSV<uchar, true> failed ! " << std::endl;
309 }
310 if (!isSuccessGIy) {
311 std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along Y on HSV<uchar, true> failed ! " << std::endl;
312 }
313 if (!(isSuccessGIx && isSuccessGIy)) {
314 std::cout << "Type: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << std::endl;
315 std::cout << "nbThread: " << nbThread << std::endl;
316 std::cout << "mask ? : " << (p_mask ? std::string("true") : std::string("false")) << std::endl;
317 vpHSVTests::print(input.second.m_I, input.first);
318 vpHSVTests::print(GIx, "GIx");
319 vpHSVTests::print(GIy, "GIy");
320 vpHSVTests::print(GIx_ref, "GIx_ref");
321 vpHSVTests::print(GIy_ref, "GIy_ref");
322 }
323 }
324
325 for (auto input: dataset.m_hsvUCfalse) {
326 vpImageFilter::gradientFilter(input.second.m_I, GIx, GIy, nbThread, p_mask, type);
327 gradientFilter(input.second.m_I, GIx_ref, GIy_ref, p_mask, type);
328 bool isSuccessGIx = vpHSVTests::areAlmostEqual(GIx, "GIx", GIx_ref, "GIx_ref");
329 bool isSuccessGIy = vpHSVTests::areAlmostEqual(GIy, "GIy", GIy_ref, "GIy_ref");
330 isSuccess = isSuccess && isSuccessGIx && isSuccessGIy;
331 if (!isSuccessGIx) {
332 std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along X on HSV<uchar, false> failed ! " << std::endl;
333 }
334 if (!isSuccessGIy) {
335 std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along Y on HSV<uchar, false> failed ! " << std::endl;
336 }
337 if (!(isSuccessGIx && isSuccessGIy)) {
338 std::cout << "Type: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << std::endl;
339 std::cout << "nbThread: " << nbThread << std::endl;
340 std::cout << "mask ? : " << (p_mask ? std::string("true") : std::string("false")) << std::endl;
341 vpHSVTests::print(input.second.m_I, input.first);
342 vpHSVTests::print(GIx, "GIx");
343 vpHSVTests::print(GIy, "GIy");
344 vpHSVTests::print(GIx_ref, "GIx_ref");
345 vpHSVTests::print(GIy_ref, "GIy_ref");
346 }
347 }
348
349 for (auto input: dataset.m_hsvDouble) {
350 vpImageFilter::gradientFilter(input.second.m_I, GIx, GIy, nbThread, p_mask, type);
351 gradientFilter(input.second.m_I, GIx_ref, GIy_ref, p_mask, type);
352 bool isSuccessGIx = vpHSVTests::areAlmostEqual(GIx, "GIx", GIx_ref, "GIx_ref");
353 bool isSuccessGIy = vpHSVTests::areAlmostEqual(GIy, "GIy", GIy_ref, "GIy_ref");
354 isSuccess = isSuccess && isSuccessGIx && isSuccessGIy;
355 if (!isSuccessGIx) {
356 std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along X on HSV<double> failed ! " << std::endl;
357 }
358 if (!isSuccessGIy) {
359 std::cerr << "ERROR: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << " along Y on HSV<double> failed ! " << std::endl;
360 }
361 if (!(isSuccessGIx && isSuccessGIy)) {
362 std::cout << "Type: " << vpImageFilter::vpCannyFiltAndGradTypeToStr(type) << std::endl;
363 std::cout << "nbThread: " << nbThread << std::endl;
364 std::cout << "mask ? : " << (p_mask ? std::string("true") : std::string("false")) << std::endl;
365 vpHSVTests::print(input.second.m_I, input.first);
366 vpHSVTests::print(GIx, "GIx");
367 vpHSVTests::print(GIy, "GIy");
368 vpHSVTests::print(GIx_ref, "GIx_ref");
369 vpHSVTests::print(GIy_ref, "GIy_ref");
370 }
371 }
372 }
373 }
374 }
375
376 if (isSuccess) {
377 std::cout << "All tests were successful !" << std::endl;
378 return EXIT_SUCCESS;
379 }
380 std::cerr << "ERROR: Something went wrong !" << std::endl;
381 return EXIT_FAILURE;
382}
383
384#else
385int main()
386{
387 std::cout << "vpHSV class is not available, please use CXX 11 standard" << std::endl;
388 return EXIT_SUCCESS;
389}
390#endif
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
Class implementing the HSV pixel format.
Definition vpHSV.h:135
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.
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.
Definition of the vpImage class member functions.
Definition vpImage.h:131
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition vpImage.h:544