opencv入门(16)--轮廓发现和凸包

16.1 轮廓发现

16.1.1 概述

轮廓发现(find contour)是基于图像边缘提取的基础寻找对象轮廓的方法。所以边缘提取的阈值选定会影响最终轮廓的发现结果。

16.1.2 相关API

findContours:发现轮廓

·void findContours( InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())

参数含义
作用在二值图像上,检测出物体轮廓
输入image

单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;8bit

contourscontours定义为“vector<vector> contours”,是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素;
hierarchy定义为“vector<Vec4i> hierarchy”,Vec4i的定义:typedef Vec<int, 4> Vec4i;(向量内每个元素都包含了4个int型变量),分别表示当前轮廓 i 的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓的编号索引。
mode

检索轮廓的模式:

RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略;
RETR_LIST:检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1。
RETR_CCOMP: 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层;
RETR_TREE: 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。

method

定义轮廓的近似方法:

CHAIN_APPROX_NONE:保存物体边界上所有连续的轮廓点到contours向量内;
CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留;
CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法;
CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法

offset轮廓像素的位移,默认(0,0)没有位移
返回值void

drawContours:绘制轮廓

·void drawContours( InputOutputArray image, OutputArrayOfArrays contours, int contourIdx, const Scalar &color, int thickness=1, int lineType=LINE_8,InputArray hierarchy=noArray(),int maxLevel=INT_MAX, Point offset=Point())

参数含义
作用绘制轮廓
输入image

输入输出图像

contourscontours定义为“vector<vector> contours”,是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素;
contourIdx轮廓索引号,如果为负值则绘制所有输入轮廓
color轮廓颜色
thickness绘制轮廓所用线条粗细度,如果值为负值,则在轮廓内部绘制
lineType线条类型,有默认值LINE_8
hierarchy定义为“vector<Vec4i> hierarchy”,Vec4i的定义:typedef Vec<int, 4> Vec4i;(向量内每个元素都包含了4个int型变量),分别表示当前轮廓 i 的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓的编号索引。
maxLevel最大层数,0只绘制当前的,1绘制当前及其内嵌的轮廓
offset轮廓像素的位移,默认(0,0)没有位移
返回值void

16.1.3 寻找轮廓方法介绍

在findContours中,不同的mode+method会有不同的效果

1)检测最外层轮廓,并且保存轮廓上所有点

mode取值“RETR_EXTERNAL”,method取值“CHAIN_APPROX_NONE”,即只检测最外层轮廓,并且保存轮廓上所有点;如下图只检测了最外层的轮廓,内层的轮廓被忽略。

轮廓:

点集:将轮廓上的所有点均保存下来

2)检测所有轮廓,但各轮廓之间彼此独立,不建立等级关系,并且仅保存轮廓上拐点信息

mode取值“RETR_LIST”,method取值“CHAIN_APPROX_SIMPLE”,即检测所有轮廓,但各轮廓之间彼此独立,不建立等级关系,并且仅保存轮廓上拐点信息:

轮廓:

点集:

3)检测所有轮廓,轮廓间建立外层、内层的等级关系,并且保存轮廓上所有点

mode取值“RETR_TREE”,method取值“CHAIN_APPROX_NONE”,即检测所有轮廓,轮廓间建立外层、内层的等级关系,并且保存轮廓上所有点。

轮廓:

点集:

16.1.4 代码示例

步骤:

  1. 将输入图像转为灰度图cvtColor
  2. 使用Canny进行轮廓提取,得到二值图
  3. 使用findContours寻找轮廓
  4. 使用drawContours绘制轮廓
Mat src,dst;
const char* OUTPUTWIN = "outputImg";
int thresholdValue = 100;
int thresholdMax = 255;
RNG rng(12345);
void CannyFindContours(int, void*)
{
	Mat brinaryImg;
	//canny边缘检测
	Canny(src, brinaryImg, thresholdValue, thresholdValue * 2,3,false);
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	//对边缘检测后的图像进行轮廓寻找
	findContours(brinaryImg, contours, hierachy, RETR_TREE, CHAIN_APPROX_NONE,Point(0,0));

	//绘制轮廓
	dst = Mat::zeros(src.size(),CV_8UC3);
	for (int i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(dst, contours,i, color,2,8, hierachy,0, Point(0, 0));
	}
	imshow(OUTPUTWIN, dst);
}


