用 Python 搭建基于 YOLO26 的多路视频分析应用
PyVideoProc 是一个基于 CUDA 加速的 Python SDK,用于高效处理多路视频流分析。它通过多进程绕过 GIL 限制,减少主机-设备数据传输,并尽可能在 GPU 上计算以提升性能。项目支持多卡、多模型推理,提供开箱即用的解决方案,适合中小型项目快速部署。安装需满足 Docker、显卡驱动和 NVIDIA 容器工具版本要求,支持模型权重转换和 TensorRT 优化。项目开源且开发
PyVideoProc
基于 PyVideoProc 的多路视频流分析实现
PyVideoProc 提供基于 CUDA 加速优化的高性能 Python SDK,可高效实现多路、多卡、多模型的视频解码、AI 推理与编码,显著降低开发复杂度并提升吞吐性能
output_2x2
⭐ 多进程绕过 GIL 限制,提升 Python 并发性能
⭐ 减少 Host-Device 数据传输,降低 GPU 显存冗余拷贝,提升推理速度
⭐ 尽可能在 GPU 上计算,以降低 CPU 计算负担
⭐ 开箱即用,简单易懂,扩展性强,适合中小型项目快速部署
| Open Source | Learning Curve | Developer Friendliness | Performance | Architecture Design | |
|---|---|---|---|---|---|
| DeepStream | ❌ | High | Low | High | Single-process, multi-threaded |
| VideoPipe | ✅ | medium(requires cpp knowledge) | Medium(requires cpp knowledge) | Medium | Single-process, multi-threaded |
| Our | ✅ | ≈ 0 | High | Medium | Multi-process, single-threaded |
项目地址:https://github.com/lmk123568/PyVideoProc
项目讲解视频:
第一步_检查
首先你需要检查本地服务器环境是否满足三个条件
- Docker 版本要大于 24.0
docker --version # 查看版本

- NVIDIA 驱动版本要大于 590
nvidia-smi

注意,作者没试过驱动小于 590 的情况,为了保险起见建议大于 590
- NVIDIA Container Toolkit 版本要大于 1.13.0
nvidia-ctk --version

