参考:
1、OpenCV: Structural Analysis and Shape Descriptors
3、pointPolygonTest() throws error for some reason - OpenCV Q&A Forum
网上挺多的示例是从像素图中查找出轮廓使用,我现在是知道多边形的点集要判断某点是否在多边形轮廓内。
要求contour是一个numpy数组或者一个OpenCV标量(即四元素vector Basic Structures — OpenCV 2.4.13.7 documentation
template<typename _Tp> class CV_EXPORTS Scalar_ : public Vec<_Tp, 4>
{
public:
//! various constructors
Scalar_();
Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);
>>> cv2.pointPolygonTest(polygon,(0.25,0.25),False)>>> polygon = [(0,0),(1,0),(0,1)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: contour is not a numpy array, neither a scalar
改用numpy:
>>> polygon = np.array([(0,0),(1,0),(0,1)],np.int32)
>>> pt = (0.25,0.25)
>>> binside = cv2.pointPolygonTest(polygon,pt,False)
>>> binside
1.0
使用python数据结构如列表构造numpy数组:
>>> import numpy as np
>>> import cv2
>>> polygon = [(0,0),(1,0),(0,1)]
>>> narr = np.array(polygon)
>>> pt = (0.25,0.25)
>>> cv2.pointPolygonTest(narr,pt,False)
1.0
>>> pt = (1,1)
>>> cv2.pointPolygonTest(narr,pt,False)
-1.0
>>> pt = (1,0)
>>> cv2.pointPolygonTest(narr,pt,False)
0.0
>>>
但如果contour的numpy数组或scalar元素的数据类型不是整数是float就会报错:
>>> contour = [(15,-5),(17,-4),(19,-5.9)]
>>> narr = np.array(contour)
>>> pt = (1,2)
>>> cv2.pointPolygonTest(narr,pt,False)
OpenCV Error: Assertion failed (total >= 0 && (depth == 4 || depth == 5)) in pointPolygonTest, file /tmp/binarydeb/ros-kinetic-opencv3-3.3.1/modules/imgproc/src/geometry.cpp, line 103
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
cv2.error: /tmp/binarydeb/ros-kinetic-opencv3-3.3.1/modules/imgproc/src/geometry.cpp:103: error: (-215) total >= 0 && (depth == 4 || depth == 5) in function pointPolygonTest
>>>
换成整数就没问题:
>>> contour = [(15,-5),(17,-4),(19,-6)]
>>> narr = np.array(contour)
>>> pt = (1,2)
>>> cv2.pointPolygonTest(narr,pt,False) #负整数没关系
-1.0
报错出处opencv3-3.3.1的geometry.cpp可在OpenCV的GitHub找到:
double cv::pointPolygonTest( InputArray _contour, Point2f pt, bool measureDist )
{
CV_INSTRUMENT_REGION()
double result = 0;
Mat contour = _contour.getMat();
int i, total = contour.checkVector(2), counter = 0;
int depth = contour.depth();
CV_Assert( total >= 0 && (depth == CV_32S || depth == CV_32F));
取contour元素深度,也就是数据类型,在Python中上例-6与-5.9分别是int和float,在我64位mac上都是4字节:
>>> sys.getsizeof(-5.9)
24
>>> sys.getsizeof(6)
24
但可能在通过CPython转到底层C++执行时会将-5.9转成8字节的double:
为了尽可能保证精度,我将contuor和pt的数据都乘个1000后取整,这对判断“点在多边形内外”的误差影响我可以接受,当double cv::pointPolygonTest( InputArray _contour, Point2f pt, bool measureDist )的measureDist为True,也就是计算点到多边形距离时将结果再除回1000就可以了,都是线性放大缩小。
20211011更新,构造np数组时指定元素类型为4字节即可
>>> contour = [(15,-5),(17,-4),(19,-5.9)]
>>> narr = np.array(contour,np.float32) # 构造np.array时指定元素类型为np.float32,4字节
>>> pt = (1,2)
>>> cv2.pointPolygonTest(narr,pt,False)
-1.0