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  * Eric Marchand
36  * Fabien Spindler
37  *
38  *****************************************************************************/
39 
40 /*!
41   \file vpDot.h
42   \brief Track a white dot
43 */
44 
45 #ifndef vpDot_hh
46 #define vpDot_hh
47 
48 #include <visp3/core/vpConfig.h>
49 #include <visp3/core/vpDisplay.h>
50 #include <visp3/core/vpImage.h>
51 #include <visp3/core/vpImagePoint.h>
52 #include <visp3/core/vpPolygon.h>
53 #include <visp3/core/vpRect.h>
54 #include <visp3/core/vpTracker.h>
55 
56 #include <fstream>
57 #include <list>
58 #include <math.h>
59 #include <vector>
60 
61 #ifdef VISP_USE_MSVC
62 #pragma comment(linker, "/STACK:256000000") // Increase max recursion depth
63 #endif
64 
65 /*!
66   \class vpDot
67 
68   \ingroup module_blob
69 
70   \brief This tracker is meant to track a dot (connected pixels with same
71   gray level) on a vpImage.
72 
73   The underground algorithm is based on a binarization of the image
74   and a connex component segmentation to determine the dot
75   characteristics (location, moments, size...).
76 
77   The following sample code shows how to grab images from a firewire camera,
78   track a blob and display the tracking results.
79 
80   \code
81 #include <visp3/blob/vpDot.h>
82 #include <visp3/gui/vpDisplayX.h>
83 #include <visp3/sensor/vp1394TwoGrabber.h>
84 
85 int main()
86 {
87 #if defined(VISP_HAVE_DC1394)
88   vpImage<unsigned char> I; // Create a gray level image container
89   vp1394TwoGrabber g(false); // Create a grabber based on libdc1394-2.x third party lib
90   g.acquire(I); // Acquire an image
91 
92 #if defined(VISP_HAVE_X11)
93   vpDisplayX d(I, 0, 0, "Camera view");
94 #endif
95   vpDisplay::display(I);
96   vpDisplay::flush(I);
97 
98   vpDot blob;
99   blob.initTracking(I);
100   blob.setGraphics(true);
101 
102   while(1) {
103     g.acquire(I); // Acquire an image
104     vpDisplay::display(I);
105     blob.track(I);
106 
107     vpDisplay::flush(I);
108   }
109 #endif
110 }
111   \endcode
112 
113   \sa vpDot2
114 */
115 class VISP_EXPORT vpDot : public vpTracker
116 {
117 public:
118   /*! \enum vpConnexityType
119   Type of connexity 4, or 8.
120   */
121   typedef enum {
122     CONNEXITY_4, /*!< For a given pixel 4 neighbors are considered (left,
123        right, up, down) */
124     CONNEXITY_8  /*!< For a given pixel 8 neighbors are considered (left,
125        right, up, down, and the 4 pixels located on the diagonal) */
126   } vpConnexityType;
127 
128 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
129 public:
130 #else
131 private:
132 #endif
133   static const unsigned int SPIRAL_SEARCH_SIZE; /*!< Spiral size for the dot search. */
134 
135   double m00; /*!< Considering the general distribution moments for \f$ N \f$
136                    points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N}
137                    u_h^i v_h^j \f$, \f$ m_{00} \f$ is a zero order moment obtained
138                    with \f$i = j = 0 \f$.
139 
140                    \sa setComputeMoments()
141                */
142   double m01; /*!< Considering the general distribution moments for \f$ N \f$
143                    points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N}
144                    u_h^i v_h^j \f$, \f$ m_{01} \f$ is a first order moment
145                    obtained with \f$i = 0 \f$ and \f$ j = 1 \f$.
146 
147                    \sa setComputeMoments()
148                */
149   double m10; /*!< Considering the general distribution moments for \f$ N \f$
150                    points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N}
151                    u_h^i v_h^j \f$, \f$ m_{10} \f$ is a first order moment
152                    obtained with \f$i = 1 \f$ and \f$ j = 0 \f$.
153 
154                    \sa setComputeMoments()
155                */
156   double m11; /*!< Considering the general distribution moments for \f$ N \f$
157                    points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N}
158                    u_h^i v_h^j \f$, \f$ m_{11} \f$ is a first order moment
159                    obtained with \f$i = 1 \f$ and \f$ j = 1 \f$.
160 
161                    \sa setComputeMoments()
162                */
163   double m20; /*!< Considering the general distribution moments for \f$ N \f$
164                    points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N}
165                    u_h^i v_h^j \f$, \f$ m_{20} \f$ is a second order moment
166                    obtained with \f$i = 2 \f$ and \f$ j = 0 \f$.
167 
168                    \sa setComputeMoments()
169                */
170   double m02; /*!< Considering the general distribution moments for \f$ N \f$
171                    points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N}
172                    u_h^i v_h^j \f$, \f$ m_{02} \f$ is a second order moment
173                    obtained with \f$i = 0 \f$ and \f$ j = 2 \f$.
174 
175                     \sa setComputeMoments()
176                */
177   double mu11; /*!< \f$ \mu_{11} \f$ is a second order centered moment defined
178                     by: \f$ \mu_{11} = m_{11} - \frac{m_{10}}{m_{00}}m_{01} \f$
179 
180                     \sa setComputeMoments()
181                 */
182   double mu20; /*!< \f$ \mu_{20} \f$ is a second order centered moment defined
183                     by: \f$ \mu_{20} = m_{20} - \frac{m_{10}}{m_{00}}m_{10} \f$
184 
185                     \sa setComputeMoments()
186                 */
187   double mu02; /*!< \f$ \mu_{02} \f$ is a second order centered moment defined
188                      by: \f$ \mu_{02} = m_{02} - \frac{m_{01}}{m_{00}}m_{01} \f$
189 
190                      \sa setComputeMoments()
191                 */
192 
193 public:
194   vpDot();
195   explicit vpDot(const vpImagePoint &ip);
196   vpDot(const vpDot &d);
197   virtual ~vpDot();
198 
199   void display(const vpImage<unsigned char> &I, vpColor color = vpColor::red, unsigned int thickness = 1) const;
200 
201   /*!
202     Gets the second order normalized centered moment \f$ n_{ij} \f$
203     as a 3-dim vector containing \f$ n_{20}, n_{11}, n_{02} \f$
204     such as \f$ n_{ij}  = \mu_{ij}/m_{00} \f$
205 
206     \return The 3-dim vector containing \f$ n_{20}, n_{11}, n_{02} \f$.
207 
208     \sa getCog(), getArea()
209   */
get_nij()210   inline vpColVector get_nij() const
211   {
212     vpColVector nij(3);
213     nij[0] = mu20 / m00;
214     nij[1] = mu11 / m00;
215     nij[2] = mu02 / m00;
216 
217     return nij;
218   }
219 
220   /*!
221     Gets the area of the blob corresponding also to the zero order moment.
222 
223     \return The blob area.
224   */
getArea()225   inline double getArea() const { return m00; }
226 
227   /*!
228 
229     Return the dot bounding box.
230 
231     \sa getWidth(), getHeight()
232 
233   */
getBBox()234   inline vpRect getBBox() const
235   {
236     vpRect bbox;
237 
238     bbox.setRect(this->u_min, this->v_min, this->u_max - this->u_min + 1, this->v_max - this->v_min + 1);
239 
240     return (bbox);
241   };
242   /*!
243     Return the location of the dot center of gravity.
244 
245     \return The coordinates of the center of gravity.
246   */
getCog()247   inline vpImagePoint getCog() const { return cog; }
248 
249   /*!
250       Return the list of all the image points on the border of the dot.
251 
252       \warning Doesn't return the image points inside the dot anymore. To get
253      those points see getConnexities().
254   */
getEdges()255   inline std::list<vpImagePoint> getEdges() const { return this->ip_edges_list; };
256 
257   /*!
258 
259     Return the list of all the image points inside the dot.
260 
261     \return The list of all the images points in the dot.
262     This list is updated after a call to track().
263 
264   */
getConnexities()265   inline std::list<vpImagePoint> getConnexities() const { return this->ip_connexities_list; };
266 
getGamma()267   inline double getGamma() const { return this->gamma; };
268   /*!
269 
270     Return the precision of the gray level of the dot. It is a double
271     precision float witch value is in ]0,1]. 1 means full precision, whereas
272     values close to 0 show a very bad precision.
273 
274   */
getGrayLevelPrecision()275   double getGrayLevelPrecision() const { return grayLevelPrecision; }
getMaxDotSize()276   double getMaxDotSize() const { return this->maxDotSizePercentage; }
277   /*!
278     Return the mean gray level value of the dot.
279   */
getMeanGrayLevel()280   double getMeanGrayLevel() const { return (this->mean_gray_level); };
281 
282   /*!
283   \return a vpPolygon made from the edges of the dot.
284   */
getPolygon()285   vpPolygon getPolygon() const { return (vpPolygon(ip_edges_list)); };
286 
287   /*!
288 
289     Return the width of the dot.
290 
291     \sa getHeight()
292 
293   */
getWidth()294   inline unsigned int getWidth() const { return (this->u_max - this->u_min + 1); };
295 
296   /*!
297 
298     Return the width of the dot.
299 
300     \sa getHeight()
301 
302   */
getHeight()303   inline unsigned int getHeight() const { return (this->v_max - this->v_min + 1); };
304 
305   void initTracking(const vpImage<unsigned char> &I);
306   void initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip);
307   void initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip, unsigned int gray_level_min,
308                     unsigned int gray_level_max);
309 
310   vpDot &operator=(const vpDot &d);
311   bool operator==(const vpDot &d) const;
312   bool operator!=(const vpDot &d) const;
313   friend VISP_EXPORT std::ostream &operator<<(std::ostream &os, vpDot &d);
314 
print(std::ostream & os)315   void print(std::ostream &os) { os << *this << std::endl; }
316 
317   /*!
318     Initialize the dot coordinates with \e ip.
319   */
setCog(const vpImagePoint & ip)320   inline void setCog(const vpImagePoint &ip) { this->cog = ip; }
321 
322   /*!
323 
324     Activates the dot's moments computation.
325 
326     \param activate true, if you want to compute the moments. If false,
327     moments are not computed.
328 
329     Computed moment are vpDot::m00, vpDot::m10, vpDot::m01, vpDot::m11,
330     vpDot::m20, vpDot::m02 and second order centered moments vpDot::mu11,
331     vpDot::mu20, vpDot::mu02 computed with respect to the blob centroid.
332 
333     The coordinates of the region's centroid (u, v) can be computed from the
334     moments by \f$u=\frac{m10}{m00}\f$ and  \f$v=\frac{m01}{m00}\f$.
335 
336   */
setComputeMoments(bool activate)337   void setComputeMoments(bool activate) { compute_moment = activate; }
338 
339   /*!
340     Set the type of connexity: 4 or 8.
341   */
setConnexity(vpConnexityType type)342   void setConnexity(vpConnexityType type) { this->connexityType = type; };
343   void setMaxDotSize(double percentage);
setGrayLevelMin(const unsigned int & level_min)344   void setGrayLevelMin(const unsigned int &level_min) { this->gray_level_min = level_min; };
setGrayLevelMax(const unsigned int & level_max)345   void setGrayLevelMax(const unsigned int &level_max) { this->gray_level_max = level_max; };
346   void setGrayLevelPrecision(const double &grayLevelPrecision);
347 
348   /*!
349     Activates the display of all the pixels of the dot during the tracking.
350     The default thickness of the overlayed drawings can be modified using
351     setGraphicsThickness().
352 
353     \warning To effectively display the dot graphics a call to
354     vpDisplay::flush() is needed.
355 
356     \param activate true to activate the display of dot pixels, false to turn
357     off the display.
358 
359     \sa setGraphicsThickness()
360   */
setGraphics(bool activate)361   void setGraphics(bool activate) { graphics = activate; }
362   /*!
363     Modify the default thickness that is set to 1 of the drawings in overlay
364     when setGraphics() is enabled.
365 
366     \sa setGraphics()
367     */
setGraphicsThickness(unsigned int t)368   void setGraphicsThickness(unsigned int t) { this->thickness = t; };
369 
370   void track(const vpImage<unsigned char> &I);
371   void track(const vpImage<unsigned char> &I, vpImagePoint &ip);
372 
373 private:
374   //! internal use only
375   std::list<vpImagePoint> ip_connexities_list;
376 
377   //! List of border points
378   std::list<vpImagePoint> ip_edges_list;
379 
380   /*! Type of connexity
381 
382    \warning In previous version this variable was called connexity
383   */
384   vpConnexityType connexityType;
385 
386   //! coordinates of the point center of gravity
387   vpImagePoint cog;
388 
389   // Bounding box
390   unsigned int u_min, u_max, v_min, v_max;
391 
392   // Flag used to allow display
393   bool graphics;
394 
395   unsigned int thickness; // Graphics thickness
396 
397   double maxDotSizePercentage;
398   unsigned char gray_level_out;
399 
400   double mean_gray_level;      // Mean gray level of the dot
401   unsigned int gray_level_min; // left threshold for binarisation
402   unsigned int gray_level_max; // right threshold for binarisation
403   double grayLevelPrecision;   // precision of the gray level of the dot.
404   // It is a double precision float witch value is in ]0,1].
405   // 1 means full precision, whereas values close to 0 show a very bad
406   // precision
407   double gamma;
408   //! flag : true moment are computed
409   bool compute_moment;
410   double nbMaxPoint;
411 
412   void init();
413   void setGrayLevelOut();
414   bool connexe(const vpImage<unsigned char> &I, unsigned int u, unsigned int v, double &mean_value, double &u_cog,
415                double &v_cog, double &n);
416   bool connexe(const vpImage<unsigned char> &I, unsigned int u, unsigned int v, double &mean_value, double &u_cog,
417                double &v_cog, double &n, std::vector<bool> &checkTab);
418   void COG(const vpImage<unsigned char> &I, double &u, double &v);
419 
420   // Static Functions
421 public:
422   static void display(const vpImage<unsigned char> &I, const vpImagePoint &cog,
423                       const std::list<vpImagePoint> &edges_list, vpColor color = vpColor::red,
424                       unsigned int thickness = 1);
425   static void display(const vpImage<vpRGBa> &I, const vpImagePoint &cog, const std::list<vpImagePoint> &edges_list,
426                       vpColor color = vpColor::red, unsigned int thickness = 1);
427 };
428 
429 #endif
430