C++版本OpenCv教程(四十六)轮廓外接多边形

由于噪声和光照的影响,物体的轮廓会出现不规则的形状,根据不规则的轮廓形状不利于对图像内容进行分析,此时需要将物体的轮廓拟合成规则的几何形状,根据需求可以将图像轮廓拟合成矩形、多边形等。本小节将介绍OpenCV 4中提供的轮廓外接多边形函数,实现图像中轮廓的形状拟合。

矩形是常见的几何形状,矩形的处理和分析方法也较为简单,OpenCV 4提供了两个函数求取轮廓外接矩形,分别是**求取轮廓最大外接矩形的boundingRect()函数和求取轮廓最小外接矩形的minAreaRect()**函数。

寻找轮廓外接最大矩形就是寻找轮廓X方向和Y方向两端的像素,该矩形长和宽分别与图像的两个轴平行。boundingRect()函数可以实现这个功能,该函数的函数原型在代码清单7-19中给出。

Rect cv::boundingRect(InputArray  array)
  • array:输入的灰度图像或者2D点集,数据类型为vector或者Mat。

该函数可以求取包含输入图像中物体轮廓或者2D点集的最大外接矩形,函数只有一个参数,可以是灰度图像或者2D点集,灰度图像的参数类型为Mat,2D点集的参数类型为vector或者Mat。该函数的返回值是一个Rect类型的变量,该变量可以直接用rectangle()函数绘制矩形。返回值共有四个参数,前两个参数是最大外接矩形左上角第一个像素的坐标,后两个参数分别表示最大外接矩形的宽和高。

最小外接矩形的四个边都与轮廓相交,该矩形的旋转角度与轮廓的形状有关,多数情况下矩形的四个边不与图像的两个轴平行。minAreaRect()函数可以求取轮廓的最小外接矩形,该函数的函数原型在代码清单7-20中给出。

RotatedRect cv::minAreaRect(InputArray  points)
  • points:输入的2D点集合

该函数可以根据输入的2D点集合计算最小的外接矩形,函数的返回值是RotatedRect类型的变量,含有矩形的中心位置、矩形的宽和高和矩形旋转的角度RotatedRect类具有两个重要的方法和属性,可以输出矩形的四个顶点和中心坐标。输出四个顶点坐标的方法是points(),假设RotatedRect类的变量为rrect,可以通过rrect.points(points)命令进行读取,其中坐标存放的变量是Point2f类型的数组。输出矩形中心坐标的属性是center,假设RotatedRect类的变量为rrect,可以通过opt=rrect.center命令进行读取,其中坐标存放的变量是Point2f类型的变量。

为了了解两个外接矩形函数的使用方法,代码清单7-21中给出了提取轮廓外接矩形的示例程序。程序中首先利用Canny算法提取图像边缘,之后通过膨胀算法将邻近的边缘连接成一个连通域,然后提取图像的轮廓,并提取每一个轮廓的最大外接矩形和最小外接矩形,最后在图像中绘制出矩形轮廓,程序的运行结果在图7-20给出。

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

int main()
{
	Mat img = imread("stuff.jpg");
	if (img.empty())
	{
		cout << "请确认图像文件名称是否正确" << endl;
		return -1;
	}
	Mat img1, img2;
	img.copyTo(img1);  //深拷贝用来绘制最大外接矩形
	img.copyTo(img2);  //深拷贝迎来绘制最小外接矩形
	imshow("img", img);

	// 去噪声与二值化
	Mat canny;
	Canny(img, canny, 80, 160, 3, false);
	imshow("", canny);

	//膨胀运算,将细小缝隙填补上
	Mat kernel = getStructuringElement(0, Size(3, 3));
	dilate(canny, canny, kernel);

	// 轮廓发现与绘制
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(canny, contours, hierarchy, 0, 2, Point());

	//寻找轮廓的外接矩形
	for (int n = 0; n < contours.size(); n++)
	{
		// 最大外接矩形
		Rect rect = boundingRect(contours[n]);
		rectangle(img1, rect, Scalar(0, 0, 255), 2, 8, 0);

		// 最小外接矩形
		RotatedRect rrect = minAreaRect(contours[n]);
		Point2f points[4];
		rrect.points(points);  //读取最小外接矩形的四个顶点
		Point2f cpt = rrect.center;  //最小外接矩形的中心

		// 绘制旋转矩形与中心位置
		for (int i = 0; i < 4; i++)
		{
			if (i == 3)
			{
				line(img2, points[i], points[0], Scalar(0, 255, 0), 2, 8, 0);
				break;
			}
			line(img2, points[i], points[i + 1], Scalar(0, 255, 0), 2, 8, 0);
		}
		//绘制矩形的中心
		circle(img, cpt, 2, Scalar(255, 0, 0), 2, 8, 0);
	}

	//输出绘制外接矩形的结果
	imshow("max", img1);
	imshow("min", img2);
	waitKey(0);
	return 0;
}

在这里插入图片描述

有时候用矩形逼近轮廓会造成较大的误差,例如图7-20中对于圆形轮廓的逼近矩形围成的面积比真实轮廓面积大,如果寻找逼近轮廓的多边形,那么多边形围成的面积会更加接近真实的圆形轮廓面积。OpenCV 4提供了**approxPolyDP()**函数用于寻找逼近轮廓的多边形,该函数的函数原型在代码清单7-22中给出。

