Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
testImageGetValue.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Test for vpImagePoint::getValue().
33  *
34 *****************************************************************************/
41 #include <iostream>
42 #include <visp3/core/vpImage.h>
43 
44 #ifdef ENABLE_VISP_NAMESPACE
45 using namespace VISP_NAMESPACE_NAME;
46 #endif
47 
48 namespace
49 {
50 template <typename PixelType> PixelType checkPixelAccess(unsigned int height, unsigned int width, double v, double u)
51 {
52  vpImage<PixelType> I(height, width);
53  for (unsigned int i = 0; i < I.getHeight(); i++) {
54  for (unsigned int j = 0; j < I.getWidth(); j++) {
55  I[i][j] = static_cast<PixelType>(i * I.getWidth() + j);
56  }
57  }
58 
59  return I.getValue(v, u);
60 }
61 
62 template <> vpRGBa checkPixelAccess(unsigned int height, unsigned int width, double v, double u)
63 {
64  vpImage<vpRGBa> I(height, width);
65  for (unsigned int i = 0; i < I.getHeight(); i++) {
66  for (unsigned int j = 0; j < I.getWidth(); j++) {
67  I[i][j] =
68  vpRGBa(static_cast<unsigned char>(i * I.getWidth() + j), static_cast<unsigned char>(i * I.getWidth() + j),
69  static_cast<unsigned char>(i * I.getWidth() + j));
70  }
71  }
72 
73  return I.getValue(v, u);
74 }
75 
76 double randomDouble(double a, double b)
77 {
78  double random = (static_cast<double>(rand())) / static_cast<double>(RAND_MAX);
79  double diff = b - a;
80  double r = random * diff;
81  return a + r;
82 }
83 
84 unsigned char randomPixelValue()
85 {
86  const int min = 0, max = 255;
87  return static_cast<unsigned char>((rand() % (max - min + 1) + min));
88 }
89 
90 template <class PixelType> PixelType getValue(const vpImage<PixelType> &I, double i, double j, bool roundValue)
91 {
92  if (i < 0 || j < 0 || i + 1 > I.getHeight() || j + 1 > I.getWidth()) {
93  throw(vpException(vpImageException::notInTheImage, "Pixel outside of the image"));
94  }
95  if (I.getHeight() * I.getWidth() == 0) {
97  }
98 
99  unsigned int iround = static_cast<unsigned int>(floor(i));
100  unsigned int jround = static_cast<unsigned int>(floor(j));
101 
102  double rratio = i - static_cast<double>(iround);
103  double cratio = j - static_cast<double>(jround);
104 
105  double rfrac = 1.0 - rratio;
106  double cfrac = 1.0 - cratio;
107 
108  unsigned int iround_1 = std::min<unsigned int>(I.getHeight() - 1, iround + 1);
109  unsigned int jround_1 = std::min<unsigned int>(I.getWidth() - 1, jround + 1);
110 
111  double value =
112  (static_cast<double>(I[iround][jround]) * rfrac + static_cast<double>(I[iround_1][jround]) * rratio) * cfrac +
113  (static_cast<double>(I[iround][jround_1]) * rfrac + static_cast<double>(I[iround_1][jround_1]) * rratio) * cratio;
114 
115  return static_cast<PixelType>(roundValue ? vpMath::round(value) : value);
116 }
117 } // namespace
118 
119 int main()
120 {
121  // Test out of image memory access
122  // vpImage::getValue(double, double)
123  {
124  // unsigned char
125  std::cout << "checkPixelAccess<unsigned char>(3, 4, 2, 3): "
126  << static_cast<unsigned int>(checkPixelAccess<unsigned char>(3, 4, 2, 3)) << std::endl;
127  try {
128  std::cout << "checkPixelAccess<unsigned char>(3, 4, -2, -3): "
129  << static_cast<unsigned int>(checkPixelAccess<unsigned char>(3, 4, -2, -3)) << std::endl;
130  std::cerr << "Out of image access exception should have been thrown" << std::endl;
131  return EXIT_FAILURE;
132  }
133  catch (...) {
134  std::cout << "\n";
135  }
136  try {
137  std::cout << "checkPixelAccess<unsigned char>(3, 4, 3, 4): "
138  << static_cast<unsigned int>(checkPixelAccess<unsigned char>(3, 4, 3, 4)) << std::endl;
139  std::cerr << "Out of image access exception should have been thrown" << std::endl;
140  return EXIT_FAILURE;
141  }
142  catch (...) {
143  std::cout << "\n";
144  }
145 
146  // vpRGBa
147  std::cout << "checkPixelAccess<vpRGBa>(3, 4, 2, 3): " << checkPixelAccess<vpRGBa>(3, 4, 2, 3) << std::endl;
148  try {
149  std::cout << "checkPixelAccess<vpRGBa>(3, 4, -2, -3): " << checkPixelAccess<vpRGBa>(3, 4, -2, -3) << std::endl;
150  std::cerr << "Out of image access exception should have been thrown" << std::endl;
151  return EXIT_FAILURE;
152  }
153  catch (...) {
154  std::cout << "\n";
155  }
156  try {
157  std::cout << "checkPixelAccess<vpRGBa>(3, 4, 3, 4): " << checkPixelAccess<vpRGBa>(3, 4, 3, 4) << std::endl;
158  std::cerr << "Out of image access exception should have been thrown" << std::endl;
159  return EXIT_FAILURE;
160  }
161  catch (...) {
162  std::cout << "\n";
163  }
164 
165  // int
166  std::cout << "checkPixelAccess<int>(3, 4, 2, 3): " << checkPixelAccess<int>(3, 4, 2, 3) << std::endl;
167  try {
168  std::cout << "checkPixelAccess<int>(3, 4, -2, -3): " << checkPixelAccess<int>(3, 4, -2, -3) << std::endl;
169  std::cerr << "Out of image access exception should have been thrown" << std::endl;
170  return EXIT_FAILURE;
171  }
172  catch (...) {
173  std::cout << "\n";
174  }
175  try {
176  std::cout << "checkPixelAccess<int>(3, 4, 3, 4): " << checkPixelAccess<int>(3, 4, 3, 4) << std::endl;
177  std::cerr << "Out of image access exception should have been thrown" << std::endl;
178  return EXIT_FAILURE;
179  }
180  catch (...) {
181  std::cout << "\n";
182  }
183 
184  // double
185  std::cout << "checkPixelAccess<double>(3, 4, 2, 3): " << checkPixelAccess<double>(3, 4, 2, 3) << std::endl;
186  try {
187  std::cout << "checkPixelAccess<double>(3, 4, -2, -3): " << checkPixelAccess<double>(3, 4, -2, -3) << std::endl;
188  std::cerr << "Out of image access exception should have been thrown" << std::endl;
189  return EXIT_FAILURE;
190  }
191  catch (...) {
192  std::cout << "\n";
193  }
194  try {
195  std::cout << "checkPixelAccess<double>(3, 4, 3, 4): " << checkPixelAccess<double>(3, 4, 3, 4) << std::endl;
196  std::cerr << "Out of image access exception should have been thrown" << std::endl;
197  return EXIT_FAILURE;
198  }
199  catch (...) {
200  std::cout << "\n";
201  }
202  }
203 
204  // Test difference between double bilinear interpolation and fixed-point interpolation
205  srand(0);
206 
207  {
208  vpImage<unsigned char> I(480, 640);
209  for (unsigned int i = 0; i < I.getHeight(); i++) {
210  for (unsigned int j = 0; j < I.getWidth(); j++) {
211  I[i][j] = randomPixelValue();
212  }
213  }
214 
215  double diff_round = 0.0, diff = 0.0;
216  vpImage<unsigned char> I1(480, 640);
217  for (unsigned int i = 0; i < I.getHeight(); i++) {
218  for (unsigned int j = 0; j < I.getWidth(); j++) {
219  double idx1 = randomDouble(0, I.getHeight() - 1);
220  double idx2 = randomDouble(0, I.getWidth() - 1);
221  unsigned char val1 = I.getValue(idx1, idx2);
222  unsigned char val2 = getValue<unsigned char>(I, idx1, idx2, true);
223  unsigned char val3 = getValue<unsigned char>(I, idx1, idx2, false);
224 
225  diff_round += std::fabs((double)val1 - (double)val2);
226  diff += std::fabs((double)val1 - (double)val3);
227  }
228  }
229 
230  double meanDiffRound = diff_round / I.getSize();
231  double meanDiff = diff / I.getSize();
232  std::cout << "diff_round: " << diff_round << " ; meanDiffRound: " << meanDiffRound << std::endl;
233  std::cout << "diff: " << diff << " ; meanDiff: " << meanDiff << std::endl;
234  const double maxInterpolationErrorDiff = 1.0;
235  if (std::fabs(meanDiffRound) > maxInterpolationErrorDiff) {
236  std::cerr << "Too much pixel difference between fixed-point vpImage::getValue(double, double) and old method."
237  << std::endl;
238  return EXIT_FAILURE;
239  }
240  }
241 
242  // Test performance double bilinear interpolation + round vs fixed-point interpolation
243  {
244  vpImage<unsigned char> I(1080, 1920);
245  for (unsigned int i = 0; i < I.getHeight(); i++) {
246  for (unsigned int j = 0; j < I.getWidth(); j++) {
247  I[i][j] = randomPixelValue();
248  }
249  }
250 
251  std::vector<std::pair<double, double> > indexes;
252  for (int cpt = 0; cpt < 1000000; cpt++) {
253  double idx1 = randomDouble(0, I.getHeight() - 1);
254  double idx2 = randomDouble(0, I.getWidth() - 1);
255  indexes.push_back(std::pair<double, double>(idx1, idx2));
256  }
257 
258  int sum1 = 0;
259  double t_optim = vpTime::measureTimeMs();
260  for (size_t cpt = 0; cpt < indexes.size(); cpt++) {
261  double idx1 = indexes[cpt].first;
262  double idx2 = indexes[cpt].second;
263  sum1 += I.getValue(idx1, idx2);
264  }
265  t_optim = vpTime::measureTimeMs() - t_optim;
266  std::cout << "\nFixed-point vpImage::getValue(double, double), sum1: " << sum1 << " in " << t_optim << " ms"
267  << std::endl;
268 
269  int sum2 = 0;
270  double t_old = vpTime::measureTimeMs();
271  for (size_t cpt = 0; cpt < indexes.size(); cpt++) {
272  double idx1 = indexes[cpt].first;
273  double idx2 = indexes[cpt].second;
274  sum2 += getValue(I, idx1, idx2, true);
275  }
276  t_old = vpTime::measureTimeMs() - t_old;
277  std::cout << "Old method, sum2: " << sum2 << " in " << t_old << " ms" << std::endl;
278  std::cout << "Speed-up: " << t_old / t_optim << "X" << std::endl;
279  }
280 
281  // Test performance double bilinear interpolation + round vs fixed-point interpolation
282  {
283  vpImage<unsigned char> I(1080, 1920);
284  for (unsigned int i = 0; i < I.getHeight(); i++) {
285  for (unsigned int j = 0; j < I.getWidth(); j++) {
286  I[i][j] = randomPixelValue();
287  }
288  }
289 
290  std::vector<std::pair<double, double> > indexes;
291  for (int cpt = 0; cpt < 1000000; cpt++) {
292  double idx1 = randomDouble(0, I.getHeight() - 1);
293  double idx2 = randomDouble(0, I.getWidth() - 1);
294  indexes.push_back(std::pair<double, double>(idx1, idx2));
295  }
296 
297  int sum1 = 0;
298  double t_optim = vpTime::measureTimeMs();
299  for (size_t cpt = 0; cpt < indexes.size(); cpt++) {
300  double idx1 = indexes[cpt].first;
301  double idx2 = indexes[cpt].second;
302  sum1 += I.getValue(idx1, idx2);
303  }
304  t_optim = vpTime::measureTimeMs() - t_optim;
305  std::cout << "\nFixed-point vpImage::getValue(double, double), sum1: " << sum1 << " in " << t_optim << " ms"
306  << std::endl;
307 
308  int sum2 = 0;
309  double t_old = vpTime::measureTimeMs();
310  for (size_t cpt = 0; cpt < indexes.size(); cpt++) {
311  double idx1 = indexes[cpt].first;
312  double idx2 = indexes[cpt].second;
313  sum2 += getValue(I, idx1, idx2, false);
314  }
315  t_old = vpTime::measureTimeMs() - t_old;
316  std::cout << "Old method (without vpMath::round()), sum2: " << sum2 << " in " << t_old << " ms" << std::endl;
317  std::cout << "Speed-up: " << t_old / t_optim << "X" << std::endl;
318  }
319 
320  // Check that getValue() still returns correct values
321  {
322  vpImage<unsigned char> I(480, 640);
323  for (unsigned int i = 0; i < I.getHeight(); i++) {
324  for (unsigned int j = 0; j < I.getWidth(); j++) {
325  I[i][j] = randomPixelValue();
326  }
327  }
328 
329  vpImage<unsigned char> I_copy(480, 640);
330  for (unsigned int i = 0; i < I_copy.getHeight(); i++) {
331  double y = static_cast<double>(i);
332 
333  for (unsigned int j = 0; j < I_copy.getWidth(); j++) {
334  double x = static_cast<double>(j);
335 
336  I_copy[i][j] = I.getValue(y, x);
337  }
338  }
339 
340  bool same = (I == I_copy);
341  std::cout << "\nCheck that getValue returns correct results for integer coordinates\n(I == I_copy)? " << same
342  << std::endl;
343  if (!same) {
344  std::cerr << "Issue with vpImage::getValue(double, double)!" << std::endl;
345  return EXIT_FAILURE;
346  }
347  }
348 
349  return EXIT_SUCCESS;
350 }
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
unsigned int getWidth() const
Definition: vpImage.h:242
Type getValue(unsigned int i, unsigned int j) const
unsigned int getSize() const
Definition: vpImage.h:221
unsigned int getHeight() const
Definition: vpImage.h:181
static int round(double x)
Definition: vpMath.h:409
Definition: vpRGBa.h:65
VISP_EXPORT double measureTimeMs()