- 计算机视觉
- 人工智能
- 深度学习
- 图像处理
【免费下载链接】kornia
🐍 Geometric Computer Vision Library for Spatial AI
LoFTR 是 Kornia 提供的一套免检测器(detector-free)局部特征匹配方案:它不依赖兴趣点检测器,而是先用 Transformer 在两张图像之间建立粗粒度(coarse-level)的稠密像素匹配,再在细粒度(fine-level)上精化这些匹配,从而在纹理匮乏区域依然能产出可靠对应点。本文围绕 LoFTR 官方文档 展开,结合仓库源码与测试,完整讲解如何在 Kornia 中加载预训练 LoFTR、理解其粗到细的匹配流水线、解读默认配置,并将其与 RANSAC、单应估计等几何工具串联,构建可落地的图像匹配与位姿估计流程。
上图展示了两张同机位、不同视角的同一建筑照片,图中每条绿色连线连接keypoints0[i]与keypoints1[i]这对匹配点。读完本文,你将掌握:LoFTR 的调用方式与输入输出约定、outdoor/indoor预训练权重的差异、粗匹配与细匹配的底层原理(含源码级证据),以及如何把匹配结果交给几何估计模块完成单应与基础矩阵求解。
什么是 LoFTR:为什么"免检测器"很重要
传统特征匹配管线是"检测 → 描述 → 匹配"的顺序流程:先用 SIFT/ORB 等检测器提取兴趣点,再计算描述子,最后在描述子空间中找最近邻。这类方法在纹理丰富的区域表现良好,但在低纹理、弱光照场景中,检测器难以产出重复且稳定的兴趣点,匹配质量随之崩塌。
LoFTR 的论文标题直接点明了其核心思想——Detector-Free Local Feature Matching with Transformers(CVPR 2021,Apache-2.0 许可)。它不再执行顺序式的检测/描述/匹配,而是:
- 在粗尺度上建立逐像素的稠密匹配(pixel-wise dense matches);
- 在细尺度上精化这些匹配(refine the good matches at a fine level)。
区别于使用 cost volume 搜索对应点的稠密方法,LoFTR 在 Transformer 中引入自注意力(self-attention)与交叉注意力(cross-attention),让两幅图像的特征相互条件化(conditioned on both images),获得全局感受野。正如论文摘要所述,Transformer 提供的全局感受野使其能够在检测器通常难以产生可重复兴趣点的低纹理区域产出稠密匹配,并在 ScanNet、HPatches、MegaDepth、InLoc 等数据集及视觉定位基准上取得领先效果。
在 Kornia 中,LoFTR 封装为kornia.feature.LoFTR模块:输入一个包含两张灰度图的字典,输出匹配像素坐标与每对匹配的置信度。
快速上手:运行 LoFTR 匹配两张图像
官方文档给出了最简运行示例,下面结合源码细节补全注释与约定:
import torch from kornia.color import rgb_to_grayscale from kornia.feature import LoFTR from kornia.io import load_image # load_image 返回 (C, H, W) 的 float 张量,取值 [0, 1];[None] 扩出 batch 维 img1 = load_image("church_1.png")[None] # (1, 3, H, W) float in [0, 1] img2 = load_image("church_2.png")[None] # pretrained: 'outdoor'(MegaDepth 训练)或 'indoor'(ScanNet 训练) matcher = LoFTR(pretrained="outdoor").eval() with torch.no_grad(): out = matcher({"image0": rgb_to_grayscale(img1), "image1": rgb_to_grayscale(img2)}) pts1, pts2, conf = out["keypoints0"], out["keypoints1"], out["confidence"] # keypoints0 / keypoints1: (N, 2),坐标为 (x, y) 像素坐标 # confidence: (N,),置信度取值 [0, 1]输入约定
从 LoFTR.forward 的文档与实现可以确认输入约定:
image0:左图,形状(N, 1, H1, W1),单通道灰度图,因此示例中先用rgb_to_grayscale转换;image1:右图,形状(N, 1, H2, W2);mask0/mask1(可选):用于标记 padding 区域的掩码,'0'表示被填充的位置,形状(N, H, W),主要服务于训练场景,推理时可省略;- 两张图尺寸可以不同,源码
forward中会检测hw0_i == hw1_i:尺寸相同时将两图拼接后一次性过骨干网络(batch 更高效、BN 收敛更好),尺寸不同时则分别前向(见 loftr.py)。
输出约定
forward返回字典,字段由 loftr.py 中的rename_keys映射而来:
| 输出字段 | 含义 | 形状 |
|---|---|---|
keypoints0 | 图 0 上的匹配点 | (NC, 2),(x, y)像素坐标 |
keypoints1 | 图 1 上的匹配点 | (NC, 2),(x, y)像素坐标 |
confidence | 每对匹配的置信度 | (NC,),取值[0, 1] |
batch_indexes | 每条匹配属于 batch 中哪一对图像 | (NC,) |
其中NC为粗匹配阈值筛选后的匹配总数。当 batch 中不止一对图像时,用out["batch_indexes"]区分每条匹配所属的图像对;若粗匹配阶段未产生任何候选,fine_preprocess会返回空张量(fine_preprocess.py),fine_matching则直接回退使用粗匹配点(fine_matching.py)。
预训练权重:outdoor、indoor 与 indoor_new
LoFTR 构造函数的pretrained参数支持三种取值,权重 URL 定义在 loftr.py:
| 取值 | 训练数据 | 说明 |
|---|---|---|
outdoor | MegaDepth | 户外场景,默认值 |
indoor | ScanNet | 室内场景 |
indoor_new | ScanNet | 较新的室内权重,加载时会自动开启temp_bug_fix位置编码修正 |
权重通过load_state_dict_from_url从 Hugging Face(hf_url)与 CVUT 镜像拉取,加载到 CPU 后执行self.load_state_dict(pretrained_dict["state_dict"]);若传入不在上述取值范围内的值,会抛出ValueError: pretrained should be None or one of ...(loftr.py)。构造函数末尾自动调用self.eval(),因此实例化后即可直接推理,无需再手动切换模式。
如果你的应用场景是室内 SLAM、室内定位,选择pretrained="indoor";室外航拍、街景、重建场景则选择pretrained="outdoor"。Kornia 应用文档 image_matching.rst 中也给出了经验法则:LoFTR 在室内场景表现最佳(对应 ScanNet 权重),而基于检测器的管线在不同场景各有取舍。若不希望下载权重,可传pretrained=None进行随机初始化(多用于自定义训练,仓库未随附训练脚本,仅保留配置与推理实现)。
源码级解析:粗到细的匹配流水线
LoFTR 的完整实现位于 kornia/feature/loftr/,目录结构即对应流水线的各阶段:
kornia/feature/loftr/ ├── backbone/resnet_fpn.py # ResNetFPN_8_2 骨干网络 ├── loftr_module/ │ ├── transformer.py # LocalFeatureTransformer 与 LoFTREncoderLayer │ ├── fine_preprocess.py # FinePreprocess 细级窗口裁剪 │ └── linear_attention.py # 线性注意力实现 └── utils/ ├── coarse_matching.py # CoarseMatching 粗级匹配(dual_softmax / sinkhorn) ├── fine_matching.py # FineMatching 细级精化(DSNT 亚像素) ├── position_encoding.py # PositionEncodingSine 二维正弦位置编码 └── geometry.py / supervision.pyLoFTR.forward的实现(loftr.py)将整个过程划分为五个阶段:
阶段 1:局部特征 CNN。backbone由build_backbone(config)构建,默认为ResNetFPN_8_2(resnet_fpn.py):单通道输入经 7×7 卷积与三层残差块下采样,输出 1/8 分辨率的粗特征feat_c与 1/2 分辨率的细特征feat_f(resolution: (8, 2)即对应此二尺度)。
阶段 2:粗级 Transformer。粗特征先叠加PositionEncodingSine二维正弦位置编码(position_encoding.py),再展平为序列[N, HW, C],送入LocalFeatureTransformer。默认粗级配置为 8 层编码层,layer_names按self/cross交替排列:自注意力让单图内部建立长程上下文,交叉注意力则让两图特征相互"看到"对方,从而获得全局感受野(transformer.py)。
阶段 3:粗级匹配。CoarseMatching(coarse_matching.py)基于置信度矩阵挑选候选点对,支持两种可微匹配算子:dual_softmax(默认,温度系数dsmax_temperature=0.1)与sinkhorn(最优传输)。同时按thr=0.2过滤低置信度匹配,并用border_rm=2剔除靠近图像边界的匹配,避免边界伪影。
阶段 4:细级精化窗口。FinePreprocess(fine_preprocess.py)以每个粗匹配点为中心,在 1/2 分辨率细特征上用F.unfold裁剪fine_window_size=5的局部窗口(5×5=25 个位置),并选择性地将粗级特征下投影后与细特征拼接(fine_concat_coarse_feat=True)作为上下文。
阶段 5:细级匹配。FineMatching(fine_matching.py)对每个窗口计算相似度热图,通过 softmax 得到概率分布,再利用 DSNT(kornia.geometry.subpix.dsnt.spatial_expectation2d)求空间期望获得亚像素精度的偏移量,叠加到粗匹配点上即得最终细匹配坐标mkpts0_f/mkpts1_f。此外还会估计坐标方差std,用于细级监督训练。
默认配置解读:LoFTR 的超参数从哪来
LoFTR构造函数的config参数默认为模块级default_cfg(loftr.py),且与官方预训练权重一一对应。完整参数如下:
default_cfg = { "backbone_type": "ResNetFPN", "resolution": (8, 2), # 粗/细特征相对原图的分辨率倍数 "fine_window_size": 5, # 细级匹配窗口边长(5x5) "fine_concat_coarse_feat": True, # 细级特征是否拼接粗级特征作上下文 "resnetfpn": {"initial_dim": 128, "block_dims": [128, 196, 256]}, "coarse": { "d_model": 256, # 粗级 Transformer 特征维度 "d_ffn": 256, "nhead": 8, "layer_names": ["self", "cross", "self", "cross", "self", "cross", "self", "cross"], "attention": "linear", # 线性注意力,降低序列长度带来的开销 "temp_bug_fix": False, # indoor_new 权重会置为 True }, "match_coarse": { "thr": 0.2, # 粗匹配置信度阈值 "border_rm": 2, # 剔除边界匹配的像素宽度 "match_type": "dual_softmax", # 或 "sinkhorn" "dsmax_temperature": 0.1, # dual_softmax 的温度 "skh_iters": 3, # sinkhorn 迭代次数 "skh_init_bin_score": 1.0, "skh_prefilter": True, "train_coarse_percent": 0.4, # 训练期粗匹配采样比例 "train_pad_num_gt_min": 200, }, "fine": {"d_model": 128, "d_ffn": 128, "nhead": 8, "layer_names": ["self", "cross"], "attention": "linear"}, }几个值得注意的源码细节:
- 线性注意力:粗级序列长度为
H/8 × W/8,随输入分辨率平方增长。LoFTREncoderLayer默认使用LinearAttention(transformer.py),将注意力计算复杂度从二次降为线性,这是 LoFTR 能处理大图的关键; temp_bug_fix与位置编码:原始论文实现的位置编码存在一个 bug(分母的括号位置不同),对最终性能影响很小。Kornia 保留两种实现以兼容官方权重,其中indoor_new权重使用修正版(temp_bug_fix=True,见 loftr.py 与 position_encoding.py);- 边界处理:
mask_border系列函数在粗匹配张量四周写入-INF(INF = 1e9,见 coarse_matching.py),使 softmax 不可能选出边界附近的位置。
源码注释明确提醒:"the config below is the one corresponding to the pretrained models. Do not change anything there, unless you want to retrain it"——即除非你要自行训练,否则不要改动default_cfg。若想自定义,可构造自己的 dict 传入LoFTR(pretrained=None, config=my_cfg)。
把匹配结果接上几何估计:RANSAC 与单应矩阵
LoFTR 输出的只是点对应关系,要恢复两视图间的几何关系,需要把这些匹配喂给几何估计模块。官方文档给出两条路径:
kornia.geometry.ransac.RANSAC:鲁棒估计基础矩阵/单应矩阵,天然抗外点;kornia.geometry.homography.find_homography_dlt:直接线性变换求单应。
示意代码如下:
import torch from kornia.feature import LoFTR from kornia.geometry.homography import find_homography_dlt from kornia.geometry.ransac import RANSAC matcher = LoFTR(pretrained="outdoor").eval() with torch.no_grad(): out = matcher({"image0": gray1, "image1": gray2}) pts1, pts2 = out["keypoints0"], out["keypoints1"] # 方式一:DLT 直接估计(对噪声敏感,适合干净匹配) H = find_homography_dlt(pts1[None], pts2[None]) # 方式二:RANSAC 鲁棒估计(推荐,天然过滤误匹配) ransac = RANSAC( model_type="homography", max_iters=10000, inl_threshold=0.5, min_samples=4, ) H, mask = ransac(pts1, pts2)Kornia 的应用级文档展示了 LoFTR 的两种典型串联方式:
- image_matching.rst:将 LoFTR 与基于检测器的管线(如 GFTT+AffNet+HardNet、LightGlue)并列对比,并演示用 RANSAC 估计几何;
- image_stitching.rst:
ImageStitcher内部即使用KF.LoFTR(pretrained="outdoor")做特征匹配,再估计单应将多图拼接为全景图。
与 LocalFeatureMatcher 的协作
kornia.feature.LocalFeatureMatcher(integrated.py)是 Kornia 的通用匹配器外壳:它接收一个局部特征模块(detector+descriptor)与一个描述子匹配器,统一输出keypoints0 / keypoints1 / lafs / confidence / batch_indexes。LoFTR 的输出字段命名(keypoints0/1、confidence、batch_indexes)与之一致,便于在同一个后处理流程中无缝切换免检测器方案与传统检测器方案。
测试验证:仓库如何保证 LoFTR 的正确性
Kornia 为 LoFTR 配备了完整的回归测试,位于 tests/feature/test_loftr.py,可作参考基线:
test_pretrained_outdoor_smoke/test_pretrained_indoor_smoke:验证LoFTR("outdoor")与LoFTR("indoor")可正常实例化并加载权重(标记为@pytest.mark.slow);test_pretrained_indoor/test_pretrained_outdoor:加载预训练模型对loftr_fund/loftr_homo数据做前向,并将keypoints0/1与预存期望张量assert_close比对,确保推理结果与发布版本一致;测试注释还提示这些期望值在 CPU 上生成,CUDA 下会因匹配集选择不同而无法对齐;test_mask:验证传入mask0/mask1时输出结构仍正常;test_gradcheck:对keypoints0输出做梯度检查,验证模块可微(这为基于 LoFTR 做端到端可微管线提供了保证);test_jit:当前标记为跳过(@pytest.mark.skip),原因是 transformer 层的zip迭代不受 TorchScript 支持,即 LoFTR 暂不支持 JIT 脚本化,做部署导出时需注意。
使用注意事项小结
- 输入必须为灰度图:
LoFTR.forward期望(N, 1, H, W),彩色图请先用kornia.color.rgb_to_grayscale转换; - 推理记得
torch.no_grad()与eval():构造函数已自动eval(),但权重仍参与梯度计算,推理时建议包裹no_grad以省显存; - 两图尺寸可不同:但为获得最佳速度与内存效率,batch 内尽量保持尺寸一致(源码对同尺寸做了拼接前向优化);
- 内存随分辨率增长:粗级序列长度与分辨率平方相关,虽然默认线性注意力缓解了开销,超大图仍需留意显存;
- 不支持 JIT/TorchScript:从测试的 skip 标记可以确认,导出到移动端等场景需要另寻方案;
- 几何后处理是标配:LoFTR 匹配中仍可能混入外点,接入 RANSAC 或 DLT 前建议先用
confidence阈值做一次粗筛。
至此,从一行LoFTR(pretrained="outdoor")到完整的匹配—几何估计流水线,你已掌握了 Kornia 中免检测器特征匹配的调用方法、内部粗到细原理与全部关键配置。下一步可以直接运行 image matching 应用 或 image stitching 应用 的完整示例,把 LoFTR 投入实际场景。
- 计算机视觉
- 人工智能
- 深度学习
- 图像处理
【免费下载链接】kornia
🐍 Geometric Computer Vision Library for Spatial AI
相关推荐
Kornia LoFTR 无检测器特征匹配实战:原理、配置与几何估计完整指南
Kornia LoFTR 无检测器特征匹配实战:原理、配置与几何估计完整指南 导读 LoFTR(Detector Free Local Feature Matc
计算机视觉深度学习人工智能图像处理Kornia 图像匹配实战指南:LoFTR 局部特征匹配与 RANSAC 几何估计
Kornia 图像匹配实战指南:LoFTR 局部特征匹配与 RANSAC 几何估计 图像匹配(Image Matching)旨在为同一场景的两幅图像找出像素与区
计算机视觉深度学习人工智能图像处理【亲测免费】 LoFTR: 基于Transformer的无检测器局部特征匹配
LoFTR: 基于Transformer的无检测器局部特征匹配 1. 项目介绍 LoFTR,即 L ocal F eature T ransformer,是CV
人工智能计算机视觉深度学习图像处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考