• 模板匹配


    模板匹配是一项在一-幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术。在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 中存放相符的变量值:

  • 相关阅读:
    详解vue静态资源打包中的坑与解决方案
    vue项目构建实战基础知识:SPA理解/RESTful接口介绍/static目录配置/axios封装/打包时map文件去除
    axios踩坑记录+拦截器使用+vue cli代理跨域proxy+webpack打包部署到服务器
    vue-cli项目开发/生产环境代理实现跨域请求+webpack配置开发/生产环境的接口地址
    vue中watch的用法总结以及报错处理Error in callback for watcher "checkList"
    Vue侦听器watch
    ES6 import 引用文件夹/目录及其处理过程
    Nginx部署前端代码实现前后端分离
    使用XmlInclude解决WebService调用时无法识别子类的异常
    WebServices中Xml的序列化
  • 原文地址:https://www.cnblogs.com/fcfc940503/p/11311948.html
Copyright © 2020-2023  润新知