int main(int argc, char** argv)
{
	//源图像
	src = imread("D:\\testimg\\CSet12\\yingbi.png");
	if (src.empty())
	{
		printf("Could not load the image...\n");
		return -1;
	}
	else;

	namedWindow("inputImg", WINDOW_AUTOSIZE);
	imshow("inputImg", src);
	namedWindow(OUTPUTWIN, WINDOW_AUTOSIZE);

	cvtColor(src,src,COLOR_BGR2GRAY);

	const char* trackbarTitle = "threshold";
	createTrackbar(trackbarTitle, OUTPUTWIN, &thresholdValue, thresholdMax, CannyFindContours);
	CannyFindContours(0, 0);


	waitKey(0);
	return 0;
}

16.2 凸包

16.2.1 概述

凸包(Convex Hull)是指在一个多边形边缘或者内部任意两个点的连线都包含在多边形边界或者内部。包含点集合S中所有点的最小凸边形称为凸包。

16.2.2 Graham扫描检测算法

算法介绍:

  1. 首先选择Y方向最低的点作为起始点P0;
  2. 从P0开始极坐标扫描,依次添加P1....Pn(根据极坐标的角度大小,逆时针方向排序)
  3. 对每个点Pi来说,吐过添加Pi点到凸包中导致一个左转向(逆时针方法)则添加该点到凸包,繁殖是一个右转向(顺时针方向)不添加到凸包中

16.2.3 相关API

·void convexHull( InputArray points, OutputArray hull, bool clockwise=false,bool returnPoints=true)

参数含义
作用查找图像中物体的凸包
输入points输入的二维点集,Mat类型数据即可(可以从findContours中获取)
hull输出参数,用于输出函数调用后找到的凸包 
clockwise操作方向,当标识符为真时,输出凸包为顺时针方向,否则为逆时针方向。
returnPoints操作标识符,默认值为true,此时返回各凸包的各个点,否则返回凸包各点的指数,当输出数组时std::vector时,此标识被忽略。
返回值void

16.2.4 代码示例

Mat src, cannyImg,dst;
const char* OUTPUTWIN = "outputImg";
int thresholdValue = 100;
int thresholdMax = 255;
RNG rng(12345);
void CannyFindContours(int, void*)
{
	Mat brinaryImg;
	//canny边缘检测,(此外可以使用阈值分割等)
    //轮廓寻找取决于边缘检测的效果
	Canny(cannyImg, brinaryImg, thresholdValue, thresholdValue * 2,3,false);
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	//对边缘检测后的图像进行轮廓寻找
	findContours(brinaryImg, contours, hierachy, RETR_TREE, CHAIN_APPROX_NONE,Point(0,0));

    //计算凸包,每个轮廓独立计算
	vector<vector<Point>> hull(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		convexHull(contours[i], hull[i], false, true);
	}
	
	for (int i = 0; i < hull.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(src, hull, i, color, 2, 8, hierachy, 0, Point(0, 0));
	}
	imshow(OUTPUTWIN, src);
}


int main(int argc, char** argv)
{
	//源图像
	src = imread("D:\\testimg\\CSet12\\yingbi.png");
	if (src.empty())
	{
		printf("Could not load the image...\n");
		return -1;
	}
	else;

	namedWindow("inputImg", WINDOW_AUTOSIZE);
	imshow("inputImg", src);
	namedWindow(OUTPUTWIN, WINDOW_AUTOSIZE);

	cvtColor(src, cannyImg,COLOR_BGR2GRAY);

	const char* trackbarTitle = "threshold";
	createTrackbar(trackbarTitle, OUTPUTWIN, &thresholdValue, thresholdMax, CannyFindContours);
	CannyFindContours(0, 0);


	waitKey(0);
	return 0;
}

需要调整算法预处理,得到更好的边缘检测结果。

16.3 参考

参考:OpenCV findContours函数_杨 戬的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值