OpenCV C++案例实战二十四《多目标匹配》

Posted Zero___Chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV C++案例实战二十四《多目标匹配》相关的知识,希望对你有一定的参考价值。

OpenCV C++案例实战二十四《多目标匹配》


前言

本案例要实现的功能是使用OpenCV中的matchTemplate实现多目标匹配。熟悉matchTemplate这个API的小伙伴都知道,仅仅单一使用matchTemplate的话只能实现单一目标匹配,而不能实现多目标匹配。那么接下来我们就一起看看如何使用matchTemplate实现多目标匹配吧。

一、图像预处理

首先加载测试图像与模板图像,如下图所示:
测试图像:

模板图像:

我们的最终目的是在测试图像中找到所有模板图像的物体,并把它框出来。

首先将图像灰度化、去噪

	Mat src_gray, src_gaussian;
	cvtColor(src, src_gray, COLOR_BGR2GRAY);
	GaussianBlur(src_gray, src_gaussian, Size(3, 3), 0);

	Mat temp_gray, temp_gaussian;
	cvtColor(temp, temp_gray, COLOR_BGR2GRAY);
	GaussianBlur(temp_gray, temp_gaussian, Size(3, 3), 0);

然后调用matchTemplate进行模板匹配,matchTemplate提供六种计算图像相似度的方法。
1、差值平方和匹配 CV_TM_SQDIFF —匹配结果越好,值越小(0)
2、标准化差值平方和匹配 CV_TM_SQDIFF_NORMED
3、相关匹配 CV_TM_CCORR —匹配结果越好,值越大;0表示匹配结果最差
4、标准相关匹配 CV_TM_CCORR_NORMED
5、相关匹配 CV_TM_CCOEFF —1:表示完全相同;0:表示完全不相同
6、标准相关匹配 CV_TM_CCOEFF_NORMED

在这里我使用的是标准相关匹配,结果如下面所示。

	Mat result;
	matchTemplate(src_gaussian, temp_gaussian, result, TM_CCOEFF_NORMED);
	normalize(result, result, 0, 1, NORM_MINMAX);

如下图所示:即为我们使用matchTemplate及归一化得到的结果

使用minMaxLoc找到最大最小值,以及它们所在坐标。

	double minVal, maxVal;
	Point minLoc, maxLoc;
	minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);

二、单目标匹配

	//单目标匹配
	rectangle(src, Rect(maxLoc.x, maxLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 5);


如图为单目标匹配结果,接下来看看多目标匹配是如何实现的。

三、多目标匹配

我们之前使用matchTemplate得到归一化之后的结果,我们需要的是使用3*3邻域非极大值抑制找到result中所有邻域最大值。

	//多目标匹配
	double quality = 0.88;  //匹配质量(0~1),越接近1,匹配程度越高
	if (quality <= 0.0)quality = 0.0;
	if (quality >= 1.0)quality = 1.0;
	double thresh = maxVal * quality;//像素阈值
	for (int i = 0; i < result.rows; i++)
	
		for (int j = 0; j < result.cols; j++)
		
			double val = result.at<float>(i, j);//灰度值

			//3*3邻域非极大值抑制
			if (val > thresh)
			
				//当前像素的灰度值大于阈值且该像素是其3*3邻域最大值时,判定其为目标
				if (result.at<float>(i - 1, j - 1) < val &&
					result.at<float>(i - 1, j) < val &&
					result.at<float>(i - 1, j + 1) < val &&
					result.at<float>(i, j - 1) < val &&
					result.at<float>(i, j + 1) < val &&
					result.at<float>(i + 1, j - 1) < val &&
					result.at<float>(i + 1, j) < val &&
					result.at<float>(i + 1, j + 1) < val)
				
					//结果绘制
					rectangle(src, Rect(j, i, temp.cols, temp.rows), Scalar(0, 255, 0), 2);

					char text[10];
					float score = result.at<float>(i, j);
					sprintf_s(text, "%.2f",score);
					putText(src, text, Point(j, i), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 0, 255), 2);
				
			
		
	

1、 效果显示


如图,我们可以看到大部分的目标都找到了。我们可以通过控制quality 匹配质量来控制我们最终的匹配精度,quality 越大,匹配程度越高。

四、结果显示

