Visual Servoing Platform  version 3.6.1 under development (2024-11-15)
catchPoseRansac2.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 RANSAC 3D pose estimation method.
32  */
33 
38 #include <visp3/core/vpConfig.h>
39 
40 #if defined(VISP_HAVE_CATCH2)
41 
42 #include <catch_amalgamated.hpp>
43 
44 #include <algorithm>
45 #include <iomanip>
46 #include <map>
47 #include <visp3/core/vpGaussRand.h>
48 #include <visp3/core/vpHomogeneousMatrix.h>
49 #include <visp3/core/vpIoTools.h>
50 #include <visp3/core/vpMath.h>
51 #include <visp3/core/vpPoint.h>
52 #include <visp3/vision/vpPose.h>
53 
54 #ifdef ENABLE_VISP_NAMESPACE
55 using namespace VISP_NAMESPACE_NAME;
56 #endif
57 
58 namespace
59 {
60 #if (VISP_HAVE_DATASET_VERSION >= 0x030300)
61 bool samePoints(const vpPoint &pt1, const vpPoint &pt2)
62 {
63  return vpMath::equal(pt1.get_oX(), pt2.get_oX(), std::numeric_limits<double>::epsilon()) &&
64  vpMath::equal(pt1.get_oY(), pt2.get_oY(), std::numeric_limits<double>::epsilon()) &&
65  vpMath::equal(pt1.get_oZ(), pt2.get_oZ(), std::numeric_limits<double>::epsilon()) &&
66  vpMath::equal(pt1.get_x(), pt2.get_x(), std::numeric_limits<double>::epsilon()) &&
67  vpMath::equal(pt1.get_y(), pt2.get_y(), std::numeric_limits<double>::epsilon());
68 }
69 
70 int checkInlierIndex(const std::vector<unsigned int> &vectorOfFoundInlierIndex,
71  const std::vector<bool> &vectorOfOutlierFlags)
72 {
73  int nbInlierIndexOk = 0;
74 
75  for (std::vector<unsigned int>::const_iterator it = vectorOfFoundInlierIndex.begin();
76  it != vectorOfFoundInlierIndex.end(); ++it) {
77  if (!vectorOfOutlierFlags[*it]) {
78  nbInlierIndexOk++;
79  }
80  }
81 
82  return nbInlierIndexOk;
83 }
84 
85 bool checkInlierPoints(const std::vector<vpPoint> &vectorOfFoundInlierPoints,
86  const std::vector<unsigned int> &vectorOfFoundInlierIndex,
87  const std::vector<vpPoint> &bunnyModelPoints_noisy)
88 {
89  for (size_t i = 0; i < vectorOfFoundInlierPoints.size(); i++) {
90  if (!samePoints(vectorOfFoundInlierPoints[i], bunnyModelPoints_noisy[vectorOfFoundInlierIndex[i]])) {
91  std::cerr << "Problem with the inlier index and the corresponding "
92  "inlier point!"
93  << std::endl;
94  std::cerr << "Returned inliers: oX=" << std::setprecision(std::numeric_limits<double>::max_digits10)
95  << vectorOfFoundInlierPoints[i].get_oX() << ", oY=" << vectorOfFoundInlierPoints[i].get_oY()
96  << ", oZ=" << vectorOfFoundInlierPoints[i].get_oZ() << " ; x=" << vectorOfFoundInlierPoints[i].get_x()
97  << ", y=" << vectorOfFoundInlierPoints[i].get_y() << std::endl;
98  const vpPoint &pt = bunnyModelPoints_noisy[vectorOfFoundInlierIndex[i]];
99  std::cerr << "Object points: oX=" << std::setprecision(std::numeric_limits<double>::max_digits10) << pt.get_oX()
100  << ", oY=" << pt.get_oY() << ", oZ=" << pt.get_oZ() << " ; x=" << pt.get_x() << ", y=" << pt.get_y()
101  << std::endl;
102  return false;
103  }
104  }
105 
106  return true;
107 }
108 
109 void readBunnyModelPoints(const std::string &filename, std::vector<vpPoint> &bunnyModelPoints,
110  std::vector<vpPoint> &bunnyModelPoints_noisy)
111 {
112  // Read the model
113  std::ifstream file(filename);
114  if (!file.is_open()) {
115  return;
116  }
117 
118  // ground truth cMo
119  const vpTranslationVector translation(-0.14568, 0.154567, 1.4462);
120  const vpRzyxVector zyxVector(vpMath::rad(12.4146f), vpMath::rad(-75.5478f), vpMath::rad(138.5607f));
121  vpHomogeneousMatrix cMo_groundTruth(translation, vpThetaUVector(zyxVector));
122 
123  vpGaussRand gaussian_noise(0.0002, 0.0);
124  double oX = 0, oY = 0, oZ = 0;
125 
126  while (file >> oX >> oY >> oZ) {
127  vpPoint pt(oX, oY, oZ);
128  pt.project(cMo_groundTruth);
129  bunnyModelPoints.push_back(pt);
130 
131  // Add a small gaussian noise to the data
132  pt.set_x(pt.get_x() + gaussian_noise());
133  pt.set_y(pt.get_y() + gaussian_noise());
134  bunnyModelPoints_noisy.push_back(pt);
135  }
136 
137  // Print the number of model points
138  std::cout << "The raw model contains " << bunnyModelPoints.size() << " points." << std::endl;
139  std::cout << "cMo_groundTruth=\n" << cMo_groundTruth << std::endl << std::endl;
140 }
141 
142 bool testRansac(const std::vector<vpPoint> &bunnyModelPoints_original,
143  const std::vector<vpPoint> &bunnyModelPoints_noisy_original, size_t nb_model_points,
144  bool test_duplicate, bool test_degenerate)
145 {
146  std::vector<vpPoint> bunnyModelPoints = bunnyModelPoints_original;
147  std::vector<vpPoint> bunnyModelPoints_noisy = bunnyModelPoints_noisy_original;
148  // Resize
149  if (nb_model_points > 0) {
150  bunnyModelPoints.resize(nb_model_points);
151  bunnyModelPoints_noisy.resize(nb_model_points);
152  }
153 
154  vpPose ground_truth_pose, real_pose;
155  vpHomogeneousMatrix cMo_estimated;
156  ground_truth_pose.addPoints(bunnyModelPoints);
157  real_pose.addPoints(bunnyModelPoints_noisy);
158  real_pose.computePose(vpPose::DEMENTHON_LAGRANGE_VIRTUAL_VS, cMo_estimated);
159  double r_vvs = ground_truth_pose.computeResidual(cMo_estimated);
160 
161  std::cout << "\ncMo estimated using VVS on data with small gaussian noise:\n" << cMo_estimated << std::endl;
162  std::cout << "Corresponding residual: " << r_vvs << std::endl;
163 
164  size_t nbOutliers = (size_t)(0.35 * bunnyModelPoints_noisy.size());
165  vpGaussRand noise(0.01, 0.008);
166  // Vector that indicates if the point is an outlier or not
167  std::vector<bool> vectorOfOutlierFlags(bunnyModelPoints_noisy.size(), false);
168  // Generate outliers points
169  for (size_t i = 0; i < nbOutliers; i++) {
170  bunnyModelPoints_noisy[i].set_x(bunnyModelPoints_noisy[i].get_x() + noise());
171  bunnyModelPoints_noisy[i].set_y(bunnyModelPoints_noisy[i].get_y() + noise());
172  vectorOfOutlierFlags[i] = true;
173  }
174 
175  if (test_duplicate) {
176  // Add some duplicate points
177  size_t nbDuplicatePoints = 100;
178  for (size_t i = 0; i < nbDuplicatePoints; i++) {
179  size_t index = (size_t)rand() % bunnyModelPoints_noisy.size();
180  vpPoint duplicatePoint = bunnyModelPoints_noisy[index];
181  bunnyModelPoints_noisy.push_back(duplicatePoint);
182  vectorOfOutlierFlags.push_back(true);
183  }
184  }
185 
186  if (test_degenerate) {
187  // Add some degenerate points
188  size_t nbDegeneratePoints = 100;
189  double degenerate_tolerence = 9.999e-7; // 1e-6 is used in the code to
190  // detect if a point is degenerate
191  // or not
192  std::vector<vpPoint> listOfDegeneratePoints;
193  for (size_t i = 0; i < nbDegeneratePoints; i++) {
194  size_t index = (size_t)rand() % bunnyModelPoints_noisy.size();
195  vpPoint degeneratePoint = bunnyModelPoints_noisy[index];
196 
197  // Object point is degenerate
198  degeneratePoint.set_oX(degeneratePoint.get_oX() + degenerate_tolerence);
199  degeneratePoint.set_oY(degeneratePoint.get_oY() + degenerate_tolerence);
200  degeneratePoint.set_oZ(degeneratePoint.get_oZ() - degenerate_tolerence);
201 
202  // Add duplicate 3D points
203  listOfDegeneratePoints.push_back(degeneratePoint);
204 
205  // Image point is degenerate
206  index = (size_t)rand() % bunnyModelPoints_noisy.size();
207  degeneratePoint = bunnyModelPoints_noisy[index];
208 
209  degeneratePoint.set_x(degeneratePoint.get_x() + degenerate_tolerence);
210  degeneratePoint.set_y(degeneratePoint.get_y() - degenerate_tolerence);
211 
212  // Add duplicate 2D points
213  listOfDegeneratePoints.push_back(degeneratePoint);
214  }
215 
216  for (std::vector<vpPoint>::const_iterator it_degenerate = listOfDegeneratePoints.begin();
217  it_degenerate != listOfDegeneratePoints.end(); ++it_degenerate) {
218  bunnyModelPoints_noisy.push_back(*it_degenerate);
219  vectorOfOutlierFlags.push_back(true);
220  }
221  }
222 
223  // Shuffle the data vector
224  std::vector<size_t> vectorOfIndex(bunnyModelPoints_noisy.size());
225  for (size_t i = 0; i < vectorOfIndex.size(); i++) {
226  vectorOfIndex[i] = i;
227  }
228 
229  // std::random_shuffle(vectorOfIndex.begin(), vectorOfIndex.end()); // std::random_shuffle is deprecated in C++14
230  std::random_device rng;
231  std::mt19937 urng(rng());
232  std::shuffle(vectorOfIndex.begin(), vectorOfIndex.end(), urng);
233 
234  std::vector<vpPoint> bunnyModelPoints_noisy_tmp = bunnyModelPoints_noisy;
235  bunnyModelPoints_noisy.clear();
236  std::vector<bool> vectorOfOutlierFlags_tmp = vectorOfOutlierFlags;
237  vectorOfOutlierFlags.clear();
238  for (std::vector<size_t>::const_iterator it = vectorOfIndex.begin(); it != vectorOfIndex.end(); ++it) {
239  bunnyModelPoints_noisy.push_back(bunnyModelPoints_noisy_tmp[*it]);
240  vectorOfOutlierFlags.push_back(vectorOfOutlierFlags_tmp[*it]);
241  }
242 
243  // Add data to vpPose
244  vpPose pose;
245  vpPose pose_ransac, pose_ransac2;
246 
247  vpPose pose_ransac_parallel, pose_ransac_parallel2;
248  pose_ransac_parallel.setUseParallelRansac(true);
249  pose_ransac_parallel2.setUseParallelRansac(true);
250 
255  for (std::vector<vpPoint>::const_iterator it = bunnyModelPoints_noisy.begin(); it != bunnyModelPoints_noisy.end();
256  ++it) {
257  pose.addPoint(*it);
258  }
259  // Test addPoints
260  pose_ransac.addPoints(bunnyModelPoints_noisy);
261  pose_ransac2.addPoints(bunnyModelPoints_noisy);
262  pose_ransac_parallel.addPoints(bunnyModelPoints_noisy);
263  pose_ransac_parallel2.addPoints(bunnyModelPoints_noisy);
264 
265  // Print the number of points in the final data vector
266  std::cout << "\nNumber of model points in the noisy data vector: " << bunnyModelPoints_noisy.size() << " points."
267  << std::endl
268  << std::endl;
269 
270  unsigned int nbInlierToReachConsensus = (unsigned int)(60.0 * (double)(bunnyModelPoints_noisy.size()) / 100.0);
271  double threshold = 0.001;
272 
273  // RANSAC with 1000 iterations
274  pose_ransac.setRansacNbInliersToReachConsensus(nbInlierToReachConsensus);
275  pose_ransac.setRansacThreshold(threshold);
276  pose_ransac.setRansacMaxTrials(1000);
277  pose_ransac_parallel.setRansacNbInliersToReachConsensus(nbInlierToReachConsensus);
278  pose_ransac_parallel.setRansacThreshold(threshold);
279  pose_ransac_parallel.setRansacMaxTrials(1000);
280 
281  pose_ransac_parallel2.setRansacNbInliersToReachConsensus(nbInlierToReachConsensus);
282  pose_ransac_parallel2.setRansacThreshold(threshold);
283  pose_ransac_parallel2.setRansacMaxTrials(vpPose::computeRansacIterations(0.99, 0.4, 4, -1));
284 
285  // RANSAC with p=0.99, epsilon=0.4
286  pose_ransac2.setRansacNbInliersToReachConsensus(nbInlierToReachConsensus);
287  pose_ransac2.setRansacThreshold(threshold);
288  int ransac_iterations = vpPose::computeRansacIterations(0.99, 0.4, 4, -1);
289  pose_ransac2.setRansacMaxTrials(ransac_iterations);
290  std::cout << "Number of RANSAC iterations to ensure p=0.99 and epsilon=0.4: " << ransac_iterations << std::endl;
291 
292  vpHomogeneousMatrix cMo_estimated_RANSAC;
293  vpChrono chrono_RANSAC;
294  chrono_RANSAC.start();
295  pose_ransac.computePose(vpPose::RANSAC, cMo_estimated_RANSAC);
296  chrono_RANSAC.stop();
297 
298  std::cout << "\ncMo estimated with RANSAC (1000 iterations) on noisy data:\n" << cMo_estimated_RANSAC << std::endl;
299  std::cout << "Computation time: " << chrono_RANSAC.getDurationMs() << " ms" << std::endl;
300 
301  double r_RANSAC_estimated = ground_truth_pose.computeResidual(cMo_estimated_RANSAC);
302  std::cout << "Corresponding residual (1000 iterations): " << r_RANSAC_estimated << std::endl;
303 
304  vpHomogeneousMatrix cMo_estimated_RANSAC_2;
305  chrono_RANSAC.start();
306  pose_ransac2.computePose(vpPose::RANSAC, cMo_estimated_RANSAC_2);
307  chrono_RANSAC.stop();
308 
309  std::cout << "\ncMo estimated with RANSAC (" << ransac_iterations << " iterations) on noisy data:\n"
310  << cMo_estimated_RANSAC_2 << std::endl;
311  std::cout << "Computation time: " << chrono_RANSAC.getDurationMs() << " ms" << std::endl;
312 
313  double r_RANSAC_estimated_2 = ground_truth_pose.computeResidual(cMo_estimated_RANSAC_2);
314  std::cout << "Corresponding residual (" << ransac_iterations << " iterations): " << r_RANSAC_estimated_2 << std::endl;
315 
317  std::cout << "\ncMo estimated with only VVS on noisy data:\n" << cMo_estimated << std::endl;
318 
319  double r_estimated = ground_truth_pose.computeResidual(cMo_estimated);
320  std::cout << "Corresponding residual: " << r_estimated << std::endl;
321 
322  vpHomogeneousMatrix cMo_estimated_RANSAC_parallel;
323  vpChrono chrono_RANSAC_parallel;
324  chrono_RANSAC_parallel.start();
325  pose_ransac_parallel.computePose(vpPose::RANSAC, cMo_estimated_RANSAC_parallel);
326  chrono_RANSAC_parallel.stop();
327 
328  std::cout << "\ncMo estimated with parallel RANSAC (1000 iterations) on "
329  "noisy data:\n"
330  << cMo_estimated_RANSAC_parallel << std::endl;
331  std::cout << "Computation time: " << chrono_RANSAC_parallel.getDurationMs() << " ms" << std::endl;
332 
333  double r_RANSAC_estimated_parallel = ground_truth_pose.computeResidual(cMo_estimated_RANSAC_parallel);
334  std::cout << "Corresponding residual (1000 iterations): " << r_RANSAC_estimated_parallel << std::endl;
335 
336  vpHomogeneousMatrix cMo_estimated_RANSAC_parallel2;
337  vpChrono chrono_RANSAC_parallel2;
338  chrono_RANSAC_parallel2.start();
339  pose_ransac_parallel2.computePose(vpPose::RANSAC, cMo_estimated_RANSAC_parallel2);
340  chrono_RANSAC_parallel2.stop();
341 
342  std::cout << "\ncMo estimated with parallel RANSAC (" << ransac_iterations << " iterations) on noisy data:\n"
343  << cMo_estimated_RANSAC_parallel2 << std::endl;
344  std::cout << "Computation time: " << chrono_RANSAC_parallel2.getDurationMs() << " ms" << std::endl;
345 
346  double r_RANSAC_estimated_parallel2 = ground_truth_pose.computeResidual(cMo_estimated_RANSAC_parallel2);
347  std::cout << "Corresponding residual (" << ransac_iterations << " iterations): " << r_RANSAC_estimated_parallel2
348  << std::endl;
349 
350  // Check inlier index
351  std::vector<unsigned int> vectorOfFoundInlierIndex = pose_ransac.getRansacInlierIndex();
352  int nbInlierIndexOk = checkInlierIndex(vectorOfFoundInlierIndex, vectorOfOutlierFlags);
353 
354  int nbTrueInlierIndex = (int)std::count(vectorOfOutlierFlags.begin(), vectorOfOutlierFlags.end(), false);
355  std::cout << "\nThere are " << nbInlierIndexOk << " true inliers found, " << vectorOfFoundInlierIndex.size()
356  << " inliers returned and " << nbTrueInlierIndex << " true inliers." << std::endl;
357 
358  // Check inlier points returned
359  std::vector<vpPoint> vectorOfFoundInlierPoints = pose_ransac.getRansacInliers();
360 
361  if (vectorOfFoundInlierPoints.size() != vectorOfFoundInlierIndex.size()) {
362  std::cerr << "The number of inlier index is different from the number of "
363  "inlier points!"
364  << std::endl;
365  return false;
366  }
367  if (!checkInlierPoints(vectorOfFoundInlierPoints, vectorOfFoundInlierIndex, bunnyModelPoints_noisy)) {
368  return false;
369  }
370 
371  // Check for RANSAC with p=0.99, epsilon=0.4
372  // Check inlier index
373  std::cout << "\nCheck for RANSAC iterations: " << ransac_iterations << std::endl;
374  std::vector<unsigned int> vectorOfFoundInlierIndex_2 = pose_ransac2.getRansacInlierIndex();
375  nbInlierIndexOk = checkInlierIndex(vectorOfFoundInlierIndex_2, vectorOfOutlierFlags);
376 
377  std::cout << "There are " << nbInlierIndexOk << " true inliers found, " << vectorOfFoundInlierIndex_2.size()
378  << " inliers returned and " << nbTrueInlierIndex << " true inliers." << std::endl;
379 
380  // Check inlier points returned
381  std::vector<vpPoint> vectorOfFoundInlierPoints_2 = pose_ransac2.getRansacInliers();
382  if (vectorOfFoundInlierPoints_2.size() != vectorOfFoundInlierIndex_2.size()) {
383  std::cerr << "The number of inlier index is different from the number of "
384  "inlier points!"
385  << std::endl;
386  return false;
387  }
388  if (!checkInlierPoints(vectorOfFoundInlierPoints_2, vectorOfFoundInlierIndex_2, bunnyModelPoints_noisy)) {
389  return false;
390  }
391 
392  // Check for parallel RANSAC
393  // Check inlier index
394  std::cout << "\nCheck for parallel RANSAC (1000 iterations)" << std::endl;
395  std::vector<unsigned int> vectorOfFoundInlierIndex_parallel = pose_ransac_parallel.getRansacInlierIndex();
396  nbInlierIndexOk = checkInlierIndex(vectorOfFoundInlierIndex_parallel, vectorOfOutlierFlags);
397 
398  std::cout << "There are " << nbInlierIndexOk << " true inliers found, " << vectorOfFoundInlierIndex_parallel.size()
399  << " inliers returned and " << nbTrueInlierIndex << " true inliers." << std::endl;
400 
401  // Check inlier points returned
402  std::vector<vpPoint> vectorOfFoundInlierPoints_parallel = pose_ransac_parallel.getRansacInliers();
403  if (vectorOfFoundInlierPoints_parallel.size() != vectorOfFoundInlierIndex_parallel.size()) {
404  std::cerr << "The number of inlier index is different from the number "
405  "of inlier points!"
406  << std::endl;
407  return false;
408  }
409  if (!checkInlierPoints(vectorOfFoundInlierPoints_parallel, vectorOfFoundInlierIndex_parallel,
410  bunnyModelPoints_noisy)) {
411  return false;
412  }
413 
414  // Check for parallel RANSAC 2
415  // Check inlier index
416  std::cout << "\nCheck for parallel RANSAC (" << ransac_iterations << " iterations)" << std::endl;
417  std::vector<unsigned int> vectorOfFoundInlierIndex_parallel2 = pose_ransac_parallel2.getRansacInlierIndex();
418  nbInlierIndexOk = checkInlierIndex(vectorOfFoundInlierIndex_parallel2, vectorOfOutlierFlags);
419 
420  std::cout << "There are " << nbInlierIndexOk << " true inliers found, " << vectorOfFoundInlierIndex_parallel2.size()
421  << " inliers returned and " << nbTrueInlierIndex << " true inliers." << std::endl;
422 
423  // Check inlier points returned
424  std::vector<vpPoint> vectorOfFoundInlierPoints_parallel2 = pose_ransac_parallel2.getRansacInliers();
425  if (vectorOfFoundInlierPoints_parallel2.size() != vectorOfFoundInlierIndex_parallel2.size()) {
426  std::cerr << "The number of inlier index is different from the number "
427  "of inlier points!"
428  << std::endl;
429  return false;
430  }
431  if (!checkInlierPoints(vectorOfFoundInlierPoints_parallel2, vectorOfFoundInlierIndex_parallel2,
432  bunnyModelPoints_noisy)) {
433  return false;
434  }
435 
436  if (r_RANSAC_estimated > threshold /*|| r_RANSAC_estimated_2 > threshold*/) {
437  std::cerr << "The pose estimated with the RANSAC method is badly estimated!" << std::endl;
438  std::cerr << "r_RANSAC_estimated=" << r_RANSAC_estimated << std::endl;
439  std::cerr << "threshold=" << threshold << std::endl;
440  return false;
441  }
442  else {
443  if (r_RANSAC_estimated_parallel > threshold) {
444  std::cerr << "The pose estimated with the parallel RANSAC method is "
445  "badly estimated!"
446  << std::endl;
447  std::cerr << "r_RANSAC_estimated_parallel=" << r_RANSAC_estimated_parallel << std::endl;
448  std::cerr << "threshold=" << threshold << std::endl;
449  return false;
450  }
451  std::cout << "The pose estimated with the RANSAC method is well estimated!" << std::endl;
452  }
453 
454  return true;
455 }
456 #endif
457 } // namespace
458 
459 TEST_CASE("Print RANSAC number of iterations", "[ransac_pose]")
460 {
461  const int sample_sizes[] = { 2, 3, 4, 5, 6, 7, 8 };
462  const double epsilon[] = { 0.05, 0.1, 0.2, 0.25, 0.3, 0.4, 0.5 };
463 
464  // Format output
465  const std::string spacing = " ";
466 
467  std::cout << spacing << " outliers percentage\n"
468  << "nb pts\\";
469  for (int cpt2 = 0; cpt2 < 7; cpt2++) {
470  std::cout << std::setfill(' ') << std::setw(5) << epsilon[cpt2] << " ";
471  }
472  std::cout << std::endl;
473 
474  std::cout << std::setfill(' ') << std::setw(7) << "+";
475  for (int cpt2 = 0; cpt2 < 6; cpt2++) {
476  std::cout << std::setw(7) << "-------";
477  }
478  std::cout << std::endl;
479 
480  for (int cpt1 = 0; cpt1 < 7; cpt1++) {
481  std::cout << std::setfill(' ') << std::setw(6) << sample_sizes[cpt1] << "|";
482 
483  for (int cpt2 = 0; cpt2 < 7; cpt2++) {
484  int ransac_iters = vpPose::computeRansacIterations(0.99, epsilon[cpt2], sample_sizes[cpt1], -1);
485  std::cout << std::setfill(' ') << std::setw(6) << ransac_iters;
486  }
487  std::cout << std::endl;
488  }
489  std::cout << std::endl;
490 }
491 
492 #if (VISP_HAVE_DATASET_VERSION >= 0x030300)
493 TEST_CASE("RANSAC pose estimation tests", "[ransac_pose]")
494 {
495  const std::vector<size_t> model_sizes = { 10, 20, 50, 100, 200, 500, 1000, 0, 0 };
496  const std::vector<bool> duplicates = { false, false, false, false, false, false, false, false, true };
497  const std::vector<bool> degenerates = { false, false, false, false, false, false, true, true, true };
498 
499  std::string visp_input_images = vpIoTools::getViSPImagesDataPath();
500  std::string model_filename = vpIoTools::createFilePath(visp_input_images, "3dmodel/bunny/bunny.xyz");
501  CHECK(vpIoTools::checkFilename(model_filename));
502 
503  std::vector<vpPoint> bunnyModelPoints, bunnyModelPoints_noisy_original;
504  readBunnyModelPoints(model_filename, bunnyModelPoints, bunnyModelPoints_noisy_original);
505  CHECK(bunnyModelPoints.size() == bunnyModelPoints_noisy_original.size());
506 
507  for (size_t i = 0; i < model_sizes.size(); i++) {
508  std::cout << "\n\n===============================================================================" << std::endl;
509  if (model_sizes[i] == 0) {
510  std::cout << "Test on " << bunnyModelPoints_noisy_original.size() << " model points." << std::endl;
511  }
512  else {
513  std::cout << "Test on " << model_sizes[i] << " model points." << std::endl;
514  }
515  std::cout << "Test duplicate: " << duplicates[i] << " ; Test degenerate: " << degenerates[i] << std::endl;
516 
517  CHECK(testRansac(bunnyModelPoints, bunnyModelPoints_noisy_original, model_sizes[i], duplicates[i], degenerates[i]));
518  }
519 }
520 #endif
521 
522 int main(int argc, char *argv[])
523 {
524 #if defined(__mips__) || defined(__mips) || defined(mips) || defined(__MIPS__)
525  // To avoid Debian test timeout
526  return EXIT_SUCCESS;
527 #endif
528 
529 #if (defined(VISP_HAVE_LAPACK) || defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_OPENCV))
530 
531  Catch::Session session; // There must be exactly one instance
532 
533  // Let Catch (using Clara) parse the command line
534  session.applyCommandLine(argc, argv);
535 
536  int numFailed = session.run();
537 
538  // numFailed is clamped to 255 as some unices only use the lower 8 bits.
539  // This clamping has already been applied, so just return it here
540  // You can also do any post run clean-up here
541  return numFailed;
542 #else
543  std::cout << "Cannot run this example: install Lapack, Eigen3 or OpenCV" << std::endl;
544  return EXIT_SUCCESS;
545 #endif
546 }
547 #else
548 int main() { return EXIT_SUCCESS; }
549 #endif
void start(bool reset=true)
Definition: vpTime.cpp:401
void stop()
Definition: vpTime.cpp:416
double getDurationMs()
Definition: vpTime.cpp:390
Class for generating random number with normal probability density.
Definition: vpGaussRand.h:117
Implementation of an homogeneous matrix and operations on such kind of matrices.
static std::string getViSPImagesDataPath()
Definition: vpIoTools.cpp:1053
static bool checkFilename(const std::string &filename)
Definition: vpIoTools.cpp:786
static std::string createFilePath(const std::string &parent, const std::string &child)
Definition: vpIoTools.cpp:1427
static double rad(double deg)
Definition: vpMath.h:129
static bool equal(double x, double y, double threshold=0.001)
Definition: vpMath.h:459
Class that defines a 3D point in the object frame and allows forward projection of a 3D point in the ...
Definition: vpPoint.h:79
double get_oX() const
Get the point oX coordinate in the object frame.
Definition: vpPoint.cpp:411
void set_x(double x)
Set the point x coordinate in the image plane.
Definition: vpPoint.cpp:464
double get_y() const
Get the point y coordinate in the image plane.
Definition: vpPoint.cpp:422
double get_oZ() const
Get the point oZ coordinate in the object frame.
Definition: vpPoint.cpp:415
void set_oY(double oY)
Set the point oY coordinate in the object frame.
Definition: vpPoint.cpp:457
double get_x() const
Get the point x coordinate in the image plane.
Definition: vpPoint.cpp:420
void set_oZ(double oZ)
Set the point oZ coordinate in the object frame.
Definition: vpPoint.cpp:459
void set_oX(double oX)
Set the point oX coordinate in the object frame.
Definition: vpPoint.cpp:455
double get_oY() const
Get the point oY coordinate in the object frame.
Definition: vpPoint.cpp:413
void set_y(double y)
Set the point y coordinate in the image plane.
Definition: vpPoint.cpp:466
Class used for pose computation from N points (pose from point only). Some of the algorithms implemen...
Definition: vpPose.h:77
void setRansacMaxTrials(const int &rM)
Definition: vpPose.h:406
static int computeRansacIterations(double probability, double epsilon, const int sampleSize=4, int maxIterations=2000)
void addPoint(const vpPoint &P)
Definition: vpPose.cpp:96
void setRansacNbInliersToReachConsensus(const unsigned int &nbC)
Definition: vpPose.h:387
@ RANSAC
Definition: vpPose.h:87
@ DEMENTHON_LAGRANGE_VIRTUAL_VS
Definition: vpPose.h:98
bool computePose(vpPoseMethodType method, vpHomogeneousMatrix &cMo, FuncCheckValidityPose func=nullptr)
Definition: vpPose.cpp:385
std::vector< unsigned int > getRansacInlierIndex() const
Definition: vpPose.h:416
void addPoints(const std::vector< vpPoint > &lP)
Definition: vpPose.cpp:103
double computeResidual(const vpHomogeneousMatrix &cMo) const
Compute and return the sum of squared residuals expressed in meter^2 for the pose matrix cMo.
Definition: vpPose.cpp:298
void setRansacFilterFlag(const RANSAC_FILTER_FLAGS &flag)
Definition: vpPose.h:459
@ PREFILTER_DEGENERATE_POINTS
Definition: vpPose.h:109
void setUseParallelRansac(bool use)
Definition: vpPose.h:490
std::vector< vpPoint > getRansacInliers() const
Definition: vpPose.h:421
void setRansacThreshold(const double &t)
Definition: vpPose.h:392
Implementation of a rotation vector as Euler angle minimal representation.
Definition: vpRzyxVector.h:184
Implementation of a rotation vector as axis-angle minimal representation.
Class that consider the case of a translation vector.