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