在这里我做了点骚操作,将模板图像与测试图像放在一张新图像上。直接上代码

	//效果绘制--可忽略
	//设置新画布,可容纳原图跟模板图像
	Mat canvas(Size(src.cols + temp.cols + 200, src.rows), CV_8UC3, Scalar::all(255));
	src.copyTo(canvas(Rect(0, 0, src.cols, src.rows)));
	//将模板图像放置在画布上
	//tempname.substr(0, tempname.find("."))--为文件名
	rectangle(canvas, Rect(src.cols +40 , 50, 200, 80), Scalar(0,255,0), -1);
	putText(canvas, tempname.substr(0, tempname.find(".")), Point(src.cols + 100 , 100), FONT_HERSHEY_SIMPLEX, 1.3, Scalar(0,0,255), 3);
	temp.copyTo(canvas(Rect(src.cols + 100, 150, temp.cols, temp.rows)));
	namedWindow("Demo", WINDOW_NORMAL);
	imshow("Demo", canvas);

1、 效果显示


五、源码

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;


int main()

	Mat src = imread("test.jpg");
	string tempname = "fox.jpg";
	Mat temp = imread(tempname);
	if (src.empty() || temp.empty())
	
		cout << "No Image..." << endl;
		system("pause");
		return -1;
	

	Mat src_gray, src_gaussian;
	cvtColor(src, src_gray, COLOR_BGR2GRAY);
	GaussianBlur(src_gray, src_gaussian, Size(3, 3), 0);

	Mat temp_gray, temp_gaussian;
	cvtColor(temp, temp_gray, COLOR_BGR2GRAY);
	GaussianBlur(temp_gray, temp_gaussian, Size(3, 3), 0);

	Mat result;
	matchTemplate(src_gaussian, temp_gaussian, result, TM_CCOEFF_NORMED);
	normalize(result, result, 0, 1, NORM_MINMAX);


	double minVal, maxVal;
	Point minLoc, maxLoc;
	minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
	
	//单目标匹配
	//rectangle(src, Rect(maxLoc.x, maxLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 5);


	//多目标匹配
	double quality = 0.88;  //匹配质量(0~1),越接近1,匹配程度越高
	if (quality <= 0.0)quality = 0.0;
	if (quality >= 1.0)quality = 1.0;
	double thresh = maxVal * quality;//像素阈值
	for (int i = 0; i < result.rows; i++)
	
		for (int j = 0; j < result.cols; j++)
		
			double val = result.at<float>(i, j);//灰度值

			//3*3邻域非极大值抑制
			if (val > thresh)
			
				//当前像素的灰度值大于阈值且该像素是其3*3邻域最大值时,判定其为目标
				if (result.at<float>(i - 1, j - 1) < val &&
					result.at<float>(i - 1, j) < val &&
					result.at<float>(i - 1, j + 1) < val &&
					result.at<float>(i, j - 1) < val &&
					result.at<float>(i, j + 1) < val &&
					result.at<float>(i + 1, j - 1) < val &&
					result.at<float>(i + 1, j) < val &&
					result.at<float>(i + 1, j + 1) < val)
				
					//结果绘制
					rectangle(src, Rect(j, i, temp.cols, temp.rows), Scalar(0, 255, 0), 2);

					char text[10];
					float score = result.at<float>(i, j);
					sprintf_s(text, "%.2f",score);
					putText(src, text, Point(j, i), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 0, 255), 2);
				
			
		
	

	//效果绘制--可忽略
	//设置新画布,可容纳原图跟模板图像
	Mat canvas(Size(src.cols + temp.cols + 200, src.rows), CV_8UC3, Scalar::all(255));
	src.copyTo(canvas(Rect(0, 0, src.cols, src.rows)));
	//将模板图像放置在画布上
	//tempname.substr(0, tempname.find("."))--为文件名
	rectangle(canvas, Rect(src.cols +40 , 50, 200, 80), Scalar(0,255,0), -1);
	putText(canvas, tempname.substr(0, tempname.find(".")), Point(src.cols + 100 , 100), FONT_HERSHEY_SIMPLEX, 1.3, Scalar(0,0,255), 3);
	temp.copyTo(canvas(Rect(src.cols + 100, 150, temp.cols, temp.rows)));
	namedWindow("Demo", WINDOW_NORMAL);
	imshow("Demo", canvas);
	waitKey(0);
	system("pause");
	return false;


总结

本文使用OpenCV C++多目标匹配,主要操作有以下几点。
1、图像预处理
2、matchTemplate模板匹配以及图像归一化
3、minMaxLoc计算图像最大最小值
4、3*3非极大值抑制寻找邻域最大值,极为目标

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于OpenCV C++案例实战二十四《多目标匹配》的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV C++案例实战三十《中文点选验证码识别》

OpenCV C++案例实战十《车牌号识别》

OpenCV竟然可以这样学!成神之路终将不远(二十四)

OpenCV竟然可以这样学!成神之路终将不远(二十七)

ElasticSearch实战(二十四)-查询语法扩展

ElasticSearch实战(二十四)-查询语法扩展