一、理论基础
模板匹配技术是为了寻找子图像。对于大小为MxN的图像f(x,y)和大小为JxK的子图像w(x,y),f与w的相关可表示为:
其种,x=0,1,2,…N-K,y=0,1,2,…M-J。
此处的目的是寻找匹配而不是对f(x,y)进行滤波操作,因此w的原点被设置在子图像的左上角,并且公式给出的形式也完全适用于J和K 为偶数的情况。
计算相关c(x,y)的过程就是在图像f(x,y)中逐点地移动子图像w(x,y),使w 的原点和点(x,y)重合,然后计算w与f中被w覆盖的图像区域对应像素的乘积之和,以此计算结果作为相关图像c(x,y)在(x,y)点的响应。相关可用于在图像f(x,y)中找到与子图像w(x,y)匹配的所有位置。实际上,当w按照上段中描述的过程移过整幅图像f之后,最大的响应点(x,y)即为最佳匹配的左上角点。我们
也可以设定一个阈值T,认为响应值大于该阈值的点均是可能的匹配位置。
模板匹配相关公式:
二、代码
例:
void MyFrame::OnPaint()
{
CPaintDC dc(this);
FILE* fp1;
if (fopen_s(&fp1, "C:\\Users\\Administrator\\Desktop\\BMP1\\BMP1\\1.bmp", "rb") != 0) {
return;
}
bmpFileHeader bfHead1;
bmpInfoHeader biHead1;
fread_s(&bfHead1, 14, 14, 1, fp1);
fread_s(&biHead1, 40, 40, 1, fp1);
uchar* p1 = (uchar*)malloc(biHead1.biSizeImage * sizeof(uchar));
fseek(fp1, bfHead1.bfOffBits, SEEK_SET);
fread_s(p1, biHead1.biSizeImage * sizeof(uchar), biHead1.biSizeImage * sizeof(uchar), 1, fp1);
FILE* fp2;
if (fopen_s(&fp2, "C:\\Users\\Administrator\\Desktop\\BMP1\\BMP1\\2.bmp", "rb") != 0) {
return;
}
bmpFileHeader bfHead2;
bmpInfoHeader biHead2;
fread_s(&bfHead2, 14, 14, 1, fp2);
fread_s(&biHead2, 40, 40, 1, fp2);
uchar* p2 = (uchar*)malloc(biHead2.biSizeImage * sizeof(uchar));
fseek(fp2, bfHead2.bfOffBits, SEEK_SET);
fread_s(p2, biHead2.biSizeImage * sizeof(uchar), biHead2.biSizeImage * sizeof(uchar), 1, fp2);
double dSumT=0; //模板元素的平方和
double dSumS; //图像子区域元素的平方和
double dSumST; //图像子区域和模板的点积
double R; //响应值
double MaxR=0;//记录当前的最大响应
//最大响应出现位置
int nMaxX;
int nMaxY;
//计算2.bmp模板元素的平方和
unsigned long x1 = 0;//原图
unsigned long x2 = 0;//模板图
x1 = 0;
for (int i = 0; i < biHead1.biHeight; i++)
{
for (int j = 0; j < biHead1.biWidth; j++)
{
dc.SetPixel(j / time, (biHead1.biHeight - i) / time, RGB(p1[x1 + 2], p1[x1 + 1], p1[x1]));
x1 += 3;
}
}
for (int m = 0; m < biHead2.biHeight; m++)
{
for (int n = 0; n < biHead2.biWidth; n++)
{
// 模板图像第x个象素的灰度值,b通道
dSumT += (double)p2[x2] * p2[x2];
x2 = x2 + 3;
}
}
//找到图像中最大响应的出现位置
for (int i = 0; i < biHead1.biHeight - biHead2.biHeight + 1; i=i+ biHead2.biHeight/4)
{
for (int j = 0; j < biHead1.biWidth - biHead2.biWidth + 1; j++)
{
dSumST = 0;
dSumS = 0;
x2 = 0;
for (int m = 0; m < biHead2.biHeight; m++)
{
x1 = 3 * (i * biHead1.biWidth + j) + 3 * m * biHead1.biWidth;
for (int n = 0; n < biHead2.biWidth; n++)
{
// 原图像第i+m行,第j+n列象素的灰度值b通道
int nGraySrc =p1[x1];
x1 = x1 + 3;
// 模板图像第m行,第n个象素的灰度值b通道
int nGrayTpl = p2[x2];
x2 = x2 + 3;
dSumS += (double)nGraySrc * nGraySrc;
dSumST += (double)nGraySrc * nGrayTpl;
}
}
R = dSumST / (sqrt(dSumS) * sqrt(dSumT));//计算相关响应
//与最大相似性比较
if (R > MaxR)
{
MaxR = R;
nMaxX = j;
nMaxY = biHead1.biHeight - i;
}
}
}
//在原图上画框框
dc.MoveTo(nMaxX / time, nMaxY / time);
dc.LineTo((nMaxX + biHead2.biWidth) / time, nMaxY / time);
dc.MoveTo(nMaxX / time, nMaxY / time);
dc.LineTo(nMaxX / time, (nMaxY - biHead2.biHeight) / time);
dc.MoveTo((nMaxX + biHead2.biWidth) / time, nMaxY / time);
dc.LineTo((nMaxX + biHead2.biWidth) / time, (nMaxY - biHead2.biHeight) / time);
dc.MoveTo(nMaxX / time, (nMaxY - biHead2.biHeight) / time);
dc.LineTo((nMaxX + biHead2.biWidth) / time, (nMaxY - biHead2.biHeight) / time);
}
注意:匹配过程时间耗费过多,于是可以多跳过几个像素点,可以看到在for循环处
i=i+ biHead2.biHeight/4
结果: