今天依然是研究图像特征检测算法的一天,不得不说,前辈的智慧真是无穷无尽,想出了那么多种算法,真是太强了,本小菜鸟只是学习一点皮毛都得研究好久。。。

好了步入正题,今天要整理的笔记内容是获取轮廓的内接圆。

内接圆的定义是:与多边形各边都相切的圆,就称为该多边形的内接圆,每个正多边形都存在一个唯一的内接圆。从定义上来看,内切圆是和多边形的边相切的,但是轮廓大多数都是不规则的,很难找到一个和轮廓各边都相切的圆,甚至说连轮廓的各条边都很难确定下来。可能这也是OpenCV中没有直接通过轮廓来获取轮廓内接圆的API的原因之一吧。

虽然OpenCV中没有直接的API可以使用,但是通过内接圆的定义,我们可以知道,如果能把轮廓看成一个多边形,那么再来求它的内接圆就会方便得多。所以,我们将一个不规则的轮廓看成一个具有许许多多短边的多边形,也就是变成一个不规则多边形。

而且,既然要求轮廓的内接圆,从圆的特点来说,想要唯一的确定一个圆,就是要知道它的圆心和半径。好的,那现在的问题就从求取轮廓的内接圆,巧妙地转变成求取某个点和一个多边形的距离和关系。

很棒的是,在上一篇笔记OpenCV(26)中,记录了一个点多边形检测的APIpointPolygonTest(),这个API能够得到某个点和某个多边形之间的关系,例如这个点是在多边形内部、外部、或者是在多边形上,还能得到该点距离多边形的像素距离。

那么,当我们使用这个API计算轮廓内的所有像素点和该轮廓的距离,然后进行距离统计:找到轮廓内部距离轮廓线最远的一个点。则该点就是这个轮廓内接圆的圆心,这个距离的绝对值就是轮廓内接圆的半径。

也就是说,如果设置pointPolygonTest()返回其像素距离而且这个像素距离是当前点距离轮廓最近的距离,那么当这个点在轮廓内部(与轮廓距离为正数),且其返回的距离是最大值的时候,这个距离就是轮廓的最大内接圆的半径,该点就是最大内接圆的圆心。既然已经知道了圆心和半径,我们就可以唯一地确定一个圆,这就是轮廓的最大内接圆。

下面看一下实现代码:

Mat src = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\contours.png");
	Mat src_gray, src_gaus, src_binary;
	cvtColor(src, src_gray, COLOR_BGR2GRAY);
	GaussianBlur(src_gray, src_gaus, Size(), 1, 1);
	threshold(src_gaus, src_binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(src_binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	for (int i = 0; i < contours.size(); i++)
	{
		Mat  dist = Mat::zeros(src.size(), CV_32F);		//定义一个Mat对象,存放原图中每个点到该轮廓的距离,为浮点型数据
		//遍历每个点,计算该点到轮廓距离
		for (int row = 0; row < src.rows; row++)
		{
			for (int col = 0; col < src.cols; col++)
			{
				//通过点多边形检测计算获得点到轮廓距离,并存放至dist中
				dist.at<float>(row, col) = pointPolygonTest(contours[i], Point(col, row), true);
			}
		}

		//计算dist中,最大值和最小值,以及其位置坐标
		double minVal, maxVal;
		Point maxloc, minloc;
		minMaxLoc(dist, &minVal, &maxVal, &minloc, &maxloc);
		int radio = abs(maxVal);			//对最大值求绝对值,即为内接圆半径
		Point center;
		center = maxloc;			//某点与轮廓距离为最大值,则该点为内接圆圆心
		circle(src, center, radio, Scalar(0, 0, 255), 2, 8, 0);

	}
		imshow("src", src);

上述代码中,首先将输入图像经过转灰度图、高斯模糊、二值化来预处理图像,然后进行轮廓检测。将检测到的轮廓进行遍历,然后对图像中的每个点去进行点多边形检测,获得该点到当前轮廓的距离。再把每个点到轮廓的距离存放在一个和原图像尺寸相同的Mat对象中,这个Mat对象的每个元素都是一个距离值。计算完所有像素点的距离值后,获取其中最大的距离值以及对应的点坐标,这个距离值就是最大内接圆的半径,点坐标就是最大内接圆的圆心。然后我们就可以通过圆心和半径将轮廓的最大内接圆绘制出来了。

下面是绘制出来的轮廓最大内接圆的效果图:

图像处理 求最大内接矩形算法和python实现 opencv最大内接圆_API


由于这里检测出来的轮廓是最外层轮廓,所以获取到的也是外层轮廓的最大内接圆。可以看到,通过这种委婉的方式求得的轮廓最大内接圆,还是能有比较好的效果的。

今天的笔记就记录到这里啦,谢谢阅读~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!