运行你的第一个 DeepStream Python 应用
什么是 DeepStream?
DeepStream 是 NVIDIA 开发的流媒体分析工具包,专为构建基于 AI 的视频分析应用而设计。它利用 GPU 加速,能够实时处理大量视频流,广泛应用于智能监控、自动驾驶等领域。
1. 环境搭建
1.1 安装 Docker
Windows 用户:
下载并安装 Docker Desktop
Linux 用户:
# 安装 Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# 安装 NVIDIA Container Toolkit(GPU 支持必需)
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker
1.2 拉取并启动 DeepStream 容器
# 拉取 DeepStream 7.1 镜像(约 8GB,请耐心等待)
docker pull nvcr.io/nvidia/deepstream:7.1-gc-triton-devel
# 启动容器并进入交互模式
docker run -it --gpus all --network=host \
nvcr.io/nvidia/deepstream:7.1-gc-triton-devel /bin/bash
参数说明:
--gpus all
:启用所有 GPU--network=host
:使用主机网络模式
1.3 安装依赖组件
进入容器后,执行以下命令安装必要组件:
# 进入 DeepStream 安装目录
cd /opt/nvidia/deepstream/deepstream-7.1/
# 安装 RTP 管理器(用于流媒体处理)
bash update_rtpmanager.sh
# 安装其他必要组件
bash user_additional_install.sh
# 安装 Python 绑定和示例应用
bash user_deepstream_python_apps_install.sh --version 1.1.4
安装完成后,/opt/nvidia/deepstream/deepstream-7.1/sources/deepstream_python_apps
目录将包含所有 Python 示例。
2. 运行第一个示例:deepstream-test1
2.1 理解 deepstream-test1
这是一个基础的目标检测应用,功能包括:
- 读取视频文件
- 使用预训练的 AI 模型检测车辆、行人、自行车等目标
- 在视频帧上绘制边界框和统计信息
- 输出检测结果
2.2 适配容器环境
由于容器环境无法直接显示图形界面,我们需要修改代码以适应无头(headless)运行环境。
进入示例目录:
cd /opt/nvidia/deepstream/deepstream-7.1/sources/deepstream_python_apps/apps/deepstream-test1
创建修改版本:
cp deepstream_test_1.py deepstream_test_1_headless.py
2.3 代码修改说明
主要修改点:
- 移除图形显示组件:注释掉平台检测和图形渲染相关代码
- 使用虚拟输出:用
fakesink
替代图形显示组件 - 保留数据处理:确保检测结果仍能正常输出到控制台
关键修改:
- 移除
platform_info
相关导入和使用 - 将图形渲染组件替换为
fakesink
- 保持检测和统计功能完整
2.4 完整的修改后代码
#!/usr/bin/env python3
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
################################################################################
import sys
sys.path.append('../')
import os
import gi
gi.require_version('Gst', '1.0')
from gi.repository import GLib, Gst
from common.bus_call import bus_call
import pyds
# 目标类别 ID 定义
PGIE_CLASS_ID_VEHICLE = 0
PGIE_CLASS_ID_BICYCLE = 1
PGIE_CLASS_ID_PERSON = 2
PGIE_CLASS_ID_ROADSIGN = 3
MUXER_BATCH_TIMEOUT_USEC = 33000
def osd_sink_pad_buffer_probe(pad, info, u_data):
"""
数据探针函数:处理每一帧的检测结果
"""
frame_number = 0
num_rects = 0
gst_buffer = info.get_buffer()
if not gst_buffer:
print("Unable to get GstBuffer")
return
# 获取批次元数据
batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
l_frame = batch_meta.frame_meta_list
while l_frame is not None:
try:
frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
except StopIteration:
break
# 初始化目标计数器
obj_counter = {
PGIE_CLASS_ID_VEHICLE: 0,
PGIE_CLASS_ID_PERSON: 0,
PGIE_CLASS_ID_BICYCLE: 0,
PGIE_CLASS_ID_ROADSIGN: 0
}
frame_number = frame_meta.frame_num
num_rects = frame_meta.num_obj_meta
l_obj = frame_meta.obj_meta_list
# 遍历检测到的目标
while l_obj is not None:
try:
obj_meta = pyds.NvDsObjectMeta.cast(l_obj.data)
except StopIteration:
break
obj_counter[obj_meta.class_id] += 1
# 设置边界框颜色(蓝色)
obj_meta.rect_params.border_color.set(0.0, 0.0, 1.0, 0.8)
try:
l_obj = l_obj.next
except StopIteration:
break
# 创建显示元数据
display_meta = pyds.nvds_acquire_display_meta_from_pool(batch_meta)
display_meta.num_labels = 1
py_nvosd_text_params = display_meta.text_params[0]
# 设置显示文本
py_nvosd_text_params.display_text = (
f"Frame Number={frame_number} Number of Objects={num_rects} "
f"Vehicle_count={obj_counter[PGIE_CLASS_ID_VEHICLE]} "
f"Person_count={obj_counter[PGIE_CLASS_ID_PERSON]}"
)
# 设置文本位置和样式
py_nvosd_text_params.x_offset = 10
py_nvosd_text_params.y_offset = 12
py_nvosd_text_params.font_params.font_name = "Serif"
py_nvosd_text_params.font_params.font_size = 10
py_nvosd_text_params.font_params.font_color.set(1.0, 1.0, 1.0, 1.0)
py_nvosd_text_params.set_bg_clr = 1
py_nvosd_text_params.text_bg_clr.set(0.0, 0.0, 0.0, 1.0)
# 输出检测结果到控制台
print(pyds.get_string(py_nvosd_text_params.display_text))
pyds.nvds_add_display_meta_to_frame(frame_meta, display_meta)
try:
l_frame = l_frame.next
except StopIteration:
break
return Gst.PadProbeReturn.OK
def main(args):
# 检查输入参数
if len(args) != 2:
sys.stderr.write("usage: %s <media file or uri>\n" % args[0])
sys.exit(1)
# 初始化 GStreamer
Gst.init(None)
# 创建 GStreamer 管道
print("Creating Pipeline")
pipeline = Gst.Pipeline()
if not pipeline:
sys.stderr.write("Unable to create Pipeline\n")
# 创建各个组件
print("Creating Source")
source = Gst.ElementFactory.make("filesrc", "file-source")
if not source:
sys.stderr.write("Unable to create Source\n")
print("Creating H264Parser")
h264parser = Gst.ElementFactory.make("h264parse", "h264-parser")
if not h264parser:
sys.stderr.write("Unable to create h264 parser\n")
print("Creating Decoder")
decoder = Gst.ElementFactory.make("nvv4l2decoder", "nvv4l2-decoder")
if not decoder:
sys.stderr.write("Unable to create Nvv4l2 Decoder\n")
print("Creating Stream Muxer")
streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
if not streammux:
sys.stderr.write("Unable to create NvStreamMux\n")
print("Creating Primary Inference Engine")
pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
if not pgie:
sys.stderr.write("Unable to create pgie\n")
print("Creating Video Converter")
nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
if not nvvidconv:
sys.stderr.write("Unable to create nvvidconv\n")
print("Creating On-Screen Display")
nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
if not nvosd:
sys.stderr.write("Unable to create nvosd\n")
# 使用 fakesink 作为输出(无头模式)
print("Creating Fake Sink (headless mode)")
sink = Gst.ElementFactory.make("fakesink", "fakesink")
if not sink:
sys.stderr.write("Unable to create fakesink\n")
# 配置组件属性
print(f"Playing file: {args[1]}")
source.set_property('location', args[1])
if os.environ.get('USE_NEW_NVSTREAMMUX') != 'yes':
streammux.set_property('width', 1920)
streammux.set_property('height', 1080)
streammux.set_property('batched-push-timeout', MUXER_BATCH_TIMEOUT_USEC)
streammux.set_property('batch-size', 1)
pgie.set_property('config-file-path', "dstest1_pgie_config.txt")
# 将所有组件添加到管道
print("Adding elements to Pipeline")
pipeline.add(source)
pipeline.add(h264parser)
pipeline.add(decoder)
pipeline.add(streammux)
pipeline.add(pgie)
pipeline.add(nvvidconv)
pipeline.add(nvosd)
pipeline.add(sink)
# 连接管道组件
print("Linking elements in the Pipeline")
source.link(h264parser)
h264parser.link(decoder)
sinkpad = streammux.request_pad_simple("sink_0")
if not sinkpad:
sys.stderr.write("Unable to get the sink pad of streammux\n")
srcpad = decoder.get_static_pad("src")
if not srcpad:
sys.stderr.write("Unable to get source pad of decoder\n")
srcpad.link(sinkpad)
streammux.link(pgie)
pgie.link(nvvidconv)
nvvidconv.link(nvosd)
nvosd.link(sink)
# 创建事件循环
loop = GLib.MainLoop()
bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect("message", bus_call, loop)
# 添加数据探针以获取检测结果
osdsinkpad = nvosd.get_static_pad("sink")
if not osdsinkpad:
sys.stderr.write("Unable to get sink pad of nvosd\n")
osdsinkpad.add_probe(Gst.PadProbeType.BUFFER, osd_sink_pad_buffer_probe, 0)
# 启动管道
print("Starting pipeline")
pipeline.set_state(Gst.State.PLAYING)
try:
loop.run()
except KeyboardInterrupt:
print("\nInterrupted by user")
# 清理资源
pipeline.set_state(Gst.State.NULL)
print("Pipeline stopped")
if __name__ == '__main__':
sys.exit(main(sys.argv))
2.5 运行应用
# 运行修改后的示例
python3 deepstream_test_1_headless.py /opt/nvidia/deepstream/deepstream-7.1/samples/streams/sample_720p.h264
3. 理解输出结果
运行成功后,你将看到类似以下的输出:
Frame Number=1 Number of Objects=3 Vehicle_count=2 Person_count=1
Frame Number=2 Number of Objects=4 Vehicle_count=3 Person_count=1
...
每行输出包含:
- Frame Number:当前处理的帧号
- Number of Objects:检测到的目标总数
- Vehicle_count:车辆数量
- Person_count:行人数量