学习template算法以及改进(一)

学习template算法(template matching)以及改进(一)

参考https://en.wikipedia.org/wiki/Template_matching#Examples_of_Use

如果模板图像具有很强的特征,则可以考虑基于特征的方法。

而对于没有强特征的模板,或者当模板图像的大部分构成匹配图像时,基于模板的方法可能是有效的:

一、Template-based approach

由于基于模板的模板匹配可能潜在地需要对大量点进行采样,可以通过将搜索和模板图像的分辨率resolution,降低相同的比例factor,并对所得结果执行操作,来减少采样点的数量。生成的缩小后的downsized图像(多分辨率mutilresolution或金字塔pyramid),提供搜索图像内的数据点的搜索窗口,使得模板不必搜索每个可行的数据点。

二、运动跟踪与遮挡处理Motion tracking and occlusion handling

在模板可能不提供直接匹配的情况下,实现特征空间eigenspaces的使用可能是有用的,特征空间是在许多不同条件下详细描述匹配对象的模板,例如不同的透视图、照明、颜色对比或可接受的匹配对象姿态。例如,如果用户正在寻找面部,则特征空间可以包括在照相机的不同位置、在不同照明条件或具有不同表达的面部的图像(模板)。

匹配图像也可能被对象遮挡或遮挡;在这些情况下,提供多个模板来覆盖每个可能的遮挡是不合理的。例如,搜索图像可以是扑克牌,并且在一些搜索图像中,卡被拿着卡的人的手指、或者被上面的另一张卡或者相机前面的任何物体遮挡。在物体可延展或可姿态的情况下,运动也成为一个问题,并且涉及运动和遮挡的问题变得模糊。【8】在这些情况下,一种可能的解决方案是将模板图像分割成多个子图像,并在每个细分上执行匹配。

三、基于互相关cross correlation或绝对差和sum of absolute differences解释的基于模板的匹配

  • (1)基于互相关cross correlation

模板匹配的基本方法使用图像补丁(模板),该图像补丁(模板)针对我们想要检测的搜索图像的特定特征而定制。
互相关输出在图像结构与掩模结构匹配的地方最高。

  1. 首先选取搜索图像的一部分作为模板。我们将搜索图像Search image称为S(x,y),其中(x,y)表示搜索图像中每个像素的坐标。我们将模板图像template称为T(x t,y
    t),其中(xt,yt)表示模板中每个像素的坐标。

  2. 然后,我们简单地将模板T(x t,y t)的中心(或原点)移动到搜索图像中的每个(x,y)点上,计算搜索图像和模板图像之间系数的乘积和。

  3. 具有最大值的位置是最佳位置。这种方法有时被称为“线性空间滤波”Linear Spatial Filtering,而模板被称为filter mask。


  • (2)基于绝对差之和sum of absolute differences

使用模板匹配处理图像平移问题的另一种方法是使用SAD(绝对差之和)度量比较像素的强度。

搜索图像中坐标(xs,ys)的像素具有强度Is(xs,ys),模板中具有坐标(xt,yt)的像素具有强度It(xt,yt)。

某一个(对应)像素强度的绝对差定义为微分:

Diff(xs, ys, xt, yt) = | Is(xs, ys) – It(xt, yt) |.

搜索图像坐标(x,y)处的像素和整个模板图像之间的微分和可以表示为:
这里写图片描述

在搜索图像中遍历其行、列的所有(x,y),得到若干SAD:
这里写图片描述
上面公式中,Srows和Scols分别表示搜索图像的行和列,Trows和Tcols分别表示模板图像的行和列。

在该方法中,SAD最小值给出了搜索图像中模板的最佳位置的估计。
该方法简单易行,但却是最慢的方法之一。

三、算法实现及结果

- ① opencv2.4.9标准头文件模板:

include<opencv2/core/core.hpp>
include<opencv2/highgui/highgui.hpp>
include<opencv2/imgproc/imgproc.hpp>
include<iostream>

