学习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
模板匹配的基本方法使用图像补丁(模板),该图像补丁(模板)针对我们想要检测的搜索图像的特定特征而定制。
互相关输出在图像结构与掩模结构匹配的地方最高。
首先选取搜索图像的一部分作为模板。我们将搜索图像Search image称为S(x,y),其中(x,y)表示搜索图像中每个像素的坐标。我们将模板图像template称为T(x t,y
t),其中(xt,yt)表示模板中每个像素的坐标。然后,我们简单地将模板T(x t,y t)的中心(或原点)移动到搜索图像中的每个(x,y)点上,计算搜索图像和模板图像之间系数的乘积和。
具有最大值的位置是最佳位置。这种方法有时被称为“线性空间滤波”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这里考虑简化问题,先对模板图像进行缩放操作,再来匹配。
- 缩小图像操作函数