Visual Servoing Platform  version 3.6.1 under development (2023-12-07)
vpPoseRansac.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
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 https://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 
39 #include <algorithm> // std::count
40 #include <cmath> // std::fabs
41 #include <float.h> // DBL_MAX
42 #include <iostream>
43 #include <limits> // numeric_limits
44 #include <map>
45 
46 #include <visp3/core/vpColVector.h>
47 #include <visp3/core/vpMath.h>
48 #include <visp3/core/vpRansac.h>
49 #include <visp3/vision/vpPose.h>
50 #include <visp3/vision/vpPoseException.h>
51 
52 #include <thread>
53 
54 #define eps 1e-6
55 
56 namespace
57 {
58 // For std::map<vpPoint>
59 struct CompareObjectPointDegenerate
60 {
61  bool operator()(const vpPoint &point1, const vpPoint &point2) const
62  {
63  const double dist1 =
64  point1.get_oX() * point1.get_oX() + point1.get_oY() * point1.get_oY() + point1.get_oZ() * point1.get_oZ();
65  const double dist2 =
66  point2.get_oX() * point2.get_oX() + point2.get_oY() * point2.get_oY() + point2.get_oZ() * point2.get_oZ();
67  if (dist1 - dist2 < -3 * eps * eps)
68  return true;
69  if (dist1 - dist2 > 3 * eps * eps)
70  return false;
71 
72  if (point1.oP[0] - point2.oP[0] < -eps)
73  return true;
74  if (point1.oP[0] - point2.oP[0] > eps)
75  return false;
76 
77  if (point1.oP[1] - point2.oP[1] < -eps)
78  return true;
79  if (point1.oP[1] - point2.oP[1] > eps)
80  return false;
81 
82  if (point1.oP[2] - point2.oP[2] < -eps)
83  return true;
84  if (point1.oP[2] - point2.oP[2] > eps)
85  return false;
86 
87  return false;
88  }
89 };
90 
91 // For std::map<vpPoint>
92 struct CompareImagePointDegenerate
93 {
94  bool operator()(const vpPoint &point1, const vpPoint &point2) const
95  {
96  const double dist1 = point1.get_x() * point1.get_x() + point1.get_y() * point1.get_y();
97  const double dist2 = point2.get_x() * point2.get_x() + point2.get_y() * point2.get_y();
98  if (dist1 - dist2 < -2 * eps * eps)
99  return true;
100  if (dist1 - dist2 > 2 * eps * eps)
101  return false;
102 
103  if (point1.p[0] - point2.p[0] < -eps)
104  return true;
105  if (point1.p[0] - point2.p[0] > eps)
106  return false;
107 
108  if (point1.p[1] - point2.p[1] < -eps)
109  return true;
110  if (point1.p[1] - point2.p[1] > eps)
111  return false;
112 
113  return false;
114  }
115 };
116 
117 // std::find_if
118 struct FindDegeneratePoint
119 {
120  explicit FindDegeneratePoint(const vpPoint &pt) : m_pt(pt) { }
121 
122  bool operator()(const vpPoint &pt)
123  {
124  return ((std::fabs(m_pt.oP[0] - pt.oP[0]) < eps && std::fabs(m_pt.oP[1] - pt.oP[1]) < eps &&
125  std::fabs(m_pt.oP[2] - pt.oP[2]) < eps) ||
126  (std::fabs(m_pt.p[0] - pt.p[0]) < eps && std::fabs(m_pt.p[1] - pt.p[1]) < eps));
127  }
128 
129  vpPoint m_pt;
130 };
131 } // namespace
132 
133 bool vpPose::vpRansacFunctor::poseRansacImpl()
134 {
135  const unsigned int size = (unsigned int)m_listOfUniquePoints.size();
136  unsigned int nbMinRandom = 4;
137  int nbTrials = 0;
138 
139  vpPoint p; // Point used to project using the estimated pose
140 
141  bool foundSolution = false;
142  while (nbTrials < m_ransacMaxTrials && m_nbInliers < m_ransacNbInlierConsensus) {
143  // Hold the list of the index of the inliers (points in the consensus set)
144  std::vector<unsigned int> cur_consensus;
145  // Hold the list of the index of the outliers
146  std::vector<unsigned int> cur_outliers;
147  // Hold the list of the index of the points randomly picked
148  std::vector<unsigned int> cur_randoms;
149  // Hold the list of the current inliers points to avoid to add a
150  // degenerate point if the flag is set
151  std::vector<vpPoint> cur_inliers;
152 
153  // Use a temporary variable because if not, the cMo passed in parameters
154  // will be modified when
155  // we compute the pose for the minimal sample sets but if the pose is not
156  // correct when we pass a function pointer we do not want to modify the
157  // cMo passed in parameters
158  vpHomogeneousMatrix cMo_tmp;
159 
160  // Vector of used points, initialized at false for all points
161  std::vector<bool> usedPt(size, false);
162 
163  vpPose poseMin;
164  for (unsigned int i = 0; i < nbMinRandom;) {
165  if ((size_t)std::count(usedPt.begin(), usedPt.end(), true) == usedPt.size()) {
166  // All points was picked once, break otherwise we stay in an infinite loop
167  break;
168  }
169 
170  // Pick a point randomly
171  unsigned int r_ = m_uniRand.uniform(0, size);
172 
173  while (usedPt[r_]) {
174  // If already picked, pick another point randomly
175  r_ = m_uniRand.uniform(0, size);
176  }
177  // Mark this point as already picked
178  usedPt[r_] = true;
179  vpPoint pt = m_listOfUniquePoints[r_];
180 
181  bool degenerate = false;
182  if (m_checkDegeneratePoints) {
183  if (std::find_if(poseMin.listOfPoints.begin(), poseMin.listOfPoints.end(), FindDegeneratePoint(pt)) !=
184  poseMin.listOfPoints.end()) {
185  degenerate = true;
186  }
187  }
188 
189  if (!degenerate) {
190  poseMin.addPoint(pt);
191  cur_randoms.push_back(r_);
192  // Increment the number of points picked
193  i++;
194  }
195  }
196 
197  if (poseMin.npt < nbMinRandom) {
198  nbTrials++;
199  continue;
200  }
201 
202  bool is_pose_valid = false;
203  double r_min = DBL_MAX;
204 
205  try {
206  is_pose_valid = poseMin.computePose(vpPose::DEMENTHON_LAGRANGE_VIRTUAL_VS, cMo_tmp);
207  r_min = poseMin.computeResidual(cMo_tmp);
208  }
209  catch (...) {
210  }
211 
212  // If residual returned is not a number (NAN), set valid to false
213  if (vpMath::isNaN(r_min)) {
214  is_pose_valid = false;
215  }
216 
217  // If at pose computation is OK we can continue, otherwise pick another random set
218  if (is_pose_valid) {
219  double r = sqrt(r_min) / (double)nbMinRandom; // FS should be r = sqrt(r_min / (double)nbMinRandom);
220  // Filter the pose using some criterion (orientation angles,
221  // translations, etc.)
222  bool isPoseValid = true;
223  if (m_func != nullptr) {
224  isPoseValid = m_func(cMo_tmp);
225  if (isPoseValid) {
226  m_cMo = cMo_tmp;
227  }
228  }
229  else {
230  // No post filtering on pose, so copy cMo_temp to cMo
231  m_cMo = cMo_tmp;
232  }
233 
234  if (isPoseValid && r < m_ransacThreshold) {
235  unsigned int nbInliersCur = 0;
236  unsigned int iter = 0;
237  for (std::vector<vpPoint>::const_iterator it = m_listOfUniquePoints.begin(); it != m_listOfUniquePoints.end();
238  ++it, iter++) {
239  p.setWorldCoordinates(it->get_oX(), it->get_oY(), it->get_oZ());
240  p.track(m_cMo);
241 
242  double error = sqrt(vpMath::sqr(p.get_x() - it->get_x()) + vpMath::sqr(p.get_y() - it->get_y()));
243  if (error < m_ransacThreshold) {
244  bool degenerate = false;
245  if (m_checkDegeneratePoints) {
246  if (std::find_if(cur_inliers.begin(), cur_inliers.end(), FindDegeneratePoint(*it)) != cur_inliers.end()) {
247  degenerate = true;
248  }
249  }
250 
251  if (!degenerate) {
252  // the point is considered as inlier if the error is below the
253  // threshold
254  nbInliersCur++;
255  cur_consensus.push_back(iter);
256  cur_inliers.push_back(*it);
257  }
258  else {
259  cur_outliers.push_back(iter);
260  }
261  }
262  else {
263  cur_outliers.push_back(iter);
264  }
265  }
266 
267  if (nbInliersCur > m_nbInliers) {
268  foundSolution = true;
269  m_best_consensus = cur_consensus;
270  m_nbInliers = nbInliersCur;
271  }
272 
273  nbTrials++;
274 
275  if (nbTrials >= m_ransacMaxTrials) {
276  foundSolution = true;
277  }
278  }
279  else {
280  nbTrials++;
281  }
282  }
283  else {
284  nbTrials++;
285  }
286  }
287 
288  return foundSolution;
289 }
290 
292 {
293  // Check only for adding / removing problem
294  // Do not take into account problem with element modification here
295  if (listP.size() != listOfPoints.size()) {
296  std::cerr << "You should not modify vpPose::listP!" << std::endl;
297  listOfPoints = std::vector<vpPoint>(listP.begin(), listP.end());
298  }
299 
300  ransacInliers.clear();
301  ransacInlierIndex.clear();
302 
303  std::vector<unsigned int> best_consensus;
304  unsigned int nbInliers = 0;
305 
306  vpHomogeneousMatrix cMo_lagrange, cMo_dementhon;
307 
308  if (listOfPoints.size() < 4) {
309  throw(vpPoseException(vpPoseException::notInitializedError, "Not enough point to compute the pose"));
310  }
311 
312  std::vector<vpPoint> listOfUniquePoints;
313  std::map<size_t, size_t> mapOfUniquePointIndex;
314 
315  // Get RANSAC flags
316  bool prefilterDegeneratePoints = ransacFlag == PREFILTER_DEGENERATE_POINTS;
317  bool checkDegeneratePoints = ransacFlag == CHECK_DEGENERATE_POINTS;
318 
319  if (prefilterDegeneratePoints) {
320  // Remove degenerate object points
321  std::map<vpPoint, size_t, CompareObjectPointDegenerate> filterObjectPointMap;
322  size_t index_pt = 0;
323  for (std::vector<vpPoint>::const_iterator it_pt = listOfPoints.begin(); it_pt != listOfPoints.end();
324  ++it_pt, index_pt++) {
325  if (filterObjectPointMap.find(*it_pt) == filterObjectPointMap.end()) {
326  filterObjectPointMap[*it_pt] = index_pt;
327  }
328  }
329 
330  std::map<vpPoint, size_t, CompareImagePointDegenerate> filterImagePointMap;
331  for (std::map<vpPoint, size_t, CompareObjectPointDegenerate>::const_iterator it = filterObjectPointMap.begin();
332  it != filterObjectPointMap.end(); ++it) {
333  if (filterImagePointMap.find(it->first) == filterImagePointMap.end()) {
334  filterImagePointMap[it->first] = it->second;
335 
336  listOfUniquePoints.push_back(it->first);
337  mapOfUniquePointIndex[listOfUniquePoints.size() - 1] = it->second;
338  }
339  }
340  }
341  else {
342  // No prefiltering
343  listOfUniquePoints = listOfPoints;
344 
345  size_t index_pt = 0;
346  for (std::vector<vpPoint>::const_iterator it_pt = listOfPoints.begin(); it_pt != listOfPoints.end();
347  ++it_pt, index_pt++) {
348  mapOfUniquePointIndex[index_pt] = index_pt;
349  }
350  }
351 
352  if (listOfUniquePoints.size() < 4) {
353  throw(vpPoseException(vpPoseException::notInitializedError, "Not enough point to compute the pose"));
354  }
355 
356  unsigned int nbThreads = 1;
357  bool executeParallelVersion = useParallelRansac;
358 
359  if (executeParallelVersion) {
360  if (nbParallelRansacThreads <= 0) {
361  // Get number of CPU threads
362  nbThreads = std::thread::hardware_concurrency();
363  if (nbThreads <= 1) {
364  nbThreads = 1;
365  executeParallelVersion = false;
366  }
367  }
368  else {
369  nbThreads = nbParallelRansacThreads;
370  }
371  }
372 
373  bool foundSolution = false;
374 
375  if (executeParallelVersion) {
376  std::vector<std::thread> threadpool;
377  std::vector<vpRansacFunctor> ransacWorkers;
378 
379  int splitTrials = ransacMaxTrials / nbThreads;
380  for (size_t i = 0; i < (size_t)nbThreads; i++) {
381  unsigned int initial_seed = (unsigned int)i; //((unsigned int) time(nullptr) ^ i);
382  if (i < (size_t)nbThreads - 1) {
383  ransacWorkers.emplace_back(cMo, ransacNbInlierConsensus, splitTrials, ransacThreshold, initial_seed,
384  checkDegeneratePoints, listOfUniquePoints, func);
385  }
386  else {
387  int maxTrialsRemainder = ransacMaxTrials - splitTrials * (nbThreads - 1);
388  ransacWorkers.emplace_back(cMo, ransacNbInlierConsensus, maxTrialsRemainder, ransacThreshold, initial_seed,
389  checkDegeneratePoints, listOfUniquePoints, func);
390  }
391  }
392 
393  for (auto &worker : ransacWorkers) {
394  threadpool.emplace_back(&vpRansacFunctor::operator(), &worker);
395  }
396 
397  for (auto &th : threadpool) {
398  th.join();
399  }
400 
401  bool successRansac = false;
402  size_t best_consensus_size = 0;
403  for (auto &worker : ransacWorkers) {
404  if (worker.getResult()) {
405  successRansac = true;
406 
407  if (worker.getBestConsensus().size() > best_consensus_size) {
408  nbInliers = worker.getNbInliers();
409  best_consensus = worker.getBestConsensus();
410  best_consensus_size = worker.getBestConsensus().size();
411  }
412  }
413  }
414 
415  foundSolution = successRansac;
416  }
417  else {
418  // Sequential RANSAC
419  vpRansacFunctor sequentialRansac(cMo, ransacNbInlierConsensus, ransacMaxTrials, ransacThreshold, 0,
420  checkDegeneratePoints, listOfUniquePoints, func);
421  sequentialRansac();
422  foundSolution = sequentialRansac.getResult();
423 
424  if (foundSolution) {
425  nbInliers = sequentialRansac.getNbInliers();
426  best_consensus = sequentialRansac.getBestConsensus();
427  }
428  }
429 
430  if (foundSolution) {
431  unsigned int nbMinRandom = 4;
432  // std::cout << "Nombre d'inliers " << nbInliers << std::endl ;
433 
434  // Display the random picked points
435  /*
436  std::cout << "Randoms : ";
437  for(unsigned int i = 0 ; i < cur_randoms.size() ; i++)
438  std::cout << cur_randoms[i] << " ";
439  std::cout << std::endl;
440  */
441 
442  // Display the outliers
443  /*
444  std::cout << "Outliers : ";
445  for(unsigned int i = 0 ; i < cur_outliers.size() ; i++)
446  std::cout << cur_outliers[i] << " ";
447  std::cout << std::endl;
448  */
449 
450  // Even if the cardinality of the best consensus set is inferior to
451  // ransacNbInlierConsensus, we want to refine the solution with data in
452  // best_consensus and return this pose. This is an approach used for
453  // example in p118 in Multiple View Geometry in Computer Vision, Hartley,
454  // R.~I. and Zisserman, A.
455  if (nbInliers >= nbMinRandom) // if(nbInliers >= (unsigned)ransacNbInlierConsensus)
456  {
457  // Refine the solution using all the points in the consensus set and
458  // with VVS pose estimation
459  vpPose pose;
460  for (size_t i = 0; i < best_consensus.size(); i++) {
461  vpPoint pt = listOfUniquePoints[best_consensus[i]];
462 
463  pose.addPoint(pt);
464  ransacInliers.push_back(pt);
465  }
466 
467  // Update the list of inlier index
468  for (std::vector<unsigned int>::const_iterator it_index = best_consensus.begin();
469  it_index != best_consensus.end(); ++it_index) {
470  ransacInlierIndex.push_back((unsigned int)mapOfUniquePointIndex[*it_index]);
471  }
472 
473  pose.setCovarianceComputation(computeCovariance);
475 
476  // In some rare cases, the final pose could not respect the pose
477  // criterion even if the 4 minimal points picked respect the pose
478  // criterion.
479  if (func != nullptr && !func(cMo)) {
480  return false;
481  }
482 
483  if (computeCovariance) {
484  covarianceMatrix = pose.covarianceMatrix;
485  }
486  }
487  else {
488  return false;
489  }
490  }
491 
492  return foundSolution;
493 }
494 
495 int vpPose::computeRansacIterations(double probability, double epsilon, const int sampleSize, int maxIterations)
496 {
497  probability = (std::max)(probability, 0.0);
498  probability = (std::min)(probability, 1.0);
499  epsilon = (std::max)(epsilon, 0.0);
500  epsilon = (std::min)(epsilon, 1.0);
501 
502  if (vpMath::nul(epsilon)) {
503  // no outliers
504  return 1;
505  }
506 
507  if (maxIterations <= 0) {
508  maxIterations = std::numeric_limits<int>::max();
509  }
510 
511  double logarg, logval, N;
512  logarg = -std::pow(1.0 - epsilon, sampleSize);
513 #ifdef VISP_HAVE_FUNC_LOG1P
514  logval = log1p(logarg);
515 #else
516  logval = log(1.0 + logarg);
517 #endif
518  if (vpMath::nul(logval, std::numeric_limits<double>::epsilon())) {
519  std::cerr << "vpMath::nul(log(1.0 - std::pow(1.0 - epsilon, "
520  "sampleSize)), std::numeric_limits<double>::epsilon())"
521  << std::endl;
522  return 0;
523  }
524 
525  N = log((std::max)(1.0 - probability, std::numeric_limits<double>::epsilon())) / logval;
526  if (logval < 0.0 && N < maxIterations) {
527  return (int)ceil(N);
528  }
529 
530  return maxIterations;
531 }
532 
533 void vpPose::findMatch(std::vector<vpPoint> &p2D, std::vector<vpPoint> &p3D,
534  const unsigned int &numberOfInlierToReachAConsensus, const double &threshold,
535  unsigned int &ninliers, std::vector<vpPoint> &listInliers, vpHomogeneousMatrix &cMo,
536  const int &maxNbTrials, bool useParallelRansac, unsigned int nthreads,
537  bool (*func)(const vpHomogeneousMatrix &))
538 {
539  vpPose pose;
540 
541  for (unsigned int i = 0; i < p2D.size(); i++) {
542  for (unsigned int j = 0; j < p3D.size(); j++) {
543  vpPoint pt(p3D[j].getWorldCoordinates());
544  pt.set_x(p2D[i].get_x());
545  pt.set_y(p2D[i].get_y());
546  pose.addPoint(pt);
547  }
548  }
549 
550  if (pose.listP.size() < 4) {
551  vpERROR_TRACE("Ransac method cannot be used in that case ");
552  vpERROR_TRACE("(at least 4 points are required)");
553  vpERROR_TRACE("Not enough point (%d) to compute the pose ", pose.listP.size());
554  throw(vpPoseException(vpPoseException::notEnoughPointError, "Not enough point (%d) to compute the pose by ransac",
555  pose.listP.size()));
556  }
557  else {
558  pose.setUseParallelRansac(useParallelRansac);
559  pose.setNbParallelRansacThreads(nthreads);
560  // Since we add duplicate points, we need to check for degenerate configuration
562  pose.setRansacMaxTrials(maxNbTrials);
563  pose.setRansacNbInliersToReachConsensus(numberOfInlierToReachAConsensus);
564  pose.setRansacThreshold(threshold);
565  pose.computePose(vpPose::RANSAC, cMo, func);
566  ninliers = pose.getRansacNbInliers();
567  listInliers = pose.getRansacInliers();
568  }
569 }
void track(const vpHomogeneousMatrix &cMo)
Implementation of an homogeneous matrix and operations on such kind of matrices.
static bool isNaN(double value)
Definition: vpMath.cpp:93
static double sqr(double x)
Definition: vpMath.h:201
static bool nul(double x, double threshold=0.001)
Definition: vpMath.h:440
Class that defines a 3D point in the object frame and allows forward projection of a 3D point in the ...
Definition: vpPoint.h:77
double get_oX() const
Get the point oX coordinate in the object frame.
Definition: vpPoint.cpp:450
void set_x(double x)
Set the point x coordinate in the image plane.
Definition: vpPoint.cpp:500
double get_y() const
Get the point y coordinate in the image plane.
Definition: vpPoint.cpp:461
double get_oZ() const
Get the point oZ coordinate in the object frame.
Definition: vpPoint.cpp:454
double get_x() const
Get the point x coordinate in the image plane.
Definition: vpPoint.cpp:459
double get_oY() const
Get the point oY coordinate in the object frame.
Definition: vpPoint.cpp:452
void setWorldCoordinates(double oX, double oY, double oZ)
Definition: vpPoint.cpp:110
void set_y(double y)
Set the point y coordinate in the image plane.
Definition: vpPoint.cpp:502
Error that can be emitted by the vpPose class and its derivatives.
@ notEnoughPointError
Not enough points to compute the pose.
@ notInitializedError
Something is not initialized.
Class used for pose computation from N points (pose from point only). Some of the algorithms implemen...
Definition: vpPose.h:79
void setRansacMaxTrials(const int &rM)
Definition: vpPose.h:405
static int computeRansacIterations(double probability, double epsilon, const int sampleSize=4, int maxIterations=2000)
void addPoint(const vpPoint &P)
Definition: vpPose.cpp:93
void setRansacNbInliersToReachConsensus(const unsigned int &nbC)
Definition: vpPose.h:386
bool poseRansac(vpHomogeneousMatrix &cMo, bool(*func)(const vpHomogeneousMatrix &)=nullptr)
@ RANSAC
Definition: vpPose.h:89
@ DEMENTHON_LAGRANGE_VIRTUAL_VS
Definition: vpPose.h:100
void setCovarianceComputation(const bool &flag)
Definition: vpPose.h:428
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, bool useParallelRansac=true, unsigned int nthreads=0, bool(*func)(const vpHomogeneousMatrix &)=nullptr)
unsigned int npt
Number of point used in pose computation.
Definition: vpPose.h:115
std::list< vpPoint > listP
Array of point (use here class vpPoint)
Definition: vpPose.h:116
double computeResidual(const vpHomogeneousMatrix &cMo) const
Compute and return the sum of squared residuals expressed in meter^2 for the pose matrix cMo.
Definition: vpPose.cpp:285
void setRansacFilterFlag(const RANSAC_FILTER_FLAGS &flag)
Definition: vpPose.h:459
@ CHECK_DEGENERATE_POINTS
Definition: vpPose.h:112
@ PREFILTER_DEGENERATE_POINTS
Definition: vpPose.h:111
unsigned int getRansacNbInliers() const
Definition: vpPose.h:410
void setUseParallelRansac(bool use)
Definition: vpPose.h:490
std::vector< vpPoint > getRansacInliers() const
Definition: vpPose.h:420
bool computePose(vpPoseMethodType method, vpHomogeneousMatrix &cMo, bool(*func)(const vpHomogeneousMatrix &)=nullptr)
Definition: vpPose.cpp:333
void setNbParallelRansacThreads(int nb)
Definition: vpPose.h:476
void setRansacThreshold(const double &t)
Definition: vpPose.h:391
vpColVector p
Definition: vpTracker.h:67
#define vpERROR_TRACE
Definition: vpDebug.h:388