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