Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
trackMeEllipse.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 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 * Tracking of an ellipse.
32 */
33
40
41#include <visp3/core/vpConfig.h>
42
43#if defined(VISP_HAVE_MODULE_ME) && defined(VISP_HAVE_DISPLAY)
44
45#include <visp3/core/vpColor.h>
46#include <visp3/core/vpImage.h>
47#include <visp3/core/vpImagePoint.h>
48#include <visp3/core/vpIoTools.h>
49#include <visp3/core/vpTime.h>
50#include <visp3/gui/vpDisplayFactory.h>
51#include <visp3/io/vpParseArgv.h>
52#include <visp3/io/vpVideoReader.h>
53#include <visp3/io/vpVideoWriter.h>
54#include <visp3/me/vpMeEllipse.h>
55
56// List of allowed command line options
57#define GETOPTARGS "Aabcdf:hi:l:p:r:s:S:t:T:vw:y"
58
59#ifdef ENABLE_VISP_NAMESPACE
60using namespace VISP_NAMESPACE_NAME;
61#endif
62
63
64void usage(const char *name, const char *badparam, const std::string &video_in_ipath, const std::string &video_in_ppath,
65 unsigned video_in_first, int video_in_last, int video_in_step, int me_range, int me_sample_step,
66 int me_threshold, unsigned int &sleep_ms);
67bool getOptions(int argc, const char **argv, std::string &video_in_ipath, std::string &video_in_ppath,
68 int &video_in_first, int &video_in_last, int &video_in_step,
69 bool &click_allowed, bool &display, bool &display_scale_auto, bool &track_circle, bool &track_arc,
70 std::string &video_out_save, int &me_range, int &me_sample_step, int &me_threshold, bool &step_by_step,
71 unsigned int &sleep_ms, bool &verbose);
72
88void 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,
90 int me_threshold, unsigned int &sleep_ms)
91{
92#if defined(VISP_HAVE_DATASET)
93#if VISP_HAVE_DATASET_VERSION >= 0x030600
94 std::string ext("png");
95#else
96 std::string ext("pgm");
97#endif
98#else
99 // We suppose that the user will download a recent dataset
100 std::string ext("png");
101#endif
102 fprintf(stdout, "\n\
103Example of ellipse/circle or arc of ellipse/circle tracking using vpMeEllipse.\n\
104\n\
105SYNOPSIS\n\
106 %s [-i <visp dataset directory>] [-p <personal image path>]\n\
107 [-f <video first image>] [-l <video last image>] [-s <video step>]\n\
108 [-r <moving-edge range] [-t <moving-edge threshold] [-S <moving-edge sample step>]\n\
109 [-w <output images sequence name>] [-T <sleep ms>]\n\
110 [-c] [-d] [-a] [-A] [-b] [-y] [-v] [-h]\n", name);
111
112 fprintf(stdout, "\n\
113OPTIONS: Default\n\
114 -i <visp dataset directory> %s\n\
115 Set visp dataset directory location.\n\
116 From this directory read images \"ellipse-1/image.%%04d.%s\"\n\
117 images.\n\
118 Setting the VISP_INPUT_IMAGE_PATH environment variable\n\
119 produces the same behaviour than using this option.\n\
120 \n\
121 -p <personal image path> %s\n\
122 Specify a personal sequence containing images \n\
123 to process.\n\
124 The format is selected by analyzing \n\
125 the filename extension.\n\
126 Example : \"C:/Temp/ViSP-images/ellipse-1/image.%%04d.%s\"\n\
127 %%04d is for the image numbering.\n\
128 \n\
129 -f <video first image> %d\n\
130 First image number to process.\n\
131 Set -1 to process the first image of the sequence.\n\
132 \n\
133 -l <video last image> %d\n\
134 Last image number to process. \n\
135 Set -1 to process images until the last image of the \n\
136 sequence.\n\
137 \n\
138 -s <video step> %d\n\
139 Step between two images.\n\
140 \n\
141 -r <moving-edge range> %d\n\
142 Moving-edge range.\n\
143 Increase value to consider large displacement. \n\
144 When set to -1, use default value. \n\
145 \n\
146 -S <moving-edge sample step> %d\n\
147 Moving-edge sample step.\n\
148 Distance between two moving-edges samples in degrees. \n\
149 When set to -1, use default value. \n\
150 \n\
151 -t <moving-edge threshold> %d\n\
152 Moving-edge threshold corresponding to the minimum \n\
153 contrast to consider. Value in range [0 ; 255] \n\
154 When set to -1, use default value. \n\
155 \n\
156 -c\n\
157 Disable the mouse click. Useful to automate the \n\
158 execution of this program without human intervention.\n\
159 \n\
160 -d \n\
161 Turn off the display.\n\
162 \n\
163 -y \n\
164 Enable step-by-step mode waiting for a mouse click to\n\
165 process next image.\n\
166 \n\
167 -a \n\
168 Enable arc of ellipse tracking.\n\
169 \n\
170 -b \n\
171 Enable circle tracking.\n\
172 \n\
173 -T %d \n\
174 Sleep time in ms before processing next image.\n\
175 Allows to slow down the image processing. \n\
176 \n\
177 -w <output images sequence name> \n\
178 Save images with tracking results in overlay.\n\
179 Example: \"result/I%%04d.png\" \n\
180 \n\
181 -A \n\
182 When display is activated using -d option, enable\n\
183 windows auto scaling to fit the screen size. \n\
184 \n\
185 -v\n\
186 Enable verbosity.\n\
187 \n\
188 -h\n\
189 Print the help.\n",
190 video_in_ipath.c_str(), ext.c_str(), video_in_ppath.c_str(), ext.c_str(), video_in_first, video_in_last,
191 video_in_step, me_range, me_sample_step, me_threshold, sleep_ms);
192
193 if (badparam) {
194 fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
195 }
196}
197
223bool getOptions(int argc, const char **argv, std::string &video_in_ipath, std::string &video_in_ppath,
224 int &video_in_first, int &video_in_last, int &video_in_step,
225 bool &click_allowed, bool &display, bool &display_scale_auto, bool &track_circle, bool &track_arc,
226 std::string &video_out_save, int &me_range, int &me_sample_step, int &me_threshold, bool &step_by_step,
227 unsigned int &sleep_ms, bool &verbose)
228{
229 const char *optarg_;
230 int c;
231 while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
232
233 switch (c) {
234 case 'A':
235 display_scale_auto = true;
236 break;
237 case 'a':
238 track_arc = true;
239 break;
240 case 'b':
241 track_circle = true;
242 break;
243 case 'c':
244 click_allowed = false;
245 break;
246 case 'd':
247 display = false;
248 break;
249 case 'f':
250 video_in_first = atoi(optarg_);
251 break;
252 case 'i':
253 video_in_ipath = std::string(optarg_);
254 break;
255 case 'l':
256 video_in_last = atoi(optarg_);
257 break;
258 case 'p':
259 video_in_ppath = std::string(optarg_);
260 break;
261 case 'r':
262 me_range = atoi(optarg_);
263 break;
264 case 's':
265 video_in_step = atoi(optarg_);
266 break;
267 case 'S':
268 me_sample_step = atoi(optarg_);
269 break;
270 case 't':
271 me_threshold = atoi(optarg_);
272 break;
273 case 'T':
274 sleep_ms = atoi(optarg_);
275 break;
276 case 'w':
277 video_out_save = std::string(optarg_);
278 break;
279 case 'v':
280 verbose = true;
281 break;
282 case 'y':
283 step_by_step = true;
284 break;
285 case 'h':
286 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, sleep_ms);
287 return false;
288
289 default:
290 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, sleep_ms);
291 return false;
292 }
293 }
294
295 if ((c == 1) || (c == -1)) {
296 // standalone param or error
297 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, sleep_ms);
298 std::cerr << "ERROR: " << std::endl;
299 std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
300 return false;
301 }
302
303 return true;
304}
305
306int main(int argc, const char **argv)
307{
308#if defined(VISP_HAVE_LAPACK) || defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_OPENCV)
309 std::string env_ipath;
310 std::string opt_ipath;
311 std::string ipath;
312 std::string opt_ppath;
313 std::string videoname;
314 int opt_first = -1;
315 int opt_last = -1;
316 int opt_step = 1;
317 int opt_me_range = 30;
318 int opt_me_sample_step = 5;
319 int opt_me_threshold = 20; // Value in [0 ; 255]
320 bool opt_click_allowed = true;
321 bool opt_display = true;
322 bool opt_display_scale_auto = false;
323 bool opt_track_circle = false;
324 bool opt_track_arc = false;
325 bool opt_verbose = false;
326 std::string opt_save;
327 bool opt_step_by_step = false;
328 unsigned int opt_sleep_ms = 0;
329 unsigned int thickness = 1;
330
331 // Declare an image, this is a gray level image (unsigned char)
332 // it size is not defined yet, it will be defined when the image is
333 // read on the disk
335 vpDisplay *display = nullptr;
337
338#if defined(VISP_HAVE_DATASET)
339#if VISP_HAVE_DATASET_VERSION >= 0x030600
340 std::string ext("png");
341#else
342 std::string ext("pgm");
343#endif
344#else
345 // We suppose that the user will download a recent dataset
346 std::string ext("png");
347#endif
348
349 try {
350 // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
351 // environment variable value
353
354 // Set the default input path
355 if (!env_ipath.empty())
356 ipath = env_ipath;
357
358 // Read the command line options
359 if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_first, opt_last, opt_step, opt_click_allowed,
360 opt_display, opt_display_scale_auto, opt_track_circle, opt_track_arc, opt_save,
361 opt_me_range, opt_me_sample_step, opt_me_threshold, opt_step_by_step, opt_sleep_ms, opt_verbose) == false) {
362 return EXIT_FAILURE;
363 }
364
365 // Get the option values
366 if (!opt_ipath.empty()) {
367 ipath = opt_ipath;
368 }
369
370 // Compare ipath and env_ipath. If they differ, we take into account
371 // the input path coming from the command line option
372 if (!opt_ipath.empty() && !env_ipath.empty() && opt_ppath.empty()) {
373 if (ipath != env_ipath) {
374 std::cout << std::endl << "WARNING: " << std::endl;
375 std::cout << " Since -i <visp image path=" << ipath << "> "
376 << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
377 << " we skip the environment variable." << std::endl;
378 }
379 }
380
381 // Test if an input path is set
382 if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()) {
383 usage(argv[0], nullptr, ipath, opt_ppath, opt_first, opt_last, opt_step, opt_me_range, opt_me_sample_step, opt_me_threshold, opt_sleep_ms);
384 std::cerr << std::endl << "ERROR:" << std::endl;
385 std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
386 << " environment variable to specify the location of the " << std::endl
387 << " image path where test images are located." << std::endl
388 << " Use -p <personal image path> option if you want to " << std::endl
389 << " use personal images." << std::endl
390 << std::endl;
391
392 return EXIT_FAILURE;
393 }
394
395 // Create output folder if needed
396 if (!opt_save.empty()) {
397 std::string parent = vpIoTools::getParent(opt_save);
398 if (!parent.empty()) {
399 std::cout << "Create output directory: " << parent << std::endl;
401 }
402 thickness += 1;
403 }
404
405 if (opt_ppath.empty()) {
406 // Set the path location of the image sequence
407 videoname = vpIoTools::createFilePath(ipath, "ellipse-1/image.%04d." + ext);
408 g.setFileName(videoname);
409 }
410 else {
411 g.setFileName(opt_ppath);
412 }
413
414 if (opt_first > 0) {
415 g.setFirstFrameIndex(opt_first);
416 }
417 if (opt_last > 0) {
418 g.setLastFrameIndex(opt_last);
419 }
420 g.setFrameStep(opt_step);
421 g.open(I);
422
423 if (opt_display) {
424 // We open a window using either X11, GTK, GDI or OpenCV
426 if (opt_display_scale_auto) {
427 display->setDownScalingFactor(vpDisplay::SCALE_AUTO);
428 }
429 std::stringstream ss;
430 if (g.isVideoFormat()) {
431 ss << "Init tracker image " << g.getFrameIndex();
432 }
433 else {
434 std::string image_name = vpIoTools::getName(vpIoTools::formatString(g.getFrameName(), g.getFrameIndex()));
435 ss << "Init tracker image " << image_name;
436 }
437 // Display size is automatically defined by the image (I) size
438 display->init(I, 10, 10, ss.str());
439 // Display the image
440 // The image class has a member that specify a pointer toward
441 // the display that has been initialized in the display declaration
442 // therefore is is no longer necessary to make a reference to the
443 // display variable.
446 }
447
448 vpVideoWriter *writer = nullptr;
450 if (!opt_save.empty()) {
451 writer = new vpVideoWriter();
452 writer->setFileName(opt_save);
453 writer->open(O);
454 }
455 vpMeEllipse me_ellipse;
456
457 vpMe me;
458 if (opt_me_range > 0) {
459 me.setRange(opt_me_range);
460 }
461 if (opt_me_sample_step > 0) {
462 me.setSampleStep(opt_me_sample_step);
463 }
464 if (opt_me_threshold > 0) {
466 me.setThreshold(opt_me_threshold);
467 }
468
469 me_ellipse.setMe(&me);
471
472 std::cout << "Video settings" << std::endl;
473 std::cout << " Name : " << g.getFrameName() << std::endl;
474 std::cout << " First image: " << g.getFirstFrameIndex() << std::endl;
475 std::cout << " Last image : " << g.getLastFrameIndex() << std::endl;
476 std::cout << " Step : " << g.getFrameStep() << std::endl;
477 std::cout << " Image size : " << I.getWidth() << " x " << I.getHeight() << std::endl;
478
479 std::cout << "Moving-edges settings" << std::endl;
480 std::cout << " Sample step : " << me_ellipse.getMe()->getSampleStep() << std::endl;
481 std::cout << " Range : " << me_ellipse.getMe()->getRange() << std::endl;
482 std::cout << " Threshold type: " << (me_ellipse.getMe()->getLikelihoodThresholdType() == vpMe::NORMALIZED_THRESHOLD ? "normalized" : "old threshold (to be avoided)") << std::endl;
483 std::cout << " Threshold : " << me_ellipse.getMe()->getThreshold() << std::endl;
484
485 if (!opt_save.empty()) {
486 std::cout << "Create video with tracking results" << std::endl;
487 std::cout << " Name : " << opt_save << std::endl;
488 }
489
490 if (opt_click_allowed) {
491 me_ellipse.initTracking(I, opt_track_circle, opt_track_arc);
492 }
493 else {
494 // Create a list of clockwise points to automate the test
495 std::vector<vpImagePoint> ip;
496 ip.push_back(vpImagePoint(195, 329));
497 ip.push_back(vpImagePoint(243, 164));
498 ip.push_back(vpImagePoint(201, 36));
499 ip.push_back(vpImagePoint(83, 126));
500 ip.push_back(vpImagePoint(33, 276));
501
502 me_ellipse.initTracking(I, ip, opt_track_circle, opt_track_arc);
503 }
504 if (opt_display) {
505 me_ellipse.display(I, vpColor::green, thickness);
507 }
508
509 if (opt_display && opt_click_allowed) {
510 std::cout << "A click to continue..." << std::endl;
512 }
513 bool quit = false;
514
515 while (!g.end() && !quit) {
516 // Read the image
517 g.acquire(I);
518 std::stringstream ss;
519 if (g.isVideoFormat()) {
520 ss << "Image " << g.getFrameIndex();
521 }
522 else {
523 std::string image_name = vpIoTools::getName(g.getFrameName());
524 ss << "Image " << image_name;
525 }
526
527 if (opt_verbose) {
528 std::cout << "-- " << ss.str() << std::endl;
529 }
530 if (opt_display) {
531 // Display the image
533 vpDisplay::setTitle(I, ss.str());
534 if (opt_click_allowed) {
535 vpDisplay::displayText(I, 20, I.getWidth() - 150, std::string("Mode: ") + (opt_step_by_step ? std::string("step-by-step") : std::string("continuous")), vpColor::red);
536 vpDisplay::displayText(I, 40, I.getWidth() - 150, ss.str(), vpColor::red);
537 vpDisplay::displayText(I, 20, 10, "Right click to exit", vpColor::red);
538 vpDisplay::displayText(I, 40, 10, "Middle click to change mode", vpColor::red);
539 if (opt_step_by_step) {
540 vpDisplay::displayText(I, 60, 10, "Left click to process next image", vpColor::red);
541 }
542 }
543 }
544 me_ellipse.track(I);
545
546 if (opt_display) {
547 me_ellipse.display(I, vpColor::green, thickness);
549 if (opt_click_allowed) {
551 if (vpDisplay::getClick(I, button, opt_step_by_step)) {
552 if (button == vpMouseButton::button3) {
553 quit = true;
554 }
555 else if (button == vpMouseButton::button2) {
556 if (opt_step_by_step) {
557 opt_step_by_step = false;
558 }
559 else {
560 opt_step_by_step = true;
561 }
562 }
563 }
564 }
565 }
566 if (!opt_save.empty()) {
568 writer->saveFrame(O);
569 }
570
571 if (opt_sleep_ms) {
572 vpTime::sleepMs(opt_sleep_ms);
573 }
574 }
575
576 if (opt_display && opt_click_allowed && !quit) {
578 }
579
580 if (writer) {
581 delete writer;
582 }
583 if (display) {
584 delete display;
585 }
586 return EXIT_SUCCESS;
587 }
588 catch (const vpException &e) {
589 std::cout << "Catch an exception: " << e << std::endl;
590 if (opt_display && opt_click_allowed) {
592 }
593 return EXIT_FAILURE;
594 }
595#else
596 (void)argc;
597 (void)argv;
598 std::cout << "Cannot run this example: install Lapack, Eigen3 or OpenCV" << std::endl;
599#endif
600}
601#else
602#include <iostream>
603
604int main()
605{
606 std::cout << "visp_me module or X11, GTK, GDI or OpenCV display "
607 "functionalities are required..."
608 << std::endl;
609 return EXIT_SUCCESS;
610}
611
612#endif
static const vpColor red
Definition vpColor.h:198
static const vpColor green
Definition vpColor.h:201
Class that defines generic functionalities for display.
Definition vpDisplay.h:171
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)
static void setTitle(const vpImage< unsigned char > &I, const std::string &windowtitle)
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)
error that can be emitted by ViSP classes.
Definition vpException.h:60
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition of the vpImage class member functions.
Definition vpImage.h:131
static std::string getViSPImagesDataPath()
static std::string formatString(const std::string &name, unsigned int val)
static std::string createFilePath(const std::string &parent, const std::string &child)
static void makeDirectory(const std::string &dirname)
static std::string getParent(const std::string &pathname)
static std::string getName(const std::string &pathname)
Class that tracks an ellipse or a circle using moving edges.
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:85
void setDisplay(vpMeSite::vpMeSiteDisplayType select)
void setMe(vpMe *me)
vpMe * getMe()
Definition vpMe.h:143
void setRange(const unsigned int &range)
Definition vpMe.h:438
void setLikelihoodThresholdType(const vpLikelihoodThresholdType likelihood_threshold_type)
Definition vpMe.h:531
void setThreshold(const double &threshold)
Definition vpMe.h:489
void setSampleStep(const double &sample_step)
Definition vpMe.h:445
double getThreshold() const
Definition vpMe.h:306
double getSampleStep() const
Definition vpMe.h:290
unsigned int getRange() const
Definition vpMe.h:283
@ NORMALIZED_THRESHOLD
Definition vpMe.h:154
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
bool isVideoFormat() const
void setLastFrameIndex(const long last_frame)
long getLastFrameIndex()
void open(vpImage< vpRGBa > &I) VP_OVERRIDE
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
void acquire(vpImage< vpRGBa > &I) VP_OVERRIDE
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)
vpDisplay * allocateDisplay()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.
VISP_EXPORT void sleepMs(double t)