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>
39 #ifdef VISP_HAVE_DISPLAY
40 #include <visp3/gui/vpPlot.h>
44 #include <visp3/core/vpParticleFilter.h>
47 #include "vpTutoCommonData.h"
48 #include "vpTutoMeanSquareFitting.h"
49 #include "vpTutoParabolaModel.h"
50 #include "vpTutoSegmentation.h"
52 #ifdef ENABLE_VISP_NAMESPACE
56 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11) && defined(VISP_HAVE_DISPLAY)
57 #ifndef DOXYGEN_SHOULD_SKIP_THIS
68 double evaluate(
const vpImagePoint &pt,
const vpTutoParabolaModel &model)
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;
88 double evaluate(
const vpColVector &coeffs,
const unsigned int &height,
const unsigned int &width,
const std::vector<vpImagePoint> &pts)
90 unsigned int nbPts =
static_cast<unsigned int>(pts.size());
93 vpTutoParabolaModel model(coeffs, height, width);
95 for (
unsigned int i = 0; i < nbPts; ++i) {
96 double squareError = evaluate(pts[i], model);
97 residuals[i] = squareError;
99 double meanSquareError = residuals.sum() /
static_cast<double>(nbPts);
100 return std::sqrt(meanSquareError);
115 const unsigned int &vertPosLegend,
const unsigned int &horPosLegend)
117 #if defined(VISP_HAVE_DISPLAY)
120 for (
unsigned int u = 0; u < width; ++u) {
121 double v = model.eval(u);
143 std::vector<vpImagePoint> automaticInitialization(tutorial::vpTutoCommonData &data)
146 const unsigned int minNbPts = data.m_degree + 1;
147 const unsigned int nbPtsToUse = 10 * minNbPts;
148 std::vector<vpImagePoint> initPoints;
151 tutorial::performSegmentationHSV(data);
154 std::vector<vpImagePoint> edgePoints = tutorial::extractSkeleton(data);
155 unsigned int nbEdgePoints =
static_cast<unsigned int>(edgePoints.size());
157 if (nbEdgePoints < nbPtsToUse) {
163 return ptA.
get_u() < ptB.get_u();
165 std::sort(edgePoints.begin(), edgePoints.end(), ptHasLowerU);
167 unsigned int idStart, idStop;
168 if (nbEdgePoints > nbPtsToUse + 20) {
171 idStop =
static_cast<unsigned int>(edgePoints.size()) - 10;
176 idStop =
static_cast<unsigned int>(edgePoints.size());
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]);
194 std::vector<vpImagePoint> manualInitialization(
const tutorial::vpTutoCommonData &data)
197 const bool waitForClick =
true;
202 const unsigned int sizeCross = 10;
203 const unsigned int thicknessCross = 2;
207 const unsigned int minNbPts = data.m_degree + 1;
208 std::vector<vpImagePoint> initPoints;
210 bool notEnoughPoints =
true;
211 while (notEnoughPoints) {
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);
221 unsigned int nbInitPoints =
static_cast<unsigned int>(initPoints.size());
222 for (
unsigned int i = 0; i < nbInitPoints; ++i) {
234 case vpMouseButton::vpMouseButtonType::button1:
235 initPoints.push_back(ipClick);
237 case vpMouseButton::vpMouseButtonType::button2:
240 case vpMouseButton::vpMouseButtonType::button3:
241 (initPoints.size() >= minNbPts ? notEnoughPoints = false : notEnoughPoints =
true);
259 vpColVector computeInitialGuess(tutorial::vpTutoCommonData &data)
262 std::vector<vpImagePoint> initPoints;
264 #ifdef VISP_HAVE_DISPLAY
266 const bool waitForClick =
true;
271 const unsigned int sizeCross = 10;
272 const unsigned int thicknessCross = 2;
275 bool automaticInit =
false;
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);
289 case vpMouseButton::vpMouseButtonType::button1:
290 automaticInit =
false;
292 case vpMouseButton::vpMouseButtonType::button3:
293 automaticInit =
true;
301 initPoints = tutorial::automaticInitialization(data);
305 initPoints = tutorial::manualInitialization(data);
310 initPoints = tutorial::automaticInitialization(data);
314 tutorial::vpTutoMeanSquareFitting lmsFitter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
315 lmsFitter.fit(initPoints);
317 std::cout <<
"---[Initial fit]---" << std::endl;
318 std::cout << lmsFitter.getModel();
319 std::cout <<
"---[Initial fit]---" << std::endl;
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) {
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);
344 return updatedCoeffs;
349 class vpTutoAverageFunctor
352 vpTutoAverageFunctor(
const unsigned int °ree,
const unsigned int &height,
const unsigned int &width)
367 vpColVector averagePolynomials(
const std::vector<vpColVector> &particles,
const std::vector<double> &weights,
const vpParticleFilter<std::vector<vpImagePoint>>::vpStateAddFunction &)
369 const unsigned int nbParticles =
static_cast<unsigned int>(particles.size());
370 const double nbParticlesAsDOuble =
static_cast<double>(nbParticles);
372 const double sumWeight = std::accumulate(weights.begin(), weights.end(), 0.);
375 const double nbPointsForAverage = 10. * nbParticlesAsDOuble;
376 std::vector<vpImagePoint> initPoints;
379 for (
unsigned int i = 0; i < nbParticles; ++i) {
381 double nbPoints = std::floor(weights[i] * nbPointsForAverage / sumWeight);
384 vpTutoParabolaModel curve(particles[i], m_height, m_width);
385 double widthAsDouble =
static_cast<double>(m_width);
387 double step = widthAsDouble / (nbPoints - 1.);
388 for (
double u = 0.; u < widthAsDouble; u += step) {
389 double v = curve.eval(u);
391 initPoints.push_back(pt);
394 else if (nbPoints == 1.) {
397 vpTutoParabolaModel curve(particles[i], m_height, m_width);
398 double u =
static_cast<double>(m_width) / 2.;
399 double v = curve.eval(u);
401 initPoints.push_back(pt);
405 vpTutoMeanSquareFitting lms(m_degree, m_height, m_width);
407 return lms.getCoeffs();
411 unsigned int m_degree;
412 unsigned int m_height;
413 unsigned int m_width;
418 class vpTutoLikelihoodFunctor
428 vpTutoLikelihoodFunctor(
const double &stdev,
const unsigned int &height,
const unsigned int &width)
432 double sigmaDistanceSquared = stdev * stdev;
433 m_constantDenominator = 1. / std::sqrt(2. * M_PI * sigmaDistanceSquared);
434 m_constantExpDenominator = -1. / (2. * sigmaDistanceSquared);
450 double likelihood(
const vpColVector &coeffs,
const std::vector<vpImagePoint> &meas)
452 double likelihood = 0.;
453 unsigned int nbPoints =
static_cast<unsigned int>(meas.size());
456 vpTutoParabolaModel model(coeffs, m_height, m_width);
460 for (
unsigned int i = 0; i < nbPoints; ++i) {
461 double squareError = tutorial::evaluate(meas[i], model);
462 residuals[i] = squareError;
469 double sumError = w.hadamard(residuals).sum();
472 likelihood = std::exp(m_constantExpDenominator * sumError / w.sum()) * m_constantDenominator;
473 likelihood = std::min(likelihood, 1.0);
474 likelihood = std::max(likelihood, 0.);
479 double m_constantDenominator;
480 double m_constantExpDenominator;
481 unsigned int m_height;
482 unsigned int m_width;
488 int main(
const int argc,
const char *argv[])
490 tutorial::vpTutoCommonData data;
491 int returnCode = data.init(argc, argv);
492 if (returnCode != tutorial::vpTutoCommonData::SOFTWARE_CONTINUE) {
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;
501 vpColVector X0 = tutorial::computeInitialGuess(data);
505 const double maxDistanceForLikelihood = data.m_pfMaxDistanceForLikelihood;
506 const double sigmaLikelihood = maxDistanceForLikelihood / 3.;
507 const unsigned int nbParticles = data.m_pfN;
508 std::vector<double> stdevsPF;
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.);
513 unsigned long seedPF;
514 const float period = 33.3f;
515 if (data.m_pfSeed < 0) {
519 seedPF = data.m_pfSeed;
521 const int nbThread = data.m_pfNbThreads;
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;
532 tutorial::vpTutoAverageFunctor averageCpter(data.m_degree, data.m_I_orig.getHeight(), data.m_I_orig.getWidth());
533 using std::placeholders::_3;
540 filter.init(X0, processFunc, likelihoodFunc, checkResamplingFunc, resamplingFunc, meanFunc);
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");
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);
563 tutorial::performSegmentationHSV(data);
566 std::vector<vpImagePoint> edgePoints = tutorial::extractSkeleton(data);
569 std::vector<vpImagePoint> noisyEdgePoints = tutorial::addSaltAndPepperNoise(edgePoints, data);
571 #ifdef VISP_HAVE_DISPLAY
581 filter.filter(noisyEdgePoints, period);
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;
597 meanRootMeanSquareErrorPF += pfError;
599 #ifdef VISP_HAVE_DISPLAY
601 tutorial::display(Xest, data.m_IskeletonNoisy,
vpColor::red, legendPFVert, legendPFHor);
604 plot.plot(0, 0, nbIter, pfError);
606 data.displayLegend(data.m_I_orig);
610 run = data.manageClicks(data.m_I_orig, data.m_stepbystep);
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;
621 #ifdef VISP_HAVE_DISPLAY
622 if (data.m_grabber.end() && (!data.m_stepbystep)) {
625 vpDisplay::displayText(data.m_I_orig, data.m_ipLegend,
"End of sequence reached. Click to exit.", data.m_colorLegend);
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;
Implementation of column vector and the associated operations.
vpRowVector transpose() const
Class to define RGB colors available for display functionalities.
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()