基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码

这篇具有很好参考价值的文章主要介绍了基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文后代码,优化效果图结尾处,最快3ms得到匹配结果
NCC,全称为Normalized Cross Correlation,即归一化互相关系数, 在模板匹配中使用的非常非常广泛,也是众多模板匹配方法中非常耀眼的存在, 这个匹配的理论核心基础公式如下:
基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码
其实Opencv的matchTemplate函数使用的就是这个公式,实测直接使用这个公式实现无旋转角度的、单目标的模板匹配时用时大概26ms(其实这个结果已经满足大部分使用需求了),但是本博主响应国家号召,秉着自强不息、实事求是、勇于钻研的心态,决定从公式层面重写该算法,毕竟opencv的函数优化起来很难,想要达到10ms之内更是几乎不可能,而重新实现公式可以继续优化,最终实现10ms内实现带旋转的、单目标检测(其实目标数量并不重要,无论是单目标还是多目标,都要检测整张图像,而单目标无非就是只在乎那个得分最高的,对于算法来说,二者并无差别)
他的理论匹配度范围是[-1,1],为-1时表示2副图像的极性完全相反(原图和反色后的图),为1则表示两幅图完全一样。一般我们在计算NCC的时候都是取的绝对值,因此,通常NCC的取值为[0,1],值越大,表示两幅图像越相似。
但是实际编程实现时,一眼可以看出,这个公式不合适,因为模板匹配的模板在大多数检测中都是固定的,所以关于模板的数值可以提前计算好,不放在整个公式里,所以改进成用下面的式子实现编码。
基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码
这个式子看上去更为复杂,我们可以把他拆解为7个部分,11道来。

  1. 这个留到最后在说。
  2. T代表的是模板,那么②对于固定的模板来说就是一个定值,在匹配前可以直接计算好,无需担忧耗时问题。
  3. I 表示的搜索图像中的和模板一样大的一个子块,很明显这个累加有多重方法可以快速的实现,比如积分图的遍历,这一项也是和参数无关的。
  4. 第四项处理方式同 ②项,无需多言。
  5. 第五项完全同第二项,同时四和五项作为一个整体也可以提前计算好,不参与匹配过程的计算。
  6. 第六项处理方式同第三项,也无需多言。
  7. 第七项完全同第三项,直接使用。

前面的分析表明,第二至第七项要买可以作为常量提前计算好,要么就可以通过某种技术实现O(1)的快速计算,那么现在我们再回过头来在看第①项,他是模板图像和搜索图像同面积区域像素的一个卷积,这个是无法用某种优化技巧去实现和模板大小无关的快速实现的,注定了他就是NCC计算式中最为耗时的部分。
那么到这里开始,咱们已经可以开始霸王硬上弓了,千里之行始于足下嘛,先看看效果再说!
基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码
很好,四秒多,基于O(n2)实现的不带旋转角度的匹配,速度还算在预料之中吧,下面先把这个代码拿出来讲讲,(本文就是讲到实现公式,提速部分会简单讲讲,但是由于种种原因,不会放出代码哦)。
首先就是界面的搭建!忽略谢谢,本博主还是个平平无奇的小学生,打上马赛克了我就安全啦。
重点就是模板的训练和模板的计算,所谓模板的训练就是把模板的数据,就是公式里包含T的部分优先计算出来。
首先定义一个存数据的结构体(代码讲解直接写注释里了)

typedef struct picture
{
    Mat    Template_gray ;
    int    cols        =0;//模板图像行列数
    int    rows        =0;

    int    x           =0;//匹配目标像素点坐标
    int    y           =0;

    double one         =0;//1234567顾名思义喽
    double two         =0;
    double three       =0;
    double four        =0;
    double five        =0;
    double six         =0;
    double seven       =0;

    double sum         =0;//灰度和
    double mean        =0;//均值
    double stddev      =0;//均差
    int    minpiex     =1;//像素精度1-10
    int    angle_start =0;
    int    angle_end   =0;
    int    angle_step  =0;
    double minscare    =0;

    double NCC_Result  =0;
    double temp        =0;//用来比较得分大小,最终得分取最小值设为1,取最大值设为0

}picture;

然后训练模板:

