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  * Defines a generic 2D polygon.
33  *
34  * Author:
35  * Amaury Dame
36  * Nicolas Melchior
37  * Romain Tallonneau
38  *
39  *****************************************************************************/
40 
41 #include <limits>
42 #include <set>
43 #include <visp3/core/vpDisplay.h>
44 #include <visp3/core/vpException.h>
45 #include <visp3/core/vpMeterPixelConversion.h>
46 #include <visp3/core/vpPolygon.h>
47 #include <visp3/core/vpUniRand.h>
48 /*!
49    Default constructor that creates an empty polygon.
50 */
vpPolygon()51 vpPolygon::vpPolygon()
52   : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
53 {
54 }
55 
56 /*!
57   Constructor which initialises the polygon thanks to the given corners.
58 
59   \warning the corners must be ordered (either clockwise or counter
60   clockwise).
61 
62   \param corners : The Points defining the corners.
63 */
vpPolygon(const std::vector<vpImagePoint> & corners)64 vpPolygon::vpPolygon(const std::vector<vpImagePoint> &corners)
65   : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
66 {
67   if (corners.size() < 3) {
68     _goodPoly = false;
69   }
70   init(corners);
71 }
72 
73 /*!
74   Constructor which initialises the polygon thanks to the given corners.
75 
76   \warning the corners must be ordered (either clockwise or counter
77   clockwise).
78 
79   \param corners : The Points defining the corners.
80 */
vpPolygon(const std::list<vpImagePoint> & corners)81 vpPolygon::vpPolygon(const std::list<vpImagePoint> &corners)
82   : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
83 {
84   if (corners.size() < 3) {
85     _goodPoly = false;
86   }
87   init(corners);
88 }
89 
90 /*!
91   Copy constructor
92 
93   \param poly : The polygon used for the initialisation.
94 */
vpPolygon(const vpPolygon & poly)95 vpPolygon::vpPolygon(const vpPolygon &poly)
96   : _corners(poly._corners), _center(poly._center), _area(poly._area), _goodPoly(poly._goodPoly), _bbox(poly._bbox),
97     m_PnPolyConstants(poly.m_PnPolyConstants), m_PnPolyMultiples(poly.m_PnPolyMultiples)
98 {
99 }
100 
101 /*!
102   Basic destructor
103 */
~vpPolygon()104 vpPolygon::~vpPolygon() {}
105 
106 /*!
107   Equal operator.
108 
109   Assign \e poly to this polygon and return a reference to it.
110 */
operator =(const vpPolygon & poly)111 vpPolygon &vpPolygon::operator=(const vpPolygon &poly)
112 {
113   _corners = poly._corners;
114   _center = poly._center;
115   _area = poly._area;
116   _goodPoly = poly._goodPoly;
117   _bbox = poly._bbox;
118   m_PnPolyConstants = poly.m_PnPolyConstants;
119   m_PnPolyMultiples = poly.m_PnPolyMultiples;
120   return *this;
121 }
122 
123 /*!
124   Initialises the triangle thanks to the collection of 2D points (in pixel).
125 
126   \warning the corners must be ordered (either clockwise or counter
127   clockwise).
128 
129   \param corners : The corners of the polyon.
130 */
buildFrom(const std::vector<vpImagePoint> & corners)131 void vpPolygon::buildFrom(const std::vector<vpImagePoint> &corners) { init(corners); }
132 
133 /*!
134   Initialises the polygon thanks to the collection of 2D points (in pixel).
135 
136   \warning the corners must be ordered (either clockwise or counter
137   clockwise).
138 
139   \param corners : The corners of the polyon.
140 */
buildFrom(const std::list<vpImagePoint> & corners)141 void vpPolygon::buildFrom(const std::list<vpImagePoint> &corners) { init(corners); }
142 
143 /*!
144   Initialises the triangle thanks to the collection of 2D points (in meter).
145   The fields \e x and \e y are used to compute the corresponding coordinates
146   in pixel thanks to the camera parameters \e cam.
147 
148   \warning the corners must be ordered (either clockwise or counter
149   clockwise).
150 
151   \param corners : The corners of the polyon.
152   \param cam : The camera parameters used to convert the coordinates from
153   meter to pixel.
154 */
buildFrom(const std::vector<vpPoint> & corners,const vpCameraParameters & cam)155 void vpPolygon::buildFrom(const std::vector<vpPoint> &corners, const vpCameraParameters &cam)
156 {
157   std::vector<vpImagePoint> ipCorners(corners.size());
158   for (unsigned int i = 0; i < corners.size(); ++i) {
159     vpMeterPixelConversion::convertPoint(cam, corners[i].get_x(), corners[i].get_y(), ipCorners[i]);
160   }
161   buildFrom(ipCorners);
162 }
163 
164 /*!
165   Initialises the polygon by (left-)clicking to add a corners to the polygon.
166   A right click is used to stop the addition of new corners.
167 
168   \param I : The image where to click to initialise the corners.
169   \param size : Cross size in terms of number of pixels that is displayed over
170   a polygon corner. \param color : Color used to display the cross over the
171   polygon corner. \param thickness : Thickness used to display the cross.
172 */
initClick(const vpImage<unsigned char> & I,unsigned int size,const vpColor & color,unsigned int thickness)173 void vpPolygon::initClick(const vpImage<unsigned char> &I, unsigned int size, const vpColor &color,
174                           unsigned int thickness)
175 {
176   vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
177   vpImagePoint ip;
178 
179   std::vector<vpImagePoint> cornersClick;
180 
181   while (button == vpMouseButton::button1) {
182     bool ret = vpDisplay::getClick(I, ip, button, true);
183     if (ret && button == vpMouseButton::button1) {
184       vpDisplay::displayCross(I, ip, size, color, thickness);
185       cornersClick.push_back(ip);
186       vpDisplay::flush(I);
187     }
188   }
189 
190   buildFrom(cornersClick);
191 }
192 
193 /*!
194   Initialises the polygon by (left-)clicking to add a corners to the polygon.
195   A right click is used to stop the addition of new corners.
196 
197   \param I : The image where to click to initialise the corners.
198   \param size : Size of the cross in terms of number of pixels that is
199   displayed over a polygon corner. \param color : Color used to display the
200   cross over the polygon corner. \param thickness : Thickness used to display
201   the cross.
202 */
initClick(const vpImage<vpRGBa> & I,unsigned int size,const vpColor & color,unsigned int thickness)203 void vpPolygon::initClick(const vpImage<vpRGBa> &I, unsigned int size, const vpColor &color, unsigned int thickness)
204 {
205   vpMouseButton::vpMouseButtonType button = vpMouseButton::button1;
206   vpImagePoint ip;
207 
208   std::vector<vpImagePoint> cornersClick;
209 
210   while (button == vpMouseButton::button1) {
211     bool ret = vpDisplay::getClick(I, ip, button, true);
212     if (ret && button == vpMouseButton::button1) {
213       vpDisplay::displayCross(I, ip, size, color, thickness);
214       cornersClick.push_back(ip);
215       vpDisplay::flush(I);
216     }
217   }
218 
219   buildFrom(cornersClick);
220 }
221 
222 /*!
223   Intialises the polygon using the collection of image points. This method
224   computes some internal variables such as center, area, ...
225 
226   \warning the corners must be ordered (either clockwise or counter
227   clockwise).
228 
229   \param corners : The corners of the polygon.
230 */
init(const std::vector<vpImagePoint> & corners)231 void vpPolygon::init(const std::vector<vpImagePoint> &corners)
232 {
233   _corners = corners;
234 
235   updateBoundingBox();
236   updateArea();
237   updateCenter();
238 
239   precalcValuesPnPoly();
240 }
241 
242 /*!
243   Intialises the polygon using the collection of image points. This method
244   computes some internal variables such as center, area, ...
245 
246   \warning the corners must be ordered (either clockwise or counter
247   clockwise).
248 
249   \param corners : The corners of the polygon.
250 */
init(const std::list<vpImagePoint> & corners)251 void vpPolygon::init(const std::list<vpImagePoint> &corners)
252 {
253   _corners.insert(_corners.begin(), corners.begin(), corners.end());
254 
255   updateBoundingBox();
256   updateArea();
257   updateCenter();
258 
259   precalcValuesPnPoly();
260 }
261 
262 /*!
263   Test if two segments are intersecting.
264 
265   \throw vpException::divideByZeroError if the two lines are aligned
266   (denominator equal to zero).
267 
268   \param ip1 : The first image point of the first segment.
269   \param ip2 : The second image point of the first segment.
270   \param ip3 : The first image point of the second segment.
271   \param ip4 : The second image point of the second segment.
272 */
testIntersectionSegments(const vpImagePoint & ip1,const vpImagePoint & ip2,const vpImagePoint & ip3,const vpImagePoint & ip4) const273 bool vpPolygon::testIntersectionSegments(const vpImagePoint &ip1, const vpImagePoint &ip2, const vpImagePoint &ip3,
274                                          const vpImagePoint &ip4) const
275 {
276   double di1 = ip2.get_i() - ip1.get_i();
277   double dj1 = ip2.get_j() - ip1.get_j();
278 
279   double di2 = ip4.get_i() - ip3.get_i();
280   double dj2 = ip4.get_j() - ip3.get_j();
281 
282   double denominator = di1 * dj2 - dj1 * di2;
283 
284   if (fabs(denominator) < std::numeric_limits<double>::epsilon()) {
285     throw vpException(vpException::divideByZeroError, "Denominator is null, lines are parallels");
286   }
287 
288   double alpha = -((ip1.get_i() - ip3.get_i()) * dj2 + di2 * (ip3.get_j() - ip1.get_j())) / denominator;
289   if (alpha < 0 || alpha >= 1) {
290     return false;
291   }
292 
293   double beta = -(di1 * (ip3.get_j() - ip1.get_j()) + dj1 * (ip1.get_i() - ip3.get_i())) / denominator;
294   if (beta < 0 || beta >= 1) {
295     return false;
296   }
297 
298   return true;
299 }
300 
301 /*!
302   Check if the 2D point \f$ ip \f$ is inside the polygon.
303 
304   \param ip : The point which have to be tested.
305   \param method : Method to use for Point In Polygon test.
306 
307   \return Returns true if the point is inside the polygon, false otherwise.
308 */
isInside(const vpImagePoint & ip,const PointInPolygonMethod & method) const309 bool vpPolygon::isInside(const vpImagePoint &ip, const PointInPolygonMethod &method) const
310 {
311   if (_corners.size() < 3) {
312     return false;
313   }
314 
315   bool test = false;
316   switch (method) {
317   case PnPolySegmentIntersection: {
318     vpImagePoint infPoint(100000, 100000); // take a point at 'inifinity'
319     vpUniRand generator;
320     infPoint.set_i(infPoint.get_i() + 1000 * generator());
321     infPoint.set_j(infPoint.get_j() + 1000 * generator()); // we add random since it appears that
322                                                            // sometimes infPoint may cause a
323                                                            // degenerated case (so realucnch and
324                                                            // hope that result will be
325                                                            // different).
326 
327     bool oddNbIntersections = false;
328     for (unsigned int i = 0; i < _corners.size(); ++i) {
329       vpImagePoint ip1 = _corners[i];
330       vpImagePoint ip2 = _corners[(i + 1) % _corners.size()];
331       bool intersection = false;
332 
333       // If the points are the same we continue without trying to found
334       // an intersection
335       if (ip1 == ip2)
336         continue;
337 
338       try {
339         intersection = testIntersectionSegments(ip1, ip2, ip, infPoint);
340       } catch (...) {
341         return isInside(ip);
342       }
343 
344       if (intersection) {
345         oddNbIntersections = !oddNbIntersections;
346       }
347     }
348 
349     test = oddNbIntersections;
350   } break;
351 
352   // Reference: http://alienryderflex.com/polygon/
353   case PnPolyRayCasting:
354   default: {
355     bool oddNodes = false;
356     for (size_t i = 0, j = _corners.size() - 1; i < _corners.size(); i++) {
357       if ((_corners[i].get_v() < ip.get_v() && _corners[j].get_v() >= ip.get_v()) ||
358           (_corners[j].get_v() < ip.get_v() && _corners[i].get_v() >= ip.get_v())) {
359         oddNodes ^= (ip.get_v() * m_PnPolyMultiples[i] + m_PnPolyConstants[i] < ip.get_u());
360       }
361 
362       j = i;
363     }
364 
365     test = oddNodes;
366   } break;
367   }
368 
369   return test;
370 }
371 
precalcValuesPnPoly()372 void vpPolygon::precalcValuesPnPoly()
373 {
374   if (_corners.size() < 3) {
375     return;
376   }
377 
378   m_PnPolyConstants.resize(_corners.size());
379   m_PnPolyMultiples.resize(_corners.size());
380 
381   for (size_t i = 0, j = _corners.size() - 1; i < _corners.size(); i++) {
382     if (vpMath::equal(_corners[j].get_v(), _corners[i].get_v(), std::numeric_limits<double>::epsilon())) {
383       m_PnPolyConstants[i] = _corners[i].get_u();
384       m_PnPolyMultiples[i] = 0.0;
385     } else {
386       m_PnPolyConstants[i] = _corners[i].get_u() -
387                              (_corners[i].get_v() * _corners[j].get_u()) / (_corners[j].get_v() - _corners[i].get_v()) +
388                              (_corners[i].get_v() * _corners[i].get_u()) / (_corners[j].get_v() - _corners[i].get_v());
389       m_PnPolyMultiples[i] = (_corners[j].get_u() - _corners[i].get_u()) / (_corners[j].get_v() - _corners[i].get_v());
390     }
391 
392     j = i;
393   }
394 }
395 
396 /*!
397   Update the \c _area attribute of the polygon using the corners.
398 
399   The area is computed using the formula:
400   \f[
401   A = \frac{1}{2} \sum_{i=0}^{n-1} (x_1 y_{i+1} - x_{i+1} y_{i})
402   \f]
403 
404 */
updateArea()405 void vpPolygon::updateArea()
406 {
407   if (_corners.size() == 0) {
408     _area = 0;
409     _goodPoly = false;
410     return;
411   }
412   _area = 0;
413 
414   for (unsigned int i = 0; i < _corners.size(); ++i) {
415     unsigned int i_p_1 = (i + 1) % _corners.size();
416     _area += _corners[i].get_j() * _corners[i_p_1].get_i() - _corners[i_p_1].get_j() * _corners[i].get_i();
417   }
418 
419   _area /= 2;
420   if (_area < 0) {
421     _area = -_area;
422   }
423 }
424 
425 /*!
426   Update the \c _center attribute of the polygon using the corners.
427 
428   The i coordinate is computed using:
429 
430   \f[
431   i = \frac{1}{6{area}} \sum_{i=0}^{n-1} (i_i + i_{i+1})(i_{i+1} j_i - j_{i+1}
432   i_i) \f]
433 
434   The computation of the j coordinate is similar.
435 */
updateCenter()436 void vpPolygon::updateCenter()
437 {
438   if (_corners.size() == 0) {
439     _center = vpImagePoint(0, 0);
440     _goodPoly = false;
441     return;
442   }
443   double i_tmp = 0;
444   double j_tmp = 0;
445 #if 0
446   for(unsigned int i=0; i<(_corners.size()-1); ++i){
447     i_tmp += (_corners[i].get_i() + _corners[i+1].get_i()) *
448              (_corners[i+1].get_i() * _corners[i].get_j() - _corners[i+1].get_j() * _corners[i].get_i());
449 
450     j_tmp += (_corners[i].get_j() + _corners[i+1].get_j()) *
451              (_corners[i+1].get_i() * _corners[i].get_j() - _corners[i+1].get_j() * _corners[i].get_i());
452   }
453 #else
454   for (unsigned int i = 0; i < _corners.size(); ++i) {
455     unsigned int i_p_1 = (i + 1) % _corners.size();
456     i_tmp += (_corners[i].get_i() + _corners[i_p_1].get_i()) *
457              (_corners[i_p_1].get_i() * _corners[i].get_j() - _corners[i_p_1].get_j() * _corners[i].get_i());
458 
459     j_tmp += (_corners[i].get_j() + _corners[i_p_1].get_j()) *
460              (_corners[i_p_1].get_i() * _corners[i].get_j() - _corners[i_p_1].get_j() * _corners[i].get_i());
461   }
462 #endif
463 
464   if (_area > 0) {
465     _center.set_i(fabs(i_tmp / (6 * _area)));
466     _center.set_j(fabs(j_tmp / (6 * _area)));
467   } else {
468     _center = _corners[0];
469     _goodPoly = false;
470   }
471 }
472 
473 /*!
474   Update bounding box of the polygon.
475 
476   \sa getBoundingBox()
477  */
updateBoundingBox()478 void vpPolygon::updateBoundingBox()
479 {
480   if (_corners.size() == 0) {
481     _bbox.setBottomRight(vpImagePoint(0, 0));
482     _bbox.setTopLeft(vpImagePoint(0, 0));
483     _goodPoly = false;
484     return;
485   }
486 
487   std::set<double> setI;
488   std::set<double> setJ;
489   for (unsigned int i = 0; i < _corners.size(); ++i) {
490     setI.insert(_corners[i].get_i());
491     setJ.insert(_corners[i].get_j());
492   }
493   vpImagePoint tl(*(setI.begin()), *(setJ.begin()));
494   std::set<double>::const_iterator iterI = setI.end();
495   std::set<double>::const_iterator iterJ = setJ.end();
496   --iterI;
497   --iterJ;
498   vpImagePoint br(*iterI, *iterJ);
499 
500   if (tl == br) {
501     _goodPoly = false;
502   }
503   _bbox.setTopLeft(tl);
504   _bbox.setBottomRight(br);
505 }
506 
507 /*!
508   Display the polygon in the image (overlay, so the image is not modified).
509   A call to the flush() method is necessary.
510 
511   \param I : The image where the polygon is displayed.
512   \param color : The color of the polygon's lines.
513   \param thickness : The thickness of the polygon's lines.
514 */
display(const vpImage<unsigned char> & I,const vpColor & color,unsigned int thickness) const515 void vpPolygon::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness) const
516 {
517   const unsigned int N = (unsigned int)_corners.size();
518   for (unsigned int i = 0; i < N; ++i) {
519     vpDisplay::displayLine(I, _corners[i], _corners[(i + 1) % N], color, thickness);
520   }
521 }
522 
523 /*!
524   Test if an image point is inside a 2D polygon.
525 
526   \param roi : List of the polygon corners.
527   \param i : i-coordinate of the image point to test.
528   \param j : j-coordinate of the image point to test.
529   \param method : Method to use for Point In Polygon test.
530 
531   \return True if the point defined by (i,j) is inside the polygon, False
532   otherwise.
533 */
isInside(const std::vector<vpImagePoint> & roi,const double & i,const double & j,const PointInPolygonMethod & method)534 bool vpPolygon::isInside(const std::vector<vpImagePoint> &roi, const double &i, const double &j,
535                          const PointInPolygonMethod &method)
536 {
537   vpPolygon poly(roi);
538   return poly.isInside(vpImagePoint(i, j), method);
539 }
540 
541 /*!
542   Return number of corners belonging to the polygon.
543  */
getSize() const544 unsigned int vpPolygon::getSize() const { return ((unsigned int)_corners.size()); }
545