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