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