void train(Mat Template,picture &src)//输入RGB图像
{
    picture_init(src);//初始化定义的结构体,防止变量值被二次累加出错
    cvtColor(Template,src.Template_gray,CV_BGR2GRAY);

    tep_sum(src.Template_gray,src);

//    qDebug()<< "src.two:" <<src.two <<endl;
//    qDebug()<< "src.mean:" << src.mean <<endl;
}

void tep_sum(Mat src_gray , picture &src)//输入灰度图的灰度值和、均值、均方差
{
    for(int i=0;i<src_gray.rows;i++)
    {
        for(int j=0;j<src_gray.cols ;j++)
        {
            src.sum += src_gray.ptr<uchar>(i)[j];//i行j列
            src.four += (src_gray.ptr<uchar>(i)[j])^2;
        }
    }

//    Mat  mat_mean, mat_stddev;
//    meanStdDev(src_gray, mat_mean, mat_stddev);//求灰度图像的均值、均方差,这是opencv自带的函数,我用它的结果和我的结果做对比,验证我自己的算法结果是否正确

//    src.mean = mat_mean.at<double>(0, 0);
//    src.stddev = mat_stddev.at<double>(0, 0);

    src.two = src.sum/(src_gray.cols*src_gray.rows);//two理应等于mean,相等则表示算法没出错
    src.five=src.two*src.sum;
//    qDebug()<< "2:"<<src.two <<endl;
//    qDebug()<< "mean:"<<src.mean <<endl;
}

然后就可以开始计算了:

void NCC_Match(Mat image_in , picture &src)
{
    for(int x=0; x< image_in.cols - src.cols - src.minpiex; x = x + src.minpiex)
    {
        for(int y=0; y< image_in.rows - src.rows - src.minpiex; y = y + src.minpiex)//注意防止溢出
        {
            NCC_sum(x , y , image_in , src );
            src.NCC_Result = fabs(src.one - src.two*src.three) / ( sqrt(fabs(src.four-src.five)) * sqrt(fabs(src.six-src.seven)));//最终得分
            if(src.NCC_Result > 0)
            {
//                qDebug()<< "x:" << x << "   y:" << y << "   score:"<< src.NCC_Result  <<endl;

                if(src.NCC_Result > src.temp)
                {
                    src.temp = src.NCC_Result;
                    src.x = x;
                    src.y = y;
                }
            }

            src.one   =0;//数字清零,不然下面会重复累加,这里很重要!博主含泪分享
            src.three =0;
            src.six   =0;
        }
    }
}

void NCC_sum(int x,int y,Mat NCC_Picture,picture &src)
{
    Mat NCC_Picture_gray;

    cvtColor(NCC_Picture,NCC_Picture_gray,CV_BGR2GRAY);
    Mat aoi = getaoi(NCC_Picture_gray,x,y,src.Template_gray.cols,src.Template_gray.rows);//自定义函数,矩形抠图

    for(int i=0; i<aoi.rows; i++)
    {
        for(int j=0;j<aoi.cols;j++)
        {
            src.three += aoi.ptr<uchar>(i)[j];
            src.six +=(aoi.ptr<uchar>(i)[j])^2;
            src.one +=src.Template_gray.ptr<uchar>(i)[j] * aoi.ptr<uchar>(i)[j];
        }
    }

//    Mat  NCC_mean, NCC_stddev;
//    meanStdDev(aoi, NCC_mean, NCC_stddev);//同上,用来检验算法正确性

//    int NCCmean = NCC_mean.at<double>(0, 0);
//    int temp1 = src.three/(aoi.cols * aoi.rows);

//    qDebug()<< "3_average:"<<temp1 <<endl;
//    qDebug()<< "3_mean:"<<NCCmean <<endl;
    src.seven = (src.three*src.three)/(src.cols*src.rows);
}

这样匹配的整个流程算是结束了,这是最耗时的算法,大家可以根据自己的需要进行改进,下面展示一下自定义函数部分:

void picture_init(picture &src)//初始化
{
    src.one =0;
    src.two =0;
    src.three =0;
    src.four =0;
    src.five =0;
    src.six =0;
    src.seven =0;
    src.sum =0;
    src.stddev =0;
    src.mean =0;
    src.NCC_Result =0;
    src.Template_gray.release();

    src.x           =0;//匹配目标像素点坐标
    src.y           =0;

    src.temp        =0;//用来比较得分大小,最终得分取最小值设为1,取最大值设为0
}

