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