Opencv-C++ Notes (17): Template Matching

1 – Concept

opencv provides a function cv::matchTemplate() specially used for template matching; the calling method is as follows:

void cv::matchTemplate(
 
    cv::InputArray image, // 用于搜索的输入图像, 8U 或 32F, 大小 W-H
 
    cv::InputArray templ, // 用于匹配的模板,和image类型相同, 大小 w-h
 
    cv::OutputArray result, // 匹配结果图像, 类型 32F, 大小 (W-w+1)-(H-h+1)
 
    int method // 用于比较的方法   );

2-- method

insert image description here

3 results

The template matching function cv::MatchTemplate calculates the similarity between the template and the image to be tested once, and stores the result in the image result, that is, the value of each point in the result image represents a similarity comparison result
; Swipe from left to right and from top to bottom on the image to be detected. Every time a pixel is reached,
an image of the same size as the template will be intercepted from the original image with the top left corner of the pixel for pixel comparison operation. During the sliding
process of the template, the comparison calculation result between the template and the currently captured image is stored in the result matrix, the size of the result is (W
w+1, H-h+1), and each position in the result (x, The value of y) represents
the calculation result of the template pixel calculation of the image intercepted with this point as the vertex in the upper left corner ; the template moves one pixel each time in the horizontal or vertical direction on the image to be tested
and then performs a comparison, so the horizontal Compare W-w+1 times, vertically compare H-h+1 times, and finally get a result matrix of (W w+1)x(H-h+1);

3.1 Acquisition and use of ROI area

cv::MinMaxLoc(result,&min_val,&max_val,&min_loco,&max_loc,NULL); Extract the maximum value (the highest similarity) and the position of the maximum value from the result (that is, the coordinate position max_loc of the maximum value max_val in the result, that is, the template The coordinates of the upper left corner when sliding, similar to the coordinates (x,y) in the figure;

 由此得到rect=cvRect(max_loc.x,max_loc.y,tmp->width,tmp->height);
    其中rect表示最佳的匹配的矩形区域;
    minVal参数表示返回的最小值,如果不需要,则使用NULL。
    maxVal参数表示返回的最大值,如果不需要,则使用NULL。
    minLoc参数表示返回的最小位置的指针(在2D情况下); 如果不需要,则使用NULL。
    maxLoc参数表示返回的最大位置的指针(在2D情况下); 如果不需要,则使用NULL

3.2, code implementation

single target match

#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>
 
using namespace std;
using namespace cv;
 
int main()
{
    
    
    Mat img, templ, result;      //img为待测图 templ是目标图片  result是结构图
    img = imread("../1.jpg");
    templ = imread("../2.jpg");
 
    //1.构建结果图像resultImg(注意大小和类型)
    //如果原图(待搜索图像)尺寸为W x H, 而模版尺寸为 w x h, 则结果图像尺寸一定是(W-w+1)x(H-h+1)
    //结果图像必须为单通道32位浮点型图像
    int result_cols = img.cols - templ.cols + 1;       //result的尺寸大小
    int result_rows = img.rows - templ.rows + 1;
    result.create(result_cols, result_rows, CV_32FC1);
 
    //2.模版匹配
    //这里我们使用的匹配算法是标准平方差匹配 method=CV_TM_SQDIFF_NORMED,数值越小匹配度越好
    matchTemplate(img, templ, result, CV_TM_SQDIFF_NORMED);
    //3.正则化(归一化到0-1)
    normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
 
    //4.找出resultImg中的最大值及其位置
    double minVal = -1;
    double maxVal;
    Point minLoc;
    Point maxLoc;
    Point matchLoc;
    cout << "匹配度:" << minVal << endl;
    // 定位极值的函数
    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
    cout << "匹配度:" << minVal << endl;
    cout << "minPosition: " << minLoc << endl;
    cout << "maxPosition: " << maxLoc << endl;
 
    matchLoc = minLoc;     
    //5.根据resultImg中的最大值位置在源图上画出矩形和中心点
    Point center = Point(minLoc.x + templ.cols / 2, minLoc.y + templ.rows / 2);
    rectangle(img, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, 8, 0);
    circle(img, center, 2, Scalar(255, 0, 0), 2);
 
    imshow("img", img);
    imshow("template", templ);
    waitKey(0);
 
    return 0;
}

insert image description here
Real-time single target recognition

#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;
 
int main()
{
    
    
    //1.定义VideoCapture类对象video,打开摄像头
    VideoCapture video(0);
    //1.1.判断是否打开
    if (!video.isOpened())
    {
    
    
        cout << "video open error!" << endl;
        return 0;
    }
    //2.循环读取视频的每一帧,对每一帧进行模版匹配
    while (1)
    {
    
    
        //2.1.读取帧
        Mat frame;
        video >> frame;
        //2.2.对帧进行异常检测
        if (frame.empty())
        {
    
    
            cout << "frame empty" << endl;
            break;
        }
        //2.3.对帧进行模版匹配
        Mat tempImg = imread("../1.jpg", CV_LOAD_IMAGE_COLOR);
        cout << "Size of template: " << tempImg.size() << endl;
        //2.3.1.构建结果图像resultImg(注意大小和类型)
        //如果原图(待搜索图像)尺寸为W x H, 而模版尺寸为 w x h, 则结果图像尺寸一定是(W-w+1)x(H-h+1)
        //结果图像必须为单通道32位浮点型图像
        int width = frame.cols - tempImg.cols + 1;
        int height = frame.rows - tempImg.rows + 1;
        Mat resultImg(Size(width, height), CV_32FC1);
        //2.3.2.模版匹配
        matchTemplate(frame, tempImg, resultImg, CV_TM_CCOEFF_NORMED);
        imshow("result", resultImg);
        //2.3.3.正则化(归一化到0-1)
        normalize(resultImg, resultImg, 0, 1, NORM_MINMAX, -1);
        //2.3.4.找出resultImg中的最大值及其位置
        double minValue = 0;
        double maxValue = 0;
        Point minPosition;
        Point maxPosition;
        minMaxLoc(resultImg, &minValue, &maxValue, &minPosition, &maxPosition);
        cout << "minValue: " << minValue << endl;
        cout << "maxValue: " << maxValue << endl;
        cout << "minPosition: " << minPosition << endl;
        cout << "maxPosition: " << maxPosition << endl;
        //2.3.5.根据resultImg中的最大值位置在源图上画出矩形
        rectangle(frame, maxPosition, Point(maxPosition.x + tempImg.cols, maxPosition.y + tempImg.rows), Scalar(0, 255, 0), 1, 8);
        imshow("srcImg", frame);
        imshow("template", tempImg);
        if (waitKey(10) == 27)
        {
    
    
            cout << "ESC退出" << endl;
            break;
        };
    }
    return 0;
}

Multiple Target Template Matching

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
 
using namespace std;
using namespace cv;
 
Point getNextMinLoc(Mat &result, Point minLoc, int maxValue, int templatW, int templatH);
 
int main()
{
    
    
    Mat src = imread("../1.jpg");
    Mat srcCopy = src.clone();
 
    Mat temp = imread("../2.jpg");
    Mat result;
 
    if (src.empty() || temp.empty())
    {
    
    
        cout << "打开图片失败" << endl;
        return 0;
    }
 
    vector<Mat> templat;
    vector<float> minV;
    vector<Point> minL;
 
    int srcW, srcH, templatW, templatH, resultH, resultW;
    srcW = src.cols;
    srcH = src.rows;
    templat.push_back(temp);
    double minValue, maxValue;
    Point minLoc, maxLoc;
 
    for (int i=0;i<10;i++)
    {
    
    
        cout << i << ": ";
        templatW = templat[i].cols;
        templatH = templat[i].rows;
 
        if (srcW < templatW || srcH < templatH)
        {
    
    
            cout << "模板不能比原图大" << endl;
            return 0;
        }
 
        resultW = srcW - templatW + 1;
        resultH = srcH - templatH + 1;
 
        result.create(Size(resultW, resultH), CV_32FC1);
        matchTemplate(src, templat[i], result, CV_TM_SQDIFF_NORMED);
 
        minMaxLoc(result, &minValue, &maxValue, &minLoc, &maxLoc);
 
        cout << "min1: " << minValue << endl;
        if (minValue<=0.070055)
        {
    
    
            rectangle(srcCopy, minLoc, Point(minLoc.x + templatW, minLoc.y + templatH), Scalar(0, 0, 255), 2, 8, 0);
 
            Point new_minLoc;
            new_minLoc = getNextMinLoc(result, minLoc, maxValue, templatW, templatH);
 
            float *data = result.ptr<float>(new_minLoc.y);
 
            cout << "min2: " << data[new_minLoc.x] << " ";
            if (data[new_minLoc.x]<=0.5)
            {
    
    
                cout << "进这个函数了:" << i << ":" << new_minLoc.x;
                cout << " " << new_minLoc.y;
                rectangle(srcCopy, new_minLoc, Point(new_minLoc.x + templatW, new_minLoc.y + templatH),
                          Scalar(0, 255, 0), 2, 8, 0);
                new_minLoc = getNextMinLoc(result, new_minLoc, maxValue, templatW, templatH);
            }
 
            float *data1 = result.ptr<float>(new_minLoc.y);
            cout << "min3: " << data1[new_minLoc.x] << " " << endl;
            if (data1[new_minLoc.x] <= 0.4)
            {
    
    
 
                rectangle(srcCopy, new_minLoc, Point(new_minLoc.x + templatW, new_minLoc.y + templatH),
                          Scalar(255, 0, 0), 2, 8, 0);
            }
        }
        cout << "#" << endl;
        Mat temp_templat;
        resize(templat[i], temp_templat, Size(templat[i].cols / 1.1, templat[i].rows / 1.1));
        templat.push_back(temp_templat);
    }
 
    imshow("结果", srcCopy);
    waitKey(0);
    return 0;
}
 
Point getNextMinLoc(Mat &result, Point minLoc, int maxValue, int templatW, int templatH)
{
    
    
    //imshow("result", result);
    //cout << "maxvalue: " << maxValue << endl;
    int startX = minLoc.x - templatW / 3;
    int startY = minLoc.y - templatH / 3;
    int endX = minLoc.x + templatW / 3;
    int endY = minLoc.y + templatH / 3;
    if (startX < 0 || startY < 0)
    {
    
    
        startX = 0;
        startY = 0;
    }
    if (endX > result.cols - 1 || endY > result.rows - 1)
    {
    
    
        endX = result.cols - 1;
        endY = result.rows - 1;
    }
    int y, x;
    for (y = startY; y < endY; y++)
    {
    
    
        for (x = startX; x < endX; x++)
        {
    
    
            float *data = result.ptr<float>(y);
 
            data[x] = maxValue;
        }
    }
    double new_minValue, new_maxValue;
    Point new_minLoc, new_maxLoc;
    minMaxLoc(result, &new_minValue, &new_maxValue, &new_minLoc, &new_maxLoc);
    //imshow("result_end", result);
    return new_minLoc;
}

insert image description here

Through the obtained results, we found that our template matching seems to be consistent with the size of the matching area and the template, so it is easy to generate errors or get unsatisfactory areas. In real life, due to the distance between the image to be tested and the camera The transformation of the distance between them, the influence of the size factor of the template, if we want to get a better matching result, we need to realize the template matching of adaptive size;

From this, we start template matching with adaptive size, and the loaded template image enters the cycle, and each cycle is scaled to a certain ratio. After template matching, we finally get the ROI area under different ratios. For all Compare the similarity of the ROI area with our template image, select the matching image with the highest similarity, and obtain the best matching ratio;

Adaptive Goal Matching

Code flow:

    1,载入待测图像与模板;

    2,将模板图像等比例放大或缩小

    3,没改变一次进行一次模板匹配

    4,得到匹配区域的图片

    5,将得到的ROI图片与原始模板进行相似性比较

    6,筛选出相似性最好的ROI区域

    7,在待测图片上进行框选

    8,输出图片
#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>
 
using namespace std;
using namespace cv;
 
int pHash(Mat matSrc1, Mat matSrc2)
//int main()
{
    
    
    Mat matDst1, matDst2;
 
//    Mat matSrc1 = imread("../1.jpg");
//    Mat matSrc2 = imread("../3.jpg");
 
    cv::resize(matSrc1, matDst1, cv::Size(32, 32), 0, 0, cv::INTER_CUBIC);
    cv::resize(matSrc2, matDst2, cv::Size(32, 32), 0, 0, cv::INTER_CUBIC);
 
    cv::cvtColor(matDst1, matDst1, CV_BGR2GRAY);
    cv::cvtColor(matDst2, matDst2, CV_BGR2GRAY);
 
    matDst1.convertTo(matDst1, CV_32F);
    matDst2.convertTo(matDst2, CV_32F);
    dct(matDst1, matDst1);
    dct(matDst2, matDst2);
 
    int iAvg1 = 0, iAvg2 = 0;
    int arr1[64], arr2[64];
 
    for (int i = 0; i < 8; i++)
    {
    
    
        uchar* data1 = matDst1.ptr<uchar>(i);
        uchar* data2 = matDst2.ptr<uchar>(i);
 
        int tmp = i * 8;
 
        for (int j = 0; j < 8; j++)
        {
    
    
            int tmp1 = tmp + j;
 
            arr1[tmp1] = data1[j];
            arr2[tmp1] = data2[j];
 
            iAvg1 += arr1[tmp1];
            iAvg2 += arr2[tmp1];
        }
    }
 
    iAvg1 /= 64;
    iAvg2 /= 64;
 
    for (int i = 0; i < 64; i++)
    {
    
    
        arr1[i] = (arr1[i] >= iAvg1) ? 1 : 0;
        arr2[i] = (arr2[i] >= iAvg2) ? 1 : 0;
    }
 
    int iDiffNum = 0;
 
    for (int i = 0; i < 64; i++)
        if (arr1[i] != arr2[i])
            ++iDiffNum;
//    cout<<iDiffNum<<endl;
    return iDiffNum;
}
 
//int main()
//{
    
    
//    Mat img,templ,result;
//    img = imread("../1.jpg");
//    templ = imread("../2.jpg");
//
//    int result_cols = img.cols - templ.cols + 1;
//    int result_rows = img.rows - templ.rows + 1;
//
//    result.create(result_cols,result_rows,CV_32FC1);
//    matchTemplate(img, templ, result, CV_TM_SQDIFF_NORMED);
//
//    Point minLoc;
//    Point maxLoc;
//    double minVal = -1;
//    double maxVal;
//
//    minMaxLoc(result,&minVal,&maxVal,&minLoc,&maxLoc,Mat());
//    cout<<"minLoc.x:"<<minLoc.x<<endl;
//    cout<<"minLoc.y:"<<minLoc.y<<endl;
//    cout<<"result_cols:"<<result_cols<<endl;
//
//    int ROI_rows =templ.rows - 0.05*templ.rows;
//    int ROI_cols =templ.cols - 0.05 *templ.cols;
//
//    Rect img_ROI = Rect(minLoc.x, minLoc.y,ROI_rows,ROI_cols);
//
//    Mat ROI = img(img_ROI);
//    pHash(ROI,templ);
//
//}
 
int main()
{
    
    
    //加载图片
    Mat src_img,temp_img,result_img;
    src_img = imread("../1.jpg");
    temp_img = imread("../2.jpg");
 
    imshow("src_img",src_img);
    imshow("temp_img",temp_img);
 
    //构建结果图像,结果图像必须是单通道32位浮点型图像
    int result_cols = src_img.cols - temp_img.cols + 1;       //result的尺寸大小
    int result_rows = src_img.rows - temp_img.rows + 1;
    result_img.create(result_cols, result_rows, CV_32FC1);
 
    int n = 0;
 
    //循环缩放模板图片
    for(int i = 0; i <10; i++)
    {
    
    
        cout<<i<<endl;
        Mat temp_imgc = temp_img.clone();
        int temp_imgc_col = temp_img.cols - i * 0.05 * temp_img.cols;
        int temp_imgc_row = temp_img.rows - i * 0.05 * temp_img.rows;
        resize(temp_imgc,temp_imgc,Size(temp_imgc_col,temp_imgc_row));
 
        //进行模板匹配
        matchTemplate(src_img,temp_imgc,result_img,0);
 
        double minVal = -1;
        double maxVal;
        Point minLoc;
        Point maxLoc;
        Point matchLoc;
        minMaxLoc(result_img, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
 
        Rect ROI = Rect(minLoc.x,minLoc.y,temp_imgc_row,temp_imgc_col);
 
        Mat img_show = src_img.clone();
        matchLoc = minLoc;
        //5.根据resultImg中的最大值位置在源图上画出矩形和中心点
        Point center = Point(minLoc.x + temp_imgc.cols / 2, minLoc.y + temp_imgc.rows / 2);
        rectangle(img_show, matchLoc, Point(matchLoc.x + temp_imgc.cols, matchLoc.y + temp_imgc.rows), Scalar(0, 255, 0), 1, 8, 0);
 
//        imshow("result",img_show);
//        waitKey(0);
 
        //获取匹配得到区域
        Rect img_ROI = Rect(matchLoc,Point(matchLoc.x + temp_imgc.cols, matchLoc.y + temp_imgc.rows));
        Mat img = src_img.clone();
        Mat ROI_img = img(img_ROI);
 
//        imshow("ROI",ROI_img);
//        waitKey(0);
 
        //进行相似度比较
        if(pHash(ROI_img,temp_img) < 20)
        {
    
    
            n = pHash(ROI_img,temp_img);
            cout<<"n="<<n<<endl;
            imshow("zhy", img_show);
            waitKey(0);
        }
        
//        //获取模板匹配得到的区域
//        double minVal;
//        double maxVal;
//        Point minLoc;
//        Point maxLoc;
//        minMaxLoc(result_img,&minVal,&maxVal,&minLoc,&maxLoc,Mat());  //找矩阵中最小位置点的坐标
//        //画出ROI区域的矩形框
//        Rect ROI = Rect(minLoc.x,minLoc.y,temp_imgc_row,temp_imgc_col);
//
//        Mat result_img_ROI = result_img(ROI);
//        cout<<i<<endl;
//        imshow("show",result_img_ROI);
//        waitKey(0);
    }
}

insert image description here

Guess you like

Origin blog.csdn.net/jiyanghao19/article/details/132291103