opencv模板匹配学习记录

位置: D:\opencv\sources\modules\imgproc\src\templmatch.cpp

源码:

void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask )
{
    CV_INSTRUMENT_REGION()

    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)))

    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

    CV_IPP_RUN_FAST(ipp_matchTemplate(img, templ, result, method))

    crossCorr( img, templ, result, result.size(), result.type(), Point(0,0), 0, 0);

    common_matchTemplate(img, templ, result, method, cn);
}

主要用到这几个函数:

  • ocl_matchTemplate
static bool ocl_matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method)
{
    int cn = _img.channels();//测试图像通道数

    if (cn > 4)
        return false;

    typedef bool (*Caller)(InputArray _img, InputArray _templ, OutputArray _result);

    static const Caller callers[] =
    {
        matchTemplate_SQDIFF, matchTemplate_SQDIFF_NORMED, matchTemplate_CCORR,
        matchTemplate_CCORR_NORMED, matchTemplate_CCOEFF, matchTemplate_CCOEFF_NORMED
    };
    const Caller caller = callers[method];//选择模板匹配方式

    return caller(_img, _templ, _result);//模板匹配
}

  • tegra::matchTemplate
  • ipp_matchTemplate
static bool ipp_matchTemplate( Mat& img, Mat& templ, Mat& result, int method)
{
    CV_INSTRUMENT_REGION_IPP()

    if(img.channels() != 1)
        return false;

    // These functions are not efficient if template size is comparable with image size
    if(templ.size().area()*4 > img.size().area())
        return false;

    if(method == CV_TM_SQDIFF)
    {
        if(ipp_sqrDistance(img, templ, result))//1
            return true;
    }
    else if(method == CV_TM_SQDIFF_NORMED)
    {
        if(ipp_crossCorr(img, templ, result, false))//2
        {
            common_matchTemplate(img, templ, result, CV_TM_SQDIFF_NORMED, 1);
            return true;
        }
    }
    else if(method == CV_TM_CCORR)
    {
        if(ipp_crossCorr(img, templ, result, false))
            return true;
    }
    else if(method == CV_TM_CCORR_NORMED)
    {
        if(ipp_crossCorr(img, templ, result, true))
            return true;
    }
    else if(method == CV_TM_CCOEFF || method == CV_TM_CCOEFF_NORMED)
    {
        if(ipp_crossCorr(img, templ, result, false))
        {
            common_matchTemplate(img, templ, result, method, 1);
            return true;
        }
    }

    return false;
}
}

Intel IPP图像库手册函数https://blog.csdn.net/x454045816/article/details/52153250

  1. ipp_sqrDistance 中用到 ippiSqrDistanceNormGetBufferSize:计算欧式距离度量的工作空间
  2. ipp_crossCorr 中用到 ippiCrossCorrNormGetBufferSize:计算十字距离的工作空间
  • common_matchTemplate
static void common_matchTemplate( Mat& img, Mat& templ, Mat& result, int method, int cn )
{
    if( method == CV_TM_CCORR )
        return;

    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;
    //numType用来表示模板匹配的方式,0表示相关匹配法,1表示相关系数匹配法,2表示平方差匹配法
    //isNormed表示是否进行归一化处理,true表示进行归一化,false表示不进行归一化处理

    double invArea = 1./((double)templ.rows * templ.cols);
    //将模板看作单位1,计算每一个像元所占的百分比(也可以理解为整个模板面积为1,计算每个像元的面积)

    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 )
        {//如果所有通道的方差的和等于0,并且使用的方法是归一化相关系数匹配方法,则返回
            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

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

    CV_Assert(sum.data != NULL);
    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 )//CV_TM_CCOEFF.CV_TM_CCOEFF_NORMED
            {
                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;
        }
    }
}
}

学习中,不断更新,请多多点赞评论指教

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大臉喵愛吃魚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值