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