38 #include <visp3/core/vpConfig.h>
39 #include <visp3/core/vpException.h>
40 #include <visp3/core/vpMouseButton.h>
41 #include <visp3/core/vpTime.h>
43 #ifdef VISP_HAVE_DISPLAY
44 #include <visp3/gui/vpPlot.h>
48 #include <visp3/core/vpParticleFilter.h>
51 #include "vpTutoCommonData.h"
52 #include "vpTutoMeanSquareFitting.h"
53 #include "vpTutoParabolaModel.h"
54 #include "vpTutoSegmentation.h"
56 #ifdef ENABLE_VISP_NAMESPACE
60 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11) && defined(VISP_HAVE_DISPLAY)
61 #ifndef DOXYGEN_SHOULD_SKIP_THIS
72 double evaluate(
const vpImagePoint &pt,
const vpTutoParabolaModel &model)
74 double u = pt.
get_u();
75 double v = pt.
get_v();
76 double v_model = model.eval(u);
77 double error = v - v_model;
78 double squareError = error * error;
92 double evaluate(
const vpColVector &coeffs,
const unsigned int &height,
const unsigned int &width,
const std::vector<vpImagePoint> &pts)
94 unsigned int nbPts =
static_cast<unsigned int>(pts.size());
97 vpTutoParabolaModel model(coeffs, height, width);
99 for (
unsigned int i = 0; i < nbPts; ++i) {
100 double squareError = evaluate(pts[i], model);
101 residuals[i] = squareError;
103 double meanSquareError = residuals.sum() /
static_cast<double>(nbPts);
104 return std::sqrt(meanSquareError);
119 const unsigned int &vertPosLegend,
const unsigned int &horPosLegend)
121 #if defined(VISP_HAVE_DISPLAY)
124 for (
unsigned int u = 0; u < width; ++u) {
125 unsigned int v =
static_cast<unsigned int>(model.eval(u));
147 std::vector<vpImagePoint> automaticInitialization(tutorial::vpTutoCommonData &data)
150 const unsigned int minNbPts = data.m_degree + 1;
151 const unsigned int nbPtsToUse = 10 * minNbPts;
152 std::vector<vpImagePoint> initPoints;
155 tutorial::performSegmentationHSV(data);
158 std::vector<vpImagePoint> edgePoints = tutorial::extractSkeleton(data);
159 unsigned int nbEdgePoints =
static_cast<unsigned int>(edgePoints.size());
161 if (nbEdgePoints < nbPtsToUse) {
167 return ptA.
get_u() < ptB.get_u();
169 std::sort(edgePoints.begin(), edgePoints.end(), ptHasLowerU);
171 unsigned int idStart, idStop;
172 if (nbEdgePoints > nbPtsToUse + 20) {
175 idStop =
static_cast<unsigned int>(edgePoints.size()) - 10;
180 idStop =
static_cast<unsigned int>(edgePoints.size());
184 unsigned int sizeWindow = idStop - idStart + 1;
185 unsigned int step = sizeWindow / (nbPtsToUse - 1);
186 for (
unsigned int id = idStart;
id <= idStop;
id += step) {
187 initPoints.push_back(edgePoints[
id]);
198 std::vector<vpImagePoint> manualInitialization(
const tutorial::vpTutoCommonData &data)
201 const bool waitForClick =
true;
206 const unsigned int sizeCross = 10;
207 const unsigned int thicknessCross = 2;
211 const unsigned int minNbPts = data.m_degree + 1;
212 std::vector<vpImagePoint> initPoints;
214 bool notEnoughPoints =
true;
215 while (notEnoughPoints) {
220 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);
221 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend + data.m_legendOffset,
"A middle click reinitialize the list of init points.", data.m_colorLegend);
222 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 unsigned int nbInitPoints =
static_cast<unsigned int>(initPoints.size());
226 for (
unsigned int i = 0; i < nbInitPoints; ++i) {
238 case vpMouseButton::vpMouseButtonType::button1:
239 initPoints.push_back(ipClick);
241 case vpMouseButton::vpMouseButtonType::button2:
244 case vpMouseButton::vpMouseButtonType::button3:
245 (initPoints.size() >= minNbPts ? notEnoughPoints = false : notEnoughPoints =
true);
263 vpColVector computeInitialGuess(tutorial::vpTutoCommonData &data)
266 std::vector<vpImagePoint> initPoints;
268 #ifdef VISP_HAVE_DISPLAY
270 const bool waitForClick =
true;
275 const unsigned int sizeCross = 10;
276 const unsigned int thicknessCross = 2;
279 bool automaticInit =
false;
283 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);
293 case vpMouseButton::vpMouseButtonType::button1:
294 automaticInit =
false;
296 case vpMouseButton::vpMouseButtonType::button3:
297 automaticInit =
true;
305 initPoints = tutorial::automaticInitialization(data);
309 initPoints = tutorial::manualInitialization(data);
314 initPoints = tutorial::automaticInitialization(data);
318 tutorial::vpTutoMeanSquareFitting lmsFitter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
319 lmsFitter.fit(initPoints);
321 std::cout <<
"---[Initial fit]---" << std::endl;
322 std::cout << lmsFitter.getModel();
323 std::cout <<
"---[Initial fit]---" << std::endl;
327 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend,
"Here are the points selected for the initialization.", data.m_colorLegend);
328 size_t nbInitPoints = initPoints.size();
329 for (
size_t i = 0; i < nbInitPoints; ++i) {
335 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()));
336 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend + data.m_legendOffset,
"A click to continue.", data.m_colorLegend);
348 return updatedCoeffs;
353 class vpTutoAverageFunctor
356 vpTutoAverageFunctor(
const unsigned int °ree,
const unsigned int &height,
const unsigned int &width)
371 vpColVector averagePolynomials(
const std::vector<vpColVector> &particles,
const std::vector<double> &weights,
const vpParticleFilter<std::vector<vpImagePoint>>::vpStateAddFunction &)
373 const unsigned int nbParticles =
static_cast<unsigned int>(particles.size());
374 const double nbParticlesAsDOuble =
static_cast<double>(nbParticles);
376 const double sumWeight = std::accumulate(weights.begin(), weights.end(), 0.);
379 const double nbPointsForAverage = 10. * nbParticlesAsDOuble;
380 std::vector<vpImagePoint> initPoints;
383 for (
unsigned int i = 0; i < nbParticles; ++i) {
385 double nbPoints = std::floor(weights[i] * nbPointsForAverage / sumWeight);
388 vpTutoParabolaModel curve(particles[i], m_height, m_width);
389 double widthAsDouble =
static_cast<double>(m_width);
391 double step = widthAsDouble / (nbPoints - 1.);
392 for (
double u = 0.; u < widthAsDouble; u += step) {
393 double v = curve.eval(u);
395 initPoints.push_back(pt);
398 else if (nbPoints == 1.) {
401 vpTutoParabolaModel curve(particles[i], m_height, m_width);
402 double u =
static_cast<double>(m_width) / 2.;
403 double v = curve.eval(u);
405 initPoints.push_back(pt);
409 vpTutoMeanSquareFitting lms(m_degree, m_height, m_width);
411 return lms.getCoeffs();
415 unsigned int m_degree;
416 unsigned int m_height;
417 unsigned int m_width;
422 class vpTutoLikelihoodFunctor
432 vpTutoLikelihoodFunctor(
const double &stdev,
const unsigned int &height,
const unsigned int &width)
436 double sigmaDistanceSquared = stdev * stdev;
437 m_constantDenominator = 1. / std::sqrt(2. * M_PI * sigmaDistanceSquared);
438 m_constantExpDenominator = -1. / (2. * sigmaDistanceSquared);
454 double likelihood(
const vpColVector &coeffs,
const std::vector<vpImagePoint> &meas)
456 double likelihood = 0.;
457 unsigned int nbPoints =
static_cast<unsigned int>(meas.size());
460 vpTutoParabolaModel model(coeffs, m_height, m_width);
464 for (
unsigned int i = 0; i < nbPoints; ++i) {
465 double squareError = tutorial::evaluate(meas[i], model);
466 residuals[i] = squareError;
473 double sumError = w.hadamard(residuals).sum();
476 likelihood = std::exp(m_constantExpDenominator * sumError / w.sum()) * m_constantDenominator;
477 likelihood = std::min(likelihood, 1.0);
478 likelihood = std::max(likelihood, 0.);
483 double m_constantDenominator;
484 double m_constantExpDenominator;
485 unsigned int m_height;
486 unsigned int m_width;
492 int main(
const int argc,
const char *argv[])
494 tutorial::vpTutoCommonData data;
495 int returnCode = data.init(argc, argv);
496 if (returnCode != tutorial::vpTutoCommonData::SOFTWARE_CONTINUE) {
499 tutorial::vpTutoMeanSquareFitting lmsFitter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
500 const unsigned int vertOffset =
static_cast<unsigned int>(data.m_legendOffset.get_i());
501 const unsigned int horOffset =
static_cast<unsigned int>(data.m_ipLegend.get_j());
502 const unsigned int legendLmsVert = data.m_I_orig.getHeight() - 3 * vertOffset;
503 const unsigned int legendLmsHor = horOffset;
504 const unsigned int legendPFVert = data.m_I_orig.getHeight() - 2 * vertOffset, legendPFHor = horOffset;
508 vpColVector X0 = tutorial::computeInitialGuess(data);
512 const double maxDistanceForLikelihood = data.m_pfMaxDistanceForLikelihood;
513 const double sigmaLikelihood = maxDistanceForLikelihood / 3.;
514 const unsigned int nbParticles = data.m_pfN;
515 std::vector<double> stdevsPF;
516 for (
unsigned int i = 0; i < data.m_degree + 1; ++i) {
517 double ampliMax = data.m_pfRatiosAmpliMax[i] * X0[i];
518 stdevsPF.push_back(ampliMax / 3.);
520 unsigned long seedPF;
521 const float period = 33.3f;
522 if (data.m_pfSeed < 0) {
526 seedPF = data.m_pfSeed;
528 const int nbThread = data.m_pfNbThreads;
533 tutorial::vpTutoLikelihoodFunctor likelihoodFtor(sigmaLikelihood, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
534 using std::placeholders::_1;
535 using std::placeholders::_2;
539 tutorial::vpTutoAverageFunctor averageCpter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
540 using std::placeholders::_3;
547 filter.init(X0, processFunc, likelihoodFunc, checkResamplingFunc, resamplingFunc, meanFunc);
551 #ifdef VISP_HAVE_DISPLAY
552 unsigned int plotHeight = 350, plotWidth = 350;
553 int plotXpos =
static_cast<int>(data.m_legendOffset.get_u());
554 int plotYpos =
static_cast<int>(data.m_I_orig.getHeight() + 4. * data.m_legendOffset.get_v());
555 vpPlot plot(1, plotHeight, plotWidth, plotXpos, plotYpos,
"Root mean-square error");
556 plot.initGraph(0, 2);
557 plot.setLegend(0, 0,
"LMS estimator");
559 plot.setLegend(0, 1,
"PF estimator");
565 unsigned int nbIter = 0;
566 double meanDtLMS = 0., meanDtPF = 0.;
567 double meanRootMeanSquareErrorLMS = 0., meanRootMeanSquareErrorPF = 0.;
568 while (!data.m_grabber.end() && run) {
569 std::cout <<
"Iter " << nbIter << std::endl;
570 data.m_grabber.acquire(data.m_I_orig);
572 tutorial::performSegmentationHSV(data);
575 std::vector<vpImagePoint> edgePoints = tutorial::extractSkeleton(data);
578 std::vector<vpImagePoint> noisyEdgePoints = tutorial::addSaltAndPepperNoise(edgePoints, data);
580 #ifdef VISP_HAVE_DISPLAY
589 lmsFitter.fit(noisyEdgePoints);
591 double lmsRootMeanSquareError = lmsFitter.evaluate(edgePoints);
592 std::cout <<
" [Least-Mean Square method] " << std::endl;
593 std::cout <<
" Coeffs = [" << lmsFitter.getCoeffs().transpose() <<
" ]" << std::endl;
594 std::cout <<
" Root Mean Square Error = " << lmsRootMeanSquareError <<
" pixels" << std::endl;
595 std::cout <<
" Fitting duration = " << dtLms <<
" ms" << std::endl;
597 meanRootMeanSquareErrorLMS += lmsRootMeanSquareError;
602 filter.filter(noisyEdgePoints, period);
611 double pfError = tutorial::evaluate(Xest, data.m_I_orig.getHeight(), data.m_I_orig.getWidth(), edgePoints);
613 std::cout <<
" [Particle Filter method] " << std::endl;
614 std::cout <<
" Coeffs = [" << Xest.
transpose() <<
" ]" << std::endl;
615 std::cout <<
" Root Mean Square Error = " << pfError <<
" pixels" << std::endl;
616 std::cout <<
" Fitting duration = " << dtPF <<
" ms" << std::endl;
618 meanRootMeanSquareErrorPF += pfError;
620 #ifdef VISP_HAVE_DISPLAY
622 lmsFitter.display<
unsigned char>(data.m_IskeletonNoisy,
vpColor::gray, legendLmsVert, legendLmsHor);
623 tutorial::display(Xest, data.m_IskeletonNoisy,
vpColor::red, legendPFVert, legendPFHor);
626 plot.plot(0, 0, nbIter, lmsRootMeanSquareError);
627 plot.plot(0, 1, nbIter, pfError);
629 data.displayLegend(data.m_I_orig);
633 run = data.manageClicks(data.m_I_orig, data.m_stepbystep);
638 double iterAsDouble =
static_cast<double>(nbIter);
640 std::cout << std::endl << std::endl <<
"-----[Statistics summary]-----" << std::endl;
642 std::cout <<
" [LMS method] " << std::endl;
643 std::cout <<
" Average Root Mean Square Error = " << meanRootMeanSquareErrorLMS / iterAsDouble <<
" pixels" << std::endl;
644 std::cout <<
" Average fitting duration = " << meanDtLMS / iterAsDouble <<
" ms" << std::endl;
646 std::cout <<
" [Particle Filter method] " << std::endl;
647 std::cout <<
" Average Root Mean Square Error = " << meanRootMeanSquareErrorPF / iterAsDouble <<
" pixels" << std::endl;
648 std::cout <<
" Average fitting duration = " << meanDtPF / iterAsDouble <<
" ms" << std::endl;
650 #ifdef VISP_HAVE_DISPLAY
651 if (data.m_grabber.end() && (!data.m_stepbystep)) {
654 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend,
"End of sequence reached. Click to exit.", data.m_colorLegend);
668 std::cerr <<
"ViSP must be compiled with C++ standard >= C++11 to use this tutorial." << std::endl;
669 std::cerr <<
"ViSP must also have a 3rd party enabling display features, such as X11 or OpenCV." << std::endl;
Implementation of column vector and the associated operations.
vpRowVector transpose() const
Class to define RGB colors available for display functionalities.
static const vpColor gray
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 of the vpImage class member functions.
unsigned int getWidth() const
unsigned int getHeight() const
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...
Contains an M-estimator and various influence function.
@ TUKEY
Tukey influence function.
void MEstimator(const vpRobustEstimatorType method, const vpColVector &residues, vpColVector &weights)
VISP_EXPORT double measureTimeMicros()
VISP_EXPORT double measureTimeMs()