void cv::approxPolyDP(InputArray  curve,
                      OutputArray  approxCurve,
                      double  epsilon,
                      bool  closed 
                      )
  • curve:输入轮廓像素点。
  • approxCurve:多边形逼近结果,以多边形顶点坐标的形式给出。
  • epsilon:逼近的精度,即原始曲线和逼近曲线之间的最大距离。
  • closed:逼近曲线是否为封闭曲线的标志, true表示曲线封闭,即最后一个顶点与第一个顶点相连。

该函数根据输入的轮廓得到最佳的逼近多边形。
函数的第一个参数是输入的轮廓2D像素点,数据类型是vector或者Mat。
第二个参数是多边形的逼近结果,以多边形顶点坐标的形式输出,是CV_32SC2类型的N×1的Mat类矩阵,可以通过输出结果的顶点数目初步判断轮廓的几何形状。
第三个参数是多边形逼近时的精度,即原始曲线和逼近曲线之间的最大距离。
第四个参数是逼近曲线是否为封闭曲线的标志, true表示曲线封闭,即最后一个顶点与第一个顶点相连。

为了了解该函数用法,在代码清单7-23中给出了对多个轮廓进行多边形逼近的示例程序。程序中首先提取了图像的边缘,然后对边缘进行腐蚀运算将靠近的边缘变成一个连通域,之后对边缘结果进行轮廓检测,并对每个轮廓进行多边形逼近,将逼近结果绘制在原图像中,并通过判断逼近多边形的顶点数目识别轮廓的形状,程序运行结果在图7-21给出。

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

//绘制轮廓函数
void drawapp(Mat result, Mat img2)
{
	for (int i = 0; i < result.rows; i++)
	{
		//最后一个坐标点与第一个坐标点连接
		if (i == result.rows - 1)
		{
			Vec2i point1 = result.at<Vec2i>(i);
			Vec2i point2 = result.at<Vec2i>(0);
			line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);
			break;
		}
		Vec2i point1 = result.at<Vec2i>(i);
		Vec2i point2 = result.at<Vec2i>(i + 1);
		line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);
	}
}

int main(int argc, const char *argv[])
{
	Mat img = imread("approx.png");
	if (img.empty())
	{
		cout << "请确认图像文件名称是否正确" << endl;
		return -1;
	}
	// 边缘检测
	Mat canny;
	Canny(img, canny, 80, 160, 3, false);
	//膨胀运算
	Mat kernel = getStructuringElement(0, Size(3, 3));
	dilate(canny, canny, kernel);

	// 轮廓发现与绘制
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(canny, contours, hierarchy, 0, 2, Point());

	//绘制多边形
	for (int t = 0; t < contours.size(); t++)
	{
		//用最小外接矩形求取轮廓中心
		RotatedRect rrect = minAreaRect(contours[t]);
		Point2f center = rrect.center;
		circle(img, center, 2, Scalar(0, 255, 0), 2, 8, 0);

		Mat result;
		approxPolyDP(contours[t], result, 4, true);  //多边形拟合
		drawapp(result, img);
		cout << "corners : " << result.rows << endl;

		//判断形状和绘制轮廓
		if (result.rows == 3)
		{
			putText(img, "triangle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
		}
		if (result.rows == 4)
		{
			putText(img, "rectangle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
		}
		if (result.rows == 8)
		{
			putText(img, "poly-8", center, 0, 1, Scalar(0, 255, 0), 1, 8);
		}
		if (result.rows > 12)
		{
			putText(img, "circle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
		}
	}
	imshow("result", img);
	waitKey(0);
	return 0;
}

在这里插入图片描述

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV中,轮廓外接多边形是一种常用的图像处理技术,它用于找到包围轮廓的最小外接多边形。下面是关于轮廓外接多边形的原理、作用和应用的简要介绍: 1. 原理: 轮廓外接多边形的原理是通过计算轮廓的凸包或最小包围矩形来确定外接多边形。凸包是包围所有轮廓点的最小凸多边形,而最小包围矩形是包围所有轮廓点的最小矩形。 2. 作用: 轮廓外接多边形在图像处理和计算机视觉中具有多种作用,包括: - 特征提取:通过外接多边形可以提取轮廓的形状特征,如面积、周长和几何形状等。 - 边界框定位:最小包围矩形可用于定位和包围物体轮廓,用于目标检测和跟踪任务。 - 区域分割:外接多边形可以帮助分割图像中的不同区域或物体。 - 物体识别和分类:根据外接多边形的特征,可以实现物体的识别和分类。 3. 应用: 轮廓外接多边形在各种计算机视觉和图像处理应用中广泛使用,包括但不限于以下领域: - 目标检测和识别:通过最小包围矩形或凸包,可以提取目标的外形特征,并进行分类和识别。 - 图像分析和处理:外接多边形可用于分割图像、提取边界、计算形状特征等。 - 视觉导航和机器人技术:外接多边形可以帮助机器人或自动导航系统识别和定位目标物体。 - 缺陷检测和质量控制:通过外接多边形可以检测产品的缺陷、判定质量。 总之,轮廓外接多边形是一种常用的图像处理技术,可以通过凸包或最小包围矩形来确定轮廓外接多边形,用于特征提取、边界框定位、区域分割和物体识别等应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值