Visual Servoing Platform  version 3.6.1 under development (2024-04-25)
vpPolygon.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 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 https://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 *****************************************************************************/
35 
36 // System
37 #include <limits>
38 #include <set>
39 
40 // Core
41 #include <visp3/core/vpDisplay.h>
42 #include <visp3/core/vpException.h>
43 #include <visp3/core/vpMeterPixelConversion.h>
44 #include <visp3/core/vpPolygon.h>
45 #include <visp3/core/vpUniRand.h>
46 
47 // Local helper
48 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
49 
50 #include <opencv2/imgproc/imgproc.hpp>
51 
57 template <typename IpContainer> std::vector<vpImagePoint> convexHull(const IpContainer &ips)
58 {
59  if (ips.size() == 0) {
61  "Convex Hull can not be computed as the input does not contain any image point.");
62  }
63 
64  // Visp -> CV
65  std::vector<cv::Point> cv_pts;
66 #if ((__cplusplus >= 201402L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L))) // Check if cxx14 or higher
67  std::transform(cbegin(ips), cend(ips), std::back_inserter(cv_pts), [](const vpImagePoint &ip) {
68  return cv::Point(static_cast<int>(ip.get_u()), static_cast<int>(ip.get_v()));
69  });
70 #elif ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
71  std::transform(begin(ips), end(ips), std::back_inserter(cv_pts), [](const vpImagePoint &ip) {
72  return cv::Point(static_cast<int>(ip.get_u()), static_cast<int>(ip.get_v()));
73  });
74 #else // cxx98
75  for (typename IpContainer::const_iterator it = ips.begin(); it != ips.end(); ++it) {
76  cv_pts.push_back(cv::Point(static_cast<int>(it->get_u()), static_cast<int>(it->get_v())));
77  }
78 #endif
79 
80  // Get convex hull from OpenCV
81  std::vector<cv::Point> cv_conv_hull_corners;
82  cv::convexHull(cv_pts, cv_conv_hull_corners);
83 
84  // CV -> Visp
85  std::vector<vpImagePoint> conv_hull_corners;
86 #if ((__cplusplus >= 201402L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L))) // Check if cxx14 or higher
87  std::transform(cbegin(cv_conv_hull_corners), cend(cv_conv_hull_corners), std::back_inserter(conv_hull_corners),
88  [](const cv::Point &pt) {
89  return vpImagePoint { static_cast<double>(pt.y), static_cast<double>(pt.x) };
90  });
91 #elif ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) // Check if cxx11 or higher
92  std::transform(begin(cv_conv_hull_corners), end(cv_conv_hull_corners), std::back_inserter(conv_hull_corners),
93  [](const cv::Point &pt) {
94  return vpImagePoint { static_cast<double>(pt.y), static_cast<double>(pt.x) };
95  });
96 #else // cxx98
97  for (std::vector<cv::Point>::const_iterator it = cv_conv_hull_corners.begin(); it != cv_conv_hull_corners.end();
98  ++it) {
99  conv_hull_corners.push_back(vpImagePoint(static_cast<double>(it->y), static_cast<double>(it->x)));
100  }
101 #endif
102 
103  return conv_hull_corners;
104 }
105 
106 #endif
107 
112  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
113 { }
114 
123 vpPolygon::vpPolygon(const std::vector<vpImagePoint> &corners)
124  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
125 {
126  if (corners.size() < 3) {
127  _goodPoly = false;
128  }
129  init(corners);
130 }
131 
140 vpPolygon::vpPolygon(const std::list<vpImagePoint> &corners)
141  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
142 {
143  if (corners.size() < 3) {
144  _goodPoly = false;
145  }
146  init(corners);
147 }
148 
155  : _corners(poly._corners), _center(poly._center), _area(poly._area), _goodPoly(poly._goodPoly), _bbox(poly._bbox),
156  m_PnPolyConstants(poly.m_PnPolyConstants), m_PnPolyMultiples(poly.m_PnPolyMultiples)
157 { }
158 
163 
170 {
171  _corners = poly._corners;
172  _center = poly._center;
173  _area = poly._area;
174  _goodPoly = poly._goodPoly;
175  _bbox = poly._bbox;
176  m_PnPolyConstants = poly.m_PnPolyConstants;
177  m_PnPolyMultiples = poly.m_PnPolyMultiples;
178  return *this;
179 }
180 
190 void vpPolygon::buildFrom(const std::vector<vpImagePoint> &corners, const bool create_convex_hull)
191 {
192  if (create_convex_hull) {
193 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
194  init(convexHull(corners));
195 #else
196  vpException(vpException::notImplementedError, "Cannot build a convex hull without OpenCV imgproc module");
197 #endif
198  }
199  else {
200  init(corners);
201  }
202 }
203 
213 void vpPolygon::buildFrom(const std::list<vpImagePoint> &corners, const bool create_convex_hull)
214 {
215  if (create_convex_hull) {
216 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
217  init(convexHull(corners));
218 #else
219  vpException(vpException::notImplementedError, "Cannot build a convex hull without OpenCV imgproc module");
220 #endif
221  }
222  else {
223  init(corners);
224  }
225 }
226 
239 void vpPolygon::buildFrom(const std::vector<vpPoint> &corners, const vpCameraParameters &cam,
240  const bool create_convex_hull)
241 {
242  std::vector<vpImagePoint> ipCorners(corners.size());
243  unsigned int corners_size = corners.size();
244  for (unsigned int i = 0; i < corners_size; ++i) {
245  vpMeterPixelConversion::convertPoint(cam, corners[i].get_x(), corners[i].get_y(), ipCorners[i]);
246  }
247  buildFrom(ipCorners, create_convex_hull);
248 }
249 
259 void vpPolygon::initClick(const vpImage<unsigned char> &I, unsigned int size, const vpColor &color,
260  unsigned int thickness)
261 {
263  vpImagePoint ip;
264 
265  std::vector<vpImagePoint> cornersClick;
266 
267  while (button == vpMouseButton::button1) {
268  bool ret = vpDisplay::getClick(I, ip, button, true);
269  if (ret && (button == vpMouseButton::button1)) {
270  vpDisplay::displayCross(I, ip, size, color, thickness);
271  cornersClick.push_back(ip);
272  vpDisplay::flush(I);
273  }
274  }
275 
276  buildFrom(cornersClick);
277 }
278 
288 void vpPolygon::initClick(const vpImage<vpRGBa> &I, unsigned int size, const vpColor &color, unsigned int thickness)
289 {
291  vpImagePoint ip;
292 
293  std::vector<vpImagePoint> cornersClick;
294 
295  while (button == vpMouseButton::button1) {
296  bool ret = vpDisplay::getClick(I, ip, button, true);
297  if (ret && (button == vpMouseButton::button1)) {
298  vpDisplay::displayCross(I, ip, size, color, thickness);
299  cornersClick.push_back(ip);
300  vpDisplay::flush(I);
301  }
302  }
303 
304  buildFrom(cornersClick);
305 }
306 
316 void vpPolygon::init(const std::vector<vpImagePoint> &corners)
317 {
318  _corners = corners;
319 
321  updateArea();
322  updateCenter();
323 
324  precalcValuesPnPoly();
325 }
326 
336 void vpPolygon::init(const std::list<vpImagePoint> &corners)
337 {
338  _corners.insert(_corners.begin(), corners.begin(), corners.end());
339 
341  updateArea();
342  updateCenter();
343 
344  precalcValuesPnPoly();
345 }
346 
358 bool vpPolygon::testIntersectionSegments(const vpImagePoint &ip1, const vpImagePoint &ip2, const vpImagePoint &ip3,
359  const vpImagePoint &ip4) const
360 {
361  double di1 = ip2.get_i() - ip1.get_i();
362  double dj1 = ip2.get_j() - ip1.get_j();
363 
364  double di2 = ip4.get_i() - ip3.get_i();
365  double dj2 = ip4.get_j() - ip3.get_j();
366 
367  double denominator = (di1 * dj2) - (dj1 * di2);
368 
369  if (fabs(denominator) < std::numeric_limits<double>::epsilon()) {
370  throw vpException(vpException::divideByZeroError, "Denominator is null, lines are parallels");
371  }
372 
373  double alpha = -(((ip1.get_i() - ip3.get_i()) * dj2) + (di2 * (ip3.get_j() - ip1.get_j()))) / denominator;
374  if ((alpha < 0) || (alpha >= 1)) {
375  return false;
376  }
377 
378  double beta = -((di1 * (ip3.get_j() - ip1.get_j())) + (dj1 * (ip1.get_i() - ip3.get_i()))) / denominator;
379  if ((beta < 0) || (beta >= 1)) {
380  return false;
381  }
382 
383  return true;
384 }
385 
394 bool vpPolygon::isInside(const vpImagePoint &ip, const PointInPolygonMethod &method) const
395 {
396  if (_corners.size() < 3) {
397  return false;
398  }
399 
400  bool test = false;
401  switch (method) {
403  vpImagePoint infPoint(100000, 100000); // take a point at 'infinity'
404  // we add random since it appears that sometimes infPoint may cause a degenerated case (so relaunch and
405  // hope that result will be different).
406  vpUniRand generator;
407  infPoint.set_i(infPoint.get_i() + (1000 * generator()));
408  infPoint.set_j(infPoint.get_j() + (1000 * generator()));
409 
410  bool oddNbIntersections = false;
411  unsigned int v_corners_size = _corners.size();
412  for (unsigned int i = 0; i < v_corners_size; ++i) {
413  vpImagePoint ip1 = _corners[i];
414  vpImagePoint ip2 = _corners[(i + 1) % _corners.size()];
415  bool intersection = false;
416 
417  // If the points are the same we continue without trying to found
418  // an intersection
419  if (ip1 == ip2) {
420  continue;
421  }
422 
423  try {
424  intersection = testIntersectionSegments(ip1, ip2, ip, infPoint);
425  }
426  catch (...) {
427  return isInside(ip);
428  }
429 
430  if (intersection) {
431  oddNbIntersections = !oddNbIntersections;
432  }
433  }
434 
435  test = oddNbIntersections;
436  }
437  break;
438 
439  // Reference: http://alienryderflex.com/polygon/
440  case PnPolyRayCasting:
441  default: {
442  bool oddNodes = false;
443  size_t v_corners_size = _corners.size();
444  for (size_t i = 0, j = (v_corners_size - 1); i < v_corners_size; ++i) {
445  if (((_corners[i].get_v() < ip.get_v()) && (_corners[j].get_v() >= ip.get_v())) ||
446  ((_corners[j].get_v() < ip.get_v()) && (_corners[i].get_v() >= ip.get_v()))) {
447  oddNodes ^= (ip.get_v() * m_PnPolyMultiples[i] + m_PnPolyConstants[i] < ip.get_u());
448  }
449 
450  j = i;
451  }
452 
453  test = oddNodes;
454  }
455  break;
456  }
457 
458  return test;
459 }
460 
461 void vpPolygon::precalcValuesPnPoly()
462 {
463  if (_corners.size() < 3) {
464  return;
465  }
466 
467  m_PnPolyConstants.resize(_corners.size());
468  m_PnPolyMultiples.resize(_corners.size());
469 
470  size_t v_corners_size = _corners.size();
471  for (size_t i = 0, j = (v_corners_size - 1); i < v_corners_size; ++i) {
472  if (vpMath::equal(_corners[j].get_v(), _corners[i].get_v(), std::numeric_limits<double>::epsilon())) {
473  m_PnPolyConstants[i] = _corners[i].get_u();
474  m_PnPolyMultiples[i] = 0.0;
475  }
476  else {
477  m_PnPolyConstants[i] = (_corners[i].get_u() -
478  ((_corners[i].get_v() * _corners[j].get_u()) / (_corners[j].get_v() - _corners[i].get_v()))) +
479  ((_corners[i].get_v() * _corners[i].get_u()) / (_corners[j].get_v() - _corners[i].get_v()));
480  m_PnPolyMultiples[i] = (_corners[j].get_u() - _corners[i].get_u()) / (_corners[j].get_v() - _corners[i].get_v());
481  }
482 
483  j = i;
484  }
485 }
486 
497 {
498  if (_corners.size() == 0) {
499  _area = 0;
500  _goodPoly = false;
501  return;
502  }
503  _area = 0;
504 
505  unsigned int v_corners_size = _corners.size();
506  for (unsigned int i = 0; i < v_corners_size; ++i) {
507  unsigned int i_p_1 = (i + 1) % _corners.size();
508  _area += (_corners[i].get_j() * _corners[i_p_1].get_i()) - (_corners[i_p_1].get_j() * _corners[i].get_i());
509  }
510 
511  _area /= 2;
512  if (_area < 0) {
513  _area = -_area;
514  }
515 }
516 
529 {
530  if (_corners.size() == 0) {
531  _center = vpImagePoint(0, 0);
532  _goodPoly = false;
533  return;
534  }
535  double i_tmp = 0;
536  double j_tmp = 0;
537 #if 0
538  for (unsigned int i = 0; i < (_corners.size() - 1); ++i) {
539  i_tmp += (_corners[i].get_i() + _corners[i + 1].get_i()) *
540  (_corners[i + 1].get_i() * _corners[i].get_j() - _corners[i + 1].get_j() * _corners[i].get_i());
541 
542  j_tmp += (_corners[i].get_j() + _corners[i + 1].get_j()) *
543  (_corners[i + 1].get_i() * _corners[i].get_j() - _corners[i + 1].get_j() * _corners[i].get_i());
544  }
545 #else
546  unsigned int v_corners_size = _corners.size();
547  for (unsigned int i = 0; i < v_corners_size; ++i) {
548  unsigned int i_p_1 = (i + 1) % _corners.size();
549  i_tmp += (_corners[i].get_i() + _corners[i_p_1].get_i()) *
550  ((_corners[i_p_1].get_i() * _corners[i].get_j()) - (_corners[i_p_1].get_j() * _corners[i].get_i()));
551 
552  j_tmp += (_corners[i].get_j() + _corners[i_p_1].get_j()) *
553  ((_corners[i_p_1].get_i() * _corners[i].get_j()) - (_corners[i_p_1].get_j() * _corners[i].get_i()));
554  }
555 #endif
556 
557  if (_area > 0) {
558  _center.set_i(fabs(i_tmp / (6 * _area)));
559  _center.set_j(fabs(j_tmp / (6 * _area)));
560  }
561  else {
562  _center = _corners[0];
563  _goodPoly = false;
564  }
565 }
566 
573 {
574  if (_corners.size() == 0) {
577  _goodPoly = false;
578  return;
579  }
580 
581  std::set<double> setI;
582  std::set<double> setJ;
583  unsigned int v_corners_size = _corners.size();
584  for (unsigned int i = 0; i < v_corners_size; ++i) {
585  setI.insert(_corners[i].get_i());
586  setJ.insert(_corners[i].get_j());
587  }
588  vpImagePoint tl(*(setI.begin()), *(setJ.begin()));
589  std::set<double>::const_iterator iterI = setI.end();
590  std::set<double>::const_iterator iterJ = setJ.end();
591  --iterI;
592  --iterJ;
593  vpImagePoint br(*iterI, *iterJ);
594 
595  if (tl == br) {
596  _goodPoly = false;
597  }
598  _bbox.setTopLeft(tl);
599  _bbox.setBottomRight(br);
600 }
601 
610 void vpPolygon::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness) const
611 {
612  const unsigned int N = static_cast<unsigned int>(_corners.size());
613  for (unsigned int i = 0; i < N; ++i) {
614  vpDisplay::displayLine(I, _corners[i], _corners[(i + 1) % N], color, thickness);
615  }
616 }
617 
629 bool vpPolygon::isInside(const std::vector<vpImagePoint> &roi, const double &i, const double &j,
630  const PointInPolygonMethod &method)
631 {
632  vpPolygon poly(roi);
633  return poly.isInside(vpImagePoint(i, j), method);
634 }
635 
639 unsigned int vpPolygon::getSize() const
640 {
641  return (static_cast<unsigned int>(_corners.size()));
642 }
Generic class defining intrinsic camera parameters.
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:152
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:59
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:85
@ notImplementedError
Not implemented.
Definition: vpException.h:81
@ divideByZeroError
Division by zero.
Definition: vpException.h:82
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:304
double get_j() const
Definition: vpImagePoint.h:125
void set_i(double ii)
Definition: vpImagePoint.h:293
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:449
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
Defines a generic 2D polygon.
Definition: vpPolygon.h:97
void display(const vpImage< unsigned char > &I, const vpColor &color, unsigned int thickness=1) const
Definition: vpPolygon.cpp:610
bool _goodPoly
Definition: vpPolygon.h:108
void updateBoundingBox()
Definition: vpPolygon.cpp:572
void init(const std::vector< vpImagePoint > &corners)
Definition: vpPolygon.cpp:316
void updateArea()
Definition: vpPolygon.cpp:496
void updateCenter()
Definition: vpPolygon.cpp:528
vpImagePoint _center
Definition: vpPolygon.h:103
vpRect _bbox
Bounding box containing the polygon.
Definition: vpPolygon.h:110
PointInPolygonMethod
Definition: vpPolygon.h:113
@ PnPolyRayCasting
Definition: vpPolygon.h:115
@ PnPolySegmentIntersection
Definition: vpPolygon.h:114
void buildFrom(const std::vector< vpImagePoint > &corners, const bool create_convex_hull=false)
Definition: vpPolygon.cpp:190
virtual ~vpPolygon()
Definition: vpPolygon.cpp:162
unsigned int getSize() const
Definition: vpPolygon.cpp:639
std::vector< vpImagePoint > _corners
Collection of image points containing the corners.
Definition: vpPolygon.h:100
vpPolygon & operator=(const vpPolygon &poly)
Definition: vpPolygon.cpp:169
double _area
Area of the polygon.
Definition: vpPolygon.h:105
void initClick(const vpImage< unsigned char > &I, unsigned int size=5, const vpColor &color=vpColor::red, unsigned int thickness=1)
Definition: vpPolygon.cpp:259
bool isInside(const vpImagePoint &iP, const PointInPolygonMethod &method=PnPolyRayCasting) const
Definition: vpPolygon.cpp:394
void setBottomRight(const vpImagePoint &bottomRight)
Definition: vpRect.h:293
void setTopLeft(const vpImagePoint &topLeft)
Definition: vpRect.h:363
Class for generating random numbers with uniform probability density.
Definition: vpUniRand.h:123