using namespace cv;

int main(){
    Mat test_image = imread("test.jpg");
    if (image.empty())
    {
        cout << "读取图片错误" << endl;
    }
    imshow("WindowName", test_image);
    waitKey(5000);
    return 0;
}

- ② 用opencv截取图像中的一部分区域

roi_img = src_img(Range(0,100),Range(50,200));

这里截取的就是原图src_img第0行至第99行,第50列至199列的区域图像。

P.S.

注意Range的两个参数范围分别为左包含右不包含。
如果操作利用Mat方法直接赋值获取的区域图像仍然会改变原图。若想直接复制出ROI区域,需要把原始图像进行clone即可。

- ③ namedWindow函数

void cv::namedWindow(
    const String &  winname,
    int   flags = WINDOW_AUTOSIZE
)

窗口的标识,可以填如下的值:

WINDOW_NORMAL设置了这个值,用户便可以改变窗口的大小
WINDOW_AUTOSIZE如果设置了这个值,窗口大小会自动调整以适应所显示的图像,并且不能手动改变窗口大小。
WINDOW_OPENGL 如果设置了这个值的话,窗口创建的时候便会支持OpenGL。

- ④ waitKey函数

int cv::waitKey(int delay = 0)

参数:等待的时间,单位:毫秒。0表示“永远”。

这个函数用来等待,读取和处理事件。当我们没有输入,只需要窗口停留,就可以将参数设为0。

- ⑤ opencv中的图像坐标系!
坐标系的坐标原点在图像的左上角。
OpenCV中坐标体系的X轴为图像水平从左往右;Y轴为图像矩形垂直从上往下。

在Point(x,y)和Rect(x,y)中,第一个参数x代表的是元素所在图像的列数,第二个参数y代表的是元素所在图像的行数。

在使用image.at(x1, x2)来访问图像中点的值的时候,x1并不是图片中对应点的x轴坐标,而是图片中对应点的y坐标。因此其访问的结果其实是访问image图像中的Point(x2, x1)点,即与image.at(Point(x2, x1))效果相同。



用上述模板匹配方法的缺点,代码如下,不具有尺度不变形!即在目标图像中只能抠出和模板图像一样大小尺度下最相似的块,但是实际上并不是最接近模板块比例的!
参考 https://blog.csdn.net/guduruyu/article/details/69231259 中minMaxLoc的用法,结合前面的截取图像的用法,得到匹配后的图像块:

#include <iostream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>  
using namespace cv;    
int main()
{
    Mat image_search = imread("reference.jpg");
    Mat image_template = imread("local1.jpg");
    Mat image_matched;
    //模板匹配
    matchTemplate(image_search, image_template, image_matched, TM_CCOEFF_NORMED);

    double minVal, maxVal;
    Point minLoc, maxLoc;
    //寻找最佳匹配的位置
    minMaxLoc(image_matched, &minVal, &maxVal, &minLoc, &maxLoc);

    Mat roi_image = image_search( Range(maxLoc.y, maxLoc.y + image_template.rows + 1), Range(maxLoc.x,  maxLoc.x + image_template.cols + 1) );
    namedWindow("SearchImage", WINDOW_NORMAL);
    namedWindow("TemplateImage", WINDOW_NORMAL);
    namedWindow("Template in SearchImage", WINDOW_NORMAL);
    imshow("SearchImage", image_search);
    imshow("TemplateImage", image_template);
    imshow("Template in SearchImage",roi_image);

    waitKey(0);
    return 0;
}


emmm这里考虑简化问题,先对模板图像进行缩放操作,再来匹配。

- 缩小图像操作函数
参考文章:https://blog.csdn.net/keith_bb/article/details/54620105 有两种方法可以缩放。
即下面两种等价:

//resize(image_template, resized_image_template, Size(), 0.2, 0.2, INTER_AREA);
resize(image_template, resized_image_template, Size(image_template.cols/5, image_template.rows/5 ), 0, 0, INTER_AREA);
  • 完整代码及测试结果
    ps下面第一个是尝试使用滑动条的代码(但是失败了。。。。)
