Visual Servoing Platform  version 3.0.1
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
testHistogram.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ViSP software.
4  * Copyright (C) 2005 - 2017 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * ("GPL") version 2 as published by the Free Software Foundation.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See http://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Test histogram computation.
32  *
33  * Authors:
34  * Souriya Trinh
35  *
36  *****************************************************************************/
37 
38 #include <visp3/core/vpImage.h>
39 #include <visp3/io/vpImageIo.h>
40 #include <visp3/io/vpParseArgv.h>
41 #include <visp3/core/vpIoTools.h>
42 #include <visp3/core/vpHistogram.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 
53 // List of allowed command line options
54 #define GETOPTARGS "cdi:t:h"
55 
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 
64  */
65 void usage(const char *name, const char *badparam, std::string ipath)
66 {
67  fprintf(stdout, "\n\
68 Test histogram.\n\
69 \n\
70 SYNOPSIS\n\
71  %s [-i <input image path>] [-t <nb threads>]\n\
72  [-h]\n \
73 ", name);
74 
75  fprintf(stdout, "\n\
76 OPTIONS: Default\n\
77  -i <input image path> %s\n\
78  Set image input path.\n\
79  From this path read \"ViSP-images/Klimt/Klimt.ppm\"\n\
80  image.\n\
81  Setting the VISP_INPUT_IMAGE_PATH environment\n\
82  variable produces the same behaviour than using\n\
83  this option.\n\
84 \n\
85  -t <nb threads>\n\
86  Set the number of threads to use for the computation.\n\
87  -h\n\
88  Print the help.\n\n",
89  ipath.c_str());
90 
91  if (badparam)
92  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
93 }
94 
106 bool getOptions(int argc, const char **argv, std::string &ipath, unsigned int &nbThreads)
107 {
108  const char *optarg_;
109  int c;
110  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
111 
112  switch (c) {
113  case 'i': ipath = optarg_; break;
114  case 't': nbThreads = (unsigned int) atoi(optarg_); break;
115  case 'h': usage(argv[0], NULL, ipath); return false; break;
116 
117  case 'c':
118  case 'd':
119  break;
120 
121  default:
122  usage(argv[0], optarg_, ipath); return false; break;
123  }
124  }
125 
126  if ((c == 1) || (c == -1)) {
127  // standalone param or error
128  usage(argv[0], NULL, ipath);
129  std::cerr << "ERROR: " << std::endl;
130  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
131  return false;
132  }
133 
134  return true;
135 }
136 
144 unsigned int histogramSum(const vpImage<unsigned char> &I, const unsigned int nbBins, const unsigned int nbThreads) {
145  unsigned int sum = 0;
146 
147  vpHistogram histogram;
148  histogram.calculate(I, nbBins, nbThreads);
149 
150  for(unsigned int cpt = 0; cpt < histogram.getSize(); cpt++) {
151  sum += histogram[cpt];
152  }
153 
154  return sum;
155 }
156 
163 bool compareHistogram(const vpImage<unsigned char> &I, const unsigned int nbBins) {
164  vpHistogram histogram_single_threaded;
165  histogram_single_threaded.calculate(I, nbBins, 1);
166 
167  vpHistogram histogram_multi_threaded;
168  histogram_multi_threaded.calculate(I, nbBins, 4);
169 
170  unsigned int sum = 0;
171  for(unsigned int cpt = 0; cpt < nbBins; cpt++) {
172  if(histogram_single_threaded[cpt] != histogram_multi_threaded[cpt]) {
173  std::cerr << "histogram_single_threaded[" << cpt << "]=" << histogram_single_threaded[cpt] <<
174  " ; histogram_multi_threaded[" << cpt << "]=" << histogram_multi_threaded[cpt] << std::endl;
175 
176  return false;
177  }
178 
179  sum += histogram_single_threaded[cpt];
180  }
181 
182  if(sum != I.getSize()) {
183  std::cerr << "Sum of histogram is different with the image size!" << std::endl;
184  return false;
185  }
186 
187  return true;
188 }
189 
190 int
191 main(int argc, const char ** argv)
192 {
193  try {
194  std::string env_ipath;
195  std::string opt_ipath;
196  std::string ipath;
197  std::string filename;
198  unsigned int nbThreads = 4;
199 
200  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH environment variable value
201  env_ipath = vpIoTools::getViSPImagesDataPath();
202 
203  // Set the default input path
204  if (! env_ipath.empty())
205  ipath = env_ipath;
206 
207 
208  // Read the command line options
209  if (getOptions(argc, argv, opt_ipath, nbThreads) == false) {
210  exit (-1);
211  }
212 
213  // Get the option values
214  if (!opt_ipath.empty())
215  ipath = opt_ipath;
216 
217  // Compare ipath and env_ipath. If they differ, we take into account
218  // the input path comming from the command line option
219  if (!opt_ipath.empty() && !env_ipath.empty()) {
220  if (ipath != env_ipath) {
221  std::cout << std::endl
222  << "WARNING: " << std::endl;
223  std::cout << " Since -i <visp image path=" << ipath << "> "
224  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
225  << " we skip the environment variable." << std::endl;
226  }
227  }
228 
229  // Test if an input path is set
230  if (opt_ipath.empty() && env_ipath.empty()){
231  usage(argv[0], NULL, ipath);
232  std::cerr << std::endl
233  << "ERROR:" << std::endl;
234  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH "
235  << std::endl
236  << " environment variable to specify the location of the " << std::endl
237  << " image path where test images are located." << std::endl << std::endl;
238  exit(-1);
239  }
240 
241 
242  //
243  // Here starts really the test
244  //
245 
246  // Create a grey level image
248 
249  // Load a grey image from the disk
250  filename = vpIoTools::createFilePath(ipath, "ViSP-images/Klimt/Klimt.ppm");
251  std::cout << "Read image: " << filename << std::endl;
252  vpImageIo::read(I, filename);
253 
254  std::cout << "I=" << I.getWidth() << "x" << I.getHeight() << std::endl;
255 
256  int nbIterations = 100;
257  unsigned int nbBins = 256;
258  unsigned int sum_single_thread = 0;
259  unsigned int sum_single_multithread = 0;
260 
261  double t_single_thread = vpTime::measureTimeMs();
262  for(int iteration = 0; iteration < nbIterations; iteration++) {
263  sum_single_thread = histogramSum(I, nbBins, 1);
264  }
265  t_single_thread = vpTime::measureTimeMs() - t_single_thread;
266 
267  double t_multithread = vpTime::measureTimeMs();
268  for(int iteration = 0; iteration < nbIterations; iteration++) {
269  sum_single_multithread = histogramSum(I, nbBins, nbThreads);
270  }
271  t_multithread = vpTime::measureTimeMs() - t_multithread;
272 
273  std::cout << "sum_single_thread=" << sum_single_thread << " ; t_single_thread=" << t_single_thread
274  << " ms ; mean=" << t_single_thread / (double) nbIterations << " ms" << std::endl;
275  std::cout << "sum_single_multithread=" << sum_single_multithread << " ; t_multithread=" << t_multithread
276  << " ms ; mean=" << t_multithread / (double) nbIterations << " ms" << std::endl;
277  std::cout << "Speed-up=" << t_single_thread / (double) t_multithread << "X" << std::endl;
278 
279  if(sum_single_thread != I.getSize() || sum_single_multithread != I.getSize()) {
280  std::cerr << "Problem with histogram!" << std::endl;
281  return -1;
282  }
283 
284  nbBins = 101;
285  if(!compareHistogram(I, nbBins)) {
286  std::cerr << "Histogram are different!" << std::endl;
287  return -1;
288  }
289 
290 
291  //Test histogram computation on empty image
292  vpHistogram histogram;
293  vpImage<unsigned char> I_test(0, 0);
294  histogram.calculate(I_test, 256, 4);
295  if(histogram.getSize() == 256) {
296  for(unsigned int cpt = 0; cpt < 256; cpt++) {
297  if(histogram[cpt] != 0) {
298  std::cerr << "Problem with histogram computation: histogram[" << cpt << "]=" << histogram[cpt]
299  << " but should be zero!" << std::endl;
300  }
301  }
302  } else {
303  std::cerr << "Bad histogram size!" << std::endl;
304  return -1;
305  }
306 
307 
308  //Test histogram computation on image size < nbThreads
309  I_test.init(3, 1);
310  I_test = 100;
311  histogram.calculate(I_test, 256, 4);
312  if(histogram.getSize() == 256) {
313  for(unsigned int cpt = 0; cpt < 256; cpt++) {
314  if(cpt == 100) {
315  if(histogram[cpt] != I_test.getSize()) {
316  std::cerr << "Problem with histogram computation: histogram[" << cpt << "]=" << histogram[cpt]
317  << " but should be: " << I_test.getSize() << std::endl;
318  return -1;
319  }
320  } else {
321  if(histogram[cpt] != 0) {
322  std::cerr << "Problem with histogram computation: histogram[" << cpt << "]=" << histogram[cpt]
323  << " but should be zero!" << std::endl;
324  }
325  }
326  }
327  } else {
328  std::cerr << "Bad histogram size!" << std::endl;
329  return -1;
330  }
331 
332 
333  //Test histogram computation on small image size
334  I_test.init(7, 1);
335  I_test = 50;
336  histogram.calculate(I_test, 256, 4);
337  if(histogram.getSize() == 256) {
338  for(unsigned int cpt = 0; cpt < 256; cpt++) {
339  if(cpt == 50) {
340  if(histogram[cpt] != I_test.getSize()) {
341  std::cerr << "Problem with histogram computation: histogram[" << cpt << "]=" << histogram[cpt]
342  << " but should be: " << I_test.getSize() << std::endl;
343  return -1;
344  }
345  } else {
346  if(histogram[cpt] != 0) {
347  std::cerr << "Problem with histogram computation: histogram[" << cpt << "]=" << histogram[cpt]
348  << " but should be zero!" << std::endl;
349  }
350  }
351  }
352  } else {
353  std::cerr << "Bad histogram size!" << std::endl;
354  return -1;
355  }
356 
357 
358  std::cout << "testHistogram is OK!" << std::endl;
359  return 0;
360  }
361  catch(vpException &e) {
362  std::cerr << "Catch an exception: " << e.what() << std::endl;
363  return 1;
364  }
365 }
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1157
unsigned int getWidth() const
Definition: vpImage.h:226
unsigned getSize() const
Definition: vpHistogram.h:277
error that can be emited by ViSP classes.
Definition: vpException.h:73
Class to compute a gray level image histogram.
Definition: vpHistogram.h:113
VISP_EXPORT double measureTimeMs()
Definition: vpTime.cpp:93
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:76
const char * what() const
unsigned int getSize() const
Definition: vpImage.h:212
static std::string createFilePath(const std::string &parent, const std::string child)
Definition: vpIoTools.cpp:1366
void calculate(const vpImage< unsigned char > &I, const unsigned int nbins=256, const unsigned int nbThreads=1)
static void read(vpImage< unsigned char > &I, const std::string &filename)
Definition: vpImageIo.cpp:205
unsigned int getHeight() const
Definition: vpImage.h:175