二值图像分析:二值图像轮廓提取

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:表示输入图像,它必须是二值图像,二值图像可以由thresholdadaptiveThresholdCannyinRange等方法得到。
  • 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)

参数解释:

扫描二维码关注公众号,回复: 12680038 查看本文章
  • 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

猜你喜欢

转载自blog.csdn.net/PecoHe/article/details/114206980