0. application介绍
相对于deepstream中大多使用video的例子,该sample向我们展示了如何使用image的解码器来进行图像的推理。该sample支持多路输入,是在deepstream-test3的基础上改进得到的。该pipeline接收MJPEG和JPEG格式的数据作为输入。
该sample的代码位于deepstream下的sources/apps/sample_apps/deepstream-image-decode-test路径下面。
1. 编译和运行
根据该sample下的README,学习一下如何编译和运行该sample。README提供的命令行如下:
To compile: # 编译
$ Set CUDA_VER in the MakeFile as per platform. # 设置CUDA的版本(应该根据自己当前平台安装的CUDA版本提供)
For Jetson, CUDA_VER=10.2 # 对于Jetson硬件平台
For x86, CUDA_VER=11.4 # 对于x86硬件平台
$ sudo make # make 编译(加不加sudo随意)
To run: # 运行推理
$ ./deepstream-image-decode-app <file1> [file2] ... [fileN] # fileN为文件的路径
e.g.
$ ./deepstream-image-decode-app file1.mjpeg file2.mjpeg
通过运行上述的命令,(只要不移动该sample代码的位置)一般情况下能编译运行起来。但是,还是解释一下Makefile中一些语句的含义:
CUDA_VER
与CUDA_VER相关的语句如下,含义只要正确的设置cuda的版本编译的时候才能找到向对应的头文件和动态链接库
CUDA_VER?=
ifeq ($(CUDA_VER),)
$(error "CUDA_VER is not set")
endif
# cuda的头文件路径
-I /usr/local/cuda-$(CUDA_VER)/include
# cuda的库文件路径
-L/usr/local/cuda-$(CUDA_VER)/lib64/
sources文件夹下的include路径
在Makefile文件中使用如下的语句链接sources路径下的include文件夹,采用的是相对路径,如果你移动了该sample代码的位置,建议使用绝对路径指定include的路径
-I../../../includes
这样我们基本上能正确的编译通过,此时在该sample的路径下会生成一个deepstream-image-decode-app的可执行文件,通过运行与如下类似的命令,便可以在显示器上看到检测结果(对于输入的图像数据,检测结果会一闪而过)
# jpg 图像
./deepstream-image-decode-app /opt/nvidia/deepstream/deepstream-6.0/samples/streams/sample_720p.jpg
# mjpeg 视频数据
./deepstream-image-decode-app /opt/nvidia/deepstream/deepstream-6.0/samples/streams/sample_720p.mjpeg
2. pipeline分析
该sample的整体pipeline结果应该如下图所示。filesrc接收jpg或mjepg数据,jpegparser对数据进行解析,nvv4l2decoder对数据进行解码,streammux将多路数据组成batch,nvinfer对输入的batch数据进行推理,nvmultistreamtiler将batch数据平铺成2D数据,nvvideoconvert将数据格式从NV12转换成RGBA(nvosd要求的数据格式), nvdsosd将检测结果渲染到frame数据中,nvegltransform和nveglelessink用于将结果显示到屏幕上。
同样,我们也可以使用gstreamer自带的功能导出pipeline。具体步骤如下:
1. 安装graphviz,该工具能将dot文件转换为pdf或者图像文件
sudo apt-get install graphviz
2. 设置GST_DEBUG_DUMP_DOT_DIR属性
设置GST_DEBUG_DUMP_DOT_DIR属性有两种方式,一种是暂时性修改(仅对当前终端有效),设置方法是在当前终端输入如下命令:
export GST_DEBUG_DUMP_DOT_DIR=./pipeline/
另一种是永久修改,通过在~/.bashrc文件中添加上面类似的语句,具体过程如下:
sudo vi ~/.bashrc
# 进入文件添加该语句, 同时需要注意的是给定的路径一定要存在,即gstreamer不会自动创建文件名
export GST_DEBUG_DUMP_DOT_DIR=/home/nvidia/pipeline/
# 保存文件
source ~/.bashrc
3. 生成pipeline的dot文件
对于gst-launch-1.0工具和deepstream-app,只要pipeline的状态改变就会生成一组新的pipeline的dot文件。正常情况下,生成的文件内容如下所示。
对于自己搭建的应用程序,需要在cpp文件中加入如下的语句:
GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline");
其中,GST_DEBUG_BIN_TO_DOT_FILE的定义如下:
#define GST_DEBUG_BIN_TO_DOT_FILE(bin, details, file_name) gst_debug_bin_to_dot_file (bin, details, file_name)
各参数的含义如下:
4. 将dot文件转换为pdf或图像文件
通过-?能查看dot支持的命令
dot -?
将dot文件转换的命令一般如下:
dot -T{format} src_file>dst_file
一般情况下,是将playing状态的dot文件转换,方式如下:
# pdf(效果比较好)
dot -Tpdf pipeline/0.00.06.619216356-ds-app-playing.dot > pipeline/pipeline-playing.pdf
# png
dot -Tpng pipeline/0.00.06.619216356-ds-app-playing.dot > pipeline/pipeline-playing.png
3. plugin使用分析
3.1 source plugin
在该sample中,提供了两种source插件:filesrc和multifilesrc
if (strstr(uri, "%d"))
{
source = gst_element_factory_make("multifilesrc", "source");
multi_file_src = TRUE;
}
else
source = gst_element_factory_make ("filesrc", "source");
filesrc接收单个文件(包括图片和视频),具体的代码分析可以参考【Gstreamer之plugin教程二-filesrc】
multifilesrc支持接收一个序列的数据,其中的index序号使用"%d"格式化表示。对于该sample,如果你的图像数据的命名是如下格式:
image
├── test0000.jpg
├── test0001.jpg
├── test0002.jpg
├── test0003.jpg
├── test0004.jpg
├── test0005.jpg
├── test0006.jpg
├── test0007.jpg
...
可以通过如下命令,推理这个序列的data:
./deepstream-image-decode-app ./image/test%04d.jpg
需要注意的是,输入的序列文件名中间不能中断或间隔多个,否则后续的数据没法检测。
3.2 保存图像推理结果
在该sample中,对于图像推理后其结果会一闪而过,因此想的是将检测结果保存下来,同时也可以参考下面这个问题。
How to save output image when running deepstream-image-decode-test?
保存图像推理结果的具体做法是添加nvvideoconvert->jpegenc->filesink这个三个plugin。代码如下:
/* --------------------------- 添加element用于输出JPEG ------------------ */
GstElement *nvvideoconvert = NULL, *jpegenc = NULL;
nvvideoconvert = gst_element_factory_make("nvvideoconvert", "nvvideo-converter2"); // RGBA -> RGB
jpegenc = gst_element_factory_make("jpegenc", "jpegenc");
sink = gst_element_factory_make("multifilesink", "mjpeg-filesink");
/* ------------------------------------------------------------------- */
g_object_set (G_OBJECT (sink), "sync", 0, NULL);
g_object_set (G_OBJECT (sink), "location", "test%d.jpg", NULL);
此时的pipeline应该如下:
filesrc->jpegparse->nvv4l2decoder->nvstreammux->nvinfer->nvmultistreamtiler->nvvideoconvert->nvdsosd->nvvideoconvert->jpegenc->filesink
3.3 将filesink修改为fakesink
使用fakesink意味着什么都不输出,具体问题描述可以参考下面链接
In deepstream_image_decoder_c,how can I change nveglglessink to fakesink?
同样问题的解决方法也在这个链接中给出,通过在sink前面添加一个queue。
#ifdef PLATFORM_TEGRA
transform = gst_element_factory_make ("queue", "nvegl-transform");
#endif
sink = gst_element_factory_make ("fakesink", "nvvideo-renderer");
上述只是对如何使用该sample进行了介绍,如果相对其进行更多的修改请阅读源代码!