Visual Servoing Platform  version 3.6.1 under development (2025-02-18)
trackDot2WithAutoDetection.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
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 https://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 auto detection of dots.
32  */
33 
41 #include <visp3/core/vpConfig.h>
42 #include <visp3/core/vpDebug.h>
43 
44 
45 #if defined(VISP_HAVE_MODULE_BLOB) && defined(VISP_HAVE_DISPLAY)
46 
47 #include <visp3/blob/vpDot2.h>
48 #include <visp3/core/vpImage.h>
49 #include <visp3/core/vpImagePoint.h>
50 #include <visp3/core/vpIoTools.h>
51 #include <visp3/gui/vpDisplayFactory.h>
52 #include <visp3/io/vpImageIo.h>
53 #include <visp3/io/vpParseArgv.h>
54 
55 // List of allowed command line options
56 #define GETOPTARGS "cdi:p:f:l:s:S:G:E:h"
57 
58 #ifdef ENABLE_VISP_NAMESPACE
59 using namespace VISP_NAMESPACE_NAME;
60 #endif
61 
76 void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, unsigned first,
77  unsigned last, unsigned step, double sizePrecision, double grayLevelPrecision,
78  double ellipsoidShapePrecision)
79 {
80 #if VISP_HAVE_DATASET_VERSION >= 0x030600
81  std::string ext("png");
82 #else
83  std::string ext("pgm");
84 #endif
85  fprintf(stdout, "\n\
86 Test auto detection of dots using vpDot2.\n\
87  \n\
88 SYNOPSIS\n\
89  %s [-i <input image path>] [-p <personal image path>]\n\
90  [-f <first image>] [-l <last image>] [-s <step>] \n\
91  [-S <size precision>] [-G <gray level precision>]\n\
92  [-E <ellipsoid shape precision>] [-c] [-d] [-h]\n",
93  name);
94 
95  fprintf(stdout, "\n\
96 OPTIONS: Default\n\
97  -i <input image path> %s\n\
98  Set image input path.\n\
99  From this path read images \n\
100  \"mire-2/image.%%04d.%s\"\n\
101  Setting the VISP_INPUT_IMAGE_PATH environment\n\
102  variable produces the same behaviour than using\n\
103  this option.\n\
104  \n\
105  -p <personal image path> %s\n\
106  Specify a personal sequence containing images \n\
107  to process.\n\
108  The format is selected by analysing the filename extension.\n\
109  Example : \"/Temp/visp-images/mire-2/image.%%04d.%s\"\n\
110  %%04d is for the image numbering.\n\
111  \n\
112  -f <first image> %u\n\
113  First image number of the sequence.\n\
114  \n\
115  -l <last image> %u\n\
116  Last image number of the sequence.\n\
117  \n\
118  -s <step> %u\n\
119  Step between two images.\n\
120  \n\
121  -S <size precision> %f\n\
122  Precision of the size of the dot. \n\
123  It is a double precision float witch value is in ]0,1].\n\
124  1 means full precision, the sizes (width, heigth, surface) \n\
125  of the dots must the same, whereas values close to 0 \n\
126  show a very bad precision.\n\
127 \n\
128  -G <gray level precision> %f\n\
129  Precision of the gray level of the dot. \n\
130  It is a double precision float witch value is in ]0,1].\n\
131  1 means full precision, the gray level must the same in \n\
132  the wall dot, whereas values close to 0 \n\
133  show a very bad precision.\n\
134 \n\
135  -E <ellipsoid shape precision> %f\n\
136  Precision of the ellipsoid shape of the dot. \n\
137  It is a double precision float witch value is in [0,1].\n\
138  1 means full precision, the shape should be a perfect ellipsoid,\n\
139  whereas values close to 0 show a very bad precision.\n\
140  0 means the shape of dots is not tested \n\
141 \n",
142 ipath.c_str(), ext.c_str(), ppath.c_str(), ext.c_str(), first, last, step, sizePrecision, grayLevelPrecision,
143 ellipsoidShapePrecision);
144 
145  fprintf(stdout, "\
146  -c\n\
147  Disable the mouse click. Useful to automate the \n\
148  execution of this program without human intervention.\n\
149  \n\
150  -d \n\
151  Turn off the display.\n\
152  \n\
153  -h\n\
154  Print the help.\n");
155 
156  if (badparam)
157  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
158 }
179 bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, unsigned &last,
180  unsigned &step, double &sizePrecision, double &grayLevelPrecision, double &ellipsoidShapePrecision,
181  bool &click_allowed, bool &display)
182 {
183  const char *optarg_;
184  int c;
185  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
186 
187  switch (c) {
188  case 'c':
189  click_allowed = false;
190  break;
191  case 'd':
192  display = false;
193  break;
194  case 'i':
195  ipath = optarg_;
196  break;
197  case 'p':
198  ppath = optarg_;
199  break;
200  case 'f':
201  first = (unsigned)atoi(optarg_);
202  break;
203  case 'l':
204  last = (unsigned)atoi(optarg_);
205  break;
206  case 's':
207  step = (unsigned)atoi(optarg_);
208  break;
209  case 'S':
210  sizePrecision = atof(optarg_);
211  break;
212  case 'G':
213  grayLevelPrecision = atof(optarg_);
214  break;
215  case 'E':
216  ellipsoidShapePrecision = atof(optarg_);
217  break;
218  case 'h':
219  usage(argv[0], nullptr, ipath, ppath, first, last, step, sizePrecision, grayLevelPrecision,
220  ellipsoidShapePrecision);
221  return false;
222  break;
223 
224  default:
225  usage(argv[0], optarg_, ipath, ppath, first, last, step, sizePrecision, grayLevelPrecision,
226  ellipsoidShapePrecision);
227  return false;
228  break;
229  }
230  }
231 
232  if ((c == 1) || (c == -1)) {
233  // standalone param or error
234  usage(argv[0], nullptr, ipath, ppath, first, last, step, sizePrecision, grayLevelPrecision,
235  ellipsoidShapePrecision);
236  std::cerr << "ERROR: " << std::endl;
237  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
238  return false;
239  }
240 
241  return true;
242 }
243 
244 int main(int argc, const char **argv)
245 {
246  try {
247  std::string env_ipath;
248  std::string opt_ipath;
249  std::string ipath;
250  std::string opt_ppath;
251  std::string dirname;
252  std::string filename;
253  unsigned opt_first = 1;
254  unsigned opt_last = 10;
255  unsigned opt_step = 1;
256  double opt_sizePrecision = 0.65;
257  double opt_grayLevelPrecision = 0.85;
258  double opt_ellipsoidShapePrecision = 0.8;
259  bool opt_click_allowed = true;
260  bool opt_display = true;
261 
262 #if VISP_HAVE_DATASET_VERSION >= 0x030600
263  std::string ext("png");
264 #else
265  std::string ext("pgm");
266 #endif
267 
268  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
269  // environment variable value
270  env_ipath = vpIoTools::getViSPImagesDataPath();
271 
272  // Set the default input path
273  if (!env_ipath.empty())
274  ipath = env_ipath;
275 
276  // Read the command line options
277  if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_first, opt_last, opt_step, opt_sizePrecision,
278  opt_grayLevelPrecision, opt_ellipsoidShapePrecision, opt_click_allowed, opt_display) == false) {
279  return EXIT_FAILURE;
280  }
281 
282  // Get the option values
283  if (!opt_ipath.empty())
284  ipath = opt_ipath;
285 
286  // Compare ipath and env_ipath. If they differ, we take into account
287  // the input path coming from the command line option
288  if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) {
289  if (ipath != env_ipath) {
290  std::cout << std::endl << "WARNING: " << std::endl;
291  std::cout << " Since -i <visp image path=" << ipath << "> "
292  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
293  << " we skip the environment variable." << std::endl;
294  }
295  }
296 
297  // Test if an input path is set
298  if (opt_ipath.empty() && env_ipath.empty()) {
299  usage(argv[0], nullptr, ipath, opt_ppath, opt_first, opt_last, opt_step, opt_sizePrecision,
300  opt_grayLevelPrecision, opt_ellipsoidShapePrecision);
301  std::cerr << std::endl << "ERROR:" << std::endl;
302  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
303  << " environment variable to specify the location of the " << std::endl
304  << " image path where test images are located." << std::endl
305  << std::endl
306  << " Use -p <personal image path> option if you want to " << std::endl
307  << " use personal images." << std::endl;
308  return EXIT_FAILURE;
309  }
310 
311  // Declare an image, this is a gray level image (unsigned char)
312  // it size is not defined yet, it will be defined when the image will
313  // read on the disk
315  vpDisplay *display = nullptr;
316  std::ostringstream s;
317  char cfilename[FILENAME_MAX];
318  unsigned iter = opt_first; // Image number
319 
320  if (opt_ppath.empty()) {
321 
322  // Warning :
323  // The image sequence is not provided with the ViSP package
324  // therefore the program will return an error :
325  // !! couldn't read file visp-images/mire-2/image.0001.png
326  //
327  // ViSP dataset is available on the visp www site
328  // https://visp.inria.fr/download/.
329 
330  // Set the path location of the image sequence
331  dirname = vpIoTools::createFilePath(ipath, "mire-2");
332 
333  // Build the name of the image file
334 
335  s.setf(std::ios::right, std::ios::adjustfield);
336  s << "image." << std::setw(4) << std::setfill('0') << iter << "." << ext;
337  filename = vpIoTools::createFilePath(dirname, s.str());
338  }
339  else {
340  snprintf(cfilename, FILENAME_MAX, opt_ppath.c_str(), iter);
341  filename = cfilename;
342  }
343  // Read the image named "filename", and put the bitmap into the image structure I.
344  // I is initialized to the correct size
345  //
346  // vpImageIo::read() may throw various exception if, for example,
347  // the file does not exist, or if the memory cannot be allocated
348  try {
349  vpCTRACE << "Load: " << filename << std::endl;
350 
351  vpImageIo::read(I, filename);
352  }
353  catch (...) {
354  // If an exception is thrown by vpImageIo::read() it will result in the end of the program.
355  std::cerr << std::endl << "ERROR:" << std::endl;
356  std::cerr << " Cannot read " << filename << std::endl;
357  if (opt_ppath.empty()) {
358  std::cerr << " Check your -i " << ipath << " option " << std::endl
359  << " or VISP_INPUT_IMAGE_PATH environment variable." << std::endl;
360  }
361  else {
362  std::cerr << " Check your -p " << opt_ppath << " option " << std::endl;
363  }
364  return EXIT_FAILURE;
365  }
366 
367  if (opt_display) {
368  // We open a window using either X11, GTK, OpenCV or GDI
370  // Display size is automatically defined by the image (I) size
371  display->init(I, 100, 100, "Display...");
372  // Display the image
373  // The image class has a member that specify a pointer toward
374  // the display that has been initialized in the display declaration
375  // therefore is is no longer necessary to make a reference to the
376  // display variable.
378  vpDisplay::flush(I);
379  }
380 
381  // Dot declaration
382  vpDot2 d;
383 
384  d.setGraphics(true);
385  if (opt_click_allowed & opt_display) {
386  d.setGrayLevelPrecision(opt_grayLevelPrecision);
387 
388  std::cout << "Please click on a dot to initialize detection" << std::endl;
389 
390  d.initTracking(I);
391  if (opt_display) {
392  vpImagePoint cog;
393  cog = d.getCog();
395  vpDisplay::flush(I);
396  }
397  d.setSizePrecision(opt_sizePrecision);
398  d.setEllipsoidShapePrecision(opt_ellipsoidShapePrecision);
399  printf("Dot characteristics: \n");
400  printf(" width : %lf\n", d.getWidth());
401  printf(" height: %lf\n", d.getHeight());
402  printf(" area: %lf\n", d.getArea());
403  printf(" gray level min: %u\n", d.getGrayLevelMin());
404  printf(" gray level max: %u\n", d.getGrayLevelMax());
405  printf(" grayLevelPrecision: %lf\n", d.getGrayLevelPrecision());
406  printf(" sizePrecision: %lf\n", d.getSizePrecision());
407  printf(" ellipsoidShapePrecision: %lf\n", d.getEllipsoidShapePrecision());
408  }
409  else {
410  // Set dot characteristics for the auto detection
411  d.setGraphics(true);
412  d.setWidth(15.0);
413  d.setHeight(12.0);
414  d.setArea(124);
415  d.setGrayLevelMin(164);
416  d.setGrayLevelMax(255);
417  d.setGrayLevelPrecision(opt_grayLevelPrecision);
418  d.setSizePrecision(opt_sizePrecision);
419  d.setEllipsoidShapePrecision(opt_ellipsoidShapePrecision);
420  }
421 
422  bool quit = false;
423  while ((iter < opt_last) && (!quit)) {
424  // set the new image name
425  if (opt_ppath.empty()) {
426  s.str("");
427  s << "image." << std::setw(4) << std::setfill('0') << iter << "." << ext;
428  filename = vpIoTools::createFilePath(dirname, s.str());
429  }
430  else {
431  snprintf(cfilename, FILENAME_MAX, opt_ppath.c_str(), iter);
432  filename = cfilename;
433  }
434  // read the image
435  vpImageIo::read(I, filename);
436 
437  if (opt_display) {
438  // Display the image
440  }
441 
442  std::cout << "Search dots in image" << filename << std::endl;
443  std::list<vpDot2> list_d;
444  d.searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), list_d);
445 
446  if (list_d.empty()) {
447  std::cout << "Dot auto detection did not work." << std::endl;
448  return EXIT_FAILURE;
449  }
450  else {
451  std::cout << std::endl << list_d.size() << " dots are detected" << std::endl;
452 
453  if (opt_display) {
454  int i = 0;
455  // Parse all founded dots for display
456  for (std::list<vpDot2>::const_iterator it = list_d.begin(); it != list_d.end(); ++it) {
457  vpImagePoint cog = (*it).getCog();
458 
459  std::cout << "Dot " << i++ << " : " << cog.get_u() << " " << cog.get_v() << std::endl;
460 
461  vpDisplay::displayCross(I, cog, 16, vpColor::blue, 3);
462  }
463  if (opt_click_allowed) {
464  vpDisplay::displayText(I, 20, 20, "Left click to continue on next image", vpColor::red);
465  vpDisplay::displayText(I, 40, 20, "Right click to quit", vpColor::red);
467  if (vpDisplay::getClick(I, button, true)) {
468  if (button == vpMouseButton::button3) {
469  quit = true;
470  }
471  }
472  }
473  vpDisplay::flush(I);
474  }
475  }
476 
477  iter += opt_step;
478  }
479  if (opt_display && opt_click_allowed && !quit) {
480  std::cout << "\nA click to exit..." << std::endl;
481  // Wait for a blocking mouse click
483  }
484 
485  if (display) {
486  delete display;
487  }
488 
489  return EXIT_SUCCESS;
490  }
491  catch (const vpException &e) {
492  std::cout << "Catch an exception: " << e << std::endl;
493  return EXIT_FAILURE;
494  }
495 }
496 #else
497 #include <iostream>
498 
499 int main()
500 {
501  std::cout << "visp_me module or X11, GTK, GDI or OpenCV display "
502  "functionalities are required..."
503  << std::endl;
504  return EXIT_SUCCESS;
505 }
506 
507 #endif
static const vpColor red
Definition: vpColor.h:198
static const vpColor blue
Definition: vpColor.h:204
static const vpColor green
Definition: vpColor.h:201
Class that defines generic functionalities for display.
Definition: vpDisplay.h:178
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
static void flush(const vpImage< unsigned char > &I)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
This tracker is meant to track a blob (connex pixels with same gray level) on a vpImage.
Definition: vpDot2.h:125
unsigned int getGrayLevelMin() const
Definition: vpDot2.h:219
unsigned int getGrayLevelMax() const
Definition: vpDot2.h:225
void setGraphics(bool activate)
Definition: vpDot2.h:318
double getEllipsoidShapePrecision() const
Definition: vpDot2.cpp:651
void searchDotsInArea(const vpImage< unsigned char > &I, int area_u, int area_v, unsigned int area_w, unsigned int area_h, std::list< vpDot2 > &niceDots)
void setGrayLevelMax(const unsigned int &max)
Definition: vpDot2.h:357
double getArea() const
Definition: vpDot2.cpp:628
void setSizePrecision(const double &sizePrecision)
Definition: vpDot2.cpp:756
void setGrayLevelPrecision(const double &grayLevelPrecision)
Definition: vpDot2.cpp:726
void setGrayLevelMin(const unsigned int &min)
Definition: vpDot2.h:338
void setHeight(const double &height)
Definition: vpDot2.cpp:695
vpImagePoint getCog() const
Definition: vpDot2.h:181
double getSizePrecision() const
Definition: vpDot2.cpp:642
double getGrayLevelPrecision() const
Definition: vpDot2.cpp:635
void setWidth(const double &width)
Definition: vpDot2.cpp:683
double getWidth() const
Definition: vpDot2.cpp:614
void setEllipsoidShapePrecision(const double &ellipsoidShapePrecision)
Definition: vpDot2.cpp:801
void setArea(const double &area)
Definition: vpDot2.cpp:707
void initTracking(const vpImage< unsigned char > &I, unsigned int size=0)
Definition: vpDot2.cpp:269
double getHeight() const
Definition: vpDot2.cpp:621
error that can be emitted by ViSP classes.
Definition: vpException.h:60
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:147
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
double get_u() const
Definition: vpImagePoint.h:136
double get_v() const
Definition: vpImagePoint.h:147
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getHeight() const
Definition: vpImage.h:181
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1053
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1427
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:70
vpDisplay * allocateDisplay()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.