README.md 22 KB

LightGlue 部署项目 - 完整文档

📋 项目概述

本项目是基于 LightGlue 的特征匹配与相机位置跟踪系统,支持实时视频流处理、UDP 图片传输、TensorRT 加速等功能。


🚀 快速开始

基础运行(异步版本,推荐)

 python demo_lightglue_camera_position_async.py ^
    --input "udp://0.0.0.0:12346" ^
    --max_keypoints 64 ^
    --use_fp16 ^
    --use_tensorrt ^
    --tensorrt_precision fp16 ^
    --depth_confidence 0.90 ^
    --width_confidence 0.95 ^
    --keypoint_threshold 0.015 ^
    --nms_radius 5

📁 项目结构

LightGlue_Deployment/
│
├── demo_lightglue_camera_position_async.py    # 异步版本主程序(推荐)
├── demo_lightglue_camera_position_single_window.py  # 单窗口版本
│
├── udp_jpeg_receiver.py                       # UDP JPEG 接收器
├── tensorrt_wrapper.py                        # TensorRT 包装器
├── trt_engine.py                              # TensorRT 引擎管理
│
├── lightglue/                                 # LightGlue 核心库
│   ├── __init__.py
│   ├── lightglue.py                           # LightGlue 匹配器
│   ├── superpoint.py                          # SuperPoint 特征提取器
│   ├── aliked.py                              # ALIKED 特征提取器
│   ├── disk.py                                # DISK 特征提取器
│   ├── sift.py                                # SIFT 特征提取器
│   ├── dog_hardnet.py                         # DoG-HardNet 特征提取器
│   ├── utils.py                               # 工具函数
│   └── viz2d.py                               # 可视化工具
│
├── scripts/                                   # 工具脚本
│   ├── build_tensorrt.py                      # TensorRT 构建脚本
│   ├── export_to_onnx.py                     # ONNX 导出脚本
│   └── demo_tensorrt_async.py                 # TensorRT 异步示例
│
├── requirements.txt                           # 基础依赖
├── requirements_tensorrt.txt                  # TensorRT 版本依赖
│
├── install.bat                                # 基础安装脚本
├── install_all.bat                            # 完整安装脚本
├── install_tensorrt.bat                        # TensorRT 安装脚本
│
└── [文档文件]
    ├── GTX1660完整部署指南.md
    ├── TENSORRT_QUICKSTART.md
    └── ...

🔄 异步 Demo 运行流程详解

整体架构

异步版本采用多线程架构,将视频采集、模型推理、可视化分离到不同线程,最大化性能:

┌─────────────────────────────────────────────────────────┐
│                   主线程(Main Thread)                   │
│  - 参数解析                                              │
│  - 模型加载(SuperPoint + LightGlue)                   │
│  - TensorRT 编译(可选)                                 │
│  - 主循环:推理 + 显示                                    │
└─────────────────────────────────────────────────────────┘
                          │
        ┌─────────────────┼─────────────────┐
        │                 │                 │
        ▼                 ▼                 ▼
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ 线程1        │  │ 线程2        │  │ 线程3        │
│              │  │              │  │              │
│ UDP接收线程   │  │ 视频流读取    │  │ 可视化线程    │
│              │  │              │  │              │
│ - 接收UDP包  │  │ - 读取帧      │  │ - Tensor转换 │
│ - 重组JPEG   │  │ - 放入队列    │  │ - 单应性计算 │
│ - 解码图片    │  │ - 非阻塞     │  │ - 绘制图像   │
└──────────────┘  └──────────────┘  └──────────────┘

详细执行流程

1. 初始化阶段(main() 函数开始)

# 步骤1: 解析命令行参数
opt = parse_args()
# - 输入源(UDP/摄像头/文件)
# - 模型参数(特征点数、阈值等)
# - 优化选项(FP16、TensorRT等)

# 步骤2: 设置设备(CUDA/CPU)
device = "cuda" if torch.cuda.is_available() else "cpu"

# 步骤3: 加载模型
extractor = SuperPoint(...).eval().to(device)  # 特征提取器
matcher = LightGlue(...).eval().to(device)     # 特征匹配器

