LineMod算法

代码来源:https://github.com/meiqua/shape_based_matching

一、总体结构说明

1、Feature结构体描述了一个特征点,即(x,y)位置,以及其量化梯度的方向(在代码中,将方向量化为8个)

2、Template结构体描述了一个模板,保存了模板的缩放大小(width,height),对应的金字塔层级(pyramid_level),以及特征序列(features)。

3、shapeInfo_producer类描述了模板的预处理,用于对模板图像进行旋转、缩放等操作,并记录每次操作的具体信息(旋转角度、缩放步长、类别等)。

4、ColorGradientPyramid类描述了制作模板的必要步骤,提供了“量化”接口、“提取模板”接口,“金字塔下采样”接口,特征点选择策略。该类分别被ColorGradientPyramid和DepthNormalPyramid继承,外部都不可见。

5、Match类描述了一个有效的模板匹配,包含了匹配的位置,类别(代码一般类别只有一个,暂时还没有多个类别的检测)、特征点相似度排序算法以及相应的模板信息(id)。

6、Detector类是最重要的类,包含添加模板、匹配等一系列操作。其中匹配函数现在底层金字塔进行全局匹配,后来在顶层金字塔局部细化匹配。即先低分辨率匹配,高分辨率局部相对精细匹配。


二、算法流程说明

模板特征点选取→保存信息→ 待测图片特征点选取→计算响应图→ 线性存储→ 与模板进行相似度计算

以scale_test为例

1、定义需要提取的特征点数量


2、创建Detector对象detector,输入特征点数和金字塔尺度数据,在这个例子中,只做两层

构造函数展开


Train部分

输入模板图片后生成 shapInfo_producer对象shapes,定义好旋转和缩放的范围即尺度。

调用shapes.produce_infos()来对信息进行存储。

调用shapes.src_of()来对模板进行相应的操作,并添加到detector.addTemplate()中。

构造函数展开

addTemplate()函数参数解释

addTemplate()函数解释:主要作用为提取模板图像的特征点,即梯度较强的点,得到 这些点的坐标和梯度方向值。

a. modality→process()构建ColorGradientPyramid对象,计算出图像的梯度信息

b. 对金字塔的每一层extractTemplate提取梯度较强的特征点

c. cropTemplate()特征点坐标调整




回到Train中,后续就是保存templates,将模板的信息,包括特征点的信息、图像金字塔层数、图像尺寸写在yaml格式文件中,接着保存infos(似乎没啥用,就是保存一些有效模板的旋转缩放信息),同样写在yaml中。

至此 Train结束。主要是对模板进行金字塔降采样,旋转缩放,根据策略选择特征点并保存。


Test部分

先定义标识

读取train阶段保存的模板信息。

输入待检测图像,并重新调整图像大小,应该将图像的weight和height设定为32的倍数,至少也得是16的倍数,方便做图像金字塔

接下来开始匹配计算,注意,阈值90是相似度评分为90

detector.match()函数展开解释

a. 先调用modality→process()构造一个ColorGradientPyramid对象,对图像计算量化后的梯度信息

b. 遍历图像金字塔,构建响应图(construct resopnse map),包括梯度方向扩散spread、梯度响应计算computeResponseMaps、线性化存储linearize。最终存储在LinearMemoryPyramid结构中。


在Match类中最重要的是重载了操作符号’<’。按照相似度排序,否则按照template_id(特征点的提取顺序)排序。

回到detector.match()函数中

类似于模板的梯度计算,计算待测图像的量化梯度方向,并保存在 std::vector<Ptr> quantizers中。

定义响应图大小,需要对每一层金字塔图像都进行响应图计算,一共有8个量化方向,因此每一层都需要8张响应图

关键函数:pyrDown(),定义了每一层金字塔的图像特征个数,调用update(),和Train过程一致,见上。

接着调用spread进行方向扩散

输入参数:原始图、扩散后的存储图、金字塔缩放尺度

orUnaligned8U()函数展开

