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