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