模板匹配

模板匹配是一项在一-幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术。在OpenCV2和OpenCV3中,模板匹配由MatchTemplate()函数完成。需要

注意,模板匹配不是基于直方图的,而是通过在输入图像上滑动图像块,对实际的图像块和输入图像进行匹配的一种匹配方法。

 

模板匹配的工作方式
    模板匹配的工作方式跟直方图的反向投影基本一样,大致过程是这样的:通过在输入图像上滑动图像块对实际的图像块和输入图像进行匹配。
    假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
  (1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
  (2)用临时图像和模板图像进行对比,对比结果记为c;
  (3)对比结果c,就是结果图像(0,0)处的像素值;
  (4)切割输入图像从(0,1)至(10,11)的临时图像,对比,并记录到结果图像;
  (5)重复(1)~(4)步直到输入图像的右下角。

 

 

程序实现了什么?

  • 载入一幅输入图像和一幅模板图像块 (template)
    • 通过使用函数 matchTemplate 实现之前所述的6种匹配方法的任一个. 用户可以通过滑动条选取任何一种方法.
  • 归一化匹配后的输出结果
  • 定位最匹配的区域
  • 用矩形标注最匹配的区域
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

Mat g_srcImage, g_tempalteImage, g_resultImage;
int g_nMatchMethod;
int g_nMaxTrackbarNum = 5;


void on_matching(int, void*)
{
	Mat srcImage;
	g_srcImage.copyTo(srcImage);
	int resultImage_cols = g_srcImage.cols - g_tempalteImage.cols + 1;
	int resultImage_rows = g_srcImage.rows - g_tempalteImage.rows + 1;
	g_resultImage.create(resultImage_cols, resultImage_rows, CV_32FC1);

	matchTemplate(g_srcImage, g_tempalteImage, g_resultImage, g_nMatchMethod);
	normalize(g_resultImage, g_resultImage, 0, 2, NORM_MINMAX, -1, Mat());
	double minValue, maxValue;
	Point minLocation, maxLocation, matchLocation;
	minMaxLoc(g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation);

	if (g_nMatchMethod == TM_SQDIFF || g_nMatchMethod == CV_TM_SQDIFF_NORMED)
	{
		matchLocation = minLocation;
	}
	else
	{
		matchLocation = maxLocation;
	}

	rectangle(srcImage, matchLocation, Point(matchLocation.x + g_tempalteImage.cols, matchLocation.y + g_tempalteImage.rows), Scalar(0, 0, 255), 2, 8, 0);
	rectangle(g_resultImage, matchLocation, Point(matchLocation.x + g_tempalteImage.cols, matchLocation.y + g_tempalteImage.rows), Scalar(0, 0, 255), 2, 8, 0);

	imshow("原始图", srcImage);
	imshow("效果图", g_resultImage);

}

int main()
{
	g_srcImage = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\02.jpg");
	if (!g_srcImage.data)
	{
		cout << "原始图读取失败" << endl;
		return -1;
	}
	g_tempalteImage = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\021.jpg");
	if (!g_tempalteImage.data)
	{
		cout << "模板图读取失败" << endl;
		return -1;
	}

	namedWindow("原始图", CV_WINDOW_AUTOSIZE);
	namedWindow("效果图", CV_WINDOW_AUTOSIZE);
	createTrackbar("方法", "原始图", &g_nMatchMethod, g_nMaxTrackbarNum, on_matching);

	on_matching(0, NULL);


	waitKey(0);

	return 0;
}

 代码解析:

  1. 定义一些全局变量, 例如原图像g_srcImage, 模板图像g_tempalteImage,和结果图像g_resultImage, 还有匹配方法;

    Mat g_srcImage, g_tempalteImage, g_resultImage;
    int g_nMatchMethod;
    int g_nMaxTrackbarNum = 5;
  2. 载入原图像和匹配块:

    img = imread( argv[1], 1 );
    templ = imread( argv[2], 1 );
    
  3. 创建窗口,显示原图像和结果图像:

    g_srcImage = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\02.jpg");
    	if (!g_srcImage.data)
    	{
    		cout << "原始图读取失败" << endl;
    		return -1;
    	}
    	g_tempalteImage = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\021.jpg");
    	if (!g_tempalteImage.data)
    	{
    		cout << "模板图读取失败" << endl;
    		return -1;
    	}
    
    	namedWindow("原始图", CV_WINDOW_AUTOSIZE);
    	namedWindow("效果图", CV_WINDOW_AUTOSIZE);
  4. 创建滑动条并输入将被使用的匹配方法. 一旦滑动条发生改变,回调函数 on_matching 就会被调用.

    createTrackbar("方法", "原始图", &g_nMatchMethod, g_nMaxTrackbarNum, on_matching);
    
        on_matching(0, NULL);
    
    
        waitKey(0);
    
        return 0;
  5. 一直等待,直到用户退出这个程序.

    waitKey(0);
    return 0;
    
  6. 让我们先看看回调函数. 首先, 它对原图像进行了一份复制:

    Mat srcImage;
    g_srcImage.copyTo(srcImage);
  7. 然后, 它创建了一幅用来存放匹配结果的输出图像矩阵. 仔细看看输出矩阵的大小(它包含了所有可能的匹配位置)

    int resultImage_cols = g_srcImage.cols - g_tempalteImage.cols + 1;
    int resultImage_rows = g_srcImage.rows - g_tempalteImage.rows + 1;
    g_resultImage.create(resultImage_cols, resultImage_rows, CV_32FC1);
  8. 执行模板匹配操作:

    matchTemplate(g_srcImage, g_tempalteImage, g_resultImage, g_nMatchMethod);

    很自然地,参数是输入图像 I, 模板图像 T, 结果图像 R 还有匹配方法 (通过滑动条给出)

  9. 我们对结果进行归一化:

    normalize(g_resultImage, g_resultImage, 0, 2, NORM_MINMAX, -1, Mat());  
  10. 通过使用函数 minMaxLoc ,我们确定结果矩阵 R 的最大值和最小值的位置.

    double minValue, maxValue;
    Point minLocation, maxLocation, matchLocation;
    minMaxLoc(g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation);  

    函数中的参数有:

    • result: 匹配结果矩阵
    • &minVal 和 &maxVal: 在矩阵 result 中存储的最小值和最大值
    • &minLoc 和 &maxLoc: 在结果矩阵中最小值和最大值的坐标.
    • Mat(): 可选的掩模
  11. 对于前二种方法 ( CV_SQDIFF 和 CV_SQDIFF_NORMED ) 最低的数值标识最好的匹配. 对于其他的, 越大的数值代表越好的匹配. 所以, 我们在 matchLoc 中存放相符的变量值:

    if (g_nMatchMethod == TM_SQDIFF || g_nMatchMethod == CV_TM_SQDIFF_NORMED)
    	{
    		matchLocation = minLocation;
    	}
    	else
    	{
    		matchLocation = maxLocation;
    	}

显示原图像和结果图像. 再用矩形框标注最符合的区域:

rec – 确定矩形的另一种方式,给左上角坐标和长宽

 

rectangle(srcImage, matchLocation, Point(matchLocation.x + g_tempalteImage.cols, matchLocation.y + g_tempalteImage.rows), Scalar(0, 0, 255), 2, 8, 0);
rectangle(g_resultImage, matchLocation, Point(matchLocation.x + g_tempalteImage.cols, matchLocation.y + g_tempalteImage.rows), Scalar(0, 0, 255), 2, 8, 0);
imshow("原始图", srcImage);
imshow("效果图", g_resultImage);

方法0,1,3,4,5都是上面这个结果,方法2是下面这个结果;

前二种方法 ( CV_SQDIFF 和 CV_SQDIFF_NORMED ) 最低的数值标识最好的匹配. 对于其他的, 越大的数值代表越好的匹配. 所以, 我们在 matchLoc 中存放相符的变量值:

 

 

 

 

 

posted @ 2019-08-06 21:24  浮沉沉浮  阅读(3257)  评论(0编辑  收藏  举报