#include <iostream>  

#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>  

#include <stdio.h>

using namespace cv;  

Mat image_search;
Mat image_template;
Mat image_matched;
Mat roi_image;
Mat resized_image_template;

const int picTypeMaxValue = 4;
int picTypeValue = 0;

//function: process picture templating
void picTPL(int, void*);

int main()
{
    image_search = imread("reference.jpg");
    namedWindow("SearchImage", WINDOW_NORMAL);
    imshow("SearchImage", image_search);

    char picTypeName[20];
    sprintf(picTypeName, "localview%d", picTypeMaxValue);

    namedWindow("TemplateImage", WINDOW_NORMAL);
    //namedWindow("ResizedTemplateImage", WINDOW_NORMAL);
    namedWindow("Template in SearchImage", WINDOW_NORMAL);

    //create track bar
    createTrackbar(picTypeName, "TemplateImage", &picTypeValue, 
                   picTypeMaxValue, picTPL);
    picTPL(picTypeValue, 0);

    waitKey(0);

    return 0;
}

void picTPL(int, void*)
{
    char name[20];
    //string name = "local" + to_string(picTypeValue) + " .jpg ";
    sprintf(name, "local%d.jpg", picTypeValue);
    image_template = imread(name);
    /*
    switch(picTypeValue)
    {
        case 0: 
            image_template = imread("local1.jpg");
        case 1: 
            image_template = imread("local2.jpg");
        case 2: 
            image_template = imread("local3.jpg");
        case 3: 
            image_template = imread("local4.jpg");
        case 4: 
            image_template = imread("local5.jpg");
    }
    */


    resize(image_template, resized_image_template, Size(), 0.2, 0.2, INTER_AREA);
    //resize(image_template, resized_image_template, Size(image_template.cols/5, image_template.rows/5 ), 0, 0, INTER_AREA);

    matchTemplate(image_search, resized_image_template, image_matched, 
                  TM_CCORR_NORMED);

    double minVal, maxVal;
    Point minLoc, maxLoc;

    //find the best position
    minMaxLoc(image_matched, &minVal, &maxVal, &minLoc, &maxLoc);

    roi_image = image_search( Range(maxLoc.y, maxLoc.y + resized_image_template.rows + 1), 
                              Range(maxLoc.x,  maxLoc.x + resized_image_template.cols + 1) );

    imshow("TemplateImage", image_template);
    //imshow("ResizedTemplateImage", resized_image_template);
    imshow("Template in SearchImage",roi_image);

}

所以还是改成老老实实地一个个处理然后保存图片。。。如下:::)

#include <iostream>  

#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>  

#include <stdio.h>

using namespace cv;  

Mat image_search;
Mat image_template;
Mat image_matched;
Mat roi_image;
Mat resized_image_template;
Mat resized_roi_image;

int main()
{
    image_search = imread("reference.jpg");
    //namedWindow("SearchImage", WINDOW_NORMAL);
    //imshow("SearchImage", image_search);

    //namedWindow("TemplateImage", WINDOW_NORMAL);
    //namedWindow("Template in SearchImage", WINDOW_NORMAL);

    int k;
    for(k = 1; k <= 5; k++)
    {
        char picName[15]; //name of 5 local pictures.
        sprintf(picName, "local%d.jpg", k);

        //read a local picture.
        image_template = imread(picName); 

        resize(image_template, resized_image_template, Size(), 0.2, 0.2, INTER_AREA);

        matchTemplate(image_search, resized_image_template, image_matched, 
                  TM_CCORR_NORMED);

        double minVal, maxVal;
        Point minLoc, maxLoc;

        //find the best position
         minMaxLoc(image_matched, &minVal, &maxVal, &minLoc, &maxLoc);

        roi_image = image_search( Range(maxLoc.y, maxLoc.y + resized_image_template.rows + 1), 
                              Range(maxLoc.x,  maxLoc.x + resized_image_template.cols + 1) );
        resize(roi_image, resized_roi_image, Size(2000,1500), 0, 0, INTER_LINEAR);

        //imshow("TemplateImage", image_template);
        //imshow("ResizedTemplateImage", resized_image_template);
        //imshow("Template in SearchImage",roi_image);

        char picSavePath[15];
        sprintf(picSavePath, "refblk%d.jpg", k);
        imwrite(picSavePath, resized_roi_image);
    }

    waitKey(0);

    return 0;
}

