Visual Servoing Platform  version 3.1.0
vpPoseRansac.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ViSP software.
4  * Copyright (C) 2005 - 2017 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  * Pose computation.
33  *
34  * Authors:
35  * Eric Marchand
36  * Aurelien Yol
37  * Souriya Trinh
38  *
39  *****************************************************************************/
40 
46 #include <algorithm> // std::count
47 #include <cmath> // std::fabs
48 #include <float.h> // DBL_MAX
49 #include <iostream>
50 #include <limits> // numeric_limits
51 #include <map>
52 #include <stdlib.h>
53 
54 #include <visp3/core/vpColVector.h>
55 #include <visp3/core/vpMath.h>
56 #include <visp3/core/vpRansac.h>
57 #include <visp3/vision/vpPose.h>
58 #include <visp3/vision/vpPoseException.h>
59 
60 #if defined(VISP_HAVE_CPP11_COMPATIBILITY)
61 #include <unordered_map>
62 #endif
63 
64 #if defined(VISP_HAVE_OPENMP)
65 #include <omp.h>
66 #endif
67 
68 #define eps 1e-6
69 
70 namespace
71 {
72 // For std::map<vpPoint>
73 struct ComparePointDuplicate {
74  bool operator()(const vpPoint &point1, const vpPoint &point2) const
75  {
76  if (point1.oP[0] < point2.oP[0])
77  return true;
78  if (point1.oP[0] > point2.oP[0])
79  return false;
80 
81  if (point1.oP[1] < point2.oP[1])
82  return true;
83  if (point1.oP[1] > point2.oP[1])
84  return false;
85 
86  if (point1.oP[2] < point2.oP[2])
87  return true;
88  if (point1.oP[2] > point2.oP[2])
89  return false;
90 
91  if (point1.p[0] < point2.p[0])
92  return true;
93  if (point1.p[0] > point2.p[0])
94  return false;
95 
96  if (point1.p[1] < point2.p[1])
97  return true;
98  if (point1.p[1] > point2.p[1])
99  return false;
100 
101  return false;
102  }
103 };
104 
105 // For std::map<vpPoint>
106 struct ComparePointAlmostDuplicate {
107  bool operator()(const vpPoint &point1, const vpPoint &point2) const
108  {
109  if (point1.oP[0] - point2.oP[0] < -eps)
110  return true;
111  if (point1.oP[0] - point2.oP[0] > eps)
112  return false;
113 
114  if (point1.oP[1] - point2.oP[1] < -eps)
115  return true;
116  if (point1.oP[1] - point2.oP[1] > eps)
117  return false;
118 
119  if (point1.oP[2] - point2.oP[2] < -eps)
120  return true;
121  if (point1.oP[2] - point2.oP[2] > eps)
122  return false;
123 
124  if (point1.p[0] - point2.p[0] < -eps)
125  return true;
126  if (point1.p[0] - point2.p[0] > eps)
127  return false;
128 
129  if (point1.p[1] - point2.p[1] < -eps)
130  return true;
131  if (point1.p[1] - point2.p[1] > eps)
132  return false;
133 
134  return false;
135  }
136 };
137 
138 // For std::map<vpPoint>
139 struct CompareObjectPointDegenerate {
140  bool operator()(const vpPoint &point1, const vpPoint &point2) const
141  {
142  if (point1.oP[0] - point2.oP[0] < -eps)
143  return true;
144  if (point1.oP[0] - point2.oP[0] > eps)
145  return false;
146 
147  if (point1.oP[1] - point2.oP[1] < -eps)
148  return true;
149  if (point1.oP[1] - point2.oP[1] > eps)
150  return false;
151 
152  if (point1.oP[2] - point2.oP[2] < -eps)
153  return true;
154  if (point1.oP[2] - point2.oP[2] > eps)
155  return false;
156 
157  return false;
158  }
159 };
160 
161 // For std::map<vpPoint>
162 struct CompareImagePointDegenerate {
163  bool operator()(const vpPoint &point1, const vpPoint &point2) const
164  {
165  if (point1.p[0] - point2.p[0] < -eps)
166  return true;
167  if (point1.p[0] - point2.p[0] > eps)
168  return false;
169 
170  if (point1.p[1] - point2.p[1] < -eps)
171  return true;
172  if (point1.p[1] - point2.p[1] > eps)
173  return false;
174 
175  return false;
176  }
177 };
178 
179 // std::find_if
180 struct FindDegeneratePoint {
181  explicit FindDegeneratePoint(const vpPoint &pt) : m_pt(pt) {}
182 
183  bool operator()(const vpPoint &pt)
184  {
185  return ((std::fabs(m_pt.oP[0] - pt.oP[0]) < eps && std::fabs(m_pt.oP[1] - pt.oP[1]) < eps &&
186  std::fabs(m_pt.oP[2] - pt.oP[2]) < eps) ||
187  (std::fabs(m_pt.p[0] - pt.p[0]) < eps && std::fabs(m_pt.p[1] - pt.p[1]) < eps));
188  }
189 
190  vpPoint m_pt;
191 };
192 
193 #if defined(VISP_HAVE_CPP11_COMPATIBILITY)
194 // For unordered_map<vpPoint>
195 struct HashDuplicate {
196  std::size_t operator()(const vpPoint &point) const
197  {
198  using std::size_t;
199  using std::hash;
200 
201  size_t res = 17;
202  res = res * 31 + hash<double>()(point.oP[0]);
203  res = res * 31 + hash<double>()(point.oP[1]);
204  res = res * 31 + hash<double>()(point.oP[2]);
205  res = res * 31 + hash<double>()(point.p[0]);
206  res = res * 31 + hash<double>()(point.p[1]);
207 
208  return res;
209  }
210 };
211 
212 // For unordered_map<vpPoint>
213 struct ComparePointDuplicateUnorderedMap {
214  bool operator()(const vpPoint &point1, const vpPoint &point2) const
215  {
216  return (std::fabs(point1.oP[0] - point2.oP[0]) < std::numeric_limits<double>::epsilon() &&
217  std::fabs(point1.oP[1] - point2.oP[1]) < std::numeric_limits<double>::epsilon() &&
218  std::fabs(point1.oP[2] - point2.oP[2]) < std::numeric_limits<double>::epsilon() &&
219  std::fabs(point1.p[0] - point2.p[0]) < std::numeric_limits<double>::epsilon() &&
220  std::fabs(point1.p[1] - point2.p[1]) < std::numeric_limits<double>::epsilon());
221  }
222 };
223 #endif
224 }
225 
226 bool vpPose::RansacFunctor::poseRansacImpl()
227 {
228  unsigned int size = (unsigned int)m_listOfUniquePoints.size();
229  int nbTrials = 0;
230  unsigned int nbMinRandom = 4;
231 
232 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
233  srand(m_initial_seed);
234 #endif
235 
236  vpPoint p; // Point used to project using the estimated pose
237 
238  bool foundSolution = false;
239  while (nbTrials < m_ransacMaxTrials && m_nbInliers < (unsigned int)m_ransacNbInlierConsensus) {
240  // Hold the list of the index of the inliers (points in the consensus set)
241  std::vector<unsigned int> cur_consensus;
242  // Hold the list of the index of the outliers
243  std::vector<unsigned int> cur_outliers;
244  // Hold the list of the index of the points randomly picked
245  std::vector<unsigned int> cur_randoms;
246  // Hold the list of the current inliers points to avoid to add a
247  // degenerate point if the flag is set
248  std::vector<vpPoint> cur_inliers;
249 
250  vpHomogeneousMatrix cMo_lagrange, cMo_dementhon;
251  // Use a temporary variable because if not, the cMo passed in parameters
252  // will be modified when
253  // we compute the pose for the minimal sample sets but if the pose is not
254  // correct when we pass a function pointer we do not want to modify the
255  // cMo passed in parameters
256  vpHomogeneousMatrix cMo_tmp;
257 
258  // Vector of used points, initialized at false for all points
259  std::vector<bool> usedPt(size, false);
260 
261  vpPose poseMin;
262  for (unsigned int i = 0; i < nbMinRandom;) {
263  if ((size_t)std::count(usedPt.begin(), usedPt.end(), true) == usedPt.size()) {
264  // All points was picked once, break otherwise we stay in an infinite
265  // loop
266  break;
267  }
268 
269 // Pick a point randomly
270 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
271  unsigned int r_ = (unsigned int)rand() % size;
272 #else
273  unsigned int r_ = (unsigned int)rand_r(&m_initial_seed) % size;
274 #endif
275 
276  while (usedPt[r_]) {
277 // If already picked, pick another point randomly
278 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
279  r_ = (unsigned int)rand() % size;
280 #else
281  r_ = (unsigned int)rand_r(&m_initial_seed) % size;
282 #endif
283  }
284  // Mark this point as already picked
285  usedPt[r_] = true;
286  vpPoint pt = m_listOfUniquePoints[r_];
287 
288  bool degenerate = false;
289  if (m_checkDegeneratePoints) {
290  if (std::find_if(poseMin.listOfPoints.begin(), poseMin.listOfPoints.end(), FindDegeneratePoint(pt)) !=
291  poseMin.listOfPoints.end()) {
292  degenerate = true;
293  }
294  }
295 
296  if (!degenerate) {
297  poseMin.addPoint(pt);
298  cur_randoms.push_back(r_);
299  // Increment the number of points picked
300  i++;
301  }
302  }
303 
304  if (poseMin.npt < nbMinRandom) {
305  nbTrials++;
306  continue;
307  }
308 
309  // Flags set if pose computation is OK
310  bool is_valid_lagrange = false;
311  bool is_valid_dementhon = false;
312 
313  // Set maximum value for residuals
314  double r_lagrange = DBL_MAX;
315  double r_dementhon = DBL_MAX;
316 
317  try {
318  poseMin.computePose(vpPose::LAGRANGE, cMo_lagrange);
319  r_lagrange = poseMin.computeResidual(cMo_lagrange);
320  is_valid_lagrange = true;
321  } catch (...) {
322  }
323 
324  try {
325  poseMin.computePose(vpPose::DEMENTHON, cMo_dementhon);
326  r_dementhon = poseMin.computeResidual(cMo_dementhon);
327  is_valid_dementhon = true;
328  } catch (...) {
329  }
330 
331  // If residual returned is not a number (NAN), set valid to false
332  if (vpMath::isNaN(r_lagrange)) {
333  is_valid_lagrange = false;
334  r_lagrange = DBL_MAX;
335  }
336 
337  if (vpMath::isNaN(r_dementhon)) {
338  is_valid_dementhon = false;
339  r_dementhon = DBL_MAX;
340  }
341 
342  // If at least one pose computation is OK,
343  // we can continue, otherwise pick another random set
344  if (is_valid_lagrange || is_valid_dementhon) {
345  double r;
346  if (r_lagrange < r_dementhon) {
347  r = r_lagrange;
348  cMo_tmp = cMo_lagrange;
349  } else {
350  r = r_dementhon;
351  cMo_tmp = cMo_dementhon;
352  }
353  r = sqrt(r) / (double)nbMinRandom;
354 
355  // Filter the pose using some criterion (orientation angles,
356  // translations, etc.)
357  bool isPoseValid = true;
358  if (m_func != NULL) {
359  isPoseValid = m_func(&cMo_tmp);
360  if (isPoseValid) {
361  m_cMo = cMo_tmp;
362  }
363  } else {
364  // No post filtering on pose, so copy cMo_temp to cMo
365  m_cMo = cMo_tmp;
366  }
367 
368  if (isPoseValid && r < m_ransacThreshold) {
369  unsigned int nbInliersCur = 0;
370  unsigned int iter = 0;
371  for (std::vector<vpPoint>::const_iterator it = m_listOfUniquePoints.begin(); it != m_listOfUniquePoints.end();
372  ++it, iter++) {
373  p.setWorldCoordinates(it->get_oX(), it->get_oY(), it->get_oZ());
374  p.track(m_cMo);
375 
376  double d = vpMath::sqr(p.get_x() - it->get_x()) + vpMath::sqr(p.get_y() - it->get_y());
377  double error = sqrt(d);
378  if (error < m_ransacThreshold) {
379  bool degenerate = false;
380  if (m_checkDegeneratePoints) {
381  if (std::find_if(cur_inliers.begin(), cur_inliers.end(), FindDegeneratePoint(*it)) != cur_inliers.end()) {
382  degenerate = true;
383  }
384  }
385 
386  if (!degenerate) {
387  // the point is considered as inlier if the error is below the
388  // threshold
389  nbInliersCur++;
390  cur_consensus.push_back(iter);
391  cur_inliers.push_back(*it);
392  } else {
393  cur_outliers.push_back(iter);
394  }
395  } else {
396  cur_outliers.push_back(iter);
397  }
398  }
399 
400  if (nbInliersCur > m_nbInliers) {
401  foundSolution = true;
402  m_best_consensus = cur_consensus;
403  m_nbInliers = nbInliersCur;
404  }
405 
406  nbTrials++;
407 
408  if (nbTrials >= m_ransacMaxTrials) {
409  foundSolution = true;
410  }
411  } else {
412  nbTrials++;
413  }
414  } else {
415  nbTrials++;
416  }
417  }
418 
419  return foundSolution;
420 }
421 
422 #if defined(VISP_HAVE_PTHREAD) || (defined(_WIN32) && !defined(WINRT_8_0))
423 vpThread::Return vpPose::poseRansacImplThread(vpThread::Args arg)
424 {
425  vpPose::RansacFunctor *f = reinterpret_cast<vpPose::RansacFunctor *>(arg);
426  (*f)();
427  return 0;
428 }
429 #endif
430 
441 {
442  // Check only for adding / removing problem
443  // Do not take into account problem with element modification here
444  if (listP.size() != listOfPoints.size()) {
445  std::cerr << "You should not modify vpPose::listP!" << std::endl;
446  listOfPoints = std::vector<vpPoint>(listP.begin(), listP.end());
447  }
448 
449  ransacInliers.clear();
450  ransacInlierIndex.clear();
451 
452  std::vector<unsigned int> best_consensus;
453  unsigned int nbInliers = 0;
454 
455  vpHomogeneousMatrix cMo_lagrange, cMo_dementhon;
456 
457  if (listOfPoints.size() < 4) {
458  // vpERROR_TRACE("Not enough point to compute the pose");
459  throw(vpPoseException(vpPoseException::notInitializedError, "Not enough point to compute the pose"));
460  }
461 
462  std::vector<vpPoint> listOfUniquePoints;
463  std::map<size_t, size_t> mapOfUniquePointIndex;
464 
465  // Get RANSAC flags
466  bool prefilterDuplicatePoints = (ransacFlags & PREFILTER_DUPLICATE_POINTS) != 0;
467  bool prefilterAlmostDuplicatePoints = (ransacFlags & PREFILTER_ALMOST_DUPLICATE_POINTS) != 0;
468  bool prefilterDegeneratePoints = (ransacFlags & PREFILTER_DEGENERATE_POINTS) != 0;
469  bool checkDegeneratePoints = (ransacFlags & CHECK_DEGENERATE_POINTS) != 0;
470 
471  if (prefilterDuplicatePoints || prefilterAlmostDuplicatePoints || prefilterDegeneratePoints) {
472  // Prefiltering
473  if (prefilterDuplicatePoints) {
474 #if defined(VISP_HAVE_CPP11_COMPATIBILITY)
475  std::unordered_map<vpPoint, size_t, HashDuplicate, ComparePointDuplicateUnorderedMap> filterMap;
476  size_t index_pt = 0;
477  for (std::vector<vpPoint>::const_iterator it_pt = listOfPoints.begin(); it_pt != listOfPoints.end();
478  ++it_pt, index_pt++) {
479  if (filterMap.find(*it_pt) == filterMap.end()) {
480  filterMap[*it_pt] = index_pt;
481 
482  listOfUniquePoints.push_back(*it_pt);
483  mapOfUniquePointIndex[listOfUniquePoints.size() - 1] = index_pt;
484  }
485  }
486 #else
487  std::map<vpPoint, size_t, ComparePointDuplicate> filterMap;
488  size_t index_pt = 0;
489  for (std::vector<vpPoint>::const_iterator it_pt = listOfPoints.begin(); it_pt != listOfPoints.end();
490  ++it_pt, index_pt++) {
491  if (filterMap.find(*it_pt) == filterMap.end()) {
492  filterMap[*it_pt] = index_pt;
493 
494  listOfUniquePoints.push_back(*it_pt);
495  mapOfUniquePointIndex[listOfUniquePoints.size() - 1] = index_pt;
496  }
497  }
498 #endif
499  } else if (prefilterAlmostDuplicatePoints) {
500  std::map<vpPoint, size_t, ComparePointAlmostDuplicate> filterMap;
501  size_t index_pt = 0;
502  for (std::vector<vpPoint>::const_iterator it_pt = listOfPoints.begin(); it_pt != listOfPoints.end();
503  ++it_pt, index_pt++) {
504  if (filterMap.find(*it_pt) == filterMap.end()) {
505  filterMap[*it_pt] = index_pt;
506 
507  listOfUniquePoints.push_back(*it_pt);
508  mapOfUniquePointIndex[listOfUniquePoints.size() - 1] = index_pt;
509  }
510  }
511  } else {
512  // Remove other degenerate object points
513  std::map<vpPoint, size_t, CompareObjectPointDegenerate> filterObjectPointMap;
514  size_t index_pt = 0;
515  for (std::vector<vpPoint>::const_iterator it_pt = listOfPoints.begin(); it_pt != listOfPoints.end();
516  ++it_pt, index_pt++) {
517  if (filterObjectPointMap.find(*it_pt) == filterObjectPointMap.end()) {
518  filterObjectPointMap[*it_pt] = index_pt;
519  }
520  }
521 
522  std::map<vpPoint, size_t, CompareImagePointDegenerate> filterImagePointMap;
523  for (std::map<vpPoint, size_t>::const_iterator it = filterObjectPointMap.begin();
524  it != filterObjectPointMap.end(); ++it) {
525  if (filterImagePointMap.find(it->first) == filterImagePointMap.end()) {
526  filterImagePointMap[it->first] = it->second;
527 
528  listOfUniquePoints.push_back(it->first);
529  mapOfUniquePointIndex[listOfUniquePoints.size() - 1] = it->second;
530  }
531  }
532  }
533  } else {
534  // No prefiltering
535  listOfUniquePoints = listOfPoints;
536 
537  size_t index_pt = 0;
538  for (std::vector<vpPoint>::const_iterator it_pt = listOfPoints.begin(); it_pt != listOfPoints.end();
539  ++it_pt, index_pt++) {
540  mapOfUniquePointIndex[index_pt] = index_pt;
541  }
542  }
543 
544  unsigned int size = (unsigned int)listOfUniquePoints.size();
545  if (size < 4) {
546  throw(vpPoseException(vpPoseException::notInitializedError, "Not enough point to compute the pose"));
547  }
548 
549  bool executeParallelVersion = useParallelRansac;
550 
551 #if defined(VISP_HAVE_PTHREAD) || (defined(_WIN32) && !defined(WINRT_8_0))
552 #define VP_THREAD_OK
553  int nbThreads = 1;
554 #endif
555 
556  if (executeParallelVersion) {
557 #if !defined(VP_THREAD_OK) && !defined(VISP_HAVE_OPENMP)
558  executeParallelVersion = false;
559  std::cerr << "Pthread or WIN32 API or OpenMP is needed to use the "
560  "parallel RANSAC version."
561  << std::endl;
562 #elif !defined(VP_THREAD_OK)
563 // Use OpenMP
564 #define PARALLEL_RANSAC_OPEN_MP
565 #elif !defined(VISP_HAVE_OPENMP)
566  if (nbParallelRansacThreads <= 0) {
567  // Cannot get the number of CPU threads so use the sequential mode
568  executeParallelVersion = false;
569  std::cerr << "OpenMP is needed to get the number of CPU threads so use "
570  "the sequential mode instead."
571  << std::endl;
572  } else {
573  nbThreads = nbParallelRansacThreads;
574  if (nbThreads == 1) {
575  executeParallelVersion = false;
576  }
577  }
578 #elif defined(VP_THREAD_OK) && defined(VISP_HAVE_OPENMP)
579  if (nbParallelRansacThreads <= 0) {
580  // Use OpenMP to get the number of CPU threads
581  nbThreads = omp_get_max_threads();
582  if (nbThreads <= 1) {
583  nbThreads = 1;
584  executeParallelVersion = false;
585  }
586  }
587 #endif
588  }
589 
590  bool foundSolution = false;
591 
592  if (executeParallelVersion) {
593 #if defined(PARALLEL_RANSAC_OPEN_MP)
594 // List of points picked randomly (minimal sample set, MSS)
595 // std::vector<unsigned int> best_randoms; // never used
596 
597 // Section of code run in parallel
598 // All variables declared before the parallel keyword are shared between the
599 // team of threads (if private keyword is not used) Code in parallel section
600 // are duplicated between the team of threads
601 #pragma omp parallel
602  {
603 #if defined(VISP_HAVE_OPENMP)
604  // Set different seeds for each thread in the team, rand() is not thread
605  // safe
606  unsigned int initial_seed = (unsigned int)omp_get_thread_num(); // same seed each time
607  //(unsigned int) (int(time(NULL)) ^ omp_get_thread_num());
608  if (omp_get_num_threads() == 1) {
609  initial_seed = 0; // Same seed at each run
610  }
611 #else
612  unsigned int initial_seed = 0;
613 #endif
614 
615 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
616  srand(initial_seed);
617 #endif
618 
619  unsigned int nbMinRandom = 4;
620 
621  // Numbers of points in the best consensus set
622  unsigned int nb_best_inliers = 0;
623 
624  // True if we found a consensus set with a size >
625  // ransacNbInlierConsensus
626  bool foundSolutionWithConsensus = false;
627 
628  vpPoint p; // Point used to project using the estimated pose
629 
630 #pragma omp for
631  for (int nbTrials = 0; nbTrials < ransacMaxTrials; nbTrials++) {
632  // Flag to check if a solution has been founded, used to "cancel" the
633  // threads
634  if (!foundSolutionWithConsensus) {
635  // Hold the list of the index of the inliers (points in the
636  // consensus set)
637  std::vector<unsigned int> cur_consensus;
638  // Hold the list of the index of the outliers
639  std::vector<unsigned int> cur_outliers;
640  // Hold the list of the index of the points randomly picked
641  std::vector<unsigned int> cur_randoms;
642  // Hold the list of the current inliers points to avoid to add a
643  // degenerate point if the flag is set
644  std::vector<vpPoint> cur_inliers;
645 
646  vpHomogeneousMatrix cMo_lagrange, cMo_dementhon;
647  vpHomogeneousMatrix cMo_tmp;
648 
649  // Vector of used points, initialized at false for all points
650  std::vector<bool> usedPt(size, false);
651 
652  vpPose poseMin;
653 
654  for (unsigned int i = 0; i < nbMinRandom;) {
655  if ((size_t)std::count(usedPt.begin(), usedPt.end(), true) == usedPt.size()) {
656  // All points was picked once, break otherwise we stay in an
657  // infinite loop
658  break;
659  }
660 
661 // Pick a point randomly
662 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
663  unsigned int r_ = (unsigned int)rand() % size;
664 #else
665  unsigned int r_ = (unsigned int)rand_r(&initial_seed) % size;
666 #endif
667 
668  while (usedPt[r_]) {
669 // If already picked, pick another point randomly
670 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
671  r_ = (unsigned int)rand() % size;
672 #else
673  r_ = (unsigned int)rand_r(&initial_seed) % size;
674 #endif
675  }
676  // Mark this point as already picked
677  usedPt[r_] = true;
678  vpPoint pt = listOfUniquePoints[r_];
679 
680  bool degenerate = false;
681  if (checkDegeneratePoints) {
682  if (std::find_if(poseMin.listOfPoints.begin(), poseMin.listOfPoints.end(), FindDegeneratePoint(pt)) !=
683  poseMin.listOfPoints.end()) {
684  degenerate = true;
685  }
686  }
687 
688  if (!degenerate) {
689  poseMin.addPoint(pt);
690  cur_randoms.push_back(r_);
691  // Increment the number of points picked
692  i++;
693  }
694  }
695 
696  if (poseMin.npt >= nbMinRandom) {
697  // Flags set if pose computation is OK
698  bool is_valid_lagrange = false;
699  bool is_valid_dementhon = false;
700 
701  // Set maximum value for residuals
702  double r = DBL_MAX;
703  double r_lagrange = DBL_MAX;
704  double r_dementhon = DBL_MAX;
705 
706  try {
707  poseMin.computePose(vpPose::LAGRANGE, cMo_lagrange);
708  r_lagrange = poseMin.computeResidual(cMo_lagrange);
709  is_valid_lagrange = true;
710  } catch (...) {
711  }
712 
713  try {
714  poseMin.computePose(vpPose::DEMENTHON, cMo_dementhon);
715  r_dementhon = poseMin.computeResidual(cMo_dementhon);
716  is_valid_dementhon = true;
717  } catch (...) {
718  }
719 
720  // If residual returned is not a number (NAN), set valid to false
721  if (vpMath::isNaN(r_lagrange)) {
722  is_valid_lagrange = false;
723  r_lagrange = DBL_MAX;
724  }
725 
726  if (vpMath::isNaN(r_dementhon)) {
727  is_valid_dementhon = false;
728  r_dementhon = DBL_MAX;
729  }
730 
731  // If at least one pose computation is OK,
732  // we can continue, otherwise pick another random set
733  if (is_valid_lagrange || is_valid_dementhon) {
734  if (r_lagrange < r_dementhon) {
735  r = r_lagrange;
736  cMo_tmp = cMo_lagrange;
737  } else {
738  r = r_dementhon;
739  cMo_tmp = cMo_dementhon;
740  }
741  r = sqrt(r) / (double)nbMinRandom;
742 
743  // Filter the pose using some criterion (orientation angles,
744  // translations, etc.)
745  bool isPoseValid = true;
746  if (func != NULL) {
747  isPoseValid = func(&cMo_tmp);
748  }
749 
750  if (isPoseValid && r < ransacThreshold) {
751  unsigned int nbInliersCur = 0;
752  unsigned int iter = 0;
753  for (std::vector<vpPoint>::const_iterator it = listOfUniquePoints.begin();
754  it != listOfUniquePoints.end(); ++it, iter++) {
755  p.setWorldCoordinates(it->get_oX(), it->get_oY(), it->get_oZ());
756  p.track(cMo_tmp);
757 
758  double d = vpMath::sqr(p.get_x() - it->get_x()) + vpMath::sqr(p.get_y() - it->get_y());
759  double error = sqrt(d);
760  if (error < ransacThreshold) {
761  bool degenerate = false;
762  if (checkDegeneratePoints) {
763  if (std::find_if(cur_inliers.begin(), cur_inliers.end(), FindDegeneratePoint(*it)) !=
764  cur_inliers.end()) {
765  degenerate = true;
766  }
767  }
768 
769  if (!degenerate) {
770  // the point is considered as inlier if the error is
771  // below the threshold
772  nbInliersCur++;
773  cur_consensus.push_back(iter);
774  cur_inliers.push_back(*it);
775  } else {
776  cur_outliers.push_back(iter);
777  }
778  } else {
779  cur_outliers.push_back(iter);
780  }
781  }
782 
783 #pragma omp critical(update_best_consensus_set)
784  {
785  if (nbInliersCur > nb_best_inliers) {
786  foundSolution = true;
787  best_consensus = cur_consensus;
788  // best_randoms = cur_randoms; // never used
789  nb_best_inliers = nbInliersCur;
790 
791  if (nbInliersCur >= ransacNbInlierConsensus) {
792  foundSolutionWithConsensus = true;
793  }
794  }
795  }
796  }
797  }
798  }
799  }
800  }
801 
802  nbInliers = best_consensus.size();
803  }
804 #elif defined(VP_THREAD_OK)
805  std::vector<vpThread *> threads((size_t)nbThreads);
806  std::vector<RansacFunctor> ransac_func((size_t)nbThreads);
807 
808  int splitTrials = ransacMaxTrials / nbThreads;
809  for (size_t i = 0; i < (size_t)nbThreads; i++) {
810  unsigned int initial_seed = (unsigned int)i; //((unsigned int) time(NULL) ^ i);
811  if (i < (size_t)nbThreads - 1) {
812  ransac_func[i] = RansacFunctor(cMo, ransacNbInlierConsensus, splitTrials, ransacThreshold, initial_seed,
813  checkDegeneratePoints, listOfUniquePoints, func);
814  } else {
815  int maxTrialsRemainder = ransacMaxTrials - splitTrials * (nbThreads - 1);
816  ransac_func[i] = RansacFunctor(cMo, ransacNbInlierConsensus, maxTrialsRemainder, ransacThreshold, initial_seed,
817  checkDegeneratePoints, listOfUniquePoints, func);
818  }
819 
820  threads[(size_t)i] = new vpThread((vpThread::Fn)poseRansacImplThread, (vpThread::Args)&ransac_func[i]);
821  }
822 
823  // Get the best pose between the threads
824  vpPose final_pose;
825  for (std::vector<vpPoint>::const_iterator it = listOfPoints.begin(); it != listOfPoints.end(); ++it) {
826  final_pose.addPoint(*it);
827  }
828 
829  for (size_t i = 0; i < (size_t)nbThreads; i++) {
830  threads[i]->join();
831  delete threads[i];
832  }
833 
834  bool successRansac = false;
835  size_t best_consensus_size = 0;
836  for (size_t i = 0; i < (size_t)nbThreads; i++) {
837  if (ransac_func[i].getResult()) {
838  successRansac = true;
839 
840  if (ransac_func[i].getBestConsensus().size() > best_consensus_size) {
841  nbInliers = ransac_func[i].getNbInliers();
842  best_consensus = ransac_func[i].getBestConsensus();
843  best_consensus_size = ransac_func[i].getBestConsensus().size();
844  }
845  }
846  }
847 
848  foundSolution = successRansac;
849 #endif
850  } else {
851  // Sequential RANSAC
852  RansacFunctor sequentialRansac(cMo, ransacNbInlierConsensus, ransacMaxTrials, ransacThreshold, 0,
853  checkDegeneratePoints, listOfUniquePoints, func);
854  sequentialRansac();
855  foundSolution = sequentialRansac.getResult();
856 
857  if (foundSolution) {
858  nbInliers = sequentialRansac.getNbInliers();
859  best_consensus = sequentialRansac.getBestConsensus();
860  }
861  }
862 
863  if (foundSolution) {
864  unsigned int nbMinRandom = 4;
865  // std::cout << "Nombre d'inliers " << nbInliers << std::endl ;
866 
867  // Display the random picked points
868  /*
869  std::cout << "Randoms : ";
870  for(unsigned int i = 0 ; i < cur_randoms.size() ; i++)
871  std::cout << cur_randoms[i] << " ";
872  std::cout << std::endl;
873  */
874 
875  // Display the outliers
876  /*
877  std::cout << "Outliers : ";
878  for(unsigned int i = 0 ; i < cur_outliers.size() ; i++)
879  std::cout << cur_outliers[i] << " ";
880  std::cout << std::endl;
881  */
882 
883  // Even if the cardinality of the best consensus set is inferior to
884  // ransacNbInlierConsensus, we want to refine the solution with data in
885  // best_consensus and return this pose. This is an approach used for
886  // example in p118 in Multiple View Geometry in Computer Vision, Hartley,
887  // R.~I. and Zisserman, A.
888  if (nbInliers >= nbMinRandom) // if(nbInliers >= (unsigned)ransacNbInlierConsensus)
889  {
890  // Refine the solution using all the points in the consensus set and
891  // with VVS pose estimation
892  vpPose pose;
893  for (size_t i = 0; i < best_consensus.size(); i++) {
894  vpPoint pt = listOfUniquePoints[best_consensus[i]];
895 
896  pose.addPoint(pt);
897  ransacInliers.push_back(pt);
898  }
899 
900  // Update the list of inlier index
901  for (std::vector<unsigned int>::const_iterator it_index = best_consensus.begin();
902  it_index != best_consensus.end(); ++it_index) {
903  ransacInlierIndex.push_back((unsigned int)mapOfUniquePointIndex[*it_index]);
904  }
905 
906  // Flags set if pose computation is OK
907  bool is_valid_lagrange = false;
908  bool is_valid_dementhon = false;
909 
910  // Set maximum value for residuals
911  double r_lagrange = DBL_MAX;
912  double r_dementhon = DBL_MAX;
913 
914  try {
915  pose.computePose(vpPose::LAGRANGE, cMo_lagrange);
916  r_lagrange = pose.computeResidual(cMo_lagrange);
917  is_valid_lagrange = true;
918  } catch (...) {
919  }
920 
921  try {
922  pose.computePose(vpPose::DEMENTHON, cMo_dementhon);
923  r_dementhon = pose.computeResidual(cMo_dementhon);
924  is_valid_dementhon = true;
925  } catch (...) {
926  }
927 
928  // If residual returned is not a number (NAN), set valid to false
929  if (vpMath::isNaN(r_lagrange)) {
930  is_valid_lagrange = false;
931  r_lagrange = DBL_MAX;
932  }
933 
934  if (vpMath::isNaN(r_dementhon)) {
935  is_valid_dementhon = false;
936  r_dementhon = DBL_MAX;
937  }
938 
939  if (is_valid_lagrange || is_valid_dementhon) {
940  if (r_lagrange < r_dementhon) {
941  cMo = cMo_lagrange;
942  } else {
943  cMo = cMo_dementhon;
944  }
945 
946  pose.setCovarianceComputation(computeCovariance);
947  pose.computePose(vpPose::VIRTUAL_VS, cMo);
948 
949  // In some rare cases, the final pose could not respect the pose
950  // criterion even if the 4 minimal points picked respect the pose
951  // criterion.
952  if (func != NULL && !func(&cMo)) {
953  return false;
954  }
955 
956  if (computeCovariance) {
957  covarianceMatrix = pose.covarianceMatrix;
958  }
959  }
960  } else {
961  return false;
962  }
963  }
964 
965  return foundSolution;
966 }
967 
984 int vpPose::computeRansacIterations(double probability, double epsilon, const int sampleSize, int maxIterations)
985 {
986  probability = (std::max)(probability, 0.0);
987  probability = (std::min)(probability, 1.0);
988  epsilon = (std::max)(epsilon, 0.0);
989  epsilon = (std::min)(epsilon, 1.0);
990 
991  if (vpMath::nul(epsilon)) {
992  // no outliers
993  return 1;
994  }
995 
996  if (maxIterations <= 0) {
997  maxIterations = std::numeric_limits<int>::max();
998  }
999 
1000  double logarg, logval, N;
1001  logarg = -std::pow(1.0 - epsilon, sampleSize);
1002 #ifdef VISP_HAVE_FUNC_LOG1P
1003  logval = log1p(logarg);
1004 #else
1005  logval = log(1.0 + logarg);
1006 #endif
1007  if (vpMath::nul(logval, std::numeric_limits<double>::epsilon())) {
1008  std::cerr << "vpMath::nul(log(1.0 - std::pow(1.0 - epsilon, "
1009  "sampleSize)), std::numeric_limits<double>::epsilon())"
1010  << std::endl;
1011  return 0;
1012  }
1013 
1014  N = log((std::max)(1.0 - probability, std::numeric_limits<double>::epsilon())) / logval;
1015  if (logval < 0.0 && N < maxIterations) {
1016  return (int)ceil(N);
1017  }
1018 
1019  return maxIterations;
1020 }
1021 
1045 void vpPose::findMatch(std::vector<vpPoint> &p2D, std::vector<vpPoint> &p3D,
1046  const unsigned int &numberOfInlierToReachAConsensus, const double &threshold,
1047  unsigned int &ninliers, std::vector<vpPoint> &listInliers, vpHomogeneousMatrix &cMo,
1048  const int &maxNbTrials)
1049 {
1050  vpPose pose;
1051 
1052  int nbPts = 0;
1053  for (unsigned int i = 0; i < p2D.size(); i++) {
1054  for (unsigned int j = 0; j < p3D.size(); j++) {
1055  vpPoint pt(p3D[j].getWorldCoordinates());
1056  pt.set_x(p2D[i].get_x());
1057  pt.set_y(p2D[i].get_y());
1058  pose.addPoint(pt);
1059  nbPts++;
1060  }
1061  }
1062 
1063  if (pose.listP.size() < 4) {
1064  vpERROR_TRACE("Ransac method cannot be used in that case ");
1065  vpERROR_TRACE("(at least 4 points are required)");
1066  vpERROR_TRACE("Not enough point (%d) to compute the pose ", pose.listP.size());
1067  throw(vpPoseException(vpPoseException::notEnoughPointError, "Not enough point (%d) to compute the pose by ransac",
1068  pose.listP.size()));
1069  } else {
1070  pose.setRansacMaxTrials(maxNbTrials);
1071  pose.setRansacNbInliersToReachConsensus(numberOfInlierToReachAConsensus);
1072  pose.setRansacThreshold(threshold);
1073  pose.computePose(vpPose::RANSAC, cMo);
1074  ninliers = pose.getRansacNbInliers();
1075  listInliers = pose.getRansacInliers();
1076  }
1077 }
void * Return
Definition: vpThread.h:41
Implementation of an homogeneous matrix and operations on such kind of matrices.
#define vpERROR_TRACE
Definition: vpDebug.h:393
static bool isNaN(const double value)
Definition: vpMath.cpp:84
void setRansacThreshold(const double &t)
Definition: vpPose.h:252
void set_x(const double x)
Set the point x coordinate in the image plane.
Definition: vpPoint.cpp:470
void track(const vpHomogeneousMatrix &cMo)
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:158
something is not initialized
std::list< vpPoint > listP
Array of point (use here class vpPoint)
Definition: vpPose.h:115
std::vector< vpPoint > getRansacInliers() const
Definition: vpPose.h:264
Class that defines what is a point.
Definition: vpPoint.h:58
bool poseRansac(vpHomogeneousMatrix &cMo, bool(*func)(vpHomogeneousMatrix *)=NULL)
static void findMatch(std::vector< vpPoint > &p2D, std::vector< vpPoint > &p3D, const unsigned int &numberOfInlierToReachAConsensus, const double &threshold, unsigned int &ninliers, std::vector< vpPoint > &listInliers, vpHomogeneousMatrix &cMo, const int &maxNbTrials=10000)
void *(* Fn)(Args)
Definition: vpThread.h:42
static bool nul(double x, double s=0.001)
Definition: vpMath.h:281
static double sqr(double x)
Definition: vpMath.h:108
void * Args
Definition: vpThread.h:40
Class used for pose computation from N points (pose from point only). Some of the algorithms implemen...
Definition: vpPose.h:79
void set_y(const double y)
Set the point y coordinate in the image plane.
Definition: vpPoint.cpp:472
bool computePose(vpPoseMethodType method, vpHomogeneousMatrix &cMo, bool(*func)(vpHomogeneousMatrix *)=NULL)
Definition: vpPose.cpp:362
unsigned int npt
Number of point used in pose computation.
Definition: vpPose.h:114
void setRansacMaxTrials(const int &rM)
Definition: vpPose.h:261
void setRansacNbInliersToReachConsensus(const unsigned int &nbC)
Definition: vpPose.h:251
void setWorldCoordinates(const double oX, const double oY, const double oZ)
Definition: vpPoint.cpp:113
Error that can be emited by the vpPose class and its derivates.
unsigned int getRansacNbInliers() const
Definition: vpPose.h:262
double get_x() const
Get the point x coordinate in the image plane.
Definition: vpPoint.cpp:429
double get_y() const
Get the point y coordinate in the image plane.
Definition: vpPoint.cpp:431
double computeResidual(const vpHomogeneousMatrix &cMo) const
Compute and return the residual expressed in meter for the pose matrix &#39;cMo&#39;.
Definition: vpPose.cpp:324
void addPoint(const vpPoint &P)
Definition: vpPose.cpp:137
static int computeRansacIterations(double probability, double epsilon, const int sampleSize=4, int maxIterations=2000)
vpColVector p
Definition: vpTracker.h:71
void setCovarianceComputation(const bool &flag)
Definition: vpPose.h:272