deepstream实现视频推理结果截图

 0. 在哪里分析GstBuffer数据

You can also get frame data in probe callback. Please refer to deepstream-test1 -> osd_sink_pad_buffer_probe()。

这个是deepstream论坛中相关问题的回答,具体可以阅读下面上个相关问题的内容

1. Access frame pointer in deepstream-app

2. Frame data extraction and pipeline halts/hangs after 7 minutes of run​​​​​​

3. Semantic Segmentation in DS 4

同样,在CSDN中也找到相关博客的介绍

1. DeepStream结合OpenCV4实现视频的分析和截图(一)

2. DeepStream结合OpenCV4实现视频的分析和截图(二

3. Ubuntu18.04.3 安装 Nvidia DeepStream 并在 P4 显卡上解码取帧存图

此外,还参考了OpenCV对于RGBA图像操作的相关内容

1. OpenCV入门(4):透明图像(RGBA)的处理

2. Opencv 图像读取与保存问题

1. 如何访问GstBuffer数据

通过上述分析,我们知道可以在probe回调函数中来保存图像信息。此时我们应该根据自己的需求,选择相应element的pad来设置probe回调函。根据OSD之前和之后获取数据,可以分为两大类:

  • OSD element之前保存数据:可以选择nvinfer的src pad(buffer type为NV12)或者nvvidconv的src pad(buffer type为RGBA)来访问数据
  • OSD element之后保存数据:也即选择osd 的src pad(buffer type为RGBA

上述两种类型之前的区别,应该在于检测信息是否已经渲染到frame_meta数据中。

下面以osd_sink_pad_buffer_probe回调函数举例,如何访问GstBuffer数据?

  /* osd_sink_pad_buffer_probe  will extract metadata received on OSD sink pad
 * and update params for drawing rectangle, object information etc. */

static GstPadProbeReturn
osd_sink_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
    gpointer u_data)
{
    GstBuffer *buf = (GstBuffer *) info->data;  

    char* src_data = NULL;
    if (!gst_buffer_map (buf, &in_map_info, GST_MAP_READ)) 
    {
          g_print ("Error: Failed to map gst buffer\n");
          gst_buffer_unmap (buf, &in_map_info);
          return GST_PAD_PROBE_OK;
    }
    NvBufSurface *surface = (NvBufSurface *)in_map_info.data;

    gst_buffer_unmap (buf, &in_map_info);
}

通过上述的代码,将GstBuffer的内容映射到NvBufSurface *surface上,此时我们可以通过NvBufSurface的对象访问对应GstBuffer的数据。

1.1 NvBufSurface结构

Holds information about batched buffers.可以类比NvDsBatchMeta 

 下面为NvBufSurface的成员变量分析,其中最重要的是确认当前buffer的memory type。

 NvBufSurface对象中的数据memType返回的类型是NvBufSurfaceMemType,通过下面的打印语句能获取到当前NvBufSurface对象的属性信息。

g_print("surface->gpuId=%d\n", surface->gpuId); 
g_print("surface->batchSize=%d\n", surface->batchSize);
g_print("surface->numFilled=%d\n", surface->numFilled);
g_print("surface->isContiguous=%d\n", surface->isContiguous);
g_print("surface->memType=%d\n", surface->memType);

返回结果为

surface->gpuId=0
surface->batchSize=1
surface->numFilled=1
surface->isContiguous=0
surface->memType=0

 buffer的memType默认情况下是0,即NVBUF_MEM_DEFAULT,这样会根据当前的硬件平台选择相应的memType。NVBUF_MEM_CUDA_DEVICE for dGPUNVBUF_MEM_SURFACE_ARRAY for Jetson.

NvBufSurfaceMemType

 2. 基于NvBufSurface保存FrameMeta数据 

此时,我们了解了NvBufSurface对象的详细信息,那么如何获取到buffer的数据呢?我们注意到surfaceList属性,提供了一个指针指向buffer的数据地址并且是array的格式。那么,接下来我们要做的是将这个数据按照对应色彩空间和batch_id保存下来。

2.1 NvBufSurfaceMap

 NvBufSurfaceMap的定义如下,作用是将GPU上的数据映射到CPU上。这个函数使用有两个需要注意的点是:

  1. 对buffer的memType有要求,在dGPU平台上,有效的memType只有NVBUF_MEM_CUDA_UNIFIED,Jetson平台上,支持NVBUF_MEM_SURFACE_ARRAY and NVBUF_MEM_HANDLE类型
  2. 在正式访问映射后的CPU数据之前需要调用 NvBufSurfaceSyncForCpu()函数

 NvBufSurfaceSyncForCpu的定义如下,只有在​​​​​​NVBUF_MEM_SURFACE_ARRAY and NVBUF_MEM_HANDLE类型下有效,因此这个函数调用只需要在Jetson平台上,而在dGPU平台上调不调用区别不大(没有任何效果)NvBufSurfaceSyncForCpu的定义如下,只有在​​​​​​

 下面的代码为NvBufSurfaceMap的框架,映射数据和解除映射相对应一起出现

  batch_meta = gst_buffer_get_nvds_batch_meta(buf);

  if (NvBufSurfaceMap(surface, -1, -1, NVBUF_MAP_READ_WRITE) == 0)
  {
    NvBufSurfaceSyncForCpu(surface, -1, -1); // 在dGPU平台上没有任何作用

    ... // 保存数据

    NvBufSurfaceUnMap(surface, -1, -1);
  }

 将数据映射到CPU上之后,通过下面两行语句能分别获取到GPU和CPU上的数据。

surface->surfaceList[batch_id].dataPtr        // GPU
surface->surfaceList[batch_id].mappedAddr.addr[0]//CPU

 完整的代码可以参考下面这个,我采用的方法是用CPU上的数据初始化一个Mat对象,并将其深拷贝,然后将RGBA的颜色空间转换为BGR,最终保存数据

NvDsMetaList * l_frame = NULL;
for (l_frame = batch_meta->frame_meta_list; l_frame != NULL; l_frame = l_frame->next) 
  {
    NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);
    guint  batch_id = frame_meta->batch_id;
    g_print("surface->surfaceList[batch_id].pitch=%d\n", surface->surfaceList[batch_id].pitch); 
    g_print("surface->surfaceList[batch_id].colorFormat=%d\n", surface->surfaceList[batch_id].colorFormat); // colorFormat = NVBUF_COLOR_FORMAT_RGBA,
    g_print("surface->surfaceList[batch_id].dataSize=%d\n", surface->surfaceList[batch_id].dataSize);
     // according to batch_id 
    uint32_t current_frame_width = surface->surfaceList[batch_id].width;  
    uint32_t current_frame_height = surface->surfaceList[batch_id].height;
    // save image
    cv::Mat current_frame_data = cv::Mat((gint)current_frame_height,
                                          (gint)current_frame_width,
                                          CV_8UC4,
                                          surface->surfaceList[batch_id].mappedAddr.addr[0],
                                          surface->surfaceList[batch_id].pitch
                                          );
    cv::Mat image_data((gint)current_frame_height,
                        (gint)current_frame_width,
                        CV_8UC4);
    current_frame_data.copyTo(image_data);
    // RGBA convert BGR 
    cv::Mat ouput_image = cv::Mat ((gint)current_frame_height,
                                (gint)current_frame_width, 
                                CV_8UC3);
    cv::cvtColor(image_data, ouput_image, cv::COLOR_RGBA2BGR);    // opencv4
    cv::imwrite("saved_image.jpg", ouput_image);
    }

2.2 Makefile文件的修改

1. 添加opencv的头文件

CFLAGS+= -I/opt/nvidia/deepstream/deepstream-5.1/sources/includes \
		 -I /usr/local/cuda-$(CUDA_VER)/include \
		 -fPIC -std=c++17 -g	\
		 -I/usr/local/opencv-4.5.1/include/opencv4

2. 添加相对应的库文件

  • -lnvbufsurface -lnvbufsurftransform
  • -L/usr/local/opencv-4.5.1/lib -lopencv_core -lopencv_imgproc -lopencv_imgcodecs 
LIBS+= -L$(LIB_INSTALL_DIR) -lnvdsgst_meta -lnvds_meta -lnvdsgst_helper -lm  -lnvbufsurface -lnvbufsurftransform \
	   -L/usr/local/cuda-$(CUDA_VER)/lib64/ -lcudart \
       -lcuda -Wl,-rpath,$(LIB_INSTALL_DIR)	\
	   -L/usr/local/opencv-4.5.1/lib -lopencv_core -lopencv_imgproc -lopencv_imgcodecs

通过上述的过程,我们就能保存带检测结果的图像帧数据。

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值