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