Visual Servoing Platform  version 3.6.1 under development (2025-02-18)
testKeyPoint-3.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Test keypoint matching with mostly OpenCV functions calls
32  * to detect potential memory leaks in testKeyPoint.cpp.
33  */
34 
42 #include <iostream>
43 
44 #include <visp3/core/vpConfig.h>
45 
46 #if defined(HAVE_OPENCV_IMGPROC) && (defined(HAVE_OPENCV_FEATURES2D) || defined(HAVE_OPENCV_FEATURES))
47 
48 #if defined(HAVE_OPENCV_FEATURES)
49 #include <opencv2/features.hpp>
50 #endif
51 
52 #if defined(HAVE_OPENCV_FEATURES2D)
53 #include <opencv2/features2d/features2d.hpp>
54 #endif
55 
56 #include <visp3/core/vpImage.h>
57 #include <visp3/core/vpIoTools.h>
58 #include <visp3/gui/vpDisplayFactory.h>
59 #include <visp3/io/vpImageIo.h>
60 #include <visp3/io/vpParseArgv.h>
61 #include <visp3/io/vpVideoReader.h>
62 
63 // List of allowed command line options
64 #define GETOPTARGS "cdh"
65 
66 #ifdef ENABLE_VISP_NAMESPACE
67 using namespace VISP_NAMESPACE_NAME;
68 #endif
69 
70 void usage(const char *name, const char *badparam);
71 bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display);
72 
80 void usage(const char *name, const char *badparam)
81 {
82  fprintf(stdout, "\n\
83 Test keypoints matching.\n\
84 \n\
85 SYNOPSIS\n\
86  %s [-c] [-d] [-h]\n",
87  name);
88 
89  fprintf(stdout, "\n\
90 OPTIONS: \n\
91 \n\
92  -c\n\
93  Disable the mouse click. Useful to automate the \n\
94  execution of this program without human intervention.\n\
95 \n\
96  -d \n\
97  Turn off the display.\n\
98 \n\
99  -h\n\
100  Print the help.\n");
101 
102  if (badparam)
103  fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
104 }
105 
117 bool getOptions(int argc, const char **argv, bool &click_allowed, bool &display)
118 {
119  const char *optarg_;
120  int c;
121  while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
122 
123  switch (c) {
124  case 'c':
125  click_allowed = false;
126  break;
127  case 'd':
128  display = false;
129  break;
130  case 'h':
131  usage(argv[0], nullptr);
132  return false;
133  break;
134 
135  default:
136  usage(argv[0], optarg_);
137  return false;
138  break;
139  }
140  }
141 
142  if ((c == 1) || (c == -1)) {
143  // standalone param or error
144  usage(argv[0], nullptr);
145  std::cerr << "ERROR: " << std::endl;
146  std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
147  return false;
148  }
149 
150  return true;
151 }
152 
153 template <typename Type>
154 void run_test(const std::string &env_ipath, bool opt_click_allowed, bool opt_display, vpImage<Type> &Iref,
155  vpImage<Type> &Icur, vpImage<Type> &Imatch)
156 {
157 #if VISP_HAVE_DATASET_VERSION >= 0x030600
158  std::string ext("png");
159 #else
160  std::string ext("pgm");
161 #endif
162  // Set the path location of the image sequence
163  std::string dirname = vpIoTools::createFilePath(env_ipath, "mbt/cube");
164 
165  // Build the name of the image files
166  std::string filenameRef = vpIoTools::createFilePath(dirname, "image0000." + ext);
167  vpImageIo::read(Iref, filenameRef);
168  std::string filenameCur = vpIoTools::createFilePath(dirname, "image%04d." + ext);
169 
170  // Init keypoints
171  cv::Ptr<cv::FeatureDetector> detector;
172  cv::Ptr<cv::DescriptorExtractor> extractor;
173  cv::Ptr<cv::DescriptorMatcher> matcher;
174 
175 #if (VISP_HAVE_OPENCV_VERSION >= 0x030000)
176  detector = cv::ORB::create();
177  extractor = cv::ORB::create();
178 #else
179  detector = cv::FeatureDetector::create("ORB");
180  extractor = cv::DescriptorExtractor::create("ORB");
181 #endif
182  matcher = cv::DescriptorMatcher::create("BruteForce-Hamming");
183 
184  std::vector<cv::KeyPoint> trainKeyPoints;
185  cv::Mat matImg, trainDescriptors;
186  vpImageConvert::convert(Iref, matImg);
187  detector->detect(matImg, trainKeyPoints);
188  extractor->compute(matImg, trainKeyPoints, trainDescriptors);
189 
190  vpVideoReader g;
191  g.setFileName(filenameCur);
192  g.open(Icur);
193  g.acquire(Icur);
194 
195  Imatch.resize(Icur.getHeight(), 2 * Icur.getWidth());
196  Imatch.insert(Iref, vpImagePoint(0, 0));
197 
198  vpDisplay *display = nullptr;
199 
200  if (opt_display) {
201 #ifdef VISP_HAVE_DISPLAY
202  display = vpDisplayFactory::allocateDisplay(Imatch, 0, 0, "ORB keypoints matching");
203  display->setDownScalingFactor(vpDisplay::SCALE_AUTO);
204 #else
205  std::cout << "No image viewer is available..." << std::endl;
206 #endif
207  }
208 
209  bool opt_click = false;
211  while (!g.end()) {
212  g.acquire(Icur);
213  Imatch.insert(Icur, vpImagePoint(0, Icur.getWidth()));
214 
215  if (opt_display) {
216  vpDisplay::display(Imatch);
217  }
218 
219  vpImageConvert::convert(Icur, matImg);
220  std::vector<cv::KeyPoint> queryKeyPoints;
221  detector->detect(matImg, queryKeyPoints);
222 
223  cv::Mat queryDescriptors;
224  extractor->compute(matImg, queryKeyPoints, queryDescriptors);
225 
226  std::vector<std::vector<cv::DMatch> > knn_matches;
227  std::vector<cv::DMatch> matches;
228  matcher->knnMatch(queryDescriptors, trainDescriptors, knn_matches, 2);
229  for (std::vector<std::vector<cv::DMatch> >::const_iterator it = knn_matches.begin(); it != knn_matches.end();
230  ++it) {
231  if (it->size() > 1) {
232  double ratio = (*it)[0].distance / (*it)[1].distance;
233  if (ratio < 0.85) {
234  matches.push_back((*it)[0]);
235  }
236  }
237  }
238 
239  if (opt_display) {
240  for (std::vector<cv::DMatch>::const_iterator it = matches.begin(); it != matches.end(); ++it) {
241  vpImagePoint leftPt(trainKeyPoints[(size_t)it->trainIdx].pt.y, trainKeyPoints[(size_t)it->trainIdx].pt.x);
242  vpImagePoint rightPt(queryKeyPoints[(size_t)it->queryIdx].pt.y,
243  queryKeyPoints[(size_t)it->queryIdx].pt.x + Iref.getWidth());
244  vpDisplay::displayLine(Imatch, leftPt, rightPt, vpColor::green);
245  }
246 
247  vpDisplay::flush(Imatch);
248  }
249 
250  // Click requested to process next image
251  if (opt_click_allowed && opt_display) {
252  if (opt_click) {
253  vpDisplay::getClick(Imatch, button, true);
254  if (button == vpMouseButton::button3) {
255  opt_click = false;
256  }
257  }
258  else {
259  // Use right click to enable/disable step by step tracking
260  if (vpDisplay::getClick(Imatch, button, false)) {
261  if (button == vpMouseButton::button3) {
262  opt_click = true;
263  }
264  else if (button == vpMouseButton::button1) {
265  break;
266  }
267  }
268  }
269  }
270  }
271 
272  if (display) {
273  delete display;
274  }
275 }
276 
277 int main(int argc, const char **argv)
278 {
279  try {
280  std::string env_ipath;
281  bool opt_click_allowed = true;
282  bool opt_display = true;
283 
284  // Read the command line options
285  if (getOptions(argc, argv, opt_click_allowed, opt_display) == false) {
286  return EXIT_FAILURE;
287  }
288 
289  // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH
290  // environment variable value
291  env_ipath = vpIoTools::getViSPImagesDataPath();
292 
293  if (env_ipath.empty()) {
294  std::cerr << "Please set the VISP_INPUT_IMAGE_PATH environment "
295  "variable value."
296  << std::endl;
297  return EXIT_FAILURE;
298  }
299 
300  {
301  vpImage<unsigned char> Iref, Icur, Imatch;
302 
303  std::cout << "-- Test on gray level images" << std::endl;
304  run_test(env_ipath, opt_click_allowed, opt_display, Iref, Icur, Imatch);
305  }
306 
307  {
308  vpImage<vpRGBa> Iref, Icur, Imatch;
309 
310  std::cout << "-- Test on color images" << std::endl;
311  run_test(env_ipath, opt_click_allowed, opt_display, Iref, Icur, Imatch);
312  }
313 
314  }
315  catch (const vpException &e) {
316  std::cerr << e.what() << std::endl;
317  return EXIT_FAILURE;
318  }
319 
320  std::cout << "testKeyPoint-3 is ok !" << std::endl;
321  return EXIT_SUCCESS;
322 }
323 
324 #else
325 int main()
326 {
327  std::cerr << "You need OpenCV library." << std::endl;
328 
329  return EXIT_SUCCESS;
330 }
331 
332 #endif
static const vpColor green
Definition: vpColor.h:201
Class that defines generic functionalities for display.
Definition: vpDisplay.h:178
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)
static void flush(const vpImage< unsigned char > &I)
@ SCALE_AUTO
Definition: vpDisplay.h:184
error that can be emitted by ViSP classes.
Definition: vpException.h:60
const char * what() const
Definition: vpException.cpp:71
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:147
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
Definition of the vpImage class member functions.
Definition: vpImage.h:131
unsigned int getWidth() const
Definition: vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:544
void insert(const vpImage< Type > &src, const vpImagePoint &topLeft)
Definition: vpImage.h:639
unsigned int getHeight() const
Definition: vpImage.h:181
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1053
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1427
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
Definition: vpParseArgv.cpp:70
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
void acquire(vpImage< vpRGBa > &I)
void open(vpImage< vpRGBa > &I)
void setFileName(const std::string &filename)
vpDisplay * allocateDisplay()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.