# 步骤4: TensorRT 优化(可选)
if opt.use_tensorrt:
    # 编译 SuperPoint encoder 为 TensorRT
    # 创建 HybridSuperPoint(TensorRT encoder + PyTorch 后处理)
    extractor = compile_with_tensorrt(extractor)

2. 视频流初始化

# 步骤1: 创建 VideoStreamer
streamer = VideoStreamer(opt.input, opt.resize, ...)
# - 检测输入类型(UDP/摄像头/文件)
# - 如果是 UDP,创建 UDPJPEGReceiver
# - 如果是摄像头,创建 cv2.VideoCapture

# 步骤2: 创建异步视频流读取器
async_streamer = AsyncVideoStreamer(streamer, queue_size=1)
# - 启动后台线程持续读取帧
# - 将最新帧放入队列(只保留最新1帧)

UDP 模式特殊处理

  • VideoStreamer.__init__() 检测到 udp:// 前缀
  • 创建 UDPJPEGReceiver 实例
  • 启动 UDP 接收线程(_receive_loop()
  • UDP 线程持续接收数据包,重组 JPEG,解码后放入队列

3. 主循环(while True:

while True:
    # ========== 阶段1: 获取帧 ==========
    frame, ret = async_streamer.read()  # 从队列获取最新帧(非阻塞)
    if not ret:
        continue  # UDP模式:继续等待
    
    # ========== 阶段2: 预处理 ==========
    frame = apply_orientation(frame, opt)  # 旋转/翻转
    frame_tensor = frame2tensor(frame, device)  # numpy → torch tensor
    
    # ========== 阶段3: 模型推理(GPU)==========
    if opt.use_fp16:
        with torch.cuda.amp.autocast():  # FP16 混合精度
            curr_data = extractor({"image": frame_tensor})      # SuperPoint
            matches01 = matcher({"image0": last_data, "image1": curr_data})  # LightGlue
    else:
        curr_data = extractor({"image": frame_tensor})
        matches01 = matcher({"image0": last_data, "image1": curr_data})
    
    # ========== 阶段4: 提交可视化任务(非阻塞)==========
    async_visualizer.submit(
        frame, last_frame, last_data, curr_data, matches01, ...
    )
    # 将原始数据提交到后台线程处理(不等待)
    
    # ========== 阶段5: 获取可视化结果(非阻塞)==========
    viz_result = async_visualizer.get_result(timeout=0.0)
    if viz_result:
        reference_view = viz_result[1]
        cv2.imshow("Camera Position in Reference", reference_view)
    
    # ========== 阶段6: 更新参考帧 ==========
    if key == 'n':
        last_data = curr_data
        last_frame = frame

4. 后台线程工作流程

线程1: AsyncVideoStreamer._reader()

while not stop:
    frame, ret = streamer.next_frame()  # 读取帧(可能阻塞)
    if ret:
        # 只保留最新帧(丢弃旧帧)
        if queue.full():
            queue.get_nowait()  # 丢弃旧帧
        queue.put(frame)  # 放入新帧

线程2: AsyncVisualizer._visualizer_worker()

while not stop:
    # 从输入队列获取数据
    viz_data = input_queue.get()
    
    # CPU操作(在主线程会阻塞,现在在后台线程)
    kpts0 = last_data["keypoints"][0].cpu().numpy()  # GPU → CPU
    kpts1 = curr_data["keypoints"][0].cpu().numpy()
    matches = matches01["matches0"][0].cpu().numpy()
    
    # 计算单应性矩阵
    current_H, mask = cv2.findHomography(mkpts0, mkpts1, ...)
    
    # 绘制参考视图
    reference_view = draw_camera_position_on_reference(...)
    
    # 放入输出队列
    output_queue.put((display_frame, reference_view, ...))

线程3: UDPJPEGReceiver._receive_loop()(仅UDP模式)

while running:
    data, addr = socket.recvfrom(65507)  # 接收UDP包
    _process_data(data)  # 处理数据包
    
    # _process_data 逻辑:
    # 1. 检测 JPEG 开始标记(FFD8)
    # 2. 累积数据到 buffer
    # 3. 检测 JPEG 结束标记(FFD9)
    # 4. 验证并解码 JPEG
    # 5. 放入 image_queue

📦 核心模块详解

1. demo_lightglue_camera_position_async.py - 异步主程序

功能:异步版本的 LightGlue 演示程序

主要类

  • AsyncVisualizer:异步可视化器

    • 在后台线程处理 CPU 操作(tensor 转换、单应性计算、绘制)
    • 使用队列传递数据,避免阻塞主线程
    • 位置:第48-175行
  • AsyncVideoStreamer:异步视频流读取器

    • 在后台线程持续读取帧
    • 只保留最新帧,丢弃旧帧(降低延迟)
    • 位置:第178-230行

主要函数

  • main():主函数

    • 初始化模型、视频流、可视化器
    • 主循环:推理 + 显示
    • 位置:第323-815行
  • parse_args():命令行参数解析

    • 输入源、模型参数、优化选项
    • 位置:第253-292行

2. udp_jpeg_receiver.py - UDP JPEG 接收器

功能:接收 UDP 传输的 JPEG 图片并重组

核心类UDPJPEGReceiver

工作原理

  1. UDP 接收_receive_loop()

    • 持续调用 socket.recvfrom(65507) 接收 UDP 包
    • 每个包最大 65507 字节(UDP 最大负载)
  2. JPEG 重组_process_data()

    • 检测 JPEG 开始标记:0xFF 0xD8(JPEG_START)
    • 累积数据到 buffer
    • 检测 JPEG 结束标记:0xFF 0xD9(JPEG_END)
    • 提取完整 JPEG 数据
  3. 验证和解码

    • 验证 JPEG 头尾标记
    • 使用 cv2.imdecode() 解码
    • 解码成功则放入队列
  4. 超时处理

    • 如果 2 秒内未收到完整图片,重置缓冲区

关键方法

  • __init__(host, port, timeout):初始化 UDP socket
  • start():启动接收线程
  • get_image(timeout):从队列获取解码后的图像
  • stop():停止接收

使用示例

receiver = UDPJPEGReceiver(host='0.0.0.0', port=5000)
receiver.start()
frame = receiver.get_image(timeout=0.1)

3. demo_lightglue_camera_position_single_window.py - 单窗口版本

功能:同步版本的演示程序(用于对比和调试)

特点

  • 所有操作在主线程执行(同步)
  • 代码更简单,易于理解
  • 性能较低(阻塞操作多)

主要类

  • VideoStreamer:视频流读取器
    • 支持多种输入源(UDP、摄像头、文件、图片序列)
    • UDP 模式集成 UDPJPEGReceiver
    • 位置:第66-216行

主要函数

  • draw_camera_position_on_reference():绘制相机位置
    • 在参考图像上绘制当前相机位置
    • 使用单应性矩阵计算位置
    • 位置:第227-350行

4. lightglue/ - LightGlue 核心库

4.1 lightglue/superpoint.py - SuperPoint 特征提取器

功能:提取图像特征点和描述符

核心类SuperPoint

工作流程

  1. 编码器:卷积网络提取特征图
  2. 特征点检测:从特征图检测关键点
  3. NMS:非极大值抑制去除重复点
  4. 描述符提取:为每个关键点计算描述符

输出

{
    "keypoints": tensor,      # [B, N, 2] 关键点坐标
    "keypoint_scores": tensor, # [B, N] 关键点分数
    "descriptors": tensor      # [B, N, 256] 描述符
}

4.2 lightglue/lightglue.py - LightGlue 匹配器

功能:匹配两幅图像的特征点

核心类LightGlue

工作流程

  1. 特征归一化:归一化关键点坐标
  2. 注意力机制:使用 Transformer 计算特征相似度
  3. 匹配计算:找到最佳匹配对
  4. 置信度过滤:根据置信度过滤匹配

输出

{
    "matches0": tensor,         # [B, N0] 匹配索引
    "matches1": tensor,         # [B, N1] 匹配索引
    "matching_scores0": tensor  # [B, N0] 匹配分数
}

4.3 lightglue/utils.py - 工具函数

功能:提供辅助函数

主要函数

  • numpy_image_to_torch():numpy 图像转 torch tensor
  • match_pair():匹配图像对的便捷函数

4.4 其他特征提取器

  • aliked.py:ALIKED 特征提取器
  • disk.py:DISK 特征提取器
  • sift.py:SIFT 特征提取器(传统方法)
  • dog_hardnet.py:DoG-HardNet 特征提取器

5. tensorrt_wrapper.py - TensorRT 包装器

功能:封装 TensorRT 编译和推理

核心类

  • TensorRTWrapper:TensorRT 推理包装器

    • 支持 Torch-TensorRT 编译
    • 自动保存/加载编译后的模型
    • 位置:第15-125行
  • SuperPointTensorRT:SuperPoint TensorRT 包装器

  • LightGlueTensorRT:LightGlue TensorRT 包装器

使用方式

wrapper = TensorRTWrapper(model, precision="fp16")
result = wrapper(input_tensor)

6. scripts/ - 工具脚本

6.1 scripts/build_tensorrt.py

功能:构建 TensorRT 引擎(ONNX → TensorRT)

6.2 scripts/export_to_onnx.py

功能:导出 PyTorch 模型为 ONNX 格式

6.3 scripts/demo_tensorrt_async.py

功能:TensorRT 异步演示示例


🔄 数据流图

UDP 模式数据流

发送端
  │
  │ UDP 包(JPEG数据片段)
  ▼
┌─────────────────────────────────┐
│ UDPJPEGReceiver._receive_loop() │ ← 线程1:接收UDP包
│  - recvfrom()                    │
│  - 检测 FFD8/FFD9                │
│  - 重组完整JPEG                  │
│  - cv2.imdecode()                │
└─────────────────────────────────┘
  │
  │ 完整图像(numpy array)
  ▼
┌─────────────────────────────────┐
│ image_queue (Queue)             │
└─────────────────────────────────┘
  │
  │ get_image()
  ▼
┌─────────────────────────────────┐
│ VideoStreamer.next_frame()      │
│  - 灰度转换                      │
│  - Resize(如需要)              │
└─────────────────────────────────┘
  │
  │ 灰度图像
  ▼
┌─────────────────────────────────┐
│ AsyncVideoStreamer._reader()    │ ← 线程2:读取帧
│  - 放入队列(只保留最新)         │
└─────────────────────────────────┘
  │
  │ frame (numpy)
  ▼
┌─────────────────────────────────┐
│ 主线程主循环                      │
│  - frame2tensor()               │
│  - SuperPoint 推理               │
│  - LightGlue 推理                │
└─────────────────────────────────┘
  │
  │ 推理结果(tensor)
  ▼
┌─────────────────────────────────┐
│ AsyncVisualizer._visualizer_     │ ← 线程3:可视化
│   worker()                       │
│  - tensor → numpy                │
│  - 计算单应性矩阵                 │
│  - 绘制参考视图                   │
└─────────────────────────────────┘
  │
  │ 绘制好的图像
  ▼
┌─────────────────────────────────┐
│ cv2.imshow()                    │
│ 显示窗口                         │
└─────────────────────────────────┘

模型推理流程

输入图像(灰度)
  │
  ▼
┌─────────────────┐
│ frame2tensor()   │ numpy → torch tensor
└─────────────────┘
  │
  │ [1, 1, H, W] tensor
  ▼
┌─────────────────────────────────┐
│ SuperPoint                      │
│  ├─ Encoder (卷积层)            │ ← TensorRT 优化部分
│  ├─ 特征点检测                  │
│  ├─ NMS                         │
│  └─ 描述符提取                  │
└─────────────────────────────────┘
  │
  │ {keypoints, descriptors, scores}
  ▼
┌─────────────────────────────────┐
│ LightGlue                       │
│  ├─ 特征归一化                  │
│  ├─ Transformer 注意力         │
│  ├─ 匹配计算                    │
│  └─ 置信度过滤                  │
└─────────────────────────────────┘
  │
  │ {matches0, matches1, scores}
  ▼
┌─────────────────────────────────┐
│ 后处理                          │
│  ├─ tensor → numpy              │
│  ├─ 计算单应性矩阵               │
│  └─ 可视化                      │
└─────────────────────────────────┘

🎯 关键设计模式

1. 异步架构

目的:减少阻塞,提高性能

实现

  • 视频采集:后台线程
  • 可视化:后台线程
  • 主线程:只做 GPU 推理(最快)

优势

  • GPU 推理不等待 I/O
  • CPU 操作不阻塞 GPU
  • 延迟更低,FPS 更高

2. 队列缓冲

目的:解耦生产者和消费者

实现

  • AsyncVideoStreamer:帧队列(size=1,只保留最新)
  • AsyncVisualizer:输入队列 + 输出队列(size=2)

优势

  • 生产者不会阻塞消费者
  • 自动丢弃旧数据(降低延迟)

3. 混合优化(TensorRT)

目的:平衡性能和兼容性

实现

  • SuperPoint encoder:TensorRT 优化(卷积层)
  • SuperPoint 后处理:PyTorch(动态操作)
  • LightGlue:PyTorch(复杂输入)

优势

  • 主要计算加速(encoder 占大部分时间)
  • 保持灵活性(动态操作不受限)

📊 性能优化要点

1. 异步处理

  • ✅ 视频采集异步(不阻塞推理)
  • ✅ 可视化异步(不阻塞推理)
  • ✅ GPU 推理连续(最大化利用率)

2. TensorRT 优化

  • ✅ SuperPoint encoder 加速(2-3倍)
  • ✅ FP16 精度(1.5-2倍)
  • ⚠️ LightGlue 未优化(复杂,难以编译)

3. 数据流优化

  • ✅ 只保留最新帧(降低延迟)
  • ✅ 队列大小最小化(减少内存)
  • ✅ 非阻塞操作(避免等待)

🔧 配置说明

输入源配置

输入格式 示例 说明
UDP JPEG udp://0.0.0.0:12346 UDP 透传 JPEG
USB 摄像头 0 摄像头索引
IP 摄像头 http://192.168.1.100:8080/video IP 摄像头 URL
视频文件 video.mp4 视频文件路径
图片序列 images/ 图片目录

性能参数

参数 默认值 说明
--max_keypoints 1024 最大特征点数(影响速度)
--resize 640 480 输入分辨率(影响速度)
--use_fp16 False FP16 半精度(提升30%)
--use_tensorrt False TensorRT 加速(提升50-100%)
--tensorrt_precision fp16 TensorRT 精度(fp16/int8)

📝 代码关键位置索引

异步版本主程序

功能 文件 行号
主函数入口 demo_lightglue_camera_position_async.py 323
参数解析 demo_lightglue_camera_position_async.py 253
模型加载 demo_lightglue_camera_position_async.py 348-360
TensorRT 编译 demo_lightglue_camera_position_async.py 364-555
主循环 demo_lightglue_camera_position_async.py 710-815
异步可视化器 demo_lightglue_camera_position_async.py 48-175
异步视频流 demo_lightglue_camera_position_async.py 178-230

UDP 接收

功能 文件 行号
UDP 接收器类 udp_jpeg_receiver.py 16-211
UDP 接收循环 udp_jpeg_receiver.py 62-85
JPEG 重组逻辑 udp_jpeg_receiver.py 87-138
UDP 集成 demo_lightglue_camera_position_single_window.py 82-111

模型核心

功能 文件 行号
SuperPoint 类 lightglue/superpoint.py -
LightGlue 类 lightglue/lightglue.py 321+
工具函数 lightglue/utils.py -

🐛 调试建议

1. 检查 UDP 接收

# 在 udp_jpeg_receiver.py 中添加调试输出
print(f"[DEBUG] Received {len(data)} bytes from {addr}")
print(f"[DEBUG] Buffer size: {len(self.buffer)}")

2. 检查帧率

# 使用 --show_fps 参数
python demo_lightglue_camera_position_async.py --show_fps ...

3. 检查 TensorRT

# 查看编译输出
# 应该看到 "[OK] Encoder compiled with TensorRT successfully"

✅ 总结

本项目采用异步多线程架构,通过以下方式实现高性能:

  1. 异步视频采集:不阻塞 GPU 推理
  2. 异步可视化:CPU 操作在后台线程
  3. TensorRT 优化:加速主要计算部分
  4. 队列缓冲:解耦生产者和消费者

核心优势

  • 高 FPS(25-35 FPS,取决于配置)
  • 低延迟(只保留最新帧)
  • 灵活输入(UDP/摄像头/文件)
  • 易于扩展(模块化设计)