Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
testImageGetValue.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 for vpImagePoint::getValue().
32 */
38
39#include <iostream>
40#include <visp3/core/vpImage.h>
41
42#ifdef ENABLE_VISP_NAMESPACE
43using namespace VISP_NAMESPACE_NAME;
44#endif
45
46namespace
47{
48template <typename PixelType> PixelType checkPixelAccess(unsigned int height, unsigned int width, double v, double u)
49{
50 vpImage<PixelType> I(height, width);
51 for (unsigned int i = 0; i < I.getHeight(); i++) {
52 for (unsigned int j = 0; j < I.getWidth(); j++) {
53 I[i][j] = static_cast<PixelType>(i * I.getWidth() + j);
54 }
55 }
56
57 return I.getValue(v, u);
58}
59
60template <> vpRGBa checkPixelAccess(unsigned int height, unsigned int width, double v, double u)
61{
62 vpImage<vpRGBa> I(height, width);
63 for (unsigned int i = 0; i < I.getHeight(); i++) {
64 for (unsigned int j = 0; j < I.getWidth(); j++) {
65 I[i][j] =
66 vpRGBa(static_cast<unsigned char>(i * I.getWidth() + j), static_cast<unsigned char>(i * I.getWidth() + j),
67 static_cast<unsigned char>(i * I.getWidth() + j));
68 }
69 }
70
71 return I.getValue(v, u);
72}
73
74double randomDouble(double a, double b)
75{
76 double random = (static_cast<double>(rand())) / static_cast<double>(RAND_MAX);
77 double diff = b - a;
78 double r = random * diff;
79 return a + r;
80}
81
82unsigned char randomPixelValue()
83{
84 const int min = 0, max = 255;
85 return static_cast<unsigned char>((rand() % (max - min + 1) + min));
86}
87
88template <class PixelType> PixelType getValue(const vpImage<PixelType> &I, double i, double j, bool roundValue)
89{
90 if (i < 0 || j < 0 || i + 1 > I.getHeight() || j + 1 > I.getWidth()) {
91 throw(vpException(vpImageException::notInTheImage, "Pixel outside of the image"));
92 }
93 if (I.getHeight() * I.getWidth() == 0) {
95 }
96
97 unsigned int iround = static_cast<unsigned int>(floor(i));
98 unsigned int jround = static_cast<unsigned int>(floor(j));
99
100 double rratio = i - static_cast<double>(iround);
101 double cratio = j - static_cast<double>(jround);
102
103 double rfrac = 1.0 - rratio;
104 double cfrac = 1.0 - cratio;
105
106 unsigned int iround_1 = std::min<unsigned int>(I.getHeight() - 1, iround + 1);
107 unsigned int jround_1 = std::min<unsigned int>(I.getWidth() - 1, jround + 1);
108
109 double value =
110 (static_cast<double>(I[iround][jround]) * rfrac + static_cast<double>(I[iround_1][jround]) * rratio) * cfrac +
111 (static_cast<double>(I[iround][jround_1]) * rfrac + static_cast<double>(I[iround_1][jround_1]) * rratio) * cratio;
112
113 return static_cast<PixelType>(roundValue ? vpMath::round(value) : value);
114}
115} // namespace
116
117int main()
118{
119 // Test out of image memory access
120 // vpImage::getValue(double, double)
121 {
122 // unsigned char
123 std::cout << "checkPixelAccess<unsigned char>(3, 4, 2, 3): "
124 << static_cast<unsigned int>(checkPixelAccess<unsigned char>(3, 4, 2, 3)) << std::endl;
125 try {
126 std::cout << "checkPixelAccess<unsigned char>(3, 4, -2, -3): "
127 << static_cast<unsigned int>(checkPixelAccess<unsigned char>(3, 4, -2, -3)) << std::endl;
128 std::cerr << "Out of image access exception should have been thrown" << std::endl;
129 return EXIT_FAILURE;
130 }
131 catch (...) {
132 std::cout << "\n";
133 }
134 try {
135 std::cout << "checkPixelAccess<unsigned char>(3, 4, 3, 4): "
136 << static_cast<unsigned int>(checkPixelAccess<unsigned char>(3, 4, 3, 4)) << std::endl;
137 std::cerr << "Out of image access exception should have been thrown" << std::endl;
138 return EXIT_FAILURE;
139 }
140 catch (...) {
141 std::cout << "\n";
142 }
143
144 // vpRGBa
145 std::cout << "checkPixelAccess<vpRGBa>(3, 4, 2, 3): " << checkPixelAccess<vpRGBa>(3, 4, 2, 3) << std::endl;
146 try {
147 std::cout << "checkPixelAccess<vpRGBa>(3, 4, -2, -3): " << checkPixelAccess<vpRGBa>(3, 4, -2, -3) << std::endl;
148 std::cerr << "Out of image access exception should have been thrown" << std::endl;
149 return EXIT_FAILURE;
150 }
151 catch (...) {
152 std::cout << "\n";
153 }
154 try {
155 std::cout << "checkPixelAccess<vpRGBa>(3, 4, 3, 4): " << checkPixelAccess<vpRGBa>(3, 4, 3, 4) << std::endl;
156 std::cerr << "Out of image access exception should have been thrown" << std::endl;
157 return EXIT_FAILURE;
158 }
159 catch (...) {
160 std::cout << "\n";
161 }
162
163 // int
164 std::cout << "checkPixelAccess<int>(3, 4, 2, 3): " << checkPixelAccess<int>(3, 4, 2, 3) << std::endl;
165 try {
166 std::cout << "checkPixelAccess<int>(3, 4, -2, -3): " << checkPixelAccess<int>(3, 4, -2, -3) << std::endl;
167 std::cerr << "Out of image access exception should have been thrown" << std::endl;
168 return EXIT_FAILURE;
169 }
170 catch (...) {
171 std::cout << "\n";
172 }
173 try {
174 std::cout << "checkPixelAccess<int>(3, 4, 3, 4): " << checkPixelAccess<int>(3, 4, 3, 4) << std::endl;
175 std::cerr << "Out of image access exception should have been thrown" << std::endl;
176 return EXIT_FAILURE;
177 }
178 catch (...) {
179 std::cout << "\n";
180 }
181
182 // double
183 std::cout << "checkPixelAccess<double>(3, 4, 2, 3): " << checkPixelAccess<double>(3, 4, 2, 3) << std::endl;
184 try {
185 std::cout << "checkPixelAccess<double>(3, 4, -2, -3): " << checkPixelAccess<double>(3, 4, -2, -3) << std::endl;
186 std::cerr << "Out of image access exception should have been thrown" << std::endl;
187 return EXIT_FAILURE;
188 }
189 catch (...) {
190 std::cout << "\n";
191 }
192 try {
193 std::cout << "checkPixelAccess<double>(3, 4, 3, 4): " << checkPixelAccess<double>(3, 4, 3, 4) << std::endl;
194 std::cerr << "Out of image access exception should have been thrown" << std::endl;
195 return EXIT_FAILURE;
196 }
197 catch (...) {
198 std::cout << "\n";
199 }
200 }
201
202 // Test difference between double bilinear interpolation and fixed-point interpolation
203 srand(0);
204
205 {
206 vpImage<unsigned char> I(480, 640);
207 for (unsigned int i = 0; i < I.getHeight(); i++) {
208 for (unsigned int j = 0; j < I.getWidth(); j++) {
209 I[i][j] = randomPixelValue();
210 }
211 }
212
213 double diff_round = 0.0, diff = 0.0;
214 vpImage<unsigned char> I1(480, 640);
215 for (unsigned int i = 0; i < I.getHeight(); i++) {
216 for (unsigned int j = 0; j < I.getWidth(); j++) {
217 double idx1 = randomDouble(0, I.getHeight() - 1);
218 double idx2 = randomDouble(0, I.getWidth() - 1);
219 unsigned char val1 = I.getValue(idx1, idx2);
220 unsigned char val2 = getValue<unsigned char>(I, idx1, idx2, true);
221 unsigned char val3 = getValue<unsigned char>(I, idx1, idx2, false);
222
223 diff_round += std::fabs(static_cast<double>(val1) - static_cast<double>(val2));
224 diff += std::fabs(static_cast<double>(val1) - static_cast<double>(val3));
225 }
226 }
227
228 double meanDiffRound = diff_round / I.getSize();
229 double meanDiff = diff / I.getSize();
230 std::cout << "diff_round: " << diff_round << " ; meanDiffRound: " << meanDiffRound << std::endl;
231 std::cout << "diff: " << diff << " ; meanDiff: " << meanDiff << std::endl;
232 const double maxInterpolationErrorDiff = 1.0;
233 if (std::fabs(meanDiffRound) > maxInterpolationErrorDiff) {
234 std::cerr << "Too much pixel difference between fixed-point vpImage::getValue(double, double) and old method."
235 << std::endl;
236 return EXIT_FAILURE;
237 }
238 }
239
240 // Test performance double bilinear interpolation + round vs fixed-point interpolation
241 {
242 vpImage<unsigned char> I(1080, 1920);
243 for (unsigned int i = 0; i < I.getHeight(); i++) {
244 for (unsigned int j = 0; j < I.getWidth(); j++) {
245 I[i][j] = randomPixelValue();
246 }
247 }
248
249 std::vector<std::pair<double, double> > indexes;
250 for (int cpt = 0; cpt < 1000000; cpt++) {
251 double idx1 = randomDouble(0, I.getHeight() - 1);
252 double idx2 = randomDouble(0, I.getWidth() - 1);
253 indexes.push_back(std::pair<double, double>(idx1, idx2));
254 }
255
256 int sum1 = 0;
257 double t_optim = vpTime::measureTimeMs();
258 for (size_t cpt = 0; cpt < indexes.size(); cpt++) {
259 double idx1 = indexes[cpt].first;
260 double idx2 = indexes[cpt].second;
261 sum1 += I.getValue(idx1, idx2);
262 }
263 t_optim = vpTime::measureTimeMs() - t_optim;
264 std::cout << "\nFixed-point vpImage::getValue(double, double), sum1: " << sum1 << " in " << t_optim << " ms"
265 << std::endl;
266
267 int sum2 = 0;
268 double t_old = vpTime::measureTimeMs();
269 for (size_t cpt = 0; cpt < indexes.size(); cpt++) {
270 double idx1 = indexes[cpt].first;
271 double idx2 = indexes[cpt].second;
272 sum2 += getValue(I, idx1, idx2, true);
273 }
274 t_old = vpTime::measureTimeMs() - t_old;
275 std::cout << "Old method, sum2: " << sum2 << " in " << t_old << " ms" << std::endl;
276 std::cout << "Speed-up: " << t_old / t_optim << "X" << std::endl;
277 }
278
279 // Test performance double bilinear interpolation + round vs fixed-point interpolation
280 {
281 vpImage<unsigned char> I(1080, 1920);
282 for (unsigned int i = 0; i < I.getHeight(); i++) {
283 for (unsigned int j = 0; j < I.getWidth(); j++) {
284 I[i][j] = randomPixelValue();
285 }
286 }
287
288 std::vector<std::pair<double, double> > indexes;
289 for (int cpt = 0; cpt < 1000000; cpt++) {
290 double idx1 = randomDouble(0, I.getHeight() - 1);
291 double idx2 = randomDouble(0, I.getWidth() - 1);
292 indexes.push_back(std::pair<double, double>(idx1, idx2));
293 }
294
295 int sum1 = 0;
296 double t_optim = vpTime::measureTimeMs();
297 for (size_t cpt = 0; cpt < indexes.size(); cpt++) {
298 double idx1 = indexes[cpt].first;
299 double idx2 = indexes[cpt].second;
300 sum1 += I.getValue(idx1, idx2);
301 }
302 t_optim = vpTime::measureTimeMs() - t_optim;
303 std::cout << "\nFixed-point vpImage::getValue(double, double), sum1: " << sum1 << " in " << t_optim << " ms"
304 << std::endl;
305
306 int sum2 = 0;
307 double t_old = vpTime::measureTimeMs();
308 for (size_t cpt = 0; cpt < indexes.size(); cpt++) {
309 double idx1 = indexes[cpt].first;
310 double idx2 = indexes[cpt].second;
311 sum2 += getValue(I, idx1, idx2, false);
312 }
313 t_old = vpTime::measureTimeMs() - t_old;
314 std::cout << "Old method (without vpMath::round()), sum2: " << sum2 << " in " << t_old << " ms" << std::endl;
315 std::cout << "Speed-up: " << t_old / t_optim << "X" << std::endl;
316 }
317
318 // Check that getValue() still returns correct values
319 {
320 vpImage<unsigned char> I(480, 640);
321 for (unsigned int i = 0; i < I.getHeight(); i++) {
322 for (unsigned int j = 0; j < I.getWidth(); j++) {
323 I[i][j] = randomPixelValue();
324 }
325 }
326
327 vpImage<unsigned char> I_copy(480, 640);
328 for (unsigned int i = 0; i < I_copy.getHeight(); i++) {
329 double y = static_cast<double>(i);
330
331 for (unsigned int j = 0; j < I_copy.getWidth(); j++) {
332 double x = static_cast<double>(j);
333
334 I_copy[i][j] = I.getValue(y, x);
335 }
336 }
337
338 bool same = (I == I_copy);
339 std::cout << "\nCheck that getValue returns correct results for integer coordinates\n(I == I_copy)? " << same
340 << std::endl;
341 if (!same) {
342 std::cerr << "Issue with vpImage::getValue(double, double)!" << std::endl;
343 return EXIT_FAILURE;
344 }
345 }
346
347 return EXIT_SUCCESS;
348}
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ notInitializedError
Image not initialized.
@ notInTheImage
Pixel not in the image.
Definition of the vpImage class member functions.
Definition vpImage.h:131
static int round(double x)
Definition vpMath.h:413
VISP_EXPORT double measureTimeMs()