Visual Servoing Platform  version 3.5.0 under development (2022-02-15)
planarObjectDetector.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  * Detection of planar surface using Fern classifier.
33  *
34  * Authors:
35  * Romain Tallonneau
36  *
37  *****************************************************************************/
50 #include <visp3/core/vpConfig.h>
51 #include <visp3/core/vpDebug.h>
52 
53 #if ((defined(VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)) && \
54  (VISP_HAVE_OPENCV_VERSION >= 0x020000) && (VISP_HAVE_OPENCV_VERSION < 0x030000))
55 
56 #include <iomanip>
57 #include <iostream>
58 #include <stdlib.h>
59 #include <visp3/core/vpConfig.h>
60 #include <visp3/core/vpImage.h>
61 #include <visp3/core/vpIoTools.h>
62 #include <visp3/core/vpTime.h>
63 #include <visp3/gui/vpDisplayGDI.h>
64 #include <visp3/gui/vpDisplayGTK.h>
65 #include <visp3/gui/vpDisplayX.h>
66 #include <visp3/io/vpImageIo.h>
67 #include <visp3/io/vpParseArgv.h>
68 #include <visp3/sensor/vp1394TwoGrabber.h>
69 #include <visp3/sensor/vpV4l2Grabber.h>
70 #include <visp3/vision/vpHomography.h>
71 #include <visp3/vision/vpPlanarObjectDetector.h>
72 
73 #define GETOPTARGS "hlcdb:i:p"
74 
75 void usage(const char *name, const char *badparam);
76 bool getOptions(int argc, const char **argv, bool &isLearning, std::string &dataFile, bool &click_allowed,
77  bool &display, bool &displayPoints, std::string &ipath);
78 
87 void usage(const char *name, const char *badparam)
88 {
89  fprintf(stdout, "\n\
90 Test of detection of planar surface using a Fern classifier. The object needs \
91  first to be learned (-l option). This learning process will create a file used\
92  to detect the object.\n\
93 \n\
94 SYNOPSIS\n\
95  %s [-l] [-h] [-b] [-c] [-d] [-p] [-i] [-s]\n", name);
96 
97  fprintf(stdout, "\n\
98 OPTIONS: \n\
99  -l\n\
100  learn an object.\n\
101 \n\
102  -i <input image path> \n\
103  Set image input path.\n\
104  From this path read \"line/image.%%04d.pgm\"\n\
105  images. \n\
106  Setting the VISP_INPUT_IMAGE_PATH environment\n\
107  variable produces the same behaviour than using\n\
108  this option.\n\
109 \n\
110  -b\n\
111  database filename to use (default is ./dataPlanar).\n\
112 \n\
113  -c\n\
114  Disable the mouse click. Useful to automaze the \n\
115  execution of this program without humain intervention.\n\
116 \n\
117  -d \n\
118  Turn off the display.\n\
119 \n\
120  -s \n\
121  Turn off the use of the sequence and use a webcam.\n\
122 \n\
123  -p \n\
124  display points of interest.\n\
125 \n\
126  -h\n\
127  Print this help.\n");
128 
129  if (badparam)
130  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
131 }
132 
149 bool getOptions(int argc, const char **argv, bool &isLearning, std::string &dataFile, bool &click_allowed,
150  bool &display, bool &displayPoints, std::string &ipath)
151 {
152  const char *optarg_;
153  int c;
154  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
155 
156  switch (c) {
157  case 'c':
158  click_allowed = false;
159  break;
160  case 'd':
161  display = false;
162  break;
163  case 'l':
164  isLearning = true;
165  break;
166  case 'h':
167  usage(argv[0], NULL);
168  return false;
169  break;
170  case 'b':
171  dataFile = optarg_;
172  break;
173  case 'p':
174  displayPoints = true;
175  break;
176  case 'i':
177  ipath = optarg_;
178  break;
179  default:
180  usage(argv[0], optarg_);
181  return false;
182  break;
183  }
184  }
185 
186  if ((c == 1) || (c == -1)) {
187  // standalone param or error
188  usage(argv[0], NULL);
189  std::cerr << "ERROR: " << std::endl;
190  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
191  return false;
192  }
193 
194  return true;
195 }
196 
197 int main(int argc, const char **argv)
198 {
199  try {
200  bool isLearning = false;
201  std::string dataFile("./dataPlanar");
202  bool opt_click_allowed = true;
203  bool opt_display = true;
204  std::string objectName("object");
205  bool displayPoints = false;
206  std::string opt_ipath;
207  std::string ipath;
208  std::string env_ipath;
209  std::string dirname;
210  std::string filename;
211 
212  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
213  // environment variable value
214  env_ipath = vpIoTools::getViSPImagesDataPath();
215 
216  // Set the default input path
217  if (!env_ipath.empty()) {
218  ipath = env_ipath;
219  }
220 
221  // Read the command line options
222  if (getOptions(argc, argv, isLearning, dataFile, opt_click_allowed, opt_display, displayPoints, opt_ipath) ==
223  false) {
224  exit(-1);
225  }
226 
227  // Get the option values
228  if (!opt_ipath.empty()) {
229  ipath = opt_ipath;
230  }
231 
232  // Compare ipath and env_ipath. If they differ, we take into account
233  // the input path comming from the command line option
234  if (!opt_ipath.empty() && !env_ipath.empty()) {
235  if (ipath != env_ipath) {
236  std::cout << std::endl << "WARNING: " << std::endl;
237  std::cout << " Since -i <visp image path=" << ipath << "> "
238  << " is different from VISP_IMAGE_PATH=" << env_ipath << std::endl
239  << " we skip the environment variable." << std::endl;
240  }
241  }
242 
243  // Test if an input path is set
244  if (opt_ipath.empty() && env_ipath.empty()) {
245  usage(argv[0], NULL);
246  std::cerr << std::endl << "ERROR:" << std::endl;
247  std::cerr << " Use -i <visp image path> option or set VISP_INPUT_IMAGE_PATH " << std::endl
248  << " environment variable to specify the location of the " << std::endl
249  << " image path where test images are located." << std::endl
250  << std::endl;
251  exit(-1);
252  }
253 
254  // Declare two images, these are gray level images (unsigned char)
257 
258  // Set the path location of the image sequence
259  dirname = vpIoTools::createFilePath(ipath, "cube");
260 
261  // Build the name of the image file
262  unsigned iter = 0; // Image number
263  std::ostringstream s;
264  s.setf(std::ios::right, std::ios::adjustfield);
265  s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm";
266  filename = vpIoTools::createFilePath(dirname, s.str());
267 
268  // Read the PGM image named "filename" on the disk, and put the
269  // bitmap into the image structure I. I is initialized to the
270  // correct size
271  //
272  // exception readPGM may throw various exception if, for example,
273  // the file does not exist, or if the memory cannot be allocated
274  try {
275  std::cout << "Load: " << filename << std::endl;
276  vpImageIo::read(Iref, filename);
277  I = Iref;
278  } catch (...) {
279  // an exception is throwned if an exception from readPGM has been
280  // catched here this will result in the end of the program Note that
281  // another error message has been printed from readPGM to give more
282  // information about the error
283  std::cerr << std::endl << "ERROR:" << std::endl;
284  std::cerr << " Cannot read " << filename << std::endl;
285  std::cerr << " Check your -i " << ipath << " option " << std::endl
286  << " or VISP_INPUT_IMAGE_PATH environment variable." << std::endl;
287  exit(-1);
288  }
289 
290 #if defined VISP_HAVE_X11
291  vpDisplayX display;
292 #elif defined VISP_HAVE_GTK
293  vpDisplayGTK display;
294 #elif defined VISP_HAVE_GDI
295  vpDisplayGDI display;
296 #endif
297 
298 #if defined VISP_HAVE_X11
299  vpDisplayX displayRef;
300 #elif defined VISP_HAVE_GTK
301  vpDisplayGTK displayRef;
302 #elif defined VISP_HAVE_GDI
303  vpDisplayGDI displayRef;
304 #endif
305 
306  // declare a planar object detector
307  vpPlanarObjectDetector planar;
308 
309  vpImagePoint corners[2];
310  if (isLearning) {
311  if (opt_display) {
312  displayRef.init(Iref, 100, 100, "Reference image");
313  vpDisplay::display(Iref);
314  vpDisplay::flush(Iref);
315  }
316  if (opt_display && opt_click_allowed) {
317  std::cout << "Click on the top left and the bottom right corners to "
318  "define the reference plane"
319  << std::endl;
320  for (int i = 0; i < 2; i++) {
321  vpDisplay::getClick(Iref, corners[i]);
322  std::cout << corners[i] << std::endl;
323  }
324  } else {
325  corners[0].set_ij(50, I.getWidth() - 100); // small ROI for the automated test
326  corners[1].set_ij(I.getHeight() - 100, I.getWidth() - 2);
327  }
328 
329  if (opt_display) {
330  // Display the rectangle which defines the part of the image where the
331  // reference points are computed.
332  vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green);
333  vpDisplay::flush(Iref);
334  }
335 
336  if (opt_click_allowed) {
337  std::cout << "Click on the image to continue" << std::endl;
338  vpDisplay::getClick(Iref);
339  }
340 
341  vpRect roi(corners[0], corners[1]);
342 
343  std::cout << "> train the classifier on the selected plane (may take "
344  "up to several minutes)."
345  << std::endl;
346  if (opt_display) {
347  vpDisplay::display(Iref);
348  vpDisplay::flush(Iref);
349  }
350  double t0 = vpTime::measureTimeMs();
351  planar.buildReference(Iref, roi);
352  std::cout << "build reference in " << vpTime::measureTimeMs() - t0 << " ms" << std::endl;
353  t0 = vpTime::measureTimeMs();
354  planar.recordDetector(objectName, dataFile);
355  std::cout << "record detector in " << vpTime::measureTimeMs() - t0 << " ms" << std::endl;
356  } else {
357  if (!vpIoTools::checkFilename(dataFile)) {
358  vpERROR_TRACE("cannot load the database with the specified name. Has "
359  "the object been learned with the -l option? ");
360  exit(-1);
361  }
362  try {
363  // load a previously recorded file
364  planar.load(dataFile, objectName);
365  } catch (...) {
366  vpERROR_TRACE("cannot load the database with the specified name. Has "
367  "the object been learned with the -l option? ");
368  exit(-1);
369  }
370  }
371 
372  if (opt_display) {
373  display.init(I, 110 + (int)Iref.getWidth(), 100, "Current image");
375  vpDisplay::flush(I);
376  }
377 
378  if (opt_display && opt_click_allowed) {
379  std::cout << "Click on the reference image to continue" << std::endl;
380  vpDisplay::displayText(Iref, vpImagePoint(15, 15), "Click on the reference image to continue", vpColor::red);
381  vpDisplay::flush(Iref);
382  vpDisplay::getClick(Iref);
383  }
384 
385  for (;;) {
386  // acquire a new image
387  iter++;
388  if (iter >= 80) {
389  break;
390  }
391  s.str("");
392  s << "image." << std::setw(4) << std::setfill('0') << iter << ".pgm";
393  filename = vpIoTools::createFilePath(dirname, s.str());
394  // read the image
395  vpImageIo::read(I, filename);
396 
397  if (opt_display) {
399  }
400 
401  double t0 = vpTime::measureTimeMs();
402  // detection of the reference planar surface
403  bool isDetected = planar.matchPoint(I);
404  std::cout << "matching in " << vpTime::measureTimeMs() - t0 << " ms" << std::endl;
405 
406  if (isDetected) {
407  vpHomography H;
408  planar.getHomography(H);
409  std::cout << " > computed homography:" << std::endl << H << std::endl;
410  if (opt_display) {
411  if (isLearning) {
412  vpDisplay::display(Iref);
413  vpDisplay::displayRectangle(Iref, corners[0], corners[1], vpColor::green);
414  planar.display(Iref, I, displayPoints);
415  vpDisplay::flush(Iref);
416  } else {
417  planar.display(I, displayPoints);
418  }
419  }
420  } else {
421  std::cout << " > reference is not detected in the image" << std::endl;
422  }
423  if (opt_display) {
424  vpDisplay::flush(I);
425  if (vpDisplay::getClick(I, false)) {
426  break;
427  }
428  }
429  }
430 
431  return EXIT_SUCCESS;
432  } catch (const vpException &e) {
433  std::cout << "Catch an exception: " << e << std::endl;
434  return EXIT_FAILURE;
435  }
436 }
437 
438 #else
439 int main()
440 {
441 #if (!(defined(VISP_HAVE_X11) || defined(VISP_HAVE_GTK) || defined(VISP_HAVE_GDI)))
442  std::cout << "You do not have X11, or GTK, or GDI (Graphical Device Interface) functionalities to display images..." << std::endl;
443  std::cout << "Tip if you are on a unix-like system:" << std::endl;
444  std::cout << "- Install X11, configure again ViSP using cmake and build again this example" << std::endl;
445  std::cout << "Tip if you are on a windows-like system:" << std::endl;
446  std::cout << "- Install GDI, configure again ViSP using cmake and build again this example" << std::endl;
447 #else
448  std::cout << "You do not have OpenCV functionalities" << std::endl;
449  std::cout << "Tip:" << std::endl;
450  std::cout << "- Install OpenCV, configure again ViSP using cmake and build again this example" << std::endl;
451 #endif
452  return EXIT_SUCCESS;
453 }
454 
455 #endif
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:149
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1365
#define vpERROR_TRACE
Definition: vpDebug.h:393
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:128
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition: vpDisplayX.h:134
error that can be emited by ViSP classes.
Definition: vpException.h:71
static const vpColor green
Definition: vpColor.h:220
static void flush(const vpImage< unsigned char > &I)
VISP_EXPORT double measureTimeMs()
Definition: vpTime.cpp:126
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:69
static const vpColor red
Definition: vpColor.h:217
Implementation of an homography and operations on homographies.
Definition: vpHomography.h:174
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1670
static void display(const vpImage< unsigned char > &I)
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Definition: vpDisplayGTK.h:134
static void displayRectangle(const vpImage< unsigned char > &I, const vpImagePoint &topLeft, unsigned int width, unsigned int height, const vpColor &color, bool fill=false, unsigned int thickness=1)
void set_ij(double ii, double jj)
Definition: vpImagePoint.h:188
unsigned int getHeight() const
Definition: vpImage.h:188
static bool checkFilename(const std::string &filename)
Definition: vpIoTools.cpp:802
void init(vpImage< unsigned char > &I, int winx=-1, int winy=-1, const std::string &title="")
Defines a rectangle in the plane.
Definition: vpRect.h:79
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:87
unsigned int getWidth() const
Definition: vpImage.h:246