总之是一个扩散过程,虽然过程还不是很懂,但似乎不像是从中心开始扩散,更像是从左上角开始,然后mipp这个也没懂,工程中还包含了一些关于mipp的头文件,像是作者自己写的一些计算优化的东西。

接着计算响应图

同样内部也是用了mipp之类的东西,只能参考一下网上的理解

这个地方 把论文中的相似度 也给离散化了。
并且事先计算了 某个方向 和 某组方向的余弦值的最大值,并且离散化, (或者称为根据余弦值 实行打分制) 存储到一个数组SIMILARITY_LUT 中,即查找表。 这个查找表中针对某个方向的值有32个元素, 总共8个方向, 所以有 256个元素。 32个元素中 , 又分为两组, 前16个是8个方向中前4个方向的各种组合 与 当前32个元素针对的方向 的余弦值的最大值对应的得分。
————————————————
版权声明:本文为CSDN博主「haithink」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/haithink/article/details/88396670

将响应图进行线性存储


这个是改变存储方式,外部两层循环用于定位,内部两层循环用于替换值。先行后列, 间隔T读取,然后写入。
最后调用matchClass()函数完成匹配过程

首先在金字塔底层进行粗匹配。目前是最高支持8192个特征点,关键函数similarity_64()

在函数similarity_64()中,对底层金字塔图像进行调整,依据搜索框大小划定搜索范围。利用模板在图像上进行覆盖滑窗,所以产生了 similarity map。 每次滑动,模板和图像产生一个相似度。模板在水平和垂直方向进行滑动, 所以 产生一个 二维的相似度矩阵。这个矩阵的宽自然就是图像的宽减去模板的宽, 也就是代码中的span_x。 高的情况类似。

在计算时候,最好的情况是所有匹配都是最大值4,而8bit的图像最大值是255,因此只能限定63个特征点。但是现在这个版本可以支持8192个了。

但是这里并不涉及模板窗口的滑动操作,只是进行线性化寻址获得响应值。

展开accessLinearMemory()函数 首先是根据标签定位到是哪个线性存储结构,根据特征点坐标直接找到线性存储的值。

accesslinearmemory 输入是输入图片的最底层 ,对于一个feature(x,y,ori),找到原input图在这个位置上对于这个方向的响应。

具体操作:response图在这里已经被重新调整大小,首先通过feature的方向确定第一个子图,然后原图片相当于被划分成立H/T * W/T 个区域,每个区域的大小是TT。首先通过对T取余数,找到在区域中的位置,然后再确定是哪一个grid(x除以T取整),因为矩阵大小实际是T^2(H/T * W/T),所以只需要行指针加上列指针,便可确定位置。

回到similarity_64()函数。将所有区域的相似度拼接在一起,即可获得相似图。

得到了最底层金字塔图像的相似图之后,需要遍历所有的像素点,确定值是否大于阈值。符合要求的则进入候选特征点。

接着将提取的特征点进入上一层金字塔图像,进行进一步的定位计算。

计算局部相似度,得到局部的相似度之后不再是根据阈值筛选,而是找相似度最大的点,然后加入到候选特征点中,下一层再从这个最大点周围去算similarity。

最后对候选特征点的相似度进行阈值过滤,小于阈值的直接抛弃。

至此,已获得所有的最佳特征点。

回到大的流程中。开始输出特征点的坐标信息,并在图中绘制出来。

至此结束。

总的来说,代码的思路还是比较清晰的,有些地方可能是能力有限,理解起来比较困难。比如一些SSE计算加速过程,mipp的使用什么的,可能需要进一步的阅读。这篇文章写得还是有点乱,我的想法是遇到一个核心就先展开解释一下,但往往这个函数比较大,讲完了就很难和前面的再续起来,这个也是我以后需要改进的地方。

这个源码主要将缩放、旋转和噪声的检测分成了三块。

