相关概念
多边形测试(Point-in-Polygon Test)是计算机图形学和几何学中的一个经典问题,用于确定一个给定点是否位于一个多边形内部。这个测试在地理信息系统、计算机图形学、游戏开发和其他领域中非常有用。
如下方这张图,左边是给定一个多边形 ,右边图像每个像素点到这个多边形的距离分布,在多边形内部显示蓝色,距离越远,颜色越深,在多边形边上显示白色,多边形外部显示红色,距离越远颜色越深。
代码示例
void pot_test(){
// 创建一个400x400的单通道图像,初始值为黑色(0)
cv::Mat image = cv::Mat::zeros(400, 400, CV_8UC1);
// 定义六边形的顶点
std::vector<cv::Point> hexagon = {
cv::Point(200, 100),
cv::Point(300, 150),
cv::Point(300, 250),
cv::Point(200, 300),
cv::Point(100, 250),
cv::Point(100, 150)
};
// 绘制六边形
cv::polylines(image, hexagon, true, cv::Scalar(255), 2);
// 寻找轮廓
std::vector<std::vector<cv::Point>> contours;
cv::findContours(image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
// 定义要测试的点 (从图片中遍历每个像素点的位置) 这里定义为32位浮点数 是因为Mat 矩阵的值为每个位置到多边形的最短位置
Mat raw_dist =Mat::zeros(image.size(),CV_32FC1);
for (int row = 0; row < raw_dist.rows; ++row) {
for (int col = 0; col < raw_dist.cols; ++col) {
//最后一个参数代表要计算距离
double dist = pointPolygonTest(contours[0],Point2f(row,col), true);
raw_dist.at<float>(row,col) =dist;
}
}
double minval,maxVal;
//得到距离最大值和最小值
minMaxLoc(raw_dist,&minval,&maxVal);
cout<<"max distance is "<<maxVal<<" min distance is "<<minval;
Mat draw_img = Mat::zeros(image.size(),CV_8UC3);
for (int row = 0; row < draw_img.rows; ++row) {
for (int col = 0; col < draw_img.cols; ++col) {
//每个像素值其实是 就是点到多边形的距离
float dist = raw_dist.at<float>(row,col);
if(dist>0){
//在多边形内部 蓝色像素表示
draw_img.at<Vec3b>(row,col)[0] = (dist/maxVal)*255 ;
}else if(dist<0){
// 在多边形外部 红色像素表示
draw_img.at<Vec3b>(row,col)[2] = (dist/minval)*255 ;
}else{
//在边上显示白色
draw_img.at<Vec3b>(row,col)[0] =255 ;
draw_img.at<Vec3b>(row,col)[1] =255 ;
draw_img.at<Vec3b>(row,col)[2] =255 ;
}
}
}
cv::imshow("source img",image);
// 显示结果图像
cv::imshow("Hexagon and Test Point", draw_img);
cv::waitKey(0);
}
代码工作流程:确定一个多边形,进行轮廓寻找,确定多边形轮廓,生成与原图大小相等的距离矩阵,使用pointPolygonTest
计算每个像素点的位置的多边形距离。对计算的距离进行归一化处理,在图像上以彩色形式显示。
pointPolygonTest 函数原型
CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );
传入的参数:
1.轮廓检测结果数组,通常是通过findContours
得到结果
2.要测试的位置
3.是否计算距离
分析函数返回结果,官方文档介绍如下;
/** @brief Performs a point-in-contour test.
The function determines whether the point is inside a contour, outside, or lies on an edge (or
coincides with a vertex). It returns positive (inside), negative (outside), or zero (on an edge)
value, correspondingly. When measureDist=false , the return value is +1, -1, and 0, respectively.
Otherwise, the return value is a signed distance between the point and the nearest contour edge.
See below a sample output of the function where each image pixel is tested against the contour:
![sample output](pics/pointpolygon.png)
@param contour Input contour.
@param pt Point tested against the contour.
@param measureDist If true, the function estimates the signed distance from the point to the
nearest contour edge. Otherwise, the function only checks if the point is inside a contour or not.
*/
如果不进行计算距离,测试点在多边形内部返回+1 ,在多边形外部返回-1,在边上返回0
如果第二个参数为True,代表需要计算距离,那么在多边形内部返回正的浮点数,在外部返回负的浮点数。在边上返回0
数学原理
cv2.pointPolygonTest()
函数的数学原理主要基于几何学中的射线投射法(Ray-Casting Method)和最小距离计算。以下是该方法的核心原理:
1. 射线投射法
射线投射法是判断一个点是否在多边形内部的常用方法。其基本思想是从该点出发,沿某个方向(通常是水平或垂直方向)发射一条射线,计算这条射线与多边形边界的交点数量。
具体步骤如下:
- 从测试点发射一条射线(通常向右水平)。
- 计算射线与多边形每条边的交点数量。
- 根据交点数量判断点的位置:
- 如果交点数量为奇数,点在多边形内部。
- 如果交点数量为偶数,点在多边形外部。
在 cv2.pointPolygonTest()
中,这种方法被用来快速判断点的位置关系。
2. 距离计算
当 measureDist
参数为 True
时,cv2.pointPolygonTest()
还会计算点到多边形边界的有符号距离。这涉及计算点到多边形所有边的最短距离:
- 对于每一条边,计算点到边的垂直距离。
- 找出最小的垂直距离作为点到多边形的最近距离。
如果点在多边形内部,这个距离是正值;如果点在多边形外部,这个距离是负值;如果点在多边形边界上,这个距离是0。