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