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()