opencv 多角度模板匹配

这篇具有很好参考价值的文章主要介绍了opencv 多角度模板匹配。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

总结一下实现多角度模板匹配踩的坑
一 、多角度匹配涉及到要使用mask,首先opencv matchTemplateMask自带的源码如下:

static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask )
{
    CV_Assert(_mask.depth() == CV_8U || _mask.depth() == CV_32F);
    CV_Assert(_mask.channels() == _templ.channels() || _mask.channels() == 1);
    CV_Assert(_templ.size() == _mask.size());
    CV_Assert(_img.size().height >= _templ.size().height &&
              _img.size().width >= _templ.size().width);

    Mat img = _img.getMat(), templ = _templ.getMat(), mask = _mask.getMat();

    if (img.depth() == CV_8U)
    {
        img.convertTo(img, CV_32F);
    }
    if (templ.depth() == CV_8U)
    {
        templ.convertTo(templ, CV_32F);
    }
    if (mask.depth() == CV_8U)
    {
        // To keep compatibility to other masks in OpenCV: CV_8U masks are binary masks
        threshold(mask, mask, 0/*threshold*/, 1.0/*maxVal*/, THRESH_BINARY);
        mask.convertTo(mask, CV_32F);
    }

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

    // If mask has only one channel, we repeat it for every image/template channel
    if (templ.type() != mask.type())
    {
        // Assertions above ensured, that depth is the same and only number of channel differ
        std::vector<Mat> maskChannels(templ.channels(), mask);
        merge(maskChannels.data(), templ.channels(), mask);
    }

    if (method == CV_TM_SQDIFF || method == CV_TM_SQDIFF_NORMED)
    {
        Mat temp_result(corrSize, CV_32F);
        Mat img2 = img.mul(img);
        Mat mask2 = mask.mul(mask);
        // If the mul() is ever unnested, declare MatExpr, *not* Mat, to be more efficient.
        // NORM_L2SQR calculates sum of squares
        double templ2_mask2_sum = norm(templ.mul(mask), NORM_L2SQR);
        crossCorr(img2, mask2, temp_result, Point(0,0), 0, 0);
        crossCorr(img, templ.mul(mask2), result, Point(0,0), 0, 0);
        // result and temp_result should not be switched, because temp_result is potentially needed
        // for normalization.
        result = -2 * result + temp_result + templ2_mask2_sum;

        if (method == CV_TM_SQDIFF_NORMED)
        {
            sqrt(templ2_mask2_sum * temp_result, temp_result);
            result /= temp_result;
        }
    }
    else if (method == CV_TM_CCORR || method == CV_TM_CCORR_NORMED)
    {
        // If the mul() is ever unnested, declare MatExpr, *not* Mat, to be more efficient.
        Mat templ_mask2 = templ.mul(mask.mul(mask));
        crossCorr(img, templ_mask2, result, Point(0,0), 0, 0);

        if (method == CV_TM_CCORR_NORMED)
        {
            Mat temp_result(corrSize, CV_32F);
            Mat img2 = img.mul(img);
            Mat mask2 = mask.mul(mask);
            // NORM_L2SQR calculates sum of squares
            double templ2_mask2_sum = norm(templ.mul(mask), NORM_L2SQR);
            crossCorr( img2, mask2, temp_result, Point(0,0), 0, 0 );
            sqrt(templ2_mask2_sum * temp_result, temp_result);
            result /= temp_result;
        }
    }
    else if (method == CV_TM_CCOEFF || method == CV_TM_CCOEFF_NORMED)
    {
        // Do mul() inline or declare MatExpr where possible, *not* Mat, to be more efficient.

        Scalar mask_sum = sum(mask);
        // T' * M where T' = M * (T - 1/sum(M)*sum(M*T))
        Mat templx_mask = mask.mul(mask.mul(templ - sum(mask.mul(templ)).div(mask_sum)));
        Mat img_mask_corr(corrSize, img.type()); // Needs separate channels

        // CCorr(I, T'*M)
        crossCorr(img, templx_mask, result, Point(0, 0), 0, 0);
        // CCorr(I, M)
        crossCorr(img, mask, img_mask_corr, Point(0, 0), 0, 0);

        // CCorr(I', T') = CCorr(I, T'*M) - sum(T'*M)/sum(M)*CCorr(I, M)
        // It does not matter what to use Mat/MatExpr, it should be evaluated to perform assign subtraction
        Mat temp_res = img_mask_corr.mul(sum(templx_mask).div(mask_sum));
        if (img.channels() == 1)
        {
            result -= temp_res;
        }
        else
        {
            // Sum channels of expression
            temp_res = temp_res.reshape(1, result.rows * result.cols);
            // channels are now columns
            reduce(temp_res, temp_res, 1, REDUCE_SUM);
            // transform back, but now with only one channel
            result -= temp_res.reshape(1, result.rows);
        }
        if (method == CV_TM_CCOEFF_NORMED)
        {
            // norm(T')
            double norm_templx = norm(mask.mul(templ - sum(mask.mul(templ)).div(mask_sum)),
                                      NORM_L2);
            // norm(I') = sqrt{ CCorr(I^2, M^2) - 2*CCorr(I, M^2)/sum(M)*CCorr(I, M)
            //                  + sum(M^2)*CCorr(I, M)^2/sum(M)^2 }
            //          = sqrt{ CCorr(I^2, M^2)
            //                  + CCorr(I, M)/sum(M)*{ sum(M^2) / sum(M) * CCorr(I,M)
            //                  - 2 * CCorr(I, M^2) } }
            Mat norm_imgx(corrSize, CV_32F);
            Mat img2 = img.mul(img);
            Mat mask2 = mask.mul(mask);
            Scalar mask2_sum = sum(mask2);
            Mat img_mask2_corr(corrSize, img.type());
            crossCorr(img2, mask2, norm_imgx, Point(0,0), 0, 0);
            crossCorr(img, mask2, img_mask2_corr, Point(0,0), 0, 0);
            temp_res = img_mask_corr.mul(Scalar(1.0, 1.0, 1.0, 1.0).div(mask_sum))
                           .mul(img_mask_corr.mul(mask2_sum.div(mask_sum)) - 2 * img_mask2_corr);
            if (img.channels() == 1)
            {
                norm_imgx += temp_res;
            }
            else
            {
                // Sum channels of expression
                temp_res = temp_res.reshape(1, result.rows*result.cols);
                // channels are now columns
                // reduce sums columns (= channels)
                reduce(temp_res, temp_res, 1, REDUCE_SUM);
                // transform back, but now with only one channel
                norm_imgx += temp_res.reshape(1, result.rows);
            }
            sqrt(norm_imgx, norm_imgx);
            result /= norm_imgx * norm_templx;
        }
    }
}

