Visual Servoing Platform  version 3.6.1 under development (2024-11-15)
perfGenericTracker.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  * Benchmark generic tracker.
32  */
33 
37 #include <visp3/core/vpConfig.h>
38 
39 #if defined(VISP_HAVE_CATCH2)
40 
41 #include <catch_amalgamated.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 #ifdef ENABLE_VISP_NAMESPACE
53 using namespace VISP_NAMESPACE_NAME;
54 #endif
55 
56 namespace
57 {
58 bool runBenchmark = false;
59 
60 template <typename Type>
61 bool read_data(const std::string &input_directory, int cpt, const vpCameraParameters &cam_depth, vpImage<Type> &I,
62  vpImage<uint16_t> &I_depth, std::vector<vpColVector> &pointcloud, vpHomogeneousMatrix &cMo)
63 {
64  static_assert(std::is_same<Type, unsigned char>::value || std::is_same<Type, vpRGBa>::value,
65  "Template function supports only unsigned char and vpRGBa images!");
66 #if VISP_HAVE_DATASET_VERSION >= 0x030600
67  std::string ext("png");
68 #else
69  std::string ext("pgm");
70 #endif
71  char buffer[FILENAME_MAX];
72  snprintf(buffer, FILENAME_MAX, std::string(input_directory + "/Images/Image_%04d." + ext).c_str(), cpt);
73  std::string image_filename = buffer;
74 
75  snprintf(buffer, FILENAME_MAX, std::string(input_directory + "/Depth/Depth_%04d.bin").c_str(), cpt);
76  std::string depth_filename = buffer;
77 
78  snprintf(buffer, FILENAME_MAX, std::string(input_directory + "/CameraPose/Camera_%03d.txt").c_str(), cpt);
79  std::string pose_filename = buffer;
80 
81  if (!vpIoTools::checkFilename(image_filename) || !vpIoTools::checkFilename(depth_filename) ||
82  !vpIoTools::checkFilename(pose_filename))
83  return false;
84 
85  vpImageIo::read(I, image_filename);
86 
87  unsigned int depth_width = 0, depth_height = 0;
88  std::ifstream file_depth(depth_filename.c_str(), std::ios::in | std::ios::binary);
89  if (!file_depth.is_open())
90  return false;
91 
92  vpIoTools::readBinaryValueLE(file_depth, depth_height);
93  vpIoTools::readBinaryValueLE(file_depth, depth_width);
94  I_depth.resize(depth_height, depth_width);
95  pointcloud.resize(depth_height * depth_width);
96 
97  const float depth_scale = 0.000030518f;
98  for (unsigned int i = 0; i < I_depth.getHeight(); i++) {
99  for (unsigned int j = 0; j < I_depth.getWidth(); j++) {
100  vpIoTools::readBinaryValueLE(file_depth, I_depth[i][j]);
101  double x = 0.0, y = 0.0, Z = I_depth[i][j] * depth_scale;
102  vpPixelMeterConversion::convertPoint(cam_depth, j, i, x, y);
103  vpColVector pt3d(4, 1.0);
104  pt3d[0] = x * Z;
105  pt3d[1] = y * Z;
106  pt3d[2] = Z;
107  pointcloud[i * I_depth.getWidth() + j] = pt3d;
108  }
109  }
110 
111  std::ifstream file_pose(pose_filename.c_str());
112  if (!file_pose.is_open()) {
113  return false;
114  }
115 
116  for (unsigned int i = 0; i < 4; i++) {
117  for (unsigned int j = 0; j < 4; j++) {
118  file_pose >> cMo[i][j];
119  }
120  }
121 
122  return true;
123 }
124 } // anonymous namespace
125 
126 TEST_CASE("Benchmark generic tracker", "[benchmark]")
127 {
128  if (runBenchmark) {
129  std::vector<int> tracker_type(2);
130  tracker_type[0] = vpMbGenericTracker::EDGE_TRACKER;
131  tracker_type[1] = vpMbGenericTracker::DEPTH_DENSE_TRACKER;
132  vpMbGenericTracker tracker(tracker_type);
133 
134  const std::string input_directory =
136 
137  const bool verbose = false;
138 #if defined(VISP_HAVE_PUGIXML)
139  const std::string configFileCam1 = input_directory + std::string("/Config/chateau.xml");
140  const std::string configFileCam2 = input_directory + std::string("/Config/chateau_depth.xml");
141  REQUIRE(vpIoTools::checkFilename(configFileCam1));
142  REQUIRE(vpIoTools::checkFilename(configFileCam2));
143  tracker.loadConfigFile(configFileCam1, configFileCam2, verbose);
144 #else
145  {
146  vpCameraParameters cam_color, cam_depth;
147  cam_color.initPersProjWithoutDistortion(700.0, 700.0, 320.0, 240.0);
148  cam_depth.initPersProjWithoutDistortion(700.0, 700.0, 320.0, 240.0);
149  tracker.setCameraParameters(cam_color, cam_depth);
150  }
151 
152  // Edge
153  vpMe me;
154  me.setMaskSize(5);
155  me.setMaskNumber(180);
156  me.setRange(8);
158  me.setThreshold(5);
159  me.setMu1(0.5);
160  me.setMu2(0.5);
161  me.setSampleStep(5);
162  tracker.setMovingEdge(me);
163 
164  // Klt
165 #if defined(VISP_HAVE_MODULE_KLT) && defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC) && defined(HAVE_OPENCV_VIDEO)
166  vpKltOpencv klt;
167  tracker.setKltMaskBorder(5);
168  klt.setMaxFeatures(10000);
169  klt.setWindowSize(5);
170  klt.setQuality(0.01);
171  klt.setMinDistance(5);
172  klt.setHarrisFreeParameter(0.02);
173  klt.setBlockSize(3);
174  klt.setPyramidLevels(3);
175 
176  tracker.setKltOpencv(klt);
177 #endif
178 
179  // Depth
180  tracker.setDepthNormalFeatureEstimationMethod(vpMbtFaceDepthNormal::ROBUST_FEATURE_ESTIMATION);
181  tracker.setDepthNormalPclPlaneEstimationMethod(2);
182  tracker.setDepthNormalPclPlaneEstimationRansacMaxIter(200);
183  tracker.setDepthNormalPclPlaneEstimationRansacThreshold(0.001);
184  tracker.setDepthNormalSamplingStep(2, 2);
185 
186  tracker.setDepthDenseSamplingStep(4, 4);
187 
188  tracker.setAngleAppear(vpMath::rad(85.0));
189  tracker.setAngleDisappear(vpMath::rad(89.0));
190  tracker.setNearClippingDistance(0.01);
191  tracker.setFarClippingDistance(2.0);
192  tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING);
193 #endif
194 
195  REQUIRE(vpIoTools::checkFilename(input_directory + "/Models/chateau.cao"));
196  tracker.loadModel(input_directory + "/Models/chateau.cao", input_directory + "/Models/chateau.cao", verbose);
197 
199  T[0][0] = -1;
200  T[0][3] = -0.2;
201  T[1][1] = 0;
202  T[1][2] = 1;
203  T[1][3] = 0.12;
204  T[2][1] = 1;
205  T[2][2] = 0;
206  T[2][3] = -0.15;
207  tracker.loadModel(input_directory + "/Models/cube.cao", verbose, T);
208 
210  vpImage<uint16_t> I_depth_raw;
211  vpHomogeneousMatrix cMo_truth;
212  std::vector<vpColVector> pointcloud;
213 
214  vpCameraParameters cam_color, cam_depth;
215  tracker.getCameraParameters(cam_color, cam_depth);
216 
217  vpHomogeneousMatrix depth_M_color;
218  depth_M_color[0][3] = -0.05;
219  tracker.setCameraTransformationMatrix("Camera2", depth_M_color);
220 
221  // load all the data in memory to not take into account I/O from disk
222  std::vector<vpImage<unsigned char> > images;
223  std::vector<vpImage<uint16_t> > depth_raws;
224  std::vector<std::vector<vpColVector> > pointclouds;
225  std::vector<vpHomogeneousMatrix> cMo_truth_all;
226  // forward
227  for (int i = 1; i <= 40; i++) {
228  if (read_data(input_directory, i, cam_depth, I, I_depth_raw, pointcloud, cMo_truth)) {
229  images.push_back(I);
230  depth_raws.push_back(I_depth_raw);
231  pointclouds.push_back(pointcloud);
232  cMo_truth_all.push_back(cMo_truth);
233  }
234  }
235  // backward
236  for (int i = 40; i >= 1; i--) {
237  if (read_data(input_directory, i, cam_depth, I, I_depth_raw, pointcloud, cMo_truth)) {
238  images.push_back(I);
239  depth_raws.push_back(I_depth_raw);
240  pointclouds.push_back(pointcloud);
241  cMo_truth_all.push_back(cMo_truth);
242  }
243  }
244 
245  // Stereo MBT
246  {
247  std::vector<std::map<std::string, int> > mapOfTrackerTypes;
248  mapOfTrackerTypes.push_back(
250  mapOfTrackerTypes.push_back(
252 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC) && defined(HAVE_OPENCV_VIDEO)
253  mapOfTrackerTypes.push_back(
254  { {"Camera1", vpMbGenericTracker::KLT_TRACKER}, {"Camera2", vpMbGenericTracker::DEPTH_DENSE_TRACKER} });
255  mapOfTrackerTypes.push_back({ {"Camera1", vpMbGenericTracker::EDGE_TRACKER | vpMbGenericTracker::KLT_TRACKER},
257  mapOfTrackerTypes.push_back({ {"Camera1", vpMbGenericTracker::EDGE_TRACKER | vpMbGenericTracker::KLT_TRACKER},
259 #endif
260 
261  std::vector<std::string> benchmarkNames = {
262  "Edge MBT",
263  "Edge + Depth dense MBT",
264 #if defined(VISP_HAVE_OPENCV)
265  "KLT MBT",
266  "KLT + depth dense MBT",
267  "Edge + KLT + depth dense MBT"
268 #endif
269  };
270 
271  std::vector<bool> monoculars = {
272  true,
273  false,
274 #if defined(VISP_HAVE_OPENCV)
275  true,
276  false,
277  false
278 #endif
279  };
280 
281  for (size_t idx = 0; idx < mapOfTrackerTypes.size(); idx++) {
282  tracker.resetTracker();
283  tracker.setTrackerType(mapOfTrackerTypes[idx]);
284 
285  const bool verbose = false;
286 #if defined(VISP_HAVE_PUGIXML)
287  tracker.loadConfigFile(configFileCam1, configFileCam2, verbose);
288 #else
289  {
290  vpCameraParameters cam_color, cam_depth;
291  cam_color.initPersProjWithoutDistortion(700.0, 700.0, 320.0, 240.0);
292  cam_depth.initPersProjWithoutDistortion(700.0, 700.0, 320.0, 240.0);
293  tracker.setCameraParameters(cam_color, cam_depth);
294  }
295 
296  // Edge
297  vpMe me;
298  me.setMaskSize(5);
299  me.setMaskNumber(180);
300  me.setRange(8);
302  me.setThreshold(5);
303  me.setMu1(0.5);
304  me.setMu2(0.5);
305  me.setSampleStep(5);
306  tracker.setMovingEdge(me);
307 
308  // Klt
309 #if defined(VISP_HAVE_MODULE_KLT) && defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC) && defined(HAVE_OPENCV_VIDEO)
310  vpKltOpencv klt;
311  tracker.setKltMaskBorder(5);
312  klt.setMaxFeatures(10000);
313  klt.setWindowSize(5);
314  klt.setQuality(0.01);
315  klt.setMinDistance(5);
316  klt.setHarrisFreeParameter(0.02);
317  klt.setBlockSize(3);
318  klt.setPyramidLevels(3);
319 
320  tracker.setKltOpencv(klt);
321 #endif
322 
323  // Depth
324  tracker.setDepthNormalFeatureEstimationMethod(vpMbtFaceDepthNormal::ROBUST_FEATURE_ESTIMATION);
325  tracker.setDepthNormalPclPlaneEstimationMethod(2);
326  tracker.setDepthNormalPclPlaneEstimationRansacMaxIter(200);
327  tracker.setDepthNormalPclPlaneEstimationRansacThreshold(0.001);
328  tracker.setDepthNormalSamplingStep(2, 2);
329 
330  tracker.setDepthDenseSamplingStep(4, 4);
331 
332  tracker.setAngleAppear(vpMath::rad(85.0));
333  tracker.setAngleDisappear(vpMath::rad(89.0));
334  tracker.setNearClippingDistance(0.01);
335  tracker.setFarClippingDistance(2.0);
336  tracker.setClipping(tracker.getClipping() | vpMbtPolygon::FOV_CLIPPING);
337 #endif
338  tracker.loadModel(input_directory + "/Models/chateau.cao", input_directory + "/Models/chateau.cao", verbose);
339  tracker.loadModel(input_directory + "/Models/cube.cao", verbose, T);
340  tracker.initFromPose(images.front(), cMo_truth_all.front());
341 
342  std::map<std::string, unsigned int> mapOfWidths, mapOfHeights;
343  mapOfWidths["Camera2"] = monoculars[idx] ? 0 : I_depth_raw.getWidth();
344  mapOfHeights["Camera2"] = monoculars[idx] ? 0 : I_depth_raw.getHeight();
345 
347 #ifndef DEBUG_DISPLAY
348  BENCHMARK(benchmarkNames[idx].c_str())
349 #else
350  vpImage<unsigned char> I_depth;
351  vpImageConvert::createDepthHistogram(I_depth_raw, I_depth);
352 
353  vpDisplayX d_color(I, 0, 0, "Color image");
354  vpDisplayX d_depth(I_depth, I.getWidth(), 0, "Depth image");
355  tracker.setDisplayFeatures(true);
356 #endif
357  {
358  tracker.initFromPose(images.front(), cMo_truth_all.front());
359 
360  for (size_t i = 0; i < images.size(); i++) {
361  const vpImage<unsigned char> &I_current = images[i];
362  const std::vector<vpColVector> &pointcloud_current = pointclouds[i];
363 
364 #ifdef DEBUG_DISPLAY
365  vpImageConvert::createDepthHistogram(depth_raws[i], I_depth);
366  I = I_current;
368  vpDisplay::display(I_depth);
369 #endif
370 
371  std::map<std::string, const vpImage<unsigned char> *> mapOfImages;
372  mapOfImages["Camera1"] = &I_current;
373 
374  std::map<std::string, const std::vector<vpColVector> *> mapOfPointclouds;
375  mapOfPointclouds["Camera2"] = &pointcloud_current;
376 
377  tracker.track(mapOfImages, mapOfPointclouds, mapOfWidths, mapOfHeights);
378  cMo = tracker.getPose();
379 
380 #ifdef DEBUG_DISPLAY
381  tracker.display(I, I_depth, cMo, depth_M_color * cMo, cam_color, cam_depth, vpColor::red, 3);
382  vpDisplay::displayFrame(I, cMo, cam_color, 0.05, vpColor::none, 3);
383  vpDisplay::displayFrame(I_depth, depth_M_color * cMo, cam_depth, 0.05, vpColor::none, 3);
384  vpDisplay::displayText(I, 20, 20, benchmarkNames[idx], vpColor::red);
386  I, 40, 20, std::string("Nb features: " + std::to_string(tracker.getError().getRows())), vpColor::red);
387 
388  vpDisplay::flush(I);
389  vpDisplay::flush(I_depth);
390  vpTime::wait(33);
391 #endif
392  }
393 
394 #ifndef DEBUG_DISPLAY
395  return cMo;
396  };
397 #else
398  }
399 #endif
400 
401  vpPoseVector pose_est(cMo);
402  vpPoseVector pose_truth(cMo_truth);
403  vpColVector t_err(3), tu_err(3);
404  for (unsigned int i = 0; i < 3; i++) {
405  t_err[i] = pose_est[i] - pose_truth[i];
406  tu_err[i] = pose_est[i + 3] - pose_truth[i + 3];
407  }
408 
409  const double max_translation_error = 0.006;
410  const double max_rotation_error = 0.03;
411  CHECK(sqrt(t_err.sumSquare()) < max_translation_error);
412  CHECK(sqrt(tu_err.sumSquare()) < max_rotation_error);
413  }
414  }
415 } // if (runBenchmark)
416 }
417 
418 int main(int argc, char *argv[])
419 {
420  Catch::Session session;
421 
422  auto cli = session.cli() // Get Catch's composite command line parser
423  | Catch::Clara::Opt(runBenchmark) // bind variable to a new option, with a hint string
424  ["--benchmark"] // the option names it will respond to
425  ("run benchmark comparing naive code with ViSP implementation"); // description string for the help output
426 
427  // Now pass the new composite back to Catch so it uses that
428  session.cli(cli);
429  session.applyCommandLine(argc, argv);
430  int numFailed = session.run();
431  return numFailed;
432 }
433 
434 #else
435 #include <iostream>
436 
437 int main() { return EXIT_SUCCESS; }
438 #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:191
static const vpColor red
Definition: vpColor.h:217
static const vpColor none
Definition: vpColor.h:229
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:147
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:542
unsigned int getHeight() const
Definition: vpImage.h:181
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1053
static bool checkFilename(const std::string &filename)
Definition: vpIoTools.cpp:786
static void readBinaryValueLE(std::ifstream &file, int16_t &short_value)
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1427
Wrapper for the KLT (Kanade-Lucas-Tomasi) feature tracker implemented in OpenCV. Thus to enable this ...
Definition: vpKltOpencv.h:74
void setBlockSize(int blockSize)
Definition: vpKltOpencv.h:267
void setQuality(double qualityLevel)
Definition: vpKltOpencv.h:356
void setHarrisFreeParameter(double harris_k)
Definition: vpKltOpencv.h:275
void setMaxFeatures(int maxCount)
Definition: vpKltOpencv.h:315
void setMinDistance(double minDistance)
Definition: vpKltOpencv.h:324
void setWindowSize(int winSize)
Definition: vpKltOpencv.h:377
void setPyramidLevels(int pyrMaxLevel)
Definition: vpKltOpencv.h:343
static double rad(double deg)
Definition: vpMath.h:129
Real-time 6D object pose tracking using its CAD model.
@ ROBUST_FEATURE_ESTIMATION
Robust scheme to estimate the normal of the plane.
Definition: vpMe.h:134
void setMu1(const double &mu_1)
Definition: vpMe.h:385
void setRange(const unsigned int &range)
Definition: vpMe.h:415
void setLikelihoodThresholdType(const vpLikelihoodThresholdType likelihood_threshold_type)
Definition: vpMe.h:505
void setMaskNumber(const unsigned int &mask_number)
Definition: vpMe.cpp:552
void setThreshold(const double &threshold)
Definition: vpMe.h:466
void setSampleStep(const double &sample_step)
Definition: vpMe.h:422
void setMaskSize(const unsigned int &mask_size)
Definition: vpMe.cpp:560
void setMu2(const double &mu_2)
Definition: vpMe.h:392
@ NORMALIZED_THRESHOLD
Definition: vpMe.h:145
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:203
VISP_EXPORT int wait(double t0, double t)