二值图像分析:二值图像轮廓提取
- 1.OpenCV中的图像轮廓
- 2.轮廓提取相关API总结
- 2.1 轮廓发现findContours
- 2.2 轮廓绘制drawContours
- 2.3 轮廓外接矩形获取boundingRect和minAreaRect
- 2.4 轮廓面积与弧长获取
- 2.代码实践
1.OpenCV中的图像轮廓
一个轮廓对应一系列的点(cv::Point()
),这些点以某种方式表示图像中的一条曲线。在OpenCV
中,轮廓通过STL
中的vector
表示,向量中的每一个值包含轮廓上下一个点的位置信息。
2.轮廓提取相关API总结
2.1 轮廓发现findContours
OpenCV
提供findContours()
函数来获取二值图像的轮廓拓扑信息,其函数原型如下:
void cv::findContours(InputOutputArray image,OutputArrayOfArrays contours,
OutputArray hierarchy,int mode,int method,Point offset = Point())
参数解释:
-
image
:表示输入图像,它必须是二值图像,二值图像可以由threshold
、adaptiveThreshold
、Canny
、inRange
等方法得到。 -
Contours
:用来获取轮廓,每个轮廓是一系列的点(cv::Point
类)集合。 -
Hierarchy
:用来保存轮廓的层次信息,因为一个轮廓里可能包含另一个轮廓,每个轮廓有四个相关信息,分别是同层下一个、前一个、第一个子节点以及父节点。 -
mode
:表示轮廓寻找时候的拓扑结构返回,常用的有2种选择:RETR_EXTERNAL
表示只返回最外层轮廓,RETR_TREE
表示返回轮廓树结构。 -
Method
:表示轮廓点集合取得是基于什么算法,常见的是基于CHAIN_APPROX_SIMPLE
链式编码方法。
2.2 轮廓绘制drawContours
对于得到轮廓,可以通过下面的API绘制每个轮廓:
void cv::drawContours(InputOutputArray image,InputArrayOfArrays contours,
int contourIdx,const Scalar& color,int thickness = 1,
int lineType = LINE_8,InputArray hierarchy = noArray(),
int maxLevel = INT_MAX,Point offset = Point())
-
image
:为绘制到的图像。 -
contours
:为轮廓集合,当中每个元素为一个轮廓。 -
contourIdx
:为 绘制contours
中序号对应的轮廓,为-1时则表示绘制所有轮廓。 -
color
:绘制使用的颜色。 -
thickness
:为正数的时候表示绘制该轮廓,为-1表示填充该轮廓。
2.3 轮廓外接矩形获取boundingRect和minAreaRect
对于二值图像的每个轮廓,OpenCV
提供了API可以求取轮廓的外接矩形,其中求取轮廓外接矩形有两种方式:最大外接矩形和最小外接矩形。
最大外接矩形API如下:
Rect cv::boundingRect(InputArray points)
参数解释:
-
points
可以一系列点的集合,对轮廓来说就是该轮廓的点集 - 返回结果是一个矩形,包含坐标及长宽等信息。
最小外接矩形API如下:
RotatedRect cv::minAreaRect(InputArray point)
参数解释:
-
points
可以一系列点的集合,对轮廓来说就是该轮廓的点集 - 返回结果是一个旋转矩形,包含下面的信息:矩形中心位置cx,cy;矩形的宽高h,w;旋转角度。
2.4 轮廓面积与弧长获取
对于二值图像的每个轮廓,可以计算轮廓的弧长与面积,然后根据轮廓的面积与弧长可以实现对不同大小对象的过滤,寻找到感兴趣的区域。OpenCV
提供了对轮廓点集计算面积的API,其原理是基于格林公式。
OpenCV
对轮廓点集计算面积的API函数如下:
double cv::contourArea(InputArray contour,bool oriented=false)
参数解释:
-
contour
:表示输入的轮廓点集。 -
oriented
:默认是false
,返回的面积是正数,如果方向参数为true
表示会根据是顺时针或者逆时针方向返回正值或者负值面积。
OpenCV
计算轮廓曲线的弧长的API函数如下:
double cv::arcLength(InputArray curve,bool closed )
参数解释:
-
curve
:表示输入的轮廓点集。 -
closed
:默认表示是否闭合区域。
2.代码实践
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat srcImage = imread("/mnt/hgfs/winshare/images/circles.jpg");
if(srcImage.empty())
{
cout<<"load image failed."<<endl;
return -1;
}
imshow("src",srcImage);
Mat dstImage,blurImage,grayImage,binaryImage;
//二值化图像
GaussianBlur(srcImage,blurImage,Size(3,3),0,0);
cvtColor(blurImage,grayImage,COLOR_BGR2GRAY);
threshold(grayImage,binaryImage,200,255,THRESH_BINARY_INV);
imshow("binary",binaryImage);
//轮廓提取
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binaryImage,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());
Mat dst1=srcImage.clone();
Mat dst2=srcImage.clone();
for(int i=0;i<contours.size();++i)
{
//绘制轮廓
drawContours(dst1,contours,i,Scalar(0,0,255),-1,8);
drawContours(dst2,contours,i,Scalar(0,0,255),1,8);
//最大外接矩形
Rect rect = boundingRect(contours[i]);
rectangle(srcImage,rect,Scalar(255,0,0),1,8,0);
//最小外接矩形
RotatedRect rrt = minAreaRect(contours[i]);
Point2f pts[4];
rrt.points(pts);
for(int i=0;i<4;++i)
{
line(srcImage,pts[i%4],pts[(i+1)%4],Scalar(0,255,0),2,8,0);
}
//中心点绘制
Point2f cpt = rrt.center;
circle(srcImage,cpt,2,Scalar(255,0,0),2,8,0);
//获取轮廓面积和弧长
cout<<"Area "<<i<<" = "<<contourArea(contours[i])<<",Length "<<i<<" = "<<arcLength(contours[i],true)<<endl;
}
imshow("dst",srcImage);
imshow("ticks=-1,contours",dst1);
imshow("ticks=1,contours",dst2);
#endif
waitKey(0);
return 0;
}
运行结果:
输入图像:
二值化图像:
绘制轮廓:
填充绘制轮廓:
最大和最小外接矩形绘制:
输出轮廓面积和弧长:
Area 0 = 25870,Length 0 = 602.156
Area 1 = 13916,Length 1 = 440.96
Area 2 = 1529,Length 2 = 147.196
Area 3 = 5761,Length 3 = 283.764