我是靠谱客的博主 鲤鱼大碗,最近开发中收集的这篇文章主要介绍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;
}

在这里插入图片描述

最后

以上就是鲤鱼大碗为你收集整理的C++版本OpenCv教程(四十六)轮廓外接多边形的全部内容,希望文章能够帮你解决C++版本OpenCv教程(四十六)轮廓外接多边形所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(32)

评论列表共有 0 条评论

立即
投稿
返回
顶部