Mat getaoi(Mat img,int x,int y,int cols,int rows)//x、y为左上角坐标,矩形抠图
{
    Rect m_select = Rect(x,y,cols,rows);
    img = img(m_select);

    return img;
}

到这,就可以将结果显示在ui中,借助代码可以更好的理解公式,而不是供大家抄袭,这段代码本身并不值得抄袭,价值意义不大,只是单纯的实现,大佬们自然能想出O(1)的算法去解决,至于带旋转角度的只需要将模板旋转后多次带入函数中计算即可。
下面简单讲讲加速的思路

为了得到更快的搜索速度,一个最容易想到的策略就是图像金字塔,图像金字塔每增加一层,图像的点数和模板的点数救减少4倍(理论数据,实际由于非2的宽度和高度,不一定正好是4倍),那么不考虑Cache等其他的因素,理论上每增加一层金字塔救可以提速16倍,因此,如果我们构建了一个4层的金字塔,那么在第4层金字塔上的一次完整匹配,其计算的次数和原始的数据相比,就能减少4096倍。

我们先以无旋转单目标为例进行简单的说明,当我们在金字塔最高层进行一次完整的匹配后,我们可以找一个全局的极值点,这就是在顶层匹配时的最佳匹配位置,此时,我们可以将顶层匹配的结果映射到金字塔的下一层中,简单的说就是将找到的匹配点坐标的X/Y值乘以2,那么为了保证在下一层中的精度,此时的搜索区域需要适当的增大,比如选择匹配点周围55或者77的一个区域,然后在这感兴趣的区域中再进行一个局部的匹配计算,此时只需要计算25或者49次小匹配的计算,当计算完毕后,再次提取出这个小区域的极值,作为本层的最终种子点,重复这个过程,直到金字塔的最底层(即原始搜索图像)为止。(自学金字塔算法即可,各种插值方法也不在这里赘述。)

除了改进算法本身时间复杂度以外(学过数据结构的都知道,无非是空间换时间),还有一个方法就是加速包,大家自行搜索SIMD指令实现算法提速,学习一下使用即可,大概可以有十倍运算能力的提高。

有人说卷积可以有FFT实现优化,没错,非常同意你的观点,但是,FFT虽然其第一个F代表了Fast,但是呢他在傅里叶的世界是快的,在我们模板匹配的空间内他受到了一种无形的压迫,在实际检测应用中还是无法接受的。

还有需要考虑的问题大概有:金字塔层数如何控制、角度离散化的间距如何设置、模板的旋转如何让处理、旋转后产生的无效部分如何处理等等

最后巴拉巴拉巴拉巴拉优化,有一定的效果,但是也是在特定的情况下初步实现了10ms内匹配成功,效果如下:其中的优化方法均是我前文提到过的,对于简单背景的单目标识别,10ms内绰绰有余了吧。
基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码
其实到这里还有大量细节没处理好,也没加上旋转角度,所以时间再次压缩也不是不可能,再优化一波图像处理的细节,就又有提升,3ms也不是不可能吧,但是此时定位准确度降低,日后优化吧,再加上旋转匹配,搭配SIMD加速指令,最终的匹配时间应该也相对来说比较可观的。(其实也是哗众取宠吧,这个结果并不能代表什么,只是阶段性的,特定条件下的处理结果,真正应用还是远远不够的,还需要大量的工作)
基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码

我也是大量阅读了网络上的资料,并通过大量尝试才有这么一点点的理解和看法,就写点东西算是回馈网络吧,只供大家入门模板匹配使用,博主本人也是刚入门小菜鸡一枚,就像我在前面的文章里说的,我们都是入门人

博主本人也在努力研究这些算法,奈何博主要学习,实在是力不从心,每天抽空研究算法竟成了休闲娱乐,计算机类越来越卷,但本博主仍不会轻言放弃,两者齐头并进,只是这研究进度自然也就缓慢了下来,后面有时间有精力,博主再慢慢更新。能看到这里的大概都是真想学点东西的人吧,等博主真正实现到10ms内后一定不会忘了大家的!文章来源地址https://www.toymoban.com/news/detail-474417.html

