一、简介

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 自行实现微调的方式

如需微调,需自行编写训练代码,可能的方案:

  1. 与匹配任务联合训练

    • 使用SuperGlue或其他匹配网络
    • 以匹配准确率为损失函数
    • 需自行实现梯度反向传播
  2. 与三维重建联合训练

    • 使用SfM重建误差为损失
    • 优化特征点位置和描述子
  3. 参考类似方法的训练流程

    • 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 使用建议

  1. 预处理很重要

    • 图像尺寸归一化(推荐1280像素宽)
    • 视角对齐(使用yaw/pitch信息)
    • 图像增强(对比度调整)
  2. 参数调优

    • 关键点数量:根据任务需求调整top_k
    • 匹配阈值:根据匹配质量要求调整threshold
  3. 结合其他信息

    • 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核心优势

  1. 轻量高效:模型小、推理快,适合实时应用
  2. 亚像素精度:关键点定位精确

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导出脚本,实测导出失败。主要有两个障碍:

  1. 可变形卷积:ALIKED使用DeformableConv2dSDDH模块,这些自定义算子没有标准ONNX算子对应
  2. 动态控制流: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阈值,或使用图像增强(对比度调整)。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