Visual Servoing Platform  version 3.5.0 under development (2022-02-15)
testPerformanceLUT.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 performance between iteration and LUT.
33  *
34  * Authors:
35  * Souriya Trinh
36  *
37  *****************************************************************************/
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <visp3/core/vpImage.h>
42 #include <visp3/core/vpIoTools.h>
43 #include <visp3/core/vpMath.h>
44 #include <visp3/io/vpImageIo.h>
45 #include <visp3/io/vpParseArgv.h>
46 
54 // List of allowed command line options
55 #define GETOPTARGS "cdi:o:t:h"
56 
57 /*
58  Print the program options.
59 
60  \param name : Program name.
61  \param badparam : Bad parameter name.
62  \param ipath: Input image path.
63  \param opath : Output image path.
64  \param user : Username.
65 
66  */
67 void usage(const char *name, const char *badparam, const std::string &ipath, const std::string &opath,
68  const std::string &user)
69 {
70  fprintf(stdout, "\n\
71 Test performance between methods to iterate over pixel image.\n\
72 \n\
73 SYNOPSIS\n\
74  %s [-i <input image path>] [-o <output image path>] [-t <nb threads>]\n\
75  [-h]\n \
76 ", name);
77 
78  fprintf(stdout, "\n\
79 OPTIONS: Default\n\
80  -i <input image path> %s\n\
81  Set image input path.\n\
82  From this path read \"Klimt/Klimt.pgm\"\n\
83  image.\n\
84  Setting the VISP_INPUT_IMAGE_PATH environment\n\
85  variable produces the same behaviour than using\n\
86  this option.\n\
87 \n\
88  -o <output image path> %s\n\
89  Set image output path.\n\
90  From this directory, creates the \"%s\"\n\
91  subdirectory depending on the username, where \n\
92  Klimt_grey.pgm output image is written.\n\
93 \n\
94  -t <nb threads> \n\
95  Set the number of threads to use for the computation.\n\
96 \n\
97  -h\n\
98  Print the help.\n\n", ipath.c_str(), opath.c_str(), user.c_str());
99 
100  if (badparam)
101  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
102 }
103 
117 bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, const std::string &user,
118  unsigned int &nbThreads)
119 {
120  const char *optarg_;
121  int c;
122  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
123 
124  switch (c) {
125  case 'i':
126  ipath = optarg_;
127  break;
128  case 'o':
129  opath = optarg_;
130  break;
131  case 't':
132  nbThreads = (unsigned int)atoi(optarg_);
133  break;
134  case 'h':
135  usage(argv[0], NULL, ipath, opath, user);
136  return false;
137  break;
138 
139  case 'c':
140  case 'd':
141  break;
142 
143  default:
144  usage(argv[0], optarg_, ipath, opath, user);
145  return false;
146  break;
147  }
148  }
149 
150  if ((c == 1) || (c == -1)) {
151  // standalone param or error
152  usage(argv[0], NULL, ipath, opath, user);
153  std::cerr << "ERROR: " << std::endl;
154  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
155  return false;
156  }
157 
158  return true;
159 }
160 
169 void iterate_method1(vpImage<vpRGBa> &I, double alpha, double beta)
170 {
171  unsigned int size = I.getWidth() * I.getHeight();
172  unsigned char *ptrStart = (unsigned char *)I.bitmap;
173  unsigned char *ptrEnd = ptrStart + size * 4;
174  unsigned char *ptrCurrent = ptrStart;
175 
176  while (ptrCurrent != ptrEnd) {
177  *ptrCurrent = vpMath::saturate<unsigned char>((*ptrCurrent) * alpha + beta);
178  ++ptrCurrent;
179  }
180 }
181 
190 void iterate_method1(vpImage<unsigned char> &I, double alpha, double beta)
191 {
192  unsigned int size = I.getWidth() * I.getHeight();
193  unsigned char *ptrStart = (unsigned char *)I.bitmap;
194  unsigned char *ptrEnd = ptrStart + size;
195  unsigned char *ptrCurrent = ptrStart;
196 
197  while (ptrCurrent != ptrEnd) {
198  *ptrCurrent = vpMath::saturate<unsigned char>((*ptrCurrent) * alpha + beta);
199  ++ptrCurrent;
200  }
201 }
202 
211 void iterate_method2(vpImage<vpRGBa> &I, double alpha, double beta)
212 {
213  for (unsigned int i = 0; i < I.getHeight(); i++) {
214  for (unsigned int j = 0; j < I.getWidth(); j++) {
215  I[i][j].R = vpMath::saturate<unsigned char>(I[i][j].R * alpha + beta);
216  I[i][j].G = vpMath::saturate<unsigned char>(I[i][j].G * alpha + beta);
217  I[i][j].B = vpMath::saturate<unsigned char>(I[i][j].B * alpha + beta);
218  I[i][j].A = vpMath::saturate<unsigned char>(I[i][j].A * alpha + beta);
219  }
220  }
221 }
222 
223 int main(int argc, const char **argv)
224 {
225  try {
226  std::string env_ipath;
227  std::string opt_ipath;
228  std::string opt_opath;
229  std::string ipath;
230  std::string opath;
231  std::string filename;
232  std::string username;
233  unsigned int nbThreads = 4;
234 
235  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
236  // environment variable value
237  env_ipath = vpIoTools::getViSPImagesDataPath();
238 
239  // Set the default input path
240  if (!env_ipath.empty())
241  ipath = env_ipath;
242 
243 // Set the default output path
244 #if defined(_WIN32)
245  opt_opath = "C:/temp";
246 #else
247  opt_opath = "/tmp";
248 #endif
249 
250  // Get the user login name
251  vpIoTools::getUserName(username);
252 
253  // Read the command line options
254  if (getOptions(argc, argv, opt_ipath, opt_opath, username, nbThreads) == false) {
255  exit(-1);
256  }
257 
258  // Get the option values
259  if (!opt_ipath.empty())
260  ipath = opt_ipath;
261  if (!opt_opath.empty())
262  opath = opt_opath;
263 
264  // Append to the output path string, the login name of the user
265  opath = vpIoTools::createFilePath(opath, username);
266 
267  // Test if the output path exist. If no try to create it
268  if (vpIoTools::checkDirectory(opath) == false) {
269  try {
270  // Create the dirname
272  } catch (...) {
273  usage(argv[0], NULL, ipath, opt_opath, username);
274  std::cerr << std::endl << "ERROR:" << std::endl;
275  std::cerr << " Cannot create " << opath << std::endl;
276  std::cerr << " Check your -o " << opt_opath << " option " << std::endl;
277  exit(-1);
278  }
279  }
280 
281  // Compare ipath and env_ipath. If they differ, we take into account
282  // the input path comming from the command line option
283  if (!opt_ipath.empty() && !env_ipath.empty()) {
284  if (ipath != env_ipath) {
285  std::cout << std::endl << "WARNING: " << std::endl;
286  std::cout << " Since -i <visp image path=" << ipath << "> "
287  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
288  << " we skip the environment variable." << std::endl;
289  }
290  }
291 
292  // Test if an input path is set
293  if (opt_ipath.empty() && env_ipath.empty()) {
294  usage(argv[0], NULL, ipath, opt_opath, username);
295  std::cerr << std::endl << "ERROR:" << std::endl;
296  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
297  << " environment variable to specify the location of the " << std::endl
298  << " image path where test images are located." << std::endl
299  << std::endl;
300  exit(-1);
301  }
302 
303  //
304  // Here starts really the test
305  //
306 
307  // Create a grey level image
308  vpImage<vpRGBa> I_iterate1, I_iterate2, I_lut;
309 
310  // Load a grey image from the disk
311  filename = vpIoTools::createFilePath(ipath, "Klimt/Klimt.ppm");
312  std::cout << "\nRead image: " << filename << std::endl;
313  vpImageIo::read(I_iterate1, filename);
314  vpImageIo::read(I_iterate2, filename);
315  vpImageIo::read(I_lut, filename);
316 
317  std::cout << "I=" << I_iterate1.getWidth() << "x" << I_iterate1.getHeight() << std::endl;
318 
319  double alpha = 1.5, beta = -30.0;
320  unsigned int nbIterations = 10;
321 
322  // Iterate method 1
323  double t_iterate1 = vpTime::measureTimeMs();
324  for (unsigned int cpt = 0; cpt < nbIterations; cpt++) {
325  iterate_method1(I_iterate1, alpha, beta);
326  }
327  t_iterate1 = vpTime::measureTimeMs() - t_iterate1;
328  std::cout << "t_iterate1=" << t_iterate1 << " ms ; t_iterate1/" << nbIterations << "="
329  << (t_iterate1 / nbIterations) << " ms" << std::endl;
330 
331  filename = vpIoTools::createFilePath(opath, "Klimt_performance_iterate1.ppm");
332  vpImageIo::write(I_iterate1, filename);
333 
334  // Iterate method 2
335  double t_iterate2 = vpTime::measureTimeMs();
336  for (unsigned int cpt = 0; cpt < nbIterations; cpt++) {
337  iterate_method2(I_iterate2, alpha, beta);
338  }
339  t_iterate2 = vpTime::measureTimeMs() - t_iterate2;
340  std::cout << "t_iterate2=" << t_iterate2 << " ms ; t_iterate2/" << nbIterations << "="
341  << (t_iterate2 / nbIterations) << " ms" << std::endl;
342 
343  filename = vpIoTools::createFilePath(opath, "Klimt_performance_iterate2.ppm");
344  vpImageIo::write(I_iterate2, filename);
345 
346  // LUT method
347  double t_lut = vpTime::measureTimeMs();
348  for (unsigned int cpt = 0; cpt < nbIterations; cpt++) {
349  // Construct the LUT
350  vpRGBa lut[256];
351  for (unsigned int i = 0; i < 256; i++) {
352  lut[i].R = vpMath::saturate<unsigned char>(alpha * i + beta);
353  lut[i].G = vpMath::saturate<unsigned char>(alpha * i + beta);
354  lut[i].B = vpMath::saturate<unsigned char>(alpha * i + beta);
355  lut[i].A = vpMath::saturate<unsigned char>(alpha * i + beta);
356  }
357 
358  I_lut.performLut(lut, nbThreads);
359  }
360  t_lut = vpTime::measureTimeMs() - t_lut;
361  std::cout << "t_lut=" << t_lut << " ms ; t_lut/" << nbIterations << "=" << (t_lut / nbIterations) << " ms"
362  << std::endl;
363 
364  filename = vpIoTools::createFilePath(opath, "Klimt_performance_lut.ppm");
365  vpImageIo::write(I_lut, filename);
366 
367  // Check results
368  bool same = true;
369  for (unsigned int i = 0; i < I_iterate1.getHeight() && same; i++) {
370  for (unsigned int j = 0; j < I_iterate1.getWidth() && same; j++) {
371  if (I_iterate1[i][j] != I_iterate2[i][j] || I_iterate1[i][j] != I_lut[i][j]) {
372  same = false;
373  }
374  }
375  }
376 
377  if (!same) {
378  std::cerr << "Color images are different!" << std::endl;
379  return -1;
380  }
381 
382  // Test LUT on grayscale image
383  vpImage<unsigned char> I_iterate_grayscale1, I_lut_grayscale;
384 
385  // Load a grayscale image from the disk
386  filename = vpIoTools::createFilePath(ipath, "Klimt/Klimt.pgm");
387  std::cout << "\nRead image: " << filename << std::endl;
388  vpImageIo::read(I_iterate_grayscale1, filename);
389  vpImageIo::read(I_lut_grayscale, filename);
390 
391  std::cout << "I_grayscale=" << I_lut_grayscale.getWidth() << "x" << I_lut_grayscale.getHeight() << std::endl;
392 
393  // Iterate method 1 on grayscale
394  double t_iterate_grayscale1 = vpTime::measureTimeMs();
395  for (unsigned int cpt = 0; cpt < nbIterations; cpt++) {
396  iterate_method1(I_iterate_grayscale1, alpha, beta);
397  }
398  t_iterate_grayscale1 = vpTime::measureTimeMs() - t_iterate_grayscale1;
399  std::cout << "t_iterate_grayscale1=" << t_iterate_grayscale1 << " ms ; t_iterate1/" << nbIterations << "="
400  << (t_iterate_grayscale1 / nbIterations) << " ms" << std::endl;
401 
402  filename = vpIoTools::createFilePath(opath, "Klimt_performance_iterate1_grayscale.pgm");
403  vpImageIo::write(I_iterate_grayscale1, filename);
404 
405  // LUT method on grayscale
406  double t_lut_grayscale = vpTime::measureTimeMs();
407  for (unsigned int cpt = 0; cpt < nbIterations; cpt++) {
408  // Construct the LUT
409  unsigned char lut[256];
410  for (unsigned int i = 0; i < 256; i++) {
411  lut[i] = vpMath::saturate<unsigned char>(alpha * i + beta);
412  }
413 
414  I_lut_grayscale.performLut(lut, nbThreads);
415  }
416  t_lut_grayscale = vpTime::measureTimeMs() - t_lut_grayscale;
417  std::cout << "t_lut_grayscale=" << t_lut_grayscale << " ms ; t_lut_grayscale/" << nbIterations << "="
418  << (t_lut_grayscale / nbIterations) << " ms" << std::endl;
419 
420  filename = vpIoTools::createFilePath(opath, "Klimt_performance_lut_grayscale.pgm");
421  vpImageIo::write(I_lut_grayscale, filename);
422 
423  // Check grayscale image
424  same = true;
425  for (unsigned int i = 0; i < I_lut_grayscale.getHeight() && same; i++) {
426  for (unsigned int j = 0; j < I_lut_grayscale.getWidth() && same; j++) {
427  if (I_lut_grayscale[i][j] != I_iterate_grayscale1[i][j]) {
428  same = false;
429  }
430  }
431  }
432 
433  if (!same) {
434  std::cerr << "Grayscale images are different!" << std::endl;
435  return -1;
436  }
437 
438  // Computation time on color image
439  vpImageIo::read(I_lut, filename);
440 
441  double t_lut_multithread = vpTime::measureTimeMs();
442  for (unsigned int cpt = 0; cpt < nbIterations * 10; cpt++) {
443  // Construct the LUT
444  vpRGBa lut[256];
445  for (unsigned int i = 0; i < 256; i++) {
446  lut[i].R = vpMath::saturate<unsigned char>(alpha * i + beta);
447  lut[i].G = vpMath::saturate<unsigned char>(alpha * i + beta);
448  lut[i].B = vpMath::saturate<unsigned char>(alpha * i + beta);
449  lut[i].A = vpMath::saturate<unsigned char>(alpha * i + beta);
450  }
451 
452  I_lut.performLut(lut, 4);
453  }
454  t_lut_multithread = vpTime::measureTimeMs() - t_lut_multithread;
455 
456  vpImageIo::read(I_lut, filename);
457 
458  double t_lut_singlethread = vpTime::measureTimeMs();
459  for (unsigned int cpt = 0; cpt < nbIterations * 10; cpt++) {
460  // Construct the LUT
461  vpRGBa lut[256];
462  for (unsigned int i = 0; i < 256; i++) {
463  lut[i].R = vpMath::saturate<unsigned char>(alpha * i + beta);
464  lut[i].G = vpMath::saturate<unsigned char>(alpha * i + beta);
465  lut[i].B = vpMath::saturate<unsigned char>(alpha * i + beta);
466  lut[i].A = vpMath::saturate<unsigned char>(alpha * i + beta);
467  }
468 
469  I_lut.performLut(lut, 1);
470  }
471  t_lut_singlethread = vpTime::measureTimeMs() - t_lut_singlethread;
472 
473  std::cout << "\nt_lut_singlethread/t_lut_multithread (color)=" << t_lut_singlethread / t_lut_multithread << "X"
474  << std::endl;
475 
476  // Computation time on grayscale image
477  vpImageIo::read(I_lut_grayscale, filename);
478 
479  t_lut_multithread = vpTime::measureTimeMs();
480  for (unsigned int cpt = 0; cpt < nbIterations * 10; cpt++) {
481  // Construct the LUT
482  unsigned char lut[256];
483  for (unsigned int i = 0; i < 256; i++) {
484  lut[i] = vpMath::saturate<unsigned char>(alpha * i + beta);
485  }
486 
487  I_lut_grayscale.performLut(lut, 4);
488  }
489  t_lut_multithread = vpTime::measureTimeMs() - t_lut_multithread;
490 
491  vpImageIo::read(I_lut_grayscale, filename);
492 
493  t_lut_singlethread = vpTime::measureTimeMs();
494  for (unsigned int cpt = 0; cpt < nbIterations * 10; cpt++) {
495  // Construct the LUT
496  unsigned char lut[256];
497  for (unsigned int i = 0; i < 256; i++) {
498  lut[i] = vpMath::saturate<unsigned char>(alpha * i + beta);
499  }
500 
501  I_lut_grayscale.performLut(lut, 1);
502  }
503  t_lut_singlethread = vpTime::measureTimeMs() - t_lut_singlethread;
504 
505  std::cout << "\nt_lut_singlethread/t_lut_multithread (grayscale)=" << t_lut_singlethread / t_lut_multithread << "X"
506  << std::endl;
507 
508  // Check performLut with multithreading and image size not divisible by 8
509  vpImage<unsigned char> I_test_grayscale(49, 7);
510  // Construct the LUT
511  unsigned char lut_grayscale[256];
512  for (unsigned int i = 0; i < 256; i++) {
513  lut_grayscale[i] = vpMath::saturate<unsigned char>(alpha * i + beta);
514  }
515  I_test_grayscale.performLut(lut_grayscale, nbThreads);
516 
517  vpImage<vpRGBa> I_test_color(49, 7);
518  // Construct the LUT
519  vpRGBa lut_color[256];
520  for (unsigned int i = 0; i < 256; i++) {
521  lut_color[i].R = vpMath::saturate<unsigned char>(alpha * i + beta);
522  lut_color[i].G = vpMath::saturate<unsigned char>(alpha * i + beta);
523  lut_color[i].B = vpMath::saturate<unsigned char>(alpha * i + beta);
524  lut_color[i].A = vpMath::saturate<unsigned char>(alpha * i + beta);
525  }
526  I_test_color.performLut(lut_color, nbThreads);
527 
528  return 0;
529  } catch (const vpException &e) {
530  std::cerr << "Catch an exception: " << e.what() << std::endl;
531  return 1;
532  }
533 }
void performLut(const Type(&lut)[256], unsigned int nbThreads=1)
Definition: vpImage.h:1679
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:149
static void makeDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:570
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1365
unsigned char B
Blue component.
Definition: vpRGBa.h:150
Type * bitmap
points toward the bitmap
Definition: vpImage.h:143
error that can be emited by ViSP classes.
Definition: vpException.h:71
unsigned char G
Green component.
Definition: vpRGBa.h:149
static void write(const vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:293
VISP_EXPORT double measureTimeMs()
Definition: vpTime.cpp:126
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
Definition: vpRGBa.h:66
static bool checkDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:420
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1670
unsigned char A
Additionnal component.
Definition: vpRGBa.h:151
static std::string getUserName()
Definition: vpIoTools.cpp:316
const char * what() const
unsigned int getHeight() const
Definition: vpImage.h:188
unsigned char R
Red component.
Definition: vpRGBa.h:148
unsigned int getWidth() const
Definition: vpImage.h:246