Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpPolygon.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 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
56template <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
126vpPolygon::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
144vpPolygon::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
195vpPolygon &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
219vpPolygon &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
246vpPolygon &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
267void 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);
281 }
282 }
283
284 buildFrom(cornersClick);
285}
286
296void 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);
309 }
310 }
311
312 buildFrom(cornersClick);
313}
314
324void vpPolygon::init(const std::vector<vpImagePoint> &corners)
325{
326 _corners = corners;
327
329 updateArea();
330 updateCenter();
331
332 precalcValuesPnPoly();
333}
334
344void 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
366bool 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
402bool 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
473void 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) {
582 _bbox.setBottomRight(vpImagePoint(0, 0));
583 _bbox.setTopLeft(vpImagePoint(0, 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
617void 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
636bool 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
646unsigned int vpPolygon::getSize() const
647{
648 return (static_cast<unsigned int>(_corners.size()));
649}
650END_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 ...
void set_j(double jj)
double get_j() const
void set_i(double ii)
double get_u() const
double get_i() const
double get_v() const
Definition of the vpImage class member functions.
Definition vpImage.h:131
static bool equal(double x, double y, double threshold=0.001)
Definition vpMath.h:470
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
void display(const vpImage< unsigned char > &I, const vpColor &color, unsigned int thickness=1) const
bool _goodPoly
Definition vpPolygon.h:189
void updateBoundingBox()
vpPolygon & buildFrom(const std::vector< vpImagePoint > &corners, const bool &create_convex_hull=false)
void init(const std::vector< vpImagePoint > &corners)
void updateArea()
void updateCenter()
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()
unsigned int getSize() const
std::vector< vpImagePoint > _corners
Collection of image points containing the corners.
Definition vpPolygon.h:181
vpPolygon & operator=(const vpPolygon &poly)
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)
bool isInside(const vpImagePoint &iP, const PointInPolygonMethod &method=PnPolyRayCasting) const
Class for generating random numbers with uniform probability density.
Definition vpUniRand.h:127