可以看到使用用了四次dft来计算卷积,目标图像要与mask卷三次,来计算目标图像在模板区域内的和,平方和。其中最后一次CCorr(I, mask2)可以省略掉,它跟CCorr(I, mask)是一样的,因为mask是二值。

 // CCorr(I, T'*M)
        crossCorr(img, templx_mask, result, Point(0, 0), 0, 0);
        // CCorr(I, M)
        crossCorr(img, mask, img_mask_corr, Point(0, 0), 0, 0);
        crossCorr(img2, mask2, norm_imgx, Point(0,0), 0, 0);
        crossCorr(img, mask2, img_mask2_corr, Point(0,0), 0, 0);

所以耗时的部分就是这三次卷积,可以用simd加速。opencv以及封装了simd指令,怎么用看这位博主OpenCV 4.x3.4.x版本以上也有中提供了强大的统一向量指令
实测,在高金字塔进行全局匹配的时候,用crossCorr来计算卷积,而用simd计算局部卷积,这样更快。
二、模板的旋转

  1. 创建一个paddingImg,其尺寸是模板的对角线长+1,然后再将模板图像拷贝到paddingImg中间去,这样旋转就不会超出边界,代码如下。
  2. 还有一个点,就是旋转的插值是最好使用INTER_NEAREST,试过其他几种,在比较高的金字塔层中匹配,会出现匹配不到的情况。
void NccMatch::RotateImg(Mat mImg, int nAngle, Mat& outImg, Mat& mask,RotatedRect* ptrMinRect, Point2d pRC)
{
    if (mImg.depth()    != CV_32F) { mImg.convertTo(mImg, CV_32F); }
    int nDiagonal       = sqrt(pow(mImg.cols, 2) + pow(mImg.rows, 2)) + 1;
    Mat paddingImg      = -1 * Mat::ones(Size(nDiagonal, nDiagonal), mImg.type());
    Rect roi(Point((nDiagonal - mImg.cols) * 0.5, (nDiagonal - mImg.rows) * 0.5), Size(mImg.cols, mImg.rows));
    mImg.copyTo(paddingImg(roi));
    Mat M = getRotationMatrix2D(Point2d(paddingImg.cols * 0.5, paddingImg.rows * 0.5), nAngle, 1.0);
    warpAffine(paddingImg, outImg, M, paddingImg.size(), INTER_NEAREST, 0, Scalar::all(-2));
    mask = outImg.clone();
    threshold(mask, mask, -1, 1, THRESH_BINARY);
    //RotatedRect rRect(Point2d(paddingImg.cols * 0.5, paddingImg.rows * 0.5), Size2f(mImg.cols, mImg.rows), -nAngle);
    ptrMinRect->center  = Point2d(paddingImg.cols * 0.5, paddingImg.rows * 0.5);
    ptrMinRect->size    = Size2f(mImg.cols, mImg.rows);
    ptrMinRect->angle   = -nAngle;
    return;
}

三,匹配
一定要把目标图像进行padding,确保模板能滑过每一个像素,不然会发现有些图,死活都匹配不上了。

