Visual Servoing Platform  version 3.3.0 under development (2020-02-17)
poseVirtualVS.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 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 http://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  * Pose computation on an object made of dots.
33  * reading of PGM image
34  * Display image using either the X11 or GTK or GDI display
35  * track 4 dots (vpDots) in the image
36  * compute the pose
37  *
38  * Authors:
39  * Eric Marchand
40  * Anthony Saunier
41  *
42  *****************************************************************************/
62 #include <iomanip>
63 #include <sstream>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <visp3/core/vpConfig.h>
67 #include <visp3/core/vpDebug.h>
68 #if (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV))
69 
70 #include <visp3/core/vpImage.h>
71 #include <visp3/core/vpImagePoint.h>
72 #include <visp3/io/vpImageIo.h>
73 
74 #include <visp3/gui/vpDisplayGDI.h>
75 #include <visp3/gui/vpDisplayGTK.h>
76 #include <visp3/gui/vpDisplayOpenCV.h>
77 #include <visp3/gui/vpDisplayX.h>
78 
79 #include <visp3/blob/vpDot.h>
80 #include <visp3/core/vpIoTools.h>
81 #include <visp3/core/vpPixelMeterConversion.h>
82 #include <visp3/io/vpParseArgv.h>
83 #include <visp3/vision/vpPose.h>
84 
85 // List of allowed command line options
86 #define GETOPTARGS "cdi:p:hf:n:s:"
87 
88 void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, unsigned first,
89  unsigned nimages, unsigned step);
90 bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, unsigned &nimages,
91  unsigned &step, bool &click_allowed, bool &display);
92 
106 void usage(const char *name, const char *badparam, std::string ipath, std::string ppath, unsigned first,
107  unsigned nimages, unsigned step)
108 {
109  fprintf(stdout, "\n\
110 Test dot tracking.\n\
111 \n\
112 SYNOPSIS\n\
113  %s [-i <input image path>] [-p <personal image path>]\n\
114  [-f <first image>] [-n <number of images>] [-s <step>][-c] [-d] [-h]\n", name);
115 
116  fprintf(stdout, "\n\
117 OPTIONS: Default\n\
118  -i <input image path> %s\n\
119  Set image input path.\n\
120  From this path read images \n\
121  \"cube/image.%%04d.pgm\"\n\
122  Setting the VISP_INPUT_IMAGE_PATH environment\n\
123  variable produces the same behaviour than using\n\
124  this option.\n\
125  \n\
126  -p <personal image path> %s\n\
127  Specify a personal sequence containing images \n\
128  to process.\n\
129  By image sequence, we mean one file per image.\n\
130  The following image file formats PNM (PGM P5, PPM P6)\n\
131  are supported. The format is selected by analysing \n\
132  the filename extension.\n\
133  Example : \"/Temp/ViSP-images/cube/image.%%04d.pgm\"\n\
134  %%04d is for the image numbering.\n\
135  \n\
136  -f <first image> %u\n\
137  First image number of the sequence.\n\
138  \n\
139  -n <number of images> %u\n\
140  Number of images to load from the sequence.\n\
141  \n\
142  -s <step> %u\n\
143  Step between two images.\n\
144 \n\
145  -c\n\
146  Disable the mouse click. Useful to automaze the \n\
147  execution of this program without humain intervention.\n\
148 \n\
149  -d \n\
150  Turn off the display.\n\
151 \n\
152  -h\n\
153  Print the help.\n", ipath.c_str(), ppath.c_str(), first, nimages, step);
154 
155  if (badparam)
156  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
157 }
178 bool getOptions(int argc, const char **argv, std::string &ipath, std::string &ppath, unsigned &first, unsigned &nimages,
179  unsigned &step, bool &click_allowed, bool &display)
180 {
181  const char *optarg_;
182  int c;
183  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
184 
185  switch (c) {
186  case 'c':
187  click_allowed = false;
188  break;
189  case 'd':
190  display = false;
191  break;
192  case 'i':
193  ipath = optarg_;
194  break;
195  case 'p':
196  ppath = optarg_;
197  break;
198  case 'f':
199  first = (unsigned)atoi(optarg_);
200  break;
201  case 'n':
202  nimages = (unsigned)atoi(optarg_);
203  break;
204  case 's':
205  step = (unsigned)atoi(optarg_);
206  break;
207  case 'h':
208  usage(argv[0], NULL, ipath, ppath, first, nimages, step);
209  return false;
210  break;
211 
212  default:
213  usage(argv[0], optarg_, ipath, ppath, first, nimages, step);
214  return false;
215  break;
216  }
217  }
218 
219  if ((c == 1) || (c == -1)) {
220  // standalone param or error
221  usage(argv[0], NULL, ipath, ppath, first, nimages, step);
222  std::cerr << "ERROR: " << std::endl;
223  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
224  return false;
225  }
226 
227  return true;
228 }
229 
230 int main(int argc, const char **argv)
231 {
232  try {
233  std::string env_ipath;
234  std::string opt_ipath;
235  std::string ipath;
236  std::string opt_ppath;
237  std::string dirname;
238  std::string filename;
239  unsigned opt_first = 0;
240  unsigned opt_nimages = 80;
241  unsigned opt_step = 1;
242  bool opt_click_allowed = true;
243  bool opt_display = true;
244 
245  int i;
246 
247  std::cout << "-------------------------------------------------------" << std::endl;
248  std::cout << " poseVirtualVS.cpp" << std::endl << std::endl;
249 
250  std::cout << " Example of dots tracking in an image sequence and pose "
251  "computation"
252  << std::endl;
253  std::cout << "-------------------------------------------------------" << std::endl;
254  std::cout << std::endl;
255 
256  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
257  // environment variable value
258  env_ipath = vpIoTools::getViSPImagesDataPath();
259 
260  // Set the default input path
261  if (!env_ipath.empty())
262  ipath = env_ipath;
263 
264  // Read the command line options
265  if (getOptions(argc, argv, opt_ipath, opt_ppath, opt_first, opt_nimages, opt_step, opt_click_allowed,
266  opt_display) == false) {
267  exit(-1);
268  }
269 
270  // Get the option values
271  if (!opt_ipath.empty())
272  ipath = opt_ipath;
273 
274  // Compare ipath and env_ipath. If they differ, we take into account
275  // the input path comming from the command line option
276  if (opt_ipath.empty() && opt_ppath.empty()) {
277  if (ipath != env_ipath) {
278  std::cout << std::endl << "WARNING: " << std::endl;
279  std::cout << " Since -i <visp image path=" << ipath << "> "
280  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
281  << " we skip the environment variable." << std::endl;
282  }
283  }
284  // Test if an input path is set
285  if (opt_ipath.empty() && env_ipath.empty() && opt_ppath.empty()) {
286  usage(argv[0], NULL, ipath, opt_ppath, opt_first, opt_nimages, opt_step);
287  std::cerr << std::endl << "ERROR:" << std::endl;
288  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
289  << " environment variable to specify the location of the " << std::endl
290  << " image path where test images are located." << std::endl
291  << " Use -p <personal image path> option if you want to " << std::endl
292  << " use personal images" << std::endl
293  << std::endl;
294  exit(-1);
295  }
296 
297  // Declare an image, this is a gray level image (unsigned char)
298  // it size is not defined yet, it will be defined when the image will
299  // read on the disk
301 
302  unsigned iter = opt_first;
303  std::ostringstream s;
304  char cfilename[FILENAME_MAX];
305 
306  if (opt_ppath.empty()) {
307 
308  // Warning :
309  // the image sequence is not provided with the ViSP package
310  // therefore the program will return you an error :
311  // !! vpImageIoPnm.cpp: readPGM(#210) :couldn't read file
312  // /ViSP-images/cube/image.0001.pgm
313  // !! poseExample.cpp: main(#95) :Error while reading the image
314  // terminate called after throwing an instance of 'vpImageException'
315  //
316  // The sequence is available on the visp www site
317  // https://visp.inria.fr/download/
318  // in the download section. It is named "ViSP-images-x.y.z.tar.gz"
319 
320  // directory name
321  dirname = vpIoTools::createFilePath(ipath, "cube");
322 
323  // Build the name of the image file
324 
325  s.setf(std::ios::right, std::ios::adjustfield);
326  s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm";
327  filename = vpIoTools::createFilePath(dirname, s.str());
328  } else {
329 
330  sprintf(cfilename, opt_ppath.c_str(), iter);
331  filename = cfilename;
332  }
333 
334  // define the vpDot structure, here 4 dots will tracked
335  vpDot d[4];
336 
337  for (i = 0; i < 4; i++) {
338  // by using setGraphics, we request to see the all the pixel of the dot
339  // in green on the screen.
340  // It uses the overlay image plane.
341  // The default of this setting is that it is time consumming
342 
343  if (opt_display) {
344  d[i].setGraphics(true);
345  } else {
346  d[i].setGraphics(false);
347  }
348  }
349 
350  // Read the PGM image named "s" on the disk, and put the bitmap into the
351  // image structure I.
352  // I is initialized to the correct size
353  //
354  // exception readPGM may throw various exception if, for example,
355  // the file does not exist, or if the memory cannot be allocated
356  try {
357  vpImageIo::read(I, filename);
358  } catch (...) {
359  // an exception is throwned if an exception from readPGM has been
360  // catched here this will result in the end of the program Note that
361  // another error message has been printed from readPGM to give more
362  // information about the error
363  if (opt_ppath.empty()) {
364  std::cerr << std::endl << "ERROR:" << std::endl;
365  std::cerr << " Cannot read " << filename << std::endl;
366  std::cerr << " Check your -i " << ipath << " option, " << std::endl
367  << " or VISP_INPUT_IMAGE_PATH environment variable" << std::endl;
368  } else {
369  std::cerr << std::endl << "ERROR:" << std::endl;
370  std::cerr << " Cannot read " << filename << std::endl;
371  std::cerr << " or your -p " << opt_ppath << " option " << std::endl << std::endl;
372  }
373  exit(-1);
374  }
375 
376 // We open a window using either the X11 or GTK or GDI window manager
377 // it will be located in 100,100 and titled "tracking using vpDot"
378 // its size is automatically defined by the image (I) size
379 #if defined VISP_HAVE_X11
380  vpDisplayX display;
381 #elif defined VISP_HAVE_GTK
382  vpDisplayGTK display;
383 #elif defined VISP_HAVE_GDI
384  vpDisplayGDI display;
385 #elif defined VISP_HAVE_OPENCV
386  vpDisplayOpenCV display;
387 #endif
388  if (opt_display) {
389  // Display size is automatically defined by the image (I) size
390  display.init(I, 100, 100, "tracking using vpDot");
391  // display the image
392  // The image class has a member that specify a pointer toward
393  // the display that has been initialized in the display declaration
394  // therefore is is no longuer necessary to make a reference to the
395  // display variable.
397  // Flush the display
398  vpDisplay::flush(I);
399  }
400 
401  vpImagePoint cog[4]; // Center of gravity of the dot
402  if (opt_display && opt_click_allowed) {
403  // dot coordinates (u,v) = (column,row)
404  std::cout << "Click the four white dots on the object corner clockwise" << std::endl;
405  for (i = 0; i < 4; i++) {
406  // tracking is initalized if no other parameters are given
407  // to the iniTracking(..) method a right mouse click on the
408  // dot is expected dot location can also be specified
409  // explicitely in the initTracking method :
410  // d.initTracking(I,ip) where ip is the image point from
411  // where the dot need to be searched.
412 
413  d[i].initTracking(I);
414  // track the dot and returns its coordinates in the image
415  // results are given in float since many many are usually considered
416  //
417  // an expcetion is thrown by the track method if
418  // - dot is lost
419  // - the number of pixel is too small
420  // - too many pixels are detected (this is usual when a "big"
421  // specularity
422  // occurs. The threshold can be modified using the
423  // setMaxDotSize() method
424  d[i].track(I, cog[i]);
425  vpDisplay::flush(I);
426  }
427  } else {
428  cog[0].set_u(194);
429  cog[0].set_v(88);
430  d[0].initTracking(I, cog[0]);
431  d[0].track(I, cog[0]);
432  vpDisplay::flush(I);
433 
434  cog[1].set_u(225);
435  cog[1].set_v(84);
436  d[1].initTracking(I, cog[1]);
437  d[1].track(I, cog[1]);
438  vpDisplay::flush(I);
439 
440  cog[2].set_u(242);
441  cog[2].set_v(114);
442  d[2].initTracking(I, cog[2]);
443  d[2].track(I, cog[2]);
444  vpDisplay::flush(I);
445 
446  cog[3].set_u(212);
447  cog[3].set_v(131);
448  d[3].initTracking(I, cog[3]);
449  d[3].track(I, cog[3]);
450  vpDisplay::flush(I);
451  }
452 
453  if (opt_display) {
454 
455  // display a red cross (size 10) in the image at the dot center
456  // of gravity location
457  //
458  // WARNING
459  // in the vpDisplay class member's when pixel coordinates
460  // are considered the first element is the row index and the second
461  // is the column index:
462  // vpDisplay::displayCross(Image, row index, column index, size,
463  // color) therefore u and v are inverted wrt to the vpDot
464  // specification
465  // Alternatively, to avoid this problem another set of member have
466  // been defined in the vpDisplay class.
467  // If the method name is postfixe with _uv the specification is :
468  // vpDisplay::displayCross_uv(Image, column index, row index, size,
469  // color)
470 
471  for (i = 0; i < 4; i++)
472  vpDisplay::displayCross(I, cog[i], 10, vpColor::red);
473 
474  // flush the X11 buffer
475  vpDisplay::flush(I);
476  }
477 
478  // --------------------------------------------------------
479  // Now wil compute the pose
480  //
481 
482  // The pose will be contained in an homogeneous matrix cMo
484 
485  // We need a structure that content both the 3D coordinates of the point
486  // in the object frame and the 2D coordinates of the point expressed in
487  // meter the vpPoint class is ok for that
488  vpPoint P[4];
489 
490  // The vpPose class mainly contents a list of vpPoint (that is (X,Y,Z, x,
491  // y) )
492  vpPose pose;
493  // the list of point is cleared (if that's not done before)
494  pose.clearPoint();
495 
496  // we set the 3D points coordinates (in meter !) in the object/world frame
497  double L = 0.04;
498  P[0].setWorldCoordinates(-L, -L, 0); // (X,Y,Z)
499  P[1].setWorldCoordinates(L, -L, 0);
500  P[2].setWorldCoordinates(L, L, 0);
501  P[3].setWorldCoordinates(-L, L, 0);
502 
503  // set the camera intrinsic parameters
504  // see more details about the model in vpCameraParameters
505  double px = 600;
506  double py = 600;
507  double u0 = 192;
508  double v0 = 144;
509  vpCameraParameters cam(px, py, u0, v0);
510 
511  // pixel-> meter conversion
512  for (i = 0; i < 4; i++) {
513  // u[i]. v[i] are expressed in pixel
514  // conversion in meter is achieved using
515  // x = (u-u0)/px
516  // y = (v-v0)/py
517  // where px, py, u0, v0 are the intrinsic camera parameters
518  double x = 0, y = 0;
519  vpPixelMeterConversion::convertPoint(cam, cog[i], x, y);
520  P[i].set_x(x);
521  P[i].set_y(y);
522  }
523 
524  // The pose structure is build, we put in the point list the set of point
525  // here both 2D and 3D world coordinates are known
526  for (i = 0; i < 4; i++) {
527  pose.addPoint(P[i]); // and added to the pose computation point list
528  }
529 
530  // compute the initial pose using Dementhon method followed by a non
531  // linear minimisation method
532 
533  // Pose by Lagrange it provides an initialization of the pose
534  pose.computePose(vpPose::LAGRANGE, cMo);
535  // the pose is now refined using the virtual visual servoing approach
536  // Warning: cMo needs to be initialized otherwise it may diverge
537  pose.computePose(vpPose::VIRTUAL_VS, cMo);
538  if (opt_display) {
539  // display the compute pose
540  pose.display(I, cMo, cam, 0.05, vpColor::red);
541  vpDisplay::flush(I);
542  }
543 
544  // Covariance Matrix Computation
545  // Uncomment if you want to compute the covariance matrix.
546  // pose.setCovarianceComputation(true); //Important if you want
547  // tracker.getCovarianceMatrix() to work.
548 
549  unsigned niter = 0;
550  // this is the loop over the image sequence
551  while (iter < opt_nimages) {
552  // set the new image name
553 
554  if (opt_ppath.empty()) {
555  s.str("");
556  s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm";
557  filename = vpIoTools::createFilePath(dirname, s.str());
558  } else {
559  sprintf(cfilename, opt_ppath.c_str(), iter);
560  filename = cfilename;
561  }
562 
563  // read the image
564  vpImageIo::read(I, filename);
565  if (opt_display) {
566  // Display the image
568  // Flush the display
569  vpDisplay::flush(I);
570  }
571  // kill the point list
572  pose.clearPoint();
573 
574  // track the dot
575  for (i = 0; i < 4; i++) {
576  // track the point
577  d[i].track(I, cog[i]);
578  if (opt_display) {
579  // display point location
580  vpDisplay::displayCross(I, cog[i], 10, vpColor::red);
581  }
582  // pixel->meter conversion
583  {
584  double x = 0, y = 0;
585  vpPixelMeterConversion::convertPoint(cam, cog[i], x, y);
586  P[i].set_x(x);
587  P[i].set_y(y);
588  }
589 
590  // and added to the pose computation point list
591  pose.addPoint(P[i]);
592  }
593  // the pose structure has been updated
594 
595  // the pose is now updated using the virtual visual servoing approach
596  // Dementhon or lagrange is no longuer necessary, pose at the
597  // previous iteration is sufficient
598  pose.computePose(vpPose::VIRTUAL_VS, cMo);
599  if (opt_display) {
600  // display the compute pose
601  pose.display(I, cMo, cam, 0.05, vpColor::red);
602 
603  vpDisplay::flush(I);
604  }
605 
606  // Covariance Matrix Display
607  // Uncomment if you want to print the covariance matrix.
608  // Make sure pose.setCovarianceComputation(true) has been called
609  // (uncomment below). std::cout << pose.getCovarianceMatrix() <<
610  // std::endl << std::endl;
611 
612  niter++;
613 
614  iter += opt_step;
615  }
616  return EXIT_SUCCESS;
617  } catch (const vpException &e) {
618  std::cout << "Catch a ViSP exception: " << e << std::endl;
619  return EXIT_FAILURE;
620  }
621 }
622 #else
623 int main()
624 {
625  std::cout << "You do not have X11, or GTK, or GDI (Graphical Device Interface) functionalities to display images..." << std::endl;
626  std::cout << "Tip if you are on a unix-like system:" << std::endl;
627  std::cout << "- Install X11, configure again ViSP using cmake and build again this example" << std::endl;
628  std::cout << "Tip if you are on a windows-like system:" << std::endl;
629  std::cout << "- Install GDI, configure again ViSP using cmake and build again this example" << std::endl;
630  return EXIT_SUCCESS;
631 }
632 #endif
bool computePose(vpPoseMethodType method, vpHomogeneousMatrix &cMo, bool(*func)(const vpHomogeneousMatrix &)=NULL)
Definition: vpPose.cpp:374
void setWorldCoordinates(double oX, double oY, double oZ)
Definition: vpPoint.cpp:113
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1292
Implementation of an homogeneous matrix and operations on such kind of matrices.
void set_u(double u)
Definition: vpImagePoint.h:226
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:128
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition: vpDisplayX.h:150
void track(const vpImage< unsigned char > &I)
Definition: vpDot.cpp:770
error that can be emited by ViSP classes.
Definition: vpException.h:71
void init(vpImage< unsigned char > &I, int winx=-1, int winy=-1, const std::string &title="")
static void convertPoint(const vpCameraParameters &cam, const double &u, const double &v, double &x, double &y)
static void flush(const vpImage< unsigned char > &I)
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
static const vpColor red
Definition: vpColor.h:179
Class that defines what is a point.
Definition: vpPoint.h:58
void set_x(double x)
Set the point x coordinate in the image plane.
Definition: vpPoint.cpp:472
void set_y(double y)
Set the point y coordinate in the image plane.
Definition: vpPoint.cpp:474
static void display(vpImage< unsigned char > &I, vpHomogeneousMatrix &cMo, vpCameraParameters &cam, double size, vpColor col=vpColor::none)
Definition: vpPose.cpp:489
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1537
static void display(const vpImage< unsigned char > &I)
The vpDisplayOpenCV allows to display image using the OpenCV library. Thus to enable this class OpenC...
Class used for pose computation from N points (pose from point only). Some of the algorithms implemen...
Definition: vpPose.h:80
Generic class defining intrinsic camera parameters.
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Definition: vpDisplayGTK.h:137
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
void set_v(double v)
Definition: vpImagePoint.h:237
static void read(vpImage< unsigned char > &I, const std::string &filename)
Definition: vpImageIo.cpp:243
This tracker is meant to track a dot (connected pixels with same gray level) on a vpImage...
Definition: vpDot.h:115
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:88
void setGraphics(bool activate)
Definition: vpDot.h:334
void addPoint(const vpPoint &P)
Definition: vpPose.cpp:149
void initTracking(const vpImage< unsigned char > &I)
Definition: vpDot.cpp:635
void clearPoint()
Definition: vpPose.cpp:134