Visual Servoing Platform  version 3.6.1 under development (2024-05-18)
perfGenericTracker.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Benchmark generic tracker.
33  *
34 *****************************************************************************/
35 
36 #include <visp3/core/vpConfig.h>
37 
38 #if defined(VISP_HAVE_CATCH2)
39 #define CATCH_CONFIG_ENABLE_BENCHMARKING
40 #define CATCH_CONFIG_RUNNER
41 #include <catch.hpp>
42 
43 #include <visp3/core/vpIoTools.h>
44 #include <visp3/io/vpImageIo.h>
45 #include <visp3/mbt/vpMbGenericTracker.h>
46 
47 // #define DEBUG_DISPLAY // uncomment to check that the tracking is correct
48 #ifdef DEBUG_DISPLAY
49 #include <visp3/gui/vpDisplayX.h>
50 #endif
51 
52 namespace
53 {
54 bool runBenchmark = false;
55 
56 template <typename Type>
57 bool read_data(const std::string &input_directory, int cpt, const vpCameraParameters &cam_depth, vpImage<Type> &I,
58  vpImage<uint16_t> &I_depth, std::vector<vpColVector> &pointcloud, vpHomogeneousMatrix &cMo)
59 {
60  static_assert(std::is_same<Type, unsigned char>::value || std::is_same<Type, vpRGBa>::value,
61  "Template function supports only unsigned char and vpRGBa images!");
62 #if VISP_HAVE_DATASET_VERSION >= 0x030600
63  std::string ext("png");
64 #else
65  std::string ext("pgm");
66 #endif
67  char buffer[FILENAME_MAX];
68  snprintf(buffer, FILENAME_MAX, std::string(input_directory + "/Images/Image_%04d." + ext).c_str(), cpt);
69  std::string image_filename = buffer;
70 
71  snprintf(buffer, FILENAME_MAX, std::string(input_directory + "/Depth/Depth_%04d.bin").c_str(), cpt);
72  std::string depth_filename = buffer;
73 
74  snprintf(buffer, FILENAME_MAX, std::string(input_directory + "/CameraPose/Camera_%03d.txt").c_str(), cpt);
75  std::string pose_filename = buffer;
76 
77  if (!vpIoTools::checkFilename(image_filename) || !vpIoTools::checkFilename(depth_filename) ||
78  !vpIoTools::checkFilename(pose_filename))
79  return false;
80 
81  vpImageIo::read(I, image_filename);
82 
83  unsigned int depth_width = 0, depth_height = 0;
84  std::ifstream file_depth(depth_filename.c_str(), std::ios::in | std::ios::binary);
85  if (!file_depth.is_open())
86  return false;
87 
88  vpIoTools::readBinaryValueLE(file_depth, depth_height);
89  vpIoTools::readBinaryValueLE(file_depth, depth_width);
90  I_depth.resize(depth_height, depth_width);
91  pointcloud.resize(depth_height * depth_width);
92 
93  const float depth_scale = 0.000030518f;
94  for (unsigned int i = 0; i < I_depth.getHeight(); i++) {
95  for (unsigned int j = 0; j < I_depth.getWidth(); j++) {
96  vpIoTools::readBinaryValueLE(file_depth, I_depth[i][j]);
97  double x = 0.0, y = 0.0, Z = I_depth[i][j] * depth_scale;
98  vpPixelMeterConversion::convertPoint(cam_depth, j, i, x, y);
99  vpColVector pt3d(4, 1.0);
100  pt3d[0] = x * Z;
101  pt3d[1] = y * Z;
102  pt3d[2] = Z;
103  pointcloud[i * I_depth.getWidth() + j] = pt3d;
104  }
105  }
106 
107  std::ifstream file_pose(pose_filename.c_str());
108  if (!file_pose.is_open()) {
109  return false;
110  }
111 
112  for (unsigned int i = 0; i < 4; i++) {
113  for (unsigned int j = 0; j < 4; j++) {
114  file_pose >> cMo[i][j];
115  }
116  }
117 
118  return true;
119 }
120 } // anonymous namespace
121 
122 TEST_CASE("Benchmark generic tracker", "[benchmark]")
123 {
124  if (runBenchmark) {
125  std::vector<int> tracker_type(2);
126  tracker_type[0] = vpMbGenericTracker::EDGE_TRACKER;
127  tracker_type[1] = vpMbGenericTracker::DEPTH_DENSE_TRACKER;
128  vpMbGenericTracker tracker(tracker_type);
129 
130  const std::string input_directory =
132 
133  const bool verbose = false;
134 #if defined(VISP_HAVE_PUGIXML)
135  const std::string configFileCam1 = input_directory + std::string("/Config/chateau.xml");
136  const std::string configFileCam2 = input_directory + std::string("/Config/chateau_depth.xml");
137  REQUIRE(vpIoTools::checkFilename(configFileCam1));
138  REQUIRE(vpIoTools::checkFilename(configFileCam2));
139  tracker.loadConfigFile(configFileCam1, configFileCam2, verbose);
140 #else
141  {
142  vpCameraParameters cam_color, cam_depth;
143  cam_color.initPersProjWithoutDistortion(700.0, 700.0, 320.0, 240.0);
144  cam_depth.initPersProjWithoutDistortion(700.0, 700.0, 320.0, 240.0);
145  tracker.setCameraParameters(cam_color, cam_depth);
146  }
147 
148  // Edge
149  vpMe me;
150  me.setMaskSize(5);
151  me.setMaskNumber(180);
152  me.setRange(8);
154  me.setThreshold(5);
155  me.setMu1(0.5);
156  me.setMu2(0.5);
157  me.setSampleStep(5);
158  tracker.setMovingEdge(me);
159 
160  // Klt
161 #if defined(VISP_HAVE_MODULE_KLT) && defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC) && defined(HAVE_OPENCV_VIDEO)
162  vpKltOpencv klt;
163  tracker.setKltMaskBorder(5);
164  klt.setMaxFeatures(10000);
165  klt.setWindowSize(5);
166  klt.setQuality(0.01);
167  klt.setMinDistance(5);
168  klt.setHarrisFreeParameter(0.02);
169  klt.setBlockSize(3);
170  klt.setPyramidLevels(3);
171 
172  tracker.setKltOpencv(klt);
173 #endif
174 
175  // Depth
176  tracker.setDepthNormalFeatureEstimationMethod(vpMbtFaceDepthNormal::ROBUST_FEATURE_ESTIMATION);
177  tracker.setDepthNormalPclPlaneEstimationMethod(2);
178  tracker.setDepthNormalPclPlaneEstimationRansacMaxIter(200);
179  tracker.setDepthNormalPclPlaneEstimationRansacThreshold(0.001);
180  tracker.setDepthNormalSamplingStep(2, 2);
181 
182  tracker.setDepthDenseSamplingStep(4, 4);
183 
184  tracker.setAngleAppear(vpMath::rad(85.0));
185  tracker.setAngleDisappear(vpMath::rad(89.0));
186  tracker.setNearClippingDistance(0.01);
187  tracker.setFarClippingDistance(2.0);
188  tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING);
189 #endif
190 
191  REQUIRE(vpIoTools::checkFilename(input_directory + "/Models/chateau.cao"));
192  tracker.loadModel(input_directory + "/Models/chateau.cao", input_directory + "/Models/chateau.cao", verbose);
193 
195  T[0][0] = -1;
196  T[0][3] = -0.2;
197  T[1][1] = 0;
198  T[1][2] = 1;
199  T[1][3] = 0.12;
200  T[2][1] = 1;
201  T[2][2] = 0;
202  T[2][3] = -0.15;
203  tracker.loadModel(input_directory + "/Models/cube.cao", verbose, T);
204 
206  vpImage<uint16_t> I_depth_raw;
207  vpHomogeneousMatrix cMo_truth;
208  std::vector<vpColVector> pointcloud;
209 
210  vpCameraParameters cam_color, cam_depth;
211  tracker.getCameraParameters(cam_color, cam_depth);
212 
213  vpHomogeneousMatrix depth_M_color;
214  depth_M_color[0][3] = -0.05;
215  tracker.setCameraTransformationMatrix("Camera2", depth_M_color);
216 
217  // load all the data in memory to not take into account I/O from disk
218  std::vector<vpImage<unsigned char> > images;
219  std::vector<vpImage<uint16_t> > depth_raws;
220  std::vector<std::vector<vpColVector> > pointclouds;
221  std::vector<vpHomogeneousMatrix> cMo_truth_all;
222  // forward
223  for (int i = 1; i <= 40; i++) {
224  if (read_data(input_directory, i, cam_depth, I, I_depth_raw, pointcloud, cMo_truth)) {
225  images.push_back(I);
226  depth_raws.push_back(I_depth_raw);
227  pointclouds.push_back(pointcloud);
228  cMo_truth_all.push_back(cMo_truth);
229  }
230  }
231  // backward
232  for (int i = 40; i >= 1; i--) {
233  if (read_data(input_directory, i, cam_depth, I, I_depth_raw, pointcloud, cMo_truth)) {
234  images.push_back(I);
235  depth_raws.push_back(I_depth_raw);
236  pointclouds.push_back(pointcloud);
237  cMo_truth_all.push_back(cMo_truth);
238  }
239  }
240 
241  // Stereo MBT
242  {
243  std::vector<std::map<std::string, int> > mapOfTrackerTypes;
244  mapOfTrackerTypes.push_back(
246  mapOfTrackerTypes.push_back(
248 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC) && defined(HAVE_OPENCV_VIDEO)
249  mapOfTrackerTypes.push_back(
251  mapOfTrackerTypes.push_back({ {"Camera1", vpMbGenericTracker::EDGE_TRACKER | vpMbGenericTracker::KLT_TRACKER},
253  mapOfTrackerTypes.push_back({ {"Camera1", vpMbGenericTracker::EDGE_TRACKER | vpMbGenericTracker::KLT_TRACKER},
255 #endif
256 
257  std::vector<std::string> benchmarkNames = {
258  "Edge MBT",
259  "Edge + Depth dense MBT",
260 #if defined(VISP_HAVE_OPENCV)
261  "KLT MBT",
262  "KLT + depth dense MBT",
263  "Edge + KLT + depth dense MBT"
264 #endif
265  };
266 
267  std::vector<bool> monoculars = {
268  true,
269  false,
270 #if defined(VISP_HAVE_OPENCV)
271  true,
272  false,
273  false
274 #endif
275  };
276 
277  for (size_t idx = 0; idx < mapOfTrackerTypes.size(); idx++) {
278  tracker.resetTracker();
279  tracker.setTrackerType(mapOfTrackerTypes[idx]);
280 
281  const bool verbose = false;
282 #if defined(VISP_HAVE_PUGIXML)
283  tracker.loadConfigFile(configFileCam1, configFileCam2, verbose);
284 #else
285  {
286  vpCameraParameters cam_color, cam_depth;
287  cam_color.initPersProjWithoutDistortion(700.0, 700.0, 320.0, 240.0);
288  cam_depth.initPersProjWithoutDistortion(700.0, 700.0, 320.0, 240.0);
289  tracker.setCameraParameters(cam_color, cam_depth);
290  }
291 
292  // Edge
293  vpMe me;
294  me.setMaskSize(5);
295  me.setMaskNumber(180);
296  me.setRange(8);
298  me.setThreshold(5);
299  me.setMu1(0.5);
300  me.setMu2(0.5);
301  me.setSampleStep(5);
302  tracker.setMovingEdge(me);
303 
304  // Klt
305 #if defined(VISP_HAVE_MODULE_KLT) && defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC) && defined(HAVE_OPENCV_VIDEO)
306  vpKltOpencv klt;
307  tracker.setKltMaskBorder(5);
308  klt.setMaxFeatures(10000);
309  klt.setWindowSize(5);
310  klt.setQuality(0.01);
311  klt.setMinDistance(5);
312  klt.setHarrisFreeParameter(0.02);
313  klt.setBlockSize(3);
314  klt.setPyramidLevels(3);
315 
316  tracker.setKltOpencv(klt);
317 #endif
318 
319  // Depth
320  tracker.setDepthNormalFeatureEstimationMethod(vpMbtFaceDepthNormal::ROBUST_FEATURE_ESTIMATION);
321  tracker.setDepthNormalPclPlaneEstimationMethod(2);
322  tracker.setDepthNormalPclPlaneEstimationRansacMaxIter(200);
323  tracker.setDepthNormalPclPlaneEstimationRansacThreshold(0.001);
324  tracker.setDepthNormalSamplingStep(2, 2);
325 
326  tracker.setDepthDenseSamplingStep(4, 4);
327 
328  tracker.setAngleAppear(vpMath::rad(85.0));
329  tracker.setAngleDisappear(vpMath::rad(89.0));
330  tracker.setNearClippingDistance(0.01);
331  tracker.setFarClippingDistance(2.0);
332  tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING);
333 #endif
334  tracker.loadModel(input_directory + "/Models/chateau.cao", input_directory + "/Models/chateau.cao", verbose);
335  tracker.loadModel(input_directory + "/Models/cube.cao", verbose, T);
336  tracker.initFromPose(images.front(), cMo_truth_all.front());
337 
338  std::map<std::string, unsigned int> mapOfWidths, mapOfHeights;
339  mapOfWidths["Camera2"] = monoculars[idx] ? 0 : I_depth_raw.getWidth();
340  mapOfHeights["Camera2"] = monoculars[idx] ? 0 : I_depth_raw.getHeight();
341 
343 #ifndef DEBUG_DISPLAY
344  BENCHMARK(benchmarkNames[idx].c_str())
345 #else
346  vpImage<unsigned char> I_depth;
347  vpImageConvert::createDepthHistogram(I_depth_raw, I_depth);
348 
349  vpDisplayX d_color(I, 0, 0, "Color image");
350  vpDisplayX d_depth(I_depth, I.getWidth(), 0, "Depth image");
351  tracker.setDisplayFeatures(true);
352 #endif
353  {
354  tracker.initFromPose(images.front(), cMo_truth_all.front());
355 
356  for (size_t i = 0; i < images.size(); i++) {
357  const vpImage<unsigned char> &I_current = images[i];
358  const std::vector<vpColVector> &pointcloud_current = pointclouds[i];
359 
360 #ifdef DEBUG_DISPLAY
361  vpImageConvert::createDepthHistogram(depth_raws[i], I_depth);
362  I = I_current;
364  vpDisplay::display(I_depth);
365 #endif
366 
367  std::map<std::string, const vpImage<unsigned char> *> mapOfImages;
368  mapOfImages["Camera1"] = &I_current;
369 
370  std::map<std::string, const std::vector<vpColVector> *> mapOfPointclouds;
371  mapOfPointclouds["Camera2"] = &pointcloud_current;
372 
373  tracker.track(mapOfImages, mapOfPointclouds, mapOfWidths, mapOfHeights);
374  cMo = tracker.getPose();
375 
376 #ifdef DEBUG_DISPLAY
377  tracker.display(I, I_depth, cMo, depth_M_color * cMo, cam_color, cam_depth, vpColor::red, 3);
378  vpDisplay::displayFrame(I, cMo, cam_color, 0.05, vpColor::none, 3);
379  vpDisplay::displayFrame(I_depth, depth_M_color * cMo, cam_depth, 0.05, vpColor::none, 3);
380  vpDisplay::displayText(I, 20, 20, benchmarkNames[idx], vpColor::red);
382  I, 40, 20, std::string("Nb features: " + std::to_string(tracker.getError().getRows())), vpColor::red);
383 
384  vpDisplay::flush(I);
385  vpDisplay::flush(I_depth);
386  vpTime::wait(33);
387 #endif
388  }
389 
390 #ifndef DEBUG_DISPLAY
391  return cMo;
392  };
393 #else
394  }
395 #endif
396 
397  vpPoseVector pose_est(cMo);
398  vpPoseVector pose_truth(cMo_truth);
399  vpColVector t_err(3), tu_err(3);
400  for (unsigned int i = 0; i < 3; i++) {
401  t_err[i] = pose_est[i] - pose_truth[i];
402  tu_err[i] = pose_est[i + 3] - pose_truth[i + 3];
403  }
404 
405  const double max_translation_error = 0.006;
406  const double max_rotation_error = 0.03;
407  CHECK(sqrt(t_err.sumSquare()) < max_translation_error);
408  CHECK(sqrt(tu_err.sumSquare()) < max_rotation_error);
409  }
410  }
411 } // if (runBenchmark)
412 }
413 
414 int main(int argc, char *argv[])
415 {
416  Catch::Session session; // There must be exactly one instance
417 
418  // Build a new parser on top of Catch's
419  using namespace Catch::clara;
420  auto cli = session.cli() // Get Catch's composite command line parser
421  | Opt(runBenchmark) // bind variable to a new option, with a hint string
422  ["--benchmark"] // the option names it will respond to
423  ("run benchmark comparing naive code with ViSP implementation"); // description string for the help output
424 
425 // Now pass the new composite back to Catch so it uses that
426  session.cli(cli);
427 
428  // Let Catch (using Clara) parse the command line
429  session.applyCommandLine(argc, argv);
430 
431  int numFailed = session.run();
432 
433  // numFailed is clamped to 255 as some unices only use the lower 8 bits.
434  // This clamping has already been applied, so just return it here
435  // You can also do any post run clean-up here
436  return numFailed;
437 }
438 #else
439 #include <iostream>
440 
441 int main() { return EXIT_SUCCESS; }
442 #endif
Generic class defining intrinsic camera parameters.
void initPersProjWithoutDistortion(double px, double py, double u0, double v0)
Implementation of column vector and the associated operations.
Definition: vpColVector.h:163
static const vpColor red
Definition: vpColor.h:211
static const vpColor none
Definition: vpColor.h:223
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition: vpDisplayX.h:128
static void display(const vpImage< unsigned char > &I)
static void displayFrame(const vpImage< unsigned char > &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, double size, const vpColor &color=vpColor::none, unsigned int thickness=1, const vpImagePoint &offset=vpImagePoint(0, 0), const std::string &frameName="", const vpColor &textColor=vpColor::black, const vpImagePoint &textOffset=vpImagePoint(15, 15))
static void flush(const vpImage< unsigned char > &I)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
Implementation of an homogeneous matrix and operations on such kind of matrices.
static void createDepthHistogram(const vpImage< uint16_t > &src_depth, vpImage< vpRGBa > &dest_rgba)
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition: vpImageIo.cpp:143
Definition of the vpImage class member functions.
Definition: vpImage.h:69
unsigned int getWidth() const
Definition: vpImage.h:245
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:798
unsigned int getHeight() const
Definition: vpImage.h:184
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1834
static bool checkFilename(const std::string &filename)
Definition: vpIoTools.cpp:1215
static void readBinaryValueLE(std::ifstream &file, int16_t &short_value)
Definition: vpIoTools.cpp:2522
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:2197
Wrapper for the KLT (Kanade-Lucas-Tomasi) feature tracker implemented in OpenCV. Thus to enable this ...
Definition: vpKltOpencv.h:73
void setBlockSize(int blockSize)
Definition: vpKltOpencv.h:266
void setQuality(double qualityLevel)
Definition: vpKltOpencv.h:355
void setHarrisFreeParameter(double harris_k)
Definition: vpKltOpencv.h:274
void setMaxFeatures(int maxCount)
Definition: vpKltOpencv.h:314
void setMinDistance(double minDistance)
Definition: vpKltOpencv.h:323
void setWindowSize(int winSize)
Definition: vpKltOpencv.h:376
void setPyramidLevels(int pyrMaxLevel)
Definition: vpKltOpencv.h:342
static double rad(double deg)
Definition: vpMath.h:127
Real-time 6D object pose tracking using its CAD model.
Definition: vpMe.h:124
void setMu1(const double &mu_1)
Definition: vpMe.h:399
void setRange(const unsigned int &range)
Definition: vpMe.h:429
void setLikelihoodThresholdType(const vpLikelihoodThresholdType likelihood_threshold_type)
Definition: vpMe.h:519
void setMaskNumber(const unsigned int &mask_number)
Definition: vpMe.cpp:488
void setThreshold(const double &threshold)
Definition: vpMe.h:480
void setSampleStep(const double &sample_step)
Definition: vpMe.h:436
void setMaskSize(const unsigned int &mask_size)
Definition: vpMe.cpp:496
void setMu2(const double &mu_2)
Definition: vpMe.h:406
@ NORMALIZED_THRESHOLD
Definition: vpMe.h:135
static void convertPoint(const vpCameraParameters &cam, const double &u, const double &v, double &x, double &y)
Implementation of a pose vector and operations on poses.
Definition: vpPoseVector.h:189
VISP_EXPORT int wait(double t0, double t)