您的位置:首页 > 运维架构

【第二部分 图像处理】第3章 Opencv图像处理进阶【3 直方图与匹配 E】

2018-03-05 20:15 706 查看

3.6模板匹配

3.6.1模板匹配概述及原理

模板匹配是一项在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术.

那么,模板匹配怎么实现呢?我们需要2幅图像:

原图像 (I): 在这幅图像里,我们希望找到一块和模板匹配的区域;

模板 (T): 将和原图像比照的图像块;

我们的目标是检测最匹配的区域:



图1

为了确定匹配区域, 我们不得不滑动模板图像和原图像进行比较 :



图2

通过滑动, 我们的意思是图像块一次移动一个像素 (从左往右,从上往下). 在每一个位置, 都进行一次度量计算来表明它是 “好” 或 “坏” 地与那个位置匹配 (或者说块图像和原图像的特定区域有多么相似).

对于 T 覆盖在 I 上的每个位置,你把度量值 保存 到 结果图像矩阵 (R) 中. 在 R 中的每个位置(x,y)都包含匹配度量值:



图3

上图就是 TM_CCORR_NORMED 方法处理后的结果图像 R。最白的位置代表最高的匹配. 正如您所见, 红色椭圆框住的位置很可能是结果图像矩阵中的最大数值, 所以这个区域 (以这个点为顶点,长宽和模板图像一样大小的矩阵) 被认为是匹配的。实际上, 我们使用函数 minMaxLoc 来定位在矩阵 R 中的最大值点 (或者最小值, 根据函数输入的匹配参数) .

3.6.2模板匹配相关API及源码

OpenCV通过函数 matchTemplate 实现了模板匹配算法。

matchTemplate( )函数

···

C++: void matchTemplate( InputArray image,

InputArray temp,

OutputArray result,

i
4000
nt method)

···

【参数】

第一个参数,image – Image where the search is running. It must be 8-bit or 32-bit floating-point.

第二个参数,templ – Searched template. It must be not greater than the source image and have the same data type.

第三个参数,result – Map of comparison results. It must be single-channel 32-bit floating-point. If image is W×H and templ is w×hw×h , then result is(W−w+1)×(H−h+1)(W−w+1)×(H−h+1) .

第四个参数,method – Parameter specifying the comparison method (see below).

可用的方法有6个:

【方法一】平方差匹配 method=CV_TM_SQDIFF

这类方法利用平方差来进行匹配,最好匹配为0.匹配越差,匹配值越大.



【方法二】标准平方差匹配 method=CV_TM_SQDIFF_NORMED



【方法三】相关匹配 method=CV_TM_CCORR

这类方法采用模板和图像间的乘法操作,所以较大的数表示匹配程度较高,0标识最坏的匹配效果。



【方法四】标准相关匹配 method=CV_TM_CCORR_NORMED



【方法五】相关匹配 method=CV_TM_CCOEFF

这类方法将模版对其均值的相对值与图像对其均值的相关值进行匹配,1表示完美匹配,-1表示糟糕的匹配,0表示没有任何相关性(随机序列)。



在这里



【方法六】标准相关匹配 method=CV_TM_CCOEFF_NORMED



通常,随着从简单的测量(平方差)到更复杂的测量(相关系数),我们可获得越来越准确的匹配(同时也意味着越来越大的计算代价)。最好的办法是对所有这些设置多做一些测试实验,以便为自己的应用选择同时兼顾速度和精度的最佳方案。

matchTemplate( )函数源代码

/*【matchTemplate ( )源代码】*********************************************************
* @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)
* @源码路径:…\opencv\sources\modules\imgproc\src\templmatch.cpp
* @起始行数: 900行
********************************************************************************/
void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask )
{
if (!_mask.empty())
{
cv::matchTemplateMask(_img, _templ, _result, method, _mask);
return;
}

int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
CV_Assert( CV_TM_SQDIFF <= method && method <= CV_TM_CCOEFF_NORMED );
CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 );

bool needswap = _img.size().height < _templ.size().height || _img.size().width < _templ.size().width;
if (needswap)
{
CV_Assert(_img.size().height <= _templ.size().height && _img.size().width <= _templ.size().width);
}

CV_OCL_RUN(_img.dims() <= 2 && _result.isUMat(),
(!needswap ? ocl_matchTemplate(_img, _templ, _result, method) : ocl_matchTemplate(_templ, _img, _result, method)))

int numType = method == CV_TM_CCORR || method == CV_TM_CCORR_NORMED ? 0 :
method == CV_TM_CCOEFF || method == CV_TM_CCOEFF_NORMED ? 1 : 2;
bool isNormed = method == CV_TM_CCORR_NORMED ||
method == CV_TM_SQDIFF_NORMED ||
method == CV_TM_CCOEFF_NORMED;

