LightGlue特征匹配
LightGlue是计算机视觉领域一个非常前沿的图像特征匹配算法,由瑞士苏黎世联邦理工学院(ETH Zurich)的计算机视觉与几何实验室(CVG)团队提出。它在2023年的计算机视觉顶级会议ICCV上发表,可以看作是著名的SuperGlue算法的“升级版”。它的核心突破在于将强大的Transformer架构与极致的运行效率结合了起来。
1. 核心特点
-
自适应计算(Adaptive Computation)是最大亮点:LightGlue能根据图像匹配的难度“智能”地调整计算量。对于视角变化小、重叠度高的“简单”图像对,它会提前结束计算;只有面对复杂的图像时,才会动用全部网络层进行精细匹配。这种机制让其推理速度能比SuperGlue快4到10倍。
-
双重“瘦身”策略:它主要用两种方式来节省算力。一是动态提前停止(Adaptive Depth),当网络浅层已经匹配得很“自信”时,就停止后续计算;二是关键点修剪(Adaptive Width),对于那些网络判断为“无法匹配”的特征点(比如在另一张图中没有对应区域的点),会在计算中途将其剔除,不再参与后续运算。
-
灵活搭档:LightGlue本身是一个“匹配器”,负责建立特征点的对应关系。它需要搭配一个“检测器”来提取特征点。它和著名的SuperPoint是黄金搭档,同时也支持DISK、ALIKED,甚至经典的SIFT特征。
2. 下载地址
github地址: https://github.com/fabio-sim/LightGlue-ONNX
进去页面后,可以提前把onnx格式的模型下载下来(虽然原代码会自动下载与导出)。


与其他特征匹配模型对比图:

