Visual Servoing Platform  version 3.0.1
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
vpPolygon.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ViSP software.
4  * Copyright (C) 2005 - 2017 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * ("GPL") version 2 as published by the Free Software Foundation.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See http://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Defines a generic 2D polygon.
32  *
33  * Author:
34  * Amaury Dame
35  * Nicolas Melchior
36  * Romain Tallonneau
37  *
38  *****************************************************************************/
39 
40 
41 
42 #include <visp3/core/vpPolygon.h>
43 #include <visp3/core/vpException.h>
44 #include <visp3/core/vpDisplay.h>
45 #include <visp3/core/vpMeterPixelConversion.h>
46 #include <visp3/core/vpUniRand.h>
47 #include <set>
48 #include <limits>
56  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
57 {
58  std::vector<vpImagePoint> corners;
59  corners.push_back(vpImagePoint(0,0));
60  corners.push_back(vpImagePoint(1,0));
61  corners.push_back(vpImagePoint(0,1));
62  init(corners);
63 }
64 
72 vpPolygon::vpPolygon(const std::vector<vpImagePoint>& corners)
73  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
74 {
75  if(corners.size() < 3){
76  _goodPoly = false;
77  }
78  init(corners);
79 }
80 
88 vpPolygon::vpPolygon(const std::list<vpImagePoint>& corners)
89  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
90 {
91  if(corners.size() < 3){
92  _goodPoly = false;
93  }
94  init(corners);
95 }
96 
103  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox()
104 {
105  _corners = poly._corners;
106  _center = poly._center;
107  _area = poly._area;
108  _goodPoly = poly._goodPoly;
109  _bbox = poly._bbox;
110  m_PnPolyConstants = poly.m_PnPolyConstants;
111  m_PnPolyMultiples = poly.m_PnPolyMultiples;
112 }
113 
118 {
119 }
120 
126 vpPolygon &
128 {
129  _corners = poly._corners;
130  _center = poly._center;
131  _area = poly._area;
132  _goodPoly = poly._goodPoly;
133  _bbox = poly._bbox;
134  m_PnPolyConstants = poly.m_PnPolyConstants;
135  m_PnPolyMultiples = poly.m_PnPolyMultiples;
136  return *this;
137 }
138 
146 void
147 vpPolygon::buildFrom(const std::vector<vpImagePoint>& corners)
148 {
149  init(corners);
150 }
151 
159 void
160 vpPolygon::buildFrom(const std::list<vpImagePoint>& corners)
161 {
162  init(corners);
163 }
164 
176 void
177 vpPolygon::buildFrom(const std::vector<vpPoint>& corners, const vpCameraParameters& cam)
178 {
179  std::vector<vpImagePoint> ipCorners(corners.size());
180  for(unsigned int i=0; i<corners.size(); ++i){
181  vpMeterPixelConversion::convertPoint(cam, corners[i].get_x(), corners[i].get_y(), ipCorners[i]);
182  }
183  buildFrom(ipCorners);
184 }
185 
192 void
194 {
196  vpImagePoint ip;
197 
198  std::vector<vpImagePoint> cornersClick;
199 
200  while(button == vpMouseButton::button1){
201  bool ret = vpDisplay::getClick(I, ip, button, true);
202  if(ret && button == vpMouseButton::button1){
204  cornersClick.push_back(ip);
205  vpDisplay::flush(I);
206  }
207  }
208 
209  buildFrom(cornersClick);
210 }
211 
212 
221 void
222 vpPolygon::init(const std::vector<vpImagePoint>& corners)
223 {
224  _corners = corners;
225 
227  updateArea();
228  updateCenter();
229 
230  precalcValuesPnPoly();
231 }
232 
233 
242 void
243 vpPolygon::init(const std::list<vpImagePoint>& corners)
244 {
245  _corners.insert(_corners.begin(), corners.begin(), corners.end());
246 
248  updateArea();
249  updateCenter();
250 
251  precalcValuesPnPoly();
252 }
253 
254 
255 
266 bool
267 vpPolygon::testIntersectionSegments(const vpImagePoint& ip1, const vpImagePoint& ip2, const vpImagePoint& ip3, const vpImagePoint& ip4) const
268 {
269  double di1 = ip2.get_i() - ip1.get_i();
270  double dj1 = ip2.get_j() - ip1.get_j();
271 
272  double di2 = ip4.get_i() - ip3.get_i();
273  double dj2 = ip4.get_j() - ip3.get_j();
274 
275  double denominator = di1 * dj2 - dj1 * di2;
276 
277  if(fabs(denominator) < std::numeric_limits<double>::epsilon()){
278  throw vpException(vpException::divideByZeroError, "Denominator is null, lines are parallels");
279  }
280 
281  double alpha = - ( ( ip1.get_i() - ip3.get_i() ) * dj2 + di2 * ( ip3.get_j() - ip1.get_j())) / denominator;
282  if(alpha < 0 || alpha >= 1){
283  return false;
284  }
285 
286  double beta = - (di1 * (ip3.get_j() - ip1.get_j() ) + dj1 * (ip1.get_i() - ip3.get_i()) ) / denominator;
287  if(beta < 0 || beta >= 1){
288  return false;
289  }
290 
291  return true;
292 }
293 
302 bool
304 {
305  if (_corners.size() < 3) {
306  std::cerr << "It is not a polygon (_corners.size() < 3)!" << std::endl;
307  return false;
308  }
309 
310  bool test = false;
311  switch (method) {
313  {
314  vpImagePoint infPoint(100000, 100000); // take a point at 'inifinity'
315  vpUniRand generator;
316  infPoint.set_i( infPoint.get_i() + 1000 * generator());
317  infPoint.set_j( infPoint.get_j() + 1000 * generator());// we add random since it appears that sometimes infPoint may cause a degenerated case (so realucnch and hope that result will be different).
318 
319  bool oddNbIntersections = false;
320  for(unsigned int i=0; i<_corners.size(); ++i){
321  vpImagePoint ip1 = _corners[i];
322  vpImagePoint ip2 = _corners[(i+1)%_corners.size()];
323  bool intersection = false;
324 
325  // If the points are the same we continue without trying to found
326  // an intersection
327  if (ip1 == ip2)
328  continue;
329 
330  try{
331  intersection = testIntersectionSegments(ip1, ip2, ip, infPoint );
332  } catch(...){
333  return isInside(ip);
334  }
335 
336  if(intersection){
337  oddNbIntersections = !oddNbIntersections;
338  }
339  }
340 
341  test = oddNbIntersections;
342  }
343  break;
344 
345  //Reference: http://alienryderflex.com/polygon/
346  case PnPolyRayCasting:
347  default:
348  {
349  bool oddNodes = false;
350  for (size_t i = 0, j = _corners.size()-1; i < _corners.size(); i++) {
351  if ((_corners[i].get_v() < ip.get_v() && _corners[j].get_v() >= ip.get_v()) || (_corners[j].get_v() < ip.get_v() && _corners[i].get_v() >= ip.get_v())) {
352  oddNodes ^= (ip.get_v()*m_PnPolyMultiples[i] + m_PnPolyConstants[i] < ip.get_u());
353  }
354 
355  j=i;
356  }
357 
358  test = oddNodes;
359  }
360  break;
361  }
362 
363  return test;
364 }
365 
366 void
367 vpPolygon::precalcValuesPnPoly() {
368  if (_corners.size() < 3) {
369  std::cerr << "It is not a polygon (_corners.size() < 3)!" << std::endl;
370  return;
371  }
372 
373  m_PnPolyConstants.resize(_corners.size());
374  m_PnPolyMultiples.resize(_corners.size());
375 
376  for (size_t i = 0, j = _corners.size()-1; i < _corners.size(); i++) {
377  if( vpMath::equal(_corners[j].get_v(), _corners[i].get_v(), std::numeric_limits<double>::epsilon())) {
378  m_PnPolyConstants[i] = _corners[i].get_u();
379  m_PnPolyMultiples[i] = 0.0;
380  } else {
381  m_PnPolyConstants[i] = _corners[i].get_u() - (_corners[i].get_v()*_corners[j].get_u()) / (_corners[j].get_v()-_corners[i].get_v()) + (_corners[i].get_v()*_corners[i].get_u())
382  / (_corners[j].get_v()-_corners[i].get_v());
383  m_PnPolyMultiples[i] = (_corners[j].get_u()-_corners[i].get_u()) / (_corners[j].get_v()-_corners[i].get_v());
384  }
385 
386  j = i;
387  }
388 }
389 
399 void
401 {
402  if(_corners.size() == 0){
403  _area = 0;
404  _goodPoly = false;
405  return;
406  }
407  _area = 0;
408 
409  for(unsigned int i=0; i<_corners.size(); ++i){
410  unsigned int i_p_1 = ( i+1 ) % _corners.size();
411  _area += _corners[i].get_j() * _corners[i_p_1].get_i()
412  - _corners[i_p_1].get_j() * _corners[i].get_i();
413  }
414 
415  _area /= 2;
416  if(_area < 0){
417  _area = - _area;
418  }
419 }
420 
421 
433 void
435 {
436  if(_corners.size() == 0){
437  _center = vpImagePoint(0, 0);
438  _goodPoly = false;
439  return;
440  }
441  double i_tmp = 0;
442  double j_tmp = 0;
443 #if 0
444  for(unsigned int i=0; i<(_corners.size()-1); ++i){
445  i_tmp += (_corners[i].get_i() + _corners[i+1].get_i()) *
446  (_corners[i+1].get_i() * _corners[i].get_j() - _corners[i+1].get_j() * _corners[i].get_i());
447 
448  j_tmp += (_corners[i].get_j() + _corners[i+1].get_j()) *
449  (_corners[i+1].get_i() * _corners[i].get_j() - _corners[i+1].get_j() * _corners[i].get_i());
450  }
451 #else
452  for(unsigned int i=0; i<_corners.size(); ++i){
453  unsigned int i_p_1 = ( i+1 ) % _corners.size();
454  i_tmp += (_corners[i].get_i() + _corners[i_p_1].get_i()) *
455  (_corners[i_p_1].get_i() * _corners[i].get_j()
456  - _corners[i_p_1].get_j() * _corners[i].get_i());
457 
458  j_tmp += (_corners[i].get_j() + _corners[i_p_1].get_j()) *
459  (_corners[i_p_1].get_i() * _corners[i].get_j()
460  - _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 
478 void
480 {
481  if(_corners.size() == 0){
484  _goodPoly = false;
485  return;
486  }
487 
488  std::set<double> setI;
489  std::set<double> setJ;
490  for(unsigned int i=0; i<_corners.size(); ++i){
491  setI.insert(_corners[i].get_i());
492  setJ.insert(_corners[i].get_j());
493  }
494  vpImagePoint tl(*(setI.begin()), *(setJ.begin()));
495  std::set<double>::const_iterator iterI = setI.end();
496  std::set<double>::const_iterator iterJ = setJ.end();
497  --iterI;
498  --iterJ;
499  vpImagePoint br(*iterI, *iterJ);
500 
501  if(tl == br){
502  _goodPoly = false;
503  }
504  _bbox.setTopLeft(tl);
505  _bbox.setBottomRight(br);
506 }
507 
516 void
517 vpPolygon::display(const vpImage<unsigned char>& I, const vpColor& color, unsigned int thickness) const
518 {
519  const unsigned int N = (unsigned int)_corners.size();
520  for(unsigned int i=0; i<N; ++i){
521  vpDisplay::displayLine(I, _corners[i], _corners[(i+1)%N], color, thickness);
522  }
523 }
524 
525 bool
526 vpPolygon::intersect(const vpImagePoint& p1, const vpImagePoint& p2, const double &i_test, const double &j_test, const double &i, const double &j)
527 {
528  double dx = p2.get_j() - p1.get_j();
529  double dy = p2.get_i() - p1.get_i();
530  double ex = j - j_test;
531  double ey = i - i_test;
532 
533  double den = dx * ey - dy * ex;
534  double t = 0, u = 0;
535  //if(den != 0){
536  if(std::fabs(den) > std::fabs(den)*std::numeric_limits<double>::epsilon()){
537  t = -( ey * ( p1.get_j() - j_test ) + ex * ( -p1.get_i() + i_test ) ) / den;
538  u = -( dx * ( -p1.get_i() + i_test ) + dy * ( p1.get_j() - j_test ) ) / den;
539  }
540  else{
541  throw vpException(vpException::divideByZeroError, "denominator null");
542  }
543  return ( t >= std::numeric_limits<double>::epsilon() && t < 1.0 && u >= std::numeric_limits<double>::epsilon() && u < 1.0);
544 }
545 
556 bool
557 vpPolygon::isInside(const std::vector<vpImagePoint>& roi, const double &i, const double &j, const PointInPolygonMethod &method)
558 {
559  vpPolygon poly(roi);
560  return poly.isInside(vpImagePoint(i, j), method);
561 }
562 
566 unsigned int vpPolygon::getSize() const
567 {
568  return ((unsigned int) _corners.size());
569 }
570 
virtual ~vpPolygon()
Definition: vpPolygon.cpp:117
double get_v() const
Definition: vpImagePoint.h:268
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
double get_i() const
Definition: vpImagePoint.h:199
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
Point coordinates conversion from normalized coordinates in meter to pixel coordinates ...
Class to define colors available for display functionnalities.
Definition: vpColor.h:121
static bool equal(double x, double y, double s=0.001)
Definition: vpMath.h:306
void init(const std::vector< vpImagePoint > &corners)
Definition: vpPolygon.cpp:222
double get_u() const
Definition: vpImagePoint.h:257
error that can be emited by ViSP classes.
Definition: vpException.h:73
vpPolygon & operator=(const vpPolygon &poly)
Definition: vpPolygon.cpp:127
vpRect _bbox
Bounding box containing the polygon.
Definition: vpPolygon.h:111
vpImagePoint _center
Center of the polygon. It is automatically computed when the corners are set.
Definition: vpPolygon.h:105
static void flush(const vpImage< unsigned char > &I)
double get_j() const
Definition: vpImagePoint.h:210
static const vpColor red
Definition: vpColor.h:163
void updateArea()
Definition: vpPolygon.cpp:400
bool isInside(const vpImagePoint &iP, const PointInPolygonMethod &method=PnPolyRayCasting) const
Definition: vpPolygon.cpp:303
unsigned int getSize() const
Definition: vpPolygon.cpp:566
Defines a generic 2D polygon.
Definition: vpPolygon.h:99
void set_i(const double ii)
Definition: vpImagePoint.h:163
void updateBoundingBox()
Definition: vpPolygon.cpp:479
PointInPolygonMethod
Definition: vpPolygon.h:114
Generic class defining intrinsic camera parameters.
void setTopLeft(const vpImagePoint &topLeft)
Definition: vpRect.h:301
void set_j(const double jj)
Definition: vpImagePoint.h:174
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
std::vector< vpImagePoint > _corners
Collection of image points containing the corners.
Definition: vpPolygon.h:103
void initClick(const vpImage< unsigned char > &I)
Definition: vpPolygon.cpp:193
bool _goodPoly
Flag set to true when the polygon is a good polygon (ie. it has more than two corners) or false other...
Definition: vpPolygon.h:109
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:88
Class for generating random numbers with uniform probability density.
Definition: vpUniRand.h:64
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1)
void updateCenter()
Definition: vpPolygon.cpp:434
void setBottomRight(const vpImagePoint &bottomRight)
Definition: vpRect.h:234
void buildFrom(const std::vector< vpImagePoint > &corners)
Definition: vpPolygon.cpp:147
double _area
Area of the polygon.
Definition: vpPolygon.h:107
void display(const vpImage< unsigned char > &I, const vpColor &color, unsigned int thickness=1) const
Definition: vpPolygon.cpp:517