轮廓外接多边形
由于噪声和光照的影响,物体的轮廓会出现不规则的形状,不规则的轮廓形状不利于对图像内容进行分析
此时需要将物体的轮廓拟合成规则的几何形状,根据需求可以将图像轮廓拟合成矩形、多边形等
矩形是常见的几何形状,矩形的处理和分析方法也比较简单,OpenCV4提供了两个函数用于求取外接矩形
分别是求取轮廓最大外接矩形的boundingRect()函数和求取轮廓最小外接矩形的minAreaRect()函数
寻找轮廓外接最大矩形就是寻找轮廓X方向和Y方向两端的像素,该矩形的长与宽分别与图像的两条轴平行
boundingRect()函数
boundingRect()函数原型:
Rect boundingRect(InputArray array)
- array:表示输入的灰度图像或者二维点集,数据类型为vector< Point >或者Mat
该函数可以求取包含输入图像中物体轮廓或者二维点集的最大外接矩形,只有一个参数
该函数的返回值是一个Rect类型的变量,该变量可以直接用rectangle()函数绘制矩形
返回值共有4个参数,前两个参数是最大外接矩形左上角第一个像素的坐标,后两个参数表示最大外接矩形的宽与高
最小外接矩形的4条边都与轮廓相交,该矩形的旋转角度与轮廓的形状有关,多数情况下矩形的4条边不与图像的两条轴平行
minAreaRect()函数
minAreaRect()函数可以求取轮廓的最小外接矩形
minAreaRect()函数原型:
RotatedRect minAreaRect(InputArray points)
- points:表示输入的二维点集合
该函数可以根据输入的二维点集合计算最小外接矩形
返回值是RotatedRect类型的变量,含有矩形的中心位置,矩形的宽和高,以及矩形旋转的角度
RotatedRect类具有两个重要的方法和属性,可以输出矩形的4个顶点和中心坐标
输出4个顶点坐标的方法是points()
假设RotatedRect类的变量为rrect,可以通过rrect.points(points)命令进行读取,坐标存放的变量是Point2f类型的数组
输出矩形中心坐标的属性是center
角度angle,rrect.angle获得角度
假设RotatedRect类的变量为rrect,可以通过opt = rrect.center命令,其中坐标存放的变量是Point2f类型
示例程序:首先利用Canny算法提取图像边缘,之后通过膨胀算法将邻近的边缘连接成一个连通域
并提取每一个轮廓的最大外接矩形和最小外接矩形,最后在图像中绘制出矩形轮廓
示例:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("E:\\CLion\\opencv_xin\\SG\\SG_Rect.png");
if (img.empty())
{
cout << "error" << endl;
return -1;
}
Mat img1,img2;
img.copyTo(img1); //深拷贝用来绘制最大外接矩形
img.copyTo(img2); //深拷贝用来绘制最小外接矩形
imshow("原图",img);
//去噪声和二值化
Mat canny;
Canny(img,canny,80,160,3,false);
imshow("",canny);
//膨胀运算,将细小缝隙填补
Mat kernel = getStructuringElement(0,Size(3,3));
dilate(canny,canny,kernel);
imshow("膨胀之后",canny);
//轮廓发现与绘制
vector<vector<Point>> contours; //轮廓
vector<Vec4i> hierarchy; //存放轮廓结构变量
findContours(canny,contours,hierarchy,0,2,Point());
//寻找轮廓的外接矩形
for (int i = 0; i < contours.size(); i++)
{
//最大外接矩形
Rect rect = boundingRect(contours[i]);
rectangle(img1,rect,Scalar(0,0,255),2,8,0);
//最小外接矩形
RotatedRect rrect = minAreaRect(contours[i]);
Point2f points[4];
rrect.points(points); //读取最小外接矩形的4个顶点
Point2f cpt = rrect.center; //最小外接矩形的中心
//绘制旋转矩形与中心位置
for (int j = 0; j < 4; j++)
{
if(j == 3)
{
line(img2,points[j],points[0], Scalar(0,255,0),2,8,0);
break;
}
line(img2,points[j],points[j + 1], Scalar(0,255,0),2,8,0);
cout << points[j] << endl;
}
//绘制矩形的中心
circle(img,cpt,2,Scalar(255,0,0),2,8,0);
}
//输出绘制的外接矩形的结果
imshow("max",img1);
imshow("min",img2);
waitKey(0);
return 0;
}
\
approxPolyDDP()函数
有时用矩形逼近轮廓会造成较大的误差
如果寻找逼近轮廓的多边形,那么多边形围成的面积会更加接近真实的圆形轮廓面积
OpenCV4提供了approxPolyDDP()函数用于寻找逼近轮廓的多边形
approxPolyDDP()函数原型:
void approxPolyDDP(InputArray curve,
OutputArray approxCurve,
double epsilon,
bool closed
)
- curve:输入轮廓像素点
- approxCurve:多边形逼近结果,以多边形顶点坐标的形式给出
- epsilon:逼近的精度,即原始曲线和逼近曲线的最大距离
- closed:逼近曲线是否为封闭曲线的标志,true表示曲线封闭,即最后一个顶点与第一个顶点相连
该函数根据输入的轮廓得到最佳的逼近多边形
第一个参数是输入的轮廓二维像素点,数据类型是vector< Point >或者Mat
第二个参数是多边形的逼近结果,以多边形顶点坐标的形式输出,是CV_32SC2类型的Nx1的Mat矩阵
可以通过输出结果的顶点数目初步判断轮廓的集合形状
第三个参数是多边形逼近时的精度,即原始曲线和逼近曲线之间的最大距离
第四个参数是逼近曲线是否为封闭曲线的标志,其中true表示曲线封闭,即最后一个顶点与第一个顶点相连
示例程序:首先提取了图像的边缘,然后对边缘进行膨胀运算,将靠近的边缘变成一个连通域,之后对边缘结果进行轮廓检测
并对每个轮廓进行多边形逼近,将逼近结果绘制在原图像中,并通过判断多边形顶点数目识别轮廓形状
示例:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
//绘制轮廓函数
void drawapp(Mat result,Mat img2)
{
for (int i = 0; i < result.rows; i++) \
{
//最后一个坐标点与第一个坐标点相连
if (i == result.rows -1)
{
Vec2i point1 = result.at<Vec2i>(i);
Vec2i point2 = result.at<Vec2i>(0);
line(img2,point1,point2,Scalar(0,0,255),2,8,0);
break;
}
Vec2i point1 = result.at<Vec2i>(i);
Vec2i point2 = result.at<Vec2i>(i + 1);
line(img2,point1,point2,Scalar(0,0,255),2,8,0);
}
}
int main()
{
Mat img = imread("E:\\CLion\\opencv_xin\\SG\\SG_Rect.png");
if (img.empty())
{
cout << "error" << endl;
return -1;
}
Mat canny;
imshow("img",img);
Canny(img,canny,80,160,3,false);
//膨胀运算
Mat kernel = getStructuringElement(0,Size(3,3));
dilate(canny,canny,kernel);
//轮廓发现与绘制
vector<vector<Point>> contours; //轮廓
vector<Vec4i> hierarchy; //存放轮廓结构变量
findContours(canny,contours,hierarchy,0,2,Point());
//绘制多边形
for (int i = 0; i < contours.size(); ++i)
{
//用最小外接矩形求取轮廓中心
RotatedRect rrect = minAreaRect(contours[i]);
Point2f center = rrect.center; //最小外接矩形的中心
circle(img,center,2,Scalar(255,0,0),2,8,0);
Mat result;
approxPolyDP(contours[i],result,4, true); //多边形逼近
drawapp(result,img);
cout << "corners:" << result.rows << endl;
}
imshow("result",img);
waitKey(0);
return 0;
}