Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
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 
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  if (corners.size() < 3) {
130  _goodPoly = false;
131  }
132  init(corners);
133 }
134 
143 vpPolygon::vpPolygon(const std::list<vpImagePoint> &corners)
144  : _corners(), _center(), _area(0.), _goodPoly(true), _bbox(), m_PnPolyConstants(), m_PnPolyMultiples()
145 {
146  if (corners.size() < 3) {
147  _goodPoly = false;
148  }
149  init(corners);
150 }
151 
158  : _corners(poly._corners), _center(poly._center), _area(poly._area), _goodPoly(poly._goodPoly), _bbox(poly._bbox),
159  m_PnPolyConstants(poly.m_PnPolyConstants), m_PnPolyMultiples(poly.m_PnPolyMultiples)
160 { }
161 
166 
173 {
174  _corners = poly._corners;
175  _center = poly._center;
176  _area = poly._area;
177  _goodPoly = poly._goodPoly;
178  _bbox = poly._bbox;
179  m_PnPolyConstants = poly.m_PnPolyConstants;
180  m_PnPolyMultiples = poly.m_PnPolyMultiples;
181  return *this;
182 }
183 
184 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
195 void vpPolygon::buildFrom(const std::vector<vpImagePoint> &corners, const bool create_convex_hull)
196 {
197  build(corners, create_convex_hull);
198 }
199 
210 void vpPolygon::buildFrom(const std::list<vpImagePoint> &corners, const bool create_convex_hull)
211 {
212  build(corners, create_convex_hull);
213 }
214 
228 void vpPolygon::buildFrom(const std::vector<vpPoint> &corners, const vpCameraParameters &cam,
229  const bool create_convex_hull)
230 {
231  build(corners, cam, create_convex_hull);
232 }
233 #endif
234 
244 vpPolygon &vpPolygon::build(const std::vector<vpImagePoint> &corners, const bool &create_convex_hull)
245 {
246  if (create_convex_hull) {
247 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
248  init(convexHull(corners));
249 #else
250  vpException(vpException::notImplementedError, "Cannot build a convex hull without OpenCV imgproc module");
251 #endif
252  }
253  else {
254  init(corners);
255  }
256  return *this;
257 }
258 
268 vpPolygon &vpPolygon::build(const std::list<vpImagePoint> &corners, const bool &create_convex_hull)
269 {
270  if (create_convex_hull) {
271 #if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC)
272  init(convexHull(corners));
273 #else
274  vpException(vpException::notImplementedError, "Cannot build a convex hull without OpenCV imgproc module");
275 #endif
276  }
277  else {
278  init(corners);
279  }
280  return *this;
281 }
282 
295 vpPolygon &vpPolygon::build(const std::vector<vpPoint> &corners, const vpCameraParameters &cam,
296  const bool &create_convex_hull)
297 {
298  std::vector<vpImagePoint> ipCorners(corners.size());
299  size_t corners_size = corners.size();
300  for (size_t i = 0; i < corners_size; ++i) {
301  vpMeterPixelConversion::convertPoint(cam, corners[i].get_x(), corners[i].get_y(), ipCorners[i]);
302  }
303  build(ipCorners, create_convex_hull);
304  return *this;
305 }
306 
316 void vpPolygon::initClick(const vpImage<unsigned char> &I, unsigned int size, const vpColor &color,
317  unsigned int thickness)
318 {
320  vpImagePoint ip;
321 
322  std::vector<vpImagePoint> cornersClick;
323 
324  while (button == vpMouseButton::button1) {
325  bool ret = vpDisplay::getClick(I, ip, button, true);
326  if (ret && (button == vpMouseButton::button1)) {
327  vpDisplay::displayCross(I, ip, size, color, thickness);
328  cornersClick.push_back(ip);
329  vpDisplay::flush(I);
330  }
331  }
332 
333  build(cornersClick);
334 }
335 
345 void vpPolygon::initClick(const vpImage<vpRGBa> &I, unsigned int size, const vpColor &color, unsigned int thickness)
346 {
348  vpImagePoint ip;
349 
350  std::vector<vpImagePoint> cornersClick;
351 
352  while (button == vpMouseButton::button1) {
353  bool ret = vpDisplay::getClick(I, ip, button, true);
354  if (ret && (button == vpMouseButton::button1)) {
355  vpDisplay::displayCross(I, ip, size, color, thickness);
356  cornersClick.push_back(ip);
357  vpDisplay::flush(I);
358  }
359  }
360 
361  build(cornersClick);
362 }
363 
373 void vpPolygon::init(const std::vector<vpImagePoint> &corners)
374 {
375  _corners = corners;
376 
378  updateArea();
379  updateCenter();
380 
381  precalcValuesPnPoly();
382 }
383 
393 void vpPolygon::init(const std::list<vpImagePoint> &corners)
394 {
395  _corners.insert(_corners.begin(), corners.begin(), corners.end());
396 
398  updateArea();
399  updateCenter();
400 
401  precalcValuesPnPoly();
402 }
403 
415 bool vpPolygon::testIntersectionSegments(const vpImagePoint &ip1, const vpImagePoint &ip2, const vpImagePoint &ip3,
416  const vpImagePoint &ip4) const
417 {
418  double di1 = ip2.get_i() - ip1.get_i();
419  double dj1 = ip2.get_j() - ip1.get_j();
420 
421  double di2 = ip4.get_i() - ip3.get_i();
422  double dj2 = ip4.get_j() - ip3.get_j();
423 
424  double denominator = (di1 * dj2) - (dj1 * di2);
425 
426  if (fabs(denominator) < std::numeric_limits<double>::epsilon()) {
427  throw vpException(vpException::divideByZeroError, "Denominator is null, lines are parallels");
428  }
429 
430  double alpha = -(((ip1.get_i() - ip3.get_i()) * dj2) + (di2 * (ip3.get_j() - ip1.get_j()))) / denominator;
431  if ((alpha < 0) || (alpha >= 1)) {
432  return false;
433  }
434 
435  double beta = -((di1 * (ip3.get_j() - ip1.get_j())) + (dj1 * (ip1.get_i() - ip3.get_i()))) / denominator;
436  if ((beta < 0) || (beta >= 1)) {
437  return false;
438  }
439 
440  return true;
441 }
442 
451 bool vpPolygon::isInside(const vpImagePoint &ip, const PointInPolygonMethod &method) const
452 {
453  if (_corners.size() < 3) {
454  return false;
455  }
456 
457  bool test = false;
458  // comment: previous implementation: switch method
459  // case PnPolySegmentIntersection:
460  if (method == PnPolySegmentIntersection) {
461  vpImagePoint infPoint(100000, 100000); // take a point at 'infinity'
462  // we add random since it appears that sometimes infPoint may cause a degenerated case (so relaunch and
463  // hope that result will be different).
464  vpUniRand generator;
465  infPoint.set_i(infPoint.get_i() + (1000 * generator()));
466  infPoint.set_j(infPoint.get_j() + (1000 * generator()));
467 
468  bool oddNbIntersections = false;
469  size_t v_corners_size = _corners.size();
470  for (size_t i = 0; i < v_corners_size; ++i) {
471  vpImagePoint ip1 = _corners[i];
472  vpImagePoint ip2 = _corners[(i + 1) % _corners.size()];
473  bool intersection = false;
474 
475  // If the points are the same we continue without trying to found
476  // an intersection
477  if (ip1 == ip2) {
478  // continue
479  }
480  else {
481  try {
482  intersection = testIntersectionSegments(ip1, ip2, ip, infPoint);
483  }
484  catch (...) {
485  return isInside(ip);
486  }
487 
488  if (intersection) {
489  oddNbIntersections = !oddNbIntersections;
490  }
491  }
492  }
493 
494  test = oddNbIntersections;
495  }
496  else {
497  // Reference: http://alienryderflex.com/polygon/
498  // comment: case PnPolyRayCasting
499  // comment: default
500  bool oddNodes = false;
501  size_t v_corners_size = _corners.size();
502  size_t j = v_corners_size - 1;
503  for (size_t i = 0; i < v_corners_size; ++i) {
504  if (((_corners[i].get_v() < ip.get_v()) && (_corners[j].get_v() >= ip.get_v())) ||
505  ((_corners[j].get_v() < ip.get_v()) && (_corners[i].get_v() >= ip.get_v()))) {
506  oddNodes ^= (ip.get_v() * m_PnPolyMultiples[i] + m_PnPolyConstants[i] < ip.get_u());
507  }
508 
509  j = i;
510  }
511 
512  test = oddNodes;
513  // comment: }
514  // comment: break
515  }
516 
517  return test;
518 }
519 
520 void vpPolygon::precalcValuesPnPoly()
521 {
522  if (_corners.size() < 3) {
523  return;
524  }
525 
526  m_PnPolyConstants.resize(_corners.size());
527  m_PnPolyMultiples.resize(_corners.size());
528 
529  size_t v_corners_size = _corners.size();
530  size_t j = v_corners_size - 1;
531  for (size_t i = 0; i < v_corners_size; ++i) {
532  if (vpMath::equal(_corners[j].get_v(), _corners[i].get_v(), std::numeric_limits<double>::epsilon())) {
533  m_PnPolyConstants[i] = _corners[i].get_u();
534  m_PnPolyMultiples[i] = 0.0;
535  }
536  else {
537  m_PnPolyConstants[i] = (_corners[i].get_u() -
538  ((_corners[i].get_v() * _corners[j].get_u()) / (_corners[j].get_v() - _corners[i].get_v()))) +
539  ((_corners[i].get_v() * _corners[i].get_u()) / (_corners[j].get_v() - _corners[i].get_v()));
540  m_PnPolyMultiples[i] = (_corners[j].get_u() - _corners[i].get_u()) / (_corners[j].get_v() - _corners[i].get_v());
541  }
542 
543  j = i;
544  }
545 }
546 
557 {
558  if (_corners.size() == 0) {
559  _area = 0;
560  _goodPoly = false;
561  return;
562  }
563  _area = 0;
564 
565  size_t v_corners_size = _corners.size();
566  for (size_t i = 0; i < v_corners_size; ++i) {
567  size_t i_p_1 = (i + 1) % _corners.size();
568  _area += (_corners[i].get_j() * _corners[i_p_1].get_i()) - (_corners[i_p_1].get_j() * _corners[i].get_i());
569  }
570 
571  _area /= 2;
572  if (_area < 0) {
573  _area = -_area;
574  }
575 }
576 
589 {
590  if (_corners.size() == 0) {
591  _center = vpImagePoint(0, 0);
592  _goodPoly = false;
593  return;
594  }
595  double i_tmp = 0;
596  double j_tmp = 0;
597 
598  size_t v_corners_size = _corners.size();
599  for (size_t i = 0; i < v_corners_size; ++i) {
600  size_t i_p_1 = (i + 1) % _corners.size();
601  i_tmp += (_corners[i].get_i() + _corners[i_p_1].get_i()) *
602  ((_corners[i_p_1].get_i() * _corners[i].get_j()) - (_corners[i_p_1].get_j() * _corners[i].get_i()));
603 
604  j_tmp += (_corners[i].get_j() + _corners[i_p_1].get_j()) *
605  ((_corners[i_p_1].get_i() * _corners[i].get_j()) - (_corners[i_p_1].get_j() * _corners[i].get_i()));
606  }
607 
608  if (_area > 0) {
609  _center.set_i(fabs(i_tmp / (6 * _area)));
610  _center.set_j(fabs(j_tmp / (6 * _area)));
611  }
612  else {
613  _center = _corners[0];
614  _goodPoly = false;
615  }
616 }
617 
624 {
625  if (_corners.size() == 0) {
628  _goodPoly = false;
629  return;
630  }
631 
632  std::set<double> setI;
633  std::set<double> setJ;
634  unsigned int v_corners_size = static_cast<unsigned int>(_corners.size());
635  for (unsigned int i = 0; i < v_corners_size; ++i) {
636  setI.insert(_corners[i].get_i());
637  setJ.insert(_corners[i].get_j());
638  }
639  vpImagePoint tl(*(setI.begin()), *(setJ.begin()));
640  std::set<double>::const_iterator iterI = setI.end();
641  std::set<double>::const_iterator iterJ = setJ.end();
642  --iterI;
643  --iterJ;
644  vpImagePoint br(*iterI, *iterJ);
645 
646  if (tl == br) {
647  _goodPoly = false;
648  }
649  _bbox.setTopLeft(tl);
650  _bbox.setBottomRight(br);
651 }
652 
661 void vpPolygon::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness) const
662 {
663  const size_t N = _corners.size();
664  for (size_t i = 0; i < N; ++i) {
665  vpDisplay::displayLine(I, _corners[i], _corners[(i + 1) % N], color, thickness);
666  }
667 }
668 
680 bool vpPolygon::isInside(const std::vector<vpImagePoint> &roi, const double &i, const double &j,
681  const PointInPolygonMethod &method)
682 {
683  vpPolygon poly(roi);
684  return poly.isInside(vpImagePoint(i, j), method);
685 }
686 
690 unsigned int vpPolygon::getSize() const
691 {
692  return (static_cast<unsigned int>(_corners.size()));
693 }
694 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:458
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:661
bool _goodPoly
Definition: vpPolygon.h:195
void updateBoundingBox()
Definition: vpPolygon.cpp:623
void init(const std::vector< vpImagePoint > &corners)
Definition: vpPolygon.cpp:373
void updateArea()
Definition: vpPolygon.cpp:556
vpPolygon & build(const std::vector< vpImagePoint > &corners, const bool &create_convex_hull=false)
Definition: vpPolygon.cpp:244
void updateCenter()
Definition: vpPolygon.cpp:588
vpImagePoint _center
Definition: vpPolygon.h:190
vpRect _bbox
Bounding box containing the polygon.
Definition: vpPolygon.h:197
PointInPolygonMethod
Definition: vpPolygon.h:106
@ PnPolySegmentIntersection
Definition: vpPolygon.h:107
VP_DEPRECATED void buildFrom(const std::vector< vpImagePoint > &corners, const bool create_convex_hull=false)
Definition: vpPolygon.cpp:195
virtual ~vpPolygon()
Definition: vpPolygon.cpp:165
unsigned int getSize() const
Definition: vpPolygon.cpp:690
std::vector< vpImagePoint > _corners
Collection of image points containing the corners.
Definition: vpPolygon.h:187
vpPolygon & operator=(const vpPolygon &poly)
Definition: vpPolygon.cpp:172
double _area
Area of the polygon.
Definition: vpPolygon.h:192
void initClick(const vpImage< unsigned char > &I, unsigned int size=5, const vpColor &color=vpColor::red, unsigned int thickness=1)
Definition: vpPolygon.cpp:316
bool isInside(const vpImagePoint &iP, const PointInPolygonMethod &method=PnPolyRayCasting) const
Definition: vpPolygon.cpp:451
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:125