Visual Servoing Platform  version 3.6.1 under development (2024-05-08)
trackMeEllipse.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  * Tracking of an ellipse.
33  *
34 *****************************************************************************/
35 
47 #include <visp3/core/vpConfig.h>
48 #include <visp3/core/vpDebug.h>
49 
50 #include <iomanip>
51 #include <sstream>
52 #include <stdio.h>
53 #include <stdlib.h>
54 
55 #if defined(VISP_HAVE_MODULE_ME) && \
56  (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV))
57 
58 #include <visp3/core/vpColor.h>
59 #include <visp3/core/vpImage.h>
60 #include <visp3/core/vpImagePoint.h>
61 #include <visp3/core/vpIoTools.h>
62 #include <visp3/gui/vpDisplayGDI.h>
63 #include <visp3/gui/vpDisplayGTK.h>
64 #include <visp3/gui/vpDisplayOpenCV.h>
65 #include <visp3/gui/vpDisplayX.h>
66 #include <visp3/io/vpParseArgv.h>
67 #include <visp3/io/vpVideoReader.h>
68 #include <visp3/io/vpVideoWriter.h>
69 #include <visp3/me/vpMeEllipse.h>
70 
71 // List of allowed command line options
72 #define GETOPTARGS "Aabcdf:hi:l:p:r:s:S:t:vw:"
73 
88 void usage(const char *name, const char *badparam, const std::string &video_in_ipath, const std::string &video_in_ppath,
89  unsigned video_in_first, int video_in_last, int video_in_step, int me_range, int me_sample_step, int me_threshold)
90 {
91 #if VISP_HAVE_DATASET_VERSION >= 0x030600
92  std::string ext("png");
93 #else
94  std::string ext("pgm");
95 #endif
96  fprintf(stdout, "\n\
97 Example of ellipse/circle or arc of ellipse/circle tracking using vpMeEllipse.\n\
98 \n\
99 SYNOPSIS\n\
100  %s [-i <visp dataset directory>] [-p <personal image path>]\n\
101  [-f <video first image>] [-l <video last image>] [-s <video step>]\n\
102  [-r <moving-edge range] [-t <moving-edge threshold] [-S <moving-edge sample step>]\n\
103  [-w <output images sequence name>] \n\
104  [-c] [-d] [-a] [-A] [-b] [-v] [-h]\n",
105  name);
106 
107  fprintf(stdout, "\n\
108 OPTIONS: Default\n\
109  -i <visp dataset directory> %s\n\
110  Set visp dataset directory location.\n\
111  From this directory read images \n\
112  \"ellipse-1/image.%%04d.%s\"\n\
113  Setting the VISP_INPUT_IMAGE_PATH environment\n\
114  variable produces the same behaviour than using\n\
115  this option.\n\
116  \n\
117  -p <personal image path> %s\n\
118  Specify a personal sequence containing images \n\
119  to process.\n\
120  The format is selected by analyzing \n\
121  the filename extension.\n\
122  Example : \"C:/Temp/ViSP-images/ellipse-1/image.%%04d.%s\"\n\
123  %%04d is for the image numbering.\n\
124  \n\
125  -f <video first image> %d\n\
126  First image number to process.\n\
127  Set -1 to process the first image of the sequence.\n\
128  \n\
129  -l <video last image> %d\n\
130  Last image number to process. \n\
131  Set -1 to process images until the last image\n\
132  of the sequence.\n\
133  \n\
134  -s <video step> %d\n\
135  Step between two images.\n\
136  \n\
137  -r <moving-edge range> %d\n\
138  Moving-edge range.\n\
139  Increase value to consider large displacement. \n\
140  When set to -1, use default value. \n\
141  \n\
142  -S <moving-edge sample step> %d\n\
143  Moving-edge sample step.\n\
144  Distance between two moving-edges samples in degrees. \n\
145  When set to -1, use default value. \n\
146  \n\
147  -t <moving-edge threshold> %d\n\
148  Moving-edge threshold corresponding to the minimum \n\
149  contrast to consider. Value in range [0 ; 255] \n\
150  When set to -1, use default value. \n\
151  \n\
152  -c\n\
153  Disable the mouse click. Useful to automate the \n\
154  execution of this program without human intervention.\n\
155  \n\
156  -d \n\
157  Turn off the display.\n\
158  \n\
159  -a \n\
160  Enable arc of ellipse tracking.\n\
161  \n\
162  -b \n\
163  Enable circle tracking.\n\
164  \n\
165  -w <output images sequence name> \n\
166  Save images with tracking results in overlay.\n\
167  Example: \"result/I%%04d.png\" \n\
168  \n\
169  -A \n\
170  When display is activated using -d option, enable\n\
171  windows auto scaling to fit the screen size. \n\
172  \n\
173  -v\n\
174  Enable verbosity.\n\
175  \n\
176  -h\n\
177  Print the help.\n",
178  video_in_ipath.c_str(), ext.c_str(), video_in_ppath.c_str(), ext.c_str(), video_in_first, video_in_last,
179  video_in_step, me_range, me_sample_step, me_threshold);
180 
181  if (badparam)
182  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
183 }
184 
210 bool getOptions(int argc, const char **argv, std::string &video_in_ipath, std::string &video_in_ppath,
211  int &video_in_first, int &video_in_last, int &video_in_step,
212  bool &click_allowed, bool &display, bool &display_scale_auto, bool &track_circle, bool &track_arc,
213  std::string &video_out_save, int &me_range, int &me_sample_step, int &me_threshold, bool &verbose)
214 {
215  const char *optarg_;
216  int c;
217  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
218 
219  switch (c) {
220  case 'A':
221  display_scale_auto = true;
222  break;
223  case 'a':
224  track_arc = true;
225  break;
226  case 'b':
227  track_circle = true;
228  break;
229  case 'c':
230  click_allowed = false;
231  break;
232  case 'd':
233  display = false;
234  break;
235  case 'f':
236  video_in_first = atoi(optarg_);
237  break;
238  case 'i':
239  video_in_ipath = std::string(optarg_);
240  break;
241  case 'l':
242  video_in_last = atoi(optarg_);
243  break;
244  case 'p':
245  video_in_ppath = std::string(optarg_);
246  break;
247  case 'r':
248  me_range = atoi(optarg_);
249  break;
250  case 's':
251  video_in_step = atoi(optarg_);
252  break;
253  case 'S':
254  me_sample_step = atoi(optarg_);
255  break;
256  case 't':
257  me_threshold = atoi(optarg_);
258  break;
259  case 'w':
260  video_out_save = std::string(optarg_);
261  break;
262  case 'v':
263  verbose = true;
264  break;
265  case 'h':
266  usage(argv[0], nullptr, video_in_ipath, video_in_ppath, video_in_first, video_in_last, video_in_step, me_range, me_sample_step, me_threshold);
267  return false;
268  break;
269 
270  default:
271  usage(argv[0], optarg_, video_in_ipath, video_in_ppath, video_in_first, video_in_last, video_in_step, me_range, me_sample_step, me_threshold);
272  return false;
273  break;
274  }
275  }
276 
277  if ((c == 1) || (c == -1)) {
278  // standalone param or error
279  usage(argv[0], nullptr, video_in_ipath, video_in_ppath, video_in_first, video_in_last, video_in_step, me_range, me_sample_step, me_threshold);
280  std::cerr << "ERROR: " << std::endl;
281  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
282  return false;
283  }
284 
285  return true;
286 }
287 
288 int main(int argc, const char **argv)
289 {
290 #if defined(VISP_HAVE_LAPACK) || defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_OPENCV)
291  std::string env_ipath;
292  std::string opt_ipath;
293  std::string ipath;
294  std::string opt_ppath;
295  std::string videoname;
296  int opt_first = -1;
297  int opt_last = -1;
298  int opt_step = 1;
299  int opt_me_range = 30;
300  int opt_me_sample_step = 5;
301  int opt_me_threshold = 20; // Value in [0 ; 255]
302  bool opt_click_allowed = true;
303  bool opt_display = true;
304  bool opt_display_scale_auto = false;
305  bool opt_track_circle = false;
306  bool opt_track_arc = false;
307  bool opt_verbose = false;
308  std::string opt_save;
309  unsigned int thickness = 1;
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 is
313  // read on the disk
315 
316  vpDisplay *display = nullptr;
317 
318  try {
319  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
320  // environment variable value
321  env_ipath = vpIoTools::getViSPImagesDataPath();
322 
323  // Set the default input path
324  if (!env_ipath.empty())
325  ipath = env_ipath;
326 
327  // Read the command line options
328  if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_first, opt_last, opt_step, opt_click_allowed,
329  opt_display, opt_display_scale_auto, opt_track_circle, opt_track_arc, opt_save,
330  opt_me_range, opt_me_sample_step, opt_me_threshold, opt_verbose) == false) {
331  return EXIT_FAILURE;
332  }
333 
334  // Get the option values
335  if (!opt_ipath.empty())
336  ipath = opt_ipath;
337 
338  // Compare ipath and env_ipath. If they differ, we take into account
339  // the input path coming from the command line option
340  if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) {
341  if (ipath != env_ipath) {
342  std::cout << std::endl << "WARNING: " << std::endl;
343  std::cout << " Since -i <visp image path=" << ipath << "> "
344  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
345  << " we skip the environment variable." << std::endl;
346  }
347  }
348 
349  // Test if an input path is set
350  if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()) {
351  usage(argv[0], nullptr, ipath, opt_ppath, opt_first, opt_last, opt_step, opt_me_range, opt_me_sample_step, opt_me_threshold);
352  std::cerr << std::endl << "ERROR:" << std::endl;
353  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
354  << " environment variable to specify the location of the " << std::endl
355  << " image path where test images are located." << std::endl
356  << " Use -p <personal image path> option if you want to " << std::endl
357  << " use personal images." << std::endl
358  << std::endl;
359 
360  return EXIT_FAILURE;
361  }
362 
363  // Create output folder if needed
364  if (!opt_save.empty()) {
365  std::string parent = vpIoTools::getParent(opt_save);
366  if (!parent.empty()) {
367  std::cout << "Create output directory: " << parent << std::endl;
368  vpIoTools::makeDirectory(parent);
369  }
370  thickness += 1;
371  }
372 
373  vpVideoReader g;
374  if (opt_ppath.empty()) {
375  // Set the path location of the image sequence
376 #if VISP_HAVE_DATASET_VERSION >= 0x030600
377  videoname = vpIoTools::createFilePath(ipath, "ellipse-1/image.%04d.png");
378 #else
379  videoname = vpIoTools::createFilePath(ipath, "ellipse-1/image.%04d.pgm");
380 #endif
381  g.setFileName(videoname);
382  }
383  else {
384  g.setFileName(opt_ppath);
385  }
386 
387  if (opt_first > 0) {
388  g.setFirstFrameIndex(opt_first);
389  }
390  if (opt_last > 0) {
391  g.setLastFrameIndex(opt_last);
392  }
393  g.setFrameStep(opt_step);
394  g.open(I);
395 
396  if (opt_display) {
397  // We open a window using either X11, GTK or GDI.
398 #if defined(VISP_HAVE_X11)
399  display = new vpDisplayX;
400 #elif defined(VISP_HAVE_GTK)
401  display = new vpDisplayGTK;
402 #elif defined(VISP_HAVE_GDI)
403  display = new vpDisplayGDI;
404 #elif defined(HAVE_OPENCV_HIGHGUI)
405  display = new vpDisplayOpenCV;
406 #endif
407  if (opt_display_scale_auto) {
408  display->setDownScalingFactor(vpDisplay::SCALE_AUTO);
409  }
410  // Display size is automatically defined by the image (I) size
411  display->init(I, 10, 10, "Current image");
412  // Display the image
413  // The image class has a member that specify a pointer toward
414  // the display that has been initialized in the display declaration
415  // therefore is is no longer necessary to make a reference to the
416  // display variable.
418  vpDisplay::flush(I);
419  }
420 
421  vpVideoWriter *writer = nullptr;
422  vpImage<vpRGBa> O;
423  if (!opt_save.empty()) {
424  writer = new vpVideoWriter();
425  writer->setFileName(opt_save);
426  writer->open(O);
427  }
428  vpMeEllipse me_ellipse;
429 
430  vpMe me;
431  if (opt_me_range > 0) {
432  me.setRange(opt_me_range);
433  }
434  if (opt_me_sample_step > 0) {
435  me.setSampleStep(opt_me_sample_step);
436  }
437  if (opt_me_threshold > 0) {
439  me.setThreshold(opt_me_threshold);
440  }
441 
442  me_ellipse.setMe(&me);
444 
445  std::cout << "Video settings" << std::endl;
446  std::cout << " Name : " << g.getFrameName() << std::endl;
447  std::cout << " First image: " << g.getFirstFrameIndex() << std::endl;
448  std::cout << " Last image : " << g.getLastFrameIndex() << std::endl;
449  std::cout << " Step : " << g.getFrameStep() << std::endl;
450  std::cout << " Image size : " << I.getWidth() << " x " << I.getHeight() << std::endl;
451 
452  std::cout << "Moving-edges settings" << std::endl;
453  std::cout << " Sample step : " << me_ellipse.getMe()->getSampleStep() << std::endl;
454  std::cout << " Range : " << me_ellipse.getMe()->getRange() << std::endl;
455  std::cout << " Threshold type: " << (me_ellipse.getMe()->getLikelihoodThresholdType() == vpMe::NORMALIZED_THRESHOLD ? "normalized" : "old threshold (to be avoided)") << std::endl;
456  std::cout << " Threshold : " << me_ellipse.getMe()->getThreshold() << std::endl;
457 
458  if (opt_click_allowed) {
459  me_ellipse.initTracking(I, opt_track_circle, opt_track_arc);
460  }
461  else {
462  // Create a list of clockwise points to automate the test
463  std::vector<vpImagePoint> ip;
464  ip.push_back(vpImagePoint(195, 329));
465  ip.push_back(vpImagePoint(243, 164));
466  ip.push_back(vpImagePoint(201, 36));
467  ip.push_back(vpImagePoint(83, 126));
468  ip.push_back(vpImagePoint(33, 276));
469 
470  me_ellipse.initTracking(I, ip, opt_track_circle, opt_track_arc);
471  }
472  if (opt_display) {
473  me_ellipse.display(I, vpColor::green, thickness);
474  }
475 
476  if (opt_display && opt_click_allowed) {
477  std::cout << "A click to continue..." << std::endl;
479  }
480  bool quit = false;
481 
482  while (!g.end() && !quit) {
483  // Read the image
484  g.acquire(I);
485  std::stringstream ss;
486  ss << "Process image " << g.getFrameIndex();
487  if (opt_verbose) {
488  std::cout << "-- " << ss.str() << std::endl;
489  }
490  if (opt_display) {
491  // Display the image
493  vpDisplay::displayText(I, 20, 10, ss.str(), vpColor::red);
494  if (opt_click_allowed) {
495  vpDisplay::displayText(I, 40, 10, "Click to exit...", vpColor::red);
496  }
497  }
498  me_ellipse.track(I);
499 
500  if (opt_display) {
501  me_ellipse.display(I, vpColor::green, thickness);
502  vpDisplay::flush(I);
503  }
504  if (!opt_save.empty()) {
505  vpDisplay::getImage(I, O);
506  writer->saveFrame(O);
507  }
508  if (opt_display && opt_click_allowed) {
509  if (vpDisplay::getClick(I, false)) {
510  quit = true;
511  }
512  }
513  }
514 
515  if (opt_display && opt_click_allowed && !quit) {
517  }
518 
519  if (writer) {
520  delete writer;
521  }
522  if (display) {
523  delete display;
524  }
525  return EXIT_SUCCESS;
526  }
527  catch (const vpException &e) {
528  std::cout << "Catch an exception: " << e << std::endl;
529  if (opt_display && opt_click_allowed) {
531  }
532  return EXIT_FAILURE;
533  }
534 #else
535  (void)argc;
536  (void)argv;
537  std::cout << "Cannot run this example: install Lapack, Eigen3 or OpenCV" << std::endl;
538 #endif
539 }
540 #else
541 #include <iostream>
542 
543 int main()
544 {
545  std::cout << "visp_me module or X11, GTK, GDI or OpenCV display "
546  "functionalities are required..."
547  << std::endl;
548  return EXIT_SUCCESS;
549 }
550 
551 #endif
static const vpColor red
Definition: vpColor.h:211
static const vpColor green
Definition: vpColor.h:214
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:128
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Definition: vpDisplayGTK.h:128
The vpDisplayOpenCV allows to display image using the OpenCV library. Thus to enable this class OpenC...
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition: vpDisplayX.h:128
Class that defines generic functionalities for display.
Definition: vpDisplay.h:173
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void getImage(const vpImage< unsigned char > &Is, vpImage< vpRGBa > &Id)
Definition: vpDisplay.cpp:138
static void flush(const vpImage< unsigned char > &I)
@ SCALE_AUTO
Definition: vpDisplay.h:179
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
error that can be emitted by ViSP classes.
Definition: vpException.h:59
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
unsigned int getWidth() const
Definition: vpImage.h:245
unsigned int getHeight() const
Definition: vpImage.h:184
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1832
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:2195
static void makeDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:981
static std::string getParent(const std::string &pathname)
Definition: vpIoTools.cpp:2086
Class that tracks an ellipse using moving edges.
Definition: vpMeEllipse.h:94
void display(const vpImage< unsigned char > &I, const vpColor &col, unsigned int thickness=1)
void initTracking(const vpImage< unsigned char > &I, bool trackCircle=false, bool trackArc=false)
void track(const vpImage< unsigned char > &I)
@ RANGE_RESULT
Definition: vpMeSite.h:75
vpMe * getMe()
Definition: vpMeTracker.h:171
void setDisplay(vpMeSite::vpMeSiteDisplayType select)
Definition: vpMeTracker.h:250
void setMe(vpMe *me)
Definition: vpMeTracker.h:278
Definition: vpMe.h:124
vpLikelihoodThresholdType getLikelihoodThresholdType() const
Definition: vpMe.h:341
void setRange(const unsigned int &range)
Definition: vpMe.h:429
void setLikelihoodThresholdType(const vpLikelihoodThresholdType likelihood_threshold_type)
Definition: vpMe.h:519
void setThreshold(const double &threshold)
Definition: vpMe.h:480
void setSampleStep(const double &sample_step)
Definition: vpMe.h:436
double getThreshold() const
Definition: vpMe.h:305
double getSampleStep() const
Definition: vpMe.h:289
unsigned int getRange() const
Definition: vpMe.h:282
@ NORMALIZED_THRESHOLD
Definition: vpMe.h:135
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
void acquire(vpImage< vpRGBa > &I)
void setLastFrameIndex(const long last_frame)
long getLastFrameIndex()
void open(vpImage< vpRGBa > &I)
void setFileName(const std::string &filename)
void setFirstFrameIndex(const long first_frame)
long getFirstFrameIndex()
void setFrameStep(const long frame_step)
long getFrameStep() const
std::string getFrameName() const
long getFrameIndex() const
Class that enables to write easily a video file or a sequence of images.
void saveFrame(vpImage< vpRGBa > &I)
void setFileName(const std::string &filename)
void open(vpImage< vpRGBa > &I)
void display(vpImage< unsigned char > &I, const std::string &title)
Display a gray-scale image.