Visual Servoing Platform  version 3.0.0
vpPoseRansac.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ViSP software.
4  * Copyright (C) 2005 - 2015 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 
53 #include <visp3/vision/vpPose.h>
54 #include <visp3/core/vpColVector.h>
55 #include <visp3/core/vpRansac.h>
56 #include <visp3/core/vpTime.h>
57 #include <visp3/core/vpList.h>
58 #include <visp3/vision/vpPoseException.h>
59 #include <visp3/core/vpMath.h>
60 
61 #define eps 1e-6
62 
63 
73 {
74  ransacInliers.clear();
75  ransacInlierIndex.clear();
76 
77  srand(0); //Fix seed here so we will have the same pseudo-random series at each run.
78  std::vector<unsigned int> best_consensus;
79  std::vector<unsigned int> cur_consensus;
80  std::vector<unsigned int> cur_outliers;
81  std::vector<unsigned int> cur_randoms;
82  unsigned int size = (unsigned int)listP.size();
83  int nbTrials = 0;
84  unsigned int nbMinRandom = 4 ;
85  unsigned int nbInliers = 0;
86  double r, r_lagrange, r_dementhon;
87 
88  vpHomogeneousMatrix cMo_lagrange, cMo_dementhon;
89 
90  if (size < 4) {
91  //vpERROR_TRACE("Not enough point to compute the pose");
93  "Not enough point to compute the pose")) ;
94  }
95 
96  bool foundSolution = false;
97 
98  while (nbTrials < ransacMaxTrials && nbInliers < (unsigned)ransacNbInlierConsensus)
99  {
100  //Hold the list of the index of the inliers (points in the consensus set)
101  cur_consensus.clear();
102 
103  //Use a temporary variable because if not, the cMo passed in parameters will be modified when
104  // we compute the pose for the minimal sample sets but if the pose is not correct when we pass
105  // a function pointer we do not want to modify the cMo passed in parameters
106  vpHomogeneousMatrix cMo_tmp;
107  cur_outliers.clear();
108  cur_randoms.clear();
109 
110  //Vector of used points, initialized at false for all points
111  std::vector<bool> usedPt(size, false);
112 
113  vpPose poseMin;
114  for(unsigned int i = 0; i < nbMinRandom;)
115  {
116  if((size_t) std::count(usedPt.begin(), usedPt.end(), true) == usedPt.size()) {
117  //All points was picked once, break otherwise we stay in an infinite loop
118  break;
119  }
120 
121  //Pick a point randomly
122  unsigned int r_ = (unsigned int) rand() % size;
123  while(usedPt[r_]) {
124  //If already picked, pick another point randomly
125  r_ = (unsigned int) rand() % size;
126  }
127  //Mark this point as already picked
128  usedPt[r_] = true;
129 
130  std::list<vpPoint>::const_iterator iter = listP.begin();
131  std::advance(iter, r_);
132  vpPoint pt = *iter;
133 
134  bool degenerate = false;
135  for(std::list<vpPoint>::const_iterator it = poseMin.listP.begin(); it != poseMin.listP.end(); ++it){
136  vpPoint ptdeg = *it;
137  if( ((fabs(pt.get_x() - ptdeg.get_x()) < 1e-6) && (fabs(pt.get_y() - ptdeg.get_y()) < 1e-6)) ||
138  ((fabs(pt.get_oX() - ptdeg.get_oX()) < 1e-6) && (fabs(pt.get_oY() - ptdeg.get_oY()) < 1e-6) && (fabs(pt.get_oZ() - ptdeg.get_oZ()) < 1e-6))){
139  degenerate = true;
140  break;
141  }
142  }
143  if(!degenerate) {
144  poseMin.addPoint(pt);
145  cur_randoms.push_back(r_);
146  //Increment the number of points picked
147  i++;
148  }
149  }
150 
151  if(poseMin.npt < nbMinRandom) {
152  nbTrials++;
153  continue;
154  }
155 
156  //Flags set if pose computation is OK
157  bool is_valid_lagrange = false;
158  bool is_valid_dementhon = false;
159 
160  //Set maximum value for residuals
161  r_lagrange = DBL_MAX;
162  r_dementhon = DBL_MAX;
163 
164  try {
165  poseMin.computePose(vpPose::LAGRANGE, cMo_lagrange);
166  r_lagrange = poseMin.computeResidual(cMo_lagrange);
167  is_valid_lagrange = true;
168  } catch(/*vpException &e*/...) {
169 // std::cerr << e.what() << std::endl;
170  }
171 
172  try {
173  poseMin.computePose(vpPose::DEMENTHON, cMo_dementhon);
174  r_dementhon = poseMin.computeResidual(cMo_dementhon);
175  is_valid_dementhon = true;
176  } catch(/*vpException &e*/...) {
177 // std::cerr << e.what() << std::endl;
178  }
179 
180  //If residual returned is not a number (NAN), set valid to false
181  if(vpMath::isNaN(r_lagrange)) {
182  is_valid_lagrange = false;
183  r_lagrange = DBL_MAX;
184  }
185 
186  if(vpMath::isNaN(r_dementhon)) {
187  is_valid_dementhon = false;
188  r_dementhon = DBL_MAX;
189  }
190 
191  //If at least one pose computation is OK,
192  //we can continue, otherwise pick another random set
193  if(is_valid_lagrange || is_valid_dementhon) {
194  if (r_lagrange < r_dementhon) {
195  r = r_lagrange;
196 // cMo = cMo_lagrange;
197  cMo_tmp = cMo_lagrange;
198  }
199  else {
200  r = r_dementhon;
201 // cMo = cMo_dementhon;
202  cMo_tmp = cMo_dementhon;
203  }
204  r = sqrt(r) / (double) nbMinRandom;
205 
206  //Filter the pose using some criterion (orientation angles, translations, etc.)
207  bool isPoseValid = true;
208  if(func != NULL) {
209  isPoseValid = func(&cMo_tmp);
210  if(isPoseValid) {
211  cMo = cMo_tmp;
212  }
213  } else {
214  //No post filtering on pose, so copy cMo_temp to cMo
215  cMo = cMo_tmp;
216  }
217 
218  if (isPoseValid && r < ransacThreshold)
219  {
220  unsigned int nbInliersCur = 0;
221  unsigned int iter = 0;
222  for (std::list<vpPoint>::const_iterator it = listP.begin(); it != listP.end(); ++it)
223  {
224  vpPoint pt = *it;
225  vpPoint p(pt) ;
226  p.track(cMo) ;
227 
228  double d = vpMath::sqr(p.get_x() - pt.get_x()) + vpMath::sqr(p.get_y() - pt.get_y()) ;
229  double error = sqrt(d) ;
230  if(error < ransacThreshold) {
231  // the point is considered as inlier if the error is below the threshold
232  // But, we need also to check if it is not a degenerate point
233  bool degenerate = false;
234 
235  for(unsigned int it_inlier_index = 0; it_inlier_index< cur_consensus.size(); it_inlier_index++){
236  std::list<vpPoint>::const_iterator it_point = listP.begin();
237  std::advance(it_point, cur_consensus[it_inlier_index]);
238  pt = *it_point;
239 
240  vpPoint ptdeg = *it;
241  if( ((fabs(pt.get_x() - ptdeg.get_x()) < 1e-6) && (fabs(pt.get_y() - ptdeg.get_y()) < 1e-6)) ||
242  ((fabs(pt.get_oX() - ptdeg.get_oX()) < 1e-6) && (fabs(pt.get_oY() - ptdeg.get_oY()) < 1e-6) && (fabs(pt.get_oZ() - ptdeg.get_oZ()) < 1e-6))){
243  degenerate = true;
244  break;
245  }
246  }
247 
248  if(!degenerate) {
249  nbInliersCur++;
250  cur_consensus.push_back(iter);
251  }
252  else {
253  cur_outliers.push_back(iter);
254  }
255  }
256  else {
257  cur_outliers.push_back(iter);
258  }
259 
260  iter++;
261  }
262 
263  if(nbInliersCur > nbInliers)
264  {
265  foundSolution = true;
266  best_consensus = cur_consensus;
267  nbInliers = nbInliersCur;
268  }
269 
270  nbTrials++;
271 
272  if(nbTrials >= ransacMaxTrials) {
273 // vpERROR_TRACE("Ransac reached the maximum number of trials");
274  foundSolution = true;
275  }
276  }
277  else {
278  nbTrials++;
279 
280  if(nbTrials >= ransacMaxTrials) {
281 // vpERROR_TRACE("Ransac reached the maximum number of trials");
282  }
283  }
284  } else {
285  nbTrials++;
286 
287  if(nbTrials >= ransacMaxTrials) {
288 // vpERROR_TRACE("Ransac reached the maximum number of trials");
289  }
290  }
291  }
292 
293  if(foundSolution) {
294 // std::cout << "Nombre d'inliers " << nbInliers << std::endl ;
295 
296  //Display the random picked points
297  /*
298  std::cout << "Randoms : ";
299  for(unsigned int i = 0 ; i < cur_randoms.size() ; i++)
300  std::cout << cur_randoms[i] << " ";
301  std::cout << std::endl;
302  */
303 
304  //Display the outliers
305  /*
306  std::cout << "Outliers : ";
307  for(unsigned int i = 0 ; i < cur_outliers.size() ; i++)
308  std::cout << cur_outliers[i] << " ";
309  std::cout << std::endl;
310  */
311 
312  //Even if the cardinality of the best consensus set is inferior to ransacNbInlierConsensus,
313  //we want to refine the solution with data in best_consensus and return this pose.
314  //This is an approach used for example in p118 in Multiple View Geometry in Computer Vision, Hartley, R.~I. and Zisserman, A.
315  if(nbInliers >= nbMinRandom) //if(nbInliers >= (unsigned)ransacNbInlierConsensus)
316  {
317  //Refine the solution using all the points in the consensus set and with VVS pose estimation
318  vpPose pose ;
319  for(unsigned i = 0 ; i < best_consensus.size(); i++)
320  {
321  std::list<vpPoint>::const_iterator iter = listP.begin();
322  std::advance(iter, best_consensus[i]);
323  vpPoint pt = *iter;
324 
325  pose.addPoint(pt) ;
326  ransacInliers.push_back(pt);
327  }
328 
329  //Update the list of inlier index
330  ransacInlierIndex = best_consensus;
331 
332  //Flags set if pose computation is OK
333  bool is_valid_lagrange = false;
334  bool is_valid_dementhon = false;
335 
336  //Set maximum value for residuals
337  r_lagrange = DBL_MAX;
338  r_dementhon = DBL_MAX;
339 
340  try {
341  pose.computePose(vpPose::LAGRANGE, cMo_lagrange);
342  r_lagrange = pose.computeResidual(cMo_lagrange);
343  is_valid_lagrange = true;
344  } catch(/*vpException &e*/...) {
345 // std::cerr << e.what() << std::endl;
346  }
347 
348  try {
349  pose.computePose(vpPose::DEMENTHON, cMo_dementhon);
350  r_dementhon = pose.computeResidual(cMo_dementhon);
351  is_valid_dementhon = true;
352  } catch(/*vpException &e*/...) {
353 // std::cerr << e.what() << std::endl;
354  }
355 
356  //If residual returned is not a number (NAN), set valid to false
357  if(vpMath::isNaN(r_lagrange)) {
358  is_valid_lagrange = false;
359  r_lagrange = DBL_MAX;
360  }
361 
362  if(vpMath::isNaN(r_dementhon)) {
363  is_valid_dementhon = false;
364  r_dementhon = DBL_MAX;
365  }
366 
367  if(is_valid_lagrange || is_valid_dementhon) {
368  if (r_lagrange < r_dementhon) {
369  cMo = cMo_lagrange;
370  }
371  else {
372  cMo = cMo_dementhon;
373  }
374 
375  pose.setCovarianceComputation(computeCovariance);
376  pose.computePose(vpPose::VIRTUAL_VS, cMo);
377 
378  //In some rare cases, the final pose could not respect the pose criterion even
379  //if the 4 minimal points picked respect the pose criterion.
380  if(func != NULL && !func(&cMo)) {
381  return false;
382  }
383 
384  if(computeCovariance) {
385  covarianceMatrix = pose.covarianceMatrix;
386  }
387  }
388  } else {
389  return false;
390  }
391  }
392 
393  return foundSolution;
394 }
395 
420 void vpPose::findMatch(std::vector<vpPoint> &p2D,
421  std::vector<vpPoint> &p3D,
422  const unsigned int &numberOfInlierToReachAConsensus,
423  const double &threshold,
424  unsigned int &ninliers,
425  std::vector<vpPoint> &listInliers,
426  vpHomogeneousMatrix &cMo,
427  const int &maxNbTrials )
428 {
429  vpPose pose;
430 
431  int nbPts = 0;
432  for(unsigned int i = 0 ; i < p2D.size() ; i++)
433  {
434  for(unsigned int j = 0 ; j < p3D.size() ; j++)
435  {
436  vpPoint pt(p3D[j].getWorldCoordinates());
437  pt.set_x(p2D[i].get_x());
438  pt.set_y(p2D[i].get_y());
439  pose.addPoint(pt);
440  nbPts++;
441  }
442  }
443 
444  if (pose.listP.size() < 4)
445  {
446  vpERROR_TRACE("Ransac method cannot be used in that case ") ;
447  vpERROR_TRACE("(at least 4 points are required)") ;
448  vpERROR_TRACE("Not enough point (%d) to compute the pose ",pose.listP.size()) ;
450  "Not enough points ")) ;
451  }
452  else
453  {
454  pose.setRansacMaxTrials(maxNbTrials);
455  pose.setRansacNbInliersToReachConsensus(numberOfInlierToReachAConsensus);
456  pose.setRansacThreshold(threshold);
457  pose.computePose(vpPose::RANSAC, cMo);
458  ninliers = pose.getRansacNbInliers();
459  listInliers = pose.getRansacInliers();
460  }
461 }
462 
463 /*
464  * Local variables:
465  * c-basic-offset: 2
466  * End:
467  */
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:84
double get_oY() const
Get the point Y coordinate in the object frame.
Definition: vpPoint.cpp:449
void setRansacThreshold(const double &t)
Definition: vpPose.h:167
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:91
Class that defines what is a point.
Definition: vpPoint.h:59
bool poseRansac(vpHomogeneousMatrix &cMo, bool(*func)(vpHomogeneousMatrix *)=NULL)
compute the pose using the Ransac approach
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:176
static double sqr(double x)
Definition: vpMath.h:110
bool computePose(vpPoseMethodType methode, vpHomogeneousMatrix &cMo, bool(*func)(vpHomogeneousMatrix *)=NULL)
compute the pose for a given method
Definition: vpPose.cpp:382
Class used for pose computation from N points (pose from point only).
Definition: vpPose.h:74
double get_oZ() const
Get the point Z coordinate in the object frame.
Definition: vpPoint.cpp:451
void set_y(const double y)
Set the point y coordinate in the image plane.
Definition: vpPoint.cpp:498
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:90
void setRansacMaxTrials(const int &rM)
Definition: vpPose.h:175
void setRansacNbInliersToReachConsensus(const unsigned int &nbC)
Definition: vpPose.h:166
double get_oX() const
Get the point X coordinate in the object frame.
Definition: vpPoint.cpp:447
Error that can be emited by the vpPose class and its derivates.
void addPoint(const vpPoint &P)
Add a new point in this array.
Definition: vpPose.cpp:151
std::vector< vpPoint > getRansacInliers() const
Definition: vpPose.h:178
void setCovarianceComputation(const bool &flag)
Definition: vpPose.h:185
double computeResidual(const vpHomogeneousMatrix &cMo) const
Compute and return the residual expressed in meter for the pose matrix 'cMo'.
Definition: vpPose.cpp:340