Mat img = _img.getMat(), templ = _templ.getMat();
if (needswap)
std::swap(img, templ);

Size corrSize(img.cols - templ.cols + 1, img.rows - templ.rows + 1);
_result.create(corrSize, CV_32F);
Mat result = _result.getMat();

#ifdef HAVE_TEGRA_OPTIMIZATION
if (tegra::useTegra() && tegra::matchTemplate(img, templ, result, method))
return;
#endif

#if defined HAVE_IPP
bool useIppMT = false;
CV_IPP_CHECK()
{
useIppMT = (templ.rows < img.rows/2 && templ.cols < img.cols/2);

if (method == CV_TM_SQDIFF && cn == 1 && useIppMT)
{
if (ipp_sqrDistance(img, templ, result))
{
CV_IMPL_ADD(CV_IMPL_IPP);
return;
}
setIppErrorStatus();
}
}
#endif

#if defined HAVE_IPP
if (cn == 1 && useIppMT)
{
if (!ipp_crossCorr(img, templ, result))
{
setIppErrorStatus();
crossCorr( img, templ, result, result.size(), result.type(), Point(0,0), 0, 0);
}
else
{
CV_IMPL_ADD(CV_IMPL_IPP);
}
}
else
#endif
crossCorr( img, templ, result, result.size(), result.type(), Point(0,0), 0, 0);

if( method == CV_TM_CCORR )
return;

double invArea = 1./((double)templ.rows * templ.cols);

Mat sum, sqsum;
Scalar templMean, templSdv;
double *q0 = 0, *q1 = 0, *q2 = 0, *q3 = 0;
double templNorm = 0, templSum2 = 0;

if( method == CV_TM_CCOEFF )
{
integral(img, sum, CV_64F);
templMean = mean(templ);
}
else
{
integral(img, sum, sqsum, CV_64F);
meanStdDev( templ, templMean, templSdv );

templNorm = templSdv[0]*templSdv[0] + templSdv[1]*templSdv[1] + templSdv[2]*templSdv[2] + templSdv[3]*templSdv[3];

if( templNorm < DBL_EPSILON && method == CV_TM_CCOEFF_NORMED )
{
result = Scalar::all(1);
return;
}

templSum2 = templNorm + templMean[0]*templMean[0] + templMean[1]*templMean[1] + templMean[2]*templMean[2] + templMean[3]*templMean[3];

if( numType != 1 )
{
templMean = Scalar::all(0);
templNorm = templSum2;
}

templSum2 /= invArea;
templNorm = std::sqrt(templNorm);
templNorm /= std::sqrt(invArea); // care of accuracy here

q0 = (double*)sqsum.data;
q1 = q0 + templ.cols*cn;
q2 = (double*)(sqsum.data + templ.rows*sqsum.step);
q3 = q2 + templ.cols*cn;
}

double* p0 = (double*)sum.data;
double* p1 = p0 + templ.cols*cn;
double* p2 = (double*)(sum.data + templ.rows*sum.step);
double* p3 = p2 + templ.cols*cn;

int sumstep = sum.data ? (int)(sum.step / sizeof(double)) : 0;
int sqstep = sqsum.data ? (int)(sqsum.step / sizeof(double)) : 0;

int i, j, k;

for( i = 0; i < result.rows; i++ )
{
float* rrow = result.ptr<float>(i);
int idx = i * sumstep;
int idx2 = i * sqstep;

for( j = 0; j < result.cols; j++, idx += cn, idx2 += cn )
{
double num = rrow[j], t;
double wndMean2 = 0, wndSum2 = 0;

if( numType == 1 )
{
for( k = 0; k < cn; k++ )
{
t = p0[idx+k] - p1[idx+k] - p2[idx+k] + p3[idx+k];
wndMean2 += t*t;
num -= t*templMean[k];
}

wndMean2 *= invArea;
}

if( isNormed || numType == 2 )
{
for( k = 0; k < cn; k++ )
{
t = q0[idx2+k] - q1[idx2+k] - q2[idx2+k] + q3[idx2+k];
wndSum2 += t;
}

if( numType == 2 )
{
num = wndSum2 - 2*num + templSum2;
num = MAX(num, 0.);
}
}

if( isNormed )
{
t = std::sqrt(MAX(wndSum2 - wndMean2,0))*templNorm;
if( fabs(num) < t )
num /= t;
else if( fabs(num) < t*1.125 )
num = num > 0 ? 1 : -1;
else
num = method != CV_TM_SQDIFF_NORMED ? 0 : 1;
}

rrow[j] = (float)num;
}
}
}


3.6.3模板匹配实例

代码参看附件【demo1】。



图4平方差匹配



图5归一化平方差匹配



图6相关匹配法



图7归一化相关匹配法



图8相关系数匹配法



图9归一化相关系数匹配法

参考:

中文

英文

本章参考附件

点击进入
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