Visual Servoing Platform version 3.5.0
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 */
67void 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\
71Test performance between methods to iterate over pixel image.\n\
72\n\
73SYNOPSIS\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\
79OPTIONS: 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
117bool 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
169void 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
190void 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
211void 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
223int 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
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}
error that can be emited by ViSP classes.
Definition: vpException.h:72
const char * what() const
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:149
static void write(const vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:293
unsigned int getWidth() const
Definition: vpImage.h:246
void performLut(const Type(&lut)[256], unsigned int nbThreads=1)
Definition: vpImage.h:1679
Type * bitmap
points toward the bitmap
Definition: vpImage.h:143
unsigned int getHeight() const
Definition: vpImage.h:188
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1365
static bool checkDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:420
static std::string getUserName()
Definition: vpIoTools.cpp:316
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1670
static void makeDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:570
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
Definition: vpRGBa.h:67
unsigned char B
Blue component.
Definition: vpRGBa.h:150
unsigned char R
Red component.
Definition: vpRGBa.h:148
unsigned char G
Green component.
Definition: vpRGBa.h:149
unsigned char A
Additionnal component.
Definition: vpRGBa.h:151
VISP_EXPORT double measureTimeMs()