- 前言
- 一、图像预处理
- 二、单目标匹配
- 三、多目标匹配
- 1、 效果显示
- 四、结果显示
- 1、 效果显示
- 五、源码
- 总结
前言
本案例要实现的功能是使用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(i, j);//灰度值
//3*3邻域非极大值抑制
if (val > thresh)
{
//当前像素的灰度值大于阈值且该像素是其3*3邻域最大值时,判定其为目标
if (result.at(i - 1, j - 1) < val &&
result.at(i - 1, j) < val &&
result.at(i - 1, j + 1) < val &&
result.at(i, j - 1) < val &&
result.at(i, j + 1) < val &&
result.at(i + 1, j - 1) < val &&
result.at(i + 1, j) < val &&
result.at(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(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#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 (i, j);//灰度值 //3*3邻域非极大值抑制 if (val > thresh) { //当前像素的灰度值大于阈值且该像素是其3*3邻域最大值时,判定其为目标 if (result.at (i - 1, j - 1) < val && result.at (i - 1, j) < val && result.at (i - 1, j + 1) < val && result.at (i, j - 1) < val && result.at (i, j + 1) < val && result.at (i + 1, j - 1) < val && result.at (i + 1, j) < val && result.at (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 (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非极大值抑制寻找邻域最大值,极为目标



