Visual Servoing Platform  version 3.2.0 under development (2019-01-22)
testImageAddSub.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 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 http://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 images addition / substraction.
33  *
34  * Author:
35  * Souriya Trinh
36  *
37  *****************************************************************************/
38 
39 #include <visp3/core/vpDebug.h>
40 #include <visp3/core/vpImage.h>
41 #include <visp3/core/vpImageTools.h>
42 #include <visp3/core/vpIoTools.h>
43 #include <visp3/io/vpImageIo.h>
44 #include <visp3/io/vpParseArgv.h>
45 
52 // List of allowed command line options
53 #define GETOPTARGS "cdi:o:n:h"
54 
55 /*
56  Print the program options.
57 
58  \param name : Program name.
59  \param badparam : Bad parameter name.
60  \param ipath: Input image path.
61  \param opath : Output image path.
62  \param user : Username.
63  \param nbiter : Number of benchmark iterations.
64  */
65 void usage(const char *name, const char *badparam, const std::string &ipath, const std::string &opath,
66  const std::string &user, int nbiter)
67 {
68  fprintf(stdout, "\n\
69 Test images addition / substraction.\n\
70 \n\
71 SYNOPSIS\n\
72  %s [-i <input image path>] [-o <output image path>] [-n <nb iterations>]\n\
73  [-h]\n \
74 ", name);
75 
76  fprintf(stdout, "\n\
77 OPTIONS: Default\n\
78  -i <input image path> %s\n\
79  Set image input path.\n\
80  From this path read \"Klimt/Klimt.pgm\"\n\
81  image.\n\
82  Setting the VISP_INPUT_IMAGE_PATH environment\n\
83  variable produces the same behaviour than using\n\
84  this option.\n\
85 \n\
86  -o <output image path> %s\n\
87  Set image output path.\n\
88  From this directory, creates the \"%s\"\n\
89  subdirectory depending on the username, where \n\
90  result output images are written.\n\
91 \n\
92  -n <nb iterations> %d\n\
93  Set the number of benchmark iterations.\n\
94 \n\
95  -h\n\
96  Print the help.\n\n", ipath.c_str(), opath.c_str(), user.c_str(), nbiter);
97 
98  if (badparam)
99  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
100 }
101 
113 bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, const std::string &user,
114  int &nbiter)
115 {
116  const char *optarg_;
117  int c;
118  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
119 
120  switch (c) {
121  case 'i':
122  ipath = optarg_;
123  break;
124  case 'o':
125  opath = optarg_;
126  break;
127  case 'n':
128  nbiter = atoi(optarg_);
129  break;
130  case 'h':
131  usage(argv[0], NULL, ipath, opath, user, nbiter);
132  return false;
133  break;
134 
135  case 'c':
136  case 'd':
137  break;
138 
139  default:
140  usage(argv[0], optarg_, ipath, opath, user, nbiter);
141  return false;
142  break;
143  }
144  }
145 
146  if ((c == 1) || (c == -1)) {
147  // standalone param or error
148  usage(argv[0], NULL, ipath, opath, user, nbiter);
149  std::cerr << "ERROR: " << std::endl;
150  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
151  return false;
152  }
153 
154  return true;
155 }
156 
157 void regularImageAdd(const vpImage<unsigned char> &I1, const vpImage<unsigned char> &I2, vpImage<unsigned char> &Ires,
158  const bool saturate)
159 {
160  if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
161  throw(vpException(vpException::dimensionError, "The two images do not have the same size"));
162  }
163 
164  if ((I1.getHeight() != Ires.getHeight()) || (I1.getWidth() != Ires.getWidth())) {
165  Ires.resize(I1.getHeight(), I1.getWidth());
166  }
167 
168  unsigned char *ptr_I1 = I1.bitmap;
169  unsigned char *ptr_I2 = I2.bitmap;
170  unsigned char *ptr_Ires = Ires.bitmap;
171 
172  for (unsigned int cpt = 0; cpt < Ires.getSize(); cpt++, ++ptr_I1, ++ptr_I2, ++ptr_Ires) {
173  *ptr_Ires = saturate ? vpMath::saturate<unsigned char>((short int)*ptr_I1 + (short int)*ptr_I2) : *ptr_I1 + *ptr_I2;
174  }
175 }
176 
177 void regularImageSubtract(const vpImage<unsigned char> &I1, const vpImage<unsigned char> &I2,
178  vpImage<unsigned char> &Ires, const bool saturate)
179 {
180  if ((I1.getHeight() != I2.getHeight()) || (I1.getWidth() != I2.getWidth())) {
181  throw(vpException(vpException::dimensionError, "The two images do not have the same size"));
182  }
183 
184  if ((I1.getHeight() != Ires.getHeight()) || (I1.getWidth() != Ires.getWidth())) {
185  Ires.resize(I1.getHeight(), I1.getWidth());
186  }
187 
188  unsigned char *ptr_I1 = I1.bitmap;
189  unsigned char *ptr_I2 = I2.bitmap;
190  unsigned char *ptr_Ires = Ires.bitmap;
191 
192  for (unsigned int cpt = 0; cpt < Ires.getSize(); cpt++, ++ptr_I1, ++ptr_I2, ++ptr_Ires) {
193  *ptr_Ires = saturate ? vpMath::saturate<unsigned char>((short int)*ptr_I1 - (short int)*ptr_I2) : *ptr_I1 - *ptr_I2;
194  }
195 }
196 
197 int main(int argc, const char **argv)
198 {
199  try {
200  std::string env_ipath;
201  std::string opt_ipath;
202  std::string opt_opath;
203  std::string ipath;
204  std::string opath;
205  std::string filename;
206  std::string username;
207  int nbIterations = 100;
208 
209  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
210  // environment variable value
211  env_ipath = vpIoTools::getViSPImagesDataPath();
212 
213  // Set the default input path
214  if (!env_ipath.empty())
215  ipath = env_ipath;
216 
217 // Set the default output path
218 #if defined(_WIN32)
219  opt_opath = "C:/temp";
220 #else
221  opt_opath = "/tmp";
222 #endif
223 
224  // Get the user login name
225  vpIoTools::getUserName(username);
226 
227  // Read the command line options
228  if (getOptions(argc, argv, opt_ipath, opt_opath, username, nbIterations) == false) {
229  exit(EXIT_FAILURE);
230  }
231 
232  // Get the option values
233  if (!opt_ipath.empty())
234  ipath = opt_ipath;
235  if (!opt_opath.empty())
236  opath = opt_opath;
237 
238  // Append to the output path string, the login name of the user
239  opath = vpIoTools::createFilePath(opath, username);
240 
241  // Test if the output path exist. If no try to create it
242  if (vpIoTools::checkDirectory(opath) == false) {
243  try {
244  // Create the dirname
246  } catch (...) {
247  usage(argv[0], NULL, ipath, opt_opath, username, nbIterations);
248  std::cerr << std::endl << "ERROR:" << std::endl;
249  std::cerr << " Cannot create " << opath << std::endl;
250  std::cerr << " Check your -o " << opt_opath << " option " << std::endl;
251  exit(EXIT_FAILURE);
252  }
253  }
254 
255  // Compare ipath and env_ipath. If they differ, we take into account
256  // the input path comming from the command line option
257  if (opt_ipath.empty()) {
258  if (ipath != env_ipath) {
259  std::cout << std::endl << "WARNING: " << std::endl;
260  std::cout << " Since -i <visp image path=" << ipath << "> "
261  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
262  << " we skip the environment variable." << std::endl;
263  }
264  }
265 
266  // Test if an input path is set
267  if (opt_ipath.empty() && env_ipath.empty()) {
268  usage(argv[0], NULL, ipath, opt_opath, username, nbIterations);
269  std::cerr << std::endl << "ERROR:" << std::endl;
270  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
271  << " environment variable to specify the location of the " << std::endl
272  << " image path where test images are located." << std::endl
273  << std::endl;
274  exit(EXIT_FAILURE);
275  }
276 
277  //
278  // Here starts really the test
279  //
280  vpImage<unsigned char> I; // Input image
281 
282  // Read the input grey image from the disk
283  filename = vpIoTools::createFilePath(ipath, "Klimt/Klimt.pgm");
284  std::cout << "Read image: " << filename << std::endl << std::endl;
285  vpImageIo::read(I, filename);
286 
287  // Test add image
288  vpImage<unsigned char> Inull(I.getHeight(), I.getWidth(), 0);
290  vpImageTools::imageAdd(I, Inull, Iadd);
291  if (Iadd != I) {
292  throw vpException(vpException::fatalError, "Problem with vpImageTools::imageAdd (Iadd != I)!");
293  } else {
294  std::cout << "(Iadd == I)? " << (Iadd == I) << std::endl;
295  }
296 
297  // Test in-place add
298  Iadd = 0;
299  vpImageTools::imageAdd(I, Iadd, Iadd);
300  if (Iadd != I) {
301  throw vpException(vpException::fatalError, "Problem with in-place vpImageTools::imageAdd (Iadd != I)!");
302  } else {
303  std::cout << "In-place (Iadd == I)? " << (Iadd == I) << std::endl;
304  }
305 
306  // Test subtract image
307  Inull = 0;
309  vpImageTools::imageSubtract(I, Inull, Isub);
310  if (Isub != I) {
311  throw vpException(vpException::fatalError, "Problem with vpImageTools::imageSubtract (Iadd != I)!");
312  } else {
313  std::cout << "(Isub == I)? " << (Isub == I) << std::endl;
314  }
315 
316  // Test in-place subtract
317  Isub = 0;
318  vpImageTools::imageSubtract(I, Isub, Isub);
319  if (Isub != I) {
320  throw vpException(vpException::fatalError, "Problem with in-place vpImageTools::imageSubtract (Isub != I)!");
321  } else {
322  std::cout << "In-place (Isub == I)? " << (Isub == I) << std::endl;
323  }
324 
325  // Test add image saturation
327  for (unsigned int cpt = 0; cpt < I2.getSize(); cpt++) {
328  I2.bitmap[cpt] = (unsigned char)cpt;
329  }
330 
331  // No saturation
332  vpImageTools::imageAdd(I, I2, Iadd, false);
333  vpImage<unsigned char> Iadd_regular;
334  regularImageAdd(I, I2, Iadd_regular, false);
335  if (Iadd != Iadd_regular) {
336  throw vpException(vpException::fatalError, "Problem with vpImageTools::imageAdd(I, I2, Iadd, "
337  "false) (Iadd != Iadd_regular)!");
338  } else {
339  std::cout << "\nNo saturation (Iadd == Iadd_regular)? " << (Iadd == Iadd_regular) << std::endl;
340  }
341 
342  // Saturation
343  vpImageTools::imageAdd(I, I2, Iadd, true);
344  regularImageAdd(I, I2, Iadd_regular, true);
345  if (Iadd != Iadd_regular) {
346  throw vpException(vpException::fatalError, "Problem with vpImageTools::imageAdd(I, I2, Iadd, "
347  "true) (Iadd != Iadd_regular)!");
348  } else {
349  std::cout << "Saturation (Iadd == Iadd_regular)? " << (Iadd == Iadd_regular) << std::endl;
350  }
351 
352  // Test subtract image saturation
353  // No saturation
354  vpImageTools::imageSubtract(I, I2, Isub, false);
355  vpImage<unsigned char> Isub_regular;
356  regularImageSubtract(I, I2, Isub_regular, false);
357  if (Isub != Isub_regular) {
358  throw vpException(vpException::fatalError, "Problem with vpImageTools::imageSubtract(I, I2, "
359  "Isub, false) (Isub != Isub_regular)!");
360  } else {
361  std::cout << "\nNo saturation (Isub == Isub_regular)? " << (Isub == Isub_regular) << std::endl;
362  }
363 
364  // Saturation
365  vpImageTools::imageSubtract(I, I2, Isub, true);
366  regularImageSubtract(I, I2, Isub_regular, true);
367  if (Isub != Isub_regular) {
368  throw vpException(vpException::fatalError, "Problem with vpImageTools::imageSubtract(I, I2, "
369  "Isub, true) (Isub != Isub_regular)!");
370  } else {
371  std::cout << "Saturation (Isub == Isub_regular)? " << (Isub == Isub_regular) << std::endl;
372  }
373 
374  // Benchmark
375  // Benchmark add no saturation
376  Iadd = 0;
377  double t_sse = vpTime::measureTimeMs();
378  for (int cpt = 0; cpt < nbIterations; cpt++) {
379  vpImageTools::imageAdd(I, Iadd, Iadd, false);
380  }
381  t_sse = vpTime::measureTimeMs() - t_sse;
382  std::cout << "\nAdd no saturation ; t_sse (" << nbIterations << " iterations)=" << t_sse << " ms" << std::endl;
383 
384  Iadd_regular = 0;
385  double t = vpTime::measureTimeMs();
386  for (int cpt = 0; cpt < nbIterations; cpt++) {
387  regularImageAdd(I, Iadd_regular, Iadd_regular, false);
388  }
389  t = vpTime::measureTimeMs() - t;
390  std::cout << "Add regular no saturation ; t (" << nbIterations << " iterations)=" << t << " ms"
391  << " ; Speed-up: " << (t / t_sse) << "X" << std::endl;
392  std::cout << "(Iadd == Iadd_regular)? " << (Iadd == Iadd_regular) << std::endl;
393 
394  // Write add no saturation
395  filename = vpIoTools::createFilePath(opath, "Klimt_add_no_sat_sse.pgm");
396  std::cout << "\nWrite: " << filename << std::endl;
397  vpImageIo::write(Iadd, filename);
398 
399  filename = vpIoTools::createFilePath(opath, "Klimt_add_no_sat.pgm");
400  std::cout << "Write: " << filename << std::endl;
401  vpImageIo::write(Iadd_regular, filename);
402 
403  // Benchmark add saturation
404  Iadd = 0;
405  t_sse = vpTime::measureTimeMs();
406  for (int cpt = 0; cpt < nbIterations; cpt++) {
407  vpImageTools::imageAdd(I, Iadd, Iadd, true);
408  }
409  t_sse = vpTime::measureTimeMs() - t_sse;
410  std::cout << "\nAdd saturation ; t_sse (" << nbIterations << " iterations)=" << t_sse << " ms" << std::endl;
411 
412  Iadd_regular = 0;
413  t = vpTime::measureTimeMs();
414  for (int cpt = 0; cpt < nbIterations; cpt++) {
415  regularImageAdd(I, Iadd_regular, Iadd_regular, true);
416  }
417  t = vpTime::measureTimeMs() - t;
418  std::cout << "Add saturation ; t (" << nbIterations << " iterations)=" << t << " ms"
419  << " ; Speed-up: " << (t / t_sse) << "X" << std::endl;
420  std::cout << "(Iadd == Iadd_regular)? " << (Iadd == Iadd_regular) << std::endl;
421 
422  // Write add no saturation
423  filename = vpIoTools::createFilePath(opath, "Klimt_add_sat_sse.pgm");
424  std::cout << "\nWrite: " << filename << std::endl;
425  vpImageIo::write(Iadd, filename);
426 
427  filename = vpIoTools::createFilePath(opath, "Klimt_add_sat.pgm");
428  std::cout << "Write: " << filename << std::endl;
429  vpImageIo::write(Iadd_regular, filename);
430 
431  // Benchmark subtract no saturation
432  Isub = I2;
433  t_sse = vpTime::measureTimeMs();
434  for (int cpt = 0; cpt < nbIterations; cpt++) {
435  vpImageTools::imageSubtract(I, Isub, Isub, false);
436  }
437  t_sse = vpTime::measureTimeMs() - t_sse;
438  std::cout << "\nSubtract no saturation ; t_sse (" << nbIterations << " iterations)=" << t_sse << " ms" << std::endl;
439 
440  Isub_regular = I2;
441  t = vpTime::measureTimeMs();
442  for (int cpt = 0; cpt < nbIterations; cpt++) {
443  regularImageSubtract(I, Isub_regular, Isub_regular, false);
444  }
445  t = vpTime::measureTimeMs() - t;
446  std::cout << "Subtract regular no saturation ; t (" << nbIterations << " iterations)=" << t << " ms"
447  << " ; Speed-up: " << (t / t_sse) << "X" << std::endl;
448  std::cout << "(Isub == Isub_regular)? " << (Isub == Isub_regular) << std::endl;
449 
450  // Write subtract no saturation
451  filename = vpIoTools::createFilePath(opath, "Klimt_subtract_no_sat_sse.pgm");
452  std::cout << "\nWrite: " << filename << std::endl;
453  vpImageIo::write(Isub, filename);
454 
455  filename = vpIoTools::createFilePath(opath, "Klimt_subtract_no_sat.pgm");
456  std::cout << "Write: " << filename << std::endl;
457  vpImageIo::write(Isub_regular, filename);
458 
459  // Benchmark subtract saturation
460  Isub = I2;
461  t_sse = vpTime::measureTimeMs();
462  for (int cpt = 0; cpt < nbIterations; cpt++) {
463  vpImageTools::imageSubtract(I, Isub, Isub, true);
464  }
465  t_sse = vpTime::measureTimeMs() - t_sse;
466  std::cout << "\nSubtract saturation ; t_sse (" << nbIterations << " iterations)=" << t_sse << " ms" << std::endl;
467 
468  Isub_regular = I2;
469  t = vpTime::measureTimeMs();
470  for (int cpt = 0; cpt < nbIterations; cpt++) {
471  regularImageSubtract(I, Isub_regular, Isub_regular, true);
472  }
473  t = vpTime::measureTimeMs() - t;
474  std::cout << "Subtract saturation ; t (" << nbIterations << " iterations)=" << t << " ms"
475  << " ; Speed-up: " << (t / t_sse) << "X" << std::endl;
476  std::cout << "(Isub == Isub_regular)? " << (Isub == Isub_regular) << std::endl;
477 
478  // Write subtract no saturation
479  filename = vpIoTools::createFilePath(opath, "Klimt_subtract_sat_sse.pgm");
480  std::cout << "\nWrite: " << filename << std::endl;
481  vpImageIo::write(Isub, filename);
482 
483  filename = vpIoTools::createFilePath(opath, "Klimt_subtract_sat.pgm");
484  std::cout << "Write: " << filename << std::endl;
485  vpImageIo::write(Isub_regular, filename);
486 
487  // Invert Klimt with image cropped
488  if (I.getWidth() >= 411 && I.getHeight() >= 507) {
489  vpRect r_crop(0, 0, 411, 507);
490  vpImage<unsigned char> I_crop;
491  vpImageTools::crop(I, r_crop, I_crop);
492  std::cout << "\nI_crop=" << I_crop.getWidth() << "x" << I_crop.getHeight() << std::endl;
493 
494  vpImage<unsigned char> I_invert(I_crop.getHeight(), I_crop.getWidth(), 255);
495  vpImageTools::imageSubtract(I_invert, I_crop, I_invert);
496 
497  vpImage<unsigned char> I_invert_regular(I_crop.getHeight(), I_crop.getWidth(), 255);
498  regularImageSubtract(I_invert_regular, I_crop, I_invert_regular, false);
499  std::cout << "(I_invert == I_invert_regular)? " << (I_invert == I_invert_regular) << std::endl;
500 
501  vpImage<unsigned char> I_white(I_crop.getHeight(), I_crop.getWidth(), 255);
502  vpImage<unsigned char> I_invert2 = I_invert;
503  vpImageTools::imageAdd(I_invert2, I_crop, I_invert2);
504  std::cout << "(I_invert2 == I_white)? " << (I_invert2 == I_white) << std::endl;
505 
506  filename = vpIoTools::createFilePath(opath, "Klimt_invert_crop.pgm");
507  std::cout << "Write: " << filename << std::endl;
508  vpImageIo::write(I_invert, filename);
509  }
510 
511  return EXIT_SUCCESS;
512  } catch (const vpException &e) {
513  std::cerr << "Catch an exception: " << e << std::endl;
514  return EXIT_FAILURE;
515  }
516 }
static bool checkDirectory(const char *dirname)
Definition: vpIoTools.cpp:467
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1316
unsigned int getWidth() const
Definition: vpImage.h:239
Type * bitmap
points toward the bitmap
Definition: vpImage.h:133
error that can be emited by ViSP classes.
Definition: vpException.h:71
VISP_EXPORT double measureTimeMs()
Definition: vpTime.cpp:88
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
static void write(const vpImage< unsigned char > &I, const std::string &filename)
Definition: vpImageIo.cpp:375
static void makeDirectory(const char *dirname)
Definition: vpIoTools.cpp:597
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1541
unsigned int getSize() const
Definition: vpImage.h:219
static std::string getUserName()
Definition: vpIoTools.cpp:298
void resize(const unsigned int h, const unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:866
static void read(vpImage< unsigned char > &I, const std::string &filename)
Definition: vpImageIo.cpp:207
static void imageAdd(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Ires, const bool saturate=false)
static void crop(const vpImage< Type > &I, double roi_top, double roi_left, unsigned int roi_height, unsigned int roi_width, vpImage< Type > &crop, unsigned int v_scale=1, unsigned int h_scale=1)
Definition: vpImageTools.h:272
static void imageSubtract(const vpImage< unsigned char > &I1, const vpImage< unsigned char > &I2, vpImage< unsigned char > &Ires, const bool saturate=false)
unsigned int getHeight() const
Definition: vpImage.h:178
Defines a rectangle in the plane.
Definition: vpRect.h:78