最后,实现的效果如下,有些测试图是用的这位大佬的https://github.com/DennisLiu1993/Fastest_Image_Pattern_Matching
步长和亚像素的计算也是参考这位大佬。
opencv 多角度模板匹配
目标图像2592x1944,模板149x150,匹配角度[0,360],耗时约为:34ms
opencv 多角度模板匹配
目标图像646x492,模板214x98,匹配角度[0,360],金字塔层数=4,耗时约为:14ms
opencv 多角度模板匹配
目标图像2592x1944,模板466x135,匹配角度[0,360],金字塔层数=5,耗时约为:58ms
opencv 多角度模板匹配目标图像830x822,模板209x95,匹配角度[0,360],金字塔层数=4,耗时约为:45ms
更新:
增加了模板文件的序列化 ,把ncc match相关功能封装成了dll,用WPF做了个简单的Demo,如下:
opencv 多角度模板匹配
opencv 多角度模板匹配
opencv 多角度模板匹配文章来源地址https://www.toymoban.com/news/detail-470103.html

到了这里,关于opencv 多角度模板匹配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Stable Diffusion 多角度人设立绘快速生成多种方法

    对于插画师构建人物立绘图设计一套多方位的人设可能要很久,但是使用SD进行操作的话就非常简单了,这个利用ControlNet骨骼图进行配置操作。 供一些样图参考,也可以使用ADetailer进行人物相关部位的修复。 准备骨骼图。 或者人物站姿立绘图。 绘制需要事先安装ControlNet。

    2024年02月13日
    浏览(28)
  • 文心一言 v.s. ChatGPT:多角度对比测评“追赶者”能否超越?

    ChatGPT自发布以来就引发了关注热潮,如今国内大模型的发展也是如火如荼、百花齐放:比如百度的文心一言、阿里的通义千问、讯飞的星火大模型等等,那么作为后起之秀的国内大模型与ChatGPT相比哪个更好用呢?“追赶者”能否实现超越?为了回答这个问题,本文将基于文

    2024年01月22日
    浏览(53)
  • 多角度分析开源ERP系统:odoo、ERP5、ERPnext

    本文将从多个方面比较分析Odoo、ERP5和ERPNext三个开源ERP系统,主要从以下角度进行分析:概述、特点、应用领域、功能、易用性和社区支持等方面,帮助读者更好地了解这三个系统并选择最适合自己的ERP系统。 Odoo、ERP5和ERPNext是三个开源ERP系统,都具有基本的财务和物流管理

    2024年02月01日
    浏览(37)
  • 连接万物,创造未来,多角度看物联网技术如何影响我们的生活

    连接万物,创造未来。从智能家居到智慧医疗,从智能车联到智慧城市,物联网技术的影响已经悄然渗透到了我们的方方面面。接下来我们将从物联网技术概述、发展的历史原因、物联网技术应用、创新和挑战几个角度深度了解物联网已经深入我们日常生活。 物联网技术(

    2024年02月07日
    浏览(55)
  • Docker 容器生命周期:创建、启动、暂停与停止----从创建到停止多角度分析

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月16日
    浏览(37)
  • 【Linux系统基础快速入门详解】find与指纹多角度分析与解决网站页面恶意修改

    鱼弦:CSDN内容合伙人、CSDN新星导师、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen) 原理详细解释: 指纹分析:指纹分析是一种通过对网站页面进行多角度的特征提取、比对和分析,以识别和解决网站页面恶意修改的方

    2024年02月07日
    浏览(32)
  • 文心一言是中文版的ChatGPT?多角度分析猜测文心一言到底是什么?

    ChatGPT爆火网络,一时间风靡不断。 AI替代人类工作的传言四起 宣布ChatGPT类似产品的公司股价大涨,这到底是什么? 国产的类似产品到底到了什么程度? 让我们一起来分析(猜猜)看! 声明:以下数据均来自官方及部分数据收集,以中国普通网民身份角度阐述,不存在捧踩,

    2024年02月02日
    浏览(33)
  • C语言深度解析:函数的形参和实参与传值和传址(多角度分析,让你有一种相见恨晚的感觉!!!!)

    目录 一、前言 二、函数的形参和实参 🍑实参 🍎形参  🍇形参与实参之间的区别  🍐形参与实参之间的联系(重点!!!!)   三、函数的传值和传址(超重点!!!) 🍒例题分析 🍍错误分析  🥝 错误修改 🍋 深入理解 值传递与址传递  💦概念理解 四、进阶练习(

    2024年02月04日
    浏览(39)
  • 图像模板匹配 opencv c++实现

    用T表示模板图像,I表示待匹配图像,切模板图像的宽为w高为h,用R表示匹配结果,匹配过程如下图所示: 1、平方差匹配算法method=TM_SQDIFF 这类方法利用平法差来进行匹配,最好匹配为0,而若匹配越差,匹配值则越大 2、归一化平方差匹配法method=TM_SQDIFF_NORMED 3、相关匹配法

    2024年02月04日
    浏览(32)
  • 模板匹配相关概念与几种不常用的模板匹配(学都学了,了解一下)

    模板匹配指的是通过模板图像与测试图像之间的比较,找到测试图像上与模板图像相似的部分,这是通过计算模板图像与测试图像中目标的相似度来实现的,可以快速地在测试图像中定位出预定义的目标 是图像处理中最基本、最常用的匹配方法 为感兴趣的对象 创建模型 :使

    2024年02月08日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包