Visual Servoing Platform  version 3.6.1 under development (2024-04-22)
vpDot2.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Track a white dot.
33  *
34  * Authors:
35  * Anthony Saunier
36  *
37 *****************************************************************************/
38 
44 #include <visp3/core/vpDisplay.h>
45 
46 // exception handling
47 #include <visp3/core/vpIoTools.h>
48 #include <visp3/core/vpMath.h>
49 #include <visp3/core/vpTrackingException.h>
50 
51 #include <cmath> // std::fabs
52 #include <iostream>
53 #include <limits> // numeric_limits
54 #include <math.h>
55 #include <visp3/blob/vpDot2.h>
56 
57 /******************************************************************************
58  *
59  * CONSTRUCTORS AND DESTRUCTORS
60  *
61 *****************************************************************************/
67 void vpDot2::init()
68 {
69  cog.set_u(0);
70  cog.set_v(0);
71 
72  width = 0;
73  height = 0;
74  surface = 0;
75  mean_gray_level = 0;
76  gray_level_min = 128;
77  gray_level_max = 255;
78  grayLevelPrecision = 0.80;
79  gamma = 1.5;
80 
81  sizePrecision = 0.65;
82  ellipsoidShapePrecision = 0.65;
83  maxSizeSearchDistancePrecision = 0.65;
85  m00 = 0.;
86  m11 = 0.;
87  m02 = 0.;
88  m20 = 0.;
89  m10 = 0.;
90  m01 = 0.;
91  mu11 = 0.;
92  mu02 = 0.;
93  mu20 = 0.;
94 
95  bbox_u_min = 0;
96  bbox_u_max = 0;
97  bbox_v_min = 0;
98  bbox_v_max = 0;
99 
100  firstBorder_u = 0;
101  firstBorder_v = 0;
102 
103  compute_moment = false;
104  graphics = false;
105  thickness = 1;
106 }
107 
112  : m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), mu11(0.), mu20(0.), mu02(0.), cog(), width(0), height(0),
113  surface(0), gray_level_min(128), gray_level_max(255), mean_gray_level(0), grayLevelPrecision(0.8), gamma(1.5),
114  sizePrecision(0.65), ellipsoidShapePrecision(0.65), maxSizeSearchDistancePrecision(0.65),
115  allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(), compute_moment(false), graphics(false),
116  thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0), firstBorder_u(0), firstBorder_v()
117 { }
118 
128  : m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), mu11(0.), mu20(0.), mu02(0.), cog(ip), width(0), height(0),
129  surface(0), gray_level_min(128), gray_level_max(255), mean_gray_level(0), grayLevelPrecision(0.8), gamma(1.5),
130  sizePrecision(0.65), ellipsoidShapePrecision(0.65), maxSizeSearchDistancePrecision(0.65),
131  allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(), compute_moment(false), graphics(false),
132  thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0), firstBorder_u(0), firstBorder_v()
133 { }
134 
138 vpDot2::vpDot2(const vpDot2 &twinDot)
139  : vpTracker(twinDot), m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), mu11(0.), mu20(0.), mu02(0.), cog(),
140  width(0), height(0), surface(0), gray_level_min(128), gray_level_max(255), mean_gray_level(0),
141  grayLevelPrecision(0.8), gamma(1.5), sizePrecision(0.65), ellipsoidShapePrecision(0.65),
142  maxSizeSearchDistancePrecision(0.65), allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(),
143  compute_moment(false), graphics(false), thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0),
144  firstBorder_u(0), firstBorder_v()
145 {
146  *this = twinDot;
147 }
148 
153 {
154  cog = twinDot.cog;
155 
156  width = twinDot.width;
157  height = twinDot.height;
158  surface = twinDot.surface;
159  gray_level_min = twinDot.gray_level_min;
160  gray_level_max = twinDot.gray_level_max;
161  mean_gray_level = twinDot.mean_gray_level;
162  grayLevelPrecision = twinDot.grayLevelPrecision;
163  gamma = twinDot.gamma;
164 
165  sizePrecision = twinDot.sizePrecision;
166  ellipsoidShapePrecision = twinDot.ellipsoidShapePrecision;
167  maxSizeSearchDistancePrecision = twinDot.maxSizeSearchDistancePrecision;
168  allowedBadPointsPercentage_ = twinDot.allowedBadPointsPercentage_;
169  area = twinDot.area;
170 
171  direction_list = twinDot.direction_list;
172  ip_edges_list = twinDot.ip_edges_list;
173 
174  compute_moment = twinDot.compute_moment;
175  graphics = twinDot.graphics;
176  thickness = twinDot.thickness;
177 
178  bbox_u_min = twinDot.bbox_u_min;
179  bbox_u_max = twinDot.bbox_u_max;
180  bbox_v_min = twinDot.bbox_v_min;
181  bbox_v_max = twinDot.bbox_v_max;
182 
183  firstBorder_u = twinDot.firstBorder_u;
184  firstBorder_v = twinDot.firstBorder_v;
185 
186  m00 = twinDot.m00;
187  m01 = twinDot.m01;
188  m11 = twinDot.m11;
189  m10 = twinDot.m10;
190  m02 = twinDot.m02;
191  m20 = twinDot.m20;
192 
193  mu11 = twinDot.mu11;
194  mu20 = twinDot.mu20;
195  mu02 = twinDot.mu02;
196 
197  return (*this);
198 }
199 
200 /******************************************************************************
201  *
202  * PUBLIC METHODS
203  *****************************************************************************/
204 
212 void vpDot2::display(const vpImage<unsigned char> &I, vpColor color, unsigned int t) const
213 {
214  vpDisplay::displayCross(I, cog, (3 * t) + 8, color, t);
215  std::list<vpImagePoint>::const_iterator it;
216 
217  std::list<vpImagePoint>::const_iterator ip_edges_list_end = ip_edges_list.end();
218  for (it = ip_edges_list.begin(); it != ip_edges_list_end; ++it) {
219  vpDisplay::displayPoint(I, *it, color);
220  }
221 }
222 
254 void vpDot2::initTracking(const vpImage<unsigned char> &I, unsigned int size)
255 {
256  while (vpDisplay::getClick(I, cog) != true) {
257  // block empty waiting user interaction
258  }
259 
260  unsigned int i = static_cast<unsigned int>(cog.get_i());
261  unsigned int j = static_cast<unsigned int>(cog.get_j());
262 
263  double Ip = pow(static_cast<double>(I[i][j]) / 255, 1 / gamma);
264 
265  if ((Ip - (1 - grayLevelPrecision)) < 0) {
266  gray_level_min = 0;
267  }
268  else {
269  gray_level_min = static_cast<unsigned int>(255 * pow(Ip - (1 - grayLevelPrecision), gamma));
270  if (gray_level_min > 255) {
271  gray_level_min = 255;
272  }
273  }
274  gray_level_max = static_cast<unsigned int>(255 * pow(Ip + (1 - grayLevelPrecision), gamma));
275  if (gray_level_max > 255) {
276  gray_level_max = 255;
277  }
278 
279  setWidth(size);
280  setHeight(size);
281 
282  track(I);
283 }
284 
312 void vpDot2::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip, unsigned int size)
313 {
314  cog = ip;
315 
316  unsigned int i = static_cast<unsigned int>(cog.get_i());
317  unsigned int j = static_cast<unsigned int>(cog.get_j());
318 
319  double Ip = pow(static_cast<double>(I[i][j]) / 255, 1 / gamma);
320 
321  if ((Ip - (1 - grayLevelPrecision)) < 0) {
322  gray_level_min = 0;
323  }
324  else {
325  gray_level_min = static_cast<unsigned int>(255 * pow(Ip - (1 - grayLevelPrecision), gamma));
326  if (gray_level_min > 255) {
327  gray_level_min = 255;
328  }
329  }
330  gray_level_max = static_cast<unsigned int>(255 * pow(Ip + (1 - grayLevelPrecision), gamma));
331  if (gray_level_max > 255) {
332  gray_level_max = 255;
333  }
334 
335  setWidth(size);
336  setHeight(size);
337 
338  track(I);
339 }
340 
380 void vpDot2::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip, unsigned int gray_lvl_min,
381  unsigned int gray_lvl_max, unsigned int size)
382 {
383  cog = ip;
384 
385  this->gray_level_min = gray_lvl_min;
386  this->gray_level_max = gray_lvl_max;
387 
388  setWidth(size);
389  setHeight(size);
390 
391  track(I);
392 }
393 
435 void vpDot2::track(const vpImage<unsigned char> &I, bool canMakeTheWindowGrow)
436 {
437  m00 = 0;
438  m11 = 0;
439  m02 = 0;
440  m20 = 0;
441  m10 = 0;
442  m01 = 0;
443 
444  // First, we will estimate the position of the tracked point
445 
446  // Set the search area to the entire image
447  setArea(I);
448 
449  // create a copy of the dot to search
450  // This copy can be saw as the previous dot used to check if the current one
451  // found with computeParameters() is similar to the previous one (see
452  // isValid() function). If the found dot is not similar (or valid), we use
453  // this copy to set the current found dot to the previous one (see below).
454  vpDot2 wantedDot(*this);
455 
456  bool found = computeParameters(I, cog.get_u(), cog.get_v());
457 
458  if (found) {
459  // test if the found dot is valid (ie similar to the previous one)
460  found = isValid(I, wantedDot);
461  if (!found) {
462  *this = wantedDot;
463  // std::cout << "The found dot is not valid" << std::endl;
464  }
465  }
466 
467  if (!found) {
468  // vpDEBUG_TRACE(0, "Search the dot in a biggest window around the
469  // last position"); vpDEBUG_TRACE(0, "Bad computed dot: ");
470  // vpDEBUG_TRACE(0, "u: %f v: %f", get_u(), get_v());
471  // vpDEBUG_TRACE(0, "w: %f h: %f", getWidth(), getHeight());
472 
473  // if estimation was wrong (get an error tracking), look for the dot
474  // closest from the estimation,
475  // i.e. search for dots in an a region of interest around the this dot and
476  // get the first element in the area.
477 
478  // first get the size of the search window from the dot size
479  double searchWindowWidth = 0.0;
480  double searchWindowHeight = 0.0;
481 
482  if ((std::fabs(getWidth()) <= std::numeric_limits<double>::epsilon()) ||
483  (std::fabs(getHeight()) <= std::numeric_limits<double>::epsilon())) {
484  searchWindowWidth = 80.;
485  searchWindowHeight = 80.;
486  }
487  else if (canMakeTheWindowGrow) {
488  searchWindowWidth = getWidth() * 5;
489  searchWindowHeight = getHeight() * 5;
490  }
491  else {
492  searchWindowWidth = getWidth();
493  searchWindowHeight = getHeight();
494  }
495 
496  std::list<vpDot2> candidates;
497  searchDotsInArea(I, static_cast<int>(this->cog.get_u() - (searchWindowWidth / 2.0)),
498  static_cast<int>(this->cog.get_v() - (searchWindowHeight / 2.0)),
499  static_cast<unsigned int>(searchWindowWidth),
500  static_cast<unsigned int>(searchWindowHeight), candidates);
501 
502  // if the vector is empty, that mean we didn't find any candidate
503  // in the area, return an error tracking.
504  if (candidates.empty()) {
505  // vpERROR_TRACE("No dot was found") ;
507  }
508 
509  // otherwise we've got our dot, update this dot's parameters
510  vpDot2 movingDot = candidates.front();
511 
512  setCog(movingDot.getCog());
513  setArea(movingDot.getArea());
514  setWidth(movingDot.getWidth());
515  setHeight(movingDot.getHeight());
516 
517  // Update the moments
518  m00 = movingDot.m00;
519  m01 = movingDot.m01;
520  m10 = movingDot.m10;
521  m11 = movingDot.m11;
522  m20 = movingDot.m20;
523  m02 = movingDot.m02;
524 
525  // Update the bounding box
526  bbox_u_min = movingDot.bbox_u_min;
527  bbox_u_max = movingDot.bbox_u_max;
528  bbox_v_min = movingDot.bbox_v_min;
529  bbox_v_max = movingDot.bbox_v_max;
530  }
531 
532  // if this dot is partially out of the image, return an error tracking.
533  if (!isInImage(I)) {
534  // vpERROR_TRACE("The center of gravity of the dot is not in the image") ;
536  "The center of gravity of the dot is not in the image"));
537  }
538 
539  // Get dots center of gravity
540  // unsigned int u = (unsigned int) this->cog.get_u();
541  // unsigned int v = (unsigned int) this->cog.get_v();
542  // Updates the min and max gray levels for the next iteration
543  // double Ip = pow((double)I[v][u]/255,1/gamma);
544  double Ip = pow(getMeanGrayLevel() / 255, 1 / gamma);
545  // printf("current value of gray level center : %i\n", I[v][u]);
546 
547  // get Mean Gray Level of I
548  if ((Ip - (1 - grayLevelPrecision)) < 0) {
549  gray_level_min = 0;
550  }
551  else {
552  gray_level_min = static_cast<unsigned int>(255 * pow(Ip - (1 - grayLevelPrecision), gamma));
553  if (gray_level_min > 255) {
554  gray_level_min = 255;
555  }
556  }
557  gray_level_max = static_cast<unsigned int>(255 * pow(Ip + (1 - grayLevelPrecision), gamma));
558  if (gray_level_max > 255) {
559  gray_level_max = 255;
560  }
561 
562  if (graphics) {
563  // display a red cross at the center of gravity's location in the image.
564  vpDisplay::displayCross(I, this->cog, (3 * thickness) + 8, vpColor::red, thickness);
565  }
566 }
567 
590 void vpDot2::track(const vpImage<unsigned char> &I, vpImagePoint &ip, bool canMakeTheWindowGrow)
591 {
592  track(I, canMakeTheWindowGrow);
593 
594  ip = this->cog;
595 }
596 
599 
605 double vpDot2::getWidth() const { return width; }
606 
612 double vpDot2::getHeight() const { return height; }
613 
619 double vpDot2::getArea() const { return fabs(surface); }
620 
626 double vpDot2::getGrayLevelPrecision() const { return grayLevelPrecision; }
627 
633 double vpDot2::getSizePrecision() const { return sizePrecision; }
634 
642 double vpDot2::getEllipsoidShapePrecision() const { return ellipsoidShapePrecision; }
643 
650 double vpDot2::getMaxSizeSearchDistancePrecision() const { return maxSizeSearchDistancePrecision; }
651 
655 double vpDot2::getDistance(const vpDot2 &distantDot) const
656 {
657  vpImagePoint cogDistantDot = distantDot.getCog();
658  double diff_u = this->cog.get_u() - cogDistantDot.get_u();
659  double diff_v = this->cog.get_v() - cogDistantDot.get_v();
660  return sqrt((diff_u * diff_u) + (diff_v * diff_v));
661 }
662 
664 
674 void vpDot2::setWidth(const double &w) { this->width = w; }
675 
686 void vpDot2::setHeight(const double &h) { this->height = h; }
687 
698 void vpDot2::setArea(const double &a) { this->surface = a; }
699 
717 void vpDot2::setGrayLevelPrecision(const double &precision)
718 {
719  double epsilon = 0.05;
720  if (grayLevelPrecision < epsilon) {
721  this->grayLevelPrecision = epsilon;
722  }
723  else if (grayLevelPrecision > 1) {
724  this->grayLevelPrecision = 1.0;
725  }
726  else {
727  this->grayLevelPrecision = precision;
728  }
729 }
747 void vpDot2::setSizePrecision(const double &precision)
748 {
749  if (sizePrecision < 0) {
750  this->sizePrecision = 0;
751  }
752  else if (sizePrecision > 1) {
753  this->sizePrecision = 1.0;
754  }
755  else {
756  this->sizePrecision = precision;
757  }
758 }
759 
792 void vpDot2::setEllipsoidShapePrecision(const double &precision)
793 {
794 
795  if (ellipsoidShapePrecision < 0) {
796  this->ellipsoidShapePrecision = 0;
797  }
798  else if (ellipsoidShapePrecision > 1) {
799  this->ellipsoidShapePrecision = 1.0;
800  }
801  else {
802  this->ellipsoidShapePrecision = precision;
803  }
804 }
805 
821 void vpDot2::setMaxSizeSearchDistancePrecision(const double &precision)
822 {
823  double epsilon = 0.05;
824  if (maxSizeSearchDistancePrecision < epsilon) {
825  this->maxSizeSearchDistancePrecision = epsilon;
826  }
827  else if (maxSizeSearchDistancePrecision > 1) {
828  this->maxSizeSearchDistancePrecision = 1.0;
829  }
830  else {
831  this->maxSizeSearchDistancePrecision = precision;
832  }
833 }
834 
843 void vpDot2::setArea(const vpImage<unsigned char> &I) { setArea(I, 0, 0, I.getWidth(), I.getHeight()); }
844 
857 void vpDot2::setArea(const vpImage<unsigned char> &I, int u, int v, unsigned int w, unsigned int h)
858 {
859  unsigned int image_w = I.getWidth();
860  unsigned int image_h = I.getHeight();
861 
862  // Bounds the area to the image
863  if (u < 0) {
864  u = 0;
865  }
866  else if (u >= static_cast<int>(image_w)) {
867  u = static_cast<int>(image_w) - 1;
868  }
869  if (v < 0) {
870  v = 0;
871  }
872  else if (v >= static_cast<int>(image_h)) {
873  v = static_cast<int>(image_h) - 1;
874  }
875 
876  if ((static_cast<unsigned int>(u) + w) > image_w) {
877  w = image_w - static_cast<unsigned int>(u) - 1;
878  }
879  if ((static_cast<unsigned int>(v) + h) > image_h) {
880  h = image_h - static_cast<unsigned int>(v) - 1;
881  }
882 
883  area.setRect(u, v, w, h);
884 }
885 
893 void vpDot2::setArea(const vpRect &a) { area = a; }
894 
896 
948 void vpDot2::searchDotsInArea(const vpImage<unsigned char> &I, std::list<vpDot2> &niceDots)
949 {
950  searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), niceDots);
951 }
952 
975 void vpDot2::searchDotsInArea(const vpImage<unsigned char> &I, int area_u, int area_v, unsigned int area_w,
976  unsigned int area_h, std::list<vpDot2> &niceDots)
977 
978 {
979  // clear the list of nice dots
980  niceDots.clear();
981 
982  // Fit the input area in the image; we keep only the common part between
983  // this area and the image.
984  setArea(I, area_u, area_v, area_w, area_h);
985 
986  // compute the size of the search grid
987  unsigned int gridWidth;
988  unsigned int gridHeight;
989  getGridSize(gridWidth, gridHeight);
990 
991  if (graphics) {
992  // Display the area were the dot is search
993  vpDisplay::displayRectangle(I, area, vpColor::blue, false, thickness);
994  }
995 
996 #ifdef DEBUG
998  vpDisplay::flush(I);
999 #endif
1000  // start the search loop; for all points of the search grid,
1001  // test if the pixel belongs to a valid dot.
1002  // if it is so eventually add it to the vector of valid dots.
1003  std::list<vpDot2> badDotsVector;
1004  std::list<vpDot2>::iterator itnice;
1005  std::list<vpDot2>::iterator itbad;
1006 
1007  vpDot2 *dotToTest = nullptr;
1008  vpDot2 tmpDot;
1009 
1010  unsigned int area_u_min = static_cast<unsigned int>(area.getLeft());
1011  unsigned int area_u_max = static_cast<unsigned int>(area.getRight());
1012  unsigned int area_v_min = static_cast<unsigned int>(area.getTop());
1013  unsigned int area_v_max = static_cast<unsigned int>(area.getBottom());
1014 
1015  unsigned int u, v;
1016  vpImagePoint cogTmpDot;
1017 
1018  for (v = area_v_min; v < area_v_max; v = v + gridHeight) {
1019  for (u = area_u_min; u < area_u_max; u = u + gridWidth) {
1020  // if the pixel we're in doesn't have the right color (outside the
1021  // graylevel interval), no need to check further, just get to the
1022  // next grid intersection.
1023  if (!hasGoodLevel(I, u, v)) {
1024  continue;
1025  }
1026 
1027  // Test if an other germ is inside the bounding box of a dot previously
1028  // detected
1029  bool good_germ = true;
1030 
1031  itnice = niceDots.begin();
1032  while ((itnice != niceDots.end()) && (good_germ == true)) {
1033  tmpDot = *itnice;
1034 
1035  cogTmpDot = tmpDot.getCog();
1036  double u0 = cogTmpDot.get_u();
1037  double v0 = cogTmpDot.get_v();
1038  double half_w = tmpDot.getWidth() / 2.;
1039  double half_h = tmpDot.getHeight() / 2.;
1040 
1041  if ((u >= (u0 - half_w)) && (u <= (u0 + half_w)) && (v >= (v0 - half_h)) && (v <= (v0 + half_h))) {
1042  // Germ is in a previously detected dot
1043  good_germ = false;
1044  }
1045  ++itnice;
1046  }
1047 
1048  if (!good_germ) {
1049  continue;
1050  }
1051 
1052  // Compute the right border position for this possible germ
1053  unsigned int border_u;
1054  unsigned int border_v;
1055  if (findFirstBorder(I, u, v, border_u, border_v) == false) {
1056  // germ is not good.
1057  // Jump all the pixels between v,u and v,
1058  // dotToTest->getFirstBorder_u()
1059  u = border_u;
1060  v = border_v;
1061  continue;
1062  }
1063 
1064  itbad = badDotsVector.begin();
1065 #define vpBAD_DOT_VALUE (*itbad)
1066  vpImagePoint cogBadDot;
1067 
1068  while ((itbad != badDotsVector.end()) && (good_germ == true)) {
1069  if ((static_cast<double>(u) >= vpBAD_DOT_VALUE.bbox_u_min) && (static_cast<double>(u) <= vpBAD_DOT_VALUE.bbox_u_max) &&
1070  (static_cast<double>(v) >= vpBAD_DOT_VALUE.bbox_v_min) && (static_cast<double>(v) <= vpBAD_DOT_VALUE.bbox_v_max)) {
1071  std::list<vpImagePoint>::const_iterator it_edges = ip_edges_list.begin();
1072  while ((it_edges != ip_edges_list.end()) && (good_germ == true)) {
1073  // Test if the germ belong to a previously detected dot:
1074  // - from the germ go right to the border and compare this
1075  // position to the list of pixels of previously detected dots
1076  cogBadDot = *it_edges;
1077  if ((std::fabs(border_u - cogBadDot.get_u()) <=
1078  (vpMath::maximum(std::fabs(static_cast<double>(border_u)), std::fabs(cogBadDot.get_u())) *
1079  std::numeric_limits<double>::epsilon())) &&
1080  (std::fabs(v - cogBadDot.get_v()) <=
1081  (vpMath::maximum(std::fabs(static_cast<double>(v)), std::fabs(cogBadDot.get_v())) *
1082  std::numeric_limits<double>::epsilon()))) {
1083  good_germ = false;
1084  }
1085  ++it_edges;
1086  }
1087  }
1088  ++itbad;
1089  }
1090 #undef vpBAD_DOT_VALUE
1091 
1092  if (!good_germ) {
1093  // Jump all the pixels between v,u and v,
1094  // dotToTest->getFirstBorder_u()
1095  u = border_u;
1096  v = border_v;
1097  continue;
1098  }
1099 
1100  vpTRACE(4, "Try germ (%d, %d)", u, v);
1101 
1102  vpImagePoint germ;
1103  germ.set_u(u);
1104  germ.set_v(v);
1105 
1106  // otherwise estimate the width, height and surface of the dot we
1107  // created, and test it.
1108  if (dotToTest != nullptr) {
1109  delete dotToTest;
1110  }
1111  dotToTest = getInstance();
1112  dotToTest->setCog(germ);
1113  dotToTest->setGrayLevelMin(getGrayLevelMin());
1114  dotToTest->setGrayLevelMax(getGrayLevelMax());
1116  dotToTest->setSizePrecision(getSizePrecision());
1117  dotToTest->setGraphics(graphics);
1118  dotToTest->setGraphicsThickness(thickness);
1119  dotToTest->setComputeMoments(true);
1120  dotToTest->setArea(area);
1121  dotToTest->setEllipsoidShapePrecision(ellipsoidShapePrecision);
1122  dotToTest->setEllipsoidBadPointsPercentage(allowedBadPointsPercentage_);
1123 
1124  // first compute the parameters of the dot.
1125  // if for some reasons this caused an error tracking
1126  // (dot partially out of the image...), check the next intersection
1127  if (dotToTest->computeParameters(I) == false) {
1128  // Jump all the pixels between v,u and v,
1129  // dotToTest->getFirstBorder_u()
1130  u = border_u;
1131  v = border_v;
1132  continue;
1133  }
1134  // if the dot to test is valid,
1135  if (dotToTest->isValid(I, *this)) {
1136  vpImagePoint cogDotToTest = dotToTest->getCog();
1137  // Compute the distance to the center. The center used here is not the
1138  // area center available by area.getCenter(area_center_u,
1139  // area_center_v) but the center of the input area which may be
1140  // partially outside the image.
1141 
1142  double area_center_u = (area_u + (area_w / 2.0)) - 0.5;
1143  double area_center_v = (area_v + (area_h / 2.0)) - 0.5;
1144 
1145  double thisDiff_u = cogDotToTest.get_u() - area_center_u;
1146  double thisDiff_v = cogDotToTest.get_v() - area_center_v;
1147  double thisDist = sqrt((thisDiff_u * thisDiff_u) + (thisDiff_v * thisDiff_v));
1148 
1149  bool stopLoop = false;
1150  itnice = niceDots.begin();
1151 
1152  while ((itnice != niceDots.end()) && (stopLoop == false)) {
1153  tmpDot = *itnice;
1154 
1155  // --comment: epsilon equals 0.001 -- detecte +sieurs points
1156  double epsilon = 3.0;
1157  // if the center of the dot is the same than the current
1158  // don't add it, test the next point of the grid
1159  cogTmpDot = tmpDot.getCog();
1160 
1161  if ((fabs(cogTmpDot.get_u() - cogDotToTest.get_u()) < epsilon) &&
1162  (fabs(cogTmpDot.get_v() - cogDotToTest.get_v()) < epsilon)) {
1163  stopLoop = true;
1164  // Jump all the pixels between v,u and v,
1165  // tmpDot->getFirstBorder_u()
1166  u = border_u;
1167  v = border_v;
1168  continue;
1169  }
1170 
1171  double otherDiff_u = cogTmpDot.get_u() - area_center_u;
1172  double otherDiff_v = cogTmpDot.get_v() - area_center_v;
1173  double otherDist = sqrt((otherDiff_u * otherDiff_u) + (otherDiff_v * otherDiff_v));
1174 
1175  // if the distance of the curent vector element to the center
1176  // is greater than the distance of this dot to the center,
1177  // then add this dot before the current vector element.
1178  if (otherDist > thisDist) {
1179  niceDots.insert(itnice, *dotToTest);
1180  ++itnice;
1181  stopLoop = true;
1182  // Jump all the pixels between v,u and v,
1183  // tmpDot->getFirstBorder_u()
1184  u = border_u;
1185  v = border_v;
1186  continue;
1187  }
1188  ++itnice;
1189  }
1190  vpTRACE(4, "End while (%d, %d)", u, v);
1191 
1192  // if we reached the end of the vector without finding the dot
1193  // or inserting it, insert it now.
1194  if ((itnice == niceDots.end()) && (stopLoop == false)) {
1195  niceDots.push_back(*dotToTest);
1196  }
1197  }
1198  else {
1199  // Store bad dots
1200  badDotsVector.push_front(*dotToTest);
1201  }
1202  }
1203  }
1204  if (dotToTest != nullptr) {
1205  delete dotToTest;
1206  }
1207 }
1208 
1229 bool vpDot2::isValid(const vpImage<unsigned char> &I, const vpDot2 &wantedDot)
1230 {
1231  double size_precision = wantedDot.getSizePrecision();
1232  double ellipsoidShape_precision = wantedDot.getEllipsoidShapePrecision();
1233 
1234  //
1235  // First, check the width, height and surface of the dot. Those parameters
1236  // must be the same.
1237  //
1238  // if ( (wantedDot.getWidth() != 0)
1239  // && (wantedDot.getHeight() != 0)
1240  // && (wantedDot.getArea() != 0) )
1241  if ((std::fabs(wantedDot.getWidth()) > std::numeric_limits<double>::epsilon()) &&
1242  (std::fabs(wantedDot.getHeight()) > std::numeric_limits<double>::epsilon()) &&
1243  (std::fabs(wantedDot.getArea()) > std::numeric_limits<double>::epsilon())) {
1244  if (std::fabs(size_precision) > std::numeric_limits<double>::epsilon()) {
1245  double epsilon = 0.001;
1246 #ifdef DEBUG
1247  std::cout << "test size precision......................\n";
1248  std::cout << "wanted dot: "
1249  << "w=" << wantedDot.getWidth() << " h=" << wantedDot.getHeight() << " s=" << wantedDot.getArea()
1250  << " precision=" << size_precision << " epsilon=" << epsilon << std::endl;
1251  std::cout << "dot found: "
1252  << "w=" << getWidth() << " h=" << getHeight() << " s=" << getArea() << std::endl;
1253 #endif
1254 
1255  if ((((wantedDot.getWidth() * size_precision) - epsilon) < getWidth()) == false) {
1256  vpDEBUG_TRACE(3, "Bad width > for dot (%g, %g)", cog.get_u(), cog.get_v());
1257 #ifdef DEBUG
1258  printf("Bad width > for dot (%g, %g)\n", cog.get_u(), cog.get_v());
1259 #endif
1260  return false;
1261  }
1262 
1263  if ((getWidth() < (wantedDot.getWidth() / (size_precision + epsilon))) == false) {
1264  vpDEBUG_TRACE(3, "Bad width > for dot (%g, %g)", cog.get_u(), cog.get_v());
1265 #ifdef DEBUG
1266  printf("Bad width %g > %g for dot (%g, %g)\n", getWidth(), wantedDot.getWidth() / (size_precision + epsilon),
1267  cog.get_u(), cog.get_v());
1268 #endif
1269  return false;
1270  }
1271 
1272  if ((((wantedDot.getHeight() * size_precision) - epsilon) < getHeight()) == false) {
1273  vpDEBUG_TRACE(3, "Bad height > for dot (%g, %g)", cog.get_u(), cog.get_v());
1274 #ifdef DEBUG
1275  printf("Bad height %g > %g for dot (%g, %g)\n", wantedDot.getHeight() * size_precision - epsilon, getHeight(),
1276  cog.get_u(), cog.get_v());
1277 #endif
1278  return false;
1279  }
1280 
1281  if ((getHeight() < (wantedDot.getHeight() / (size_precision + epsilon))) == false) {
1282  vpDEBUG_TRACE(3, "Bad height > for dot (%g, %g)", cog.get_u(), cog.get_v());
1283 #ifdef DEBUG
1284  printf("Bad height %g > %g for dot (%g, %g)\n", getHeight(), wantedDot.getHeight() / (size_precision + epsilon),
1285  cog.get_u(), cog.get_v());
1286 #endif
1287  return false;
1288  }
1289 
1290  if ((((wantedDot.getArea() * (size_precision * size_precision)) - epsilon) < getArea()) == false) {
1291  vpDEBUG_TRACE(3, "Bad surface > for dot (%g, %g)", cog.get_u(), cog.get_v());
1292 #ifdef DEBUG
1293  printf("Bad surface %g > %g for dot (%g, %g)\n",
1294  wantedDot.getArea() * (size_precision * size_precision) - epsilon, getArea(), cog.get_u(), cog.get_v());
1295 #endif
1296  return false;
1297  }
1298 
1299  if ((getArea() < (wantedDot.getArea() / ((size_precision * size_precision) + epsilon))) == false) {
1300  vpDEBUG_TRACE(3, "Bad surface > for dot (%g, %g)", cog.get_u(), cog.get_v());
1301 #ifdef DEBUG
1302  printf("Bad surface %g < %g for dot (%g, %g)\n", getArea(),
1303  wantedDot.getArea() / (size_precision * size_precision + epsilon), cog.get_u(), cog.get_v());
1304 #endif
1305  return false;
1306  }
1307  }
1308  }
1309  //
1310  // Now we can proceed to more advanced (and costy) checks.
1311  // First check there is a white (>level) elipse within dot
1312  // Then check the dot is surrounded by a black ellipse.
1313  //
1314  int nb_point_to_test = 20; // Nb points to test on inner and outside ellipsoid
1315  int nb_bad_points = 0;
1316  int nb_max_bad_points = static_cast<int>(nb_point_to_test * allowedBadPointsPercentage_);
1317  double step_angle = (2 * M_PI) / nb_point_to_test;
1318 
1319  // --comment: if ellipsoidShape_precision diff 0 and compute_moment is true
1320  if ((std::fabs(ellipsoidShape_precision) > std::numeric_limits<double>::epsilon()) && compute_moment) {
1321  // Chaumette, Image Moments: A General and Useful Set of Features for Visual Servoing, TRO 2004, eq 15
1322 
1323  // -comment: mu11 = m11 - m00 * xg * yg = m11 - m00 * m10/m00 * m01/m00
1324  // -comment: = m11 - m10 * m01 / m00
1325  // -comment: mu20 = m20 - m00 * xg^2 = m20 - m00 * m10/m00 * m10/m00
1326  // -comment: = m20 - m10^2 / m00
1327  // -comment: mu02 = m02 - m01^2 / m00
1328  // -comment: alpha = 1/2 arctan( 2 * mu11 / (mu20 - mu02) )
1329  //
1330  // -comment: a1^2 = 2 / m00 * (mu02 + mu20 + sqrt( (mu20 - mu02)^2 + 4mu11^2) )
1331  //
1332  // -comment: a2^2 = 2 / m00 * (mu02 + mu20 - sqrt( (mu20 - mu02)^2 + 4mu11^2) )
1333 
1334  // we compute parameters of the estimated ellipse
1335  double tmp1 = (((m01 * m01) - (m10 * m10)) / m00) + (m20 - m02);
1336  double tmp2 = m11 - ((m10 * m01) / m00);
1337  double Sqrt = sqrt((tmp1 * tmp1) + (4 * tmp2 * tmp2));
1338  double a1 = sqrt((2 / m00) * (((m20 + m02) - (((m10 * m10) + (m01 * m01)) / m00)) + Sqrt));
1339  double a2 = sqrt((2 / m00) * (((m20 + m02) - (((m10 * m10) + (m01 * m01)) / m00)) - Sqrt));
1340  double alpha = 0.5 * atan2(2 * ((m11 * m00) - (m10 * m01)), ((((m20 - m02) * m00) - (m10 * m10)) + (m01 * m01)));
1341 
1342  // to be able to track small dots, minorize the ellipsoid radius for the
1343  // inner test
1344  a1 -= 1.0;
1345  a2 -= 1.0;
1346 
1347  double innerCoef = ellipsoidShape_precision;
1348  unsigned int u, v;
1349  double cog_u = this->cog.get_u();
1350  double cog_v = this->cog.get_v();
1351 
1352  vpImagePoint ip;
1353  nb_bad_points = 0;
1354  for (double theta = 0.; theta < (2 * M_PI); theta += step_angle) {
1355  u = static_cast<unsigned int>(cog_u + (innerCoef * ((a1 * cos(alpha) * cos(theta)) - (a2 * sin(alpha) * sin(theta)))));
1356  v = static_cast<unsigned int>(cog_v + (innerCoef * ((a1 * sin(alpha) * cos(theta)) + (a2 * cos(alpha) * sin(theta)))));
1357  if (!this->hasGoodLevel(I, u, v)) {
1358 #ifdef DEBUG
1359  printf("Inner circle pixel (%u, %u) has bad level for dot (%g, %g): "
1360  "%d not in [%u, %u]\n",
1361  u, v, cog_u, cog_v, I[v][u], gray_level_min, gray_level_max);
1362 #endif
1363  ++nb_bad_points;
1364  }
1365  if (graphics) {
1366  for (unsigned int t = 0; t < thickness; ++t) {
1367  ip.set_u(u + t);
1368  ip.set_v(v);
1370  }
1371  }
1372 #ifdef DEBUG
1374  vpDisplay::flush(I);
1375 #endif
1376  }
1377  if (nb_bad_points > nb_max_bad_points) {
1378 #ifdef DEBUG
1379  printf("Inner ellipse has %d bad points. Max allowed is %d\n", nb_bad_points, nb_max_bad_points);
1380 #endif
1381  return false;
1382  }
1383  // to be able to track small dots, maximize the ellipsoid radius for the
1384  // inner test
1385  a1 += 2.0;
1386  a2 += 2.0;
1387 
1388  double outCoef = 2 - ellipsoidShape_precision; // --comment: 1.6
1389  nb_bad_points = 0;
1390  for (double theta = 0.; theta < (2 * M_PI); theta += step_angle) {
1391  u = static_cast<unsigned int>(cog_u + (outCoef * ((a1 * cos(alpha) * cos(theta)) - (a2 * sin(alpha) * sin(theta)))));
1392  v = static_cast<unsigned int>(cog_v + (outCoef * ((a1 * sin(alpha) * cos(theta)) + (a2 * cos(alpha) * sin(theta)))));
1393 #ifdef DEBUG
1394  // vpDisplay::displayRectangle(I, area, vpColor::yellow);
1395  vpDisplay::displayCross(I, (int)v, (int)u, 7, vpColor::purple);
1396  vpDisplay::flush(I);
1397 #endif
1398  // If outside the area, continue
1399  if ((static_cast<double>(u) < area.getLeft()) ||
1400  (static_cast<double>(u) > area.getRight()) ||
1401  (static_cast<double>(v) < area.getTop()) ||
1402  (static_cast<double>(v) > area.getBottom())) {
1403  continue;
1404  }
1405  if (!this->hasReverseLevel(I, u, v)) {
1406 #ifdef DEBUG
1407  printf("Outside circle pixel (%u, %u) has bad level for dot (%g, "
1408  "%g): %d not in [%u, %u]\n",
1409  u, v, cog_u, cog_v, I[v][u], gray_level_min, gray_level_max);
1410 #endif
1411  ++nb_bad_points;
1412  }
1413  if (graphics) {
1414  for (unsigned int t = 0; t < thickness; ++t) {
1415  ip.set_u(u + t);
1416  ip.set_v(v);
1417 
1419  }
1420  }
1421  }
1422  }
1423  if (nb_bad_points > nb_max_bad_points) {
1424 #ifdef DEBUG
1425  printf("Outside ellipse has %d bad points. Max allowed is %d\n", nb_bad_points, nb_max_bad_points);
1426 #endif
1427  return false;
1428  }
1429 
1430  return true;
1431 }
1432 
1451 bool vpDot2::hasGoodLevel(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v) const
1452 {
1453  if (!isInArea(u, v)) {
1454  return false;
1455  }
1456 
1457  if ((I[v][u] >= gray_level_min) && (I[v][u] <= gray_level_max)) {
1458  return true;
1459  }
1460  else {
1461  return false;
1462  }
1463 }
1464 
1477 bool vpDot2::hasReverseLevel(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v) const
1478 {
1479 
1480  if (!isInArea(u, v)) {
1481  return false;
1482  }
1483 
1484  if ((I[v][u] < gray_level_min) || (I[v][u] > gray_level_max)) {
1485  return true;
1486  }
1487  else {
1488  return false;
1489  }
1490 }
1491 
1500 vpDot2 *vpDot2::getInstance() { return new vpDot2(); }
1501 
1517 void vpDot2::getFreemanChain(std::list<unsigned int> &freeman_chain) const { freeman_chain = direction_list; }
1518 
1519 /******************************************************************************
1520  *
1521  * PRIVATE METHODS
1522  *
1523  ******************************************************************************/
1524 
1556 bool vpDot2::computeParameters(const vpImage<unsigned char> &I, const double &v_u, const double &v_v)
1557 {
1558  direction_list.clear();
1559  ip_edges_list.clear();
1560 
1561  double est_u = v_u; // estimated
1562  double est_v = v_v;
1563 
1564  // if u has default value, set it to the actual center value
1565  // if( est_u == -1.0 )
1566  if (std::fabs(est_u + 1.0) <= (vpMath::maximum(std::fabs(est_u), 1.) * std::numeric_limits<double>::epsilon())) {
1567  est_u = this->cog.get_u();
1568  }
1569 
1570  // if v has default value, set it to the actual center value
1571  // if( est_v == -1.0 )
1572  if (std::fabs(est_v + 1.0) <= (vpMath::maximum(std::fabs(est_v), 1.) * std::numeric_limits<double>::epsilon())) {
1573  est_v = this->cog.get_v();
1574  }
1575 
1576  // if the estimated position of the dot is out of the image, not need to
1577  // continue, return an error tracking
1578  if (!isInArea(static_cast<unsigned int>(est_u), static_cast<unsigned int>(est_v))) {
1579  vpDEBUG_TRACE(3,
1580  "Initial pixel coordinates (%d, %d) for dot tracking are "
1581  "not in the area",
1582  static_cast<int>(est_u), static_cast<int>(est_v));
1583  return false;
1584  }
1585 
1586  bbox_u_min = static_cast<int>(I.getWidth());
1587  bbox_u_max = 0;
1588  bbox_v_min = static_cast<int>(I.getHeight());
1589  bbox_v_max = 0;
1590 
1591  // if the first point doesn't have the right level then there's no point to
1592  // continue.
1593  if (!hasGoodLevel(I, static_cast<unsigned int>(est_u), static_cast<unsigned int>(est_v))) {
1594  vpDEBUG_TRACE(3, "Can't find a dot from pixel (%d, %d) coordinates", static_cast<unsigned int>(est_u), static_cast<unsigned int>(est_v));
1595  return false;
1596  }
1597 
1598  // find the border
1599 
1600  if (!findFirstBorder(I, static_cast<unsigned int>(est_u), static_cast<unsigned int>(est_v), this->firstBorder_u, this->firstBorder_v)) {
1601 
1602  vpDEBUG_TRACE(3, "Can't find first border (%d, %d) coordinates", static_cast<int>(est_u), static_cast<int>(est_v));
1603  return false;
1604  }
1605 
1606  unsigned int dir = 6;
1607 
1608  // Determine the first element of the Freeman chain
1609  computeFreemanChainElement(I, this->firstBorder_u, this->firstBorder_v, dir);
1610  unsigned int firstDir = dir;
1611 
1612  // if we are now out of the image, return an error tracking
1613  if (!isInArea(this->firstBorder_u, this->firstBorder_v)) {
1614  vpDEBUG_TRACE(3, "Border pixel coordinates (%d, %d) of the dot are not in the area", this->firstBorder_u,
1615  this->firstBorder_v);
1616  return false;
1617  }
1618 
1619  // store the new direction and dot border coordinates.
1620  direction_list.push_back(dir);
1621  vpImagePoint ip;
1622  ip.set_u(this->firstBorder_u);
1623  ip.set_v(this->firstBorder_v);
1624 
1625  ip_edges_list.push_back(ip);
1626 
1627  int border_u = static_cast<int>(this->firstBorder_u);
1628  int border_v = static_cast<int>(this->firstBorder_v);
1629  int du, dv;
1630  float dS, dMu, dMv, dMuv, dMu2, dMv2;
1631  m00 = 0.0;
1632  m10 = 0.0;
1633  m01 = 0.0;
1634  m11 = 0.0;
1635  m20 = 0.0;
1636  m02 = 0.0;
1637  // while we didn't come back to the first point, follow the border
1638  do {
1639  // if it was asked, show the border
1640  if (graphics) {
1641  for (int t = 0; t < static_cast<int>(thickness); ++t) {
1642  ip.set_u(border_u + t);
1643  ip.set_v(border_v);
1644 
1646  }
1647  }
1648 #ifdef DEBUG
1649  vpDisplay::displayPoint(I, border_v, border_u, vpColor::red);
1650  vpDisplay::flush(I);
1651 #endif
1652  // Determine the increments for the parameters
1653  computeFreemanParameters(border_u, border_v, dir, du, dv,
1654  dS, // surface
1655  dMu, dMv, // first order moments
1656  dMuv, dMu2, dMv2); // second order moment
1657 
1658  // Update the parameters
1659  border_u += du; // Next position on the border
1660  border_v += dv;
1661  m00 += dS; // enclosed area
1662  m10 += dMu; // First order moment along v axis
1663  m01 += dMv; // First order moment along u axis
1664  if (compute_moment) {
1665  m11 += dMuv; // Second order moment
1666  m20 += dMu2; // Second order moment along v axis
1667  m02 += dMv2; // Second order moment along u axis
1668  }
1669  // if we are now out of the image, return an error tracking
1670  if (!isInArea(static_cast<unsigned int>(border_u), static_cast<unsigned int>(border_v))) {
1671 
1672  vpDEBUG_TRACE(3, "Dot (%d, %d) is not in the area", border_u, border_v);
1673  // Can Occur on a single pixel dot located on the top border
1674  return false;
1675  }
1676 
1677  // store the new direction and dot border coordinates.
1678 
1679  direction_list.push_back(dir);
1680 
1681  ip.set_u(border_u);
1682  ip.set_v(border_v);
1683  ip_edges_list.push_back(ip);
1684 
1685  // update the extreme point of the dot.
1686  if (border_v < bbox_v_min) {
1687  bbox_v_min = border_v;
1688  }
1689  if (border_v > bbox_v_max) {
1690  bbox_v_max = border_v;
1691  }
1692  if (border_u < bbox_u_min) {
1693  bbox_u_min = border_u;
1694  }
1695  if (border_u > bbox_u_max) {
1696  bbox_u_max = border_u;
1697  }
1698 
1699  // move around the tracked entity by following the border.
1700  if (computeFreemanChainElement(I, static_cast<unsigned int>(border_u), static_cast<unsigned int>(border_v), dir) == false) {
1701  vpDEBUG_TRACE(3, "Can't compute Freeman chain for dot (%d, %d)", border_u, border_v);
1702  return false;
1703  }
1704 
1705  // vpTRACE("border_u: %d border_v: %d dir: %d", border_u, border_v,
1706  // dir);
1707 
1708  } while (((getFirstBorder_u() != static_cast<unsigned int>(border_u)) || (getFirstBorder_v() != static_cast<unsigned int>(border_v)) ||
1709  (firstDir != dir)) &&
1710  isInArea(static_cast<unsigned int>(border_u), static_cast<unsigned int>(border_v)));
1711 
1712 #ifdef VP_DEBUG
1713 #if VP_DEBUG_MODE == 3
1714  vpDisplay::flush(I);
1715 #endif
1716 #endif
1717 
1718  // if the surface is one or zero , the center of gravity wasn't properly
1719  // detected. Return an error tracking.
1720  // if( m00 == 0 || m00 == 1 )
1721  if ((std::fabs(m00) <= std::numeric_limits<double>::epsilon()) ||
1722  (std::fabs(m00 - 1.) <= (vpMath::maximum(std::fabs(m00), 1.) * std::numeric_limits<double>::epsilon()))) {
1723  vpDEBUG_TRACE(3, "The center of gravity of the dot wasn't properly detected");
1724  return false;
1725  }
1726  else // compute the center
1727  {
1728  // this magic formula gives the coordinates of the center of gravity
1729  double tmpCenter_u = m10 / m00;
1730  double tmpCenter_v = m01 / m00;
1731 
1732  // Updates the second order centered moments
1733  if (compute_moment) {
1734  mu11 = m11 - (tmpCenter_u * m01);
1735  mu02 = m02 - (tmpCenter_v * m01);
1736  mu20 = m20 - (tmpCenter_u * m10);
1737  }
1738 
1739  cog.set_u(tmpCenter_u);
1740  cog.set_v(tmpCenter_v);
1741  }
1742 
1743  width = (bbox_u_max - bbox_u_min) + 1;
1744  height = (bbox_v_max - bbox_v_min) + 1;
1745  surface = m00;
1746 
1747  computeMeanGrayLevel(I);
1748  return true;
1749 }
1750 
1766 bool vpDot2::findFirstBorder(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v,
1767  unsigned int &border_u, unsigned int &border_v)
1768 {
1769  // find the border
1770 
1771  // NOTE:
1772  // from here we use int and not double. This is because we don't have
1773  // rounding problems and it's actually more a trouble than smth else to
1774  // work with double when navigating around the dot.
1775  border_u = u;
1776  border_v = v;
1777  double epsilon = 0.001;
1778 
1779 #ifdef DEBUG
1780  std::cout << "gray level: " << gray_level_min << " " << gray_level_max << std::endl;
1781 #endif
1782  while (hasGoodLevel(I, border_u + 1, border_v) && (border_u < area.getRight()) /*I.getWidth()*/) {
1783  // if the width of this dot was initialised and we already crossed the dot
1784  // on more than the max possible width, no need to continue, return an
1785  // error tracking
1786  if ((getWidth() > 0) && ((border_u - u) > ((getWidth() / getMaxSizeSearchDistancePrecision()) + epsilon))) {
1787  vpDEBUG_TRACE(3,
1788  "The found dot (%d, %d, %d) has a greater width than the "
1789  "required one",
1790  u, v, border_u);
1791  return false;
1792  }
1793 #ifdef DEBUG
1794  vpDisplay::displayPoint(I, (int)border_v, (int)border_u + 1, vpColor::green);
1795  vpDisplay::flush(I);
1796 #endif
1797 
1798  ++border_u;
1799  }
1800  return true;
1801 }
1802 
1821 bool vpDot2::computeFreemanChainElement(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v,
1822  unsigned int &element)
1823 {
1824 
1825  if (hasGoodLevel(I, u, v)) {
1826  unsigned int v_u = u;
1827  unsigned int v_v = v;
1828  // get the point on the right of the point passed in
1829  updateFreemanPosition(v_u, v_v, (element + 2) % 8);
1830  if (hasGoodLevel(I, v_u, v_v)) {
1831  element = (element + 2) % 8; // turn right
1832  }
1833  else {
1834  unsigned int v_u1 = u;
1835  unsigned int v_v1 = v;
1836  updateFreemanPosition(v_u1, v_v1, (element + 1) % 8);
1837 
1838  if (hasGoodLevel(I, v_u1, v_v1)) {
1839  element = (element + 1) % 8; // turn diag right
1840  }
1841  else {
1842  unsigned int v_u2 = u;
1843  unsigned int v_v2 = v;
1844  updateFreemanPosition(v_u2, v_v2, element); // same direction
1845 
1846  if (hasGoodLevel(I, v_u2, v_v2)) {
1847  // element = element; // keep same dir
1848  }
1849  else {
1850  unsigned int v_u3 = u;
1851  unsigned int v_v3 = v;
1852  updateFreemanPosition(v_u3, v_v3, (element + 7) % 8); // diag left
1853 
1854  if (hasGoodLevel(I, v_u3, v_v3)) {
1855  element = (element + 7) % 8; // turn diag left
1856  }
1857  else {
1858  unsigned int v_u4 = u;
1859  unsigned int v_v4 = v;
1860  updateFreemanPosition(v_u4, v_v4, (element + 6) % 8); // left
1861 
1862  if (hasGoodLevel(I, v_u4, v_v4)) {
1863  element = (element + 6) % 8; // turn left
1864  }
1865  else {
1866  unsigned int v_u5 = u;
1867  unsigned int v_v5 = v;
1868  updateFreemanPosition(v_u5, v_v5, (element + 5) % 8); // left
1869 
1870  if (hasGoodLevel(I, v_u5, v_v5)) {
1871  element = (element + 5) % 8; // turn diag down
1872  }
1873  else {
1874  unsigned int v_u6 = u;
1875  unsigned int v_v6 = v;
1876  updateFreemanPosition(v_u6, v_v6, (element + 4) % 8); // left
1877 
1878  if (hasGoodLevel(I, v_u6, v_v6)) {
1879  element = (element + 4) % 8; // turn down
1880  }
1881  else {
1882  unsigned int v_u7 = u;
1883  unsigned int v_v7 = v;
1884  updateFreemanPosition(v_u7, v_v7, (element + 3) % 8); // diag
1885 
1886  if (hasGoodLevel(I, v_u7, v_v7)) {
1887  element = (element + 3) % 8; // turn diag right down
1888  }
1889  else {
1890  // No neighbor with a good level
1891  //
1892  return false;
1893  }
1894  }
1895  }
1896  }
1897  }
1898  }
1899  }
1900  }
1901  }
1902 
1903  else {
1904  return false;
1905  }
1906 
1907  return true;
1908 }
1909 
1939 void vpDot2::computeFreemanParameters(const int &u_p, const int &v_p, unsigned int &element, int &du, int &dv,
1940  float &dS, float &dMu, float &dMv, float &dMuv, float &dMu2, float &dMv2)
1941 {
1942  du = 0;
1943  dv = 0;
1944  dMuv = 0;
1945  dMu2 = 0;
1946  dMv2 = 0;
1947 
1948  /*
1949  3 2 1
1950  \ | /
1951  \|/
1952  4 ------- 0
1953  /|\
1954  / | \
1955  5 6 7
1956  */
1957  switch (element) {
1958  case 0: // go right
1959  du = 1;
1960  dS = static_cast<float>(v_p);
1961  dMu = 0.0;
1962  dMv = static_cast<float>(0.5 * v_p * v_p);
1963  if (compute_moment) {
1964  dMuv = static_cast<float>(0.25 * v_p * v_p * ((2 * u_p) + 1));
1965  dMu2 = 0;
1966  dMv2 = static_cast<float>((1.0 / 3.) * v_p * v_p * v_p);
1967  }
1968  break;
1969 
1970  case 1: // go right top
1971  du = 1;
1972  dv = 1;
1973  dS = static_cast<float>(v_p + 0.5);
1974  dMu = -static_cast<float>((0.5 * u_p * (u_p + 1)) + (1.0 / 6.0));
1975  dMv = static_cast<float>((0.5 * v_p * (v_p + 1)) + (1.0 / 6.0));
1976  if (compute_moment) {
1977  float half_u_p = static_cast<float>(0.5 * u_p);
1978  dMuv = static_cast<float>((v_p * v_p * (0.25 + half_u_p)) + (v_p * ((1. / 3.) + half_u_p)) + ((1. / 6.) * u_p) + 0.125);
1979  dMu2 = static_cast<float>(((-1. / 3.) * u_p * ((u_p * u_p) + (1.5 * u_p) + 1.)) - (1. / 12.0));
1980  dMv2 = static_cast<float>(((1. / 3.) * v_p * ((v_p * v_p) + (1.5 * v_p) + 1.)) + (1. / 12.0));
1981  }
1982  break;
1983 
1984  case 2: // go top
1985  dv = 1;
1986  dS = 0.0;
1987  dMu = static_cast<float>(-0.5 * u_p * u_p);
1988  dMv = 0.0;
1989  if (compute_moment) {
1990  dMuv = 0;
1991  dMu2 = static_cast<float>((-1.0 / 3.) * u_p * u_p * u_p);
1992  dMv2 = 0;
1993  }
1994  break;
1995 
1996  case 3:
1997  du = -1;
1998  dv = 1;
1999  dS = static_cast<float>(-v_p - 0.5);
2000  dMu = -static_cast<float>((0.5 * u_p * (u_p - 1)) + (1.0 / 6.0));
2001  dMv = -static_cast<float>((0.5 * v_p * (v_p + 1)) + (1.0 / 6.0));
2002  if (compute_moment) {
2003  float half_u_p = static_cast<float>(0.5 * u_p);
2004  dMuv = static_cast<float>((((v_p * v_p * (0.25 - half_u_p)) + (v_p * ((1. / 3.) - half_u_p))) - ((1. / 6.) * u_p)) + 0.125);
2005  dMu2 = static_cast<float>(((-1. / 3.) * u_p * (((u_p * u_p) - (1.5 * u_p)) + 1.)) - (1. / 12.0));
2006  dMv2 = static_cast<float>(((-1. / 3.) * v_p * ((v_p * v_p) + (1.5 * v_p) + 1.)) - (1. / 12.0));
2007  }
2008  break;
2009 
2010  case 4:
2011  du = -1;
2012  dS = static_cast<float>(-v_p);
2013  dMv = static_cast<float>(-0.5 * v_p * v_p);
2014  dMu = 0.0;
2015  if (compute_moment) {
2016  dMuv = static_cast<float>(-0.25 * v_p * v_p * ((2 * u_p) - 1));
2017  dMu2 = 0;
2018  dMv2 = static_cast<float>((-1.0 / 3.) * v_p * v_p * v_p);
2019  }
2020  break;
2021 
2022  case 5:
2023  du = -1;
2024  dv = -1;
2025  dS = static_cast<float>(-v_p + 0.5);
2026  dMu = static_cast<float>((0.5 * u_p * (u_p - 1)) + (1.0 / 6.0));
2027  dMv = static_cast<float>(-((0.5 * v_p * (v_p - 1)) + (1.0 / 6.0)));
2028  if (compute_moment) {
2029  float half_u_p = static_cast<float>(0.5 * u_p);
2030  dMuv = static_cast<float>((((v_p * v_p * (0.25 - half_u_p)) - (v_p * ((1. / 3.) - half_u_p))) - ((1. / 6.) * u_p)) + 0.125);
2031  dMu2 = static_cast<float>(((1. / 3.) * u_p * (((u_p * u_p) - (1.5 * u_p)) + 1.)) - (1. / 12.0));
2032  dMv2 = static_cast<float>(((-1. / 3.) * v_p * (((v_p * v_p) - (1.5 * v_p)) + 1.)) - (1. / 12.0));
2033  }
2034  break;
2035 
2036  case 6:
2037  dv = -1;
2038  dS = 0.0;
2039  dMu = static_cast<float>(0.5 * u_p * u_p);
2040  dMv = 0.0;
2041  if (compute_moment) {
2042  dMuv = 0;
2043  dMu2 = static_cast<float>((1.0 / 3.) * u_p * u_p * u_p);
2044  dMv2 = 0;
2045  }
2046  break;
2047 
2048  case 7:
2049  du = 1;
2050  dv = -1;
2051  dS = static_cast<float>(v_p - 0.5);
2052  dMu = static_cast<float>((0.5 * u_p * (u_p + 1)) + (1.0 / 6.0));
2053  dMv = static_cast<float>((0.5 * v_p * (v_p - 1)) + (1.0 / 6.0));
2054  if (compute_moment) {
2055  float half_u_p = static_cast<float>(0.5 * u_p);
2056  dMuv = static_cast<float>(((v_p * v_p * (0.25 + half_u_p)) - (v_p * ((1. / 3.) + half_u_p))) + ((1. / 6.) * u_p) + 0.125);
2057  dMu2 = static_cast<float>(((1. / 3.) * u_p * ((u_p * u_p) + (1.5 * u_p) + 1.)) + (1. / 12.0));
2058  dMv2 = static_cast<float>(((1. / 3.) * v_p * (((v_p * v_p) - (1.5 * v_p)) + 1.)) - (1. / 12.0));
2059  }
2060  break;
2061 
2062  default:
2063  std::cout << "to complete the default" << std::endl;
2064  }
2065 }
2066 
2080 void vpDot2::updateFreemanPosition(unsigned int &u, unsigned int &v, const unsigned int &dir)
2081 {
2082  switch (dir) {
2083  case 0:
2084  u += 1;
2085  break;
2086  case 1:
2087  u += 1;
2088  v += 1;
2089  break;
2090  case 2:
2091  v += 1;
2092  break;
2093  case 3:
2094  u -= 1;
2095  v += 1;
2096  break;
2097  case 4:
2098  u -= 1;
2099  break;
2100  case 5:
2101  u -= 1;
2102  v -= 1;
2103  break;
2104  case 6:
2105  v -= 1;
2106  break;
2107  case 7:
2108  u += 1;
2109  v -= 1;
2110  break;
2111  default:
2112  std::cout << "In vpDot2::updateFreemanPosition dir not identified" << std::endl;
2113  }
2114 }
2115 
2127 bool vpDot2::isInImage(const vpImage<unsigned char> &I) const { return isInImage(I, cog); }
2128 
2140 bool vpDot2::isInImage(const vpImage<unsigned char> &I, const vpImagePoint &ip) const
2141 {
2142  unsigned int h = I.getHeight();
2143  unsigned int w = I.getWidth();
2144  double u = ip.get_u();
2145  double v = ip.get_v();
2146 
2147  if ((u < 0) || (u >= w)) {
2148  return false;
2149  }
2150  if ((v < 0) || (v >= h)) {
2151  return false;
2152  }
2153  return true;
2154 }
2155 
2167 bool vpDot2::isInArea(const unsigned int &u, const unsigned int &v) const
2168 {
2169  unsigned int area_u_min = static_cast<unsigned int>(area.getLeft());
2170  unsigned int area_u_max = static_cast<unsigned int>(area.getRight());
2171  unsigned int area_v_min = static_cast<unsigned int>(area.getTop());
2172  unsigned int area_v_max = static_cast<unsigned int>(area.getBottom());
2173 
2174  if ((u < area_u_min) || (u > area_u_max)) {
2175  return false;
2176  }
2177  if ((v < area_v_min) || (v > area_v_max)) {
2178  return false;
2179  }
2180  return true;
2181 }
2182 
2194 void vpDot2::getGridSize(unsigned int &gridWidth, unsigned int &gridHeight)
2195 {
2196  // first get the research grid width and height Note that
2197  // 1/sqrt(2)=cos(pi/4). The grid squares should be small enough to be
2198  // contained in the dot. We gent this here if the dot is a perfect disc.
2199  // More accurate criterium to define the grid should be implemented if
2200  // necessary
2201  gridWidth = static_cast<unsigned int>((getWidth() * getMaxSizeSearchDistancePrecision()) / sqrt(2.));
2202  gridHeight = static_cast<unsigned int>((getHeight() * getMaxSizeSearchDistancePrecision()) / sqrt(2.0));
2203 
2204  if (gridWidth == 0) {
2205  gridWidth = 1;
2206  }
2207  if (gridHeight == 0) {
2208  gridHeight = 1;
2209  }
2210 }
2211 
2224 void vpDot2::computeMeanGrayLevel(const vpImage<unsigned char> &I)
2225 {
2226  int cog_u = static_cast<int>(cog.get_u());
2227  int cog_v = static_cast<int>(cog.get_v());
2228 
2229  unsigned int sum_value = 0;
2230  unsigned int nb_pixels = 0;
2231 
2232  for (unsigned int i = static_cast<unsigned int>(this->bbox_u_min); i <= static_cast<unsigned int>(this->bbox_u_max); ++i) {
2233  unsigned int pixel_gray = static_cast<unsigned int>(I[static_cast<unsigned int>(cog_v)][i]);
2234  if ((pixel_gray >= getGrayLevelMin()) && (pixel_gray <= getGrayLevelMax())) {
2235  sum_value += pixel_gray;
2236  ++nb_pixels;
2237  }
2238  }
2239  for (unsigned int i = static_cast<unsigned int>(this->bbox_v_min); i <= static_cast<unsigned int>(this->bbox_v_max); ++i) {
2240  unsigned char pixel_gray = I[i][static_cast<unsigned int>(cog_u)];
2241  if ((pixel_gray >= getGrayLevelMin()) && (pixel_gray <= getGrayLevelMax())) {
2242  sum_value += pixel_gray;
2243  ++nb_pixels;
2244  }
2245  }
2246  if (nb_pixels < 10) { // could be good to choose the min nb points from area of dot
2247  // add diagonals points to have enough point
2248  int imin, imax;
2249  if ((cog_u - bbox_u_min) >(cog_v - bbox_v_min)) {
2250  imin = cog_v - bbox_v_min;
2251  }
2252  else {
2253  imin = cog_u - bbox_u_min;
2254  }
2255  if ((bbox_u_max - cog_u) > (bbox_v_max - cog_v)) {
2256  imax = bbox_v_max - cog_v;
2257  }
2258  else {
2259  imax = bbox_u_max - cog_u;
2260  }
2261  for (int i = -imin; i <= imax; ++i) {
2262  unsigned int pixel_gray = static_cast<unsigned int>(I[static_cast<unsigned int>(cog_v + i)][static_cast<unsigned int>(cog_u + i)]);
2263  if ((pixel_gray >= getGrayLevelMin()) && (pixel_gray <= getGrayLevelMax())) {
2264  sum_value += pixel_gray;
2265  ++nb_pixels;
2266  }
2267  }
2268 
2269  if ((cog_u - bbox_u_min) > (bbox_v_max - cog_v)) {
2270  imin = bbox_v_max - cog_v;
2271  }
2272  else {
2273  imin = cog_u - bbox_u_min;
2274  }
2275  if ((bbox_u_max - cog_u) > (cog_v - bbox_v_min)) {
2276  imax = cog_v - bbox_v_min;
2277  }
2278  else {
2279  imax = bbox_u_max - cog_u;
2280  }
2281 
2282  for (int i = -imin; i <= imax; ++i) {
2283  unsigned char pixel_gray = I[static_cast<unsigned int>(cog_v - i)][static_cast<unsigned int>(cog_u + i)];
2284  if ((pixel_gray >= getGrayLevelMin()) && (pixel_gray <= getGrayLevelMax())) {
2285  sum_value += pixel_gray;
2286  ++nb_pixels;
2287  }
2288  }
2289  }
2290 
2291  if (nb_pixels == 0) {
2292  // should never happen
2293  throw(vpTrackingException(vpTrackingException::notEnoughPointError, "No point was found"));
2294  }
2295  else {
2296  mean_gray_level = sum_value / nb_pixels;
2297  }
2298 }
2299 
2318 vpMatrix vpDot2::defineDots(vpDot2 dot[], const unsigned int &n, const std::string &dotFile, vpImage<unsigned char> &I,
2319  vpColor col, bool trackDot)
2320 {
2321  vpMatrix Cogs(n, 2);
2322  vpImagePoint cog;
2323  unsigned int i;
2324  bool fromFile = vpIoTools::checkFilename(dotFile.c_str());
2325  if (fromFile) {
2326  vpMatrix::loadMatrix(dotFile, Cogs);
2327  std::cout << Cogs.getRows() << " dots loaded from file " << dotFile << std::endl;
2328  }
2329 
2330  // test number of cogs in file
2331  if (Cogs.getRows() < n) {
2332  std::cout << "Dot file has a wrong number of dots : redefining them" << std::endl;
2333  fromFile = false;
2334  }
2335 
2336  // read from file and tracks the dots
2337  if (fromFile) {
2338  try {
2339  for (i = 0; i < n; ++i) {
2340  cog.set_uv(Cogs[i][0], Cogs[i][1]);
2341  dot[i].setGraphics(true);
2342  dot[i].setCog(cog);
2343  if (trackDot) {
2344  dot[i].initTracking(I, cog);
2345  dot[i].track(I);
2346  vpDisplay::displayCross(I, cog, 10, col);
2347  }
2348  }
2349  }
2350  catch (...) {
2351  std::cout << "Cannot track dots from file" << std::endl;
2352  fromFile = false;
2353  }
2354  vpDisplay::flush(I);
2355 
2356  // check that dots are far away ones from the other
2357  for (i = 0; ((i < n) && fromFile); ++i) {
2358  double d = sqrt(vpMath::sqr(dot[i].getHeight()) + vpMath::sqr(dot[i].getWidth()));
2359  for (unsigned int j = 0; ((j < n) && fromFile); ++j) {
2360  if (j != i) {
2361  if (dot[i].getDistance(dot[j]) < d) {
2362  fromFile = false;
2363  std::cout << "Dots from file seem incoherent" << std::endl;
2364  }
2365  }
2366  }
2367  }
2368  }
2369 
2370  if (!fromFile) {
2371  vpDisplay::display(I);
2372  vpDisplay::flush(I);
2373 
2374  std::cout << "Click on the " << n << " dots clockwise starting from upper/left dot..." << std::endl;
2375  for (i = 0; i < n; ++i) {
2376  if (trackDot) {
2377  dot[i].setGraphics(true);
2378  dot[i].initTracking(I);
2379  cog = dot[i].getCog();
2380  }
2381  else {
2382  vpDisplay::getClick(I, cog);
2383  dot[i].setCog(cog);
2384  }
2385  Cogs[i][0] = cog.get_u();
2386  Cogs[i][1] = cog.get_v();
2387  vpDisplay::displayCross(I, cog, 10, col);
2388  vpDisplay::flush(I);
2389  }
2390  }
2391 
2392  if ((!fromFile) && (dotFile != "")) {
2393  vpMatrix::saveMatrix(dotFile, Cogs);
2394  std::cout << Cogs.getRows() << " dots written to file " << dotFile << std::endl;
2395  }
2396 
2397  // back to non graphic mode
2398  for (i = 0; i < n; ++i) {
2399  dot[i].setGraphics(false);
2400  }
2401 
2402  return Cogs;
2403 }
2404 
2421 void vpDot2::trackAndDisplay(vpDot2 dot[], const unsigned int &n, vpImage<unsigned char> &I,
2422  std::vector<vpImagePoint> &cogs, vpImagePoint *cogStar)
2423 {
2424  unsigned int i;
2425  // tracking
2426  for (i = 0; i < n; ++i) {
2427  dot[i].track(I);
2428  cogs.push_back(dot[i].getCog());
2429  }
2430  // trajectories
2431  unsigned int cogs_size = cogs.size();
2432  for (i = n; i < cogs_size; ++i) {
2433  vpDisplay::displayCircle(I, cogs[i], 4, vpColor::green, true);
2434  }
2435  // initial position
2436  for (i = 0; i < n; ++i) {
2437  vpDisplay::displayCircle(I, cogs[i], 4, vpColor::blue, true);
2438  }
2439  // if exists, desired position
2440  if (cogStar != nullptr) {
2441  for (i = 0; i < n; ++i) {
2442  vpDisplay::displayDotLine(I, cogStar[i], dot[i].getCog(), vpColor::red);
2443  vpDisplay::displayCircle(I, cogStar[i], 4, vpColor::red, true);
2444  }
2445  }
2446  vpDisplay::flush(I);
2447 }
2448 
2464  const std::list<vpImagePoint> &edges_list, vpColor color, unsigned int thickness)
2465 {
2466  vpDisplay::displayCross(I, cog, (3 * thickness) + 8, color, thickness);
2467  std::list<vpImagePoint>::const_iterator it;
2468 
2469  std::list<vpImagePoint>::const_iterator edges_list_end = edges_list.end();
2470  for (it = edges_list.begin(); it != edges_list_end; ++it) {
2471  vpDisplay::displayPoint(I, *it, color);
2472  }
2473 }
2474 
2489 void vpDot2::display(const vpImage<vpRGBa> &I, const vpImagePoint &cog, const std::list<vpImagePoint> &edges_list,
2490  vpColor color, unsigned int thickness)
2491 {
2492  vpDisplay::displayCross(I, cog, (3 * thickness) + 8, color, thickness);
2493  std::list<vpImagePoint>::const_iterator it;
2494  std::list<vpImagePoint>::const_iterator edges_list_end = edges_list.end();
2495  for (it = edges_list.begin(); it != edges_list_end; ++it) {
2496  vpDisplay::displayPoint(I, *it, color);
2497  }
2498 }
2499 
2505 VISP_EXPORT std::ostream &operator<<(std::ostream &os, vpDot2 &d) { return (os << "(" << d.getCog() << ")"); }
friend std::ostream & operator<<(std::ostream &s, const vpArray2D< Type > &A)
Definition: vpArray2D.h:600
unsigned int getRows() const
Definition: vpArray2D.h:337
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:152
static const vpColor red
Definition: vpColor.h:211
static const vpColor blue
Definition: vpColor.h:217
static const vpColor purple
Definition: vpColor.h:222
static const vpColor green
Definition: vpColor.h:214
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void displayCircle(const vpImage< unsigned char > &I, const vpImageCircle &circle, const vpColor &color, bool fill=false, unsigned int thickness=1)
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 displayDotLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1)
static void displayRectangle(const vpImage< unsigned char > &I, const vpImagePoint &topLeft, unsigned int width, unsigned int height, const vpColor &color, bool fill=false, unsigned int thickness=1)
This tracker is meant to track a blob (connex pixels with same gray level) on a vpImage.
Definition: vpDot2.h:124
unsigned int getGrayLevelMin() const
Definition: vpDot2.h:214
vpDot2()
Definition: vpDot2.cpp:111
unsigned int getGrayLevelMax() const
Definition: vpDot2.h:220
void track(const vpImage< unsigned char > &I, bool canMakeTheWindowGrow=true)
Definition: vpDot2.cpp:435
double m02
Definition: vpDot2.h:411
double m01
Definition: vpDot2.h:387
void setGraphics(bool activate)
Definition: vpDot2.h:310
double mu11
Definition: vpDot2.h:420
void setMaxSizeSearchDistancePrecision(const double &maxSizeSearchDistancePrecision)
Definition: vpDot2.cpp:821
vpDot2 & operator=(const vpDot2 &twinDot)
Definition: vpDot2.cpp:152
static void trackAndDisplay(vpDot2 dot[], const unsigned int &n, vpImage< unsigned char > &I, std::vector< vpImagePoint > &cogs, vpImagePoint *cogStar=nullptr)
Definition: vpDot2.cpp:2421
void setGraphicsThickness(unsigned int t)
Definition: vpDot2.h:318
double getEllipsoidShapePrecision() const
Definition: vpDot2.cpp:642
void searchDotsInArea(const vpImage< unsigned char > &I, int area_u, int area_v, unsigned int area_w, unsigned int area_h, std::list< vpDot2 > &niceDots)
Definition: vpDot2.cpp:975
void display(const vpImage< unsigned char > &I, vpColor color=vpColor::red, unsigned int thickness=1) const
Definition: vpDot2.cpp:212
double m20
Definition: vpDot2.h:402
double m00
Definition: vpDot2.h:371
void setGrayLevelMax(const unsigned int &max)
Definition: vpDot2.h:346
double getArea() const
Definition: vpDot2.cpp:619
void setSizePrecision(const double &sizePrecision)
Definition: vpDot2.cpp:747
void setGrayLevelPrecision(const double &grayLevelPrecision)
Definition: vpDot2.cpp:717
double m11
Definition: vpDot2.h:395
void setGrayLevelMin(const unsigned int &min)
Definition: vpDot2.h:330
void getFreemanChain(std::list< unsigned int > &freeman_chain) const
Definition: vpDot2.cpp:1517
void setHeight(const double &height)
Definition: vpDot2.cpp:686
double getMaxSizeSearchDistancePrecision() const
Definition: vpDot2.cpp:650
void setCog(const vpImagePoint &ip)
Definition: vpDot2.h:256
vpImagePoint getCog() const
Definition: vpDot2.h:176
double m10
Definition: vpDot2.h:379
double getSizePrecision() const
Definition: vpDot2.cpp:633
double getGrayLevelPrecision() const
Definition: vpDot2.cpp:626
void setEllipsoidBadPointsPercentage(const double &percentage=0.0)
Definition: vpDot2.h:285
double getDistance(const vpDot2 &distantDot) const
Definition: vpDot2.cpp:655
double mu02
Definition: vpDot2.h:430
void setWidth(const double &width)
Definition: vpDot2.cpp:674
double mu20
Definition: vpDot2.h:425
double getWidth() const
Definition: vpDot2.cpp:605
void setEllipsoidShapePrecision(const double &ellipsoidShapePrecision)
Definition: vpDot2.cpp:792
double getMeanGrayLevel() const
Definition: vpDot2.h:229
void setArea(const double &area)
Definition: vpDot2.cpp:698
void setComputeMoments(bool activate)
Definition: vpDot2.h:271
void initTracking(const vpImage< unsigned char > &I, unsigned int size=0)
Definition: vpDot2.cpp:254
static vpMatrix defineDots(vpDot2 dot[], const unsigned int &n, const std::string &dotFile, vpImage< unsigned char > &I, vpColor col=vpColor::blue, bool trackDot=true)
Definition: vpDot2.cpp:2318
double getHeight() const
Definition: vpDot2.cpp:612
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_j() const
Definition: vpImagePoint.h:125
double get_u() const
Definition: vpImagePoint.h:136
void set_u(double u)
Definition: vpImagePoint.h:330
void set_uv(double u, double v)
Definition: vpImagePoint.h:352
void set_v(double v)
Definition: vpImagePoint.h:341
double get_i() const
Definition: vpImagePoint.h:114
double get_v() const
Definition: vpImagePoint.h:147
unsigned int getWidth() const
Definition: vpImage.h:245
unsigned int getHeight() const
Definition: vpImage.h:184
static bool checkFilename(const std::string &filename)
Definition: vpIoTools.cpp:1213
static Type maximum(const Type &a, const Type &b)
Definition: vpMath.h:252
static double sqr(double x)
Definition: vpMath.h:201
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:146
static bool loadMatrix(const std::string &filename, vpArray2D< double > &M, bool binary=false, char *header=nullptr)
Definition: vpMatrix.h:748
static bool saveMatrix(const std::string &filename, const vpArray2D< double > &M, bool binary=false, const char *header="")
Definition: vpMatrix.h:896
Defines a rectangle in the plane.
Definition: vpRect.h:76
double getLeft() const
Definition: vpRect.h:170
void setRect(double l, double t, double w, double h)
Definition: vpRect.h:330
double getRight() const
Definition: vpRect.h:176
double getBottom() const
Definition: vpRect.h:94
double getTop() const
Definition: vpRect.h:189
Class that defines what is a feature generic tracker.
Definition: vpTracker.h:59
Error that can be emitted by the vpTracker class and its derivatives.
@ featureLostError
Tracker lost feature.
@ notEnoughPointError
Not enough point to track.
#define vpTRACE
Definition: vpDebug.h:405
#define vpDEBUG_TRACE
Definition: vpDebug.h:473