本项目是基于 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
└── ...
异步版本采用多线程架构,将视频采集、模型推理、可视化分离到不同线程,最大化性能:
┌─────────────────────────────────────────────────────────┐
│ 主线程(Main Thread) │
│ - 参数解析 │
│ - 模型加载(SuperPoint + LightGlue) │
│ - TensorRT 编译(可选) │
│ - 主循环:推理 + 显示 │
└─────────────────────────────────────────────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 线程1 │ │ 线程2 │ │ 线程3 │
│ │ │ │ │ │
│ UDP接收线程 │ │ 视频流读取 │ │ 可视化线程 │
│ │ │ │ │ │
│ - 接收UDP包 │ │ - 读取帧 │ │ - Tensor转换 │
│ - 重组JPEG │ │ - 放入队列 │ │ - 单应性计算 │
│ - 解码图片 │ │ - 非阻塞 │ │ - 绘制图像 │
└──────────────┘ └──────────────┘ └──────────────┘
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)
# 步骤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 实例_receive_loop())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
线程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
demo_lightglue_camera_position_async.py - 异步主程序功能:异步版本的 LightGlue 演示程序
主要类:
AsyncVisualizer:异步可视化器
AsyncVideoStreamer:异步视频流读取器
主要函数:
main():主函数
parse_args():命令行参数解析
udp_jpeg_receiver.py - UDP JPEG 接收器功能:接收 UDP 传输的 JPEG 图片并重组
核心类:UDPJPEGReceiver
工作原理:
UDP 接收(_receive_loop())
socket.recvfrom(65507) 接收 UDP 包JPEG 重组(_process_data())
0xFF 0xD8(JPEG_START)buffer0xFF 0xD9(JPEG_END)验证和解码
cv2.imdecode() 解码超时处理
关键方法:
__init__(host, port, timeout):初始化 UDP socketstart():启动接收线程get_image(timeout):从队列获取解码后的图像stop():停止接收使用示例:
receiver = UDPJPEGReceiver(host='0.0.0.0', port=5000)
receiver.start()
frame = receiver.get_image(timeout=0.1)
demo_lightglue_camera_position_single_window.py - 单窗口版本功能:同步版本的演示程序(用于对比和调试)
特点:
主要类:
VideoStreamer:视频流读取器
UDPJPEGReceiver主要函数:
draw_camera_position_on_reference():绘制相机位置
lightglue/ - LightGlue 核心库lightglue/superpoint.py - SuperPoint 特征提取器功能:提取图像特征点和描述符
核心类:SuperPoint
工作流程:
输出:
{
"keypoints": tensor, # [B, N, 2] 关键点坐标
"keypoint_scores": tensor, # [B, N] 关键点分数
"descriptors": tensor # [B, N, 256] 描述符
}
lightglue/lightglue.py - LightGlue 匹配器功能:匹配两幅图像的特征点
核心类:LightGlue
工作流程:
输出:
{
"matches0": tensor, # [B, N0] 匹配索引
"matches1": tensor, # [B, N1] 匹配索引
"matching_scores0": tensor # [B, N0] 匹配分数
}
lightglue/utils.py - 工具函数功能:提供辅助函数
主要函数:
numpy_image_to_torch():numpy 图像转 torch tensormatch_pair():匹配图像对的便捷函数aliked.py:ALIKED 特征提取器disk.py:DISK 特征提取器sift.py:SIFT 特征提取器(传统方法)dog_hardnet.py:DoG-HardNet 特征提取器tensorrt_wrapper.py - TensorRT 包装器功能:封装 TensorRT 编译和推理
核心类:
TensorRTWrapper:TensorRT 推理包装器
SuperPointTensorRT:SuperPoint TensorRT 包装器
LightGlueTensorRT:LightGlue TensorRT 包装器
使用方式:
wrapper = TensorRTWrapper(model, precision="fp16")
result = wrapper(input_tensor)
scripts/ - 工具脚本scripts/build_tensorrt.py功能:构建 TensorRT 引擎(ONNX → TensorRT)
scripts/export_to_onnx.py功能:导出 PyTorch 模型为 ONNX 格式
scripts/demo_tensorrt_async.py功能:TensorRT 异步演示示例
发送端
│
│ 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 │
│ ├─ 计算单应性矩阵 │
│ └─ 可视化 │
└─────────────────────────────────┘
目的:减少阻塞,提高性能
实现:
优势:
目的:解耦生产者和消费者
实现:
AsyncVideoStreamer:帧队列(size=1,只保留最新)AsyncVisualizer:输入队列 + 输出队列(size=2)优势:
目的:平衡性能和兼容性
实现:
优势:
| 输入格式 | 示例 | 说明 |
|---|---|---|
| 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_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 |
- |
# 在 udp_jpeg_receiver.py 中添加调试输出
print(f"[DEBUG] Received {len(data)} bytes from {addr}")
print(f"[DEBUG] Buffer size: {len(self.buffer)}")
# 使用 --show_fps 参数
python demo_lightglue_camera_position_async.py --show_fps ...
# 查看编译输出
# 应该看到 "[OK] Encoder compiled with TensorRT successfully"
本项目采用异步多线程架构,通过以下方式实现高性能:
核心优势: