Visual Servoing Platform  version 3.6.1 under development (2024-05-08)
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::getMaxSizeSearchDistPrecision() 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::setMaxSizeSearchDistPrecision(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  /*
1324  // -comment: mu11 = m11 - m00 * xg * yg = m11 - m00 * m10/m00 * m01/m00
1325  // -comment: = m11 - m10 * m01 / m00
1326  // -comment: mu20 = m20 - m00 * xg^2 = m20 - m00 * m10/m00 * m10/m00
1327  // -comment: = m20 - m10^2 / m00
1328  // -comment: mu02 = m02 - m01^2 / m00
1329  // -comment: alpha = 1/2 arctan( 2 * mu11 / (mu20 - mu02) )
1330  //
1331  // -comment: a1^2 = 2 / m00 * (mu02 + mu20 + sqrt( (mu20 - mu02)^2 + 4mu11^2) )
1332  //
1333  // -comment: a2^2 = 2 / m00 * (mu02 + mu20 - sqrt( (mu20 - mu02)^2 + 4mu11^2) )
1334  */
1335  // we compute parameters of the estimated ellipse
1336  double tmp1 = (((m01 * m01) - (m10 * m10)) / m00) + (m20 - m02);
1337  double tmp2 = m11 - ((m10 * m01) / m00);
1338  double Sqrt = sqrt((tmp1 * tmp1) + (4 * tmp2 * tmp2));
1339  double a1 = sqrt((2 / m00) * (((m20 + m02) - (((m10 * m10) + (m01 * m01)) / m00)) + Sqrt));
1340  double a2 = sqrt((2 / m00) * (((m20 + m02) - (((m10 * m10) + (m01 * m01)) / m00)) - Sqrt));
1341  double alpha = 0.5 * atan2(2 * ((m11 * m00) - (m10 * m01)), ((((m20 - m02) * m00) - (m10 * m10)) + (m01 * m01)));
1342 
1343  // to be able to track small dots, minorize the ellipsoid radius for the
1344  // inner test
1345  a1 -= 1.0;
1346  a2 -= 1.0;
1347 
1348  double innerCoef = ellipsoidShape_precision;
1349  unsigned int u, v;
1350  double cog_u = this->cog.get_u();
1351  double cog_v = this->cog.get_v();
1352 
1353  vpImagePoint ip;
1354  nb_bad_points = 0;
1355  for (double theta = 0.; theta < (2 * M_PI); theta += step_angle) {
1356  u = static_cast<unsigned int>(cog_u + (innerCoef * ((a1 * cos(alpha) * cos(theta)) - (a2 * sin(alpha) * sin(theta)))));
1357  v = static_cast<unsigned int>(cog_v + (innerCoef * ((a1 * sin(alpha) * cos(theta)) + (a2 * cos(alpha) * sin(theta)))));
1358  if (!this->hasGoodLevel(I, u, v)) {
1359 #ifdef DEBUG
1360  printf("Inner circle pixel (%u, %u) has bad level for dot (%g, %g): "
1361  "%d not in [%u, %u]\n",
1362  u, v, cog_u, cog_v, I[v][u], gray_level_min, gray_level_max);
1363 #endif
1364  ++nb_bad_points;
1365  }
1366  if (graphics) {
1367  for (unsigned int t = 0; t < thickness; ++t) {
1368  ip.set_u(u + t);
1369  ip.set_v(v);
1371  }
1372  }
1373 #ifdef DEBUG
1375  vpDisplay::flush(I);
1376 #endif
1377  }
1378  if (nb_bad_points > nb_max_bad_points) {
1379 #ifdef DEBUG
1380  printf("Inner ellipse has %d bad points. Max allowed is %d\n", nb_bad_points, nb_max_bad_points);
1381 #endif
1382  return false;
1383  }
1384  // to be able to track small dots, maximize the ellipsoid radius for the
1385  // inner test
1386  a1 += 2.0;
1387  a2 += 2.0;
1388 
1389  double outCoef = 2 - ellipsoidShape_precision; // --comment: 1.6
1390  nb_bad_points = 0;
1391  for (double theta = 0.; theta < (2 * M_PI); theta += step_angle) {
1392  u = static_cast<unsigned int>(cog_u + (outCoef * ((a1 * cos(alpha) * cos(theta)) - (a2 * sin(alpha) * sin(theta)))));
1393  v = static_cast<unsigned int>(cog_v + (outCoef * ((a1 * sin(alpha) * cos(theta)) + (a2 * cos(alpha) * sin(theta)))));
1394 #ifdef DEBUG
1395  // vpDisplay::displayRectangle(I, area, vpColor::yellow);
1396  vpDisplay::displayCross(I, (int)v, (int)u, 7, vpColor::purple);
1397  vpDisplay::flush(I);
1398 #endif
1399  // If outside the area, continue
1400  if ((static_cast<double>(u) < area.getLeft()) ||
1401  (static_cast<double>(u) > area.getRight()) ||
1402  (static_cast<double>(v) < area.getTop()) ||
1403  (static_cast<double>(v) > area.getBottom())) {
1404  // continue
1405  }
1406  else {
1407  if (!this->hasReverseLevel(I, u, v)) {
1408 #ifdef DEBUG
1409  printf("Outside circle pixel (%u, %u) has bad level for dot (%g, "
1410  "%g): %d not in [%u, %u]\n",
1411  u, v, cog_u, cog_v, I[v][u], gray_level_min, gray_level_max);
1412 #endif
1413  ++nb_bad_points;
1414  }
1415  if (graphics) {
1416  for (unsigned int t = 0; t < thickness; ++t) {
1417  ip.set_u(u + t);
1418  ip.set_v(v);
1419 
1421  }
1422  }
1423  }
1424  }
1425  }
1426  if (nb_bad_points > nb_max_bad_points) {
1427 #ifdef DEBUG
1428  printf("Outside ellipse has %d bad points. Max allowed is %d\n", nb_bad_points, nb_max_bad_points);
1429 #endif
1430  return false;
1431  }
1432 
1433  return true;
1434 }
1435 
1454 bool vpDot2::hasGoodLevel(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v) const
1455 {
1456  if (!isInArea(u, v)) {
1457  return false;
1458  }
1459 
1460  if ((I[v][u] >= gray_level_min) && (I[v][u] <= gray_level_max)) {
1461  return true;
1462  }
1463  else {
1464  return false;
1465  }
1466 }
1467 
1480 bool vpDot2::hasReverseLevel(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v) const
1481 {
1482 
1483  if (!isInArea(u, v)) {
1484  return false;
1485  }
1486 
1487  if ((I[v][u] < gray_level_min) || (I[v][u] > gray_level_max)) {
1488  return true;
1489  }
1490  else {
1491  return false;
1492  }
1493 }
1494 
1503 vpDot2 *vpDot2::getInstance() { return new vpDot2(); }
1504 
1520 void vpDot2::getFreemanChain(std::list<unsigned int> &freeman_chain) const { freeman_chain = direction_list; }
1521 
1522 /******************************************************************************
1523  *
1524  * PRIVATE METHODS
1525  *
1526  ******************************************************************************/
1527 
1559 bool vpDot2::computeParameters(const vpImage<unsigned char> &I, const double &v_u, const double &v_v)
1560 {
1561  direction_list.clear();
1562  ip_edges_list.clear();
1563 
1564  double est_u = v_u; // estimated
1565  double est_v = v_v;
1566 
1567  // if u has default value, set it to the actual center value
1568  // if( est_u == -1.0 )
1569  if (std::fabs(est_u + 1.0) <= (vpMath::maximum(std::fabs(est_u), 1.) * std::numeric_limits<double>::epsilon())) {
1570  est_u = this->cog.get_u();
1571  }
1572 
1573  // if v has default value, set it to the actual center value
1574  // if( est_v == -1.0 )
1575  if (std::fabs(est_v + 1.0) <= (vpMath::maximum(std::fabs(est_v), 1.) * std::numeric_limits<double>::epsilon())) {
1576  est_v = this->cog.get_v();
1577  }
1578 
1579  // if the estimated position of the dot is out of the image, not need to
1580  // continue, return an error tracking
1581  if (!isInArea(static_cast<unsigned int>(est_u), static_cast<unsigned int>(est_v))) {
1582  vpDEBUG_TRACE(3,
1583  "Initial pixel coordinates (%d, %d) for dot tracking are "
1584  "not in the area",
1585  static_cast<int>(est_u), static_cast<int>(est_v));
1586  return false;
1587  }
1588 
1589  bbox_u_min = static_cast<int>(I.getWidth());
1590  bbox_u_max = 0;
1591  bbox_v_min = static_cast<int>(I.getHeight());
1592  bbox_v_max = 0;
1593 
1594  // if the first point doesn't have the right level then there's no point to
1595  // continue.
1596  if (!hasGoodLevel(I, static_cast<unsigned int>(est_u), static_cast<unsigned int>(est_v))) {
1597  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));
1598  return false;
1599  }
1600 
1601  // find the border
1602 
1603  if (!findFirstBorder(I, static_cast<unsigned int>(est_u), static_cast<unsigned int>(est_v), this->firstBorder_u, this->firstBorder_v)) {
1604 
1605  vpDEBUG_TRACE(3, "Can't find first border (%d, %d) coordinates", static_cast<int>(est_u), static_cast<int>(est_v));
1606  return false;
1607  }
1608 
1609  unsigned int dir = 6;
1610 
1611  // Determine the first element of the Freeman chain
1612  computeFreemanChainElement(I, this->firstBorder_u, this->firstBorder_v, dir);
1613  unsigned int firstDir = dir;
1614 
1615  // if we are now out of the image, return an error tracking
1616  if (!isInArea(this->firstBorder_u, this->firstBorder_v)) {
1617  vpDEBUG_TRACE(3, "Border pixel coordinates (%d, %d) of the dot are not in the area", this->firstBorder_u,
1618  this->firstBorder_v);
1619  return false;
1620  }
1621 
1622  // store the new direction and dot border coordinates.
1623  direction_list.push_back(dir);
1624  vpImagePoint ip;
1625  ip.set_u(this->firstBorder_u);
1626  ip.set_v(this->firstBorder_v);
1627 
1628  ip_edges_list.push_back(ip);
1629 
1630  int border_u = static_cast<int>(this->firstBorder_u);
1631  int border_v = static_cast<int>(this->firstBorder_v);
1632  int du, dv;
1633  float dS, dMu, dMv, dMuv, dMu2, dMv2;
1634  m00 = 0.0;
1635  m10 = 0.0;
1636  m01 = 0.0;
1637  m11 = 0.0;
1638  m20 = 0.0;
1639  m02 = 0.0;
1640  // while we didn't come back to the first point, follow the border
1641  do {
1642  // if it was asked, show the border
1643  if (graphics) {
1644  for (int t = 0; t < static_cast<int>(thickness); ++t) {
1645  ip.set_u(border_u + t);
1646  ip.set_v(border_v);
1647 
1649  }
1650  }
1651 #ifdef DEBUG
1652  vpDisplay::displayPoint(I, border_v, border_u, vpColor::red);
1653  vpDisplay::flush(I);
1654 #endif
1655  // Determine the increments for the parameters
1656  computeFreemanParameters(border_u, border_v, dir, du, dv,
1657  dS, // surface
1658  dMu, dMv, // first order moments
1659  dMuv, dMu2, dMv2); // second order moment
1660 
1661  // Update the parameters
1662  border_u += du; // Next position on the border
1663  border_v += dv;
1664  m00 += dS; // enclosed area
1665  m10 += dMu; // First order moment along v axis
1666  m01 += dMv; // First order moment along u axis
1667  if (compute_moment) {
1668  m11 += dMuv; // Second order moment
1669  m20 += dMu2; // Second order moment along v axis
1670  m02 += dMv2; // Second order moment along u axis
1671  }
1672  // if we are now out of the image, return an error tracking
1673  if (!isInArea(static_cast<unsigned int>(border_u), static_cast<unsigned int>(border_v))) {
1674 
1675  vpDEBUG_TRACE(3, "Dot (%d, %d) is not in the area", border_u, border_v);
1676  // Can Occur on a single pixel dot located on the top border
1677  return false;
1678  }
1679 
1680  // store the new direction and dot border coordinates.
1681 
1682  direction_list.push_back(dir);
1683 
1684  ip.set_u(border_u);
1685  ip.set_v(border_v);
1686  ip_edges_list.push_back(ip);
1687 
1688  // update the extreme point of the dot.
1689  if (border_v < bbox_v_min) {
1690  bbox_v_min = border_v;
1691  }
1692  if (border_v > bbox_v_max) {
1693  bbox_v_max = border_v;
1694  }
1695  if (border_u < bbox_u_min) {
1696  bbox_u_min = border_u;
1697  }
1698  if (border_u > bbox_u_max) {
1699  bbox_u_max = border_u;
1700  }
1701 
1702  // move around the tracked entity by following the border.
1703  if (computeFreemanChainElement(I, static_cast<unsigned int>(border_u), static_cast<unsigned int>(border_v), dir) == false) {
1704  vpDEBUG_TRACE(3, "Can't compute Freeman chain for dot (%d, %d)", border_u, border_v);
1705  return false;
1706  }
1707 
1708  // vpTRACE("border_u: %d border_v: %d dir: %d", border_u, border_v,
1709  // dir);
1710 
1711  } while (((getFirstBorder_u() != static_cast<unsigned int>(border_u)) || (getFirstBorder_v() != static_cast<unsigned int>(border_v)) ||
1712  (firstDir != dir)) &&
1713  isInArea(static_cast<unsigned int>(border_u), static_cast<unsigned int>(border_v)));
1714 
1715 #ifdef VP_DEBUG
1716 #if VP_DEBUG_MODE == 3
1717  vpDisplay::flush(I);
1718 #endif
1719 #endif
1720 
1721  // if the surface is one or zero , the center of gravity wasn't properly
1722  // detected. Return an error tracking.
1723  // if( m00 == 0 || m00 == 1 )
1724  if ((std::fabs(m00) <= std::numeric_limits<double>::epsilon()) ||
1725  (std::fabs(m00 - 1.) <= (vpMath::maximum(std::fabs(m00), 1.) * std::numeric_limits<double>::epsilon()))) {
1726  vpDEBUG_TRACE(3, "The center of gravity of the dot wasn't properly detected");
1727  return false;
1728  }
1729  else // compute the center
1730  {
1731  // this magic formula gives the coordinates of the center of gravity
1732  double tmpCenter_u = m10 / m00;
1733  double tmpCenter_v = m01 / m00;
1734 
1735  // Updates the second order centered moments
1736  if (compute_moment) {
1737  mu11 = m11 - (tmpCenter_u * m01);
1738  mu02 = m02 - (tmpCenter_v * m01);
1739  mu20 = m20 - (tmpCenter_u * m10);
1740  }
1741 
1742  cog.set_u(tmpCenter_u);
1743  cog.set_v(tmpCenter_v);
1744  }
1745 
1746  width = (bbox_u_max - bbox_u_min) + 1;
1747  height = (bbox_v_max - bbox_v_min) + 1;
1748  surface = m00;
1749 
1750  computeMeanGrayLevel(I);
1751  return true;
1752 }
1753 
1769 bool vpDot2::findFirstBorder(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v,
1770  unsigned int &border_u, unsigned int &border_v)
1771 {
1772  // find the border
1773 
1774  // NOTE:
1775  // from here we use int and not double. This is because we don't have
1776  // rounding problems and it's actually more a trouble than smth else to
1777  // work with double when navigating around the dot.
1778  border_u = u;
1779  border_v = v;
1780  double epsilon = 0.001;
1781 
1782 #ifdef DEBUG
1783  std::cout << "gray level: " << gray_level_min << " " << gray_level_max << std::endl;
1784 #endif
1785  while (hasGoodLevel(I, border_u + 1, border_v) && (border_u < area.getRight()) /*I.getWidth()*/) {
1786  // if the width of this dot was initialised and we already crossed the dot
1787  // on more than the max possible width, no need to continue, return an
1788  // error tracking
1789  if ((getWidth() > 0) && ((border_u - u) > ((getWidth() / getMaxSizeSearchDistPrecision()) + epsilon))) {
1790  vpDEBUG_TRACE(3,
1791  "The found dot (%d, %d, %d) has a greater width than the "
1792  "required one",
1793  u, v, border_u);
1794  return false;
1795  }
1796 #ifdef DEBUG
1797  vpDisplay::displayPoint(I, (int)border_v, (int)border_u + 1, vpColor::green);
1798  vpDisplay::flush(I);
1799 #endif
1800 
1801  ++border_u;
1802  }
1803  return true;
1804 }
1805 
1824 bool vpDot2::computeFreemanChainElement(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v,
1825  unsigned int &element)
1826 {
1827 
1828  if (hasGoodLevel(I, u, v)) {
1829  unsigned int v_u = u;
1830  unsigned int v_v = v;
1831  // get the point on the right of the point passed in
1832  updateFreemanPosition(v_u, v_v, (element + 2) % 8);
1833  if (hasGoodLevel(I, v_u, v_v)) {
1834  element = (element + 2) % 8; // turn right
1835  }
1836  else {
1837  unsigned int v_u1 = u;
1838  unsigned int v_v1 = v;
1839  updateFreemanPosition(v_u1, v_v1, (element + 1) % 8);
1840 
1841  if (hasGoodLevel(I, v_u1, v_v1)) {
1842  element = (element + 1) % 8; // turn diag right
1843  }
1844  else {
1845  unsigned int v_u2 = u;
1846  unsigned int v_v2 = v;
1847  updateFreemanPosition(v_u2, v_v2, element); // same direction
1848 
1849  if (hasGoodLevel(I, v_u2, v_v2)) {
1850  // element = element; // keep same dir
1851  }
1852  else {
1853  unsigned int v_u3 = u;
1854  unsigned int v_v3 = v;
1855  updateFreemanPosition(v_u3, v_v3, (element + 7) % 8); // diag left
1856 
1857  if (hasGoodLevel(I, v_u3, v_v3)) {
1858  element = (element + 7) % 8; // turn diag left
1859  }
1860  else {
1861  unsigned int v_u4 = u;
1862  unsigned int v_v4 = v;
1863  updateFreemanPosition(v_u4, v_v4, (element + 6) % 8); // left
1864 
1865  if (hasGoodLevel(I, v_u4, v_v4)) {
1866  element = (element + 6) % 8; // turn left
1867  }
1868  else {
1869  unsigned int v_u5 = u;
1870  unsigned int v_v5 = v;
1871  updateFreemanPosition(v_u5, v_v5, (element + 5) % 8); // left
1872 
1873  if (hasGoodLevel(I, v_u5, v_v5)) {
1874  element = (element + 5) % 8; // turn diag down
1875  }
1876  else {
1877  unsigned int v_u6 = u;
1878  unsigned int v_v6 = v;
1879  updateFreemanPosition(v_u6, v_v6, (element + 4) % 8); // left
1880 
1881  if (hasGoodLevel(I, v_u6, v_v6)) {
1882  element = (element + 4) % 8; // turn down
1883  }
1884  else {
1885  unsigned int v_u7 = u;
1886  unsigned int v_v7 = v;
1887  updateFreemanPosition(v_u7, v_v7, (element + 3) % 8); // diag
1888 
1889  if (hasGoodLevel(I, v_u7, v_v7)) {
1890  element = (element + 3) % 8; // turn diag right down
1891  }
1892  else {
1893  // No neighbor with a good level
1894  //
1895  return false;
1896  }
1897  }
1898  }
1899  }
1900  }
1901  }
1902  }
1903  }
1904  }
1905 
1906  else {
1907  return false;
1908  }
1909 
1910  return true;
1911 }
1912 
1942 void vpDot2::computeFreemanParameters(const int &u_p, const int &v_p, unsigned int &element, int &du, int &dv,
1943  float &dS, float &dMu, float &dMv, float &dMuv, float &dMu2, float &dMv2)
1944 {
1945  du = 0;
1946  dv = 0;
1947  dMuv = 0;
1948  dMu2 = 0;
1949  dMv2 = 0;
1950 
1951  /*
1952  3 2 1
1953  \ | /
1954  \|/
1955  4 ------- 0
1956  /|\
1957  / | \
1958  5 6 7
1959  */
1960  switch (element) {
1961  case 0: // go right
1962  du = 1;
1963  dS = static_cast<float>(v_p);
1964  dMu = 0.0;
1965  dMv = static_cast<float>(0.5 * v_p * v_p);
1966  if (compute_moment) {
1967  dMuv = static_cast<float>(0.25 * v_p * v_p * ((2 * u_p) + 1));
1968  dMu2 = 0;
1969  dMv2 = static_cast<float>((1.0 / 3.) * v_p * v_p * v_p);
1970  }
1971  break;
1972 
1973  case 1: // go right top
1974  du = 1;
1975  dv = 1;
1976  dS = static_cast<float>(v_p + 0.5);
1977  dMu = -static_cast<float>((0.5 * u_p * (u_p + 1)) + (1.0 / 6.0));
1978  dMv = static_cast<float>((0.5 * v_p * (v_p + 1)) + (1.0 / 6.0));
1979  if (compute_moment) {
1980  float half_u_p = static_cast<float>(0.5 * u_p);
1981  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);
1982  dMu2 = static_cast<float>(((-1. / 3.) * u_p * ((u_p * u_p) + (1.5 * u_p) + 1.)) - (1. / 12.0));
1983  dMv2 = static_cast<float>(((1. / 3.) * v_p * ((v_p * v_p) + (1.5 * v_p) + 1.)) + (1. / 12.0));
1984  }
1985  break;
1986 
1987  case 2: // go top
1988  dv = 1;
1989  dS = 0.0;
1990  dMu = static_cast<float>(-0.5 * u_p * u_p);
1991  dMv = 0.0;
1992  if (compute_moment) {
1993  dMuv = 0;
1994  dMu2 = static_cast<float>((-1.0 / 3.) * u_p * u_p * u_p);
1995  dMv2 = 0;
1996  }
1997  break;
1998 
1999  case 3:
2000  du = -1;
2001  dv = 1;
2002  dS = static_cast<float>(-v_p - 0.5);
2003  dMu = -static_cast<float>((0.5 * u_p * (u_p - 1)) + (1.0 / 6.0));
2004  dMv = -static_cast<float>((0.5 * v_p * (v_p + 1)) + (1.0 / 6.0));
2005  if (compute_moment) {
2006  float half_u_p = static_cast<float>(0.5 * u_p);
2007  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);
2008  dMu2 = static_cast<float>(((-1. / 3.) * u_p * (((u_p * u_p) - (1.5 * u_p)) + 1.)) - (1. / 12.0));
2009  dMv2 = static_cast<float>(((-1. / 3.) * v_p * ((v_p * v_p) + (1.5 * v_p) + 1.)) - (1. / 12.0));
2010  }
2011  break;
2012 
2013  case 4:
2014  du = -1;
2015  dS = static_cast<float>(-v_p);
2016  dMv = static_cast<float>(-0.5 * v_p * v_p);
2017  dMu = 0.0;
2018  if (compute_moment) {
2019  dMuv = static_cast<float>(-0.25 * v_p * v_p * ((2 * u_p) - 1));
2020  dMu2 = 0;
2021  dMv2 = static_cast<float>((-1.0 / 3.) * v_p * v_p * v_p);
2022  }
2023  break;
2024 
2025  case 5:
2026  du = -1;
2027  dv = -1;
2028  dS = static_cast<float>(-v_p + 0.5);
2029  dMu = static_cast<float>((0.5 * u_p * (u_p - 1)) + (1.0 / 6.0));
2030  dMv = static_cast<float>(-((0.5 * v_p * (v_p - 1)) + (1.0 / 6.0)));
2031  if (compute_moment) {
2032  float half_u_p = static_cast<float>(0.5 * u_p);
2033  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);
2034  dMu2 = static_cast<float>(((1. / 3.) * u_p * (((u_p * u_p) - (1.5 * u_p)) + 1.)) - (1. / 12.0));
2035  dMv2 = static_cast<float>(((-1. / 3.) * v_p * (((v_p * v_p) - (1.5 * v_p)) + 1.)) - (1. / 12.0));
2036  }
2037  break;
2038 
2039  case 6:
2040  dv = -1;
2041  dS = 0.0;
2042  dMu = static_cast<float>(0.5 * u_p * u_p);
2043  dMv = 0.0;
2044  if (compute_moment) {
2045  dMuv = 0;
2046  dMu2 = static_cast<float>((1.0 / 3.) * u_p * u_p * u_p);
2047  dMv2 = 0;
2048  }
2049  break;
2050 
2051  case 7:
2052  du = 1;
2053  dv = -1;
2054  dS = static_cast<float>(v_p - 0.5);
2055  dMu = static_cast<float>((0.5 * u_p * (u_p + 1)) + (1.0 / 6.0));
2056  dMv = static_cast<float>((0.5 * v_p * (v_p - 1)) + (1.0 / 6.0));
2057  if (compute_moment) {
2058  float half_u_p = static_cast<float>(0.5 * u_p);
2059  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);
2060  dMu2 = static_cast<float>(((1. / 3.) * u_p * ((u_p * u_p) + (1.5 * u_p) + 1.)) + (1. / 12.0));
2061  dMv2 = static_cast<float>(((1. / 3.) * v_p * (((v_p * v_p) - (1.5 * v_p)) + 1.)) - (1. / 12.0));
2062  }
2063  break;
2064 
2065  default:
2066  std::cout << "to complete the default" << std::endl;
2067  }
2068 }
2069 
2083 void vpDot2::updateFreemanPosition(unsigned int &u, unsigned int &v, const unsigned int &dir)
2084 {
2085  switch (dir) {
2086  case 0:
2087  u += 1;
2088  break;
2089  case 1:
2090  u += 1;
2091  v += 1;
2092  break;
2093  case 2:
2094  v += 1;
2095  break;
2096  case 3:
2097  u -= 1;
2098  v += 1;
2099  break;
2100  case 4:
2101  u -= 1;
2102  break;
2103  case 5:
2104  u -= 1;
2105  v -= 1;
2106  break;
2107  case 6:
2108  v -= 1;
2109  break;
2110  case 7:
2111  u += 1;
2112  v -= 1;
2113  break;
2114  default:
2115  std::cout << "In vpDot2::updateFreemanPosition dir not identified" << std::endl;
2116  }
2117 }
2118 
2130 bool vpDot2::isInImage(const vpImage<unsigned char> &I) const { return isInImage(I, cog); }
2131 
2143 bool vpDot2::isInImage(const vpImage<unsigned char> &I, const vpImagePoint &ip) const
2144 {
2145  unsigned int h = I.getHeight();
2146  unsigned int w = I.getWidth();
2147  double u = ip.get_u();
2148  double v = ip.get_v();
2149 
2150  if ((u < 0) || (u >= w)) {
2151  return false;
2152  }
2153  if ((v < 0) || (v >= h)) {
2154  return false;
2155  }
2156  return true;
2157 }
2158 
2170 bool vpDot2::isInArea(const unsigned int &u, const unsigned int &v) const
2171 {
2172  unsigned int area_u_min = static_cast<unsigned int>(area.getLeft());
2173  unsigned int area_u_max = static_cast<unsigned int>(area.getRight());
2174  unsigned int area_v_min = static_cast<unsigned int>(area.getTop());
2175  unsigned int area_v_max = static_cast<unsigned int>(area.getBottom());
2176 
2177  if ((u < area_u_min) || (u > area_u_max)) {
2178  return false;
2179  }
2180  if ((v < area_v_min) || (v > area_v_max)) {
2181  return false;
2182  }
2183  return true;
2184 }
2185 
2197 void vpDot2::getGridSize(unsigned int &gridWidth, unsigned int &gridHeight)
2198 {
2199  // first get the research grid width and height Note that
2200  // 1/sqrt(2)=cos(pi/4). The grid squares should be small enough to be
2201  // contained in the dot. We gent this here if the dot is a perfect disc.
2202  // More accurate criterium to define the grid should be implemented if
2203  // necessary
2204  gridWidth = static_cast<unsigned int>((getWidth() * getMaxSizeSearchDistPrecision()) / sqrt(2.));
2205  gridHeight = static_cast<unsigned int>((getHeight() * getMaxSizeSearchDistPrecision()) / sqrt(2.0));
2206 
2207  if (gridWidth == 0) {
2208  gridWidth = 1;
2209  }
2210  if (gridHeight == 0) {
2211  gridHeight = 1;
2212  }
2213 }
2214 
2227 void vpDot2::computeMeanGrayLevel(const vpImage<unsigned char> &I)
2228 {
2229  int cog_u = static_cast<int>(cog.get_u());
2230  int cog_v = static_cast<int>(cog.get_v());
2231 
2232  unsigned int sum_value = 0;
2233  unsigned int nb_pixels = 0;
2234 
2235  for (unsigned int i = static_cast<unsigned int>(this->bbox_u_min); i <= static_cast<unsigned int>(this->bbox_u_max); ++i) {
2236  unsigned int pixel_gray = static_cast<unsigned int>(I[static_cast<unsigned int>(cog_v)][i]);
2237  if ((pixel_gray >= getGrayLevelMin()) && (pixel_gray <= getGrayLevelMax())) {
2238  sum_value += pixel_gray;
2239  ++nb_pixels;
2240  }
2241  }
2242  for (unsigned int i = static_cast<unsigned int>(this->bbox_v_min); i <= static_cast<unsigned int>(this->bbox_v_max); ++i) {
2243  unsigned char pixel_gray = I[i][static_cast<unsigned int>(cog_u)];
2244  if ((pixel_gray >= getGrayLevelMin()) && (pixel_gray <= getGrayLevelMax())) {
2245  sum_value += pixel_gray;
2246  ++nb_pixels;
2247  }
2248  }
2249  if (nb_pixels < 10) { // could be good to choose the min nb points from area of dot
2250  // add diagonals points to have enough point
2251  int imin, imax;
2252  if ((cog_u - bbox_u_min) >(cog_v - bbox_v_min)) {
2253  imin = cog_v - bbox_v_min;
2254  }
2255  else {
2256  imin = cog_u - bbox_u_min;
2257  }
2258  if ((bbox_u_max - cog_u) > (bbox_v_max - cog_v)) {
2259  imax = bbox_v_max - cog_v;
2260  }
2261  else {
2262  imax = bbox_u_max - cog_u;
2263  }
2264  for (int i = -imin; i <= imax; ++i) {
2265  unsigned int pixel_gray = static_cast<unsigned int>(I[static_cast<unsigned int>(cog_v + i)][static_cast<unsigned int>(cog_u + i)]);
2266  if ((pixel_gray >= getGrayLevelMin()) && (pixel_gray <= getGrayLevelMax())) {
2267  sum_value += pixel_gray;
2268  ++nb_pixels;
2269  }
2270  }
2271 
2272  if ((cog_u - bbox_u_min) > (bbox_v_max - cog_v)) {
2273  imin = bbox_v_max - cog_v;
2274  }
2275  else {
2276  imin = cog_u - bbox_u_min;
2277  }
2278  if ((bbox_u_max - cog_u) > (cog_v - bbox_v_min)) {
2279  imax = cog_v - bbox_v_min;
2280  }
2281  else {
2282  imax = bbox_u_max - cog_u;
2283  }
2284 
2285  for (int i = -imin; i <= imax; ++i) {
2286  unsigned char pixel_gray = I[static_cast<unsigned int>(cog_v - i)][static_cast<unsigned int>(cog_u + i)];
2287  if ((pixel_gray >= getGrayLevelMin()) && (pixel_gray <= getGrayLevelMax())) {
2288  sum_value += pixel_gray;
2289  ++nb_pixels;
2290  }
2291  }
2292  }
2293 
2294  if (nb_pixels == 0) {
2295  // should never happen
2296  throw(vpTrackingException(vpTrackingException::notEnoughPointError, "No point was found"));
2297  }
2298  else {
2299  mean_gray_level = sum_value / nb_pixels;
2300  }
2301 }
2302 
2321 vpMatrix vpDot2::defineDots(vpDot2 dot[], const unsigned int &n, const std::string &dotFile, vpImage<unsigned char> &I,
2322  vpColor col, bool trackDot)
2323 {
2324  vpMatrix Cogs(n, 2);
2325  vpImagePoint cog;
2326  unsigned int i;
2327  bool fromFile = vpIoTools::checkFilename(dotFile.c_str());
2328  if (fromFile) {
2329  vpMatrix::loadMatrix(dotFile, Cogs);
2330  std::cout << Cogs.getRows() << " dots loaded from file " << dotFile << std::endl;
2331  }
2332 
2333  // test number of cogs in file
2334  if (Cogs.getRows() < n) {
2335  std::cout << "Dot file has a wrong number of dots : redefining them" << std::endl;
2336  fromFile = false;
2337  }
2338 
2339  // read from file and tracks the dots
2340  if (fromFile) {
2341  try {
2342  for (i = 0; i < n; ++i) {
2343  cog.set_uv(Cogs[i][0], Cogs[i][1]);
2344  dot[i].setGraphics(true);
2345  dot[i].setCog(cog);
2346  if (trackDot) {
2347  dot[i].initTracking(I, cog);
2348  dot[i].track(I);
2349  vpDisplay::displayCross(I, cog, 10, col);
2350  }
2351  }
2352  }
2353  catch (...) {
2354  std::cout << "Cannot track dots from file" << std::endl;
2355  fromFile = false;
2356  }
2357  vpDisplay::flush(I);
2358 
2359  // check that dots are far away ones from the other
2360  for (i = 0; ((i < n) && fromFile); ++i) {
2361  double d = sqrt(vpMath::sqr(dot[i].getHeight()) + vpMath::sqr(dot[i].getWidth()));
2362  for (unsigned int j = 0; ((j < n) && fromFile); ++j) {
2363  if (j != i) {
2364  if (dot[i].getDistance(dot[j]) < d) {
2365  fromFile = false;
2366  std::cout << "Dots from file seem incoherent" << std::endl;
2367  }
2368  }
2369  }
2370  }
2371  }
2372 
2373  if (!fromFile) {
2374  vpDisplay::display(I);
2375  vpDisplay::flush(I);
2376 
2377  std::cout << "Click on the " << n << " dots clockwise starting from upper/left dot..." << std::endl;
2378  for (i = 0; i < n; ++i) {
2379  if (trackDot) {
2380  dot[i].setGraphics(true);
2381  dot[i].initTracking(I);
2382  cog = dot[i].getCog();
2383  }
2384  else {
2385  vpDisplay::getClick(I, cog);
2386  dot[i].setCog(cog);
2387  }
2388  Cogs[i][0] = cog.get_u();
2389  Cogs[i][1] = cog.get_v();
2390  vpDisplay::displayCross(I, cog, 10, col);
2391  vpDisplay::flush(I);
2392  }
2393  }
2394 
2395  if ((!fromFile) && (dotFile != "")) {
2396  vpMatrix::saveMatrix(dotFile, Cogs);
2397  std::cout << Cogs.getRows() << " dots written to file " << dotFile << std::endl;
2398  }
2399 
2400  // back to non graphic mode
2401  for (i = 0; i < n; ++i) {
2402  dot[i].setGraphics(false);
2403  }
2404 
2405  return Cogs;
2406 }
2407 
2424 void vpDot2::trackAndDisplay(vpDot2 dot[], const unsigned int &n, vpImage<unsigned char> &I,
2425  std::vector<vpImagePoint> &cogs, vpImagePoint *cogStar)
2426 {
2427  unsigned int i;
2428  // tracking
2429  for (i = 0; i < n; ++i) {
2430  dot[i].track(I);
2431  cogs.push_back(dot[i].getCog());
2432  }
2433  // trajectories
2434  unsigned int cogs_size = cogs.size();
2435  for (i = n; i < cogs_size; ++i) {
2436  vpDisplay::displayCircle(I, cogs[i], 4, vpColor::green, true);
2437  }
2438  // initial position
2439  for (i = 0; i < n; ++i) {
2440  vpDisplay::displayCircle(I, cogs[i], 4, vpColor::blue, true);
2441  }
2442  // if exists, desired position
2443  if (cogStar != nullptr) {
2444  for (i = 0; i < n; ++i) {
2445  vpDisplay::displayDotLine(I, cogStar[i], dot[i].getCog(), vpColor::red);
2446  vpDisplay::displayCircle(I, cogStar[i], 4, vpColor::red, true);
2447  }
2448  }
2449  vpDisplay::flush(I);
2450 }
2451 
2467  const std::list<vpImagePoint> &edges_list, vpColor color, unsigned int thickness)
2468 {
2469  vpDisplay::displayCross(I, cog, (3 * thickness) + 8, color, thickness);
2470  std::list<vpImagePoint>::const_iterator it;
2471 
2472  std::list<vpImagePoint>::const_iterator edges_list_end = edges_list.end();
2473  for (it = edges_list.begin(); it != edges_list_end; ++it) {
2474  vpDisplay::displayPoint(I, *it, color);
2475  }
2476 }
2477 
2492 void vpDot2::display(const vpImage<vpRGBa> &I, const vpImagePoint &cog, const std::list<vpImagePoint> &edges_list,
2493  vpColor color, unsigned int thickness)
2494 {
2495  vpDisplay::displayCross(I, cog, (3 * thickness) + 8, color, thickness);
2496  std::list<vpImagePoint>::const_iterator it;
2497  std::list<vpImagePoint>::const_iterator edges_list_end = edges_list.end();
2498  for (it = edges_list.begin(); it != edges_list_end; ++it) {
2499  vpDisplay::displayPoint(I, *it, color);
2500  }
2501 }
2502 
2508 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:413
double m01
Definition: vpDot2.h:389
void setGraphics(bool activate)
Definition: vpDot2.h:310
double mu11
Definition: vpDot2.h:422
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:2424
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 setMaxSizeSearchDistPrecision(const double &maxSizeSearchDistancePrecision)
Definition: vpDot2.cpp:821
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:404
double m00
Definition: vpDot2.h:373
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:397
void setGrayLevelMin(const unsigned int &min)
Definition: vpDot2.h:330
void getFreemanChain(std::list< unsigned int > &freeman_chain) const
Definition: vpDot2.cpp:1520
void setHeight(const double &height)
Definition: vpDot2.cpp:686
double getMaxSizeSearchDistPrecision() 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:381
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:432
void setWidth(const double &width)
Definition: vpDot2.cpp:674
double mu20
Definition: vpDot2.h:427
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:2321
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