OpenCv中matchShapes()函数的使用

cv::matchShapes()函数的使用

在老版本的OpenCV中,该函数的原型为cvMatchShapes(),其使用方法可以参考博客:https://blog.csdn.net/hhy018/article/details/39080947
在OpenCv3及以上的版本中保留了cvMatchShapes()函数;但这里主要介绍与其功能一样的matchShapes()函数,相比与之前的版本,该函数使用起来更加方便,对向量和Mat类的支持度更好。

1.参数解释
OpenCv中matchShapes()函数的使用_第1张图片
参考网站:https://docs.opencv.org/4.5.5/d3/dc0/group__imgproc__shape.html#gaadc90cb16e2362c9bd6e7363e6e4c317
contour1:轮廓向量或者灰度图。
contour2:轮廓向量或者灰度图。
method:使用的比较方式。
parameter:现在没用,直接设为0。

注:contour1与contour2无顺序之分;contour的类型一般为vector的点集,由findContours()函数求出,也可以自己给定;coutours还可以是双通道的Mat矩阵。
method的具体枚举类型及对应的计算方法请参考:
method用法介绍
下面用两张图片来进行演示:
图片使用Windows自带软件-画图进行绘制的,大家可以绘制自己的两张图用于验证程序。注意确保只有黑白两色。
OpenCv中matchShapes()函数的使用_第2张图片
OpenCv中matchShapes()函数的使用_第3张图片
这两张图片里面的物体轮廓形状、大小完全一样。第二个图片相对于第一个旋转了90度。图片里面只有黑白两个颜色,所以确保我们在一张图片中只会检测到一个轮廓,可以方便我们的演示。
下面依次提取这两个轮廓,并进行匹配。预期结果是完全匹配,程序输出的匹配结果为0。

2.示例代码:



#include
#include
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
using namespace std;
using namespace cv;
int main()
{
	Mat img1 = cv::imread("C:\\Users\\86994\\Desktop\\1.png"); // 读入图片1
	Mat img2 = cv::imread("C:\\Users\\86994\\Desktop\\2.png"); // 读入图片2
	Mat img1_copy = img1.clone(); // 深拷贝图片,用于后面在其上绘制轮廓
	Mat img2_copy = img2.clone();
	
	// 初始化存储轮廓的向量
	vector<vector<Point>>contours1;
	vector<vector<Point>>contours2;
	
	// 初始化轮廓层次结构存储向量
	// Vec4i为OpenCV内置的四维向量(数组),里面的数据为int类型
	std::vector<cv::Vec4i> hierarchy1;
	std::vector<cv::Vec4i> hierarchy2;

	cv::cvtColor(img1, img1, cv::COLOR_BGR2GRAY); // 灰度化img1
	cv::cvtColor(img2, img2, cv::COLOR_BGR2GRAY); // 灰度化img2

	threshold(img1, img1, 150, 255, THRESH_BINARY); // 二值化去掉不必要的杂色,大于150的灰度值置为255
	threshold(img2, img2, 150, 255, THRESH_BINARY);

	Mat canny1, canny2;

	// 边缘检测为找轮廓做准备。因为我这里的图片很简单,这样就可以了。
	// 实际中可能需要其它很多方法的辅助方法才能找到自己想要的边缘。
	//cv::Canny(img1, canny1, 100, 200); 
	//cv::Canny(img2, canny2, 100, 200);
	// 建议使用拉普拉斯计算边缘,比canny能更好地检测出主体成分,避免噪声干扰
	Laplacian(img1, canny1, 16);
	Laplacian(img2, canny2, 16);

	// 求取第一个轮廓,因为我的轮廓是画出来的,为了简单演示,只会有一个轮廓
	// 把这个轮廓作为比较的标准
	findContours(canny1, contours1, hierarchy1, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
	
	// 求取第二个轮廓,它只是相对于第一个轮廓旋转了90度
	findContours(canny2, contours2, hierarchy2, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));

	// 多边形拟合以及打印轮廓的一些几何信息
	vector<Point> cont1, cont2;
	approxPolyDP(contours1[0], cont1, 2, true); // 多边形拟合,减少不必要的点
	approxPolyDP(contours2[0], cont2, 2, true);
	cout << "轮廓数量:" << contours1.size() << "    " << contours2.size() << endl;
	cout << "周长:" << arcLength(contours1[0],true) << "    " << arcLength(contours2[0], true) << endl;
	cout << "面积:" << contourArea(contours1[0]) << "    " << contourArea(contours2[0]) << endl;
	cout << "多边形周长:" << arcLength(cont1,true) << "    " << arcLength(cont2, true) << endl;
	cout << "多边形面积:" << contourArea(cont1) << "    " << contourArea(cont2) << endl;
	cout << "图像深度:" << img1_copy.type() << endl;
	

	// 我们这里每副图只会得到一个轮廓,索引0表示将该轮廓取出
	// 注意:vector>contours1中的vector才是单条轮廓,它由一系列点组成
	// 在实际中,我们得到的往往不是一条轮廓,需要使用其它算法找到我们想要的轮廓,比如使用简单排序找周长最长的轮廓。
	double matchRes = matchShapes(contours1[0], contours2[0], CONTOURS_MATCH_I2, 0);
	double matchResPoly = matchShapes(cont1, cont2, CONTOURS_MATCH_I2, 0);

	// 输出匹配结果,由于两个轮廓形状一致,应该期待输出0,表示完全匹配
	cout << "原轮廓匹配的结果 = " << matchRes << endl;
	cout << "原轮廓的拟合多边形匹配的结果 = " << matchResPoly << endl;

	// 在原图像的拷贝上绘制轮廓
	drawContours(img1_copy, contours1, 0, (0, 0, 255), 5);
	drawContours(img2_copy, contours2, 0, (0, 0, 255), 5);
	imshow("img1WithContour", img1_copy);
	imshow("img2WithContour", img2_copy);
	waitKey();
}


结果如下:
OpenCv中matchShapes()函数的使用_第4张图片

这个匹配结果确实符合预期,多边形结果稍大,说明丢失了一些信息,但仍然是可以使用的。
轮廓检测的效果图:
OpenCv中matchShapes()函数的使用_第5张图片
OpenCv中matchShapes()函数的使用_第6张图片
可以用drawContours函数看看我们给函数的轮廓是不是我们想要的。
大家可以自己用windows的画图去画几张图片检验一下。
这个程序。
OpenCv中matchShapes()函数的使用_第7张图片

返回值为double类型,0表示完全匹配,越大表示匹配程度越低。这个函数主要是使用轮廓的矩进行匹配,轮廓的旋转和平移对结果几乎没有影响,但建议对轮廓先使用多边形拟合后再匹配,可以减少不必要的干扰。
建议直接传轮廓进去(类型为:vector),最为方便。除了轮廓之外还可以用双通道的Mat数组(用的少),数组类型为双通道的int,表示为CV_32SC2。

总结:
在实际使用里,一张图片可能会有很多很多边缘,也会检测出多达上千条轮廓,这个时候我们可以使用轮廓周长或面积对轮廓进行排序,找到我们想要的轮廓。再把该轮廓与我们的模板轮廓对比,模板轮廓可以是画出来的,也可以是一个实际的模板图像里面的某一物体的轮廓,怎样选取轮廓视具体问题而定。

end------------

你可能感兴趣的:(opencv学习笔记,opencv)