第二步_运行
当本地环境满足以上条件后,按照以下步骤运行
这里强烈建议生成镜像容器运行,不建议自己头铁装环境,因为你装环境花的时间足够你研究明白 docker 了
- 生成镜像
clone 本项目,生成包含完整开发环境的镜像
git clone https://github.com/lmk123568/PyVideoProc.git
cd PyVideoProc/docker
docker build -t pyvideoproc:cuda12.8 .
镜像生成后,进入容器,不报错即成功
docker run -it \
--gpus all \
-e NVIDIA_DRIVER_CAPABILITIES=all \
-v {your_path}/PyVideoProc:/workspace \
pyvideoproc:cuda12.8 \
bash
后续示例代码默认在容器内/workspace运行
⚠️ 不推荐自己本地装环境,如果一定要自己装,请参考 Dockerfile
- 编译加速包
python scripts/setup.py install
这里面包含了硬件编解码、YOLO26 推理优化的 C++ 实现,并通过 Pybind11 给 Python 调用
- 训练模型权重转换
将通过 ultralytics 训练的pt模型导入到当前目录(/workspace)下(示例模型为 yolo26n.pt)
python scripts/pt2trt.py --w ./yolo26n.pt --fp16
转换过程中会与 ultralytics 官方结果进行推理对齐
💡 TensorRT 编译生成 .engine 过程中,推理尺寸默认设置为
(576,1024),可以跳过letterbox降低计算开销
💡 遇到警告
requirements: Ultralytics requirement ['onnxruntime-gpu'] not found, attempting AutoUpdate...可以Ctrl + C跳过
- 运行
开启 MPS(Multi-Process Service)
nvidia-cuda-mps-control -d
# echo quit | nvidia-cuda-mps-control 关闭 MPS
阅读理解其代码并运行
python main.py
⭐ 一些重要的细节 ⭐
- MPS 多进程服务
在 NVIDIA 显卡中,MPS 通常指的是 Multi-Process Service(多进程服务),全称为 NVIDIA Multi-Process Service (MPS)。多个进程可以同时向 GPU 提交任务(kernel),GPU 调度器可将它们交错执行(时间片或并行),从而更好地填满计算单元
MPS 天然适合 Python 多进程性质,利用 MPS,进一步加速多进程下模型推理速度
- YOLO26 检测前处理优化
通常来说,ultralytics 的 YOLO 检测算法前处理步骤通常是
其中 LetterBox 是 YOLO 系列目标检测模型中常用的一种图像保持宽高比的缩放与填充方法,用于将任意尺寸的输入图像统一调整为目标模型所需尺寸(如 640×640),同时避免图像内容被拉伸变形,这方法主要用于训练
但是在推理阶段,我们可以不需要它,特别是在视频分析场景下,解码的每帧图像分辨率比固定为16:9,我们可以选取解码分辨率为 576x1024 尺寸的图像作为模型输入,这样好处有两点,一个是尺寸固定比为16:9,不用缩放,一个是长宽都是32的倍数,可以5次下采样,不用做填充。这样就完全跳过 LetterBox,增加推理速度
同时带来的好处是,比原来 640x640 相比,567x1024 图像带的像素信息量更多,训练时候设置size=1024,可以进一步提高场景检测精度。这其实是一种 trade off,在作者接触的业务场景下,尽管提升分辨率带来的计算开销大,但是在跳过 LetterBox 以及更高的精度配合 INT8 量化的优化下,多余的计算开销可以忽略不计
最后,在 GPU 上实现剩下的前处理,用一个大 CUDA Kernel 融合,降低计算过程中的显存碎片,最终前处理流程图如下
值得注意的是,YOLO26 的后处理比较简单,不需要优化
- CPU 和 GPU 之间的数据拷贝优化
除了 DeepStream 之外,大部分多路视频流框架都是硬解码出来的帧会放在 CPU 上,在推理的过程中,CPU的帧数据会再一次拷贝到 GPU 上面,这样就有一定的资源消耗,为什么不直接硬解解码出来的帧直接在GPU上面直接给推理呢?
PyVideoProc 提供了解码 SDK,解码后直接返回在 GPU 上的 torch.Tensor,可以像 Pytorch 那样操作 GPU 的张量,后续推理分析都不影响,只要保证解码的张量在 GPU 运行即可,这样就能大幅度降低 CPU 的压力,让计算尽可能在 GPU 上
经过优化的流程图如下
解码 SDK 采用了 ffmpeg+npp,解码到后处理(nv12toBGR、尺寸缩放等)都在 GPU 上进行,代码如下
decoder = pvp.Decoder(
self.input_url, # 支持 rtsp、mp4
enable_frame_skip=False, # 禁用跳帧,确保每一帧都被解码
output_width=1024, # 输出图像宽度(像素)
output_height=576, # 输出图像高度(像素)
enable_auto_reconnect=True, # 网络中断时自动重连
reconnect_delay_ms=10000, # 每次重连尝试前等待 10 秒
max_reconnects=0, # 最大重连次数(0 表示无限重试)
open_timeout_ms=5000, # 打开流的超时时间(5 秒)
read_timeout_ms=5000, # 读取数据包的超时时间(5 秒)
buffer_size=4 * 1024 * 1024, # 缓冲区大小(4MB),用于容忍网络抖动
max_delay_ms=200, # 允许的最大解码延迟(毫秒)
reorder_queue_size=32, # B帧重排序队列长度
decoder_threads=2, # 解码线程数量
surfaces=2, # 用于缓冲的 CUDA 显存表面数量
)
frame, pts = decoder.next_frame()
# frame 是解码出来的帧,类型是 torch.Tensor,图像格式是 BGR
# pts 是当前帧时间戳
- 多路多卡多模型
多进程运行,每个进程表示一路,代码中用 Pipeline 表示
多卡启动的原理很简单,每个进程启动会有
class Pipeline(mp.Process):
def __init__(self, gpu, input_url, output_url):
super().__init__()
os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu)
巧妙利用多进程之间资源隔离特性启动多卡,有时候就是这么巧
多模型需要在 run 函数内定义,核心代码如下
class Pipeline(mp.Process):
def __init__(self, gpu, input_url, output_url):
super().__init__()
os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu)
def run(self):
# 解码器
decoder = pvp.Decoder('test.mp4')
# 检测器
det = pvp.Yolo26DetTRT('yolo26n.engine', 0.25)
# 循环
while 1:
frame, _ = decoder.next_frame()
det_results = det(frame)
# 后续可以添加多个模型,需要自己实现
# frame 是 torch.Tensor 张量,在 GPU 上
# 尽量将预处理操作在 torch.Tensor GPU 上处理
# 后续我补几个 ocr、pose、face、seg、cls 的例子
- 终端输出表示什么意思
[02/11/2026-08:06:32] Pipeline: xxx, Det: 1.35ms, Track: 1.89ms, Draw: 1.34ms, Encode: 0.35ms, Event: 0.00ms, Wait: 35.00ms
- Det 表示平均每帧检测消耗时间
- Track 表示平均每帧跟踪消耗时间
- Draw 表示平均每帧绘图消耗时间
- Encode 表示平均每帧编码消耗时间
- Wait 表示平均等待下一帧的消耗时间
其中 Wait 很重要,25 fps 的视频,每帧间隔为 40ms,所以这一整个串行推理必须保证在 40ms 内推理完毕
实际上得益于 CPU、GPU 良好的计算性能,加上对代码的一些底层优化,很多业务场景计算都能在毫秒级别
如果你的串行 Pipeline 比较复杂,可以在解码 SDK 上开启跳帧检测,这样每帧间隔就是 80ms 了
- PyVideoProc 是什么意思
Py 代表用python语言开发,Video 代表处理视频流,Proc 有两层意思,一个是处理,编解码推理这种,一个是进程的意思,表示多进程运行。缩写为 PVP
参考
- NVIDIA 编解码矩阵:https://developer.nvidia.com/video-encode-decode-support-matrix
- NVIDIA Video Codec SDK:https://developer.nvidia.com/video-codec-sdk
更多推荐
所有评论(0)