结果如图:
这里写图片描述



四、改进为模板不完整地出现在搜索图像中的情况

如果不考虑模板图像会有一部分不再搜索图像中,直接用上述类似代码直接调用模板匹配函数,代码和结果如下:

#include <iostream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>  
#include <stdio.h>

using namespace cv;  

Mat image_template;

Mat image_search0;
Mat image_search1;

Mat resized_image_template0;
Mat resized_image_template1;

Mat image_matched0;
Mat image_matched1;

Mat roi_image0;
Mat roi_image1;

Mat resized_roi_image0;
Mat resized_roi_image1;

int main()
{
    //read images
    image_search0 = imread("ref0.jpg");
    image_search1 = imread("ref1.jpg");
    image_template = imread("local.jpg"); 

    //show reference pictures
    namedWindow("SearchImage0", WINDOW_NORMAL);
    imshow("SearchImage0", image_search0);
    namedWindow("SearchImage1", WINDOW_NORMAL);
    imshow("SearchImage1", image_search1);

    //create a window for showing template pic
    namedWindow("TemplateImage", WINDOW_NORMAL);

    //create 2 windows for showing matched results
    namedWindow("Template in SearchImage0", WINDOW_NORMAL);
    namedWindow("Template in SearchImage1", WINDOW_NORMAL);

    resize(image_template, resized_image_template0, Size(), 0.38, 0.38, INTER_AREA);
    resize(image_template, resized_image_template1, Size(), 0.6, 0.6, INTER_AREA);

    matchTemplate(image_search0, resized_image_template0, image_matched0, 
                  TM_CCORR_NORMED);
    matchTemplate(image_search1, resized_image_template1, image_matched1, 
                  TM_CCORR_NORMED);

    double minVal0, maxVal0;
    Point minLoc0, maxLoc0;
    double minVal1, maxVal1;
    Point minLoc1, maxLoc1;

    //find the best position
    minMaxLoc(image_matched0, &minVal0, &maxVal0, &minLoc0, &maxLoc0);
    minMaxLoc(image_matched1, &minVal1, &maxVal1, &minLoc1, &maxLoc1);

    roi_image0 = image_search0( Range(maxLoc0.y, maxLoc0.y + resized_image_template0.rows + 1), 
                              Range(maxLoc0.x,  maxLoc0.x + resized_image_template0.cols + 1) );

    roi_image1 = image_search1( Range(maxLoc1.y, maxLoc1.y + resized_image_template1.rows + 1), 
                              Range(maxLoc1.x,  maxLoc1.x + resized_image_template1.cols + 1) );


    resize(roi_image0, resized_roi_image0, Size(2064,1544), 0, 0, INTER_LINEAR);
    resize(roi_image1, resized_roi_image1, Size(2064,1544), 0, 0, INTER_LINEAR);

    imshow("TemplateImage", image_template);

    imshow("Template in SearchImage0", resized_roi_image0);
    imshow("Template in SearchImage1", resized_roi_image1);

    char picSavePath0[15];
    char picSavePath1[15];

    sprintf(picSavePath0, "refblk0.jpg");
    imwrite(picSavePath0, resized_roi_image0);

    sprintf(picSavePath1, "refblk1.jpg");
    imwrite(picSavePath1, resized_roi_image1);

    waitKey(0);

    return 0;
}

这里写图片描述

可见匹配效果不好!!!

下转下一篇。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值