论文:LightGlue: Local Feature Matching at Light Speed
代码:https://github.com/cvg/LightGlue
作者:1 ETH Zurich__2 Microsoft Mixed Reality & AI Lab
Abstract
提出的LightGlue是一个深度神经网络用于学习图像间的局部特征匹配。我们回顾了SuperGlue的多种设计结构,它是稀疏匹配的sota模型,进行了简单以及高效的改进。让LightGlue变得更加的高效,在空间存储以及计算速度上,让模型能够更加精准,更容易训练。关键的是LightGlue模型能够自适应问题的难度,当视觉大面积重叠或者有限的外观差异时,推理可以更快而且更容易去匹配。具有令人期待的前景用于延迟敏感的深度匹配的3D重建中。代码和训练的模型已开源。
1. Introduction
本文利用这些优点设计的LightGlue,是一个更精确、更有效、比SuperGlue更容易训练的模型。我们重新审视了它的设计决定,结合了很多简单、有效的架构修改。我们提炼了一个配方,在有效的资源下去有效的训练深度匹配器,只用少数的GPU和时间训练出sota的精度。如图1,LightGlue对比显存稀疏或者密集的匹配器,在效率和精度的平衡是最优的。
图1LightGlue匹配稀疏的特征更快、更好,对比现存的方法比如SuperGlue。它的自适应停止机制给了一个细粒度的控制在于速度和精度的平衡上。模型和密集的匹配器LoFTR在精度下相接近,但速度是8倍(在户外条件下)。
不像先前的方法,LIghtGlue能自适应不同难度的图像对,基于不同的视觉重叠度、外貌变化、判别信息。图2表明在更具简单的情况比复杂的情况下的推理速度更快,这和人类处理视觉的信息是一致的。通过2点来实现的:1)在每组计算块后预测一个系列的对应关系;2)使模型能够自我反思预测是否还需要进一步的计算。LIghtGlue会在早期阶段丢弃不匹配的点,把它的注意力集中在可用的区域。
实验结果证明,我们的LIghtGlue是即插即用的替代品对比于SuperGlue:从2组局部特征中预测一个较强的匹配,只需要一点点时间。比如SLAM这种对延迟敏感的引用中部署深度匹配是十分令人期待的。或者重众包数据中重建更大的场景。
图2 深度自适应,LIghtGlue在简单图像对上匹配的速度比困难图像对更快,因为它能够在更早的层上停止,当它对预测更有信心的时候。
2. Related work
Matching images
Deep matchers
Making Transformers efficient
3. Fast feature matching
Problem formulation:LIghtGlue和SuperGlue一样在图像A和B上提取2组局部特征进行部分匹配预测。每个局部特征 i i i组成由2D点位置 p i p_i pi,归一化成图像的大小,以及视觉描述符 d i d_i di。图像A和B由M和N个局部特征索引为 A : = 1 , . . . , M A:={1,...,M} A:=1,...,M和 B : = 1 , . . . , N B:={1,...,N} B:=1,...,N。
设计的LIghtGlue输出一些列对应关系 M = ( i , j ) 属于 A × B M={(i,j)} 属于 A×B M=(i,j)属于A×B。每个点至少匹配一次,因为都是由独特的3D点生成的,有些关键点因为遮挡或者不可重复导致无法匹配。和先前的工作一样,我们在A和B的局部特征间寻求一个软部分赋值矩阵P,从中我们可以提取对应关系。
Overview-Figure 3:LIghtGlue是由相同个L层组成,共同处理2个集合。每个层由自、交叉注意力单元组成用于更新每个点的表示。一个分类器来进行决定,每一层是否停止推理,为了避免不必要的计算。一个轻量级的头最终从系列的表示中计算出部分分配。
图3 LIghtGlue结构,输入一对局部特征(d, p),通过位置编码和自、交叉注意力单元来增强视觉表述。一个置信度分类器c帮助决定是否停止推理。如果有些点是可信的,推理程序会继续处理到下一层,但是我们会修剪哪些确定是不匹配的点。一旦到达可信的状态,LIghtGlue会预测一个分配,基于点对间的相似度和匹配性。
Transformer backbone
我们联合每个局部特征 i i i从图像I中得到状态 x i I x_i^I xiI。状态初始化为共同的视觉描述 d i I d_i^I diI以及随后由每一层更新。我们定义一层为连续的自、交叉注意力单元组成。
Attention unit:每个单元,一个MLP从原图像S中整合的信息 m i m_i mi中更新状态:
[·|·]堆叠2个向量,并行的从两张图像上计算所有的点。在自注意力单元,每张图像I都从同一图像的点提取信息,因此S=I。在交叉注意力单元中,每张图像都从另外一张图像上提取信息。
信息是由注意力机制计算得到的,从图像S上的所有状态j得到加权平均值:
W是一个映射矩阵,a是一个注意力分数在图像I和S的点i和j中。对于自注意力和交叉注意力单元计算这个分数是不同的。
Self-attention:每个点都关注相同图像上的全部点。每张图像都执行相同的操作,因此为了表示清晰放弃了上标I的标注。对于每个点i,当前的状态 x i x_i xi首先被分解成k和q向量,通过不同的线性映射。在点i和j间定义的计算注意力分数如下所示:
其中,R是一个相对位置间的旋转编码[64]。把空间划分成2个d/2的子空间,每一个都旋转一定的角度,参考文献[35],映射得到一个可学习的偏执:
位置编码是注意力中关键的一部分,它允许基于它们的位置处理不同的元素。我们注意到,在投影相机几何中,视觉观察的位置对于相机在图像平面内的平移是等变的:来自相同正交平面上的三维点的二维点以相同的方式平移,它们的相对距离保持不机在图像平面内的平移是等变的:来自相同前视平面的3D点的2D点。这需要一种编码,只捕获点的相对而不是绝对位置。
旋转编码能够使得模型去检索点i出于学习位置的点j。位置编码不会应用到 v j v_j vj,也不会溢出到状态 x i x_i xi。位置编码对于所有层都是相同的,因此只会被计算和存储一次。
大概意思:运用相对位置编码来增加模型的收敛速度以及匹配准确率,相对位置编码就是点与点的坐标根据公式进行旋转编码。这个相对位置编码每一层都是共用的,感觉它应该不是可学习的而是直接根据点与点的相对关系计算出来的。
3.2. Correspondence prediction
设计了一个轻量级的头部,基于任意层的更新状态来预测一个分配。
Assignment scores:首先计算图像所有点之间的一个成对的分配矩阵S:
Linear是一个带有偏执的可学习线性变换。分数编码反应每对图像是否是3维空间的2维映射的相同点。每个点都有一个匹配分数:
分数编码代表点i有个对应点的概率。在另外一张图像中没有检测到的点,当遮挡或者不匹配时,概率为0。
Correespondences:我们通过结合相似度和一个匹配分数整合成一个软部分标记矩阵P:
一对点(i,j)产生一个对应的一致关系,当点都被预测为匹配时以及它们的相似度在图像中比其它点都要高。我们选择 P i j P_ij Pij比阈值大且比其它行和列中元素都要要大的对。
大概意思:把分配矩阵进行解耦成相似度分数和可匹配能力得分,然后综合计算出一致性的最终分配矩阵P。上面公式8做的是按照行或者列进行softmax归一化,因为每个点对应关系是唯一的。一个点与其它全部匹配点的概率和需要为1。自注意力的时候有位置编码,然而交叉注意力的时候是没有位置编码的哟。
图4点净化。LIghtGlue在聚合上下文时,能够尽早发现有些红色的点是无法匹配的,能够在浅层就舍弃他们。另外哪些不可信的点会在深层中逐渐舍弃。这导致在较小了推理时间,只把搜索空间利用在较好的匹配点上,能够更快的找到匹配点。
3.3. Adaptive depth and width
我们增加2种机制去避免不必要的计算以及节省推理时间。i) 通过输入图像对的匹配困难程度,将减少层的数量;ii) 我们会尽早剔除置信度低的点。
Confidence classifier:LightGlue的骨干通过上下文来增强视觉描述符。较高重叠区域以及较少的外观差异,这样容易的图像对通常是很可靠的。这种情况下,前面的层的置信度就较高了和后面的层的决策相同。我们可以输出这些决策以及中断推理。
每层结束后,LightGlue推断置信度从每个点的预测分配中:
较高的值表示i的表示是可靠和最终的,它要么匹配,要么不匹配。这收到多种工作的启发,应用它的策略在语言和视觉任务。这个MLP在最坏的情况下只会增加额外百分之2的推理时间,但是在大部分情况下都会介绍大量时间。
Exit criterion:对于l层来说,一个点的KaTeX parse error: Undefined control sequence: \λ at position 5: c_i>\̲λ̲_l代表它是被需要的。如果所有点有足够比例的α是有信心时停止推理:
我们注意到分类器它本身在浅层时是不可信的。根据分类器的验证准确度逐层衰减 λ l λ_l λl,阈值α直接控制和平衡精度和推理时间。
Point pruning:当退出标准没有满足时,被预测为即自信又无法匹配的点在随后的层中也不太可能帮助匹配其它的点。这些点例如在图像中明显可不见的区域。将在浅层的时候就舍弃他们并且只把保留的点传到下一个阶段。这个操作显著减小了二次复杂度注意力的计算量,而且不是很影响精度。
大概意思:为了自适应输入难度来加快模型的推理速度,因此有两种方式:1.根据可信分类器和可匹配分数来舍弃哪些模型有把握分配正确的点且为不匹配的点。第二种就是根据阈值来满足退出条件,当足够多的点都可正确分配时,可在浅层直接退出得到最终结果。这样的结果应该是和最终结果一致的,如果图像越难匹配那么浅层退出的概率就越小。
3.4. Supervision
以两个阶段来训练LightGlue:首先训练它去预测一致性,然后去训练置信度分类器。后者并不影响最终层的精度或者运行的收敛。‘
Correspondences:我们通过从2个视角的变换来估计真实片区和分配矩阵P。给定一个单应性或者像素级深度和相应的姿态,我们来把A warp变换到图像B,反之亦然。真实匹配矩阵M在两张图像上有较低的投影误差和一致深度的点对。一些点是被标记为无法匹配的点,当他们和其他点重投影的深度误差足够大时。然后最小化模型预测每一层l的分配对数似然,推动LightGlue尽早的预测对应关系。
损失在于平衡正向和反向的样本。
Confidence classifier:然后我们将训练公式9的MLP去预测每一层的预测结果是否与最终结果相同。
大概意思就是训练每一层的预测结果是否和标签匹配,然后对每一层的分类结果用二值交叉熵来进行决定。
大概意思:训练是分两个阶段,第一个阶段把主干网络训练好,然后第二个阶段来训练可信分类器和舍弃无法匹配的点。这样可以保证模型的收敛而且用极小的代价就可以提升运行速度。
3.5. Comparison with SuperGlue
LightGlue是收到SUperGlue的启发,但是和它不同在精度、有效性上更好且更容易训练。
Positional encoding:SuperGlue编码点的绝对位置用MLP,并且很早就与决策符来融合它们。我们发现在通过这些层后模型趋向于遗忘它的位置信息。LIghtGlue依赖于一个相对的位置编码这在图像间更容易比较,它被添加在每个自注意力单元。这使得更容易利用位置并提升深层的准确性。
Prediction head:SuperGlue使用Sinkhorn算法来解决不同的最优化传输问题。它包含了对行和列的多次归一化,在计算和存储上都更加昂贵。它还增加了一个额外的垃圾箱来拒绝不可匹配的点。我们发现垃圾箱纠缠了所有点的相似分数并产生了次有的训练动态。LIghtGlue解耦了相似度和匹配能力,能够实现更加有效的预测。能够使网络产生更加清晰的梯度,
Deep supervision:由于Sinkhorn的计算开算,SuperGlue无法再每一层进行预测只能在最后一层进行监督。轻量化的LightGlue能够在每一次进行分配的预测并且监督它。这会让收敛的速度更快并且允许在之后的任意层进行推理,这就是该模型有效的关键。
4. Details that matter
Recipe(秘诀):LightGlue是有监督的训练设置跟随SuperGlue一致。首先我们在1M图像上合成的单应性样本来进行预训练。这种增强提供了一个全面且没有噪声的监督,但是需要细微的调整。LightGlue然后再MegaDepth数据集进行微调,包含1M张来自人群的图像,拥有196个旅游城市坐标,通过SfM来进行相机校准和恢复,密集深度有多视角立体得到。因为大模型容易过度适应这种独特的场景。预训练在模型的泛化能力中至关重要,但是在最近的研究中杯忽略了。
Training tricks:LIghtGlue的结构能够提升训练的速度、稳定性和准确性,我们发现细节有较大的影响。图5表现出减小需要训练模型的资源,对于SUperGlue来讲。这降低了训练的成本并让广泛的社区更容易接触到深度匹配器。
图5,更容易训练。LightGlue的结构在合成单应性数据集上预训练实现更快的收敛。在5M的图像对上,LIghtGlue实现了低于33%的损失以及在最终层由多4%的Recall值。SuperGlue达到相同的精度需要超过7天的训练。
由于MegaDepth得到的深度图通常是不完整的,我们还将具有较大的极误差点标记为不匹配点。精细的调整和余弦退火学习率提高了准确率。更多的特征点也能够提升性能:每张图像用2k个特征点替换1k个点。BS也很重要,我们采用梯度检查点和混合精度去拟合32对图像在24GB的显存上。
Implementation details:LightGlue 有L=9个层。每个注意力单元有4个头。所有表示的维度d=256。在整个论文中,使用的是有效优化的自注意力。更多细节在附录中。
训练LIghtGlue的局部特征采用SuperPoint和SIFT,但也同时兼容其它任何类型。在MegaDepth上微调模型时,我们采用Sun等人的数据分割[65]来避免对图像匹配挑战中包含的场景进行训练。
5. Experiment
我们评估LIghtGlue在任务单应性估计、相关位置估计、视觉定位。也会分享设计决策的影响。
Setup:和SuperGlue一样,我们报告了与标签在重投影误差3个像素以内的精度和召回率。我们也评估了单应性矩阵变换的鲁棒性和一致性方法:RANSAC和DLT方法。对于每对图像,我们计算了4个角的平均重投影误差,并报告了1和5像素的AUC曲线面积。准寻基准测试的最佳实践,与过去工作不同的是,我们分别采用sota鲁棒性的估计方法和对每个方法的阈值调整,然后报告了最好的结果。
Baselines:和[65]的设置一样,resize全部图像到的较小维度到480像素。我们采用SuperPoint提取的1024个局部特征来评估稀疏匹配器。还比较了与LIghtGlue最近邻匹配的相互检查与深度匹配SuperGlue和SGMNet方法。我们采用官方的模型在户外数据集上进行训练。作为参考,我们也评估了LoFTR、MatchFormer和ASpanFormer,为了公平起见只选用前1024个匹配。
Results:表1表明与SuperGlue和SGMNet相比,产生了较高的一致性,拥有较高的精度和接近的召回率。当使用DLT时,产生了更改的精度比其它匹配方法。LIghtGlue使用简单的DLT能够比计算昂贵且较慢的LO-RANSAC进行非线性优化。在5个像素阈值的偏差了,LIghtGlue模型也能够取得比LoFTR更加精确的结果,尽管收到稀疏关键点的干扰。
在HPatchse数据集上的单应性估计。LightGlue产生了更好的一致性比其它稀疏匹配的方法,拥有最高的精度P和较高的召回率R。当使用RANSAC和更快的DLT估计是,能产生更好的单应性矩阵。LightGlue与密集的匹配器LoFTR相比是有竞争力的。
5.2. Relative pose estimation
我们评估科LIghtGlue在户外场景中表现出强烈遮挡、具有调整的光线和结构变换的姿态估计。
与5.1分析类似,跳过。
相关姿态估计,在MegaDepth1500数据集上,LightGlue能够预测更加精确的一致性和更高的姿态准确性,速度还比乡村的稀疏匹配器更快。与密集匹配器在时间上比较也具有竞争性,采用LO-RANSAC估计是能够比LoFTR和MatchFormer有更好的性能。自适应的策略极大的减小了运行时间,而且只会牺牲一点点准确率。
5.3. End-to-end Structure-from-Motion
在两个相关的基准上评估多视角重建任务。
*DISK+NN和SP+SG使用测试时间延长,而LightGlue不使用。为了比较这些微调的基准,我们增加了关键点的数量,比如DISK(8K).LoFTR-SfM聚类密集匹配器用SuperPoint检测。LIghtGlue由于其它稀疏的基准在立体和多视角任务中,甚至远超与公共排行榜上微调的基准。
5.4. Outdoor visual localization
在Aachen Day-Night dataset数据集上,LIghtGlue的性能与SuperGlue不相上下,但是能快2.5倍,优化时要快4倍。SGMNet and ClusterGNN在该数据集上,运行较慢且鲁棒性较差。
5.5. Insights
Ablation study:
在合成单应性矩阵上进行消融实验。a和b匹配能力和位置编码影响了这个准确性但是没有影响运行时间。c双向的交叉注意力能够更快而且没有舍弃精度。d感谢深度监督,在简单的图像对上浅层就可以产生较好的预测结果。
Adaptivity:
自适应深度和宽度的影响。在小场景下尽早停止是有帮助的,因为网络在半层之后就停止了。在较难的场景网络需要更多的层去收敛,但是较小重叠的图像对运行网络更加积极的净化网络的宽度。整体来说自适应深度和宽度净化减少了大概百分之33的运行时间,在简单的图像对上能够更为有效。
有用的匹配能力,匹配能力帮助过滤掉视觉上相似的异常点,只保留早期对应的绿色点。
Efficiency:
运行时间和关键点的数量,LightGlue大概比SuperGlue快35%,自适应的深度和宽度能够让模型更快。SGMNet只在4K个关键点时较快,但对于标准输入大小来说慢的多。
运行代码
按照官方的github下载下来代码,然后新建main.py,运行下面代码即可,把图像的路径替换了。
"""
Author: YidaChen
Time is: 2024/7/7
this Code:
"""
import matplotlib.pyplot as pltfrom lightglue import LightGlue, SuperPoint, DISK, SIFT, ALIKED, DoGHardNet
from lightglue.utils import load_image, rbdfrom lightglue import LightGlue, SuperPoint, DISK
from lightglue.utils import load_image, rbd
from lightglue import viz2d
import torch# SuperPoint+LightGlue
# extractor = SuperPoint(max_num_keypoints=2048).eval().cuda() # load the extractor
# matcher = LightGlue(features='superpoint').eval().cuda() # load the matcher# or DISK+LightGlue, ALIKED+LightGlue or SIFT+LightGlue
# extractor = DISK(max_num_keypoints=2048).eval().cuda() # load the extractor
# matcher = LightGlue(features='disk').eval().cuda() # load the matcher# load each image as a torch.Tensor on GPU with shape (3,H,W), normalized in [0,1]
# image0 = load_image('/Users/yida/Desktop/1.png').cuda()
# image1 = load_image('/Users/yida/Desktop/2.png').cuda()# SuperPoint+LightGlue
extractor = SuperPoint(max_num_keypoints=2048).eval() # load the extractor
matcher = LightGlue(features='superpoint').eval() # load the matcher# or DISK+LightGlue, ALIKED+LightGlue or SIFT+LightGlue
extractor = DISK(max_num_keypoints=2048).eval() # load the extractor
matcher = LightGlue(features='disk').eval() # load the matcher# load each image as a torch.Tensor on GPU with shape (3,H,W), normalized in [0,1]
image0 = load_image('1.png')
image1 = load_image('2.png')# extract local features
feats0 = extractor.extract(image0) # auto-resize the image, disable with resize=None
feats1 = extractor.extract(image1)# # match the features
# matches01 = matcher({'image0': feats0, 'image1': feats1})
# feats0, feats1, matches01 = [rbd(x) for x in [feats0, feats1, matches01]] # remove batch dimension
# matches = matches01['matches'] # indices with shape (K,2)
# points0 = feats0['keypoints'][matches[..., 0]] # coordinates in image #0, shape (K,2)
# points1 = feats1['keypoints'][matches[..., 1]] # coordinates in image #1, shape (K,2)matches01 = matcher({"image0": feats0, "image1": feats1})
feats0, feats1, matches01 = [rbd(x) for x in [feats0, feats1, matches01]
] # remove batch dimensionkpts0, kpts1, matches = feats0["keypoints"], feats1["keypoints"], matches01["matches"]
m_kpts0, m_kpts1 = kpts0[matches[..., 0]], kpts1[matches[..., 1]]axes = viz2d.plot_images([image0, image1])
viz2d.plot_matches(m_kpts0, m_kpts1, color="lime", lw=0.2)
viz2d.add_text(0, f'Stop after {matches01["stop"]} layers', fs=20)kpc0, kpc1 = viz2d.cm_prune(matches01["prune0"]), viz2d.cm_prune(matches01["prune1"])
viz2d.plot_images([image0, image1])
viz2d.plot_keypoints([kpts0, kpts1], colors=[kpc0, kpc1], ps=10)plt.show()
6. Conclusion
本文提出了LightGlue,一个深度神经网络训练用于图像间的稀疏局部特征匹配。基于SuperGlue,我们将注意力的力量和匹配问题以及Transformer的创新相结合。我们给了模型自己观测自己预测置信度的能力。这产生了一个优雅的策略能够基于不同难度的图像对自适应计算量。在深度和广度上都能够自适应:1.如果所有预测都被准备好了,推理时会在浅层停止。2.被任务不匹配的点,将在下一步中提前舍弃。最终的模型LightGlue比长久以来无与伦比的模型SuperGlue要更加精确、更快、更容易训练。LightGlue是一个简单的替代品,只有优势。为了推动社区的发展,代码将被公开。
个人总结
- 2024年07月07日14:22:41,整篇文章感觉创新点很巧妙,通过模型自己学习自己决策的可信程度来进行判别和设置阈值的提前退出;
- 但是,阈值感觉很难收敛和设定,模型它能不能设置成循环的形式,然后自适应判断是否能够退出;
- 在行人重识别中能不能用稀疏的局部特征来进行检索呢?
- 特征点匹配能够解决仿射变换,能不能用更加密集的点来解决弹性变换呢?
- 去跑一下代码,感受一下。
- 代码感觉对细微的像素级差异感知不是很明显。