LineMod源码梳理相关推荐

  1. ed2k 网络中搜索资源并选择资源下载的分析及eMule源码梳理

    上一篇博客中,客户端已连接到ed2k网络及客户端与服务器交互的eMule源码梳理,这里将开始搜索资源并下载及客户端与客户端交互的eMule源码梳理 emule 源码下载地址  http://downl ...

  2. Android 源码梳理

    Android 源码梳理 前言 作为霜枫司机一年学习的总结,附上帅照一张. 目录 1. Android系统启动过程分析 2. Linux内核文件系统 3. Android进程间通信源码梳理 4. An ...

  3. 【ORB-SLAM2源码梳理5】关于双目帧Frame的构造函数

    文章目录 前言 一.双目图像帧Frame的构造函数 二.计算特征点匹配与成功匹配点对的深度ComputeStereoMatches() 三.具体过程 1. 准备阶段 2. 右目图每行特征点统计 3. ...

  4. vite预构建源码梳理

    对于"为什么要进行依赖预构建?"这个问题vite 文档已经解释的很清楚了,那么预构建大概的流程是什么样的呢? 启动预构建 从文档中我们知道在服务启动前会进行预构建,对应源码位置在s ...

  5. 【ORB-SLAM2源码梳理6】Track()函数的第一步:单目初始化MonocularInitialization()

    文章目录 前言 一.Track()函数 二.单目初始化MonocularInitialization() 1. 判断单目初始化器是否创建,若没有就创建. 2. 已创建初始化器,判断特征点数目 3. 在 ...

  6. Linux动态库加载函数dlopen源码梳理(一)

    下载了libc的源码,现在就开始libc源码的学习,最近了解到了linux动态库的相关知识,那么就从linux动态库加载函数dlopen进行梳理学习吧. 如果还没下载libc源码,可通过 https: ...

  7. Android_HandlerThread 源码梳理

    前言 Android 多线程还有HandleThread,看名字就可以能感觉到得到,会是handler和Thread的综合使用.那到底是怎么样的呢,现在就跟随Android的源码来看看他的工作原理是什 ...

  8. 源码梳理——Jedis中的集合JedisByteHashMap

    一.JedisByteHashMap JedisByteHashMap是Jedis中实现存储键和值均为byte[]字节数组的Map集合类,它利用HashMap作为键-值对实际存储集合,对Map中的方法 ...

  9. vsomeip源码梳理 -- OfferService流程

    本文基于vsomeip 3.1.20.3总结而成 源码地址:https://github.com/GENIVI/vsomeip.git 本文主要涉及vsomeip库中的如下代码: implementa ...

最新文章

  1. sqlserver 自增ID插入指定数据
  2. JavaScript的Cookie操作
  3. android lame wav 转 mp3,Android JNI Lame编解码实现wav到MP3的转换
  4. 【java】java 并发编程 ArrayBlockingQueue
  5. html文档不是本地电脑,电脑浏览器打不开本地html文件
  6. 【Qt开发】StyleSheet使用总结
  7. 用法 the_英语冠词:不定冠词a,an和定冠词the的用法
  8. Postfix 电子邮件系统精要
  9. servlet-02-HTTP协议
  10. Java出现OutOfMemoryError
  11. 讲讲那些 H-桥电路 的基本道理
  12. 亲民地理第39期-佛山(2)南风古灶
  13. win10运行程序提示“为了对电脑进行保护,已经阻止此应用” 解决方法
  14. unity3d android访问剪贴板,Airtest 和 poco 的 swipe 接口使用总结
  15. 服务器系统修复工具,Windows Repair(系统修复工具) v4.9.0
  16. Excel 入门基础
  17. Leetcode77 组合
  18. Python爬虫——爬取壁纸
  19. 又一篇Android Recovery的文章
  20. 游戏辅助原理与制作01.扫雷-01基址

热门文章

  1. 0111总结-函数与极限-高等数学
  2. python中append的用法是什么?
  3. 【python学习】-字典学习(访问字典所有键与值、修改与更新字典、删除字典)
  4. 值得 .NET 开发者了解的15个特性
  5. python相关参考文献_[编程]Python数据分析
  6. Linux下的Makefile编写与优化
  7. 关于_map文件的全面解析
  8. 三层神经网络实现分类器
  9. jsrun怎么拿里面的代码?
  10. 【Kawasaki川崎机器人码垛程序】CP180L-AC01