测试一个点是否在给定的多边形内部,边缘或者外部。
OpenCV提供的API
- 说明
函数确定是否给定的点在轮廓的外部或者内部或者在边缘线上。函数返回正数(点在轮廓内部)、负数(点在轮廓外部)、零(点在边缘上)。当measureDist参数设定为false,返回值为+1、-1、0。否则,返回值为点与最邻近轮廓点的距离。 - 声明
double pointPolygonTest(
InputArray contour,
Point2f pt,
bool measureDist
);
- 参数
contour | 输入的轮廓 |
pt | 待测试的点。 |
measureDist | 设置为true时,返回实际距离值。若返回值为正,表示点在多边形内部,返回值为负,表示在多边形外部,返回值为0,表示在多边形上。 |
应用
步骤:
- 构建一张400x400大小的图片, Mat::Zero(400, 400, CV_8UC1)
- 画上一个六边形的闭合区域line
- 发现轮廓
- 对图像中所有像素点做点 多边形测试,得到距离,归一化后显示。
void pointPolyTest1() {
//1 绘制待处理的图像
const int r = 100;
Mat src = Mat::zeros(Size(400, 400), CV_8U);
//1.1 绘制一系列的点
vector<Point2f> points(6);
points[0] = Point(r * 3 / 2, static_cast<int>(1.34 * r));
points[1] = Point(r * 1, 2 * r);
points[2] = Point(r * 3 / 2, static_cast<int>(2.866 * r));
points[3] = Point(r * 5 / 2, static_cast<int>(2.866 * r));
points[4] = Point(r * 3, 2 * r);
points[5] = Point(r * 5 / 2, static_cast<int>(1.34 * r));
//1.2 绘制出由点连接而成的直线
for (int i = 0; i < 6; i++)
{
line(src, points[i], points[(i + 1) % 6], Scalar(255), 2);
}
//2 查找轮廓
vector<vector<Point>> contours;
Mat dst;
src.copyTo(dst);
findContours(dst, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
//3 测试是在轮廓内还是轮廓外
//测试要生成一张距离的图
Mat dst_dist = Mat::zeros(dst.size(), CV_32F);
//3.1 对每一个像素做多边形测试,得到了每一个点跟多边形的距离
for (size_t i = 0; i < dst.rows; i++)
{
for (size_t j = 0; j < dst.cols; j++)
{
//3.2 多边形测试,返回像素到轮廓的距离
double dist = pointPolygonTest(contours[0], Point2f(static_cast<float>(j), static_cast<float>(i)), true);
//3.3 每个像素点存放的是距离值
dst_dist.at<float>(i, j) = static_cast<float>(dist);
/*输出在轮廓内的所有点的距离
int z = 0;
if (dist>0)
{
cout << "[" << i << "," << j << "]: " << dist << " ";
z++;
if (z % 6 == 0)
cout << endl;
}
*/
}
}
//4 寻找最大、最小值、以及最大值的坐标
//由于最大的值为正数,肯定在轮廓内;以轮廓内距离轮廓最远的点为圆心,距离轮廓最远的值为半径画圆,圆一定是在多边形内部,即为多边形的最大内接圆的半径
double minValue, maxValue;
Point maxDistPt;
minMaxLoc(dst_dist, &minValue, &maxValue,NULL,&maxDistPt);
//5 根据最大最小值把像素重新算出来然后生成一张图
Mat drawImg = Mat::zeros(src.size(), CV_8UC3);
for (size_t i = 0; i < src.rows; i++)
{
for (size_t j = 0; j < src.cols; j++)
{
//5.1 对每一个像素获取距离值
float dist = dst_dist.at<float>(i, j);
if (dist < 0) {//在多边形的外部
// blue通道
//距离轮廓越远的点,颜色越偏向于蓝色
drawImg.at<Vec3b>(i, j)[0] = (uchar)(255 - abs(dist) * 255 / minValue);
}
else if (dist > 0) //在多边形的内部
{
// red通道--bgr
//距离轮廓越近的点,颜色越偏向于红色
drawImg.at<Vec3b>(i, j)[2] = (uchar)(255 - dist * 255 / maxValue);
}
else //在多边形的边界线上
{
//颜色为白色
drawImg.at<Vec3b>(i, j)[0] = 255;
drawImg.at<Vec3b>(i, j)[1] = 255;
drawImg.at<Vec3b>(i, j)[2] = 255;
}
}
}
circle(drawImg, maxDistPt, int(maxValue), Scalar(255, 255, 255),2);
imshow("src", src);
imshow("dst", drawImg);
waitKey(0);
}
void pointPolyTest(Mat &src){
//1 图片预处理
//1.1 图片转化成灰度图像
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
//1.2 图像模糊消除噪音
Mat blur_img;
blur(gray, blur_img, Size(3, 3), Point(-1, -1));
//1.3 图片转化成2值图像
Mat binary;
threshold(blur_img, binary, 127, 255, THRESH_BINARY | THRESH_OTSU);
//2 查找轮廓
vector<vector<Point>> contours;
Mat dst;
binary.copyTo(dst);
findContours(dst, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
drawContours(dst, contours, 1, Scalar::all(255), 2, 8);
//3 测试是在轮廓内还是轮廓外
//测试要生成一张距离的图
Mat dst_dist = Mat::zeros(dst.size(), CV_32F);
//3.1 对每一个像素做多边形测试,得到了每一个点跟多边形的距离
for (size_t i = 0; i < dst.rows; i++)
{
for (size_t j = 0; j < dst.cols; j++)
{
//3.2 多边形测试,返回像素到轮廓的距离
double dist = pointPolygonTest(contours[1], Point2f(static_cast<float>(j), static_cast<float>(i)), true);
//3.3 每个像素点存放的是距离值
dst_dist.at<float>(i, j) = static_cast<float>(dist);
}
}
//4 寻找最大、最小值、以及最大值的坐标
//由于最大的值为正数,肯定在轮廓内;以轮廓内距离轮廓最远的点为圆心,距离轮廓最远的值为半径画圆,圆一定是在多边形内部,即为多边形的最大内接圆的半径
double minValue, maxValue;
Point maxDistPt;
minMaxLoc(dst_dist, &minValue, &maxValue, NULL, &maxDistPt);
//5 根据最大最小值把像素重新算出来然后生成一张图
Mat drawImg = Mat::zeros(src.size(), CV_8UC3);
for (size_t i = 0; i < src.rows; i++)
{
for (size_t j = 0; j < src.cols; j++)
{
//5.1 对每一个像素获取距离值
float dist = dst_dist.at<float>(i, j);
if (dist < 0) {//在多边形的外部
// blue通道
//距离轮廓越远的点,颜色越偏向于蓝色
drawImg.at<Vec3b>(i, j)[0] = (uchar)(255 - abs(dist) * 255 / minValue);
}
else if (dist > 0) //在多边形的内部
{
// red通道--bgr
//距离轮廓越近的点,颜色越偏向于红色
drawImg.at<Vec3b>(i, j)[2] = (uchar)(255 - dist * 255 / maxValue);
}
else //在多边形的边界线上
{
//颜色为白色
drawImg.at<Vec3b>(i, j)[0] = 255;
drawImg.at<Vec3b>(i, j)[1] = 255;
drawImg.at<Vec3b>(i, j)[2] = 255;
}
}
}
circle(drawImg, maxDistPt, int(maxValue), Scalar(255, 255, 255), 2);
imshow("src", src);
imshow("dst", drawImg);
waitKey(0);
}