SuperPoint
-
背景与概述 :SuperPoint 是一个自监督的全卷积神经网络,用于提取图像中的兴趣点及其描述子。它在 2018 年由 Magic Leap 提出,通过在合成数据集上预训练一个基础检测器 MagicPoint,然后利用同胚适应技术对真实图像数据集进行标记,从而得到一个增强的检测器 SuperPoint,使其在真实世界图像上具有可靠性。
-
网络结构 :其架构基于编码器 - 解码器结构。编码器是一个类似 VGG 的卷积神经网络,用于提取图像特征;解码器则分为两个分支,一个分支用于预测特征点的位置,另一个分支用于生成特征点的描述子。
-
工作原理 :输入一张图像后,编码器对图像进行特征提取,得到特征图。然后,一个解码器分支通过对特征图的处理,使用 softmax 和非极大值抑制等操作,得到图像中特征点的位置。另一个解码器分支则对特征图进行插值等操作,生成每个特征点的描述子。
-
优势 :能够在一张图像中找到具有高区分度的特征点,并为每个特征点生成具有强描述能力的向量描述子。SuperPoint 提取的特征点数量远大于传统方法如 SIFT 等,且具有较好的稳定性和可重复性,在不同的光照条件、视角变化和图像尺度下都能保持较好的性能。
-
应用场景 :常用于图像匹配、三维重建、视觉定位、SLAM 等任务中,如在 SLAM 中,可作为前端的特征点提取模块,为后端的位姿估计和地图构建提供可靠的特征点信息。
SuperGlue
-
背景与概述 :SuperGlue 是由 Magic Leap 和苏黎世联邦理工学院(ETH)合作开发的一种用于图像特征匹配的神经网络。它于 2020 年提出,在 CVPR2020 图像匹配挑战赛中排名第一,与 SuperPoint 配合使用效果极佳。
-
网络结构 :主要由注意力图神经网络和最优匹配层组成。注意力图神经网络包括关键点编码和注意力聚合网络,关键点编码将关键点位置和描述子编码为一个向量,再通过多层感知机将低维向量映射为高维向量;注意力聚合网络则使用自注意力层和交叉注意力层交替更新特征点的表示。
-
工作原理 :输入两组图像的特征点坐标和描述子,如图像 A 的特征点坐标 p_A、描述子 d_A 以及图像 B 的特征点坐标 p_B、描述子 d_B。首先对输入的特征点和描述子进行编码,得到初始的特征表示。然后通过自注意力机制和交叉注意力机制交替作用,聚合图像内和图像间的特征信息,更新特征点的表示。接着将匹配问题建模为一个可微的最优传输问题,使用Sinkhorn-Knopp 算法求解,得到特征点之间的匹配关系和匹配分数。
-
优势 :能够充分利用图像内的特征关系以及两幅图像间的特征关系,对特征点进行更准确的匹配。它可以自动学习特征点之间的匹配模式,适应不同的场景和图像变化,具有很强的泛化能力,并且能够有效地处理特征点的可见性和遮挡问题。
-
应用场景 :除了在图像匹配任务中表现出色外,还广泛应用于三维重建、视觉定位、目标识别等领域。例如在多视角三维重建中,通过匹配不同视角图像中的特征点,为三维点云的生成和模型的构建提供准确的对应关系。
superpoint_superglue_deployment
superpoint_superglue_deployment 是一个用于简化 SuperPoint 和 SuperGlue 模型部署的库。
安装
可以直接通过 pip 进行安装:
pip install superpoint_superglue_deployment
使用
- 导入库 :先导入 opencv-python、numpy 等依赖库以及 superpoint_superglue_deployment 库中的 Matcher 模块。
- 准备图像 :读取待匹配的两张图像,分别以彩色和灰度模式读取。
- 初始化匹配器 :创建 Matcher 对象,并传入配置字典,其中可对 SuperPoint 和 SuperGlue 的相关参数以及是否使用 GPU 进行设置。
- 进行匹配 :调用匹配器的 match() 方法,传入灰度图像数据,获取查询图和参考图的特征点、关键点描述符以及匹配结果。
- 计算单应性矩阵 :利用 OpenCV 的 findHomography 函数,根据匹配结果计算单应性矩阵,以确定图像之间的变换关系。
- 绘制匹配结果 :使用 OpenCV 的 drawMatches 函数将匹配结果可视化,并保存绘制后的图像。
示例代码
import cv2
import numpy as np
from loguru import logger
from superpoint_superglue_deployment import Matcherdef main():# 读取图像query_image = cv2.imread("./data/images/one_pillar_pagoda_1.jpg")ref_image = cv2.imread("./data/images/one_pillar_pagoda_2.jpg")query_gray = cv2.imread("./data/images/one_pillar_pagoda_1.jpg", 0)ref_gray = cv2.imread("./data/images/one_pillar_pagoda_2.jpg", 0)# 初始化匹配器superglue_matcher = Matcher({"superpoint": {"input_shape": (-1, -1),"keypoint_threshold": 0.003,},"superglue": {"match_threshold": 0.5,},"use_gpu": True,})# 进行匹配query_kpts, ref_kpts, _, _, matches = superglue_matcher.match(query_gray, ref_gray)# 计算单应性矩阵M, mask = cv2.findHomography(np.float64([query_kpts[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2),np.float64([ref_kpts[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2),method=cv2.USAC_MAGSAC,ransacReprojThreshold=5.0,maxIters=10000,confidence=0.95,)logger.info(f"number of inliers: {mask.sum()}")matches = np.array(matches)[np.all(mask > 0, axis=1)]matches = sorted(matches, key=lambda match: match.distance)# 绘制匹配结果matched_image = cv2.drawMatches(query_image,query_kpts,ref_image,ref_kpts,matches[:50],None,flags=2,)cv2.imwrite("matched_image.jpg", matched_image)if __name__ == "__main__":main()