Visual Servoing Platform  version 3.6.1 under development (2024-11-15)
tutorial-pf-curve-fitting-pf.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 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 
32 
33 #include <visp3/core/vpConfig.h>
34 #include <visp3/core/vpException.h>
35 #include <visp3/core/vpMouseButton.h>
36 #include <visp3/core/vpTime.h>
37 #include <visp3/core/vpUniRand.h>
38 
39 #ifdef VISP_HAVE_DISPLAY
40 #include <visp3/gui/vpPlot.h>
41 #endif
42 
44 #include <visp3/core/vpParticleFilter.h>
46 
47 #include "vpTutoCommonData.h"
48 #include "vpTutoMeanSquareFitting.h"
49 #include "vpTutoParabolaModel.h"
50 #include "vpTutoSegmentation.h"
51 
52 #ifdef ENABLE_VISP_NAMESPACE
53 using namespace VISP_NAMESPACE_NAME;
54 #endif
55 
56 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11) && defined(VISP_HAVE_DISPLAY)
57 #ifndef DOXYGEN_SHOULD_SKIP_THIS
58 namespace tutorial
59 {
61 
68 double evaluate(const vpImagePoint &pt, const vpTutoParabolaModel &model)
69 {
70  double u = pt.get_u();
71  double v = pt.get_v();
72  double v_model = model.eval(u);
73  double error = v - v_model;
74  double squareError = error * error;
75  return squareError;
76 }
77 
88 double evaluate(const vpColVector &coeffs, const unsigned int &height, const unsigned int &width, const std::vector<vpImagePoint> &pts)
89 {
90  unsigned int nbPts = static_cast<unsigned int>(pts.size());
91  vpColVector residuals(nbPts);
92  vpColVector weights(nbPts, 1.);
93  vpTutoParabolaModel model(coeffs, height, width);
94  // Compute the residuals
95  for (unsigned int i = 0; i < nbPts; ++i) {
96  double squareError = evaluate(pts[i], model);
97  residuals[i] = squareError;
98  }
99  double meanSquareError = residuals.sum() / static_cast<double>(nbPts);
100  return std::sqrt(meanSquareError);
101 }
103 
105 
113 template<typename T>
114 void display(const vpColVector &coeffs, const vpImage<T> &I, const vpColor &color,
115  const unsigned int &vertPosLegend, const unsigned int &horPosLegend)
116 {
117 #if defined(VISP_HAVE_DISPLAY)
118  unsigned int width = I.getWidth();
119  vpTutoParabolaModel model(coeffs, I.getHeight(), I.getWidth());
120  for (unsigned int u = 0; u < width; ++u) {
121  double v = model.eval(u);
122  vpDisplay::displayPoint(I, static_cast<int>(v), static_cast<int>(u), color, 1);
123  vpDisplay::displayText(I, vertPosLegend, horPosLegend, "Particle Filter model", color);
124  }
125 #else
126  (void)coeffs;
127  (void)I;
128  (void)color;
129  (void)vertPosLegend;
130  (void)horPosLegend;
131 #endif
132 }
134 
136 
143 std::vector<vpImagePoint> automaticInitialization(tutorial::vpTutoCommonData &data)
144 {
145  // Initialization-related variables
146  const unsigned int minNbPts = data.m_degree + 1;
147  const unsigned int nbPtsToUse = 10 * minNbPts;
148  std::vector<vpImagePoint> initPoints;
149 
150  // Perform HSV segmentation
151  tutorial::performSegmentationHSV(data);
152 
153  // Extracting the skeleton of the mask
154  std::vector<vpImagePoint> edgePoints = tutorial::extractSkeleton(data);
155  unsigned int nbEdgePoints = static_cast<unsigned int>(edgePoints.size());
156 
157  if (nbEdgePoints < nbPtsToUse) {
158  return edgePoints;
159  }
160 
161  // Uniformly extract init points
162  auto ptHasLowerU = [](const vpImagePoint &ptA, const vpImagePoint &ptB) {
163  return ptA.get_u() < ptB.get_u();
164  };
165  std::sort(edgePoints.begin(), edgePoints.end(), ptHasLowerU);
166 
167  unsigned int idStart, idStop;
168  if (nbEdgePoints > nbPtsToUse + 20) {
169  // Avoid extreme points in case it's noise
170  idStart = 10;
171  idStop = static_cast<unsigned int>(edgePoints.size()) - 10;
172  }
173  else {
174  // We need to take all the points because we don't have enough
175  idStart = 0;
176  idStop = static_cast<unsigned int>(edgePoints.size());
177  }
178 
179  // Sample uniformly the points starting from the left of the image to the right
180  unsigned int sizeWindow = idStop - idStart + 1;
181  unsigned int step = sizeWindow / (nbPtsToUse - 1);
182  for (unsigned int id = idStart; id <= idStop; id += step) {
183  initPoints.push_back(edgePoints[id]);
184  }
185  return initPoints;
186 }
187 
194 std::vector<vpImagePoint> manualInitialization(const tutorial::vpTutoCommonData &data)
195 {
196  // Interaction variables
197  const bool waitForClick = true;
198  vpImagePoint ipClick;
200 
201  // Display variables
202  const unsigned int sizeCross = 10;
203  const unsigned int thicknessCross = 2;
204  const vpColor colorCross = vpColor::red;
205 
206  // Initialization-related variables
207  const unsigned int minNbPts = data.m_degree + 1;
208  std::vector<vpImagePoint> initPoints;
209 
210  bool notEnoughPoints = true;
211  while (notEnoughPoints) {
212  // Initial display of the images
213  vpDisplay::display(data.m_I_orig);
214 
215  // Display the how-to
216  vpDisplay::displayText(data.m_I_orig, data.m_ipLegend, "Left click to add init point (min.: " + std::to_string(minNbPts) + "), right click to estimate the initial coefficients of the Particle Filter.", data.m_colorLegend);
217  vpDisplay::displayText(data.m_I_orig, data.m_ipLegend + data.m_legendOffset, "A middle click reinitialize the list of init points.", data.m_colorLegend);
218  vpDisplay::displayText(data.m_I_orig, data.m_ipLegend + data.m_legendOffset + data.m_legendOffset, "If not enough points have been selected, a right click has no effect.", data.m_colorLegend);
219 
220  // Display the already selected points
221  unsigned int nbInitPoints = static_cast<unsigned int>(initPoints.size());
222  for (unsigned int i = 0; i < nbInitPoints; ++i) {
223  vpDisplay::displayCross(data.m_I_orig, initPoints[i], sizeCross, colorCross, thicknessCross);
224  }
225 
226  // Update the display
227  vpDisplay::flush(data.m_I_orig);
228 
229  // Get the user input
230  vpDisplay::getClick(data.m_I_orig, ipClick, button, waitForClick);
231 
232  // Either add the clicked point to the list of initial points or stop the loop if enough points are available
233  switch (button) {
234  case vpMouseButton::vpMouseButtonType::button1:
235  initPoints.push_back(ipClick);
236  break;
237  case vpMouseButton::vpMouseButtonType::button2:
238  initPoints.clear();
239  break;
240  case vpMouseButton::vpMouseButtonType::button3:
241  (initPoints.size() >= minNbPts ? notEnoughPoints = false : notEnoughPoints = true);
242  break;
243  default:
244  break;
245  }
246  }
247 
248  return initPoints;
249 }
250 
259 vpColVector computeInitialGuess(tutorial::vpTutoCommonData &data)
260 {
261  // Vector that contains the init points
262  std::vector<vpImagePoint> initPoints;
263 
264 #ifdef VISP_HAVE_DISPLAY
265  // Interaction variables
266  const bool waitForClick = true;
267  vpImagePoint ipClick;
269 
270  // Display variables
271  const unsigned int sizeCross = 10;
272  const unsigned int thicknessCross = 2;
273  const vpColor colorCross = vpColor::red;
274 
275  bool automaticInit = false;
276 
277  // Initial display of the images
278  vpDisplay::display(data.m_I_orig);
279  vpDisplay::displayText(data.m_I_orig, data.m_ipLegend, "Left click to manually select the init points, right click to automatically initialize the PF", data.m_colorLegend);
280 
281  // Update the display
282  vpDisplay::flush(data.m_I_orig);
283 
284  // Get the user input
285  vpDisplay::getClick(data.m_I_orig, ipClick, button, waitForClick);
286 
287  // Either use the automatic initialization or the manual one depending on the user input
288  switch (button) {
289  case vpMouseButton::vpMouseButtonType::button1:
290  automaticInit = false;
291  break;
292  case vpMouseButton::vpMouseButtonType::button3:
293  automaticInit = true;
294  break;
295  default:
296  break;
297  }
298 
299  if (automaticInit) {
300  // Get automatically the init points from the segmented image
301  initPoints = tutorial::automaticInitialization(data);
302  }
303  else {
304  // Get manually the init points from the original image
305  initPoints = tutorial::manualInitialization(data);
306  }
307 
308 #else
309  // Get the init points from the segmented image
310  initPoints = tutorial::automaticInitialization(data);
311 #endif
312 
313  // Compute the coefficients of the parabola using Least-Mean-Square minimization.
314  tutorial::vpTutoMeanSquareFitting lmsFitter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
315  lmsFitter.fit(initPoints);
316  vpColVector X0 = lmsFitter.getCoeffs();
317  std::cout << "---[Initial fit]---" << std::endl;
318  std::cout << lmsFitter.getModel();
319  std::cout << "---[Initial fit]---" << std::endl;
320 
321  // Display info about the initialization
322  vpDisplay::display(data.m_I_orig);
323  vpDisplay::displayText(data.m_I_orig, data.m_ipLegend, "Here are the points selected for the initialization.", data.m_colorLegend);
324  unsigned int nbInitPoints = static_cast<unsigned int>(initPoints.size());
325  for (unsigned int i = 0; i < nbInitPoints; ++i) {
326  const vpImagePoint &ip = initPoints[i];
327  vpDisplay::displayCross(data.m_I_orig, ip, sizeCross, colorCross, thicknessCross);
328  }
329 
330  // Update display and wait for click
331  lmsFitter.display(data.m_I_orig, vpColor::red, static_cast<unsigned int>(data.m_ipLegend.get_v() + 2 * data.m_legendOffset.get_v()), static_cast<unsigned int>(data.m_ipLegend.get_u()));
332  vpDisplay::displayText(data.m_I_orig, data.m_ipLegend + data.m_legendOffset, "A click to continue.", data.m_colorLegend);
333  vpDisplay::flush(data.m_I_orig);
334  vpDisplay::getClick(data.m_I_orig, waitForClick);
335 
336  return X0;
337 }
339 
341 vpColVector fx(const vpColVector &coeffs, const double &/*dt*/)
342 {
343  vpColVector updatedCoeffs = coeffs; // We use a constant position model
344  return updatedCoeffs;
345 }
347 
349 class vpTutoAverageFunctor
350 {
351 public:
352  vpTutoAverageFunctor(const unsigned int &degree, const unsigned int &height, const unsigned int &width)
353  : m_degree(degree)
354  , m_height(height)
355  , m_width(width)
356  { }
357 
367  vpColVector averagePolynomials(const std::vector<vpColVector> &particles, const std::vector<double> &weights, const vpParticleFilter<std::vector<vpImagePoint>>::vpStateAddFunction &)
368  {
369  const unsigned int nbParticles = static_cast<unsigned int>(particles.size());
370  const double nbParticlesAsDOuble = static_cast<double>(nbParticles);
371  // Compute the sum of the weights to be able to determine the "importance" of a particle with regard to the whole set
372  const double sumWeight = std::accumulate(weights.begin(), weights.end(), 0.);
373 
374  // Defining the total number of control points we want to generate
375  const double nbPointsForAverage = 10. * nbParticlesAsDOuble;
376  std::vector<vpImagePoint> initPoints;
377 
378  // Creating control points by each particle
379  for (unsigned int i = 0; i < nbParticles; ++i) {
380  // The number of control points a particle can generate is proportional to the ratio of its weight w.r.t. the sum of the weights
381  double nbPoints = std::floor(weights[i] * nbPointsForAverage / sumWeight);
382  if (nbPoints > 1.) {
383  // The particle has a weight high enough to deserve more than one points
384  vpTutoParabolaModel curve(particles[i], m_height, m_width);
385  double widthAsDouble = static_cast<double>(m_width);
386  // Uniform sampling of the control points along the polynomial model
387  double step = widthAsDouble / (nbPoints - 1.);
388  for (double u = 0.; u < widthAsDouble; u += step) {
389  double v = curve.eval(u);
390  vpImagePoint pt(v, u);
391  initPoints.push_back(pt);
392  }
393  }
394  else if (nbPoints == 1.) {
395  // The weight of the particle make it have only one control point
396  // We sample it at the middle of the image
397  vpTutoParabolaModel curve(particles[i], m_height, m_width);
398  double u = static_cast<double>(m_width) / 2.;
399  double v = curve.eval(u);
400  vpImagePoint pt(v, u);
401  initPoints.push_back(pt);
402  }
403  }
404  // We use Least-Mean Square minimization to compute the polynomial model that best fits all the control points
405  vpTutoMeanSquareFitting lms(m_degree, m_height, m_width);
406  lms.fit(initPoints);
407  return lms.getCoeffs();
408  }
409 
410 private:
411  unsigned int m_degree;
412  unsigned int m_height;
413  unsigned int m_width;
414 };
416 
418 class vpTutoLikelihoodFunctor
419 {
420 public:
428  vpTutoLikelihoodFunctor(const double &stdev, const unsigned int &height, const unsigned int &width)
429  : m_height(height)
430  , m_width(width)
431  {
432  double sigmaDistanceSquared = stdev * stdev;
433  m_constantDenominator = 1. / std::sqrt(2. * M_PI * sigmaDistanceSquared);
434  m_constantExpDenominator = -1. / (2. * sigmaDistanceSquared);
435  }
436 
438 
450  double likelihood(const vpColVector &coeffs, const std::vector<vpImagePoint> &meas)
451  {
452  double likelihood = 0.;
453  unsigned int nbPoints = static_cast<unsigned int>(meas.size());
454 
455  // Generate a model from the coefficients stored in the particle state
456  vpTutoParabolaModel model(coeffs, m_height, m_width);
457 
458  // Compute the residual between each measurement point and its equivalent in the model
459  vpColVector residuals(nbPoints);
460  for (unsigned int i = 0; i < nbPoints; ++i) {
461  double squareError = tutorial::evaluate(meas[i], model);
462  residuals[i] = squareError;
463  }
464 
465  // Use Tukey M-estimator to be robust against outliers
466  vpRobust Mestimator;
467  vpColVector w(nbPoints, 1.);
468  Mestimator.MEstimator(vpRobust::TUKEY, residuals, w);
469  double sumError = w.hadamard(residuals).sum();
470 
471  // Compute the likelihood as a Gaussian function
472  likelihood = std::exp(m_constantExpDenominator * sumError / w.sum()) * m_constantDenominator;
473  likelihood = std::min(likelihood, 1.0); // Clamp to have likelihood <= 1.
474  likelihood = std::max(likelihood, 0.); // Clamp to have likelihood >= 0.
475  return likelihood;
476  }
478 private:
479  double m_constantDenominator;
480  double m_constantExpDenominator;
481  unsigned int m_height;
482  unsigned int m_width;
483 };
485 }
486 #endif
487 
488 int main(const int argc, const char *argv[])
489 {
490  tutorial::vpTutoCommonData data;
491  int returnCode = data.init(argc, argv);
492  if (returnCode != tutorial::vpTutoCommonData::SOFTWARE_CONTINUE) {
493  return returnCode;
494  }
495  const unsigned int vertOffset = static_cast<unsigned int>(data.m_legendOffset.get_i());
496  const unsigned int horOffset = static_cast<unsigned int>(data.m_ipLegend.get_j());
497  const unsigned int legendPFVert = data.m_I_orig.getHeight() - 2 * vertOffset, legendPFHor = horOffset;
498 
499  // Initialize the attributes of the PF
501  vpColVector X0 = tutorial::computeInitialGuess(data);
503 
505  const double maxDistanceForLikelihood = data.m_pfMaxDistanceForLikelihood; // The maximum allowed distance between a particle and the measurement, leading to a likelihood equal to 0..
506  const double sigmaLikelihood = maxDistanceForLikelihood / 3.; // The standard deviation of likelihood function.
507  const unsigned int nbParticles = data.m_pfN; // Number of particles to use
508  std::vector<double> stdevsPF; // Standard deviation for each state component
509  for (unsigned int i = 0; i < data.m_degree + 1; ++i) {
510  double ampliMax = data.m_pfRatiosAmpliMax[i] * X0[i];
511  stdevsPF.push_back(ampliMax / 3.);
512  }
513  unsigned long seedPF; // Seed for the random generators of the PF
514  const float period = 33.3f; // 33.3ms i.e. 30Hz
515  if (data.m_pfSeed < 0) {
516  seedPF = static_cast<unsigned long>(vpTime::measureTimeMicros());
517  }
518  else {
519  seedPF = data.m_pfSeed;
520  }
521  const int nbThread = data.m_pfNbThreads;
523 
525  vpParticleFilter<std::vector<vpImagePoint>>::vpProcessFunction processFunc = tutorial::fx;
526  tutorial::vpTutoLikelihoodFunctor likelihoodFtor(sigmaLikelihood, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
527  using std::placeholders::_1;
528  using std::placeholders::_2;
529  vpParticleFilter<std::vector<vpImagePoint>>::vpLikelihoodFunction likelihoodFunc = std::bind(&tutorial::vpTutoLikelihoodFunctor::likelihood, &likelihoodFtor, _1, _2);
530  vpParticleFilter<std::vector<vpImagePoint>>::vpResamplingConditionFunction checkResamplingFunc = vpParticleFilter<std::vector<vpImagePoint>>::simpleResamplingCheck;
531  vpParticleFilter<std::vector<vpImagePoint>>::vpResamplingFunction resamplingFunc = vpParticleFilter<std::vector<vpImagePoint>>::simpleImportanceResampling;
532  tutorial::vpTutoAverageFunctor averageCpter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
533  using std::placeholders::_3;
534  vpParticleFilter<std::vector<vpImagePoint>>::vpFilterFunction meanFunc = std::bind(&tutorial::vpTutoAverageFunctor::averagePolynomials, &averageCpter, _1, _2, _3);
536 
538  // Initialize the PF
539  vpParticleFilter<std::vector<vpImagePoint>> filter(nbParticles, stdevsPF, seedPF, nbThread);
540  filter.init(X0, processFunc, likelihoodFunc, checkResamplingFunc, resamplingFunc, meanFunc);
542 
544 #ifdef VISP_HAVE_DISPLAY
545  unsigned int plotHeight = 350, plotWidth = 350;
546  int plotXpos = static_cast<int>(data.m_legendOffset.get_u());
547  int plotYpos = static_cast<int>(data.m_I_orig.getHeight() + 4. * data.m_legendOffset.get_v());
548  vpPlot plot(1, plotHeight, plotWidth, plotXpos, plotYpos, "Root mean-square error");
549  plot.initGraph(0, 1);
550  plot.setLegend(0, 0, "PF estimator");
551  plot.setColor(0, 0, vpColor::red);
552 #endif
554 
555  bool run = true;
556  unsigned int nbIter = 0;
557  double meanDtPF = 0.;
558  double meanRootMeanSquareErrorPF = 0.;
559  while (!data.m_grabber.end() && run) {
560  std::cout << "Iter " << nbIter << std::endl;
561  data.m_grabber.acquire(data.m_I_orig);
562 
563  tutorial::performSegmentationHSV(data);
564 
566  std::vector<vpImagePoint> edgePoints = tutorial::extractSkeleton(data);
567 
569  std::vector<vpImagePoint> noisyEdgePoints = tutorial::addSaltAndPepperNoise(edgePoints, data);
570 
571 #ifdef VISP_HAVE_DISPLAY
573  vpDisplay::display(data.m_I_orig);
574  vpDisplay::display(data.m_I_segmented);
575  vpDisplay::display(data.m_IskeletonNoisy);
576 #endif
577 
579  double tPF = vpTime::measureTimeMs();
581  filter.filter(noisyEdgePoints, period);
583  double dtPF = vpTime::measureTimeMs() - tPF;
584 
586  vpColVector Xest = filter.computeFilteredState();
588 
590  double pfError = tutorial::evaluate(Xest, data.m_I_orig.getHeight(), data.m_I_orig.getWidth(), edgePoints);
592  std::cout << " [Particle Filter method] " << std::endl;
593  std::cout << " Coeffs = [" << Xest.transpose() << " ]" << std::endl;
594  std::cout << " Root Mean Square Error = " << pfError << " pixels" << std::endl;
595  std::cout << " Fitting duration = " << dtPF << " ms" << std::endl;
596  meanDtPF += dtPF;
597  meanRootMeanSquareErrorPF += pfError;
598 
599 #ifdef VISP_HAVE_DISPLAY
600  // Update image overlay
601  tutorial::display(Xest, data.m_IskeletonNoisy, vpColor::red, legendPFVert, legendPFHor);
602 
603  // Update plot
604  plot.plot(0, 0, nbIter, pfError);
605  // Display the images with overlayed info
606  data.displayLegend(data.m_I_orig);
607  vpDisplay::flush(data.m_I_orig);
608  vpDisplay::flush(data.m_I_segmented);
609  vpDisplay::flush(data.m_IskeletonNoisy);
610  run = data.manageClicks(data.m_I_orig, data.m_stepbystep);
611 #endif
612  ++nbIter;
613  }
614 
615  double iterAsDouble = static_cast<double>(nbIter);
616  std::cout << std::endl << std::endl << "-----[Statistics summary]-----" << std::endl;
617  std::cout << " [Particle Filter method] " << std::endl;
618  std::cout << " Average Root Mean Square Error = " << meanRootMeanSquareErrorPF / iterAsDouble << " pixels" << std::endl;
619  std::cout << " Average fitting duration = " << meanDtPF / iterAsDouble << " ms" << std::endl;
620 
621 #ifdef VISP_HAVE_DISPLAY
622  if (data.m_grabber.end() && (!data.m_stepbystep)) {
624  vpDisplay::display(data.m_I_orig);
625  vpDisplay::displayText(data.m_I_orig, data.m_ipLegend, "End of sequence reached. Click to exit.", data.m_colorLegend);
626 
628  vpDisplay::flush(data.m_I_orig);
629 
631  vpDisplay::getClick(data.m_I_orig, true);
632  }
633 #endif
634  return 0;
635 }
636 #else
637 int main()
638 {
639  std::cerr << "ViSP must be compiled with C++ standard >= C++11 to use this tutorial." << std::endl;
640  std::cerr << "ViSP must also have a 3rd party enabling display features, such as X11 or OpenCV." << std::endl;
641  return EXIT_FAILURE;
642 }
643 #endif
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
vpRowVector transpose() const
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
static const vpColor red
Definition: vpColor.h:217
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
static void flush(const vpImage< unsigned char > &I)
static void displayPoint(const vpImage< unsigned char > &I, const vpImagePoint &ip, const vpColor &color, unsigned int thickness=1)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
double get_u() const
Definition: vpImagePoint.h:136
double get_v() const
Definition: vpImagePoint.h:147
Definition of the vpImage class member functions.
Definition: vpImage.h:131
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getHeight() const
Definition: vpImage.h:181
The class permits to use a Particle Filter.
This class enables real time drawing of 2D or 3D graphics. An instance of the class open a window whi...
Definition: vpPlot.h:112
Contains an M-estimator and various influence function.
Definition: vpRobust.h:84
@ TUKEY
Tukey influence function.
Definition: vpRobust.h:89
void MEstimator(const vpRobustEstimatorType method, const vpColVector &residues, vpColVector &weights)
Definition: vpRobust.cpp:130
VISP_EXPORT double measureTimeMicros()
VISP_EXPORT double measureTimeMs()