ALIKED特征提取与匹配
一、简介
ALIKED(A Lightweight and Keypoint Detection)是一种基于深度学习的局部特征提取方法,由清华大学团队开发。它能够同时检测关键点并生成描述子,具有轻量化、高精度的特点,官方提到的旋转不变性在我的任务中没有得到很好的效果。
git仓库: https://github.com/Shiaoming/ALIKED
二、安装与测试
2.1 环境要求
Python >= 3.7
PyTorch >= 1.8
CUDA >= 10.2 (GPU版本)
OpenCV >= 4.0
numpy
tqdm
2.2 安装步骤
# 1. 克隆仓库
git clone https://github.com/Shiaoming/ALIKED.git
cd ALIKED
# 2. 安装依赖
pip install torch torchvision opencv-python numpy tqdm matplotlib
# 3. 下载预训练模型(自动下载或手动下载到models/目录)
# 模型文件:aliked-t16.pth, aliked-n16.pth, aliked-n16rot.pth, aliked-n32.pth
2.3 快速测试
import cv2
import torch
from nets.aliked import ALIKED
# 初始化模型
model = ALIKED(
model_name='aliked-n16rot', # 模型名称
device='cuda', # 运行设备
top_k=2048, # 最大关键点数(-1表示阈值模式)
scores_th=0.2, # 关键点得分阈值
n_limit=4096 # 关键点数上限
)
# 读取图像
img = cv2.imread('image.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 提取特征
result = model.run(img_rgb)
# 输出结果
keypoints = result['keypoints'] # N×2 关键点坐标
descriptors = result['descriptors'] # N×128 描述子
scores = result['scores'] # N 关键点得分
print(f'检测到 {len(keypoints)} 个关键点')
2.4 批量测试脚本
import os
import cv2
import numpy as np
from nets.aliked import ALIKED
def extract_features_batch(image_dir, model):
"""批量提取图像特征"""
features = {}
for img_name in sorted(os.listdir(image_dir)):
if img_name.endswith('.jpg') or img_name.endswith('.png'):
img = cv2.imread(os.path.join(image_dir, img_name))
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
result = model.run(img_rgb)
features[img_name] = {
'keypoints': result['keypoints'],
'descriptors': result['descriptors']
}
return features
# 使用示例
model = ALIKED(model_name='aliked-n16rot', device='cuda')
features = extract_features_batch('/path/to/images', model)
三、技术特点
3.1 主要特点
| 特点 | 说明 |
|---|---|
| 轻量化设计 | 模型参数量小,推理速度快 |
| 亚像素精度 | 关键点坐标为浮点数,精度约0.001像素 |
| 端到端训练 | 关键点检测和描述子生成联合优化 |
| 旋转不变性 | aliked-n16rot版本支持旋转不变(实测大角度图像对效果不佳) |
| 高密度提取 | 支持提取数千个关键点(可配置上限) |
3.2 与传统方法对比
| 方法 | 关键点检测方式 | 描述子维度 | 旋转不变 | 推理速度 |
|---|---|---|---|---|
| SIFT | DoG(手工设计) | 128维 | 是 | 较慢 |
| ORB | FAST(手工设计) | 256位二进制 | 是 | 快 |
| SuperPoint | CNN检测 | 256维 | 部分 | 中等 |
| ALIKED | DKD(可微分) | 128维 | 可选 | 快 |
3.3 DKD(Differentiable Keypoint Detection)
ALIKED使用DKD模块检测关键点,与传统方法的主要区别:
传统方法(SIFT/ORB):
图像 → 手工设计算子(DoG/FAST) → 关键点
ALIKED方法:
图像 → 神经网络 → 得分图 → DKD模块 → 关键点
↑
可微分,支持端到端训练
DKD的优势:
- 检测过程可微分,可与下游任务联合训练
- 得分图由神经网络学习,适应复杂场景
- 支持亚像素级定位
四、模型类型与规格
4.1 模型版本
ALIKED提供4个预训练模型:
| 模型名称 | 参数量 | 描述子维度 | 特点 | 适用场景 |
|---|---|---|---|---|
aliked-t16 |
最小 | 64维 | 超轻量 | 移动端、实时应用 |
aliked-n16 |
中等 | 128维 | 标准版 | 通用匹配任务 |
aliked-n16rot |
中等 | 128维 | 旋转不变 | 大视角变化场景(存疑) |
aliked-n32 |
较大 | 128维 | 高精度 | 精细匹配、三维重建 |
4.2 模型参数配置
每个模型的网络配置:
# aliked-t16 (轻量版)
{'c1': 8, 'c2': 16, 'c3': 32, 'c4': 64, 'dim': 64, 'K': 3, 'M': 16}
# aliked-n16 (标准版)
{'c1': 16, 'c2': 32, 'c3': 64, 'c4': 128, 'dim': 128, 'K': 3, 'M': 16}
# aliked-n16rot (旋转不变版)
{'c1': 16, 'c2': 32, 'c3': 64, 'c4': 128, 'dim': 128, 'K': 3, 'M': 16}
# aliked-n32 (高精度版)
{'c1': 16, 'c2': 32, 'c3': 64, 'c4': 128, 'dim': 128, 'K': 3, 'M': 32}
参数说明:
c1-c4: 各层通道数dim: 描述子维度K: SDDH模块的邻居数M: 描述子采样点数
4.3 推理参数
model = ALIKED(
model_name='aliked-n16rot', # 选择模型
device='cuda', # cuda/cpu
top_k=2048, # 关键点数量上限
# -1: 阈值模式(无上限)
# >0: top-K模式(固定上限)
scores_th=0.2, # 关键点得分阈值(阈值模式下有效)
n_limit=4096, # 绝对上限(防止内存溢出)
load_pretrained=True # 是否加载预训练权重
)
五、资源占用分析
5.1 模型文件大小
| 模型 | 文件大小 |
|---|---|
| aliked-t16 | 78 KB |
| aliked-n16 | 2.7 MB |
| aliked-n16rot | 2.7 MB |
| aliked-n32 | 3.9 MB |
5.2 GPU内存占用
测试条件:图像尺寸1280×959,top_k=4096,测试设备:NVIDIA RTX 4090 (24GB)
| 模型 | GPU显存 | 推理时间 |
|---|---|---|
| aliked-t16 | ~200MB | ~0.03s |
| aliked-n16 | ~400MB | ~0.04s |
| aliked-n16rot | ~400MB | ~0.04s |
| aliked-n32 | ~600MB | ~0.08s |
5.3 CPU推理性能
图像尺寸1280×959,top_k=2048,测试设备:Montage Jintide C5218R (40核@2.3GHz)
| 模型 | 推理时间 |
|---|---|
| aliked-t16 | ~0.5s |
| aliked-n16 | ~1.2s |
| aliked-n16rot | ~1.2s |
| aliked-n32 | ~2.0s |
5.4 特征数据大小
每张图像提取4096个关键点:
- 关键点坐标: 4096×2×8 = 65KB (float64)
- 描述子: 4096×128×4 = 2MB (float32)
六、特征提取详解
6.1 网络结构
输入图像 (H×W×3)
↓
特征编码器 (ResNet风格)
↓ block1 → x1 (H×W×c1)
↓ block2 → x2 (H/2×W/2×c2)
↓ block3 → x3 (H/8×W/8×c3)
↓ block4 → x4 (H/32×W/32×c4)
↓
特征聚合 (多尺度融合)
↓ x1 + upsample(x2) + upsample(x3) + upsample(x4)
↓ → feature_map (H×W×dim)
↓
得分头 (Score Head)
↓ → score_map (H×W×1)
↓
关键点检测 (DKD)
↓ → keypoints (N×2, 归一化坐标[-1,1])
↓
描述子生成 (SDDH)
↓ → descriptors (N×128, L2归一化)
6.2 关键点检测流程
# 1. 生成得分图
score_map = torch.sigmoid(score_head(feature_map))
# 2. DKD检测关键点
# DKD原理:在得分图上找局部最大值
# 使用soft-argmax实现可微分检测
keypoints_normalized = DKD(score_map, top_k, scores_th)
# 3. 坐标转换(归一化坐标 → 像素坐标)
# 归一化坐标范围:[-1, 1]
# 像素坐标范围:[0, W-1] × [0, H-1]
wh = torch.tensor([W-1, H-1])
keypoints = wh * (keypoints_normalized + 1) / 2
# 结果:亚像素级精度的关键点坐标
# 示例:(545.0031, 341.1964) - 小数部分表示亚像素精度
6.3 描述子生成
# SDDH (Sparse Descriptors from Dense feature Heads)
# 在关键点位置采样描述子
# 1. 从feature_map中采样
# 每个关键点周围采样M个点
descriptors = SDDH(feature_map, keypoints)
# 2. L2归一化
descriptors = F.normalize(descriptors, p=2, dim=1)
# 结果:128维归一化描述子
# 用于后续匹配计算相似度
七、特征匹配方法
7.1 MNN匹配算法
ALIKED默认使用**MNN(Mutual Nearest Neighbor)**匹配:
def mnn_matcher(desc1, desc2, threshold=0.75):
"""
双向最近邻匹配
参数:
desc1: 第一张图描述子 (N1×128)
desc2: 第二张图描述子 (N2×128)
threshold: 相似度阈值
返回:
matches: 匹配对索引 (M×2)
"""
# 1. 计算相似度矩阵(内积)
# 由于描述子已L2归一化,内积等于余弦相似度
sim = desc1 @ desc2.T # N1×N2矩阵
# 2. 过滤低相似度匹配
sim[sim < threshold] = 0
# 3. 找双向最近邻
nn12 = np.argmax(sim, axis=1) # desc1各点在desc2中的最近邻
nn21 = np.argmax(sim, axis=0) # desc2各点在desc1中的最近邻
# 4. 双向一致性检验
# 只有A→B和B→A都互为最近邻才算匹配
ids1 = np.arange(N1)
mask = (ids1 == nn21[nn12]) # 检验双向一致性
# 5. 提取匹配对
matches = np.stack([ids1[mask], nn12[mask]])
return matches.T
7.2 MNN匹配原理图解
图像1关键点: kp1 kp2 kp3 kp4 kp5
↓ ↓ ↓ ↓ ↓
描述子1: d1 d2 d3 d4 d5
图像2关键点: kpA kpB kpC kpD
↓ ↓ ↓ ↓
描述子2: dA dB dC dD
相似度矩阵:
dA dB dC dD
d1 0.85 0.30 0.20 0.10 → 最近邻: kpA
d2 0.40 0.88 0.30 0.20 → 最近邻: kpB
d3 0.20 0.30 0.92 0.40 → 最近邻: kpC
d4 0.10 0.20 0.40 0.75 → 最近邻: kpD
d5 0.30 0.50 0.30 0.60 → 最近邻: kpB
反向检验:
dA → 最近邻d1 → d1最近邻kpA ✓ 匹配成功
dB → 最近邻d2 → d2最近邻kpB ✓ 匹配成功
dC → 最近邻d3 → d3最近邻kpC ✓ 匹配成功
dD → 最近邻d4 → d4最近邻kpD ✓ 匹配成功
d5 → 最近邻dB → dB最近邻d2 ✗ 不匹配(dB的最近邻是d2,不是d5)
7.3 匹配参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
threshold |
0.75 | 相似度阈值,低于此值的匹配被过滤 |
| 值越高,匹配越严格(误匹配少) | ||
| 值越低,匹配越宽松(匹配数多) |
调参建议:
- 高精度场景:threshold=0.8
- 通用场景:threshold=0.75(默认)
- 匹配数不足:threshold=0.7
八、模型微调
8.1 是否支持微调
理论上支持,但官方未公开训练代码。
ALIKED的网络架构设计上支持端到端训练(DKD和SDDH模块都是可微分的),但官方GitHub仓库仅提供了推理代码和预训练模型,未发布训练脚本和训练数据。
8.2 自行实现微调的方式
如需微调,需自行编写训练代码,可能的方案:
-
与匹配任务联合训练
- 使用SuperGlue或其他匹配网络
- 以匹配准确率为损失函数
- 需自行实现梯度反向传播
-
与三维重建联合训练
- 使用SfM重建误差为损失
- 优化特征点位置和描述子
-
参考类似方法的训练流程
- SuperPoint、Disk等方法的训练策略可作为参考
- 需要构建合适的训练数据集
8.3 训练数据准备(自行实现)
# 训练数据格式示例
training_data = {
'image_pairs': [
{
'image1': 'path/to/img1.jpg',
'image2': 'path/to/img2.jpg',
'matches': [[kp1_idx, kp2_idx], ...], # Ground truth匹配
'overlap_ratio': 0.5 # 重叠比例
}
]
}
8.4 微调注意事项
- 无官方训练代码:需自行实现训练流程,有一定难度
- 需要足够大的训练数据集(数千对图像)
- 建议从预训练模型开始,避免从头训练
- 注意验证集划分,防止过拟合
- 可参考学术界公开的局部特征训练方法(如SuperPoint的训练策略)
九、适用场景
9.1 推荐使用场景
| 场景 | 推荐模型 | 推荐参数 |
|---|---|---|
| 无人机航拍 | aliked-n16rot | top_k=4096, threshold=0.75 |
| 三维重建 | aliked-n32 | top_k=4096, threshold=0.7 |
| 视觉定位 | aliked-n16rot | top_k=2048, threshold=0.8 |
| 实时应用 | aliked-t16 | top_k=1024, threshold=0.75 |
| 移动端应用 | aliked-t16 | top_k=512, threshold=0.75 |
9.2 不推荐场景
- 极大视角变化(超过90°):特征提取可能失败
- 极低纹理区域(如光滑墙面):关键点数量不足
- 动态场景(运动物体):匹配不稳定
- 跨模态匹配(红外与可见光):描述子不兼容
9.3 使用建议
-
预处理很重要
- 图像尺寸归一化(推荐1280像素宽)
- 视角对齐(使用yaw/pitch信息)
- 图像增强(对比度调整)
-
参数调优
- 关键点数量:根据任务需求调整top_k
- 匹配阈值:根据匹配质量要求调整threshold
-
结合其他信息
- GPS/IMU信息筛选配对
- 几何约束(如RANSAC)后处理
十、存在问题与改进方向
10.1 当前存在的问题
| 问题 | 具体表现 | 影响程度 |
|---|---|---|
| 关键点分布不均 | 图像边缘区域关键点少 | 中等 |
| 重复纹理误匹配 | 相似结构可能错误匹配 | 中等 |
| 极端视角失效 | 大视角变化检测失败 | 高 |
| 边缘图像匹配少 | 重叠少的图像匹配不足 | 中等 |
| 无几何验证 | MNN不包含几何约束 | 中等 |
10.2 改进方案
方案A:增加关键点数量和均匀性
# 修改参数增加关键点
TOP_K = 4096 # 增加关键点上限
SCORES_TH = 0.15 # 降低阈值,保留更多关键点
N_LIMIT = 8192 # 提高绝对上限
# 效果:关键点数量翻倍,最少参与轨迹点提升约100%
方案B:网格均匀采样
def grid_sample_keypoints(keypoints, scores, grid_size=10, min_per_grid=5):
"""确保每个网格区域有均匀的关键点分布"""
h, w = image.shape[:2]
grid_h, grid_w = h // grid_size, w // grid_size
# 按网格分组
grid_kpts = defaultdict(list)
for kp, score in zip(keypoints, scores):
gx, gy = int(kp[0]/grid_w), int(kp[1]/grid_h)
grid_kpts[(gx, gy)].append((kp, score))
# 每网格保留高分关键点
selected = []
for grid, kpts in grid_kpts.items():
sorted_kpts = sorted(kpts, key=lambda x: x[1], reverse=True)
selected.extend([k[0] for k in sorted_kpts[:min_per_grid]])
return selected
方案C:使用SuperGlue匹配
# 替代MNN,使用图神经网络匹配
from models.matching import Matching
config = {
'superpoint': {'nms_radius': 4, 'keypoint_threshold': 0.005},
'superglue': {'weights': 'outdoor', 'sinkhorn_iterations': 20}
}
matcher = Matching(config)
result = matcher(img1, img2)
# SuperGlue优势:
# 1. 考虑几何约束,减少误匹配
# 2. 全局最优匹配分配
# 3. 自动处理遮挡情况
方案D:结合GPS筛选配对
# 只对GPS距离较近的图像配对
# 减少无效匹配,提高效率
def filter_pairs_by_gps(images, gps_data, distance_threshold=100):
"""基于GPS距离筛选图像对"""
pairs = []
for i, img1 in enumerate(images):
for j, img2 in enumerate(images):
if i < j:
dist = calculate_gps_distance(gps_data[img1], gps_data[img2])
if dist <= distance_threshold:
pairs.append((img1, img2))
return pairs
# 效果:减少匹配计算量,提高有效匹配率
方案E:添加几何验证
def geometric_verification(matches, kpts1, kpts2):
"""使用RANSAC进行几何验证"""
import cv2
if len(matches) < 8:
return matches
# 提取匹配点坐标
pts1 = kpts1[matches[:, 0]]
pts2 = kpts2[matches[:, 1]]
# RANSAC计算基础矩阵
F, mask = cv2.findFundamentalMat(pts1, pts2, cv2.FM_RANSAC, 1.0, 0.99)
# 过滤误匹配
verified_matches = matches[mask.ravel() == 1]
return verified_matches
# 效果:过滤误匹配,提高匹配质量
10.3 升级路径建议
当前状态 → 问题诊断 → 选择改进方案 → 测试验证
诊断步骤:
1. 统计匹配数量分布 → 如边缘匹配少 → 方案A或B
2. 检查误匹配率 → 如误匹配多 → 方案C或E
3. 检查计算效率 → 如效率低 → 方案D
十一、总结
11.1 ALIKED核心优势
- 轻量高效:模型小、推理快,适合实时应用
- 亚像素精度:关键点定位精确
11.2 使用要点
| 要点 | 建议 |
|---|---|
| 模型选择 | 根据场景选择合适版本(t16/n16/n16rot/n32) |
| 参数配置 | 根据匹配需求调整top_k和threshold |
| 图像预处理 | 尺寸归一化、视角对齐能显著提升效果 |
| 配对筛选 | 结合GPS/几何信息减少无效匹配 |
11.3 快速参考
# 最常用配置
model = ALIKED(
model_name='aliked-n16rot', # 旋转不变版
device='cuda',
top_k=4096,
scores_th=0.15
)
# 提取特征
result = model.run(image_rgb)
keypoints = result['keypoints'] # N×2 坐标
descriptors = result['descriptors'] # N×128 描述子
# MNN匹配
matches = mnn_matcher(desc1, desc2, threshold=0.75)
附录:常见问题解答
Q1: ALIKED和SuperPoint有什么区别?
A: ALIKED使用DKD检测关键点(可微分),SuperPoint使用固定阈值检测。ALIKED支持旋转不变版本,描述子维度更小(128 vs 256)。
Q2: 如何选择top_k参数?
A: 根据应用场景:
- 精细匹配:top_k=4096或更高
- 实时应用:top_k=2048
- 移动端:top_k=512
Q3: GPU推理和CPU推理性能差异多大?
A: GPU推理约0.03-0.08秒,CPU约0.5-2秒,差距约10-20倍。
Q4: ALIKED是否支持ONNX导出?
A: 不支持。官方未提供ONNX导出脚本,实测导出失败。主要有两个障碍:
- 可变形卷积:ALIKED使用
DeformableConv2d和SDDH模块,这些自定义算子没有标准ONNX算子对应 - 动态控制流:DKD模块中存在基于tensor值的动态判断(如
if masks.sum() == 0),JIT tracer无法追踪
导出报错示例:
RuntimeError: output did not have observable data dependence with trace inputs
如需C++部署,建议直接使用libtorch部署推理。
Q5: 如何处理无纹理区域?
A: 可以降低scores_th阈值,或使用图像增强(对比度调整)。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)