实现带有 OpenCV 集成示例的自定义 GStreamer 插件#
DeepStream SDK 支持一种机制,通过修改示例插件 (gst-dsexample
) 在参考应用程序中添加第三方或自定义算法。该插件的源代码位于 SDK 的 sources/gst-plugins/gst-dsexample
目录中。此插件是为 GStreamer 1.14.1 编写的,但与较新版本的 GStreamer 兼容。此插件派生自 GstBaseTransform
类:https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstBaseTransform.html
注意
要启用 OpenCV 功能,请在插件 Makefile 中使用标志 WITH_OPENCV=1
编译 dsexample
插件。
示例插件的描述:gst-dsexample#
GStreamer 示例插件 (gst-dsexample
) 演示了以下内容
处理整个帧,并在需要时进行缩小/颜色转换。
处理主检测器检测到的对象,具体来说,从帧中裁剪这些对象,然后处理裁剪后的对象。
使用 OpenCV 原位修改缓冲区帧内容
包含该插件的两个版本。请参阅插件的
Makefile
和 README 以在它们之间切换简单 (gstdsexample.cpp) - 顺序预处理和处理
优化 (gstdsexample_optimized.cpp) - 并行批处理预处理和处理
此版本包含一个简单的静态库 dsexample_lib
,该库演示了自定义库与此 Gstreamer
插件之间的接口。该库生成 Obj_label
形式的简单标签。该库实现了以下函数
DsExampleCtxInit — 初始化自定义库
DsExampleCtxDeinit — 反初始化自定义库
DsExampleProcess – 处理输入帧
GStreamer 插件本身是一个标准的就地转换插件。由于它不生成新缓冲区,而只是添加/更新现有元数据,因此该插件实现了就地转换。一些代码是标准的 GStreamer 插件样板代码(例如,plugin_init
、class_init
、instance_init
)。其他感兴趣的函数如下
GstBaseTransfrom 类函数#
start — 获取资源,分配内存,初始化示例库。
stop — 反初始化示例库,并释放资源和内存。
set_caps — 获取流经此元素的视频的功能(即分辨率、颜色格式、帧速率)。可以在此处完成依赖于输入视频格式的分配/初始化。
transform_ip — 在简单版本中实现。当插件从上游元素接收到缓冲区时调用。
查找主检测器的元数据。
使用
get_converted_mat
预处理帧/对象裁剪,以获取推送到库所需的缓冲区。将数据推送到示例库。弹出示例库输出。使用
attach_metadata_full_frame
或attach_metadata_object
附加/更新元数据。或者,就地修改帧内容,以使用
blur_objects
模糊对象。
submit_input_buffer — 在优化版本中实现。当插件从上游元素接收到缓冲区时调用。与
gst_dsexample_output_loop
并行工作以提高性能。查找主检测器的元数据。
创建要预处理的帧/对象批次。预处理批次并将预处理后的输出推送到处理线程。
在前一批次上预处理时,处理线程处理较旧的批次。
其他支持函数#
get_converted_mat — 缩放、转换或裁剪输入缓冲区,可以是完整帧,也可以是对象(基于其在主检测器元数据中的坐标)。
attach_metadata_full_frame — 显示插件如何为其插件检测到的对象附加自己的元数据。
attach_metadata_object — 显示插件如何更新主检测器检测到的对象的标签。
blur_objects — 就地修改缓冲区帧内容,以使用 OpenCV GaussianBlur 模糊对象。在 dGPU 上运行时,请确保插件的输入内存类型为
NVBUF_MEM_CUDA_UNIFIED
。gst_dsexample_output_loop — 与
submit_input_buffer
并行工作以提高性能。等待批次的预处理完成
使用
dsexample_lib
API 处理批次使用
attach_metadata\_*
函数之一附加输出
注意
在 Jetson 设备上,自定义 GStreamer 插件必须导出环境变量 DS_NEW_BUFAPI 并将其值设置为 1。有关插件 (Gst-dsexample
) 中的示例,请参阅 gst_dsexample_class_init()
。
启用和配置示例插件#
预编译的 deepstream-app 二进制文件已具有解析配置并将示例元素添加到管道的功能。要启用和配置插件,请将以下部分添加到现有配置文件(例如,source4_720p_dec_infer-resnet_tracker_sgie_tiled_display_int8.txt
)
[ds-example]
enable=1
processing-width=640
processing-height=480
full-frame=0
blur-objects=0
unique-id=15
在自定义应用程序/管道中使用示例插件#
示例插件可以在 gst-launch
管道中使用。管道也可以在自定义应用程序中构建。
构建用于在全帧模式下运行插件的管道 构建用于在全帧模式下运行插件的管道,命令如下。
对于 Jetson
$ gst-launch-1.0 filesrc location= <mp4-file> ! qtdemux ! h264parse ! nvv4l2decoder ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvvideoconvert ! dsexample full-frame=1 <other-properties> ! nvdsosd ! nv3dsink
对于 Tesla
$ gst-launch-1.0 filesrc location= <mp4-file> ! qtdemux ! h264parse ! nvv4l2decoder ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvvideoconvert ! dsexample full-frame=1 <other-properties> ! nvdsosd ! nveglglessink
构建用于运行插件以处理主模型检测到的对象的管道 构建用于运行插件以处理主模型检测到的对象的管道,命令如下。
对于 Jetson
$ gst-launch-1.0 filesrc location= <mp4-file> ! qtdemux ! h264parse ! nvv4l2decoder ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvinfer config-file-path= <primary-detector-config> ! nvvideoconvert ! dsexample full-frame=0 <other-properties> ! nvdsosd ! nv3dsink
对于 Tesla
$ gst-launch-1.0 filesrc location= <mp4-file> ! qtdemux ! h264parse ! nvv4l2decoder ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvinfer config-file-path= <primary-detector-config> ! nvvideoconvert ! dsexample full-frame=0 <other-properties> ! nvdsosd ! nveglglessink
构建用于运行插件以模糊主模型检测到的对象的管道 构建用于运行插件以模糊主模型检测到的对象的管道,命令如下
对于 Jetson
$ gst-launch-1.0 filesrc location= <mp4-file> ! qtdemux ! h264parse ! nvv4l2decoder ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvinfer config-file-path= <primary-detector-config> ! nvvideoconvert ! 'video/x-raw(memory:NVMM), format=RGBA' ! dsexample full-frame=0 blur-objects=1 ! nvdsosd ! nv3dsink
对于 Tesla
$ gst-launch-1.0 filesrc location= <mp4-file> ! qtdemux ! h264parse ! nvv4l2decoder ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvinfer config-file-path= <primary-detector-config> ! nvvideoconvert nvbuf-memory-type= nvbuf-mem-cuda-unified ! 'video/x-raw(memory:NVMM), format=RGBA' ! dsexample full-frame=0 blur-objects=1 ! nvdsosd ! nveglglessink
在示例插件中实现自定义逻辑#
要在插件中实现自定义逻辑,请将以下函数调用列表替换为任何其他自定义库的相应函数。
DsExampleCtxInit
DsExampleCtxDeinit
DsExampleProcess
blur_objects
根据库的输入要求,get_converted_mat
也可能需要修改。
为示例插件添加 NVTX API#
与其他 DeepStreamSDK GStreamer 插件一样,NVTX API 也可以添加到自定义插件中。有关这些 API 的更多信息,请参见 https://docs.nvda.net.cn/gameworks/content/gameworkslibrary/nvtx/nvidia_tools_extension_library_nvtx.htm。按照以下步骤为自定义插件添加 NVTX API
在插件的源代码中包含
nvtx3/nvToolsExt.h
标头。要测量范围,通常使用两个 API
nvtxRangePushA (context)
- 此组件/插件的分析开始的点。nvtxRangePop ()
- 此组件/插件的分析停止的点。
确保标记的放置位置使得插件的核心功能在上述两个 API 之间执行。这将准确地了解延迟。
为自定义插件运行 NSight,以获取有关在这两个标记之间运行的任务的信息。
在 OpenCV 中访问 NvBufSurface 内存#
NvBufSurface
中的 CUDA 和 CPU 内存可以通过 OpenCV 的 cv::cuda::GpuMat
和 cv::Mat
接口访问。在这种情况下,NvBufSurface
可以与 OpenCV 中实现的任何计算机视觉算法一起使用。以下代码片段显示了如何在 OpenCV 中访问和使用 NvBufSurface
的 CUDA 内存。
cv::cuda::GpuMat gpuMat;
const int aDstOrder[] = {2,0,1,3};
unsigned int index = 0; // Index of the buffer in the batch.
unsigned int width, height; // set width and height of buffer
NvBufSurface *input_buf; // Pointer to input NvBufSurface
gpuMat = cv::cuda::GpuMat(height, width, CV_8UC4,
(void *)input_buf->surfaceList[index].dataPtr);
或
gpuMat = cv::cuda::GpuMat(height, width, CV_8UC4,
(void *) input_buf->surfaceList[index].dataPtr,
input_buf->surfaceList[index].pitch);
cv::cuda::swapChannels(gpuMat, aDstOrder);
在 Jetson 平台上,如果 NvBufSurface
的内存类型为 NVBUF_MEM_SURFACE_ARRAY,则应先通过 CUDA-EGL 互操作将其转换为 CUDA,然后再在 OpenCV 中访问它。请参考 sources/gst-plugins/gst-dsexample/gstdsexample.cpp
以访问 OpenCV 矩阵 (cv::Mat
) 中的 NvBufSurface
内存。以下是所需的步骤
使用 NvBufSurfaceMapEglImage() 从 NvBufSurface 创建 EGL 图像
使用 cuGraphicsEGLRegisterImage() 在 cuda 中注册 EGL 图像
使用 cuGraphicsResourceGetMappedEglFrame() 映射 EGL 帧以获取 cuda 指针
有关更多详细信息,请参阅文件 /opt/nvidia/deepstream/deepstream/sources/gst-plugins/gst-nvinfer/gstnvinfer_allocator.cpp
中的 gst_nvinfer_allocator_alloc。