到了这里,关于基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • OpenCV数字图像处理实战二:模板匹配(C++)

    (1)首先需要一个模板图像 T(子图像)和一个待检测的图像(源图像 S) (2)在待检测图像从左到右,从上到下计算模板图像与重叠子图像的匹配度,匹配度越高,两者相同的可能性越大。 3.1 单模板匹配 注意:result的长宽正好是(原图-模板图)的长宽,result图中白亮程

    2024年02月15日
    浏览(33)
  • 基于qt+halcon实现视觉定位模板匹配【附部分源码】

    本文主要实现基于qt5.3做一个视觉定位识别的功能,halcon版本使用的是halcon12.0,调用halcon的dll来实现二次开发,下边从头开始设置。 与前面的python版、MFC版、Qt版一样,可供不同的开发者进行学习使用。 编程环境:qt5.3 halcon12.0 IDE: VisualStudio 2010 本次项目的效果视频: 基于

    2024年01月24日
    浏览(45)
  • 基于opencv的c++图像处理(灰度变换)

    基于opencv的c++接口,实现常用的图像灰度变换方法,包括了线性变换、图像反转、对数变换和伽马变换。 函数 cv::normalize 标准化缩放和移动输入数组元素 当 normType=NORM_MINMAX 时(仅适用于密集数组)。可选掩码指定要规范化的子数组。这意味着在子数组上计算范数或 min-n-ma

    2024年02月04日
    浏览(50)
  • OpenCv案例(十二):基于OpenCVSharp学习之模板匹配寻找距离中心位置最近的目标

    1:需求:在原图中,有多个特征点和模板图像一模一样,因此,寻找原图中中心位置最近的特征点位(模板匹配详解);原图如下所示: 模板图像:                                                                          2:现要求匹配找

    2024年01月24日
    浏览(44)
  • 基于opencv4.5多目标/多角度与多尺度模板匹配(含源码)

    在OpenCV中有个用于模板匹配的基本函数matchTemplate(),该函数使用某模板在搜索图像中进行搜索时,只能搜索到和模板完全一样的地方,一旦在搜索图像中要搜索的区域相较于模板是旋转了、放大缩小了或者部分遮掩了就无法匹配到结果了,实际项目应用不太友好. 本文主要介绍

    2024年02月02日
    浏览(44)
  • 【OpenCV-Python】——单/多模板匹配&分水岭算法图像分割&图像金字塔&交互式前景提取

    目录 前言: 1、模板匹配 1.1 单目标匹配 1.2 多目标匹配 2、图像分割

    2024年02月07日
    浏览(35)
  • OpenCV 笔记:cv2.matchTemplate() 单模板匹配和多模板匹配

            模板匹配是用来在一副大图中搜寻查找模版图像位置的方法。         模板匹配实现简单(2~3行代码),计算效率高,不需要执行阈值化、边缘检测等操作来生成二值化图像。 但是: 如果输入图像中存在变化的因素,包括旋转、缩放、视角变化等,模板匹配很容

    2024年02月05日
    浏览(35)
  • OpenCV 模板匹配 matchTemplate

    模板匹配是一项在一副图像中寻找与另一幅模板图像最匹配(相似)部分的技术。模板匹配不是基于直方图的,而是通过在输入图像上滑动图像块(模板)同时对比相似度,来对模板和输入图像进行匹配的一种方法。 应用: (1)目标查找定位 (2)运动物体跟踪 image:待搜

    2024年02月04日
    浏览(49)
  • OpenCV(十九):模板匹配

    1.模板匹配:      OpenCV提供了一个模板匹配函数,用于在图像中寻找给定模板的匹配位置。 2.图像模板匹配函数matchTemplate  void matchTemplate( InputArray image, InputArray templ, OutputArray result, int method, InputArray mask = noArray() ); image:待模板匹配的原图像,图像数据类型为CV 8U和CV 32F两者中

    2024年02月09日
    浏览(30)
  • Opencv——图像模板匹配

    什么是模板匹配呢? 看到这里大家是否会觉得很熟悉的感觉涌上心头!在人脸识别是不是也会看见 等等。 模板匹配可以看作是对象检测的一种非常基本的形式。使用模板匹配,我们可以使用包含要检测对象的“模板”来检测输入图像中的对象。 参数:(img: 原始图像、

    2024年02月16日
    浏览(29)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包