36#include <visp3/core/vpHistogram.h>
37#include <visp3/core/vpImage.h>
38#include <visp3/core/vpUniRand.h>
39#include <visp3/core/vpIoTools.h>
40#include <visp3/io/vpImageIo.h>
41#include <visp3/io/vpParseArgv.h>
51#define GETOPTARGS "cdi:t:h"
53#ifdef ENABLE_VISP_NAMESPACE
65void usage(
const char *name,
const char *badparam, std::string ipath)
71 %s [-i <input image path>] [-t <nb threads>]\n\
78 -i <input image path> %s\n\
79 Set image input path.\n\
80 From this path read \"Klimt/Klimt.ppm\"\n\
82 Setting the VISP_INPUT_IMAGE_PATH environment\n\
83 variable produces the same behaviour than using\n\
87 Set the number of threads to use for the computation.\n\
93 fprintf(stdout,
"\nERROR: Bad parameter [%s]\n", badparam);
107bool getOptions(
int argc,
const char **argv, std::string &ipath,
unsigned int &nbThreads)
118 nbThreads =
static_cast<unsigned int>(atoi(optarg_));
121 usage(argv[0],
nullptr, ipath);
129 usage(argv[0], optarg_, ipath);
134 if ((c == 1) || (c == -1)) {
136 usage(argv[0],
nullptr, ipath);
137 std::cerr <<
"ERROR: " << std::endl;
138 std::cerr <<
" Bad argument " << optarg_ << std::endl << std::endl;
154 unsigned int sum = 0;
157 histogram.
calculate(I, nbBins, nbThreads);
159 for (
unsigned int cpt = 0; cpt < histogram.
getSize(); cpt++) {
160 sum += histogram[cpt];
175 histogram_single_threaded.
calculate(I, nbBins, 1);
178 histogram_multi_threaded.
calculate(I, nbBins, 4);
180 unsigned int sum = 0;
181 for (
unsigned int cpt = 0; cpt < nbBins; cpt++) {
182 if (histogram_single_threaded[cpt] != histogram_multi_threaded[cpt]) {
183 std::cerr <<
"histogram_single_threaded[" << cpt <<
"]=" << histogram_single_threaded[cpt]
184 <<
" ; histogram_multi_threaded[" << cpt <<
"]=" << histogram_multi_threaded[cpt] << std::endl;
189 sum += histogram_single_threaded[cpt];
192 if (sum != I.getSize()) {
193 std::cerr <<
"Sum of histogram is different with the image size!" << std::endl;
200int main(
int argc,
const char **argv)
203 std::string env_ipath;
204 std::string opt_ipath;
207 unsigned int nbThreads = 4;
214 if (!env_ipath.empty())
218 if (getOptions(argc, argv, opt_ipath, nbThreads) ==
false) {
223 if (!opt_ipath.empty())
228 if (!opt_ipath.empty() && !env_ipath.empty()) {
229 if (ipath != env_ipath) {
230 std::cout << std::endl <<
"WARNING: " << std::endl;
231 std::cout <<
" Since -i <visp image path=" << ipath <<
"> "
232 <<
" is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
233 <<
" we skip the environment variable." << std::endl;
238 if (opt_ipath.empty() && env_ipath.empty()) {
239 usage(argv[0],
nullptr, ipath);
240 std::cerr << std::endl <<
"ERROR:" << std::endl;
241 std::cerr <<
" Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
242 <<
" environment variable to specify the location of the " << std::endl
243 <<
" image path where test images are located." << std::endl
257 std::cout <<
"Read image: " <<
filename << std::endl;
260 std::cout <<
"I=" << I.getWidth() <<
"x" << I.getHeight() << std::endl;
262 int nbIterations = 100;
263 unsigned int nbBins = 256;
264 unsigned int sum_single_thread = 0;
265 unsigned int sum_multi_thread = 0;
268 for (
int iteration = 0; iteration < nbIterations; iteration++) {
269 sum_single_thread = histogramSum(I, nbBins, 1);
274 for (
int iteration = 0; iteration < nbIterations; iteration++) {
275 sum_multi_thread = histogramSum(I, nbBins, nbThreads);
279 std::cout <<
"sum_single_thread=" << sum_single_thread <<
" ; t_single_thread=" << t_single_thread
280 <<
" ms ; mean=" << t_single_thread /
static_cast<double>(nbIterations) <<
" ms" << std::endl;
281 std::cout <<
"sum_multi_thread (nbThreads=" << nbThreads <<
")=" << sum_multi_thread <<
" ; t_multi_thread=" << t_multi_thread
282 <<
" ms ; mean=" << t_multi_thread /
static_cast<double>(nbIterations) <<
" ms" << std::endl;
283 std::cout <<
"Speed-up=" << t_single_thread /
static_cast<double>(t_multi_thread) <<
"X" << std::endl;
285 if (sum_single_thread != I.getSize() || sum_multi_thread != I.getSize()) {
286 std::cerr <<
"Problem with histogram!" << std::endl;
291 if (!compareHistogram(I, nbBins)) {
292 std::cerr <<
"Histogram are different!" << std::endl;
297 std::cout <<
"Test histogram computation on empty image" << std::endl << std::flush;
301 if (histogram.
getSize() == 256) {
302 for (
unsigned int cpt = 0; cpt < 256; cpt++) {
303 if (histogram[cpt] != 0) {
304 std::cerr <<
"Problem with histogram computation: histogram[" << cpt <<
"]=" << histogram[cpt]
305 <<
" but should be zero!" << std::endl;
310 std::cerr <<
"Bad histogram size!" << std::endl;
315 std::cout <<
"Test histogram computation on image size < nbThreads" << std::endl << std::flush;
319 if (histogram.
getSize() == 256) {
320 for (
unsigned int cpt = 0; cpt < 256; cpt++) {
322 if (histogram[cpt] != I_test.getSize()) {
323 std::cerr <<
"Problem with histogram computation: histogram[" << cpt <<
"]=" << histogram[cpt]
324 <<
" but should be: " << I_test.
getSize() << std::endl;
329 if (histogram[cpt] != 0) {
330 std::cerr <<
"Problem with histogram computation: histogram[" << cpt <<
"]=" << histogram[cpt]
331 <<
" but should be zero!" << std::endl;
337 std::cerr <<
"Bad histogram size!" << std::endl;
342 std::cout <<
"Test histogram computation on small image size" << std::endl << std::flush;
346 if (histogram.
getSize() == 256) {
347 for (
unsigned int cpt = 0; cpt < 256; cpt++) {
349 if (histogram[cpt] != I_test.getSize()) {
350 std::cerr <<
"Problem with histogram computation: histogram[" << cpt <<
"]=" << histogram[cpt]
351 <<
" but should be: " << I_test.
getSize() << std::endl;
356 if (histogram[cpt] != 0) {
357 std::cerr <<
"Problem with histogram computation: histogram[" << cpt <<
"]=" << histogram[cpt]
358 <<
" but should be zero!" << std::endl;
364 std::cerr <<
"Bad histogram size!" << std::endl;
369 unsigned int nbRows = 4, nbCols = 15;
370 I.init(nbRows, nbCols);
371 for (
unsigned int r = 0;
r < nbRows; ++
r) {
373 for (
unsigned int i = 1;
i <= 5; ++
i) {
374 for (
unsigned int count = 0; count <
i; ++count) {
381 std::cout <<
"I:" << std::endl;
382 std::cout << I << std::endl;
385 std::vector<unsigned int> expectedNumber(nbBins, 0);
386 expectedNumber[1] = 1;
387 expectedNumber[2] = 3;
388 expectedNumber[3] = 6;
389 expectedNumber[4] = 10;
390 expectedNumber[5] = 14;
391 expectedNumber[6] = 12;
392 expectedNumber[7] = 9;
393 expectedNumber[8] = 5;
397 for (
unsigned int bin = 0; bin < nbBins; ++bin) {
398 if (histo[bin] != expectedNumber[bin]) {
399 std::cerr <<
"Problem with histogram computation: histogram[" << bin <<
"]=" << histo[bin]
400 <<
" but should be: " << expectedNumber[bin] << std::endl;
405 std::vector<unsigned int> expectedNumberSmooth(nbBins, 0);
406 expectedNumberSmooth[1] = 1;
407 expectedNumberSmooth[2] = 3;
408 expectedNumberSmooth[3] = 6;
409 expectedNumberSmooth[4] = 10;
410 expectedNumberSmooth[5] = 12;
411 expectedNumberSmooth[6] = 11;
412 expectedNumberSmooth[7] = 8;
413 expectedNumberSmooth[8] = 4;
414 expectedNumberSmooth[9] = 1;
418 for (
unsigned int bin = 0; bin < nbBins; ++bin) {
419 if (histo[bin] != expectedNumberSmooth[bin]) {
420 std::cerr <<
"Problem with smooth computation: histogram[" << bin <<
"]=" << histo[bin]
421 <<
" but should be: " << expectedNumberSmooth[bin] << std::endl;
428 for (
unsigned int r = 0;
r < nbRows; ++
r) {
429 for (
unsigned int c = 0; c < nbCols; ++c) {
430 if ((r == 0) || (r == (nbRows - 1))) {
433 if ((c == 0) || (c == (nbCols - 1))) {
439 std::cout <<
"I to which is applied the mask:" << std::endl;
440 for (
unsigned int r = 0;
r < nbRows; ++
r) {
441 for (
unsigned int c = 0; c < nbCols; ++c) {
443 std::cout << static_cast<int>(I[r][c]);
450 std::cout << std::endl;
454 histoWithMaskMono.
setMask(&mask);
455 histoWithMaskMono.
calculate(I, nbBins, 1);
458 histoWithMaskMulti.
setMask(&mask);
459 histoWithMaskMulti.
calculate(I, nbBins, 2);
461 std::vector<unsigned int> expectedNumberMask(nbBins, 0);
462 expectedNumberMask[1] = 1;
463 expectedNumberMask[2] = 3;
464 expectedNumberMask[3] = 4;
465 expectedNumberMask[4] = 5;
466 expectedNumberMask[5] = 7;
467 expectedNumberMask[6] = 4;
468 expectedNumberMask[7] = 5;
469 expectedNumberMask[8] = 5;
471 for (
unsigned int bin = 0; bin < nbBins; ++bin) {
472 if (histoWithMaskMono[bin] != expectedNumberMask[bin]) {
473 std::cerr <<
"Problem when using mask: histogram[" << bin <<
"]=" << histoWithMaskMono[bin]
474 <<
" but should be: " << expectedNumberMask[bin] << std::endl;
478 if (histoWithMaskMulti[bin] != expectedNumberMask[bin]) {
479 std::cerr <<
"Problem when using mask: histogram[" << bin <<
"]=" << histoWithMaskMulti[bin]
480 <<
" but should be: " << expectedNumberMask[bin] << std::endl;
485#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
489 double min = 0., max = 1.;
490 double desiredStep = (max - min) /
static_cast<double>(nbBins);
493 for (
unsigned int r = 0;
r < nbRows; ++
r) {
495 for (
unsigned int i = 1;
i <= 5; ++
i) {
496 for (
unsigned int count = 0; count <
i; ++count) {
497 double value = uniRand.uniform(0., desiredStep) + desiredStep *
static_cast<double>(
i +
r - 1);
504 std::cout <<
"Id = " << std::endl;
505 for (
unsigned int r = 0;
r < nbRows; ++
r) {
506 for (
unsigned int c = 0; c < nbCols; ++c) {
507 std::cout << std::setprecision(3) << Id[
r][c];
510 std::cout << std::endl;
514 std::vector<unsigned char> expectedNumberDouble(nbBins, 0), expectedNumberDoubleMask(nbBins, 0);
515 for (
unsigned int i = 0;
i < nbBins; ++
i) {
516 expectedNumberDouble[
i] = expectedNumber[
i + 1];
517 expectedNumberDoubleMask[
i] = expectedNumberMask[
i + 1];
521 histoDoubleMono.
calculate(Id, min, max, step, nbBins, 1);
524 std::cerr <<
"Problem with histogram computation of floating point images" << std::endl;
525 std::cout <<
"Computed step: " <<
step <<
" but should be: " << desiredStep << std::endl;
528 for (
unsigned int bin = 0; bin < nbBins; ++bin) {
529 if (histoDoubleMono[bin] != expectedNumberDouble[bin]) {
530 std::cerr <<
"Problem with monothread histogram computation of floating point images: histogram[" << bin <<
"]=" << histoDoubleMono[bin]
531 <<
" but should be: " << expectedNumberDouble[bin] << std::endl;
538 histoDoubleMulti.
calculate(Id, min, max, step, nbBins, 2);
541 std::cerr <<
"Problem with histogram computation of floating point images" << std::endl;
542 std::cout <<
"Computed step: " <<
step <<
" but should be: " << desiredStep << std::endl;
545 for (
unsigned int bin = 0; bin < nbBins; ++bin) {
546 if (histoDoubleMulti[bin] != expectedNumberDouble[bin]) {
547 std::cerr <<
"Problem with multithread histogram computation of floating point images: histogram[" << bin <<
"]=" << histoDoubleMulti[bin]
548 <<
" but should be: " << expectedNumberDouble[bin] << std::endl;
553 std::cout <<
"Id with mask= " << std::endl;
554 for (
unsigned int r = 0;
r < nbRows; ++
r) {
555 for (
unsigned int c = 0; c < nbCols; ++c) {
557 std::cout << std::setprecision(3) << Id[
r][c];
560 std::cout <<
"XXXXX";
564 std::cout << std::endl;
569 histoDoubleMonoMask.
setMask(&mask);
570 histoDoubleMonoMask.
calculate(Id, min, max, step, nbBins, 2);
573 histoDoubleMultiMask.
setMask(&mask);
574 histoDoubleMultiMask.
calculate(Id, min, max, step, nbBins, 2);
576 for (
unsigned int bin = 0; bin < nbBins; ++bin) {
577 if (histoDoubleMonoMask[bin] != expectedNumberDoubleMask[bin]) {
578 std::cerr <<
"Problem with monothread histogram computation of floating point images using mask: histogram[" << bin <<
"]=" << histoDoubleMonoMask[bin]
579 <<
" but should be: " << expectedNumberDoubleMask[bin] << std::endl;
583 if (histoDoubleMultiMask[bin] != expectedNumberDoubleMask[bin]) {
584 std::cerr <<
"Problem with multithread histogram computation of floating point images using mask: histogram[" << bin <<
"]=" << histoDoubleMultiMask[bin]
585 <<
" but should be: " << expectedNumberDoubleMask[bin] << std::endl;
591 std::cout <<
"testHistogram is OK!" << std::endl;
595 std::cerr <<
"Catch an exception: " <<
e.what() << std::endl;
error that can be emitted by ViSP classes.
Class to compute a gray level image histogram.
void smooth(unsigned int fsize=3)
void calculate(const vpImage< unsigned char > &I, unsigned int nbins=256, unsigned int nbThreads=1)
void setMask(const vpImage< bool > *p_mask)
Set a mask to ignore pixels for which the mask is false.
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition of the vpImage class member functions.
static bool equal(double x, double y, double threshold=0.001)
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Class for generating random numbers with uniform probability density.
VISP_EXPORT double measureTimeMs()