概念介绍 - 点多边形测试
- 测试一个点是否在给定的多边形内部,边缘或者外部
API介绍 cv::pointPolygonTest
double pointPolygonTest( //返回数据是类型
InputArray contour,// 输入的轮廓
Point2f pt, // 测试点
bool measureDist // 是否返回距离值,如果是false,1表示在内面,0表示在边界上,-1表示在外部,true返回实际距离
)
演示代码-步骤
- 构建一张400x400大小的图片, Mat::Zero(400, 400, CV_8UC1)
- 画上一个六边形的闭合区域line
- 发现轮廓
- 对图像中所有像素点做点 多边形测试,得到距离,归一化后显示。
程序代码:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
int main( int argc, char** argv ){
// 绘制一张6边形的图像
const int r = 100;
Mat src = Mat::zeros(r * 4, r * 4, CV_8UC1);
// 绘制一系列点创建一个轮廓:
vector<Point2f> vert(6);
vert[0] = Point(3 * r / 2, static_cast<int>(1.34*r));
vert[1] = Point(1 * r, 2 * r);
vert[2] = Point(3 * r / 2, static_cast<int>(2.866*r));
vert[3] = Point(5 * r / 2, static_cast<int>(2.866*r));
vert[4] = Point(3 * r, 2 * r);
vert[5] = Point(5 * r / 2, static_cast<int>(1.34*r));
// 在src内部绘制,并显示出来
for (int i = 0; i < 6; i++) {
line(src, vert[i], vert[(i + 1) % 6], Scalar(255), 3, 8, 0);
}
char* source_window = "Source";
namedWindow( source_window, CV_WINDOW_AUTOSIZE );
imshow( source_window, src );
// 得到轮廓
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
//也可以写成:src.copyTo(src_copy);
Mat src_copy = src.clone();// findContours 会改动输入图像 src 中元素的值,所以这里需要完全复制一份src
//发现轮廓
findContours( src_copy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
// 计算到轮廓的距离,也可以写成:Mat raw_dist = Mat::zeros(src_copy.size(), CV_32FC1);
Mat raw_dist( src.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(static_cast<float>(col), static_cast<float>(row)), true);//点多边形测试
raw_dist.at<float>(row, col) = static_cast<float>(dist);
//raw_dist.at<float>(row,col) = pointPolygonTest( contours[0], Point2f(i,j), true );
}
}
//绘制原图轮廓
Mat dst = Mat::zeros(src.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++) {
drawContours(dst, contours, i, Scalar(0, i == 0 ? 0 : 255, 255), 2, 8, hierarchy, 0, Point(0, 0));
}
imshow("foundContours33", dst);
// 绘制距离色差图,类似于 距离变换
double minVal, maxVal;
minMaxLoc( raw_dist, &minVal, &maxVal, 0, 0, Mat() );//获取最大最小距离,方便颜色归一化到255之间
minVal = abs(minVal); maxVal = abs(maxVal);
// 图形化的显示距离
Mat drawImg = Mat::zeros( src.size(), CV_8UC3 );//用彩色图反差距离
for (int row = 0; row < drawImg.rows; row++) {
for (int col = 0; col < drawImg.cols; col++) {
float dist = raw_dist.at<float>(row, col);
if (dist > 0) { // 轮廓内部,越靠近轮廓中心点越黑
drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(1.0 - (dist / maxVal)) * 255);//蓝
}
else if (dist < 0) { // 轮廓外部,越远离轮廓越黑
drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(1.0 - (dist / minVal)) * 255);//红
}
else { // 轮廓边线上,白色
drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(255 - dist));
drawImg.at<Vec3b>(row, col)[1] = (uchar)(abs(255 - dist));
drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(255 - dist));
}
}
}
// 创建窗口显示结果
namedWindow( "Distance", CV_WINDOW_AUTOSIZE );
imshow( "Distance", drawImg );
/* //与上面“图形化的显示距离”代码的功能相同,只是不同的显示方法而已,可以忽略
// 图形化的显示距离
Mat drawing = Mat::zeros( src.size(), CV_8UC3 );//用彩色图反差距离
for( int j = 0; j < src.rows; j++ ){
for( int i = 0; i < src.cols; i++ ){
if( raw_dist.at<float>(j,i) < 0 ){
drawing.at<Vec3b>(j,i)[0] = 255 - (int) abs(raw_dist.at<float>(j,i))*255/minVal;
}else if( raw_dist.at<float>(j,i) > 0 ){
drawing.at<Vec3b>(j,i)[2] = 255 - (int) raw_dist.at<float>(j,i)*255/maxVal;
}else{
drawing.at<Vec3b>(j,i)[0] = 255; drawing.at<Vec3b>(j,i)[1] = 255; drawing.at<Vec3b>(j,i)[2] = 255;
}
}
}
// 创建窗口显示结果
namedWindow( "drawing", CV_WINDOW_AUTOSIZE );
imshow( "drawing", drawing );
*/
waitKey(0);
return(0);
}
运行截图: