Visual Servoing Platform  version 3.6.1 under development (2024-12-04)
histogram.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 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 https://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  * Example of Histogram manipulation.
33  *
34  *
35 *****************************************************************************/
36 
43 #include <visp3/core/vpConfig.h>
44 #include <visp3/core/vpDebug.h>
45 #include <visp3/core/vpHistogram.h>
46 #include <visp3/core/vpImage.h>
47 #include <visp3/core/vpIoTools.h>
48 #include <visp3/io/vpImageIo.h>
49 #include <visp3/io/vpParseArgv.h>
50 
51 #include <iomanip>
52 #include <sstream>
53 #include <stdio.h>
54 #include <stdlib.h>
55 
56 // List of allowed command line options
57 #define GETOPTARGS "i:o:h"
58 
59 #ifdef ENABLE_VISP_NAMESPACE
60 using namespace VISP_NAMESPACE_NAME;
61 #endif
62 
81 void usage(const char *name, const char *badparam, std::string ipath, std::string opath, std::string user)
82 {
83  fprintf(stdout, "\n\
84 Read an image on the disk, display it using X11, display some\n\
85 features (line, circle, characters) in overlay and finally write \n\
86 the image and the overlayed features in an image on the disk.\n\
87 \n\
88 SYNOPSIS\n\
89  %s [-i <input image path>] [-o <output histogram path>]\n\
90  [-h]\n\
91 ",
92 name);
93 
94  fprintf(stdout, "\n\
95 OPTIONS: Default\n\
96  -i <input image path> %s\n\
97  Set image input path.\n\
98  From this path read \"Klimt/Klimt.pgm\"\n\
99  image.\n\
100  Setting the VISP_INPUT_IMAGE_PATH environment\n\
101  variable produces the same behaviour than using\n\
102  this option.\n\
103 \n\
104  -o <output histogram path> %s\n\
105  From this directory, creates the \"%s\"\n\
106  subdirectory depending on the username, where \n\
107  \"histogram.txt\" is saved.\n\
108 \n\
109  -h\n\
110  Print the help.\n\n",
111  ipath.c_str(), opath.c_str(), user.c_str());
112 
113  if (badparam) {
114  fprintf(stderr, "ERROR: \n");
115  fprintf(stderr, "\nBad parameter [%s]\n", badparam);
116  }
117 }
131 bool getOptions(int argc, const char **argv, std::string &ipath, std::string &opath, const std::string &user)
132 {
133  const char *optarg_;
134  int c;
135  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
136 
137  switch (c) {
138  case 'i':
139  ipath = optarg_;
140  break;
141  case 'o':
142  opath = optarg_;
143  break;
144  case 'h':
145  usage(argv[0], nullptr, ipath, opath, user);
146  return false;
147  break;
148 
149  default:
150  usage(argv[0], optarg_, ipath, opath, user);
151  return false;
152  break;
153  }
154  }
155 
156  if ((c == 1) || (c == -1)) {
157  // standalone param or error
158  usage(argv[0], nullptr, ipath, opath, user);
159  std::cerr << "ERROR: " << std::endl;
160  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
161  return false;
162  }
163 
164  return true;
165 }
166 
167 int main(int argc, const char **argv)
168 {
169  try {
170  std::string env_ipath;
171  std::string opt_ipath;
172  std::string opt_opath;
173  std::string ipath;
174  std::string opath;
175  std::string filename;
176  std::string username;
177 
178  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
179  // environment variable value
180  env_ipath = vpIoTools::getViSPImagesDataPath();
181 
182  // Set the default input path
183  if (!env_ipath.empty())
184  ipath = env_ipath;
185 
186 // Set the default output path
187 #if defined(_WIN32)
188  opt_opath = "C:/temp";
189 #else
190  opt_opath = "/tmp";
191 #endif
192 
193  // Get the user login name
194  vpIoTools::getUserName(username);
195 
196  // Read the command line options
197  if (getOptions(argc, argv, opt_ipath, opt_opath, username) == false) {
198  return EXIT_FAILURE;
199  }
200 
201  // Get the option values
202  if (!opt_ipath.empty())
203  ipath = opt_ipath;
204  if (!opt_opath.empty())
205  opath = opt_opath;
206 
207  // Append to the output path string, the login name of the user
208  std::string dirname = vpIoTools::createFilePath(opath, username);
209 
210  // Test if the output path exist. If no try to create it
211  if (vpIoTools::checkDirectory(dirname) == false) {
212  try {
213  // Create the dirname
214  vpIoTools::makeDirectory(dirname);
215  }
216  catch (...) {
217  usage(argv[0], nullptr, ipath, opath, username);
218  std::cerr << std::endl << "ERROR:" << std::endl;
219  std::cerr << " Cannot create " << dirname << std::endl;
220  std::cerr << " Check your -o " << opath << " option " << std::endl;
221  return EXIT_FAILURE;
222  }
223  }
224 
225  // Compare ipath and env_ipath. If they differ, we take into account
226  // the input path coming from the command line option
227  if (opt_ipath.empty()) {
228  if (ipath != env_ipath) {
229  std::cout << std::endl << "WARNING: " << std::endl;
230  std::cout << " Since -i <visp image path=" << ipath << "> "
231  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
232  << " we skip the environment variable." << std::endl;
233  }
234  }
235 
236  // Test if an input path is set
237  if (opt_ipath.empty() && env_ipath.empty()) {
238  usage(argv[0], nullptr, ipath, opath, username);
239  std::cerr << std::endl << "ERROR:" << std::endl;
240  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
241  << " environment variable to specify the location of the " << std::endl
242  << " image path where test images are located." << std::endl
243  << std::endl;
244  return EXIT_FAILURE;
245  }
246 
247  // Create a grey level image
249 
250  // Load a grey image from the disk
251  filename = ipath;
252  if (opt_ipath.empty())
253  filename = vpIoTools::createFilePath(ipath, "Klimt/Klimt.pgm");
254 
255  std::cout << "Read: " << filename << std::endl;
256  vpImageIo::read(I, filename);
257 
258  unsigned char distance = 60;
259  vpHistogram h;
260 
261  // Computes the histogram from the image
262  h.calculate(I);
263 
264  // Save the histogram
265  filename = dirname + vpIoTools::path("/histogram.txt");
266  std::cout << "Save the histogram in: " << filename << std::endl;
267  h.write(filename);
268 
269  // Smooth the histogram
270  h.smooth();
271  // Save the histogram
272  filename = dirname + vpIoTools::path("/histogram_smoothed.txt");
273  std::cout << "Save the smoothed histogram in: " << filename << std::endl;
274  h.write(filename);
275 
276  std::list<vpHistogramPeak> peaks;
277  unsigned int nbpeaks = 0;
278 
279  // get all the histogram peaks
280  nbpeaks = h.getPeaks(peaks);
281 
282  vpTRACE("List of peaks");
283  vpTRACE("Nb peaks: %d", nbpeaks);
284  if (nbpeaks) {
285  for (std::list<vpHistogramPeak>::const_iterator it = peaks.begin(); it != peaks.end(); ++it) {
286  vpHistogramPeak p = *it;
287  vpTRACE("Peak: gray level: %d value: %d", p.getLevel(), p.getValue());
288  }
289  }
290 
291  // sort all the histogram peaks list to have the highest peak at the
292  // beginning of the list, the smallest at the end.
293  nbpeaks = h.sort(peaks);
294 
295  vpTRACE("Sorted list of peaks");
296  vpTRACE("Nb peaks: %d", nbpeaks);
297  if (nbpeaks) {
298  for (std::list<vpHistogramPeak>::const_iterator it = peaks.begin(); it != peaks.end(); ++it) {
299  vpHistogramPeak p = *it;
300  vpTRACE("Peak: gray level: %d value: %d", p.getLevel(), p.getValue());
301  }
302  }
303 
304  // Get the two highest histogram peaks. peak1 is the highest
305  vpHistogramPeak peak1, peak2;
306  nbpeaks = h.getPeaks(distance, peak1, peak2);
307  if (nbpeaks != 2) {
308  std::cout << "Not a bimodal histogram..." << std::endl;
309  }
310  else {
311  vpTRACE("Bimodal histogram: main peak1: %d-%d second peak2: %d-%d", peak1.getLevel(), peak1.getValue(),
312  peak2.getLevel(), peak2.getValue());
313  }
314 
315  // Get the valey between the two highest peaks
316  vpHistogramValey valey;
317  if (h.getValey(peak1, peak2, valey) == false) {
318  vpTRACE("No valey found...");
319  }
320  else {
321  vpTRACE("Valey: %d-%d", valey.getLevel(), valey.getValue());
322  }
323 
324  vpHistogramValey valeyl, valeyr;
325 
326  {
327  // Search the two valeys around peak1
328  unsigned ret = h.getValey(distance, peak1, valeyl, valeyr);
329  if (ret == 0x00) {
330  vpTRACE("No left and right valey for peak %d-%d...", peak1.getLevel(), peak1.getValue());
331  }
332  else if (ret == 0x10) {
333  vpTRACE("No right valey for peak %d-%d...", peak1.getLevel(), peak1.getValue());
334  vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue());
335  }
336  else if (ret == 0x01) {
337  vpTRACE("No left valey for peak %d-%d...", peak1.getLevel(), peak1.getValue());
338  vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue());
339  }
340  else if (ret == 0x11) {
341  vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue());
342  vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue());
343  }
344  }
345  {
346  // Search the two valeys around peak2
347  unsigned ret = h.getValey(distance, peak2, valeyl, valeyr);
348  if (ret == 0x00) {
349  vpTRACE("No left and right valey for peak %d-%d...", peak2.getLevel(), peak2.getValue());
350  }
351  else if (ret == 0x10) {
352  vpTRACE("No right valey for peak %d-%d...", peak2.getLevel(), peak2.getValue());
353  vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue());
354  }
355  else if (ret == 0x01) {
356  vpTRACE("No left valey for peak %d-%d...", peak2.getLevel(), peak2.getValue());
357  vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue());
358  }
359  else if (ret == 0x11) {
360  vpTRACE("Left valey: %d-%d", valeyl.getLevel(), valeyl.getValue());
361  vpTRACE("Right valey: %d-%d", valeyr.getLevel(), valeyr.getValue());
362  }
363  }
364 
366  // get the valey between the two highest peaks. Here we don't know
367  // which of peakl or peakr is the highest.
368  vpHistogramPeak peakl, peakr;
369  if (h.getPeaks(distance, peakl, peakr, valey) == false) {
370  std::cout << "Not a bimodal histogram..." << std::endl;
371  }
372  else {
373  vpTRACE("Bimodal histogram: valey %d-%d for peakl: %d-%d peakr: %d-%d", valey.getLevel(), valey.getValue(),
374  peakl.getLevel(), peakl.getValue(), peakr.getLevel(), peakr.getValue());
375  }
376  return EXIT_SUCCESS;
377  }
378  catch (const vpException &e) {
379  std::cout << "Catch an exception: " << e << std::endl;
380  return EXIT_FAILURE;
381  }
382 }
error that can be emitted by ViSP classes.
Definition: vpException.h:60
Declaration of the peak (maximum value) in a gray level image histogram.
unsigned getValue() const
unsigned char getLevel() const
Declaration of the valey (minimum value) in a gray level image histogram.
unsigned char getLevel() const
unsigned getValue() const
Class to compute a gray level image histogram.
Definition: vpHistogram.h:106
unsigned getPeaks(std::list< vpHistogramPeak > &peaks)
void smooth(unsigned int fsize=3)
void calculate(const vpImage< unsigned char > &I, unsigned int nbins=256, unsigned int nbThreads=1)
unsigned sort(std::list< vpHistogramPeak > &peaks)
unsigned getValey(std::list< vpHistogramValey > &valey)
bool write(const std::string &filename)
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:147
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1053
static std::string path(const std::string &pathname)
Definition: vpIoTools.cpp:1005
static bool checkDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:396
static std::string getUserName()
Definition: vpIoTools.cpp:285
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1427
static void makeDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:550
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:70