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