1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 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 http://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  * Fabien Spindler
36  * Anthony Saunier
37  *
38  *****************************************************************************/
39 
40 /*!
41   \file vpDot2.cpp
42   \brief Track a dot.
43 */
44 
45 //#define DEBUG
46 
47 #include <visp3/core/vpDisplay.h>
48 
49 // exception handling
50 #include <visp3/core/vpIoTools.h>
51 #include <visp3/core/vpMath.h>
52 #include <visp3/core/vpTrackingException.h>
53 
54 #include <cmath> // std::fabs
55 #include <iostream>
56 #include <limits> // numeric_limits
57 #include <math.h>
58 #include <visp3/blob/vpDot2.h>
59 
60 /******************************************************************************
61  *
62  *      CONSTRUCTORS AND DESTRUCTORS
63  *
64  *****************************************************************************/
65 /*!
66 
67   Initialize the tracker with default parameters.
68 
69 */
init()70 void vpDot2::init()
71 {
72   cog.set_u(0);
73   cog.set_v(0);
74 
75   width = 0;
76   height = 0;
77   surface = 0;
78   mean_gray_level = 0;
79   gray_level_min = 128;
80   gray_level_max = 255;
81   grayLevelPrecision = 0.80;
82   gamma = 1.5;
83 
84   sizePrecision = 0.65;
85   ellipsoidShapePrecision = 0.65;
86   maxSizeSearchDistancePrecision = 0.65;
87   setEllipsoidBadPointsPercentage();
88   m00 = m11 = m02 = m20 = m10 = m01 = 0.;
89   mu11 = mu02 = mu20 = 0.;
90 
91   bbox_u_min = bbox_u_max = bbox_v_min = bbox_v_max = 0;
92 
93   firstBorder_u = 0;
94   firstBorder_v = 0;
95 
96   compute_moment = false;
97   graphics = false;
98   thickness = 1;
99 }
100 
101 /*!
102   Default constructor. Just do basic default initialization.
103 */
vpDot2()104 vpDot2::vpDot2()
105   : m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), mu11(0.), mu20(0.), mu02(0.), cog(), width(0), height(0),
106     surface(0), gray_level_min(128), gray_level_max(255), mean_gray_level(0), grayLevelPrecision(0.8), gamma(1.5),
107     sizePrecision(0.65), ellipsoidShapePrecision(0.65), maxSizeSearchDistancePrecision(0.65),
108     allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(), compute_moment(false), graphics(false),
109     thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0), firstBorder_u(0), firstBorder_v()
110 {
111 }
112 
113 /*!
114 
115   Constructor initialize the coordinates of the gravity center of the dot to
116   the image point \e ip.  Rest is the same as the default constructor.
117 
118   \param ip : An image point with sub-pixel coordinates.
119 
120 */
vpDot2(const vpImagePoint & ip)121 vpDot2::vpDot2(const vpImagePoint &ip)
122   : m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), mu11(0.), mu20(0.), mu02(0.), cog(ip), width(0), height(0),
123     surface(0), gray_level_min(128), gray_level_max(255), mean_gray_level(0), grayLevelPrecision(0.8), gamma(1.5),
124     sizePrecision(0.65), ellipsoidShapePrecision(0.65), maxSizeSearchDistancePrecision(0.65),
125     allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(), compute_moment(false), graphics(false),
126     thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0), firstBorder_u(0), firstBorder_v()
127 {
128 }
129 
130 /*!
131   Copy constructor.
132 */
vpDot2(const vpDot2 & twinDot)133 vpDot2::vpDot2(const vpDot2 &twinDot)
134   : vpTracker(twinDot), m00(0.), m10(0.), m01(0.), m11(0.), m20(0.), m02(0.), mu11(0.), mu20(0.), mu02(0.), cog(),
135     width(0), height(0), surface(0), gray_level_min(128), gray_level_max(255), mean_gray_level(0),
136     grayLevelPrecision(0.8), gamma(1.5), sizePrecision(0.65), ellipsoidShapePrecision(0.65),
137     maxSizeSearchDistancePrecision(0.65), allowedBadPointsPercentage_(0.), area(), direction_list(), ip_edges_list(),
138     compute_moment(false), graphics(false), thickness(1), bbox_u_min(0), bbox_u_max(0), bbox_v_min(0), bbox_v_max(0),
139     firstBorder_u(0), firstBorder_v()
140 {
141   *this = twinDot;
142 }
143 
144 /*!
145   = operator.
146 */
operator =(const vpDot2 & twinDot)147 vpDot2 &vpDot2::operator=(const vpDot2 &twinDot)
148 {
149   cog = twinDot.cog;
150 
151   width = twinDot.width;
152   height = twinDot.height;
153   surface = twinDot.surface;
154   gray_level_min = twinDot.gray_level_min;
155   gray_level_max = twinDot.gray_level_max;
156   mean_gray_level = twinDot.mean_gray_level;
157   grayLevelPrecision = twinDot.grayLevelPrecision;
158   gamma = twinDot.gamma;
159   ;
160   sizePrecision = twinDot.sizePrecision;
161   ellipsoidShapePrecision = twinDot.ellipsoidShapePrecision;
162   maxSizeSearchDistancePrecision = twinDot.maxSizeSearchDistancePrecision;
163   allowedBadPointsPercentage_ = twinDot.allowedBadPointsPercentage_;
164   area = twinDot.area;
165 
166   direction_list = twinDot.direction_list;
167   ip_edges_list = twinDot.ip_edges_list;
168 
169   compute_moment = twinDot.compute_moment;
170   graphics = twinDot.graphics;
171   thickness = twinDot.thickness;
172 
173   bbox_u_min = twinDot.bbox_u_min;
174   bbox_u_max = twinDot.bbox_u_max;
175   bbox_v_min = twinDot.bbox_v_min;
176   bbox_v_max = twinDot.bbox_v_max;
177 
178   firstBorder_u = twinDot.firstBorder_u;
179   firstBorder_v = twinDot.firstBorder_v;
180 
181   m00 = twinDot.m00;
182   m01 = twinDot.m01;
183   m11 = twinDot.m11;
184   m10 = twinDot.m10;
185   m02 = twinDot.m02;
186   m20 = twinDot.m20;
187 
188   mu11 = twinDot.mu11;
189   mu20 = twinDot.mu20;
190   mu02 = twinDot.mu02;
191 
192   return (*this);
193 }
194 
195 /*!
196   Destructor... do nothing for the moment.
197 */
~vpDot2()198 vpDot2::~vpDot2() {}
199 
200 /******************************************************************************
201  *
202  *      PUBLIC METHODS
203  *****************************************************************************/
204 
205 /*!
206   Display in overlay the dot edges and center of gravity.
207 
208   \param I : Image.
209   \param color : The color used for the display.
210   \param t : Thickness of the displayed cross located at the dot cog.
211 */
display(const vpImage<unsigned char> & I,vpColor color,unsigned int t) const212 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 
222 /*!
223 
224   Initialize the tracking with a mouse click on the image and update the dot
225   characteristics (center of gravity, moments) by a call to track().
226 
227   Wait a user click in a white area in the image. The clicked pixel
228   will be the starting point from which the dot will be tracked.
229 
230   To get center of gravity of the dot, see getCog(). To compute the
231   moments see setComputeMoments(). To get the width or height of the
232   dot, call getWidth() and getHeight(). The area of the dot is
233   given by getArea().
234 
235   \param I : Image.
236   \param size : Size of the dot to track.
237 
238   If no valid dot was found in the window, return an exception.
239 
240   \exception vpTrackingException::featureLostError : If the dot initialization
241   failed. The initialization can fail if the following characteristics are
242   not valid;
243   - The gray level is between gray level min and gray level max.
244 
245   - The shape should be ellipsoid if
246     setEllipsoidShapePrecision(ellipsoidShapePrecision) is used.
247     This is the default case. To track a non ellipsoid shape use
248     setEllipsoidShapePrecision(0).
249 
250   \sa track()
251 
252 */
initTracking(const vpImage<unsigned char> & I,unsigned int size)253 void vpDot2::initTracking(const vpImage<unsigned char> &I, unsigned int size)
254 {
255   while (vpDisplay::getClick(I, cog) != true) {}
256 
257   unsigned int i = (unsigned int)cog.get_i();
258   unsigned int j = (unsigned int)cog.get_j();
259 
260   double Ip = pow((double)I[i][j] / 255, 1 / gamma);
261 
262   if (Ip - (1 - grayLevelPrecision) < 0) {
263     gray_level_min = 0;
264   } else {
265     gray_level_min = (unsigned int)(255 * pow(Ip - (1 - grayLevelPrecision), gamma));
266     if (gray_level_min > 255)
267       gray_level_min = 255;
268   }
269   gray_level_max = (unsigned int)(255 * pow(Ip + (1 - grayLevelPrecision), gamma));
270   if (gray_level_max > 255)
271     gray_level_max = 255;
272 
273   setWidth(size);
274   setHeight(size);
275 
276   try {
277     track(I);
278   } catch (const vpException &e) {
279     // vpERROR_TRACE("Error caught") ;
280     throw(e);
281   }
282 }
283 
284 /*!
285 
286   Initialize the tracking for a dot supposed to be located at (u,v) and update
287   the dot characteristics (center of gravity, moments) by a call to track().
288 
289   \param I : Image to process.
290 
291   \param ip : Location of the starting point from which the dot will be
292   tracked in the image.
293 
294   \param size : Size of the dot to track.
295 
296   To get center of gravity of the dot, see getCog(). To compute the
297   moments see setComputeMoments().
298 
299   If no valid dot was found in the window, return an exception.
300 
301   \exception vpTrackingException::featureLostError : If the dot initialization
302   failed. The initialization can fail if the following characteristics are
303   not valid;
304   - The gray level is between gray level min and gray level max.
305 
306   - The shape should be ellipsoid if
307     setEllipsoidShapePrecision(ellipsoidShapePrecision) is used.
308     This is the default case. To track a non ellipsoid shape use
309     setEllipsoidShapePrecision(0).
310 */
initTracking(const vpImage<unsigned char> & I,const vpImagePoint & ip,unsigned int size)311 void vpDot2::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip, unsigned int size)
312 {
313   cog = ip;
314 
315   unsigned int i = (unsigned int)cog.get_i();
316   unsigned int j = (unsigned int)cog.get_j();
317 
318   double Ip = pow((double)I[i][j] / 255, 1 / gamma);
319 
320   if (Ip - (1 - grayLevelPrecision) < 0) {
321     gray_level_min = 0;
322   } else {
323     gray_level_min = (unsigned int)(255 * pow(Ip - (1 - grayLevelPrecision), gamma));
324     if (gray_level_min > 255)
325       gray_level_min = 255;
326   }
327   gray_level_max = (unsigned int)(255 * pow(Ip + (1 - grayLevelPrecision), gamma));
328   if (gray_level_max > 255)
329     gray_level_max = 255;
330 
331   setWidth(size);
332   setHeight(size);
333 
334   try {
335     track(I);
336   } catch (const vpException &e) {
337     // vpERROR_TRACE("Error caught") ;
338     throw(e);
339   }
340 }
341 
342 /*!
343 
344   Initialize the tracking for a dot supposed to be located at (u,v) and update
345   the dot characteristics (center of gravity, moments) by a call to track().
346 
347   The sub pixel coordinates of the dot are updated. To get the center
348   of gravity coordinates of the dot, use getCog(). To
349   compute the moments use setComputeMoments(true) before a call to
350   initTracking().
351 
352   \param I : Image to process.
353 
354   \param ip : Location of the starting point from which the dot will
355   be tracked in the image.
356 
357   \param gray_lvl_min : Minimum gray level threshold used to segment the dot;
358   value comprised between 0 and 255.
359 
360   \param gray_lvl_max : Maximum gray level threshold used to segment the
361   dot; value comprised between 0 and 255. \e gray_level_max should be
362   greater than \e gray_level_min.
363 
364   \param size : Size of the dot to track.
365 
366   If no valid dot was found in the window, return an exception.
367 
368   \exception vpTrackingException::featureLostError : If the dot initialization
369   failed. The initialization can fail if the following characteristics are
370   not valid;
371   - The gray level is between gray level min and gray level max.
372 
373   - The shape should be ellipsoid if
374     setEllipsoidShapePrecision(ellipsoidShapePrecision) is used.
375     This is the default case. To track a non ellipsoid shape use
376     setEllipsoidShapePrecision(0).
377 
378   \sa track(), getCog()
379 
380 */
initTracking(const vpImage<unsigned char> & I,const vpImagePoint & ip,unsigned int gray_lvl_min,unsigned int gray_lvl_max,unsigned int size)381 void vpDot2::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip, unsigned int gray_lvl_min,
382                           unsigned int gray_lvl_max, unsigned int size)
383 {
384   cog = ip;
385 
386   this->gray_level_min = gray_lvl_min;
387   this->gray_level_max = gray_lvl_max;
388 
389   setWidth(size);
390   setHeight(size);
391 
392   try {
393     track(I);
394   } catch (const vpException &e) {
395     // vpERROR_TRACE("Error caught") ;
396     throw(e);
397   }
398 }
399 
400 /*!
401 
402   Try to locate the dot in the image:
403 
404   - First, estimate the new position of the dot, using its previous position.
405   - Then compute the center of gravity (surface, width height) of the
406     tracked entity from the Freeman chain elements.
407   - If the dot is lost (estimated point too dark, too much surface
408   change,...), search the dot in a window around the previous position.
409   - If no valid dot was found in the window, return an exception.
410 
411   \param I : Image.
412 
413   \param canMakeTheWindowGrow: if true, the size of the searching area is
414   increased if the blob is not found, otherwise it stays the same. Default
415   value is true.
416 
417   \exception vpTrackingException::featureLostError : If the dot tracking
418   failed. The tracking can fail if the following characteristics are not
419   valid;
420   - The gray level is between gray level min and gray level max.
421 
422   - The size (width or height) and the surface (in terms of number of
423     pixels) should not differ to much with the previous dot.
424 
425   - The shape should be ellipsoid if
426     setEllipsoidShapePrecision(ellipsoidShapePrecision) is used.
427     This is the default case. To track a non ellipsoid shape use
428     setEllipsoidShapePrecision(0).
429 
430   To get the center of gravity of the dot, call getCog(). To get the
431   width or height of the dot, call getWidth() and getHeight(). The area of the
432   dot is given by getArea().
433 
434   To compute all the inertia moments associated to the dot see
435   setComputeMoments().
436 
437   To get the pixels coordinates on the dot boundary, see getList_u() and
438   getList_v().
439 
440 */
track(const vpImage<unsigned char> & I,bool canMakeTheWindowGrow)441 void vpDot2::track(const vpImage<unsigned char> &I, bool canMakeTheWindowGrow)
442 {
443   m00 = m11 = m02 = m20 = m10 = m01 = 0;
444 
445   // First, we will estimate the position of the tracked point
446 
447   // Set the search area to the entire image
448   setArea(I);
449 
450   // create a copy of the dot to search
451   // This copy can be saw as the previous dot used to check if the current one
452   // found with computeParameters() is similar to the previous one (see
453   // isValid() function). If the found dot is not similar (or valid), we use
454   // this copy to set the current found dot to the previous one (see below).
455   vpDot2 wantedDot(*this);
456 
457   //   vpDEBUG_TRACE(0, "Previous dot: ");
458   //   vpDEBUG_TRACE(0, "u: %f v: %f", get_u(), get_v());
459   //   vpDEBUG_TRACE(0, "w: %f h: %f", getWidth(), getHeight());
460   bool found = computeParameters(I, cog.get_u(), cog.get_v());
461 
462   if (found) {
463     // test if the found dot is valid (ie similar to the previous one)
464     found = isValid(I, wantedDot);
465     if (!found) {
466       *this = wantedDot;
467       // std::cout << "The found dot is not valid" << std::endl;
468     }
469   }
470 
471   if (!found) {
472     //     vpDEBUG_TRACE(0, "Search the dot in a biggest window around the
473     //     last position"); vpDEBUG_TRACE(0, "Bad computed dot: ");
474     //     vpDEBUG_TRACE(0, "u: %f v: %f", get_u(), get_v());
475     //     vpDEBUG_TRACE(0, "w: %f h: %f", getWidth(), getHeight());
476 
477     // if estimation was wrong (get an error tracking), look for the dot
478     // closest from the estimation,
479     // i.e. search for dots in an a region of interest around the this dot and
480     // get the first element in the area.
481 
482     // first get the size of the search window from the dot size
483     double searchWindowWidth = 0.0, searchWindowHeight = 0.0;
484     // if( getWidth() == 0 || getHeight() == 0 )
485     if (std::fabs(getWidth()) <= std::numeric_limits<double>::epsilon() ||
486         std::fabs(getHeight()) <= std::numeric_limits<double>::epsilon()) {
487       searchWindowWidth = 80.;
488       searchWindowHeight = 80.;
489     } else if (canMakeTheWindowGrow) {
490       searchWindowWidth = getWidth() * 5;
491       searchWindowHeight = getHeight() * 5;
492     } else {
493       searchWindowWidth = getWidth();
494       searchWindowHeight = getHeight();
495     }
496 
497     std::list<vpDot2> candidates;
498     searchDotsInArea(I, (int)(this->cog.get_u() - searchWindowWidth / 2.0),
499                      (int)(this->cog.get_v() - searchWindowHeight / 2.0), (unsigned int)searchWindowWidth,
500                      (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") ;
506       throw(vpTrackingException(vpTrackingException::featureLostError, "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   //   else {
532   //     // test if the found dot is valid,
533   //     if( ! isValid( I, wantedDot ) ) {
534   //       *this = wantedDot;
535   //       vpERROR_TRACE("The found dot is invalid:",
536   // 		    "- could be a problem of size (width or height) or "
537   // 		    "  surface (number of pixels) which differ too much "
538   // 		    "  to the previous one "
539   // 		    "- or a problem of the shape which is not ellipsoid if "
540   // 		    "  use setEllipsoidShapePrecision(double
541   // ellipsoidShapePrecision) "
542   //         "  which is the default case. "
543   // 		    "  To track a non ellipsoid shape use
544   // setEllipsoidShapePrecision(0)") ;
545   //       throw(vpTrackingException(vpTrackingException::featureLostError,
546   // 				"The found dot is invalid")) ;
547   //     }
548   //   }
549 
550   // if this dot is partially out of the image, return an error tracking.
551   if (!isInImage(I)) {
552     // vpERROR_TRACE("The center of gravity of the dot is not in the image") ;
553     throw(vpTrackingException(vpTrackingException::featureLostError,
554                               "The center of gravity of the dot is not in the image"));
555   }
556 
557   // Get dots center of gravity
558   // unsigned int u = (unsigned int) this->cog.get_u();
559   // unsigned int v = (unsigned int) this->cog.get_v();
560   // Updates the min and max gray levels for the next iteration
561   // double Ip = pow((double)I[v][u]/255,1/gamma);
562   double Ip = pow(getMeanGrayLevel() / 255, 1 / gamma);
563   // printf("current value of gray level center : %i\n", I[v][u]);
564 
565   // getMeanGrayLevel(I);
566   if (Ip - (1 - grayLevelPrecision) < 0) {
567     gray_level_min = 0;
568   } else {
569     gray_level_min = (unsigned int)(255 * pow(Ip - (1 - grayLevelPrecision), gamma));
570     if (gray_level_min > 255)
571       gray_level_min = 255;
572   }
573   gray_level_max = (unsigned int)(255 * pow(Ip + (1 - grayLevelPrecision), gamma));
574   if (gray_level_max > 255)
575     gray_level_max = 255;
576 
577   // printf("%i %i \n",gray_level_max,gray_level_min);
578   if (graphics) {
579     // display a red cross at the center of gravity's location in the image.
580 
581     vpDisplay::displayCross(I, this->cog, 3 * thickness + 8, vpColor::red, thickness);
582     // vpDisplay::flush(I);
583   }
584 }
585 
586 /*!
587 
588   Track and get the new dot coordinates. See track() for a more complete
589   description
590 
591   \param[in] I : Image to process.
592 
593   \param[out] ip : Sub pixel coordinate of the tracked dot center of gravity.
594 
595   \param[in] canMakeTheWindowGrow : if true, the size of the searching area is
596   increased if the blob is not found, otherwise it stays the same. Default
597   value is true.
598 
599   The behavior of this method is similar to the following code:
600   \code
601   vpDot2 d;
602   d.track(I);
603   vpImagePoint cog = d.getCog();
604   \endcode
605 
606   \sa track()
607 */
track(const vpImage<unsigned char> & I,vpImagePoint & ip,bool canMakeTheWindowGrow)608 void vpDot2::track(const vpImage<unsigned char> &I, vpImagePoint &ip, bool canMakeTheWindowGrow)
609 {
610   track(I, canMakeTheWindowGrow);
611 
612   ip = this->cog;
613 }
614 
615 ///// GET METHODS
616 ////////////////////////////////////////////////////////////////
617 
618 /*!
619   Return the width of the dot.
620 
621   \sa getHeight()
622 */
getWidth() const623 double vpDot2::getWidth() const { return width; }
624 
625 /*!
626   Return the height of the dot.
627 
628   \sa getWidth()
629 */
getHeight() const630 double vpDot2::getHeight() const { return height; }
631 
632 /*!
633   Return the area of the dot.
634 
635   The area of the dot is also given by \f$|m00|\f$.
636 */
getArea() const637 double vpDot2::getArea() const { return fabs(surface); }
638 
639 /*!
640   Return the precision of the gray level of the dot. It is a double
641   precision float which value is in [0,1]. 1 means full precision, whereas
642   values close to 0 show a very bad precision.
643 */
getGrayLevelPrecision() const644 double vpDot2::getGrayLevelPrecision() const { return grayLevelPrecision; }
645 
646 /*!
647   Return the precision of the size of the dot. It is a double
648   precision float which value is in [0.05,1]. 1 means full precision, whereas
649   values close to 0 show a very bad precision.
650 */
getSizePrecision() const651 double vpDot2::getSizePrecision() const { return sizePrecision; }
652 
653 /*!
654   Return the precision of the ellipsoid shape of the dot. It is a double
655   precision float which value is in [0,1]. 1 means full precision, whereas
656   values close to 0 show a very bad precision.
657 
658   \sa setEllipsoidShapePrecision()
659 */
getEllipsoidShapePrecision() const660 double vpDot2::getEllipsoidShapePrecision() const { return ellipsoidShapePrecision; }
661 
662 /*!
663   Return the precision of the search maximum distance to get the starting
664   point on a dot border. It is a double precision float which value is in
665   [0.05,1]. 1 means full precision, whereas values close to 0 show a very bad
666   precision.
667 */
getMaxSizeSearchDistancePrecision() const668 double vpDot2::getMaxSizeSearchDistancePrecision() const { return maxSizeSearchDistancePrecision; }
669 
670 /*!
671   Return the distance between the two center of dots.
672 */
getDistance(const vpDot2 & distantDot) const673 double vpDot2::getDistance(const vpDot2 &distantDot) const
674 {
675   vpImagePoint cogDistantDot = distantDot.getCog();
676   double diff_u = this->cog.get_u() - cogDistantDot.get_u();
677   double diff_v = this->cog.get_v() - cogDistantDot.get_v();
678   return sqrt(diff_u * diff_u + diff_v * diff_v);
679 }
680 
681 ///// SET METHODS ////////////////////////////////////////////////////////////
682 
683 /*!
684 
685   Set the width of the dot. This is meant to be used to search a dot in an
686   area.
687 
688   \param w : Width of a dot to search in a region of interest.
689 
690   \sa setHeight(), setArea(), setSizePrecision()
691 */
setWidth(const double & w)692 void vpDot2::setWidth(const double &w) { this->width = w; }
693 
694 /*!
695 
696   Set the height of the dot. This is meant to be used to search a dot in an
697   area.
698 
699   \param h : Height of a dot to search in a region of interest.
700 
701   \sa setWidth(), setArea(), setSizePrecision()
702 
703 */
setHeight(const double & h)704 void vpDot2::setHeight(const double &h) { this->height = h; }
705 
706 /*!
707 
708   Set the area of the dot. This is meant to be used to search a dot in a
709   region of interest.
710 
711   \param a : Area of a dot to search in a region of interest.
712 
713   \sa setWidth(), setHeight(), setSizePrecision()
714 
715 */
setArea(const double & a)716 void vpDot2::setArea(const double &a) { this->surface = a; }
717 
718 /*!
719 
720   Set the precision of the gray level of the dot.
721 
722   \param precision : It is a double precision float which value is in [0.05,1]:
723   - 1 means full precision, whereas values close to 0 show a very bad
724   accuracy.
725   - Values lower or equal to 0.05 are brought back to 0.05.
726   - Values higher than  1 are brought back to 1
727   If the initial gray level is I, the gray levels of the dot will be between :
728   \f$Imin=255*\big((\frac{I}{255})^{{\gamma}^{-1}}-(1-grayLevelPrecision)\big)^{\gamma}\f$
729   and
730   \f$Imax=255*\big((\frac{I}{255})^{{\gamma}^{-1}}+(1-grayLevelPrecision)\big)^{\gamma}\f$
731   with \f$\gamma=1.5\f$ .
732 
733   \sa setGrayLevelMin(), setGrayLevelMax()
734 */
setGrayLevelPrecision(const double & precision)735 void vpDot2::setGrayLevelPrecision(const double &precision)
736 {
737   double epsilon = 0.05;
738   if (grayLevelPrecision < epsilon) {
739     this->grayLevelPrecision = epsilon;
740   } else if (grayLevelPrecision > 1) {
741     this->grayLevelPrecision = 1.0;
742   } else {
743     this->grayLevelPrecision = precision;
744   }
745 }
746 /*!
747 
748   Set the precision of the size of the dot. Used to test the validity of the
749   dot
750 
751   \param precision : It is a double precision float which value is in [0,1]:
752   - this is the limit ratio between the tested parameter and the measured one.
753     minSize = sizePrecision*originalSize ; maxSize =
754   originalSize/sizePrecision ;
755   - 1 means full precision, whereas values close to 0 show a very bad
756   accuracy.
757   - Values lower or equal to 0 are brought back to 0.
758   - Values higher than 1 are brought back to 1.
759   - To desactivate validity test set sizePrecision to 0
760 
761   \sa setWidth(), setHeight(), setArea()
762 */
setSizePrecision(const double & precision)763 void vpDot2::setSizePrecision(const double &precision)
764 {
765   if (sizePrecision < 0) {
766     this->sizePrecision = 0;
767   } else if (sizePrecision > 1) {
768     this->sizePrecision = 1.0;
769   } else {
770     this->sizePrecision = precision;
771   }
772 }
773 
774 /*!
775   Indicates if the dot should have an ellipsoid shape to be valid.
776   \param precision : It is a double precision float which value is in [0,1]:
777 
778   - 1 means full precision, whereas values close to 0 show a very bad
779   accuracy.
780   - Values lower or equal to 0 are brought back to 0.
781   - Values higher than  1 are brought back to 1.
782   To track a non ellipsoid shape use setEllipsoidShapePrecision(0).
783 
784   The following example show how to track a blob with a height constraint on
785   an ellipsoid shape. The tracking will fail if the shape is not ellipsoid.
786   \code
787   vpDot2 dot;
788   dot.setEllipsoidShapePrecision(0.9); // to track a blob with a height
789   constraint attendee on a circle shape
790   ...
791   dot.track();
792   \endcode
793 
794   This other example shows how to remove any constraint on the shape. Here the
795   tracker will be able to track any shape, including square or rectangular
796   shapes.
797   \code
798   vpDot2 dot;
799   dot.setEllipsoidShapePrecision(0.); // to track a blob without any constraint on the shape
800   ...
801   dot.track();
802   \endcode
803 
804   \sa getEllipsoidShapePrecision()
805 */
setEllipsoidShapePrecision(const double & precision)806 void vpDot2::setEllipsoidShapePrecision(const double &precision)
807 {
808 
809   if (ellipsoidShapePrecision < 0) {
810     this->ellipsoidShapePrecision = 0;
811   } else if (ellipsoidShapePrecision > 1) {
812     this->ellipsoidShapePrecision = 1.0;
813   } else {
814     this->ellipsoidShapePrecision = precision;
815   }
816 }
817 
818 /*!
819 
820   Set the precision of the search maximum distance to get the starting point
821   on a dot border. A too low value mean a large search area.
822 
823   \param precision : It is a double precision float which value is in
824   [0.05,1]:
825   - this is the limit ratio between the tested parameter and the measured one.
826      distance < getWidth()/(getSizePrecision()+epsilon);
827   - 1 means full precision, whereas values close to 0 show a very bad
828   accuracy.
829   - Values lower or equal to 0.05 are brought back to 0.05
830   - Values higher than 1 are brought back to 1.
831 
832 */
setMaxSizeSearchDistancePrecision(const double & precision)833 void vpDot2::setMaxSizeSearchDistancePrecision(const double &precision)
834 {
835   double epsilon = 0.05;
836   if (maxSizeSearchDistancePrecision < epsilon) {
837     this->maxSizeSearchDistancePrecision = epsilon;
838   } else if (maxSizeSearchDistancePrecision > 1) {
839     this->maxSizeSearchDistancePrecision = 1.0;
840   } else {
841     this->maxSizeSearchDistancePrecision = precision;
842   }
843 }
844 
845 /*!
846 
847   Set the parameters of the area in which a dot is search to the image
848   dimension.
849 
850   \param I : Image.
851 
852 */
setArea(const vpImage<unsigned char> & I)853 void vpDot2::setArea(const vpImage<unsigned char> &I) { setArea(I, 0, 0, I.getWidth(), I.getHeight()); }
854 
855 /*!
856 
857   Set the parameters of an area by setting the upper-left corner coordinates
858   (u, v), width and height.
859 
860   \param I : The image we are working with.
861   \param u : Area horizontal left coordinate.
862   \param v : Area vertical top coordinate.
863   \param w : Area width.
864   \param h : Area height.
865 
866 */
setArea(const vpImage<unsigned char> & I,int u,int v,unsigned int w,unsigned int h)867 void vpDot2::setArea(const vpImage<unsigned char> &I, int u, int v, unsigned int w, unsigned int h)
868 {
869   unsigned int image_w = I.getWidth();
870   unsigned int image_h = I.getHeight();
871 
872   // Bounds the area to the image
873   if (u < 0)
874     u = 0;
875   else if (u >= (int)image_w)
876     u = (int)image_w - 1;
877   if (v < 0)
878     v = 0;
879   else if (v >= (int)image_h)
880     v = (int)image_h - 1;
881 
882   if (((unsigned int)u + w) > image_w)
883     w = image_w - (unsigned int)u - 1;
884   if (((unsigned int)v + h) > image_h)
885     h = image_h - (unsigned int)v - 1;
886 
887   area.setRect(u, v, w, h);
888 }
889 
890 /*!
891 
892   Set the parameters of the area.
893 
894   \param a : Area.
895 
896 */
setArea(const vpRect & a)897 void vpDot2::setArea(const vpRect &a) { area = a; }
898 
899 ///// CLASS FUNCTIONALITY ////////////////////////////////////////////////////
900 
901 /*!
902 
903   Look for a list of dot matching this dot parameters within the entire
904   image.
905 
906   \warning Allocates memory for the list of dots returned by this method.
907   Desallocation has to be done by yourself.
908 
909   \param I : Image.
910 
911   \param niceDots: List of the dots that are found.
912 
913   Before calling this method, dot characteristics to found have to be set
914   like:
915 
916   \code
917   vpDot2 d;
918 
919   // Set dot characteristics for the auto detection
920   d.setWidth(24);
921   d.setHeight(23);
922   d.setArea(412);
923   d.setGrayLevelMin(160);
924   d.setGrayLevelMax(255);
925   d.setGrayLevelPrecision(0.8);
926   d.setSizePrecision(0.65);
927   d.setEllipsoidShapePrecision(0.65);
928   \endcode
929 
930   To search dots in the whole image:
931   \code
932   std::list<vpDot2> list_d;
933   d.searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), list_d) ;
934   \endcode
935 
936   The number of dots found in the area is given by:
937   \code
938   std::cout << list_d.size();
939   \endcode
940 
941   To parse all the dots:
942   \code
943   std::list<vpDot2>::iterator it;
944   for (it = list_d.begin(); it != list_d.end(); ++it) {
945       vpDot2 tmp_d = *it;
946   }
947   \endcode
948 
949   \sa searchDotsInArea(vpImage<unsigned char>&, int, int, unsigned int,
950   unsigned int, std::list<vpDot2> &)
951 */
searchDotsInArea(const vpImage<unsigned char> & I,std::list<vpDot2> & niceDots)952 void vpDot2::searchDotsInArea(const vpImage<unsigned char> &I, std::list<vpDot2> &niceDots)
953 {
954   searchDotsInArea(I, 0, 0, I.getWidth(), I.getHeight(), niceDots);
955 }
956 
957 /*!
958 
959   Look for a list of dot matching this dot parameters within a region of
960   interest defined by a rectangle in the image. The rectangle upper-left
961   coordinates are given by
962   (\e area_u, \e area_v). The size of the rectangle is given by \e area_w and
963   \e area_h.
964 
965   \param I : Image to process.
966   \param area_u : Coordinate (column) of the upper-left area corner.
967   \param area_v : Coordinate (row) of the upper-left area corner.
968 
969   \param area_w : Width or the area in which a dot is searched.
970   \param area_h : Height or the area in which a dot is searched.
971 
972   \param niceDots: List of the dots that are found.
973 
974   \warning Allocates memory for the list of vpDot2 returned by this method.
975   Desallocation has to be done by yourself, see searchDotsInArea()
976 
977   \sa searchDotsInArea(vpImage<unsigned char>& I, std::list<vpDot2> &)
978 */
searchDotsInArea(const vpImage<unsigned char> & I,int area_u,int area_v,unsigned int area_w,unsigned int area_h,std::list<vpDot2> & niceDots)979 void vpDot2::searchDotsInArea(const vpImage<unsigned char> &I, int area_u, int area_v, unsigned int area_w,
980                               unsigned int area_h, std::list<vpDot2> &niceDots)
981 
982 {
983   // clear the list of nice dots
984   niceDots.clear();
985 
986   // Fit the input area in the image; we keep only the common part between
987   // this area and the image.
988   setArea(I, area_u, area_v, area_w, area_h);
989 
990   // compute the size of the search grid
991   unsigned int gridWidth;
992   unsigned int gridHeight;
993   getGridSize(gridWidth, gridHeight);
994 
995   if (graphics) {
996     // Display the area were the dot is search
997     vpDisplay::displayRectangle(I, area, vpColor::blue, false, thickness);
998     // vpDisplay::flush(I);
999   }
1000 
1001 #ifdef DEBUG
1002   vpDisplay::displayRectangle(I, area, vpColor::blue);
1003   vpDisplay::flush(I);
1004 #endif
1005   // start the search loop; for all points of the search grid,
1006   // test if the pixel belongs to a valid dot.
1007   // if it is so eventually add it to the vector of valid dots.
1008   std::list<vpDot2> badDotsVector;
1009   std::list<vpDot2>::iterator itnice;
1010   std::list<vpDot2>::iterator itbad;
1011 
1012   vpDot2 *dotToTest = NULL;
1013   vpDot2 tmpDot;
1014 
1015   unsigned int area_u_min = (unsigned int)area.getLeft();
1016   unsigned int area_u_max = (unsigned int)area.getRight();
1017   unsigned int area_v_min = (unsigned int)area.getTop();
1018   unsigned int area_v_max = (unsigned int)area.getBottom();
1019 
1020   unsigned int u, v;
1021   vpImagePoint cogTmpDot;
1022 
1023   for (v = area_v_min; v < area_v_max; v = v + gridHeight) {
1024     for (u = area_u_min; u < area_u_max; u = u + gridWidth) {
1025       // if the pixel we're in doesn't have the right color (outside the
1026       // graylevel interval), no need to check further, just get to the
1027       // next grid intersection.
1028       if (!hasGoodLevel(I, u, v))
1029         continue;
1030 
1031       // Test if an other germ is inside the bounding box of a dot previously
1032       // detected
1033       bool good_germ = true;
1034 
1035       itnice = niceDots.begin();
1036       while (itnice != niceDots.end() && good_germ == true) {
1037         tmpDot = *itnice;
1038 
1039         cogTmpDot = tmpDot.getCog();
1040         double u0 = cogTmpDot.get_u();
1041         double v0 = cogTmpDot.get_v();
1042         double half_w = tmpDot.getWidth() / 2.;
1043         double half_h = tmpDot.getHeight() / 2.;
1044 
1045         if (u >= (u0 - half_w) && u <= (u0 + half_w) && v >= (v0 - half_h) && v <= (v0 + half_h)) {
1046           // Germ is in a previously detected dot
1047           good_germ = false;
1048         }
1049         ++itnice;
1050       }
1051 
1052       if (!good_germ)
1053         continue;
1054 
1055       // Compute the right border position for this possible germ
1056       unsigned int border_u;
1057       unsigned int border_v;
1058       if (findFirstBorder(I, u, v, border_u, border_v) == false) {
1059         // germ is not good.
1060         // Jump all the pixels between v,u and v,
1061         // dotToTest->getFirstBorder_u()
1062         u = border_u;
1063         v = border_v;
1064         continue;
1065       }
1066 
1067       itbad = badDotsVector.begin();
1068 #define vpBAD_DOT_VALUE (*itbad)
1069       vpImagePoint cogBadDot;
1070 
1071       while (itbad != badDotsVector.end() && good_germ == true) {
1072         if ((double)u >= vpBAD_DOT_VALUE.bbox_u_min && (double)u <= vpBAD_DOT_VALUE.bbox_u_max &&
1073             (double)v >= vpBAD_DOT_VALUE.bbox_v_min && (double)v <= vpBAD_DOT_VALUE.bbox_v_max) {
1074           std::list<vpImagePoint>::const_iterator it_edges = ip_edges_list.begin();
1075           while (it_edges != ip_edges_list.end() && good_germ == true) {
1076             // Test if the germ belong to a previously detected dot:
1077             // - from the germ go right to the border and compare this
1078             //   position to the list of pixels of previously detected dots
1079             cogBadDot = *it_edges;
1080             // if( border_u == cogBadDot.get_u() && v == cogBadDot.get_v()) {
1081             if ((std::fabs(border_u - cogBadDot.get_u()) <=
1082                  vpMath::maximum(std::fabs((double)border_u), std::fabs(cogBadDot.get_u())) *
1083                      std::numeric_limits<double>::epsilon()) &&
1084                 (std::fabs(v - cogBadDot.get_v()) <=
1085                  vpMath::maximum(std::fabs((double)v), std::fabs(cogBadDot.get_v())) *
1086                      std::numeric_limits<double>::epsilon())) {
1087               good_germ = false;
1088             }
1089             ++it_edges;
1090           }
1091         }
1092         ++itbad;
1093       }
1094 #undef vpBAD_DOT_VALUE
1095 
1096       if (!good_germ) {
1097         // Jump all the pixels between v,u and v,
1098         // dotToTest->getFirstBorder_u()
1099         u = border_u;
1100         v = border_v;
1101         continue;
1102       }
1103 
1104       vpTRACE(4, "Try germ (%d, %d)", u, v);
1105 
1106       vpImagePoint germ;
1107       germ.set_u(u);
1108       germ.set_v(v);
1109 
1110       // otherwise estimate the width, height and surface of the dot we
1111       // created, and test it.
1112       if (dotToTest != NULL)
1113         delete dotToTest;
1114       dotToTest = getInstance();
1115       dotToTest->setCog(germ);
1116       dotToTest->setGrayLevelMin(getGrayLevelMin());
1117       dotToTest->setGrayLevelMax(getGrayLevelMax());
1118       dotToTest->setGrayLevelPrecision(getGrayLevelPrecision());
1119       dotToTest->setSizePrecision(getSizePrecision());
1120       dotToTest->setGraphics(graphics);
1121       dotToTest->setGraphicsThickness(thickness);
1122       dotToTest->setComputeMoments(true);
1123       dotToTest->setArea(area);
1124       dotToTest->setEllipsoidShapePrecision(ellipsoidShapePrecision);
1125       dotToTest->setEllipsoidBadPointsPercentage(allowedBadPointsPercentage_);
1126 
1127       // first compute the parameters of the dot.
1128       // if for some reasons this caused an error tracking
1129       // (dot partially out of the image...), check the next intersection
1130       if (dotToTest->computeParameters(I) == false) {
1131         // Jump all the pixels between v,u and v,
1132         // dotToTest->getFirstBorder_u()
1133         u = border_u;
1134         v = border_v;
1135         continue;
1136       }
1137       // if the dot to test is valid,
1138       if (dotToTest->isValid(I, *this)) {
1139         vpImagePoint cogDotToTest = dotToTest->getCog();
1140         // Compute the distance to the center. The center used here is not the
1141         // area center available by area.getCenter(area_center_u,
1142         // area_center_v) but the center of the input area which may be
1143         // partially outside the image.
1144 
1145         double area_center_u = area_u + area_w / 2.0 - 0.5;
1146         double area_center_v = area_v + area_h / 2.0 - 0.5;
1147 
1148         double thisDiff_u = cogDotToTest.get_u() - area_center_u;
1149         double thisDiff_v = cogDotToTest.get_v() - area_center_v;
1150         double thisDist = sqrt(thisDiff_u * thisDiff_u + thisDiff_v * thisDiff_v);
1151 
1152         bool stopLoop = false;
1153         itnice = niceDots.begin();
1154 
1155         while (itnice != niceDots.end() && stopLoop == false) {
1156           tmpDot = *itnice;
1157 
1158           // double epsilon = 0.001; // detecte +sieurs points
1159           double epsilon = 3.0;
1160           // if the center of the dot is the same than the current
1161           // don't add it, test the next point of the grid
1162           cogTmpDot = tmpDot.getCog();
1163 
1164           if (fabs(cogTmpDot.get_u() - cogDotToTest.get_u()) < epsilon &&
1165               fabs(cogTmpDot.get_v() - cogDotToTest.get_v()) < epsilon) {
1166             stopLoop = true;
1167             // Jump all the pixels between v,u and v,
1168             // tmpDot->getFirstBorder_u()
1169             u = border_u;
1170             v = border_v;
1171             continue;
1172           }
1173 
1174           double otherDiff_u = cogTmpDot.get_u() - area_center_u;
1175           double otherDiff_v = cogTmpDot.get_v() - area_center_v;
1176           double otherDist = sqrt(otherDiff_u * otherDiff_u + otherDiff_v * otherDiff_v);
1177 
1178           // if the distance of the curent vector element to the center
1179           // is greater than the distance of this dot to the center,
1180           // then add this dot before the current vector element.
1181           if (otherDist > thisDist) {
1182             niceDots.insert(itnice, *dotToTest);
1183             ++itnice;
1184             stopLoop = true;
1185             // Jump all the pixels between v,u and v,
1186             // tmpDot->getFirstBorder_u()
1187             u = border_u;
1188             v = border_v;
1189             continue;
1190           }
1191           ++itnice;
1192         }
1193         vpTRACE(4, "End while (%d, %d)", u, v);
1194 
1195         // if we reached the end of the vector without finding the dot
1196         // or inserting it, insert it now.
1197         if (itnice == niceDots.end() && stopLoop == false) {
1198           niceDots.push_back(*dotToTest);
1199         }
1200       } else {
1201         // Store bad dots
1202         badDotsVector.push_front(*dotToTest);
1203       }
1204     }
1205   }
1206   if (dotToTest != NULL)
1207     delete dotToTest;
1208 }
1209 
1210 /*!
1211 
1212   Check if the dot is "like" the wanted dot passed in.
1213 
1214   Compare the following characteristics of the dot to the wanted dot;
1215   - the size (width or height)
1216   - the surface (number of pixels)
1217   - the geometry; the shape should be ellipsoid if
1218     setEllipsoidShapePrecision(double ellispoidShapePrecision) is used.
1219 
1220   \return If it is so, return true, otherwise return false.
1221 
1222   \warning Parameters of the wanted dot (width, height, surface, in level, out
1223   level, accuracy) must already be set before; see
1224   searchDotsInArea(vpImage<unsigned char>& I)
1225 
1226   \param I : Image.
1227   \param wantedDot : Wanted dot passed in.
1228 
1229 */
isValid(const vpImage<unsigned char> & I,const vpDot2 & wantedDot)1230 bool vpDot2::isValid(const vpImage<unsigned char> &I, const vpDot2 &wantedDot)
1231 {
1232   double size_precision = wantedDot.getSizePrecision();
1233   double ellipsoidShape_precision = wantedDot.getEllipsoidShapePrecision();
1234 
1235   //
1236   // First, check the width, height and surface of the dot. Those parameters
1237   // must be the same.
1238   //
1239   // if (   (wantedDot.getWidth()   != 0)
1240   //  && (wantedDot.getHeight()  != 0)
1241   //  && (wantedDot.getArea() != 0) )
1242   if ((std::fabs(wantedDot.getWidth()) > std::numeric_limits<double>::epsilon()) &&
1243       (std::fabs(wantedDot.getHeight()) > std::numeric_limits<double>::epsilon()) &&
1244       (std::fabs(wantedDot.getArea()) > std::numeric_limits<double>::epsilon()))
1245   // if (size_precision!=0){
1246   {
1247     if (std::fabs(size_precision) > std::numeric_limits<double>::epsilon()) {
1248       double epsilon = 0.001;
1249 #ifdef DEBUG
1250       std::cout << "test size precision......................\n";
1251       std::cout << "wanted dot: "
1252                 << "w=" << wantedDot.getWidth() << " h=" << wantedDot.getHeight() << " s=" << wantedDot.getArea()
1253                 << " precision=" << size_precision << " epsilon=" << epsilon << std::endl;
1254       std::cout << "dot found: "
1255                 << "w=" << getWidth() << " h=" << getHeight() << " s=" << getArea() << std::endl;
1256 #endif
1257 
1258       if ((wantedDot.getWidth() * size_precision - epsilon < getWidth()) == false) {
1259         vpDEBUG_TRACE(3, "Bad width > for dot (%g, %g)", cog.get_u(), cog.get_v());
1260 #ifdef DEBUG
1261         printf("Bad width > for dot (%g, %g)\n", cog.get_u(), cog.get_v());
1262 #endif
1263         return false;
1264       }
1265 
1266       if ((getWidth() < wantedDot.getWidth() / (size_precision + epsilon)) == false) {
1267         vpDEBUG_TRACE(3, "Bad width > for dot (%g, %g)", cog.get_u(), cog.get_v());
1268 #ifdef DEBUG
1269         printf("Bad width %g > %g for dot (%g, %g)\n", getWidth(), wantedDot.getWidth() / (size_precision + epsilon),
1270                cog.get_u(), cog.get_v());
1271 #endif
1272         return false;
1273       }
1274 
1275       if ((wantedDot.getHeight() * size_precision - epsilon < getHeight()) == false) {
1276         vpDEBUG_TRACE(3, "Bad height > for dot (%g, %g)", cog.get_u(), cog.get_v());
1277 #ifdef DEBUG
1278         printf("Bad height %g > %g for dot (%g, %g)\n", wantedDot.getHeight() * size_precision - epsilon, getHeight(),
1279                cog.get_u(), cog.get_v());
1280 #endif
1281         return false;
1282       }
1283 
1284       if ((getHeight() < wantedDot.getHeight() / (size_precision + epsilon)) == false) {
1285         vpDEBUG_TRACE(3, "Bad height > for dot (%g, %g)", cog.get_u(), cog.get_v());
1286 #ifdef DEBUG
1287         printf("Bad height %g > %g for dot (%g, %g)\n", getHeight(), wantedDot.getHeight() / (size_precision + epsilon),
1288                cog.get_u(), cog.get_v());
1289 #endif
1290         return false;
1291       }
1292 
1293       if ((wantedDot.getArea() * (size_precision * size_precision) - epsilon < getArea()) == false) {
1294         vpDEBUG_TRACE(3, "Bad surface > for dot (%g, %g)", cog.get_u(), cog.get_v());
1295 #ifdef DEBUG
1296         printf("Bad surface %g > %g for dot (%g, %g)\n",
1297                wantedDot.getArea() * (size_precision * size_precision) - epsilon, getArea(), cog.get_u(), cog.get_v());
1298 #endif
1299         return false;
1300       }
1301 
1302       if ((getArea() < wantedDot.getArea() / (size_precision * size_precision + epsilon)) == false) {
1303         vpDEBUG_TRACE(3, "Bad surface > for dot (%g, %g)", cog.get_u(), cog.get_v());
1304 #ifdef DEBUG
1305         printf("Bad surface %g < %g for dot (%g, %g)\n", getArea(),
1306                wantedDot.getArea() / (size_precision * size_precision + epsilon), cog.get_u(), cog.get_v());
1307 #endif
1308         return false;
1309       }
1310     }
1311   }
1312   //
1313   // Now we can proceed to more advanced (and costy) checks.
1314   // First check there is a white (>level) elipse within dot
1315   // Then check the dot is surrounded by a black ellipse.
1316   //
1317   int nb_point_to_test = 20; // Nb points to test on inner and outside ellipsoid
1318   int nb_bad_points = 0;
1319   int nb_max_bad_points = (int)(nb_point_to_test * allowedBadPointsPercentage_);
1320   double step_angle = 2 * M_PI / nb_point_to_test;
1321 
1322   //  if (ellipsoidShape_precision != 0 && compute_moment) {
1323   if (std::fabs(ellipsoidShape_precision) > std::numeric_limits<double>::epsilon() && compute_moment) {
1324     // Chaumette, Image Moments: A General and Useful Set of Features for Visual Servoing, TRO 2004, eq 15
1325 
1326     // mu11 = m11 - m00 * xg * yg = m11 - m00 * m10/m00 * m01/m00
1327     //      = m11 - m10 * m01 / m00
1328     // mu20 = m20 - m00 * xg^2 = m20 - m00 * m10/m00 * m10/m00
1329     //      = m20 - m10^2 / m00
1330     // mu02 = m02 - m01^2 / m00
1331     // alpha = 1/2 arctan( 2 * mu11 / (mu20 - mu02) )
1332     //
1333     // a1^2 = 2 / m00 * (mu02 + mu20 + sqrt( (mu20 - mu02)^2 + 4mu11^2) )
1334     //
1335     // a2^2 = 2 / m00 * (mu02 + mu20 - sqrt( (mu20 - mu02)^2 + 4mu11^2) )
1336 
1337     // we compute parameters of the estimated ellipse
1338     double tmp1 = (m01 * m01 - m10 * m10) / m00 + (m20 - m02);
1339     double tmp2 = m11 - m10 * m01 / m00;
1340     double Sqrt = sqrt(tmp1 * tmp1 + 4 * tmp2 * tmp2);
1341     double a1 = sqrt(2 / m00 * ((m20 + m02) - (m10 * m10 + m01 * m01) / m00 + Sqrt));
1342     double a2 = sqrt(2 / m00 * ((m20 + m02) - (m10 * m10 + m01 * m01) / m00 - Sqrt));
1343     double alpha = 0.5 * atan2(2 * (m11 * m00 - m10 * m01), ((m20 - m02) * m00 - m10 * m10 + m01 * m01));
1344 
1345     // to be able to track small dots, minorize the ellipsoid radius for the
1346     // inner test
1347     a1 -= 1.0;
1348     a2 -= 1.0;
1349 
1350     double innerCoef = ellipsoidShape_precision;
1351     unsigned int u, v;
1352     double cog_u = this->cog.get_u();
1353     double cog_v = this->cog.get_v();
1354 
1355     vpImagePoint ip;
1356     nb_bad_points = 0;
1357     for (double theta = 0.; theta < 2 * M_PI; theta += step_angle) {
1358       u = (unsigned int)(cog_u + innerCoef * (a1 * cos(alpha) * cos(theta) - a2 * sin(alpha) * sin(theta)));
1359       v = (unsigned int)(cog_v + innerCoef * (a1 * sin(alpha) * cos(theta) + a2 * cos(alpha) * sin(theta)));
1360       if (!this->hasGoodLevel(I, u, v)) {
1361 // 	vpTRACE("Inner circle pixel (%d, %d) has bad level for dot (%g, %g)",
1362 // 		u, v, cog_u, cog_v);
1363 #ifdef DEBUG
1364         printf("Inner circle pixel (%u, %u) has bad level for dot (%g, %g): "
1365                "%d not in [%u, %u]\n",
1366                u, v, cog_u, cog_v, I[v][u], gray_level_min, gray_level_max);
1367 #endif
1368         // return false;
1369         nb_bad_points++;
1370       }
1371       if (graphics) {
1372         for (unsigned int t = 0; t < thickness; t++) {
1373           ip.set_u(u + t);
1374           ip.set_v(v);
1375           vpDisplay::displayPoint(I, ip, vpColor::green);
1376         }
1377       }
1378 #ifdef DEBUG
1379       vpDisplay::displayPoint(I, ip, vpColor::green);
1380       vpDisplay::flush(I);
1381 #endif
1382     }
1383     if (nb_bad_points > nb_max_bad_points) {
1384 #ifdef DEBUG
1385       printf("Inner ellipse has %d bad points. Max allowed is %d\n", nb_bad_points, nb_max_bad_points);
1386 #endif
1387       return false;
1388     }
1389     // to be able to track small dots, maximize the ellipsoid radius for the
1390     // inner test
1391     a1 += 2.0;
1392     a2 += 2.0;
1393 
1394     double outCoef = 2 - ellipsoidShape_precision; // 1.6;
1395     nb_bad_points = 0;
1396     for (double theta = 0.; theta < 2 * M_PI; theta += step_angle) {
1397       u = (unsigned int)(cog_u + outCoef * (a1 * cos(alpha) * cos(theta) - a2 * sin(alpha) * sin(theta)));
1398       v = (unsigned int)(cog_v + outCoef * (a1 * sin(alpha) * cos(theta) + a2 * cos(alpha) * sin(theta)));
1399 #ifdef DEBUG
1400       // vpDisplay::displayRectangle(I, area, vpColor::yellow);
1401       vpDisplay::displayCross(I, (int)v, (int)u, 7, vpColor::purple);
1402       vpDisplay::flush(I);
1403 #endif
1404       // If outside the area, continue
1405       if ((double)u < area.getLeft() || (double)u > area.getRight() || (double)v < area.getTop() ||
1406           (double)v > area.getBottom()) {
1407         continue;
1408       }
1409       if (!this->hasReverseLevel(I, u, v)) {
1410 // 	vpTRACE("Outside circle pixel (%d, %d) has bad level for dot (%g,
1411 // %g)", 		u, v, cog_u, cog_v);
1412 #ifdef DEBUG
1413         printf("Outside circle pixel (%u, %u) has bad level for dot (%g, "
1414                "%g): %d not in [%u, %u]\n",
1415                u, v, cog_u, cog_v, I[v][u], gray_level_min, gray_level_max);
1416 #endif
1417         nb_bad_points++;
1418         // return false;
1419       }
1420       if (graphics) {
1421         for (unsigned int t = 0; t < thickness; t++) {
1422           ip.set_u(u + t);
1423           ip.set_v(v);
1424 
1425           vpDisplay::displayPoint(I, ip, vpColor::green);
1426         }
1427       }
1428     }
1429   }
1430   if (nb_bad_points > nb_max_bad_points) {
1431 #ifdef DEBUG
1432     printf("Outside ellipse has %d bad points. Max allowed is %d\n", nb_bad_points, nb_max_bad_points);
1433 #endif
1434     return false;
1435   }
1436 
1437   return true;
1438 }
1439 
1440 /*!
1441 
1442   Check if a the pixel of coordinates (u, v) is in the image and has
1443   a good level to belong to this dot.
1444 
1445   \param I : Image.
1446   \param u : Pixel to test.
1447   \param v : Pixel to test.
1448 
1449   \return true : If the pixel of coordinates (u, v) is in the area and
1450   has a value between the min and max levels fixed by setGrayLevelMin() and
1451   setGrayLevelMax().
1452 
1453   \return false : Otherwise
1454 
1455   \sa setGrayLevelMin(), setGrayLevelMax()
1456 
1457 */
hasGoodLevel(const vpImage<unsigned char> & I,const unsigned int & u,const unsigned int & v) const1458 bool vpDot2::hasGoodLevel(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v) const
1459 {
1460   if (!isInArea(u, v))
1461     return false;
1462 
1463   if (I[v][u] >= gray_level_min && I[v][u] <= gray_level_max) {
1464     return true;
1465   } else {
1466     return false;
1467   }
1468 }
1469 
1470 /*!
1471 
1472   Check if a the pixel of coordinates (u, v) in the image has a good level to
1473   be a dark zone around the dot.
1474 
1475   \param I : Image.
1476   \param u : Pixel to test.
1477   \param v : Pixel to test.
1478 
1479   \return true if it is so, and false otherwise.
1480 
1481 */
hasReverseLevel(const vpImage<unsigned char> & I,const unsigned int & u,const unsigned int & v) const1482 bool vpDot2::hasReverseLevel(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v) const
1483 {
1484 
1485   if (!isInArea(u, v))
1486     return false;
1487 
1488   if (I[v][u] < gray_level_min || I[v][u] > gray_level_max) {
1489     return true;
1490   } else {
1491     return false;
1492   }
1493 }
1494 
1495 /*!
1496   Return a new instance of vpDot2.
1497 
1498   Should be used by child classed to return their own instance of vpDot2.
1499 
1500   \return An instance of vpDot2.
1501 
1502 */
getInstance()1503 vpDot2 *vpDot2::getInstance() { return new vpDot2(); }
1504 
1505 /*!
1506 
1507   Returns the list of Freeman chain code used to turn around the dot
1508   counterclockwise.
1509 
1510   \return List of Freeman chain list [0, ..., 7]
1511   - 0 : right
1512   - 1 : top right
1513   - 2 : top
1514   - 3 : top left
1515   - 4 : left
1516   - 5 : down left
1517   - 6 : down
1518   - 7 : down right
1519 */
getFreemanChain(std::list<unsigned int> & freeman_chain) const1520 void vpDot2::getFreemanChain(std::list<unsigned int> &freeman_chain) const { freeman_chain = direction_list; }
1521 
1522 /******************************************************************************
1523  *
1524  *      PRIVATE METHODS
1525  *
1526  ******************************************************************************/
1527 
1528 /*!
1529 
1530   Compute all the parameters of the dot (center, width, height, surface,
1531   inertia moments...).
1532 
1533   This is done the following way:
1534 
1535   - First, we check the point (_u, _v) passed in has the right level in the
1536     image
1537 
1538   - Then we cross the tracked entity from left to right until we reach it's
1539     border.
1540 
1541   - We follow this border until we come back to the first point or we get to
1542     border of the image. Each time we update variables used to compute the
1543     dot parameters
1544 
1545   \param I : The image we are working with.
1546 
1547   \param _u : A pixel coordinate inside the dot.
1548 
1549   \param _v : A pixel coordinate inside the dot.
1550 
1551   \return false : If a dot can't be found around pixel coordinates given as
1552   parameter
1553 
1554   \return true : If a dot was found.
1555 
1556   \sa getFirstBorder_u(), getFirstBorder_v()
1557 
1558 */
computeParameters(const vpImage<unsigned char> & I,const double & _u,const double & _v)1559 bool vpDot2::computeParameters(const vpImage<unsigned char> &I, const double &_u, const double &_v)
1560 {
1561   direction_list.clear();
1562   ip_edges_list.clear();
1563 
1564   double est_u = _u; // estimated
1565   double est_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((unsigned int)est_u, (unsigned int)est_v)) {
1582     vpDEBUG_TRACE(3,
1583                   "Initial pixel coordinates (%d, %d) for dot tracking are "
1584                   "not in the area",
1585                   (int)est_u, (int)est_v);
1586     return false;
1587   }
1588 
1589   bbox_u_min = (int)I.getWidth();
1590   bbox_u_max = 0;
1591   bbox_v_min = (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, (unsigned int)est_u, (unsigned int)est_v)) {
1597     vpDEBUG_TRACE(3, "Can't find a dot from pixel (%d, %d) coordinates", (int)est_u, (int)est_v);
1598     return false;
1599   }
1600 
1601   // find the border
1602 
1603   if (!findFirstBorder(I, (unsigned int)est_u, (unsigned int)est_v, this->firstBorder_u, this->firstBorder_v)) {
1604 
1605     vpDEBUG_TRACE(3, "Can't find first border (%d, %d) coordinates", (int)est_u, (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 = (int)this->firstBorder_u;
1631   int border_v = (int)this->firstBorder_v;
1632 
1633   //   vpTRACE("-----------------------------------------");
1634   //   vpTRACE("first border_u: %d border_v: %d dir: %d",
1635   // 	this->firstBorder_u, this->firstBorder_v,firstDir);
1636   int du, dv;
1637   float dS, dMu, dMv, dMuv, dMu2, dMv2;
1638   m00 = 0.0;
1639   m10 = 0.0;
1640   m01 = 0.0;
1641   m11 = 0.0;
1642   m20 = 0.0;
1643   m02 = 0.0;
1644   // while we didn't come back to the first point, follow the border
1645   do {
1646     // if it was asked, show the border
1647     if (graphics) {
1648       for (int t = 0; t < (int)thickness; t++) {
1649         ip.set_u(border_u + t);
1650         ip.set_v(border_v);
1651 
1652         vpDisplay::displayPoint(I, ip, vpColor::red);
1653       }
1654       // vpDisplay::flush(I);
1655     }
1656 #ifdef DEBUG
1657     vpDisplay::displayPoint(I, border_v, border_u, vpColor::red);
1658     vpDisplay::flush(I);
1659 #endif
1660     // Determine the increments for the parameters
1661     computeFreemanParameters(border_u, border_v, dir, du, dv,
1662                              dS,                // surface
1663                              dMu, dMv,          // first order moments
1664                              dMuv, dMu2, dMv2); // second order moment
1665 
1666     // Update the parameters
1667     border_u += du; // Next position on the border
1668     border_v += dv;
1669     m00 += dS;  // enclosed area
1670     m10 += dMu; // First order moment along v axis
1671     m01 += dMv; // First order moment along u axis
1672     if (compute_moment) {
1673       m11 += dMuv; // Second order moment
1674       m20 += dMu2; // Second order moment along v axis
1675       m02 += dMv2; // Second order moment along u axis
1676     }
1677     // if we are now out of the image, return an error tracking
1678     if (!isInArea((unsigned int)border_u, (unsigned int)border_v)) {
1679 
1680       vpDEBUG_TRACE(3, "Dot (%d, %d) is not in the area", border_u, border_v);
1681       // Can Occur on a single pixel dot located on the top border
1682       return false;
1683     }
1684 
1685     // store the new direction and dot border coordinates.
1686 
1687     direction_list.push_back(dir);
1688 
1689     ip.set_u(border_u);
1690     ip.set_v(border_v);
1691     ip_edges_list.push_back(ip);
1692 
1693     // vpDisplay::getClick(I);
1694 
1695     // update the extreme point of the dot.
1696     if (border_v < bbox_v_min)
1697       bbox_v_min = border_v;
1698     if (border_v > bbox_v_max)
1699       bbox_v_max = border_v;
1700     if (border_u < bbox_u_min)
1701       bbox_u_min = border_u;
1702     if (border_u > bbox_u_max)
1703       bbox_u_max = border_u;
1704 
1705     // move around the tracked entity by following the border.
1706     if (computeFreemanChainElement(I, (unsigned int)border_u, (unsigned int)border_v, dir) == false) {
1707       vpDEBUG_TRACE(3, "Can't compute Freeman chain for dot (%d, %d)", border_u, border_v);
1708       return false;
1709     }
1710 
1711     //     vpTRACE("border_u: %d border_v: %d dir: %d", border_u, border_v,
1712     //     dir);
1713 
1714   } while ((getFirstBorder_u() != (unsigned int)border_u || getFirstBorder_v() != (unsigned int)border_v ||
1715             firstDir != dir) &&
1716            isInArea((unsigned int)border_u, (unsigned int)border_v));
1717 
1718 #ifdef VP_DEBUG
1719 #if VP_DEBUG_MODE == 3
1720   vpDisplay::flush(I);
1721 #endif
1722 #endif
1723 
1724   // if the surface is one or zero , the center of gravity wasn't properly
1725   // detected. Return an error tracking.
1726   // if( m00 == 0 || m00 == 1 )
1727   if (std::fabs(m00) <= std::numeric_limits<double>::epsilon() ||
1728       std::fabs(m00 - 1.) <= vpMath::maximum(std::fabs(m00), 1.) * std::numeric_limits<double>::epsilon()) {
1729     vpDEBUG_TRACE(3, "The center of gravity of the dot wasn't properly detected");
1730     return false;
1731   } else // compute the center
1732   {
1733     // this magic formula gives the coordinates of the center of gravity
1734     double tmpCenter_u = m10 / m00;
1735     double tmpCenter_v = m01 / m00;
1736 
1737     // Updates the second order centered moments
1738     if (compute_moment) {
1739       mu11 = m11 - tmpCenter_u * m01;
1740       mu02 = m02 - tmpCenter_v * m01;
1741       mu20 = m20 - tmpCenter_u * m10;
1742     }
1743 
1744     // check the center is in the image... never know...
1745     //     if( !hasGoodLevel( I, (unsigned int)tmpCenter_u,
1746     // 		       (unsigned int)tmpCenter_v ) )
1747     //     {
1748     //       vpDEBUG_TRACE(3, "The center of gravity of the dot (%g, %g) has
1749     //       not a good in level", tmpCenter_u, tmpCenter_v); return false;
1750     //     }
1751 
1752     cog.set_u(tmpCenter_u);
1753     cog.set_v(tmpCenter_v);
1754   }
1755 
1756   width = bbox_u_max - bbox_u_min + 1;
1757   height = bbox_v_max - bbox_v_min + 1;
1758   surface = m00;
1759 
1760   computeMeanGrayLevel(I);
1761   return true;
1762 }
1763 
1764 /*!
1765   Find the starting point on a dot border from an other point in the dot.
1766   the dot border is computed from this point.
1767 
1768   \param I : Image.
1769   \param u : The row coordinate of a pixel in the dot.
1770   \param v : The column coordinate of a pixel in the dot.
1771   \param border_u : The row coordinate of the found starting point.
1772   \param border_v : The column coordinate of the found starting point.
1773 
1774   \return false if the width of this dot was initialised and we already
1775 crossed the dot on more than the max possible width. Return true if success.
1776 
1777   \sa computeParameters()
1778 */
findFirstBorder(const vpImage<unsigned char> & I,const unsigned int & u,const unsigned int & v,unsigned int & border_u,unsigned int & border_v)1779 bool vpDot2::findFirstBorder(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v,
1780                              unsigned int &border_u, unsigned int &border_v)
1781 {
1782   // find the border
1783 
1784   // NOTE:
1785   // from here we use int and not double. This is because we don't have
1786   // rounding problems and it's actually more a trouble than smth else to
1787   // work with double when navigating around the dot.
1788   border_u = u;
1789   border_v = v;
1790   double epsilon = 0.001;
1791 
1792 #ifdef DEBUG
1793   std::cout << "gray level: " << gray_level_min << " " << gray_level_max << std::endl;
1794 #endif
1795   while (hasGoodLevel(I, border_u + 1, border_v) && border_u < area.getRight() /*I.getWidth()*/) {
1796     // if the width of this dot was initialised and we already crossed the dot
1797     // on more than the max possible width, no need to continue, return an
1798     // error tracking
1799     if (getWidth() > 0 && (border_u - u) > getWidth() / (getMaxSizeSearchDistancePrecision() + epsilon)) {
1800       vpDEBUG_TRACE(3,
1801                     "The found dot (%d, %d, %d) has a greater width than the "
1802                     "required one",
1803                     u, v, border_u);
1804       return false;
1805     }
1806 #ifdef DEBUG
1807     vpDisplay::displayPoint(I, (int)border_v, (int)border_u + 1, vpColor::green);
1808     vpDisplay::flush(I);
1809 #endif
1810 
1811     border_u++;
1812   }
1813   return true;
1814 }
1815 
1816 /*!
1817 
1818   Considering a pixel (u, v) compute the next element of the Freeman chain
1819   code.
1820 
1821   According to the gray level of pixel (u, v) and his eight neighbors
1822   determine the next element of the chain in order to turn around the dot
1823   counterclockwise.
1824 
1825   \param I : The image we are working with.
1826   \param v : The row coordinate of a pixel on a border.
1827   \param u : The column coordinate of the pixel on a border.
1828   \param element : The next freeman element chain code (0, 1, 2, 3, 4, 5, 6,
1829   7) with 0 for right moving, 2 for down, 4 for left and 6 for up moving.
1830 
1831   \return false if an element cannot be found. Occurs for example with an area
1832   constituted by a single pixel. Return true if success.
1833 */
computeFreemanChainElement(const vpImage<unsigned char> & I,const unsigned int & u,const unsigned int & v,unsigned int & element)1834 bool vpDot2::computeFreemanChainElement(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v,
1835                                         unsigned int &element)
1836 {
1837 
1838   if (hasGoodLevel(I, u, v)) {
1839     unsigned int _u = u;
1840     unsigned int _v = v;
1841     // get the point on the right of the point passed in
1842     updateFreemanPosition(_u, _v, (element + 2) % 8);
1843     if (hasGoodLevel(I, _u, _v)) {
1844       element = (element + 2) % 8; // turn right
1845     } else {
1846       unsigned int _u1 = u;
1847       unsigned int _v1 = v;
1848       updateFreemanPosition(_u1, _v1, (element + 1) % 8);
1849 
1850       if (hasGoodLevel(I, _u1, _v1)) {
1851         element = (element + 1) % 8; // turn diag right
1852       } else {
1853         unsigned int _u2 = u;
1854         unsigned int _v2 = v;
1855         updateFreemanPosition(_u2, _v2, element); // same direction
1856 
1857         if (hasGoodLevel(I, _u2, _v2)) {
1858           // element = element;      // keep same dir
1859         } else {
1860           unsigned int _u3 = u;
1861           unsigned int _v3 = v;
1862           updateFreemanPosition(_u3, _v3, (element + 7) % 8); // diag left
1863 
1864           if (hasGoodLevel(I, _u3, _v3)) {
1865             element = (element + 7) % 8; // turn diag left
1866           } else {
1867             unsigned int _u4 = u;
1868             unsigned int _v4 = v;
1869             updateFreemanPosition(_u4, _v4, (element + 6) % 8); // left
1870 
1871             if (hasGoodLevel(I, _u4, _v4)) {
1872               element = (element + 6) % 8; // turn left
1873             } else {
1874               unsigned int _u5 = u;
1875               unsigned int _v5 = v;
1876               updateFreemanPosition(_u5, _v5, (element + 5) % 8); // left
1877 
1878               if (hasGoodLevel(I, _u5, _v5)) {
1879                 element = (element + 5) % 8; // turn diag down
1880               } else {
1881                 unsigned int _u6 = u;
1882                 unsigned int _v6 = v;
1883                 updateFreemanPosition(_u6, _v6, (element + 4) % 8); // left
1884 
1885                 if (hasGoodLevel(I, _u6, _v6)) {
1886                   element = (element + 4) % 8; // turn down
1887                 } else {
1888                   unsigned int _u7 = u;
1889                   unsigned int _v7 = v;
1890                   updateFreemanPosition(_u7, _v7, (element + 3) % 8); // diag
1891 
1892                   if (hasGoodLevel(I, _u7, _v7)) {
1893                     element = (element + 3) % 8; // turn diag right down
1894                   } else {
1895                     // No neighbor with a good level
1896                     //
1897                     return false;
1898                   }
1899                 }
1900               }
1901             }
1902           }
1903         }
1904       }
1905     }
1906   }
1907 
1908   else {
1909     return false;
1910   }
1911 
1912   return true;
1913 }
1914 
1915 /*!
1916 
1917   Given the previous position of a pixel (u_p, v_p) on the dot border and the
1918   direction to reach the next pixel on the border, compute Freeman parameters.
1919 
1920   \param u_p : Previous value of the row coordinate of a pixel on a border.
1921   \param v_p : Previous value of the column coordinate of a pixel on a border.
1922   \param du : Increment to go from previous to next pixel on the dot border.
1923   \param dv : Increment to go from previous to next pixel on the dot border.
1924 
1925   \param dS : Enclosed area increases. Cumulated values of dS gives m00.
1926 
1927   \param dMu : First order moment along v axis increases. Cumulated values of
1928   dMu gives m10.
1929 
1930   \param dMv : First order moment along u axis increases. Cumulated values of
1931   dMv gives m01.
1932 
1933   \param dMuv : Moment increases. Cumulated values of dMuv gives m11.
1934 
1935   \param dMu2 : Second order moment along v axis increases. Cumulated values
1936   of dMu2 gives m20.
1937 
1938   \param dMv2 : Second order moment along u axis increases. Cumulated values
1939   of dMv2 gives m02.
1940 
1941   Considering the previous coordinates (u_p, v_p) of a pixel on a border, the
1942   next coordinates (u, v) are given by: u = u_p + du and v = v_p + dv
1943 
1944 
1945 */
computeFreemanParameters(const int & u_p,const int & v_p,unsigned int & element,int & du,int & dv,float & dS,float & dMu,float & dMv,float & dMuv,float & dMu2,float & dMv2)1946 void vpDot2::computeFreemanParameters(const int &u_p, const int &v_p, unsigned int &element, int &du, int &dv,
1947                                       float &dS, float &dMu, float &dMv, float &dMuv, float &dMu2, float &dMv2)
1948 {
1949   du = 0;
1950   dv = 0;
1951   dMuv = 0;
1952   dMu2 = 0;
1953   dMv2 = 0;
1954 
1955   /*
1956            3  2  1
1957             \ | /
1958              \|/
1959          4 ------- 0
1960              /|\
1961             / | \
1962            5  6  7
1963   */
1964   switch (element) {
1965   case 0: // go right
1966     du = 1;
1967     dS = (float)v_p;
1968     dMu = 0.0;
1969     dMv = (float)(0.5 * v_p * v_p);
1970     if (compute_moment) {
1971       dMuv = (float)(0.25 * v_p * v_p * (2 * u_p + 1));
1972       dMu2 = 0;
1973       dMv2 = (float)(1.0 / 3. * v_p * v_p * v_p);
1974     }
1975     break;
1976 
1977   case 1: // go right top
1978     du = 1;
1979     dv = 1;
1980     dS = (float)(v_p + 0.5);
1981     dMu = -(float)(0.5 * u_p * (u_p + 1) + 1.0 / 6.0);
1982     dMv = (float)(0.5 * v_p * (v_p + 1) + 1.0 / 6.0);
1983     if (compute_moment) {
1984       float half_u_p = (float)(0.5 * u_p);
1985       dMuv = (float)(v_p * v_p * (0.25 + half_u_p) + v_p * (1. / 3. + half_u_p) + 1. / 6. * u_p + 0.125);
1986       dMu2 = (float)(-1. / 3. * u_p * (u_p * u_p + 1.5 * u_p + 1.) - 1. / 12.0);
1987       dMv2 = (float)(1. / 3. * v_p * (v_p * v_p + 1.5 * v_p + 1.) + 1. / 12.0);
1988     }
1989     break;
1990 
1991   case 2: // go top
1992     dv = 1;
1993     dS = 0.0;
1994     dMu = (float)(-0.5 * u_p * u_p);
1995     dMv = 0.0;
1996     if (compute_moment) {
1997       dMuv = 0;
1998       dMu2 = (float)(-1.0 / 3. * u_p * u_p * u_p);
1999       dMv2 = 0;
2000     }
2001     break;
2002 
2003   case 3:
2004     du = -1;
2005     dv = 1;
2006     dS = (float)(-v_p - 0.5);
2007     dMu = -(float)(0.5 * u_p * (u_p - 1) + 1.0 / 6.0);
2008     dMv = -(float)(0.5 * v_p * (v_p + 1) + 1.0 / 6.0);
2009     if (compute_moment) {
2010       float half_u_p = (float)(0.5 * u_p);
2011       dMuv = (float)(v_p * v_p * (0.25 - half_u_p) + v_p * (1. / 3. - half_u_p) - 1. / 6. * u_p + 0.125);
2012       dMu2 = (float)(-1. / 3. * u_p * (u_p * u_p - 1.5 * u_p + 1.) - 1. / 12.0);
2013       dMv2 = (float)(-1. / 3. * v_p * (v_p * v_p + 1.5 * v_p + 1.) - 1. / 12.0);
2014     }
2015     break;
2016 
2017   case 4:
2018     du = -1;
2019     dS = (float)(-v_p);
2020     dMv = (float)(-0.5 * v_p * v_p);
2021     dMu = 0.0;
2022     if (compute_moment) {
2023       dMuv = (float)(-0.25 * v_p * v_p * (2 * u_p - 1));
2024       dMu2 = 0;
2025       dMv2 = (float)(-1.0 / 3. * v_p * v_p * v_p);
2026     }
2027     break;
2028 
2029   case 5:
2030     du = -1;
2031     dv = -1;
2032     dS = (float)(-v_p + 0.5);
2033     dMu = (float)(0.5 * u_p * (u_p - 1) + 1.0 / 6.0);
2034     dMv = (float)(-(0.5 * v_p * (v_p - 1) + 1.0 / 6.0));
2035     if (compute_moment) {
2036       float half_u_p = (float)(0.5 * u_p);
2037       dMuv = (float)(v_p * v_p * (0.25 - half_u_p) - v_p * (1. / 3. - half_u_p) - 1. / 6. * u_p + 0.125);
2038       dMu2 = (float)(1. / 3. * u_p * (u_p * u_p - 1.5 * u_p + 1.) - 1. / 12.0);
2039       dMv2 = (float)(-1. / 3. * v_p * (v_p * v_p - 1.5 * v_p + 1.) - 1. / 12.0);
2040     }
2041     break;
2042 
2043   case 6:
2044     dv = -1;
2045     dS = 0.0;
2046     dMu = (float)(0.5 * u_p * u_p);
2047     dMv = 0.0;
2048     if (compute_moment) {
2049       dMuv = 0;
2050       dMu2 = (float)(1.0 / 3. * u_p * u_p * u_p);
2051       dMv2 = 0;
2052     }
2053     break;
2054 
2055   case 7:
2056     du = 1;
2057     dv = -1;
2058     dS = (float)(v_p - 0.5);
2059     dMu = (float)(0.5 * u_p * (u_p + 1) + 1.0 / 6.0);
2060     dMv = (float)(0.5 * v_p * (v_p - 1) + 1.0 / 6.0);
2061     if (compute_moment) {
2062       float half_u_p = (float)(0.5 * u_p);
2063       dMuv = (float)(v_p * v_p * (0.25 + half_u_p) - v_p * (1. / 3. + half_u_p) + 1. / 6. * u_p + 0.125);
2064       dMu2 = (float)(1. / 3. * u_p * (u_p * u_p + 1.5 * u_p + 1.) + 1. / 12.0);
2065       dMv2 = (float)(1. / 3. * v_p * (v_p * v_p - 1.5 * v_p + 1.) - 1. / 12.0);
2066     }
2067     break;
2068   }
2069 }
2070 
2071 /*!
2072 
2073   From a pixel coordinate and a direction, update the pixel coordinates after
2074   moving forward.
2075 
2076   \param v : The row coordinate of the pixel, updated by this method.
2077 
2078   \param u : The column coordinate of the pixel, updated by this method.
2079 
2080   \param dir : The direction in the image, 0=right, 1, 2=down, 3, 4=left, 5,
2081   6=up and 7.
2082 
2083 */
updateFreemanPosition(unsigned int & u,unsigned int & v,const unsigned int & dir)2084 void vpDot2::updateFreemanPosition(unsigned int &u, unsigned int &v, const unsigned int &dir)
2085 {
2086   switch (dir) {
2087   case 0:
2088     u += 1;
2089     break;
2090   case 1:
2091     u += 1;
2092     v += 1;
2093     break;
2094   case 2:
2095     v += 1;
2096     break;
2097   case 3:
2098     u -= 1;
2099     v += 1;
2100     break;
2101   case 4:
2102     u -= 1;
2103     break;
2104   case 5:
2105     u -= 1;
2106     v -= 1;
2107     break;
2108   case 6:
2109     v -= 1;
2110     break;
2111   case 7:
2112     u += 1;
2113     v -= 1;
2114     break;
2115   }
2116 }
2117 
2118 /*!
2119 
2120   Test if a pixel is in the image. Points of the border are not
2121   considered to be in the image.  Call the isInImage( vpImage<unsigned
2122   char> &I, const vpImagePoint &) method.
2123 
2124   \param I : The image.
2125 
2126   \return true if the pixel of coordinates (posI, posJ) is in the image and
2127   false otherwise.
2128 */
isInImage(const vpImage<unsigned char> & I) const2129 bool vpDot2::isInImage(const vpImage<unsigned char> &I) const { return isInImage(I, cog); }
2130 
2131 /*!
2132 
2133   Test if a pixel is in the image. Points of the border are not considered to
2134   be in the image.
2135 
2136   \param I : The image.
2137   \param ip : An image point.
2138 
2139   \return true if the image point \e ip is in the image and false
2140   otherwise.
2141 */
isInImage(const vpImage<unsigned char> & I,const vpImagePoint & ip) const2142 bool vpDot2::isInImage(const vpImage<unsigned char> &I, const vpImagePoint &ip) const
2143 {
2144   unsigned int h = I.getHeight();
2145   unsigned int w = I.getWidth();
2146   double u = ip.get_u();
2147   double v = ip.get_v();
2148 
2149   if (u < 0 || u >= w)
2150     return false;
2151   if (v < 0 || v >= h)
2152     return false;
2153   return true;
2154 }
2155 
2156 /*!
2157 
2158   Test if a pixel is in a region of interest. Points of the border are not
2159   considered to be in the area.
2160 
2161   \param u : The column coordinate of the pixel.
2162   \param v : The row coordinate of the pixel .
2163 
2164   \return true if the pixel of coordinates (u, v) is in the image and false
2165   otherwise.
2166 */
isInArea(const unsigned int & u,const unsigned int & v) const2167 bool vpDot2::isInArea(const unsigned int &u, const unsigned int &v) const
2168 {
2169   unsigned int area_u_min = (unsigned int)area.getLeft();
2170   unsigned int area_u_max = (unsigned int)area.getRight();
2171   unsigned int area_v_min = (unsigned int)area.getTop();
2172   unsigned int area_v_max = (unsigned int)area.getBottom();
2173 
2174   if (u < area_u_min || u > area_u_max)
2175     return false;
2176   if (v < area_v_min || v > area_v_max)
2177     return false;
2178   return true;
2179 }
2180 
2181 /*!
2182 
2183   Get the search grid size used to found a dot in a region of interest. This
2184   grid is used to parse only some pixels of the search area.
2185 
2186   \param gridWidth : Number of pixels between to vertical lines of the grid
2187 
2188   \param gridHeight : Number of pixels between to horizontal lines of the grid
2189 
2190 
2191 */
getGridSize(unsigned int & gridWidth,unsigned int & gridHeight)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 = (unsigned int)(getWidth() * getMaxSizeSearchDistancePrecision() / sqrt(2.));
2200   gridHeight = (unsigned int)(getHeight() * getMaxSizeSearchDistancePrecision() / sqrt(2.0));
2201 
2202   if (gridWidth == 0)
2203     gridWidth = 1;
2204   if (gridHeight == 0)
2205     gridHeight = 1;
2206 }
2207 
2208 /*!
2209 
2210   Compute an approximation of  mean gray level of the dot.
2211   We compute it by searching the mean of vertical and diagonal points
2212   which gray is between min and max gray level.
2213 
2214   \param I: The image.
2215 
2216   \return the mean gray level
2217 
2218 
2219 */
computeMeanGrayLevel(const vpImage<unsigned char> & I)2220 void vpDot2::computeMeanGrayLevel(const vpImage<unsigned char> &I)
2221 {
2222   int cog_u = (int)cog.get_u();
2223   int cog_v = (int)cog.get_v();
2224 
2225   unsigned int sum_value = 0;
2226   unsigned int nb_pixels = 0;
2227 
2228   for (unsigned int i = (unsigned int)this->bbox_u_min; i <= (unsigned int)this->bbox_u_max; i++) {
2229     unsigned int pixel_gray = (unsigned int)I[(unsigned int)cog_v][i];
2230     if (pixel_gray >= getGrayLevelMin() && pixel_gray <= getGrayLevelMax()) {
2231       sum_value += pixel_gray;
2232       nb_pixels++;
2233     }
2234   }
2235   for (unsigned int i = (unsigned int)this->bbox_v_min; i <= (unsigned int)this->bbox_v_max; i++) {
2236     unsigned char pixel_gray = I[i][(unsigned int)cog_u];
2237     if (pixel_gray >= getGrayLevelMin() && pixel_gray <= getGrayLevelMax()) {
2238       sum_value += pixel_gray;
2239       nb_pixels++;
2240     }
2241   }
2242   if (nb_pixels < 10) { // could be good to choose the min nb points from area of dot
2243     // add diagonals points to have enough point
2244     int imin, imax;
2245     if ((cog_u - bbox_u_min) > (cog_v - bbox_v_min)) {
2246       imin = cog_v - bbox_v_min;
2247     } else {
2248       imin = cog_u - bbox_u_min;
2249     }
2250     if ((bbox_u_max - cog_u) > (bbox_v_max - cog_v)) {
2251       imax = bbox_v_max - cog_v;
2252     } else {
2253       imax = bbox_u_max - cog_u;
2254     }
2255     for (int i = -imin; i <= imax; i++) {
2256       unsigned int pixel_gray = (unsigned int)I[(unsigned int)(cog_v + i)][(unsigned int)(cog_u + i)];
2257       if (pixel_gray >= getGrayLevelMin() && pixel_gray <= getGrayLevelMax()) {
2258         sum_value += pixel_gray;
2259         nb_pixels++;
2260       }
2261     }
2262 
2263     if ((cog_u - bbox_u_min) > (bbox_v_max - cog_v)) {
2264       imin = bbox_v_max - cog_v;
2265     } else {
2266       imin = cog_u - bbox_u_min;
2267     }
2268     if ((bbox_u_max - cog_u) > (cog_v - bbox_v_min)) {
2269       imax = cog_v - bbox_v_min;
2270     } else {
2271       imax = bbox_u_max - cog_u;
2272     }
2273 
2274     for (int i = -imin; i <= imax; i++) {
2275       unsigned char pixel_gray = I[(unsigned int)(cog_v - i)][(unsigned int)(cog_u + i)];
2276       if (pixel_gray >= getGrayLevelMin() && pixel_gray <= getGrayLevelMax()) {
2277         sum_value += pixel_gray;
2278         nb_pixels++;
2279       }
2280     }
2281   }
2282 
2283   if (nb_pixels == 0) {
2284     // should never happen
2285     throw(vpTrackingException(vpTrackingException::notEnoughPointError, "No point was found"));
2286   } else {
2287     mean_gray_level = sum_value / nb_pixels;
2288   }
2289 }
2290 
2291 /*!
2292   Define a number of dots from a file.
2293   If the file does not exist, define it by clicking an image, the dots are
2294   then saved into the file.
2295 
2296   If the dots from the file cannot be tracked in the image, will ask to click
2297   them.
2298 
2299         \param dot : dot2 array
2300         \param n : number of dots, array dimension
2301         \param dotFile : path for the file
2302         \param I : image
2303         \param col : color to print the dots (default Blue)
2304         \param trackDot : if true, tracks the dots in the image, if false
2305   simply loads the coordinates (default true)
2306 
2307     \return an nx2 matrix with the coordinates of the dots
2308 */
defineDots(vpDot2 dot[],const unsigned int & n,const std::string & dotFile,vpImage<unsigned char> & I,vpColor col,bool trackDot)2309 vpMatrix vpDot2::defineDots(vpDot2 dot[], const unsigned int &n, const std::string &dotFile, vpImage<unsigned char> &I,
2310                             vpColor col, bool trackDot)
2311 {
2312   vpMatrix Cogs(n, 2);
2313   vpImagePoint cog;
2314   unsigned int i;
2315   bool fromFile = vpIoTools::checkFilename(dotFile.c_str());
2316   if (fromFile) {
2317     vpMatrix::loadMatrix(dotFile, Cogs);
2318     std::cout << Cogs.getRows() << " dots loaded from file " << dotFile << std::endl;
2319   }
2320 
2321   // test number of cogs in file
2322   if (Cogs.getRows() < n) {
2323     std::cout << "Dot file has a wrong number of dots : redefining them" << std::endl;
2324     fromFile = false;
2325   }
2326 
2327   // read from file and tracks the dots
2328   if (fromFile) {
2329     try {
2330       for (i = 0; i < n; ++i) {
2331         cog.set_uv(Cogs[i][0], Cogs[i][1]);
2332         dot[i].setGraphics(true);
2333         dot[i].setCog(cog);
2334         if (trackDot) {
2335           dot[i].initTracking(I, cog);
2336           dot[i].track(I);
2337           vpDisplay::displayCross(I, cog, 10, col);
2338         }
2339       }
2340     } catch (...) {
2341       std::cout << "Cannot track dots from file" << std::endl;
2342       fromFile = false;
2343     }
2344     vpDisplay::flush(I);
2345 
2346     // check that dots are far away ones from the other
2347     for (i = 0; i < n && fromFile; ++i) {
2348       double d = sqrt(vpMath::sqr(dot[i].getHeight()) + vpMath::sqr(dot[i].getWidth()));
2349       for (unsigned int j = 0; j < n && fromFile; ++j)
2350         if (j != i)
2351           if (dot[i].getDistance(dot[j]) < d) {
2352             fromFile = false;
2353             std::cout << "Dots from file seem incoherent" << std::endl;
2354           }
2355     }
2356   }
2357 
2358   if (!fromFile) {
2359     vpDisplay::display(I);
2360     vpDisplay::flush(I);
2361 
2362     std::cout << "Click on the " << n << " dots clockwise starting from upper/left dot..." << std::endl;
2363     for (i = 0; i < n; i++) {
2364       if (trackDot) {
2365         dot[i].setGraphics(true);
2366         dot[i].initTracking(I);
2367         cog = dot[i].getCog();
2368       } else {
2369         vpDisplay::getClick(I, cog);
2370         dot[i].setCog(cog);
2371       }
2372       Cogs[i][0] = cog.get_u();
2373       Cogs[i][1] = cog.get_v();
2374       vpDisplay::displayCross(I, cog, 10, col);
2375       vpDisplay::flush(I);
2376     }
2377   }
2378 
2379   if (!fromFile && (dotFile != "")) {
2380     vpMatrix::saveMatrix(dotFile, Cogs);
2381     std::cout << Cogs.getRows() << " dots written to file " << dotFile << std::endl;
2382   }
2383 
2384   // back to non graphic mode
2385   for (i = 0; i < n; ++i)
2386     dot[i].setGraphics(false);
2387 
2388   return Cogs;
2389 }
2390 
2391 /*!
2392   Tracks a number of dots in an image and displays their trajectories
2393 
2394   \param dot : dot2 array
2395 
2396   \param n : number of dots, array dimension
2397 
2398   \param I : image
2399 
2400   \param cogs : vector of vpImagePoint that will be updated with the new
2401   dots, will be displayed in green
2402 
2403   \param cogStar (optional) : array of
2404   vpImagePoint indicating the desired position (default NULL), will be
2405   displayed in red
2406 */
trackAndDisplay(vpDot2 dot[],const unsigned int & n,vpImage<unsigned char> & I,std::vector<vpImagePoint> & cogs,vpImagePoint * cogStar)2407 void vpDot2::trackAndDisplay(vpDot2 dot[], const unsigned int &n, vpImage<unsigned char> &I,
2408                              std::vector<vpImagePoint> &cogs, vpImagePoint *cogStar)
2409 {
2410   unsigned int i;
2411   // tracking
2412   for (i = 0; i < n; ++i) {
2413     dot[i].track(I);
2414     cogs.push_back(dot[i].getCog());
2415   }
2416   // trajectories
2417   for (i = n; i < cogs.size(); ++i)
2418     vpDisplay::displayCircle(I, cogs[i], 4, vpColor::green, true);
2419   // initial position
2420   for (i = 0; i < n; ++i)
2421     vpDisplay::displayCircle(I, cogs[i], 4, vpColor::blue, true);
2422   // if exists, desired position
2423   if (cogStar != NULL)
2424     for (i = 0; i < n; ++i) {
2425       vpDisplay::displayDotLine(I, cogStar[i], dot[i].getCog(), vpColor::red);
2426       vpDisplay::displayCircle(I, cogStar[i], 4, vpColor::red, true);
2427     }
2428   vpDisplay::flush(I);
2429 }
2430 
2431 /*!
2432 
2433   Display the dot center of gravity and its list of edges.
2434 
2435   \param I : The image used as background.
2436 
2437   \param cog : The center of gravity.
2438 
2439   \param edges_list : The list of edges;
2440 
2441   \param color : Color used to display the dot.
2442 
2443   \param thickness : Thickness of the dot.
2444 */
display(const vpImage<unsigned char> & I,const vpImagePoint & cog,const std::list<vpImagePoint> & edges_list,vpColor color,unsigned int thickness)2445 void vpDot2::display(const vpImage<unsigned char> &I, const vpImagePoint &cog,
2446                      const std::list<vpImagePoint> &edges_list, vpColor color, unsigned int thickness)
2447 {
2448   vpDisplay::displayCross(I, cog, 3 * thickness + 8, color, thickness);
2449   std::list<vpImagePoint>::const_iterator it;
2450 
2451   for (it = edges_list.begin(); it != edges_list.end(); ++it) {
2452     vpDisplay::displayPoint(I, *it, color);
2453   }
2454 }
2455 
2456 /*!
2457 
2458   Display the dot center of gravity and its list of edges.
2459 
2460   \param I : The image used as background.
2461 
2462   \param cog : The center of gravity.
2463 
2464   \param edges_list : The list of edges;
2465 
2466   \param color : Color used to display the dot.
2467 
2468   \param thickness : Thickness of the dot.
2469 */
display(const vpImage<vpRGBa> & I,const vpImagePoint & cog,const std::list<vpImagePoint> & edges_list,vpColor color,unsigned int thickness)2470 void vpDot2::display(const vpImage<vpRGBa> &I, const vpImagePoint &cog, const std::list<vpImagePoint> &edges_list,
2471                      vpColor color, unsigned int thickness)
2472 {
2473   vpDisplay::displayCross(I, cog, 3 * thickness + 8, color, thickness);
2474   std::list<vpImagePoint>::const_iterator it;
2475 
2476   for (it = edges_list.begin(); it != edges_list.end(); ++it) {
2477     vpDisplay::displayPoint(I, *it, color);
2478   }
2479 }
2480 
2481 /*!
2482   Writes the dot center of gravity coordinates in the frame (i,j) (For more
2483   details about the orientation of the frame see the vpImagePoint
2484   documentation) to the stream \e os, and returns a reference to the stream.
2485 */
operator <<(std::ostream & os,vpDot2 & d)2486 VISP_EXPORT std::ostream &operator<<(std::ostream &os, vpDot2 &d) { return (os << "(" << d.getCog() << ")"); }
2487