OpenCV笔记10——寻找轮廓和凸包以及绘制轮廓的外接矩形和圆

在这里插入图片描述

1. 寻找轮廓

1.1 相关API

在这里插入图片描述
说明:
在这里插入图片描述

  1. 第一个参数:输入的图像是经过边缘提取处理后的二值化图像;
  2. conturs向量是用来存储轮廓点的,可以这样理解:一个轮廓的所有点用一个小容器vector,所有小容器再用一个大容器vector装起来,所以像下面这样定义第二个参数:vector<vector<Point>> contours;,相当于是一个二维向量吧,如下:
    在这里插入图片描述
  3. 第三个参数是轮廓的索引值;
  4. 第四个参数:轮廓检索模式,有四种,如下:

第一种:cv::RETR_EXTERNAL(仅检索最外层的轮廓)
在这里插入图片描述
第二种:cv::RETR_LIST(检索所有轮廓并将它们放入列表中)
在这里插入图片描述

第三种:cv::RETR_CCOMP(检索所有轮廓,将它们组织为两级层次结构,其中顶层边界是组件的外部边界,第二级边界是孔的边界)
在这里插入图片描述
第四种:cv::RETR_CCOMP(检索所有轮廓并建立树形的嵌套层次结构)
在这里插入图片描述

5.第六个参数是轮廓逼近算法,有以下四种:

 CHAIN_APPROX_NONE
 CHAIN_APPROX_SIMPLE
 CHAIN_APPROX_TC89_L1
 CHAIN_APPROX_TC89_KCOS

1.2 步骤

1.图像灰度化处理
2.滤波去噪
3.二值化
4.形态学操作
5.寻找轮廓
6.绘制轮廓

1.3 实验代码

主函数:

int main()
{
    img = imread("E:/coin.jpg");
    cvtColor(img, img_gray, CV_BGR2GRAY);//图像灰度化
	medianBlur(img_gray, img_gray, 3);//进行中值滤波,去除一些噪声点
	createTrackbar("Threshold", str_OutputWindowTitle, &threshold_value, threshold_max, Find_Contours);
	Find_Contours(0, 0);
	
	waitKey(0);
	return 0;
}

寻找轮廓函数:

void Find_Contours(int, void*)
{
	//二值化
	threshold(img_gray, img_bin, threshold_value, threshold_max, THRESH_BINARY);
	
	//根据实际二值化的效果进行相应的形态学操作
	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
	morphologyEx(img_bin, img_bin, MORPH_CLOSE, element);//闭操作
	imshow("bin+morphologyEx", img_bin);
	
	//轮廓发现,轮廓绘制
	vector<vector<Point>> contours;//双层,意思是有很多个轮廓,每个轮廓有很多个点
	vector<Vec4i> hierachy;//4个元素分别存储该轮廓的【后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓】的索引编号
	findContours(img_bin, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	dst = Mat::zeros(img.size(), CV_8UC3);//新建一个图像用来绘制寻找到的轮廓
	
	//绘制所有轮廓像素点
	for (auto 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, 1, 8, hierachy, 0, Point(0, 0));//重新绘制轮廓
	}
	imshow(str_OutputWindowTitle, dst);
}

1.4 运行结果

在这里插入图片描述
适当调整阈值使其达到最好的效果:
在这里插入图片描述
根据提取到的轮廓点绘制找到的轮廓:
在这里插入图片描述


2. 凸包

2.1 凸包的原理

是一个计算几何(图形学)中的概念,凸包是一个过某些点作一个多边形,使这个多边形能把所有点都“包”起来。当这个多边形是凸多边形的时候,我们就叫它“凸包”。

举个形象的栗子:木板上定了十几个钉子,把一根橡皮筋撑大,套住所有钉子,然后让橡皮筋自然收缩,并繃紧,则橡皮筋形成的闭曲线就是凸包。

在这里插入图片描述
图OpenCV参考手册

更多关于“凸包”的理论知识参考下面的几篇文章:

1.什么是“凸包”

2.凸包问题——概述

3.数学:凸包算法详解

2.2 相关API

在这里插入图片描述

2.3 步骤

1.寻找轮廓
2.发现凸包
3.绘制凸包

2.4 实验代码

