本案例要实现的功能是使用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);
}
}
}
}
如图,我们可以看到大部分的目标都找到了。我们可以通过控制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);
#include
#include
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非极大值抑制寻找邻域最大值,极为目标