运行你的第一个 DeepStream Python 应用

运行你的第一个 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 代码修改说明

主要修改点:

  1. 移除图形显示组件:注释掉平台检测和图形渲染相关代码
  2. 使用虚拟输出:用 fakesink 替代图形显示组件
  3. 保留数据处理:确保检测结果仍能正常输出到控制台

关键修改:

  • 移除 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:行人数量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值