Visual Servoing Platform  version 3.6.1 under development (2025-01-25)
vpPolygon.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
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 https://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 
34 // System
35 #include <limits>
36 #include <set>
37 
38 // Core
39 #include <visp3/core/vpDisplay.h>
40 #include <visp3/core/vpException.h>
41 #include <visp3/core/vpMeterPixelConversion.h>
42 #include <visp3/core/vpPolygon.h>
43 #include <visp3/core/vpUniRand.h>
44 
45 // Local helper
46 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
47 
48 #include <opencv2/imgproc/imgproc.hpp>
49 
50 
56 template <typename IpContainer> std::vector<VISP_NAMESPACE_ADDRESSING vpImagePoint> convexHull(const IpContainer &ips)
57 {
58 #ifdef ENABLE_VISP_NAMESPACE
59  using namespace VISP_NAMESPACE_NAME;
60 #endif
61  if (ips.size() == 0) {
63  "Convex Hull can not be computed as the input does not contain any image point.");
64  }
65 
66  // Visp -> CV
67  std::vector<cv::Point> cv_pts;
68 #if ((__cplusplus >= 201402L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L))) // Check if cxx14 or higher
69  std::transform(cbegin(ips), cend(ips), std::back_inserter(cv_pts), [](const vpImagePoint &ip) {
70  return cv::Point(static_cast<int>(ip.get_u()), static_cast<int>(ip.get_v()));
71  });
72 #elif ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
73  std::transform(begin(ips), end(ips), std::back_inserter(cv_pts), [](const vpImagePoint &ip) {
74  return cv::Point(static_cast<int>(ip.get_u()), static_cast<int>(ip.get_v()));
75  });
76 #else // cxx98
77  for (typename IpContainer::const_iterator it = ips.begin(); it != ips.end(); ++it) {
78  cv_pts.push_back(cv::Point(static_cast<int>(it->get_u()), static_cast<int>(it->get_v())));
79  }
80 #endif
81 
82  // Get convex hull from OpenCV
83  std::vector<cv::Point> cv_conv_hull_corners;
84  cv::convexHull(cv_pts, cv_conv_hull_corners);
85 
86  // CV -> Visp
87  std::vector<vpImagePoint> conv_hull_corners;
88 #if ((__cplusplus >= 201402L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L))) // Check if cxx14 or higher
89  std::transform(cbegin(cv_conv_hull_corners), cend(cv_conv_hull_corners), std::back_inserter(conv_hull_corners),
90  [](const cv::Point &pt) {
91  return vpImagePoint { static_cast<double>(pt.y), static_cast<double>(pt.x) };
92  });
93 #elif ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
94  std::transform(begin(cv_conv_hull_corners), end(cv_conv_hull_corners), std::back_inserter(conv_hull_corners),
95  [](const cv::Point &pt) {
96  return vpImagePoint { static_cast<double>(pt.y), static_cast<double>(pt.x) };
97  });
98 #else // cxx98
99  for (std::vector<cv::Point>::const_iterator it = cv_conv_hull_corners.begin(); it != cv_conv_hull_corners.end();
100  ++it) {
101  conv_hull_corners.push_back(vpImagePoint(static_cast<double>(it->y), static_cast<double>(it->x)));
102  }
103 #endif
104 
105  return conv_hull_corners;
106 }
107 
108 #endif
109 
110 BEGIN_VISP_NAMESPACE
115  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
116 { }
117 
126 vpPolygon::vpPolygon(const std::vector<vpImagePoint> &corners)
127  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
128 {
129  const unsigned int val_3 = 3;
130  if (corners.size() < val_3) {
131  _goodPoly = false;
132  }
133  init(corners);
134 }
135 
144 vpPolygon::vpPolygon(const std::list<vpImagePoint> &corners)
145  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
146 {
147  const unsigned int val_3 = 3;
148  if (corners.size() < val_3) {
149  _goodPoly = false;
150  }
151  init(corners);
152 }
153 
160  : _corners(poly._corners), _center(poly._center), _area(poly._area), _goodPoly(poly._goodPoly), _bbox(poly._bbox),
161  m_PnPolyConstants(poly.m_PnPolyConstants), m_PnPolyMultiples(poly.m_PnPolyMultiples)
162 { }
163 
168 
175 {
176  _corners = poly._corners;
177  _center = poly._center;
178  _area = poly._area;
179  _goodPoly = poly._goodPoly;
180  _bbox = poly._bbox;
181  m_PnPolyConstants = poly.m_PnPolyConstants;
182  m_PnPolyMultiples = poly.m_PnPolyMultiples;
183  return *this;
184 }
185 
195 vpPolygon &vpPolygon::buildFrom(const std::vector<vpImagePoint> &corners, const bool &create_convex_hull)
196 {
197  if (create_convex_hull) {
198 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
199  init(convexHull(corners));
200 #else
201  vpException(vpException::notImplementedError, "Cannot build a convex hull without OpenCV imgproc module");
202 #endif
203  }
204  else {
205  init(corners);
206  }
207  return *this;
208 }
209 
219 vpPolygon &vpPolygon::buildFrom(const std::list<vpImagePoint> &corners, const bool &create_convex_hull)
220 {
221  if (create_convex_hull) {
222 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
223  init(convexHull(corners));
224 #else
225  vpException(vpException::notImplementedError, "Cannot build a convex hull without OpenCV imgproc module");
226 #endif
227  }
228  else {
229  init(corners);
230  }
231  return *this;
232 }
233 
246 vpPolygon &vpPolygon::buildFrom(const std::vector<vpPoint> &corners, const vpCameraParameters &cam,
247  const bool &create_convex_hull)
248 {
249  std::vector<vpImagePoint> ipCorners(corners.size());
250  size_t corners_size = corners.size();
251  for (size_t i = 0; i < corners_size; ++i) {
252  vpMeterPixelConversion::convertPoint(cam, corners[i].get_x(), corners[i].get_y(), ipCorners[i]);
253  }
254  buildFrom(ipCorners, create_convex_hull);
255  return *this;
256 }
257 
267 void vpPolygon::initClick(const vpImage<unsigned char> &I, unsigned int size, const vpColor &color,
268  unsigned int thickness)
269 {
271  vpImagePoint ip;
272 
273  std::vector<vpImagePoint> cornersClick;
274 
275  while (button == vpMouseButton::button1) {
276  bool ret = vpDisplay::getClick(I, ip, button, true);
277  if (ret && (button == vpMouseButton::button1)) {
278  vpDisplay::displayCross(I, ip, size, color, thickness);
279  cornersClick.push_back(ip);
280  vpDisplay::flush(I);
281  }
282  }
283 
284  buildFrom(cornersClick);
285 }
286 
296 void vpPolygon::initClick(const vpImage<vpRGBa> &I, unsigned int size, const vpColor &color, unsigned int thickness)
297 {
299  vpImagePoint ip;
300 
301  std::vector<vpImagePoint> cornersClick;
302 
303  while (button == vpMouseButton::button1) {
304  bool ret = vpDisplay::getClick(I, ip, button, true);
305  if (ret && (button == vpMouseButton::button1)) {
306  vpDisplay::displayCross(I, ip, size, color, thickness);
307  cornersClick.push_back(ip);
308  vpDisplay::flush(I);
309  }
310  }
311 
312  buildFrom(cornersClick);
313 }
314 
324 void vpPolygon::init(const std::vector<vpImagePoint> &corners)
325 {
326  _corners = corners;
327 
329  updateArea();
330  updateCenter();
331 
332  precalcValuesPnPoly();
333 }
334 
344 void vpPolygon::init(const std::list<vpImagePoint> &corners)
345 {
346  _corners.insert(_corners.begin(), corners.begin(), corners.end());
347 
349  updateArea();
350  updateCenter();
351 
352  precalcValuesPnPoly();
353 }
354 
366 bool vpPolygon::testIntersectionSegments(const vpImagePoint &ip1, const vpImagePoint &ip2, const vpImagePoint &ip3,
367  const vpImagePoint &ip4) const
368 {
369  double di1 = ip2.get_i() - ip1.get_i();
370  double dj1 = ip2.get_j() - ip1.get_j();
371 
372  double di2 = ip4.get_i() - ip3.get_i();
373  double dj2 = ip4.get_j() - ip3.get_j();
374 
375  double denominator = (di1 * dj2) - (dj1 * di2);
376 
377  if (fabs(denominator) < std::numeric_limits<double>::epsilon()) {
378  throw vpException(vpException::divideByZeroError, "Denominator is null, lines are parallels");
379  }
380 
381  double alpha = -(((ip1.get_i() - ip3.get_i()) * dj2) + (di2 * (ip3.get_j() - ip1.get_j()))) / denominator;
382  if ((alpha < 0) || (alpha >= 1)) {
383  return false;
384  }
385 
386  double beta = -((di1 * (ip3.get_j() - ip1.get_j())) + (dj1 * (ip1.get_i() - ip3.get_i()))) / denominator;
387  if ((beta < 0) || (beta >= 1)) {
388  return false;
389  }
390 
391  return true;
392 }
393 
402 bool vpPolygon::isInside(const vpImagePoint &ip, const PointInPolygonMethod &method) const
403 {
404  const int val_1000 = 1000;
405  const unsigned int val_3 = 3;
406  if (_corners.size() < val_3) {
407  return false;
408  }
409 
410  bool test = false;
411  // comment: previous implementation: switch method
412  // case PnPolySegmentIntersection:
413  if (method == PnPolySegmentIntersection) {
414  vpImagePoint infPoint(100000, 100000); // take a point at 'infinity'
415  // we add random since it appears that sometimes infPoint may cause a degenerated case (so relaunch and
416  // hope that result will be different).
417  vpUniRand generator;
418  infPoint.set_i(infPoint.get_i() + (val_1000 * generator()));
419  infPoint.set_j(infPoint.get_j() + (val_1000 * generator()));
420 
421  bool oddNbIntersections = false;
422  size_t v_corners_size = _corners.size();
423  for (size_t i = 0; i < v_corners_size; ++i) {
424  vpImagePoint ip1 = _corners[i];
425  vpImagePoint ip2 = _corners[(i + 1) % _corners.size()];
426  bool intersection = false;
427 
428  // If the points are the same we continue without trying to found
429  // an intersection
430  if (ip1 == ip2) {
431  // continue
432  }
433  else {
434  try {
435  intersection = testIntersectionSegments(ip1, ip2, ip, infPoint);
436  }
437  catch (...) {
438  return isInside(ip);
439  }
440 
441  if (intersection) {
442  oddNbIntersections = !oddNbIntersections;
443  }
444  }
445  }
446 
447  test = oddNbIntersections;
448  }
449  else {
450  // Reference: http://alienryderflex.com/polygon/
451  // comment: case PnPolyRayCasting
452  // comment: default
453  bool oddNodes = false;
454  size_t v_corners_size = _corners.size();
455  size_t j = v_corners_size - 1;
456  for (size_t i = 0; i < v_corners_size; ++i) {
457  if (((_corners[i].get_v() < ip.get_v()) && (_corners[j].get_v() >= ip.get_v())) ||
458  ((_corners[j].get_v() < ip.get_v()) && (_corners[i].get_v() >= ip.get_v()))) {
459  oddNodes ^= ( ((ip.get_v() * m_PnPolyMultiples[i]) + m_PnPolyConstants[i]) < ip.get_u());
460  }
461 
462  j = i;
463  }
464 
465  test = oddNodes;
466  // comment: }
467  // comment: break
468  }
469 
470  return test;
471 }
472 
473 void vpPolygon::precalcValuesPnPoly()
474 {
475  const std::size_t val_3 = 3;
476  if (_corners.size() < val_3) {
477  return;
478  }
479 
480  m_PnPolyConstants.resize(_corners.size());
481  m_PnPolyMultiples.resize(_corners.size());
482 
483  size_t v_corners_size = _corners.size();
484  size_t j = v_corners_size - 1;
485  for (size_t i = 0; i < v_corners_size; ++i) {
486  if (vpMath::equal(_corners[j].get_v(), _corners[i].get_v(), std::numeric_limits<double>::epsilon())) {
487  m_PnPolyConstants[i] = _corners[i].get_u();
488  m_PnPolyMultiples[i] = 0.0;
489  }
490  else {
491  m_PnPolyConstants[i] = (_corners[i].get_u() -
492  ((_corners[i].get_v() * _corners[j].get_u()) / (_corners[j].get_v() - _corners[i].get_v()))) +
493  ((_corners[i].get_v() * _corners[i].get_u()) / (_corners[j].get_v() - _corners[i].get_v()));
494  m_PnPolyMultiples[i] = (_corners[j].get_u() - _corners[i].get_u()) / (_corners[j].get_v() - _corners[i].get_v());
495  }
496 
497  j = i;
498  }
499 }
500 
511 {
512  if (_corners.size() == 0) {
513  _area = 0;
514  _goodPoly = false;
515  return;
516  }
517  _area = 0;
518 
519  size_t v_corners_size = _corners.size();
520  for (size_t i = 0; i < v_corners_size; ++i) {
521  size_t i_p_1 = (i + 1) % _corners.size();
522  _area += (_corners[i].get_j() * _corners[i_p_1].get_i()) - (_corners[i_p_1].get_j() * _corners[i].get_i());
523  }
524 
525  const int val_2 = 2;
526  _area /= val_2;
527  if (_area < 0) {
528  _area = -_area;
529  }
530 }
531 
544 {
545  if (_corners.size() == 0) {
546  _center = vpImagePoint(0, 0);
547  _goodPoly = false;
548  return;
549  }
550  double i_tmp = 0;
551  double j_tmp = 0;
552 
553  size_t v_corners_size = _corners.size();
554  for (size_t i = 0; i < v_corners_size; ++i) {
555  size_t i_p_1 = (i + 1) % _corners.size();
556  i_tmp += (_corners[i].get_i() + _corners[i_p_1].get_i()) *
557  ((_corners[i_p_1].get_i() * _corners[i].get_j()) - (_corners[i_p_1].get_j() * _corners[i].get_i()));
558 
559  j_tmp += (_corners[i].get_j() + _corners[i_p_1].get_j()) *
560  ((_corners[i_p_1].get_i() * _corners[i].get_j()) - (_corners[i_p_1].get_j() * _corners[i].get_i()));
561  }
562 
563  const int val_6 = 6;
564  if (_area > 0) {
565  _center.set_i(fabs(i_tmp / (val_6 * _area)));
566  _center.set_j(fabs(j_tmp / (val_6 * _area)));
567  }
568  else {
569  _center = _corners[0];
570  _goodPoly = false;
571  }
572 }
573 
580 {
581  if (_corners.size() == 0) {
584  _goodPoly = false;
585  return;
586  }
587 
588  std::set<double> setI;
589  std::set<double> setJ;
590  unsigned int v_corners_size = static_cast<unsigned int>(_corners.size());
591  for (unsigned int i = 0; i < v_corners_size; ++i) {
592  setI.insert(_corners[i].get_i());
593  setJ.insert(_corners[i].get_j());
594  }
595  vpImagePoint tl(*(setI.begin()), *(setJ.begin()));
596  std::set<double>::const_iterator iterI = setI.end();
597  std::set<double>::const_iterator iterJ = setJ.end();
598  --iterI;
599  --iterJ;
600  vpImagePoint br(*iterI, *iterJ);
601 
602  if (tl == br) {
603  _goodPoly = false;
604  }
605  _bbox.setTopLeft(tl);
606  _bbox.setBottomRight(br);
607 }
608 
617 void vpPolygon::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness) const
618 {
619  const size_t N = _corners.size();
620  for (size_t i = 0; i < N; ++i) {
621  vpDisplay::displayLine(I, _corners[i], _corners[(i + 1) % N], color, thickness);
622  }
623 }
624 
636 bool vpPolygon::isInside(const std::vector<vpImagePoint> &roi, const double &i, const double &j,
637  const PointInPolygonMethod &method)
638 {
639  vpPolygon poly(roi);
640  return poly.isInside(vpImagePoint(i, j), method);
641 }
642 
646 unsigned int vpPolygon::getSize() const
647 {
648  return (static_cast<unsigned int>(_corners.size()));
649 }
650 END_VISP_NAMESPACE
Generic class defining intrinsic camera parameters.
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
static void flush(const vpImage< unsigned char > &I)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
@ notImplementedError
Not implemented.
Definition: vpException.h:69
@ divideByZeroError
Division by zero.
Definition: vpException.h:70
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
void set_j(double jj)
Definition: vpImagePoint.h:309
double get_j() const
Definition: vpImagePoint.h:125
void set_i(double ii)
Definition: vpImagePoint.h:298
double get_u() const
Definition: vpImagePoint.h:136
double get_i() const
Definition: vpImagePoint.h:114
double get_v() const
Definition: vpImagePoint.h:147
static bool equal(double x, double y, double threshold=0.001)
Definition: vpMath.h:459
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
Defines a generic 2D polygon.
Definition: vpPolygon.h:103
void display(const vpImage< unsigned char > &I, const vpColor &color, unsigned int thickness=1) const
Definition: vpPolygon.cpp:617
bool _goodPoly
Definition: vpPolygon.h:189
void updateBoundingBox()
Definition: vpPolygon.cpp:579
vpPolygon & buildFrom(const std::vector< vpImagePoint > &corners, const bool &create_convex_hull=false)
Definition: vpPolygon.cpp:195
void init(const std::vector< vpImagePoint > &corners)
Definition: vpPolygon.cpp:324
void updateArea()
Definition: vpPolygon.cpp:510
void updateCenter()
Definition: vpPolygon.cpp:543
vpImagePoint _center
Definition: vpPolygon.h:184
vpRect _bbox
Bounding box containing the polygon.
Definition: vpPolygon.h:191
PointInPolygonMethod
Definition: vpPolygon.h:106
@ PnPolySegmentIntersection
Definition: vpPolygon.h:107
virtual ~vpPolygon()
Definition: vpPolygon.cpp:167
unsigned int getSize() const
Definition: vpPolygon.cpp:646
std::vector< vpImagePoint > _corners
Collection of image points containing the corners.
Definition: vpPolygon.h:181
vpPolygon & operator=(const vpPolygon &poly)
Definition: vpPolygon.cpp:174
double _area
Area of the polygon.
Definition: vpPolygon.h:186
void initClick(const vpImage< unsigned char > &I, unsigned int size=5, const vpColor &color=vpColor::red, unsigned int thickness=1)
Definition: vpPolygon.cpp:267
bool isInside(const vpImagePoint &iP, const PointInPolygonMethod &method=PnPolyRayCasting) const
Definition: vpPolygon.cpp:402
void setBottomRight(const vpImagePoint &bottomRight)
Definition: vpRect.h:296
void setTopLeft(const vpImagePoint &topLeft)
Definition: vpRect.h:366
Class for generating random numbers with uniform probability density.
Definition: vpUniRand.h:127