模板匹配&&频域相位相关

8 篇文章 0 订阅
2 篇文章 0 订阅

        最近在做图像拼接项目,原打算用大众化的sift点匹配然后拼接。发现sift算法运算量巨大,不能满足拼接的实时要求,同时特征点法需要不同程度的干预(比如,需要随机抽样来删选优质的匹配点对),不满足自动匹准的要求。最后,只得尝试着用基于区域匹配的方法。    

      基于区域的方法其实就是模板匹配了,opencv已经为我们提供函数:

void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method );

        模板匹配就是在一幅图像中寻找和模板图像最相似的区域。该函数的功能为,在输入源图像中滑动框,寻找各个位置与模板图像的相似度,并将结果保存在结果矩阵中。该矩阵的每一个点的灰度表示与模板T的匹配程度。然后可以通过函数minMaxLoc()定位矩阵中的最大值(or最小值)。

匹配的方法有:

CV_TM_SQDIFF 平方差匹配法,最好的匹配为0,值越大匹配越差

CV_TM_SQDIFF_NORMED 归一化平方差匹配法

CV_TM_CCORR 相关匹配法,采用乘法操作,数值越大表明匹配越好

CV_TM_CCORR_NORMED 归一化相关匹配法

CV_TM_CCOEFF 相关系数匹配法,最好的匹配为1,-1表示最差的匹配

CV_TM_CCOEFF_NORMED 归一化相关系数匹配法

前面两种方法为越小的值表示越匹配,后四种方法值越大越匹配。


      当然,纯粹调用opencv的模板匹配来拼接图像,那真是太naive了。参考模板匹配,提出利用频域相位相关法拼接,大致思路如下:

1,对图像做必要的预处理;

2,对图像进行傅里叶变换,并进行相关度计算,求取两幅图像的重叠区域;

3,在重叠区域选取合适的子块计算图像的旋转因子。

4,线性融合。



最后稍微了解下opencv提供的源码:


void matchTemplate( const Mat& _img, const Mat& _templ, Mat& result, int method )
{
    CV_Assert( CV_TM_SQDIFF <= method && method <= CV_TM_CCOEFF_NORMED );
    //numType用来表示模板匹配的方式,0表示相关匹配法,1表示相关系数匹配法,2表示平方差匹配法
    //isNormed表示是否进行归一化处理,true表示进行归一化,false表示不进行归一化处理
    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, templ = _templ;
    if( img.rows < templ.rows || img.cols < templ.cols )
        std::swap(img, templ);

    CV_Assert( (img.depth() == CV_8U || img.depth() == CV_32F) &&
               img.type() == templ.type() );

   //crossCorr函数是将输入图像做了一次DFT变换(离散傅里叶变换),将空间域的图像转换到频率域中来进行处理,并将处理的结果存放在result中
    int cn = img.channels();
    crossCorr( img, templ, result,
               Size(img.cols - templ.cols + 1, img.rows - templ.rows + 1),
               CV_32F, Point(0,0), 0, 0);

    //如果是相关匹配方法,此处已经计算完毕,返回
    if( method == CV_TM_CCORR )
        return;

    //将模板看作单位1,计算每一个像元所占的百分比(也可以理解为整个模板面积为1,计算每个像元的面积)
    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 = CV_SQR(templSdv[0]) + CV_SQR(templSdv[1]) +
                    CV_SQR(templSdv[2]) + CV_SQR(templSdv[3]);//计算所有通道的方差和

        if( templNorm < DBL_EPSILON && method == CV_TM_CCOEFF_NORMED )
        {//如果所有通道的方差的和等于0,并且使用的方法是归一化相关系数匹配方法,则返回
            result = Scalar::all(1);
            return;
        }

        templSum2 = templNorm +
                     CV_SQR(templMean[0]) + CV_SQR(templMean[1]) +
                     CV_SQR(templMean[2]) + CV_SQR(templMean[3]);//计算所有通道的均值的平方和

        if( numType != 1 )//匹配方式不是相关系数,对模板均值向量和templNorm重新赋值
        {
            templMean = Scalar::all(0);
            templNorm = templSum2;
        }

        templSum2 /= invArea;
        templNorm = sqrt(templNorm);
        templNorm /= 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;
    }

     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 = (float*)(result.data + i*result.step);
        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 += CV_SQR(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 = 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;
        }
    }
}





  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Matlab中的FFT(快速傅里叶变换)函数可用于实现模板匹配定位算法。模板匹配定位是一种图像处理技术,用于在一幅图像中找寻目标图像的位置或区域。 首先,我们需要创建一个目标模板,该模板是我们希望在原始图像中定位的目标图像的子集。模板可以是任何感兴趣的对象,如人脸、车辆或文本等。 接下来,我们使用FFT函数对原始图像和目标模板进行傅里叶变换。FFT将这些图像从空域转换到频域,这样我们就可以在频域中进行匹配操作。对于原始图像和目标模板,我们分别获取其幅度谱和相位谱。 然后,我们需要对目标模板的幅度谱和相位谱进行归一化处理。这是为了使它们具有相似的尺度,并且在匹配过程中排除原始图像的亮度和对比度变化的影响。 接下来,我们对原始图像的幅度谱和相位谱进行相应的归一化处理。 然后,我们可以使用幅度谱和相位谱的对比度进行模板匹配。具体而言,我们对原始图像的幅度谱和相位谱与目标模板的幅度谱和相位谱进行逐像素相乘,并使用IFFT(逆傅里叶变换)将结果转换回空域。 最后,我们可以使用峰值检测方法在结果图像中找到最高峰值所对应的位置,该位置即为目标模板在原始图像中的定位。 通过这种方式,我们可以在Matlab中使用FFT函数实现模板匹配定位。傅里叶变换能够显著提高模板匹配的效率,使得目标定位更加精确和鲁棒。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值