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 * 37 *****************************************************************************/ 38 39 /* 40 \file vpDot2.h 41 \brief This tracker is meant to track some zones on a vpImage. 42 */ 43 44 #ifndef vpDot2_hh 45 #define vpDot2_hh 46 47 #include <visp3/core/vpColor.h> 48 #include <visp3/core/vpImage.h> 49 #include <visp3/core/vpImagePoint.h> 50 #include <visp3/core/vpPolygon.h> 51 #include <visp3/core/vpRect.h> 52 #include <visp3/core/vpTracker.h> 53 54 #include <list> 55 #include <vector> 56 57 /*! 58 \class vpDot2 59 60 \ingroup module_blob 61 62 \brief This tracker is meant to track a blob (connex pixels with same 63 gray level) on a vpImage. 64 65 The underground algorithm is based on a binarisation of the image 66 and then on a contour detection using the Freeman chain coding to 67 determine the blob characteristics (location, moments, size...). 68 69 The binarisation is done using gray level minimum and maximum values 70 that define the admissible gray levels of the blob. You can specify these 71 levels by setGrayLevelMin() and setGrayLevelMax(). These levels are also 72 set automatically by setGrayLevelPrecision(). The algorithm allows 73 to track white objects on a black background and vice versa. 74 75 When a blob is found, some tests are done to see if it is valid: 76 - A blob is considered by default as ellipsoid. The found blob could 77 be rejected if the shape is not ellipsoid. To determine if the shape 78 is ellipsoid the algorithm consider an inner and outside ellipse. 79 Sampled points on these two ellipses should have the right gray levels. 80 Along the inner ellipse the sampled points should have gray levels 81 that are in the gray level minimum and maximum bounds, while 82 on the outside ellipse, the gray levels should be out of the gray level 83 bounds. To set the percentage of the sample points which should have the 84 right levels use setEllipsoidBadPointsPercentage(). The distance between the 85 inner ellpsoid and the blob contour, as well the distance between the 86 blob contour and the outside ellipse is fixed by 87 setEllipsoidShapePrecision(). If you want to track a non ellipsoid shape, 88 and turn off this validation test, you have to call 89 setEllipsoidShapePrecision(0). 90 - The width, height and surface of the blob are compared to the 91 corresponding values of the previous blob. If they differ to much 92 the blob could be rejected. To set the admissible distance you can 93 use setSizePrecision(). 94 95 Note that track() and searchDotsInArea() are the most important features 96 of this class. 97 98 - track() estimate the current position of the dot using its previous 99 position, then try to compute the new parameters of the dot. If everything 100 went ok, tracking succeeds, otherwise we search this dot in a window 101 around the last position of the dot. 102 103 - searchDotsInArea() enable to find dots similar to this dot in a window. It 104 is used when there was a problem performing basic tracking of the dot, but 105 can also be used to find a certain type of dots in the full image. 106 107 The following sample code available in 108 tutorial-blob-tracker-live-firewire.cpp shows how to grab images from a 109 firewire camera, track a blob and display the tracking results. 110 111 \include tutorial-blob-tracker-live-firewire.cpp 112 A line by line explanation of the previous example is provided in 113 \ref tutorial-tracking-blob. 114 115 This other example available in tutorial-blob-auto-tracker.cpp shows firstly 116 how to detect in the first image all the blobs that match some 117 characteristics in terms of size, area, gray level. Secondly, it shows how 118 to track all the dots that are detected. 119 120 \include tutorial-blob-auto-tracker.cpp 121 A line by line explanation of this last example is also provided in 122 \ref tutorial-tracking-blob, section \ref tracking_blob_tracking. 123 124 \sa vpDot 125 */ 126 class VISP_EXPORT vpDot2 : public vpTracker 127 { 128 public: 129 vpDot2(); 130 explicit vpDot2(const vpImagePoint &ip); 131 vpDot2(const vpDot2 &twinDot); 132 virtual ~vpDot2(); 133 134 static vpMatrix defineDots(vpDot2 dot[], const unsigned int &n, const std::string &dotFile, vpImage<unsigned char> &I, 135 vpColor col = vpColor::blue, bool trackDot = true); 136 137 void display(const vpImage<unsigned char> &I, vpColor color = vpColor::red, unsigned int thickness = 1) const; 138 139 /*! 140 Gets the second order normalized centered moment \f$ n_{ij} \f$ 141 as a 3-dim vector containing \f$ n_{20}, n_{11}, n_{02} \f$ 142 such as \f$ n_{ij} = \mu_{ij}/m_{00} \f$ 143 144 \return The 3-dim vector containing \f$ n_{20}, n_{11}, n_{02} \f$. 145 146 \sa getCog(), getArea() 147 */ get_nij()148 inline vpColVector get_nij() const 149 { 150 vpColVector nij(3); 151 nij[0] = mu20 / m00; 152 nij[1] = mu11 / m00; 153 nij[2] = mu02 / m00; 154 155 return nij; 156 } 157 158 double getArea() const; 159 /*! 160 161 Return the dot bounding box. 162 163 \sa getWidth(), getHeight() 164 165 */ getBBox()166 inline vpRect getBBox() const 167 { 168 vpRect bbox; 169 170 bbox.setRect(this->bbox_u_min, this->bbox_v_min, this->bbox_u_max - this->bbox_u_min + 1, 171 this->bbox_v_max - this->bbox_v_min + 1); 172 173 return (bbox); 174 }; 175 /*! 176 Return the location of the dot center of gravity. 177 178 \return The coordinates of the center of gravity. 179 */ getCog()180 inline vpImagePoint getCog() const { return cog; } 181 182 double getDistance(const vpDot2 &distantDot) const; 183 /*! 184 185 Return the list of all the image points on the dot 186 border. 187 188 \param edges_list : The list of all the images points on the dot 189 border. This list is update after a call to track(). 190 191 */ getEdges(std::list<vpImagePoint> & edges_list)192 void getEdges(std::list<vpImagePoint> &edges_list) const { edges_list = this->ip_edges_list; }; 193 /*! 194 195 Return the list of all the image points on the dot 196 border. 197 198 \return The list of all the images points on the dot 199 border. This list is update after a call to track(). 200 201 */ getEdges()202 std::list<vpImagePoint> getEdges() const { return (this->ip_edges_list); }; 203 /*! 204 Get the percentage of sampled points that are considered non conform 205 in terms of the gray level on the inner and the ouside ellipses. 206 207 \sa setEllipsoidBadPointsPercentage() 208 */ getEllipsoidBadPointsPercentage()209 double getEllipsoidBadPointsPercentage() const { return allowedBadPointsPercentage_; } 210 211 double getEllipsoidShapePrecision() const; 212 void getFreemanChain(std::list<unsigned int> &freeman_chain) const; 213 getGamma()214 inline double getGamma() const { return this->gamma; }; 215 /*! 216 Return the color level of pixels inside the dot. 217 218 \sa getGrayLevelMax() 219 */ getGrayLevelMin()220 inline unsigned int getGrayLevelMin() const { return gray_level_min; }; 221 /*! 222 Return the color level of pixels inside the dot. 223 224 \sa getGrayLevelMin() 225 */ getGrayLevelMax()226 inline unsigned int getGrayLevelMax() const { return gray_level_max; }; 227 double getGrayLevelPrecision() const; 228 229 double getHeight() const; 230 double getMaxSizeSearchDistancePrecision() const; 231 /*! 232 \return The mean gray level value of the dot. 233 */ getMeanGrayLevel()234 double getMeanGrayLevel() const { return (this->mean_gray_level); }; 235 /*! 236 \return a vpPolygon made from the edges of the dot. 237 */ getPolygon()238 vpPolygon getPolygon() const { return (vpPolygon(ip_edges_list)); }; 239 double getSizePrecision() const; 240 double getWidth() const; 241 242 void initTracking(const vpImage<unsigned char> &I, unsigned int size = 0); 243 void initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip, unsigned int size = 0); 244 void initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip, unsigned int gray_lvl_min, 245 unsigned int gray_lvl_max, unsigned int size = 0); 246 247 vpDot2 &operator=(const vpDot2 &twinDot); 248 friend VISP_EXPORT std::ostream &operator<<(std::ostream &os, vpDot2 &d); 249 print(std::ostream & os)250 void print(std::ostream &os) { os << *this << std::endl; } 251 void searchDotsInArea(const vpImage<unsigned char> &I, int area_u, int area_v, unsigned int area_w, 252 unsigned int area_h, std::list<vpDot2> &niceDots); 253 254 void searchDotsInArea(const vpImage<unsigned char> &I, std::list<vpDot2> &niceDots); 255 256 void setArea(const double &area); 257 /*! 258 Initialize the dot coordinates with \e ip. 259 */ setCog(const vpImagePoint & ip)260 inline void setCog(const vpImagePoint &ip) { this->cog = ip; } 261 /*! 262 263 Activates the dot's moments computation. 264 265 \param activate true, if you want to compute the moments. If false, 266 moments are not computed. 267 268 Computed moment are vpDot::m00, vpDot::m10, vpDot::m01, vpDot::m11, 269 vpDot::m20, vpDot::m02 and second order centereed moments vpDot::mu11, 270 vpDot::mu20, vpDot::mu02 computed with respect to the blob centroid. 271 272 The coordinates of the region's centroid (u, v) can be computed from the 273 moments by \f$u=\frac{m10}{m00}\f$ and \f$v=\frac{m01}{m00}\f$. 274 275 */ setComputeMoments(bool activate)276 void setComputeMoments(bool activate) { compute_moment = activate; } 277 278 /*! 279 Set the percentage of sampled points that are considered non conform 280 in terms of the gray level on the inner and the ouside ellipses. 281 Points located on the inner ellipse should have the same gray level 282 than the blob, while points located on the outside ellipse should 283 have a different gray level. 284 285 \param percentage : Percentage of points sampled with bad gray level 286 on the inner and outside ellipses that are admissible. 0 means 287 that all the points should have a right level, while a value of 1 288 means that all the points can have a bad gray level. 289 */ 290 void setEllipsoidBadPointsPercentage(const double &percentage = 0.0) 291 { 292 if (percentage < 0.) 293 allowedBadPointsPercentage_ = 0.; 294 else if (percentage > 1.) 295 allowedBadPointsPercentage_ = 1.; 296 else 297 allowedBadPointsPercentage_ = percentage; 298 } 299 300 void setEllipsoidShapePrecision(const double &ellipsoidShapePrecision); 301 /*! 302 Activates the display of the border of the dot during the tracking. 303 The default thickness of the overlayed drawings can be modified using 304 setGraphicsThickness(). 305 306 \warning To effectively display the dot graphics a call to 307 vpDisplay::flush() is needed. 308 309 \param activate If true, the border of the dot will be painted. false to 310 turn off border painting. 311 312 \sa setGraphicsThickness() 313 */ setGraphics(bool activate)314 void setGraphics(bool activate) { graphics = activate; } 315 /*! 316 Modify the default thickness that is set to 1 of the drawings in overlay 317 when setGraphics() is enabled. 318 319 \sa setGraphics() 320 */ setGraphicsThickness(unsigned int t)321 void setGraphicsThickness(unsigned int t) { this->thickness = t; }; 322 /*! 323 324 Set the color level of the dot to search a dot in a region of interest. This 325 level will be used to know if a pixel in the image belongs to the dot or 326 not. Only pixels with higher level can belong to the dot. If the level is 327 lower than the minimum level for a dot, set the level to MIN_IN_LEVEL. 328 329 \param min : Color level of a dot to search in a region of interest. 330 331 \sa setGrayLevelMax(), setGrayLevelPrecision() 332 333 */ setGrayLevelMin(const unsigned int & min)334 inline void setGrayLevelMin(const unsigned int &min) 335 { 336 if (min > 255) 337 this->gray_level_min = 255; 338 else 339 this->gray_level_min = min; 340 }; 341 342 /*! 343 344 Set the color level of pixels surrounding the dot. This is meant to be used 345 to search a dot in a region of interest. 346 347 \param max : Intensity level of a dot to search in a region of interest. 348 349 \sa setGrayLevelMin(), setGrayLevelPrecision() 350 */ setGrayLevelMax(const unsigned int & max)351 inline void setGrayLevelMax(const unsigned int &max) 352 { 353 if (max > 255) 354 this->gray_level_max = 255; 355 else 356 this->gray_level_max = max; 357 }; 358 void setGrayLevelPrecision(const double &grayLevelPrecision); 359 void setHeight(const double &height); 360 void setMaxSizeSearchDistancePrecision(const double &maxSizeSearchDistancePrecision); 361 void setSizePrecision(const double &sizePrecision); 362 void setWidth(const double &width); 363 364 void track(const vpImage<unsigned char> &I, bool canMakeTheWindowGrow = true); 365 void track(const vpImage<unsigned char> &I, vpImagePoint &cog, bool canMakeTheWindowGrow = true); 366 367 static void trackAndDisplay(vpDot2 dot[], const unsigned int &n, vpImage<unsigned char> &I, 368 std::vector<vpImagePoint> &cogs, vpImagePoint *cogStar = NULL); 369 370 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS 371 public: 372 #else 373 private: 374 #endif 375 double m00; /*!< Considering the general distribution moments for \f$ N \f$ 376 points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N} 377 u_h^i v_h^j \f$, \f$ m_{00} \f$ is a zero order moment obtained 378 with \f$i = j = 0 \f$. This moment corresponds to the dot 379 surface. 380 381 \sa setComputeMoments() 382 */ 383 double m10; /*!< Considering the general distribution moments for \f$ N \f$ 384 points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N} 385 u_h^i v_h^j \f$, \f$ m_{10} \f$ is a first order moment 386 obtained with \f$i = 1 \f$ and \f$ j = 0 \f$. \f$ m_{10} \f$ 387 corresponds to the inertia first order moment along the v axis. 388 389 \sa setComputeMoments() 390 */ 391 double m01; /*!< Considering the general distribution moments for \f$ N \f$ 392 points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N} 393 u_h^i v_h^j \f$, \f$ m_{01} \f$ is a first order moment 394 obtained with \f$i = 0 \f$ and \f$ j = 1 \f$. \f$ m_{01} \f$ 395 corresponds to the inertia first order moment along the u axis. 396 397 \sa setComputeMoments() 398 */ 399 double m11; /*!< Considering the general distribution moments for \f$ N \f$ 400 points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N} 401 u_h^i v_h^j \f$, \f$ m_{11} \f$ is a first order moment 402 obtained with \f$i = 1 \f$ and \f$ j = 1 \f$. 403 404 \sa setComputeMoments() 405 */ 406 double m20; /*!< Considering the general distribution moments for \f$ N \f$ 407 points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N} 408 u_h^i v_h^j \f$, \f$ m_{20} \f$ is a second order moment 409 obtained with \f$i = 2 \f$ and \f$ j = 0 \f$. \f$ m_{20} \f$ 410 corresponds to the inertia second order moment along the v 411 axis. 412 413 \sa setComputeMoments() 414 */ 415 double m02; /*!< Considering the general distribution moments for \f$ N \f$ 416 points defined by the relation \f$ m_{ij} = \sum_{h=0}^{N} 417 u_h^i v_h^j \f$, \f$ m_{02} \f$ is a second order moment 418 obtained with \f$i = 0 \f$ and \f$ j = 2 \f$. \f$ m_{02} \f$ 419 corresponds to the inertia second order moment along the u 420 axis. 421 422 \sa setComputeMoments() 423 */ 424 double mu11; /*!< \f$ \mu_{11} \f$ is a second order centered moment defined 425 by: \f$ \mu_{11} = m_{11} - \frac{m_{10}}{m_{00}}m_{01} \f$ 426 427 \sa setComputeMoments() 428 */ 429 double mu20; /*!< \f$ \mu_{20} \f$ is a second order centered moment defined 430 by: \f$ \mu_{20} = m_{20} - \frac{m_{10}}{m_{00}}m_{10} \f$ 431 432 \sa setComputeMoments() 433 */ 434 double mu02; /*!< \f$ \mu_{02} \f$ is a second order centered moments defined 435 by: \f$ \mu_{02} = m_{02} - \frac{m_{01}}{m_{00}}m_{01} \f$ 436 437 \sa setComputeMoments() 438 */ 439 440 private: 441 virtual bool isValid(const vpImage<unsigned char> &I, const vpDot2 &wantedDot); 442 443 virtual bool hasGoodLevel(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v) const; 444 virtual bool hasReverseLevel(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v) const; 445 446 virtual vpDot2 *getInstance(); 447 448 void init(); 449 450 bool computeParameters(const vpImage<unsigned char> &I, const double &u = -1.0, const double &v = -1.0); 451 452 bool findFirstBorder(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v, 453 unsigned int &border_u, unsigned int &border_v); 454 void computeMeanGrayLevel(const vpImage<unsigned char> &I); 455 456 /*! 457 458 Get the starting point on a dot border. The dot border is 459 computed from this point. 460 461 \sa getFirstBorder_v() 462 463 */ getFirstBorder_u()464 unsigned int getFirstBorder_u() const { return this->firstBorder_u; } 465 /*! 466 467 Get the starting point on a dot border. The dot border is 468 computed from this point. 469 470 \sa getFirstBorder_u() 471 472 */ getFirstBorder_v()473 unsigned int getFirstBorder_v() const { return this->firstBorder_v; } 474 475 bool computeFreemanChainElement(const vpImage<unsigned char> &I, const unsigned int &u, const unsigned int &v, 476 unsigned int &element); 477 void computeFreemanParameters(const int &u_p, const int &v_p, unsigned int &element, int &du, int &dv, float &dS, 478 float &dMu, float &dMv, float &dMuv, float &dMu2, float &dMv2); 479 void updateFreemanPosition(unsigned int &u, unsigned int &v, const unsigned int &dir); 480 481 bool isInImage(const vpImage<unsigned char> &I) const; 482 bool isInImage(const vpImage<unsigned char> &I, const vpImagePoint &ip) const; 483 484 bool isInArea(const unsigned int &u, const unsigned int &v) const; 485 486 void getGridSize(unsigned int &gridWidth, unsigned int &gridHeight); 487 void setArea(const vpImage<unsigned char> &I, int u, int v, unsigned int w, unsigned int h); 488 void setArea(const vpImage<unsigned char> &I); 489 void setArea(const vpRect &a); 490 491 unsigned char getMeanGrayLevel(vpImage<unsigned char> &I) const; 492 //! coordinates (float) of the point center of gravity 493 vpImagePoint cog; 494 495 double width; 496 double height; 497 double surface; 498 unsigned int gray_level_min; // minumum gray level for the dot. 499 // pixel with lower level don't belong 500 // to this dot. 501 502 unsigned int gray_level_max; // maximum gray level for the dot. 503 // pixel with higher level don't belong 504 // to this dot. 505 double mean_gray_level; // Mean gray level of the dot 506 double grayLevelPrecision; 507 double gamma; 508 double sizePrecision; 509 double ellipsoidShapePrecision; 510 double maxSizeSearchDistancePrecision; 511 double allowedBadPointsPercentage_; 512 // Area where the dot is to search 513 vpRect area; 514 515 // other 516 std::list<unsigned int> direction_list; 517 std::list<vpImagePoint> ip_edges_list; 518 519 // flag 520 bool compute_moment; // true moment are computed 521 bool graphics; // true for graphic overlay display 522 523 unsigned int thickness; // Graphics thickness 524 525 // Bounding box 526 int bbox_u_min, bbox_u_max, bbox_v_min, bbox_v_max; 527 528 // The first point coodinate on the dot border 529 unsigned int firstBorder_u; 530 unsigned int firstBorder_v; 531 532 // Static funtions 533 public: 534 static void display(const vpImage<unsigned char> &I, const vpImagePoint &cog, 535 const std::list<vpImagePoint> &edges_list, vpColor color = vpColor::red, 536 unsigned int thickness = 1); 537 static void display(const vpImage<vpRGBa> &I, const vpImagePoint &cog, const std::list<vpImagePoint> &edges_list, 538 vpColor color = vpColor::red, unsigned int thickness = 1); 539 }; 540 541 #endif 542