3. 代码与运行效果
这里直接给出修改后的,容易拿去即用的代码。
import numpy as np import cv2 import onnxruntime as ort import os def preprocess(image, height=512, width=512): """严格复刻原版正常脚本的预处理:强制Resize,转RGB,只除以255,无Mean/Std""" if len(image.shape) == 2: image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) elif len(image.shape) == 3 and image.shape[2] == 4: image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR) # 转为RGB并强制缩放 image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) image = cv2.resize(image, (width, height)) image = image.astype(np.float32) / 255.0 if len(image.shape) == 2: image = np.stack([image, image, image], axis=2) # 转换为 (C, H, W) 并增加 Batch 维度 image = np.transpose(image, (2, 0, 1)) image = image[np.newaxis, :, :, :] # (1, C, H, W) return image def judge_similarity(img1, img2, session, threshold_count=100, threshold_conf=0.8, input_size=512, save_path="output/result_matches.jpg"): # 1. 预处理 tensor1 = preprocess(img1, input_size, input_size) tensor2 = preprocess(img2, input_size, input_size) input_tensor = np.concatenate([tensor1, tensor2], axis=0) # 2. 推理 input_name = session.get_inputs()[0].name outputs = session.run(None, {input_name: input_tensor}) keypoints = outputs[0] # 形状通常为 (2, N, 2) matches = outputs[1] # 形状为 (M, 3) mscores = outputs[2] # 形状为 (N,) 与 keypoints 对应 # 【核心修复】根据原版正常代码解析 matches 和 mscores if matches.shape[0] == 0: print("⚠️ 警告: 没有找到任何匹配点!") return {"原始总匹配数": 0, "高置信度匹配数": 0, "RANSAC 内点数": 0, "平均置信度": 0, "判定结果": "【不同类】"} kpt0_indices = matches[:, 1].astype(np.int64) kpt1_indices = matches[:, 2].astype(np.int64) score_indices = matches[:, 0].astype(np.int64) # 提取真实的匹配坐标 mkpts0 = keypoints[0][kpt0_indices] mkpts1 = keypoints[1][kpt1_indices] # 提取真实的置信度 match_scores = mscores[score_indices] # 3. 高置信度过滤 high_conf_indices = np.where(match_scores > threshold_conf)[0] high_conf_src_pts = mkpts0[high_conf_indices].astype(np.float32) high_conf_dst_pts = mkpts1[high_conf_indices].astype(np.float32) high_conf_count = len(high_conf_indices) avg_conf = np.mean(match_scores) # 4. RANSAC 几何校验 inlier_count = 0 final_inliers_mask = None if high_conf_count >= 4: src_reshaped = high_conf_src_pts.reshape(-1, 1, 2) dst_reshaped = high_conf_dst_pts.reshape(-1, 1, 2) try: H, inliers = cv2.findHomography(src_reshaped, dst_reshaped, cv2.RANSAC, 5.0) if inliers is not None: final_inliers_mask = inliers.ravel() > 0 inlier_count = int(np.sum(final_inliers_mask)) else: final_inliers_mask = np.ones(high_conf_count, dtype=bool) inlier_count = high_conf_count except cv2.error: final_inliers_mask = np.ones(high_conf_count, dtype=bool) inlier_count = high_conf_count is_same_class = inlier_count >= threshold_count # 5. 可视化保存 (由于原版是强制 Resize,坐标就是缩放后的 512x512,需要再映射回原图) h1, w1 = img1.shape[:2] h2, w2 = img2.shape[:2] # 坐标缩放回原图大小 scale_x1, scale_y1 = w1 / input_size, h1 / input_size scale_x2, scale_y2 = w2 / input_size, h2 / input_size vis_src_pts = (high_conf_src_pts * np.array([scale_x1, scale_y1])).astype(int) vis_dst_pts = (high_conf_dst_pts * np.array([scale_x2, scale_y2])).astype(int) # 拼接图像 vis_img = np.zeros((max(h1, h2), w1 + w2, 3), dtype=np.uint8) vis_img[:h1, :w1] = img1 vis_img[:h2, w1:w1 + w2] = img2 for i in range(len(vis_src_pts)): color = (0, 255, 0) if final_inliers_mask[i] else (0, 0, 255) pt1 = tuple(vis_src_pts[i]) pt2 = tuple(vis_dst_pts[i] + np.array([w1, 0])) cv2.line(vis_img, pt1, pt2, color, 1, cv2.LINE_AA) os.makedirs(os.path.dirname(save_path) if os.path.dirname(save_path) else ".", exist_ok=True) cv2.imwrite(save_path, vis_img) # 6. 结果汇总 result = { "原始总匹配数": int(len(matches)), f"高置信度匹配数 (> {threshold_conf})": high_conf_count, "RANSAC 内点数": inlier_count, "平均置信度": round(float(avg_conf), 4), "判定结果": "【同一类】" if is_same_class else "【不同类】", "可视化保存路径": save_path } return result if __name__ == '__main__': IMAGE_PATH_1 = r"test_images\001.png" IMAGE_PATH_2 = r"test_images\002.png" ONNX_MODEL_PATH = r"weights\raco_aliked_lightglue_pipeline_k512.onnx" # 判定阈值 COUNT_THRESHOLD = 100 CONF_THRESHOLD = 0.8 image1 = cv2.imread(IMAGE_PATH_1) image2 = cv2.imread(IMAGE_PATH_2) if image1 is None or image2 is None: print("❌ 错误:无法读取图像,请检查图片路径。") elif not os.path.exists(ONNX_MODEL_PATH): print("❌ 错误:找不到 ONNX 模型文件,请检查模型路径。") else: print("🚀 正在加载 ONNX 模型...") session = ort.InferenceSession(ONNX_MODEL_PATH, providers=['CPUExecutionProvider']) print("✅ 模型加载成功!") print("🔍 正在执行特征匹配、RANSAC校验与相似度判断...") result = judge_similarity( image1, image2, session, threshold_count=COUNT_THRESHOLD, threshold_conf=CONF_THRESHOLD, save_path="output/final_judgment.jpg" ) print("\n" + "=" * 40) print("📊 终极判定结果:") for key, value in result.items(): print(f" - {key}: {value}") print("=" * 40)
效果及打印如下:






前面说了,如果不想下载,可以自动下载并且导出模型:
from pathlib import Path from lightglue_dynamo.cli import export, infer from lightglue_dynamo.config import Extractor, InferenceDevice extractor_type = Extractor.superpoint # Extractor.disk model_path = Path(f"weights/{extractor_type}_lightglue_pipeline.onnx") #导出的目录 export(extractor_type=extractor_type, output=model_path, batch_size=0, height=0, width=0, num_keypoints=1024)
要用到项目下的lightglue_dynamo:

3. 应用场景
- 同类图片相似度对比
-
三维重建(比如把一堆照片变成3D模型)
-
视觉SLAM(机器人/无人机定位导航)
-
图像拼接(全景图、卫星图拼接)
-
视觉定位(手机AR、相册搜索)
小结:总的来说,LightGlue用“自适应”的思路,在精度和速度之间找到了一个很好的平衡点,简单图片简单处理,复杂图片认真处理,在CPU下测试速度也是较快的。如果你正在做图像匹配相关的工作,它绝对值得一试。最后把自己下载的两个模型(raco_aliked_lightglue_pipeline_k512.onnx与raco_aliked_lightglue_pipeline_k1024.onnx)也放到网盘了,最后,本文借助AI完成(仅供参考)。
通过网盘分享的文件:light_glue_onnx
链接: https://pan.baidu.com/s/17ACPjJx-145RVE6NJVvJQA 提取码: ckr7
若存在不足或错误之处,欢迎评论与指正!

浙公网安备 33010602011771号