SuperPoint:深度学习特征点+描述子
【原文链接】:https://www.vincentqin.tech/posts/superpoint/
本文出自近几年备受瞩目的创业公司MagicLeap,发表在CVPR 2018,一作Daniel DeTone,[paper],[slides],[code]。
这篇文章设计了一种自监督网络框架,能够同时提取特征点的位置以及描述子。相比于patch-based方法,本文提出的算法能够在原始图像提取到像素级精度的特征点的位置及其描述子。
本文提出了一种单应性适应(Homographic Adaptation
)的策略以增强特征点的复检率以及跨域的实用性(这里跨域指的是synthetic-to-real的能力,网络模型在虚拟数据集上训练完成,同样也可以在真实场景下表现优异的能力)。
介绍
诸多应用(诸如SLAM/SfM/相机标定/立体匹配)的首要一步就是特征点提取,这里的特征点指的是能够在不同光照&不同视角下都能够稳定且可重复检测的2D图像点位置。
基于CNN的算法几乎在以图像作为输入的所有领域表现出相比于人类特征工程更加优秀的表达能力。目前已经有一些工作做类似的任务,例如人体位姿估计,目标检测以及室内布局估计等。这些算法以通常以大量的人工标注作为GT,这些精心设计的网络用来训练以得到人体上的角点,例如嘴唇的边缘点亦或人体的关节点,但是这里的问题是这里的点实际是ill-defined(我的理解是,这些点有可能是特征点,但仅仅是一个大概的位置,是特征点的子集,并没有真正的把特征点的概念定义清楚)。
本文采用了非人工监督的方法提取真实场景的特征点。本文设计了一个由特征点检测器监督的具有伪真值数据集,而非是大量的人工标记。为了得到伪真值,本文首先在大量的虚拟数据集上训练了一个全卷积网络(FCNN),这些虚拟数据集由一些基本图形组成,例如有线段、三角形、矩形和立方体等,这些基本图形具有没有争议的特征点位置,文中称这些特征点为MagicPoint
,这个pre-trained的检测器就是MagicPoint
检测器。这些MagicPoint
在虚拟场景的中检测特征点的性能明显优于传统方式,但是在真实的复杂场景中表现不佳,此时作者提出了一种多尺度多变换的方法Homographic Adaptation
。对于输入图像而言,Homographic Adaptation
通过对图像进行多次不同的尺度/角度变换来帮助网络能够在不同视角不同尺度观测到特征点。
综上:SuperPoint = MagicPoint+Homographic Adaptation
算法优劣对比
- 基于图像块的算法导致特征点位置精度不够准确;
- 特征点与描述子分开进行训练导致运算资源的浪费,网络不够精简,实时性不足;或者仅仅训练特征点或者描述子的一种,不能用同一个网络进行联合训练;
网络结构
上图可见特征点检测器以及描述子网络共享一个单一的前向encoder,只是在decoder时采用了不同的结构,根据任务的不同学习不同的网络参数。这也是本框架与其他网络的不同之处:其他网络采用的是先训练好特征点检测网络,然后再去进行对特征点描述网络进行训练。
网络共分成以下4个主要部分,在此进行详述:
1. Shared Encoder 共享的编码网络
从上图可以看到,整体而言,本质上有两个网络,只是前半部分共享了一部分而已。本文利用了VGG-style的encoder以用于降低图像尺寸,encoder包括卷积层,max-pooling层,以及非线性激活层。通过3个max-pooling层将图像的尺寸变成Hc=H/8H_c = H/8Hc=H/8和Hc=H/8H_c = H/8Hc=H/8,经过encoder之后,图像由I∈RH×WI \in \mathcal{R}^{H \times W}I∈RH×W变为张量B∈RHc×Wc×F\mathcal{B} \in \mathbb{R}^{H_c \times W_c \times F}B∈RHc×Wc×F
2. Interest Point Decoder
这里介绍的是特征点的解码端。每个像素的经过该解码器的输出是该像素是特征点的概率(probability of “point-ness”)。
通常而言,我们可以通过反卷积得到上采样的图像,但是这种操作会导致计算量的骤增以及会引入一种“checkerboard artifacts”。因此本文设计了一种带有“特定解码器”(这种解码器没有参数)的特征点检测头以减小模型计算量(子像素卷积)。
例如:输入张量的维度是RHc×Wc×65\mathbb{R}^{H_c \times W_c \times 65}RHc×Wc×65,输出维度RH×W\mathbb{R}^{H \times W}RH×W,即图像的尺寸。这里的65表示原图8×88 \times 88×8的局部区域,加上一个非特征点dustbin
。通过在channel维度上做softmax,非特征点dustbin会被删除,同时会做一步图像的reshape
:RHc×Wc×64⇒RH×W\mathbb{R}^{H_c \times W_c \times 64} \Rightarrow \mathbb{R}^{H \times W}RHc×Wc×64⇒RH×W 。(这就是**子像素卷积**的意思,俗称像素洗牌)
抛出特征点解码端部分代码:
# Compute the dense keypoint scores
cPa = self.relu(self.convPa(x))
scores = self.convPb(cPa) # DIM: N x 65 x H/8 x W/8
scores = torch.nn.functional.softmax(scores, 1)[:, :-1] # DIM: N x 64 x H/8 x W/8
b, _, h, w = scores.shape
scores = scores.permute(0, 2, 3, 1).reshape(b, h, w, 8, 8) # DIM: N x H/8 x W/8 x 8 x 8
scores = scores.permute(0, 1, 3, 2, 4).reshape(b, h*8, w*8) # DIM: N x H x W
这个过程看似比较繁琐,但是这其实就是一个由depth to space
的过程,以N = 1为例,上述过程如下图所示:
上图中所示的3个蓝色小块的就是对应的一个cell经过depth to space
后得到的,易知其尺寸是8×88 \times 88×8。
注意 :这里解释一下为何此作者设置选择增加一个dustbin通道,以及为何先进行softmax再进行slice操作,先进行slice再进行softmax是否可行?(
scores = torch.nn.functional.softmax(scores, 1)[:, :-1]
)
之所以要设置65个通道,这是因为算法要应对不存在特征点的情况。注意到之后的一步中使用了softmax,也就是说沿着通道维度把各个数值通过运算后加和为1。如果没有Dustbin通道,这里就会产生一个问题:若该cell处没有特征点,此时经过softmax后,每个通道上的响应就会出现受到噪声干扰造成异常随机,在随后的特征点选择一步中会将非特征点判定为特征,这个过程由下图左图所示。在添加Dustbin之后,在没有特征的情况下,只有在Dustbin通道的响应值很大,在后续的特征点判断阶段,此时该图像块的响应都很小,会成功判定为无特征点,这个过程由下图右图所示。
上述过程中得到的scores
就是图像上特征点的概率(或者叫做特征响应,后文中响应值即表示概率值),概率越大,该点越有可能是特征点。之后作者进行了一步nms,即非极大值抑制(simple_nms
的实现见文末),随后选择响应值较大的位置作为特征点。
scores = simple_nms(scores, self.config['nms_radius'])
keypoints = [ torch.nonzero(s > self.config['keypoint_threshold']) for s in scores]
nms的效果如下,左图是未使用nms时score的样子,响应值极大的位置周围也聚集着响应较大的点,如果不进行nms,特征点将会很集中;右图是进行nms操作后的score,响应值极大的位置周围的响应为0。
nms前后对应的特征点的位置如下所示,可见nms对于避免特征点位置过于集中起到了比较大的作用。
熟悉SuperPoint的同学应该注意到了,Daniel在CVPR 2018公开的实现中nms在特征点提取之后,而Sarlin于CVPR 2020年公开SuperGlue的同时对SuperPoint进行了重构,后者在score上进行nms,这两种实现上存在一些差异。
下面给出的是Daniel在CVPR 2018开源的SuperPoint推理代码节选。
nodust = nodust.transpose(1, 2, 0)
heatmap = np.reshape(nodust, [Hc, Wc, self.cell, self.cell])
heatmap = np.transpose(heatmap, [0, 2, 1, 3])
heatmap = np.reshape(heatmap, [Hc*self.cell, Wc*self.cell])
xs, ys = np.where(heatmap >= self.conf_thresh) # Confidence threshold.
if len(xs) == 0:return np.zeros((3, 0)), None, None
pts = np.zeros((3, len(xs))) # Populate point data sized 3xN.
pts[0, :] = ys
pts[1, :] = xs
pts[2, :] = heatmap[xs, ys]
pts, _ = self.nms_fast(pts, H, W, dist_thresh=self.nms_dist) # Apply NMS.
但Sarlin为何要这么做呢?本人在Github上提交了一个#issue112咨询了Sarlin,如下是他的回复,总结起来就重构后的代码优势有两点:1. 更加快速,能够在GPU上运行,常数级时间复杂度;2. 支持多图像输入。
3. Descriptor Decoder
首先利用类似于UCN的网络得到一个半稠密的描述子(此处参考文献UCN),这样可以减少算法训练内存开销同时减少算法运行时间。之后通过双三次多项式插值得到其余描述,然后通过L2-normalizes
归一化描述子得到统一的长度描述。特征维度由D∈RHc×Wc×D\mathcal{D} \in \mathbb{R}^{H_c \times W_c \times D}D∈RHc×Wc×D变为RH×W×D\mathbb{R}^{H\times W \times D}RH×W×D 。
由特征点得到其描述子的过程文中没有细讲,看了一下源代码就明白了。其实该过程主要用了一个函数即grid_sample
,画了一个草图作为解释。
- 图像尺寸归一化:首先对图像的尺寸进行归一化,(-1,-1)表示原来图像的(0,0)位置,(1,1)表示原来图像的(H-1,W-1)位置,这样一来,特征点的位置也被归一化到了相应的位置。
- 构建grid:将归一化后的特征点罗列起来,构成一个尺度为1*1*K*2的张量,其中K表示特征数量,2分别表示xy坐标。
- 特征点位置反归一化:根据输入张量的H与W对grid(1,1,0,:)(表示第一个特征点,其余特征点类似)进行反归一化,其实就是按照比例进行缩放+平移,得到反归一化特征点在张量某个slice(通道)上的位置;但是这个位置可能并非为整像素,此时要对其进行双线性插值补齐,然后其余slice按照同样的方式进行双线性插值。注:代码中实际的就是双线性插值,并非文中讲的双三次插值;
- 输出维度:1*C*1*K。
描述子解码部分代码如下:
# Compute the dense descriptors
cDa = self.relu(self.convDa(x))
descriptors = self.convDb(cDa) # DIM: N x 256 x H/8 x W/8
descriptors = torch.nn.functional.normalize(descriptors, p=2, dim=1) #按通道进行归一化# Extract descriptors
# 根据特征点位置插值得到描述子, DIM: N x 256 x Mdescriptors = [sample_descriptors(k[None], d[None], 8)[0]for k, d in zip(keypoints, descriptors)]
4. 误差构建
L(X,X′,D,D′;Y,Y′,S)=Lp(X,Y)+Lp(X′,Y′)+λLd(D,D′,S)\begin{array}{l}{\mathcal{L}\left(\mathcal{X}, \mathcal{X}^{\prime}, \mathcal{D}, \mathcal{D}^{\prime} ; Y, Y^{\prime}, S\right)=} \\ {\qquad \mathcal{L}_{p}(\mathcal{X}, Y)+\mathcal{L}_{p}\left(\mathcal{X}^{\prime}, Y^{\prime}\right)+\lambda \mathcal{L}_{d}\left(\mathcal{D}, \mathcal{D}^{\prime}, S\right)}\end{array} L(X,X′,D,D′;Y,Y′,S)=Lp(X,Y)+Lp(X′,Y′)+λLd(D,D′,S)
可见损失函数由两项组成,其中一项为特征点检测lossLp\mathcal{L}_{p}Lp ,另外一项是描述子的lossLd\mathcal{L}_{d}Ld。
对于检测项loss,此时采用了交叉熵损失函数:
Lp(X,Y)=1HcWc∑h=1w=1Hc,Wclp(xhw;yhw)\mathcal{L}_{p}(\mathcal{X}, Y)=\frac{1}{H_{c} W_{c}} \sum_{h=1 \atop w=1}^{H_{c}, W_{c}} l_{p}\left(\mathbf{x}_{h w} ; y_{h w}\right) Lp(X,Y)=HcWc1w=1h=1∑Hc,Wclp(xhw;yhw)
其中:
lp(xhw;y)=−log(exp(xhwy)∑k=165exp(xhwk))l_{p}\left(\mathbf{x}_{h w} ; y\right)=-\log \left(\frac{\exp \left(\mathbf{x}_{h w y}\right)}{\sum_{k=1}^{65} \exp \left(\mathbf{x}_{h w k}\right)}\right) lp(xhw;y)=−log(∑k=165exp(xhwk)exp(xhwy))
此时类似于一个多分类任务,log\loglog 运算内部就是cell中元素为特征点的概率(即softmax
之后的值),即样本xhw\mathbf{x}_{hw}xhw属于特征的概率。这是一个2D location classifier,每个8x8的范围内只能有一个特征点,即图像中最多有$H \times W / 64 $个SuperPoint特征点。
描述子的损失函数:
Ld(D,D′,S)=1(HcWc)2∑h=1w=1Hc,Wc∑h′=1w′=1Hc,Wcld(dhw,dh′w′′;shwh′w′)\mathcal{L}_{d}\left(\mathcal{D}, \mathcal{D}^{\prime}, S\right)=\frac{1}{\left(H_{c} W_{c}\right)^{2}} \sum_{h=1 \atop w=1}^{H_{c}, W_{c}} \sum_{h^{\prime}=1 \atop w^{\prime}=1}^{H_{c}, W_{c}} l_{d}\left(\mathbf{d}_{h w}, \mathbf{d}_{h^{\prime} w^{\prime}}^{\prime} ; s_{h w h^{\prime} w^{\prime}}\right) Ld(D,D′,S)=(HcWc)21w=1h=1∑Hc,Wcw′=1h′=1∑Hc,Wcld(dhw,dh′w′′;shwh′w′)
其中ldl_{d}ld为Hinge-loss
(合页损失函数,用于SVM,如支持向量的软间隔,可以保证最后解的稀疏性);
ld(d,d′;s)=λd∗s∗max(0,mp−dTd′)+(1−s)∗max(0,dTd′−mn)l_{d}\left(\mathbf{d}, \mathbf{d}^{\prime} ; s\right)=\lambda_{d} * s * \max \left(0, m_{p}-\mathbf{d}^{T} \mathbf{d}^{\prime}\right)+(1-s) * \max \left(0, \mathbf{d}^{T} \mathbf{d}^{\prime}-m_{n}\right) ld(d,d′;s)=λd∗s∗max(0,mp−dTd′)+(1−s)∗max(0,dTd′−mn)
同时指示函数为shwh′w′s_{h w h^{\prime} w^{\prime}}shwh′w′,SSS表示所有正确匹配对集合:
shwh′w′={1,if ∥Hphw^−ph′w′∥≤80,otherwise s_{h w h^{\prime} w^{\prime}}=\left\{\begin{array}{ll}{1,} & {\text { if }\left\|\widehat{\mathcal{H} \mathbf{p}_{h w}}-\mathbf{p}_{h^{\prime} w^{\prime}}\right\| \leq 8} \\ {0,} & {\text { otherwise }}\end{array}\right. shwh′w′={1,0, if ∥∥∥Hphw−ph′w′∥∥∥≤8 otherwise
上式中的p\mathbf{p}p是cell的中心点坐标,Hp\mathcal{H} \mathbf{p}Hp与p′\mathbf{p}^{\prime}p′的距离小于8个pixel的认为是正确的匹配,这其实对应于cell上的的1个pixel。
让我们仔细看一下这个损失函数,这其实是一个Double margin Siamese loss
。当正例描述子余弦相似度dTd′\mathbf{d}^T\mathbf{d}^{\prime}dTd′大于mpm_pmp时,此时不需要惩罚;但如果该相似度较小时,此时就要惩罚了;负样本时我们的目标是让dTd′\mathbf{d}^T\mathbf{d}^{\prime}dTd′变小,但网络性能不佳时可能这个值很大(大于上式中的mnm_nmn),此时要惩罚这种现象,网络权重经过调整后使得该loss降低,对应的描述子相似度降低;
让我们再看一下这个所谓的Double margin Siamese loss
,上图示中的连线表示distdistdist函数。想象一下,我们希望正例
SuperPoint:深度学习特征点+描述子相关推荐
- 机器学习 深度学习 ai_人工智能,机器学习,深度学习-特征和差异
机器学习 深度学习 ai Artificial Intelligence (AI) will and is currently taking over an important role in our ...
- PCA对特征点描述子降维
降维在机器学习领域其实是很重要的一部分,因为在高维情形下回出现样本稀疏,计算距离.内积困难,是所有机器学习面临的共同问题,被称为维数灾难(Curse of dimensionality),而降维就是解 ...
- 基于深度学习特征的植物病虫害检测
ABSTRACT 及时.准确地诊断植物病害,对于防止农业生产的损失和农产品的损失或减少具有重要作用.为了解决这类问题,可以使用基于机器学习的方法.近年来,在图像处理中应用尤其广泛的深度学习为精准农业 ...
- 【视频课】深度学习入门必修,子欲学算法,必先搞数据!
前言 欢迎大家关注有三AI的视频课程系列,我们的视频课程系列共分为5层境界,内容和学习路线图如下: 第1层:掌握学习算法必要的预备知识,包括Python编程,深度学习基础,数据使用,框架使用. 第2层 ...
- 深度学习特征归一化方法——BN、LN、IN、GN
前言 最近看到Group Normalization的论文,主要提到了四个特征归一化方法:Batch Norm.Layer Norm.Instance Norm.Group Norm.此外,论文还提到 ...
- 深度学习——特征点检测和目标检测
特征点检测 假设你正在构建一个人脸识别应用,出于某种原因,你希望算法可以给出眼角的具体位置.眼角坐标为(
- PCL:点云特征描述子3D_object_recognition_(descriptors)
PCL官网:https://pointclouds.org/ 翻译自该网站:http://robotica.unileon.es/index.php/PCL/OpenNI_tutorial_4:_3D ...
- ehd边缘直方图描述子 matlab,一种新的图像空间特征提取方法
计 算 机 工 程第卷 第3期 38 Computer EngineeringV ol.38 No.3 文章编号:1000-3428(2012)03-0218-03·图形图像处理· 2012年2月 F ...
- 深度学习跨层网络结构--特征融合
网络连接结构 个人理解,如有偏差,欢迎指出. ResNet ResNet ResNet 为了解决模型退化问题,创新性的使用了恒等映射,将上一层可能不需要改变的信息,通过跨层链接以逐个相加的方式,叠加到 ...
最新文章
- Symfony2Book16:Symfony2内部02-内核
- 怎样判别蓄电池的好坏?
- C++类class和结构体struct区别
- UI5 Source code map机制的细节介绍
- python爬虫中文乱码_解决Python爬虫处理文件时候中文名称出现乱码问题
- msdb 数据库_如何检索有关存储在MSDB数据库中的SSIS包的信息
- 根服务器修改密钥,更改 SQL Server 实例的服务主密钥
- 力荐收藏:新QC七大工具(完整版)
- 系统自动化制作工资条,很简单,还能发送短信息
- 明翰英语教学系列之音标篇V0.2(持续更新)
- java地铁最短距离_地铁线路最短路径(项目实现)
- linux mantis安装 yum,CentOS 安装和配置 Mantis
- luogu 2184 贪婪大陆
- android 听筒模式外放模式的切换,YY项目之Android 听筒 扬声器 切换
- 人生中最重要的是什么?
- java 关键字 保留字_什么是Java关键字和保留字?
- 产线流量测试解决方案
- 魅族手机计算机视频教程,魅族手机屏幕电脑录制教程
- 2020.7.16集训总结
- Windows的文件系统