void Get_ConvexHull(int, void*)
{
	//二值化
	threshold(img_gray, img_bin, threshold_value, threshold_max, THRESH_BINARY);
	Mat element = getStructuringElement(MORPH_RECT, Size(9, 9));

	//根据实际二值化的效果进行相应的形态学操作
	morphologyEx(img_bin, img_bin, MORPH_CLOSE, element);
	imshow("bin+morphologyEx", img_bin);

	//轮廓发现
	vector<vector<Point>> contours;
	findContours(img_bin, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//发现轮廓

	//寻找凸包
	vector<vector<Point>> convex(contours.size());
	for (size_t i = 0; i < contours.size(); i++) 
	{
		convexHull(contours[i], convex[i]);
	}

	//绘制凸包
	dst = Mat::zeros(img.size(), CV_8UC3);
	for (size_t j = 0; j < contours.size(); j++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		//drawContours(dst, contours, j, color);//绘制轮廓,可以对比凸包
		drawContours(dst, convex, j, color);//绘制凸包
	}
	imshow(str_OutputWindowTitle, dst);
}

2.5 运行结果

第一组:
在这里插入图片描述
在这里插入图片描述


对照组1:
在这里插入图片描述


对照组2:
在这里插入图片描述


3. 在轮廓外绘制矩形和圆

3.1 相关API

API功能:使用多边形简化轮廓;

在这里插入图片描述
注:epsilon参数的意义是,这是原始多边形和最终近似多边形之间允许的最大偏差,取值越大得到的多边形越简单。
在这里插入图片描述

为了验证一下,取下图其中的一个轮廓做测试,直接执行findContours之后,该轮廓的总点数为85,如下图:
在这里插入图片描述
当执行approxPolyDP()之后,epsilon值取3的时候,简化后的轮廓有9个点,绘制出来的多边形是9个边,如下图:
在这里插入图片描述
epsilon值取7的时候,简化后的轮廓有6个点,绘制出来的多边形是6个边,如下图:
在这里插入图片描述


API功能:获取简化后多边形轮廓的外接矩形;
在这里插入图片描述
在这里插入图片描述


API功能:获取简化后多边形轮廓外接圆的“圆心坐标”和“半径”,注意数据类型;
在这里插入图片描述


API功能:获取简化后多边形轮廓的“最适合”椭圆;
在这里插入图片描述
在这里插入图片描述


API功能:获取简化后多边形轮廓的“最适合”(最小的)的矩形;

在这里插入图片描述
在这里插入图片描述

3.2 步骤

1.灰度化、滤波
2.二值化、相关形态学操作
3.寻找轮廓
4.轮廓简化
5.依次获取轮廓外接矩形、外接圆、最小矩形、最小椭圆;
6.绘制需要的形状

3.3 实验代码

void Get_PolyRrc_Cir(int, void*) 
{
	//二值化
	threshold(img_gray, img_bin, threshold_value, threshold_max, THRESH_BINARY_INV);

	//根据实际二值化的效果进行相应的形态学操作
	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
	morphologyEx(img_bin, img_bin, MORPH_CLOSE, element);
	imshow("bin+morphologyEx", img_bin);

	//寻找轮廓
	vector<vector<Point>>controus;
	findContours(img_bin, controus, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

	vector<vector<Point>> controus_ploy(controus.size());//简化后的多边形轮廓集合
	vector<Rect> poly_rects(controus.size());//轮廓外接矩形
	vector<Point2f>circles(controus.size());//轮廓外接圆圆心坐标
	vector<float> radius(controus.size());//轮廓外接圆半径
	vector<RotatedRect>min_ellipse(controus.size());//轮廓最终形成的最小椭圆
	vector<RotatedRect>min_Rects(controus.size());//轮廓最终形成的最小的旋转矩形
	Point2f All_Rec_Point[4];//存储旋转矩形的四个顶点

	for (size_t i = 0; i < controus.size(); i++) 
	{
		//轮廓简化
		approxPolyDP(Mat(controus[i]), controus_ploy[i], 7, true);

		poly_rects[i] = boundingRect(controus_ploy[i]);//获取轮廓周围最小矩形
		minEnclosingCircle(controus_ploy[i], circles[i], radius[i]);//获取轮廓周围最小圆
		if (controus_ploy[i].size() > 5) 
		{
			min_ellipse[i] = fitEllipse(controus_ploy[i]);//得到一个最小椭圆,若contours_ploy[i]的点数size小于5会报错
			min_Rects[i] = minAreaRect(controus_ploy[i]);//得到一个旋转的矩形
		}
	}

	//绘制外接矩形、圆、椭圆和旋转矩形
	img.copyTo(dst);
	for (size_t j = 0; j < controus.size(); j++) 
	{
		if (controus_ploy[j].size() > 4) 
		{
			//contours_ploy[i]的点数size小于5的,不处理
			Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
			rectangle(dst, poly_rects[j], color, 2);//画矩形
			circle(dst, circles[j], radius[j], color, 2);//画圆
			ellipse(dst, min_ellipse[j], color, 2);//画椭圆
			min_Rects[j].points(All_Rec_Point);
			//Rect min_Rec(All_Rec_Point[0], All_Rec_Point[2]);//只通过两点画出来的圆不是旋转的,哈哈
			//rectangle(dst, min_Rec, color, 2);
			for (int r = 0; r < 4; r++) 
			{
				line(dst, All_Rec_Point[r], All_Rec_Point[(r + 1) % 4], color, 2);//通过获得旋转矩形的四个顶点,画旋转矩形
			}
		}
	}
	imshow(str_OutputWindowTitle, dst);
}

3.4 运行结果

在这里插入图片描述


参考文章:
1.https://blog.csdn.net/weixin_41695564/article/details/80085012
2.https://blog.csdn.net/qq_26907755/article/details/81740206
3.https://www.cnblogs.com/little-monkey/p/7429381.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值