Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
tutorial-pf-curve-fitting-all.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 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// System includes
34#include <algorithm>
35#include <vector>
36
37// ViSP includes
38#include <visp3/core/vpConfig.h>
39#include <visp3/core/vpException.h>
40#include <visp3/core/vpMath.h>
41#include <visp3/core/vpMouseButton.h>
42#include <visp3/core/vpTime.h>
43
44#ifdef VISP_HAVE_DISPLAY
45#include <visp3/gui/vpPlot.h>
46#endif
47
49#include <visp3/core/vpParticleFilter.h>
51
52#include "vpTutoCommonData.h"
53#include "vpTutoMeanSquareFitting.h"
54#include "vpTutoParabolaModel.h"
55#include "vpTutoSegmentation.h"
56
57#ifdef ENABLE_VISP_NAMESPACE
58using namespace VISP_NAMESPACE_NAME;
59#endif
60
61#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11) && defined(VISP_HAVE_DISPLAY)
62#ifndef DOXYGEN_SHOULD_SKIP_THIS
63namespace tutorial
64{
66
74double evaluate(const vpImagePoint &pt, const vpTutoParabolaModel &model)
75{
76 double u = pt.get_u();
77 double v = pt.get_v();
78 double v_model = model.eval(u);
79 double error = v - v_model;
80 double squareError = error * error;
81 return squareError;
82}
83
94double evaluate(const vpColVector &coeffs, const unsigned int &height, const unsigned int &width, const std::vector<vpImagePoint> &pts)
95{
96 unsigned int nbPts = static_cast<unsigned int>(pts.size());
97 vpColVector residuals(nbPts);
98 vpColVector weights(nbPts, 1.);
99 vpTutoParabolaModel model(coeffs, height, width);
100 // Compute the residuals
101 for (unsigned int i = 0; i < nbPts; ++i) {
102 double squareError = evaluate(pts[i], model);
103 residuals[i] = squareError;
104 }
105 double meanSquareError = residuals.sum() / static_cast<double>(nbPts);
106 return std::sqrt(meanSquareError);
107}
109
111
119template<typename T>
120void display(const vpColVector &coeffs, const vpImage<T> &I, const vpColor &color,
121 const unsigned int &vertPosLegend, const unsigned int &horPosLegend)
122{
123#if defined(VISP_HAVE_DISPLAY)
124 unsigned int width = I.getWidth();
125 vpTutoParabolaModel model(coeffs, I.getHeight(), I.getWidth());
126 for (unsigned int u = 0; u < width; ++u) {
127 unsigned int v = static_cast<unsigned int>(model.eval(u));
128 vpDisplay::displayPoint(I, v, u, color, 1);
129 vpDisplay::displayText(I, vertPosLegend, horPosLegend, "Particle Filter model", color);
130 }
131#else
132 (void)coeffs;
133 (void)I;
134 (void)color;
135 (void)vertPosLegend;
136 (void)horPosLegend;
137#endif
138}
140
142
149std::vector<vpImagePoint> automaticInitialization(tutorial::vpTutoCommonData &data)
150{
151 // Initialization-related variables
152 const unsigned int minNbPts = data.m_degree + 1;
153 const unsigned int nbPtsToUse = 10 * minNbPts;
154 std::vector<vpImagePoint> initPoints;
155
156 // Perform HSV segmentation
157 tutorial::performSegmentationHSV(data);
158
159 // Extracting the skeleton of the mask
160 std::vector<vpImagePoint> edgePoints = tutorial::extractSkeleton(data);
161 unsigned int nbEdgePoints = static_cast<unsigned int>(edgePoints.size());
162
163 if (nbEdgePoints < nbPtsToUse) {
164 return edgePoints;
165 }
166
167 // Uniformly extract init points
168 auto ptHasLowerU = [](const vpImagePoint &ptA, const vpImagePoint &ptB) {
169 return ptA.get_u() < ptB.get_u();
170 };
171 std::sort(edgePoints.begin(), edgePoints.end(), ptHasLowerU);
172
173 unsigned int idStart, idStop;
174 if (nbEdgePoints > nbPtsToUse + 20) {
175 // Avoid extreme points in case it's noise
176 idStart = 10;
177 idStop = static_cast<unsigned int>(edgePoints.size()) - 10;
178 }
179 else {
180 // We need to take all the points because we don't have enough
181 idStart = 0;
182 idStop = static_cast<unsigned int>(edgePoints.size());
183 }
184
185 // Sample uniformly the points starting from the left of the image to the right
186 unsigned int sizeWindow = idStop - idStart + 1;
187 unsigned int step = sizeWindow / (nbPtsToUse - 1);
188 for (unsigned int id = idStart; id <= idStop; id += step) {
189 initPoints.push_back(edgePoints[id]);
190 }
191 return initPoints;
192}
193
200std::vector<vpImagePoint> manualInitialization(const tutorial::vpTutoCommonData &data)
201{
202 // Interaction variables
203 const bool waitForClick = true;
204 vpImagePoint ipClick;
206
207 // Display variables
208 const unsigned int sizeCross = 10;
209 const unsigned int thicknessCross = 2;
210 const vpColor colorCross = vpColor::red;
211
212 // Initialization-related variables
213 const unsigned int minNbPts = data.m_degree + 1;
214 std::vector<vpImagePoint> initPoints;
215
216 bool notEnoughPoints = true;
217 while (notEnoughPoints) {
218 // Initial display of the images
219 vpDisplay::display(data.m_I_orig);
220
221 // Display the how-to
222 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);
223 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend + data.m_legendOffset, "A middle click reinitialize the list of init points.", data.m_colorLegend);
224 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);
225
226 // Display the already selected points
227 unsigned int nbInitPoints = static_cast<unsigned int>(initPoints.size());
228 for (unsigned int i = 0; i < nbInitPoints; ++i) {
229 vpDisplay::displayCross(data.m_I_orig, initPoints[i], sizeCross, colorCross, thicknessCross);
230 }
231
232 // Update the display
233 vpDisplay::flush(data.m_I_orig);
234
235 // Get the user input
236 vpDisplay::getClick(data.m_I_orig, ipClick, button, waitForClick);
237
238 // Either add the clicked point to the list of initial points or stop the loop if enough points are available
239 switch (button) {
241 initPoints.push_back(ipClick);
242 break;
244 initPoints.clear();
245 break;
247 (initPoints.size() >= minNbPts ? notEnoughPoints = false : notEnoughPoints = true);
248 break;
249 default:
250 break;
251 }
252 }
253
254 return initPoints;
255}
256
265vpColVector computeInitialGuess(tutorial::vpTutoCommonData &data)
266{
267 // Vector that contains the init points
268 std::vector<vpImagePoint> initPoints;
269
270#ifdef VISP_HAVE_DISPLAY
271 // Interaction variables
272 const bool waitForClick = true;
273 vpImagePoint ipClick;
275
276 // Display variables
277 const unsigned int sizeCross = 10;
278 const unsigned int thicknessCross = 2;
279 const vpColor colorCross = vpColor::red;
280
281 bool automaticInit = false;
282
283 // Initial display of the images
284 vpDisplay::display(data.m_I_orig);
285 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);
286
287 // Update the display
288 vpDisplay::flush(data.m_I_orig);
289
290 // Get the user input
291 vpDisplay::getClick(data.m_I_orig, ipClick, button, waitForClick);
292
293 // Either use the automatic initialization or the manual one depending on the user input
294 switch (button) {
296 automaticInit = false;
297 break;
299 automaticInit = true;
300 break;
301 default:
302 break;
303 }
304
305 if (automaticInit) {
306 // Get automatically the init points from the segmented image
307 initPoints = tutorial::automaticInitialization(data);
308 }
309 else {
310 // Get manually the init points from the original image
311 initPoints = tutorial::manualInitialization(data);
312 }
313
314#else
315 // Get the init points from the segmented image
316 initPoints = tutorial::automaticInitialization(data);
317#endif
318
319 // Compute the coefficients of the parabola using Least-Mean-Square minimization.
320 tutorial::vpTutoMeanSquareFitting lmsFitter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
321 lmsFitter.fit(initPoints);
322 vpColVector X0 = lmsFitter.getCoeffs();
323 std::cout << "---[Initial fit]---" << std::endl;
324 std::cout << lmsFitter.getModel();
325 std::cout << "---[Initial fit]---" << std::endl;
326
327 // Display info about the initialization
328 vpDisplay::display(data.m_I_orig);
329 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend, "Here are the points selected for the initialization.", data.m_colorLegend);
330 size_t nbInitPoints = initPoints.size();
331 for (size_t i = 0; i < nbInitPoints; ++i) {
332 const vpImagePoint &ip = initPoints[i];
333 vpDisplay::displayCross(data.m_I_orig, ip, sizeCross, colorCross, thicknessCross);
334 }
335
336 // Update display and wait for click
337 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()));
338 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend + data.m_legendOffset, "A click to continue.", data.m_colorLegend);
339 vpDisplay::flush(data.m_I_orig);
340 vpDisplay::getClick(data.m_I_orig, waitForClick);
341
342 return X0;
343}
345
347vpColVector fx(const vpColVector &coeffs, const double &/*dt*/)
348{
349 vpColVector updatedCoeffs = coeffs; // We use a constant position model
350 return updatedCoeffs;
351}
353
355class vpTutoAverageFunctor
356{
357public:
358 vpTutoAverageFunctor(const unsigned int &degree, const unsigned int &height, const unsigned int &width)
359 : m_degree(degree)
360 , m_height(height)
361 , m_width(width)
362 { }
363
373 vpColVector averagePolynomials(const std::vector<vpColVector> &particles, const std::vector<double> &weights, const vpParticleFilter<std::vector<vpImagePoint>>::vpStateAddFunction &/**/)
374 {
375 const unsigned int nbParticles = static_cast<unsigned int>(particles.size());
376 const double nbParticlesAsDOuble = static_cast<double>(nbParticles);
377 // Compute the sum of the weights to be able to determine the "importance" of a particle with regard to the whole set
378 const double sumWeight = std::accumulate(weights.begin(), weights.end(), 0.);
379
380 // Defining the total number of control points we want to generate
381 const double nbPointsForAverage = 10. * nbParticlesAsDOuble;
382 std::vector<vpImagePoint> initPoints;
383
384 // Creating control points by each particle
385 for (unsigned int i = 0; i < nbParticles; ++i) {
386 // 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
387 double nbPoints = std::floor(weights[i] * nbPointsForAverage / sumWeight);
388 if (nbPoints > 1.) {
389 // The particle has a weight high enough to deserve more than one points
390 vpTutoParabolaModel curve(particles[i], m_height, m_width);
391 double widthAsDouble = static_cast<double>(m_width);
392 // Uniform sampling of the control points along the polynomial model
393 double step = widthAsDouble / (nbPoints - 1.);
394 for (double u = 0.; u < widthAsDouble; u += step) {
395 double v = curve.eval(u);
396 vpImagePoint pt(v, u);
397 initPoints.push_back(pt);
398 }
399 }
400 else if (vpMath::equal(nbPoints, 1.)) {
401 // The weight of the particle make it have only one control point
402 // We sample it at the middle of the image
403 vpTutoParabolaModel curve(particles[i], m_height, m_width);
404 double u = static_cast<double>(m_width) / 2.;
405 double v = curve.eval(u);
406 vpImagePoint pt(v, u);
407 initPoints.push_back(pt);
408 }
409 }
410 // We use Least-Mean Square minimization to compute the polynomial model that best fits all the control points
411 vpTutoMeanSquareFitting lms(m_degree, m_height, m_width);
412 lms.fit(initPoints);
413 return lms.getCoeffs();
414 }
415
416private:
417 unsigned int m_degree;
418 unsigned int m_height;
419 unsigned int m_width;
420};
422
424class vpTutoLikelihoodFunctor
425{
426public:
434 vpTutoLikelihoodFunctor(const double &stdev, const unsigned int &height, const unsigned int &width)
435 : m_height(height)
436 , m_width(width)
437 {
438 double sigmaDistanceSquared = stdev * stdev;
439 m_constantDenominator = 1. / std::sqrt(2. * M_PI * sigmaDistanceSquared);
440 m_constantExpDenominator = -1. / (2. * sigmaDistanceSquared);
441 }
442
444
456 double likelihood(const vpColVector &coeffs, const std::vector<vpImagePoint> &meas)
457 {
458 double likelihood = 0.;
459 unsigned int nbPoints = static_cast<unsigned int>(meas.size());
460
461 // Generate a model from the coefficients stored in the particle state
462 vpTutoParabolaModel model(coeffs, m_height, m_width);
463
464 // Compute the residual between each measurement point and its equivalent in the model
465 vpColVector residuals(nbPoints);
466 for (unsigned int i = 0; i < nbPoints; ++i) {
467 double squareError = tutorial::evaluate(meas[i], model);
468 residuals[i] = squareError;
469 }
470
471 // Use Tukey M-estimator to be robust against outliers
472 vpRobust Mestimator;
473 vpColVector w(nbPoints, 1.);
474 Mestimator.MEstimator(vpRobust::TUKEY, residuals, w);
475 double sumError = w.hadamard(residuals).sum();
476
477 // Compute the likelihood as a Gaussian function
478 likelihood = std::exp(m_constantExpDenominator * sumError / w.sum()) * m_constantDenominator;
479 likelihood = std::min(likelihood, 1.0); // Clamp to have likelihood <= 1.
480 likelihood = std::max(likelihood, 0.); // Clamp to have likelihood >= 0.
481 return likelihood;
482 }
484private:
485 double m_constantDenominator;
486 double m_constantExpDenominator;
487 unsigned int m_height;
488 unsigned int m_width;
489};
491}
492#endif
493
494int main(const int argc, const char *argv[])
495{
496 tutorial::vpTutoCommonData data;
497 int returnCode = data.init(argc, argv);
498 if (returnCode != tutorial::vpTutoCommonData::SOFTWARE_CONTINUE) {
499 return returnCode;
500 }
501 tutorial::vpTutoMeanSquareFitting lmsFitter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
502 const unsigned int vertOffset = static_cast<unsigned int>(data.m_legendOffset.get_i());
503 const unsigned int horOffset = static_cast<unsigned int>(data.m_ipLegend.get_j());
504 const unsigned int legendLmsVert = data.m_I_orig.getHeight() - 3 * vertOffset;
505 const unsigned int legendLmsHor = horOffset;
506 const unsigned int legendPFVert = data.m_I_orig.getHeight() - 2 * vertOffset, legendPFHor = horOffset;
507
508 // Initialize the attributes of the PF
510 vpColVector X0 = tutorial::computeInitialGuess(data);
512
514 const double maxDistanceForLikelihood = data.m_pfMaxDistanceForLikelihood; // The maximum allowed distance between a particle and the measurement, leading to a likelihood equal to 0..
515 const double sigmaLikelihood = maxDistanceForLikelihood / 3.; // The standard deviation of likelihood function.
516 const unsigned int nbParticles = data.m_pfN; // Number of particles to use
517 std::vector<double> stdevsPF; // Standard deviation for each state component
518 for (unsigned int i = 0; i < data.m_degree + 1; ++i) {
519 double ampliMax = data.m_pfRatiosAmpliMax[i] * X0[i];
520 stdevsPF.push_back(ampliMax / 3.);
521 }
522 unsigned long seedPF; // Seed for the random generators of the PF
523 const float period = 33.3f; // 33.3ms i.e. 30Hz
524 if (data.m_pfSeed < 0) {
525 seedPF = static_cast<unsigned long>(vpTime::measureTimeMicros());
526 }
527 else {
528 seedPF = data.m_pfSeed;
529 }
530 const int nbThread = data.m_pfNbThreads;
532
534 vpParticleFilter<std::vector<vpImagePoint>>::vpProcessFunction processFunc = tutorial::fx;
535 tutorial::vpTutoLikelihoodFunctor likelihoodFtor(sigmaLikelihood, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
536 using std::placeholders::_1;
537 using std::placeholders::_2;
538 vpParticleFilter<std::vector<vpImagePoint>>::vpLikelihoodFunction likelihoodFunc = std::bind(&tutorial::vpTutoLikelihoodFunctor::likelihood, &likelihoodFtor, _1, _2);
539 vpParticleFilter<std::vector<vpImagePoint>>::vpResamplingConditionFunction checkResamplingFunc = vpParticleFilter<std::vector<vpImagePoint>>::simpleResamplingCheck;
540 vpParticleFilter<std::vector<vpImagePoint>>::vpResamplingFunction resamplingFunc = vpParticleFilter<std::vector<vpImagePoint>>::simpleImportanceResampling;
541 tutorial::vpTutoAverageFunctor averageCpter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
542 using std::placeholders::_3;
543 vpParticleFilter<std::vector<vpImagePoint>>::vpFilterFunction meanFunc = std::bind(&tutorial::vpTutoAverageFunctor::averagePolynomials, &averageCpter, _1, _2, _3);
545
547 // Initialize the PF
548 vpParticleFilter<std::vector<vpImagePoint>> filter(nbParticles, stdevsPF, seedPF, nbThread);
549 filter.init(X0, processFunc, likelihoodFunc, checkResamplingFunc, resamplingFunc, meanFunc);
551
553#ifdef VISP_HAVE_DISPLAY
554 unsigned int plotHeight = 350, plotWidth = 350;
555 int plotXpos = static_cast<int>(data.m_legendOffset.get_u());
556 int plotYpos = static_cast<int>(data.m_I_orig.getHeight() + 4. * data.m_legendOffset.get_v());
557 vpPlot plot(1, plotHeight, plotWidth, plotXpos, plotYpos, "Root mean-square error");
558 plot.initGraph(0, 2);
559 plot.setLegend(0, 0, "LMS estimator");
560 plot.setColor(0, 0, vpColor::gray);
561 plot.setLegend(0, 1, "PF estimator");
562 plot.setColor(0, 1, vpColor::red);
563#endif
565
566 bool run = true;
567 unsigned int nbIter = 0;
568 double meanDtLMS = 0., meanDtPF = 0.;
569 double meanRootMeanSquareErrorLMS = 0., meanRootMeanSquareErrorPF = 0.;
570 while (!data.m_grabber.end() && run) {
571 std::cout << "Iter " << nbIter << std::endl;
572 data.m_grabber.acquire(data.m_I_orig);
573
574 tutorial::performSegmentationHSV(data);
575
577 std::vector<vpImagePoint> edgePoints = tutorial::extractSkeleton(data);
578
580 std::vector<vpImagePoint> noisyEdgePoints = tutorial::addSaltAndPepperNoise(edgePoints, data);
581
582#ifdef VISP_HAVE_DISPLAY
584 vpDisplay::display(data.m_I_orig);
585 vpDisplay::display(data.m_I_segmented);
586 vpDisplay::display(data.m_IskeletonNoisy);
587#endif
588
590 double tLms = vpTime::measureTimeMs();
591 lmsFitter.fit(noisyEdgePoints);
592 double dtLms = vpTime::measureTimeMs() - tLms;
593 double lmsRootMeanSquareError = lmsFitter.evaluate(edgePoints);
594 std::cout << " [Least-Mean Square method] " << std::endl;
595 std::cout << " Coeffs = [" << lmsFitter.getCoeffs().transpose() << " ]" << std::endl;
596 std::cout << " Root Mean Square Error = " << lmsRootMeanSquareError << " pixels" << std::endl;
597 std::cout << " Fitting duration = " << dtLms << " ms" << std::endl;
598 meanDtLMS += dtLms;
599 meanRootMeanSquareErrorLMS += lmsRootMeanSquareError;
600
602 double tPF = vpTime::measureTimeMs();
604 filter.filter(noisyEdgePoints, period);
606 double dtPF = vpTime::measureTimeMs() - tPF;
607
609 vpColVector Xest = filter.computeFilteredState();
611
613 double pfError = tutorial::evaluate(Xest, data.m_I_orig.getHeight(), data.m_I_orig.getWidth(), edgePoints);
615 std::cout << " [Particle Filter method] " << std::endl;
616 std::cout << " Coeffs = [" << Xest.transpose() << " ]" << std::endl;
617 std::cout << " Root Mean Square Error = " << pfError << " pixels" << std::endl;
618 std::cout << " Fitting duration = " << dtPF << " ms" << std::endl;
619 meanDtPF += dtPF;
620 meanRootMeanSquareErrorPF += pfError;
621
622#ifdef VISP_HAVE_DISPLAY
623 // Update image overlay
624 lmsFitter.display<unsigned char>(data.m_IskeletonNoisy, vpColor::gray, legendLmsVert, legendLmsHor);
625 tutorial::display(Xest, data.m_IskeletonNoisy, vpColor::red, legendPFVert, legendPFHor);
626
627 // Update plot
628 plot.plot(0, 0, nbIter, lmsRootMeanSquareError);
629 plot.plot(0, 1, nbIter, pfError);
630 // Display the images with overlayed info
631 data.displayLegend(data.m_I_orig);
632 vpDisplay::flush(data.m_I_orig);
633 vpDisplay::flush(data.m_I_segmented);
634 vpDisplay::flush(data.m_IskeletonNoisy);
635 run = data.manageClicks(data.m_I_orig, data.m_stepbystep);
636#endif
637 ++nbIter;
638 }
639
640 double iterAsDouble = static_cast<double>(nbIter);
641
642 std::cout << std::endl << std::endl << "-----[Statistics summary]-----" << std::endl;
643
644 std::cout << " [LMS method] " << std::endl;
645 std::cout << " Average Root Mean Square Error = " << meanRootMeanSquareErrorLMS / iterAsDouble << " pixels" << std::endl;
646 std::cout << " Average fitting duration = " << meanDtLMS / iterAsDouble << " ms" << std::endl;
647
648 std::cout << " [Particle Filter method] " << std::endl;
649 std::cout << " Average Root Mean Square Error = " << meanRootMeanSquareErrorPF / iterAsDouble << " pixels" << std::endl;
650 std::cout << " Average fitting duration = " << meanDtPF / iterAsDouble << " ms" << std::endl;
651
652#ifdef VISP_HAVE_DISPLAY
653 if (data.m_grabber.end() && (!data.m_stepbystep)) {
655 vpDisplay::display(data.m_I_orig);
656 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend, "End of sequence reached. Click to exit.", data.m_colorLegend);
657
659 vpDisplay::flush(data.m_I_orig);
660
662 vpDisplay::getClick(data.m_I_orig, true);
663 }
664#endif
665 return 0;
666}
667#else
668int main()
669{
670 std::cerr << "ViSP must be compiled with C++ standard >= C++11 to use this tutorial." << std::endl;
671 std::cerr << "ViSP must also have a 3rd party enabling display features, such as X11 or OpenCV." << std::endl;
672 return EXIT_FAILURE;
673}
674#endif
Implementation of column vector and the associated operations.
static const vpColor red
Definition vpColor.h:198
static const vpColor gray
Definition vpColor.h:195
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)
double get_u() const
double get_v() const
unsigned int getWidth() const
Definition vpImage.h:242
unsigned int getHeight() const
Definition vpImage.h:181
static bool equal(double x, double y, double threshold=0.001)
Definition vpMath.h:470
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:117
@ 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 measureTimeMs()
VISP_EXPORT double measureTimeMicros()