目录
SuperPoint:一种自监督网络框架,能够同时提取特征点的位置以及描述子
**传统方法的问题:**基于图像块的算法导致特征点位置精度不够准确;特征点与描述子分开进行训练导致运算资源的浪费,网络不够精简,实时性不足;仅仅训练特征点或者描述子的一种,不能用同一个网络进行联合训练。
如下图整个训练流程分为三部分:
(a)Inter Point Pre-Training(特征点预训练)
(b)Interest Point Self-Labeling(自监督标签)
(c)Joint Training (联合训练)
1.特征点预训练
创建合成数据集 Synthetic Shapes(Labeled Interest Point Images ) 利用合成数据集训练的检测器成MagicPoint(Base Detector)虽然是在合成的数据集上进行训练的,但是论文中提到MagicPoint在Corner-Like Structure(角点类似结构)的现实场景也具备一定的泛化能力,而面对更加普遍的场景,MagicPoint的效果就会下降,为此作者增加了第二步,即Homographic Adaption,第二步 自监督标签。
2.自监督标签
3.整体网络结构
SuperPoint的网络结构如下图所示:
3.1 先对图像进行卷积
图像特征通常包括两部分,特征点和特征描述子,上图中两个分支即分别同时提取特征点和特征描述子。网络首先使用了VGG-Style的Encoder用于降低图像尺寸提取特征,Encoder部分由卷积层、Max-Pooling层和非线性激活层组成,通过三个Max-Pooling层将图像尺寸变为输出的1/8,代码如下:
# Shared Encoder
x = self.relu(self.conv1a(data['image']))
x = self.relu(self.conv1b(x))
x = self.pool(x)
x = self.relu(self.conv2a(x))
x = self.relu(self.conv2b(x))
x = self.pool(x)
x = self.relu(self.conv3a(x))
x = self.relu(self.conv3b(x))
x = self.pool(x)
x = self.relu(self.conv4a(x))
x = self.relu(self.conv4b(x)) # x的输出维度是(N,128,W/8, H/8)
# N代表样本数量
# 128 表示特征图的通道数 代表输出 x 中包含了 128 个不同的特征图,每个特征图都捕获了输入数据中的不同特征信息
# W/8, H/8 特征图的宽度和高度相对于初始输入图片的尺寸已经缩小了 8 倍。
3.2 特征点提取部分(Interest Point Decoder)
对于特征点提取部分,网络先将维度( W / 8 , H / 8 , 128 )的特征处理为( W / 8 , H / 8 , 65 )大小,这里的65的含义是特征图的每一个像素表示原图8 × 8 的局部区域加上一个当局部区域不存在特征点时用于输出的Dustbin通道,通过Softmax以及Reshape的操作,最终特征会恢复为原图大小。
其中65 是 = 64 +1 先把128通道数 变为64通道数 ,再加一个参数层(Dustbin通道),这一层是为了8×8的局部区域内没有特征点时,经过Softmax后64维的特征势必还是会有一个相对较大的值输出,但加入Dustbin通道后就可以避免这个问题。
然后对得到的特征图,进行**归一化(Softmax)和纬度变化(Reshape)**操作。
具体代码如下:
# 计算密集关键点分数
cPa = self.relu(self.convPa(x)) # x维度是(N,128,W/8, H/8)
scores = self.convPb(cPa) # scores维度是(N,65,W/8, H/8)
#下面这为什么由65 变为 64 了,是因为对 scores 进行 softmax(归一化操作)操作,得到概率分布,并且去掉最后一个通道
#这样最后一个参数层便可以去掉了
scores = torch.nn.functional.softmax(scores, 1)[:, :-1] # scores维度是(N,64,W/8, H/8)
#获取 scores 的形状信息,在这里,scores 是一个四维张量,表示为(batch_size(数据的批量大小), channels, height, width)
b, _, h, w = scores.shape
scores = scores.permute(0, 2, 3, 1).reshape(b, h, w, 8, 8) # 进行纬度变化 scores维度是(N,W/8, H/8, 8, 8)
scores = scores.permute(0, 1, 3, 2, 4).reshape(b, h*8, w*8) # 再次进行纬度变化 scores维度是(N,W/8, H/8)
#对 scores 进行非极大值抑制(NMS)处理,为了进一步提取关键点或者去除冗余信息,并得到关键点分数
scores = simple_nms(scores, self.config['nms_radius'])
3.3 特征描述子提取部分(Descriptor Decoder)
对于特征描述子提取部分,同理,我们还是使用encoder层的输出(H,W,128)。经过卷积解码器得到(H,W,256),双线性插值扩大尺寸(H,W,256),最后对每一个像素的描述子(256维)进行L2归一化。
双三次插值(Bicubic Interpolation)是一种常用的图像处理和计算机图形学中的插值方法,用于在离散网格上对图像进行平滑的插值,它可以用于放大或缩小图像,并且相比线性插值更能保持图像细节和平滑度。
3.4 损失函数
首先我们看下基于特征点和特征向量是如何建立损失函数的,损失函数公式如下:
4.实验结果对比 和 不足
可以看到第四行SuperPoint的表现其实很差,我们知道单应变化成立的前提条件是场景中存在平面,而第四行的场景很显然不太满足这种条件,因此效果也会比较差,这也体现了SuperPoint在自监督训练下的一些不足之处。
5.整体代码详解
from pathlib import Path
import torch
from torch import nn
#非极大值抑制(Non-maximum Suppressio