如何自己编写英伟达jetson开发板中deepstream框架插件
什么是gstreamer插件?
简单的说,deepstream框架是英伟达jetson系列边缘设备中的加速框架,想要在该框架中运行自己的算法,需要编写gstreamer插件,因为在该框架中,视频流的处理是由一个个gstreamer插件构成,包括视频编解码,视频格式转换,算法推理,视频推流等插件,并且需要在deepstream app中将需要用到的插件连接起来,构成流水线来运行。gstreamer插件就相当于deepstream框架中的一个个模块,模块间仅有数据传输,互不影响。
如何编写gstreamer插件
首先我们需要找到deepstream文件夹下source/gst-plugins/gstdsexample.cpp和gstdsexample.h文件(模板代码),仿照其代码格式进行修改,此处采用在插件中调用算法的方式(之前试过分别写前处理、推理、后处理插件,再连接起来的方式,发现框架找不到这三个插件)
- 首先我们先来看gstdsexample.h文件 ;
//定义你自己的头文件名称
#ifndef __GST_MYMETHOD_H__
#define __GST_MYMETHOD_H__
#include <gst/base/gstbasetransform.h>
#include <gst/video/video.h>
/* Open CV headers */
#pragma GCC diagnostic push
#if __GNUC__ >= 8
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#pragma GCC diagnostic pop
#include <cuda.h>
#include <cuda_runtime.h>
#include "nvbufsurface.h"
#include "nvbufsurftransform.h"
#include "gst-nvquery.h"
#include "gstnvdsmeta.h"
// mymethod_lib中放你自己算法的源码,然后在此处引入头文件进行调用
#include "mymethod_lib/mymethod_lib.h"
//1. det
#include "mymethod_lib/det/simple_yolo.hpp"
*/
/* Package and library details required for plugin_init */
/*插件描述信息,其中PACKAGE字段指定了插件的名称,通过该名称实现插件的调用*/
#define PACKAGE "mymethod"
#define VERSION "1.0"
#define LICENSE "Proprietary"
#define DESCRIPTION "my sot track method example plugin for integration with DeepStream on DGPU"
#define BINARY_PACKAGE "NVIDIA DeepStream 3rdparty IP integration example plugin"
#define URL "http://nvidia.com/"
G_BEGIN_DECLS
/* Standard boilerplate stuff 固定结构,声明插件实例与插件类*/
typedef struct _GstMyMethod GstMyMethod;
typedef struct _GstMyMethodClass GstMyMethodClass;
/* Standard boilerplate stuff 固定结构*/
#define GST_TYPE_MYMETHOD (gst_mymethod_get_type())
#define GST_MYMETHOD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MYMETHOD,GstMyMethod))
#define GST_MYMETHOD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MYMETHOD,GstMyMethodClass))
#define GST_MYMETHOD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_MYMETHOD, GstMyMethodClass))
#define GST_IS_MYMETHOD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MYMETHOD))
#define GST_IS_MYMETHOD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MYMETHOD))
#define GST_MYMETHOD_CAST(obj) ((GstMyMethod *)(obj))
//类的实例对象
struct _GstMyMethod
{
GstBaseTransform base_trans;
// Unique ID of the element. The labels generated by the element will be
// updated at index `unique_id` of attr_info array in NvDsObjectParams.
/*元素的唯一ID。元素生成的标签将在NvDsObjectParams中attr_info数组的索引“unique_id”处更新。*/
guint unique_id;
// Frame number of the current input buffer
guint64 frame_num;
// CUDA Stream used for allocating the CUDA task
cudaStream_t cuda_stream;
// Host buffer to store RGB data for use by algorithm
//用于存储RGB数据以供算法使用的主缓冲区
void *host_rgb_buf;
// the intermediate scratch buffer for conversions RGBA
//用于转换的中间暂存缓冲区RGBA
NvBufSurface *inter_buf;
// OpenCV mat containing RGB data
cv::Mat *cvmat;
// Input video info (resolution, color format, framerate, etc)
GstVideoInfo video_info;
// Resolution at which frames/objects should be processed
gint processing_width;
gint processing_height;
// Flag which defince igpu/dgpu
guint is_integrated;
// Amount of objects processed in single call to algorithm
guint batch_size;
// GPU ID on which we expect to execute the task
guint gpu_id;
// 下面是自己算法需要用到的一些变量或者参数,按自己的需求来写
//1. det params
std::shared_ptr<SimpleYolo::Infer> engine_v5s;
gchar* model_name;
};
//类(固定写法,只需要改成自己的名字即可)
// Boiler plate stuff
struct _GstMyMethodClass
{
GstBaseTransformClass parent_class;
};
GType gst_mymethod_get_type (void);
G_END_DECLS
#endif /* __GST_MYMETHOD_H__ */
- 然后再看gstdsexample.cpp是如何写的,代码较多,就不直接放全部代码
- 第一部分是添加头文件和定义一些需要的函数,固定写法,只需要将mymethod改成自己的名字;
//添加需要使用的头文件
#include <string.h>
#include <string>
#include <sstream>
#include <iostream>
#include <ostream>
#include <fstream>
#include "gstmymethod.h"
#include <sys/time.h>
#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>
GST_DEBUG_CATEGORY_STATIC (gst_mymethod_debug);
#define GST_CAT_DEFAULT gst_mymethod_debug
#define USE_EGLIMAGE 1
/* enable to write transformed cvmat to files */
/* #define MYMETHOD_DEBUG */
static GQuark _dsmeta_quark = 0;
// classes(此处写用目标检测算法时的类别)
static const char* cocolabels[] = {
"army"
};
//设置插件属性的函数,通过属性可以对插件内部的变量进行赋值,方便调整一些参数
static void gst_mymethod_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_mymethod_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
// 获取输入图像的信息
static gboolean gst_mymethod_set_caps (GstBaseTransform * btrans,
GstCaps * incaps, GstCaps * outcaps);
// 资源初始化,如算法初始化,插件初始化等
static gboolean gst_mymethod_start (GstBaseTransform * btrans);
// 资源销毁
static gboolean gst_mymethod_stop (GstBaseTransform * btrans);
// 算法调用
static GstFlowReturn gst_mymethod_transform_ip (GstBaseTransform *
btrans, GstBuffer * inbuf);
// 1. det attach metadata 将检测结果传给下游插件
static void attach_metadata_det(GstMyMethod * dsexample, NvDsFrameMeta *frame_meta,
gdouble scale_ratio, SimpleYolo::BoxArray boxes);
- 第二部分是定义插件属性的默认值, 这部分的枚举变量和插件属性的默认值按需修改,其他为固定写法,只需要将mymethod改成自己的名字;
/* Enum to identify properties 插件属性的枚举变量*/
enum
{
PROP_GPU_DEVICE_ID,
PROP_MODEL_NAME,
};
#define CHECK_NVDS_MEMORY_AND_GPUID(object, surface) \
({ int _errtype=0;\
do { \
if ((surface->memType == NVBUF_MEM_DEFAULT || surface->memType == NVBUF_MEM_CUDA_DEVICE) && \
(surface->gpuId != object->gpu_id)) { \
GST_ELEMENT_ERROR (object, RESOURCE, FAILED, \
("Input surface gpu-id doesnt match with configured gpu-id for element," \
" please allocate input using unified memory, or use same gpu-ids"),\
("surface-gpu-id=%d,%s-gpu-id=%d",surface->gpuId,GST_ELEMENT_NAME(object),\
object->gpu_id)); \
_errtype = 1;\
} \
} while(0); \
_errtype; \
})
/* Default values for properties 插件属性默认值*/
#define DEFAULT_GPU_ID 0
#define DEFAULT_MODEL_NAME ""
#define RGB_BYTES_PER_PIXEL 3
#define RGBA_BYTES_PER_PIXEL 4
#define CHECK_NPP_STATUS(npp_status,error_str) do { \
if ((npp_status) != NPP_SUCCESS) { \
g_print ("Error: %s in %s at line %d: NPP Error %d\n", \
error_str, __FILE__, __LINE__, npp_status); \
goto error; \
} \
} while (0)
#define CHECK_CUDA_STATUS(cuda_status,error_str) do { \
if ((cuda_status) != cudaSuccess) { \
g_print ("Error: %s in %s at line %d (%s)\n", \
error_str, __FILE__, __LINE__, cudaGetErrorName(cuda_status)); \
goto error; \
} \
} while (0)
/* By default NVIDIA Hardware allocated memory flows through the pipeline. We
* will be processing on this type of memory only. */
//设置插件sink和src端的连接方式与属性,这里设置数据存放在GPU,所以内存指定为"memory:NVMM"
#define GST_CAPS_FEATURE_MEMORY_NVMM "memory:NVMM"
static GstStaticPadTemplate gst_mymethod_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
(GST_CAPS_FEATURE_MEMORY_NVMM,
"{ NV12, RGBA, I420 }")));
static GstStaticPadTemplate gst_mymethod_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
(GST_CAPS_FEATURE_MEMORY_NVMM,
"{ NV12, RGBA, I420 }")));
/* Define our element type. Standard GObject/GStreamer boilerplate stuff 固定结构*/
#define gst_mymethod_parent_class parent_class
G_DEFINE_TYPE (GstMyMethod, gst_mymethod, GST_TYPE_BASE_TRANSFORM);
- 第三部分是配置函数的实现,基本是固定写法,其中一些属性按需添加,需要将mymethod改成自己的名字 ;
/* Install properties, set sink and src pad capabilities, override the required
* functions of the base class, These are common to all instances of the
* element.
*/
// 插件的配置函数,配置子类的实现、插件属性、插件描述信息等
static void
gst_mymethod_class_init (GstMyMethodClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseTransformClass *gstbasetransform_class;
/* Indicates we want to use DS buf api */
g_setenv ("DS_NEW_BUFAPI", "1", TRUE);
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasetransform_class = (GstBaseTransformClass *) klass;
/* Overide base class functions 重构类的函数实现*/
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mymethod_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mymethod_get_property);
gstbasetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_mymethod_set_caps);
gstbasetransform_class->start = GST_DEBUG_FUNCPTR (gst_mymethod_start);
gstbasetransform_class->stop = GST_DEBUG_FUNCPTR (gst_mymethod_stop);
//默认直通模式下,所以子类重构的是transform_ip函数
gstbasetransform_class->transform_ip =
GST_DEBUG_FUNCPTR (gst_mymethod_transform_ip);
/* Install properties 注册插件属性*/
g_object_class_install_property (gobject_class, PROP_GPU_DEVICE_ID,
g_param_spec_uint ("gpu-id",
"Set GPU Device ID",
"Set GPU Device ID", 0,
G_MAXUINT, 0,
GParamFlags
(G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)));
g_object_class_install_property (gobject_class, PROP_MODEL_NAME,
g_param_spec_string ("model-name", "v5 model name/Path",
"Path/name to the model file for this instance of nvinfer",
DEFAULT_MODEL_NAME,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_PLAYING)));
/* Set sink and src pad capabilities 设置sink和src的pad capabilities属性*/
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_mymethod_src_template));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_mymethod_sink_template));
/* Set metadata describing the element 设置插件描述信息*/
gst_element_class_set_details_simple (gstelement_class,
"MyMethod plugin",
"MyMethod Plugin",
"some functions (det, mut, sot)",
"NVIDIA Corporation. Post on Deepstream for Tesla forum for any queries "
"@ https://devtalk.nvidia.com/default/board/209/");
}
static void
gst_mymethod_init (GstMyMethod * mymethod)
{
GstBaseTransform *btrans = GST_BASE_TRANSFORM (mymethod);
/* We will not be generating a new buffer. Just adding / updating metadata. */
//设置是否基于同一内存空间处理buffer,在直通模式下,inbuf和outbuf是同一内存空间的
gst_base_transform_set_in_place (GST_BASE_TRANSFORM (btrans), TRUE);
/* We do not want to change the input caps. Set to passthrough. transform_ip
* is still called. */
//设置是否是直通模式,直通模式下调用的是transform_ip函数,非直通模式下调用transform
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (btrans), TRUE);
/* Initialize all property variables to default values 设置插件属性默认值*/
mymethod->gpu_id = DEFAULT_GPU_ID;
mymethod->model_name = DEFAULT_MODEL_NAME;
/* This quark is required to identify NvDsMeta when iterating through
* the buffer metadatas */
if (!_dsmeta_quark)
_dsmeta_quark = g_quark_from_static_string (NVDS_META_STRING);
}
/* Function called when a property of the element is set. Standard boilerplate.
设置插件属性信息(标准模板)*/
static void
gst_mymethod_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstMyMethod *mymethod = GST_MYMETHOD (object);
switch (prop_id) {
case PROP_GPU_DEVICE_ID:
mymethod->gpu_id = g_value_get_uint (value);
break;
case PROP_MODEL_NAME:
//g_free (mymethod->model_name);
mymethod->model_name = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/* Function called when a property of the element is requested. Standard
* boilerplate.
获取插件属性信息(标准模板)*/
static void
gst_mymethod_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstMyMethod *mymethod = GST_MYMETHOD (object);
switch (prop_id) {
case PROP_GPU_DEVICE_ID:
g_value_set_uint (value, mymethod->gpu_id);
break;
case PROP_MODEL_NAME:
g_value_set_string (value, mymethod->model_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
- 第四部分是资源初始化和销毁函数以及获取图像信息函数的实现 ,只需添加自己算法的初始化代码即可,其他为固定写法,需要将mymethod改成自己的名字;
/**
* Initialize all resources and start the output thread
初始化资源,如算法初始化*/
static gboolean
gst_mymethod_start (GstBaseTransform * btrans)
{
GstMyMethod *mymethod = GST_MYMETHOD (btrans);
NvBufSurfaceCreateParams create_params;
MyMethodInitParams init_params =
{ mymethod->processing_width, mymethod->processing_height};
GstQuery *queryparams = NULL;
guint batch_size = 1;
int val = -1;
// 1.det
int deviceid = 0;
auto mode_name = SimpleYolo::mode_string(SimpleYolo::Mode::FP16);
SimpleYolo::set_device(deviceid);
std::string model_file_v5s = mymethod->model_name; //"yolov5s_dynamic.FP16.engine";
mymethod->engine_v5s = SimpleYolo::create_infer(
model_file_v5s,
SimpleYolo::Type::V5,
deviceid,
0.4f,
0.5f
);
if (mymethod->engine_v5s == nullptr) {
std::cout << "init det model failed !";
return 0;
}
/* Algorithm specific initializations and resource allocation. 算法特定的初始化和资源分配*/
/*
mymethod->mymethodlib_ctx = MyMethodCtxInit (&init_params);
GST_DEBUG_OBJECT (mymethod, "ctx lib %p \n", mymethod->mymethodlib_ctx);
*/
CHECK_CUDA_STATUS (cudaSetDevice (mymethod->gpu_id),
"Unable to set cuda device");
cudaDeviceGetAttribute (&val, cudaDevAttrIntegrated, mymethod->gpu_id);
mymethod->is_integrated = val;
mymethod->batch_size = 1;
queryparams = gst_nvquery_batch_size_new ();
if (gst_pad_peer_query (GST_BASE_TRANSFORM_SINK_PAD (btrans), queryparams)
|| gst_pad_peer_query (GST_BASE_TRANSFORM_SRC_PAD (btrans), queryparams)) {
if (gst_nvquery_batch_size_parse (queryparams, &batch_size)) {
mymethod->batch_size = batch_size;
}
}
GST_DEBUG_OBJECT (mymethod, "Setting batch-size %d \n",
mymethod->batch_size);
gst_query_unref (queryparams);
CHECK_CUDA_STATUS (cudaStreamCreate (&mymethod->cuda_stream),
"Could not create cuda stream");
if (mymethod->inter_buf)
NvBufSurfaceDestroy (mymethod->inter_buf);
mymethod->inter_buf = NULL;
/* An intermediate buffer for NV12/RGBA to BGR conversion will be
* required. Can be skipped if custom algorithm can work directly on NV12/RGBA.
图像数据格式转换 NV12/RGBA to BGR 若自定义算法能在NV12/RGBA上处理,那可不用转换*/
create_params.gpuId = mymethod->gpu_id;
create_params.width = mymethod->processing_width;
create_params.height = mymethod->processing_height;
create_params.size = 0;
create_params.colorFormat = NVBUF_COLOR_FORMAT_RGBA;
create_params.layout = NVBUF_LAYOUT_PITCH;
if(mymethod->is_integrated) {
create_params.memType = NVBUF_MEM_DEFAULT;
}
else {
create_params.memType = NVBUF_MEM_CUDA_PINNED;
}
if (NvBufSurfaceCreate (&mymethod->inter_buf, 1,
&create_params) != 0) {
GST_ERROR ("Error: Could not allocate internal buffer for mymethod");
goto error;
}
/* Create host memory for storing converted/scaled interleaved RGB data */
CHECK_CUDA_STATUS (cudaMallocHost (&mymethod->host_rgb_buf,
mymethod->processing_width * mymethod->processing_height *
RGB_BYTES_PER_PIXEL), "Could not allocate cuda host buffer");
GST_DEBUG_OBJECT (mymethod, "allocated cuda buffer %p \n",
mymethod->host_rgb_buf);
//开启opencv就需要设置该参数来保存opencv图像数据
/* CV Mat containing interleaved RGB data. This call does not allocate memory.
* It uses host_rgb_buf as data. */
mymethod->cvmat =
new cv::Mat (mymethod->processing_height, mymethod->processing_width,
CV_8UC3, mymethod->host_rgb_buf,
mymethod->processing_width * RGB_BYTES_PER_PIXEL);
if (!mymethod->cvmat)
goto error;
GST_DEBUG_OBJECT (mymethod, "created CV Mat\n");
return TRUE;
error:
if (mymethod->host_rgb_buf) {
cudaFreeHost (mymethod->host_rgb_buf);
mymethod->host_rgb_buf = NULL;
}
if (mymethod->cuda_stream) {
cudaStreamDestroy (mymethod->cuda_stream);
mymethod->cuda_stream = NULL;
}
/*
if (mymethod->mymethodlib_ctx)
MyMethodCtxDeinit (mymethod->mymethodlib_ctx);
return FALSE;
*/
}
/**
* Stop the output thread and free up all the resources
*/
static gboolean
gst_mymethod_stop (GstBaseTransform * btrans)
{
GstMyMethod *mymethod = GST_MYMETHOD (btrans);
if (mymethod->inter_buf)
NvBufSurfaceDestroy(mymethod->inter_buf);
mymethod->inter_buf = NULL;
if (mymethod->cuda_stream)
cudaStreamDestroy (mymethod->cuda_stream);
mymethod->cuda_stream = NULL;
#ifdef WITH_OPENCV
delete mymethod->cvmat;
mymethod->cvmat = NULL;
#endif
mymethod->my_method = 0;
if (mymethod->host_rgb_buf) {
cudaFreeHost (mymethod->host_rgb_buf);
mymethod->host_rgb_buf = NULL;
}
GST_DEBUG_OBJECT (mymethod, "deleted CV Mat \n");
/* Deinit the algorithm library */
//MyMethodCtxDeinit (mymethod->mymethodlib_ctx);
//mymethod->mymethodlib_ctx = NULL;
GST_DEBUG_OBJECT (mymethod, "ctx lib released \n");
return TRUE;
}
/**
* Called when source / sink pad capabilities have been negotiated.
获取协商好的caps参数,并进行相应的处理。如获取输入图像的宽高等信息*/
static gboolean
gst_mymethod_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
GstCaps * outcaps)
{
GstMyMethod *mymethod = GST_MYMETHOD (btrans);
/* Save the input video information, since this will be required later. */
gst_video_info_from_caps (&mymethod->video_info, incaps);
//判断接收数据是否符合要求(这里是需要符合opencv的接收数据)
/* requires RGBA format for blurring the objects in opencv */
if (mymethod->video_info.finfo->format != GST_VIDEO_FORMAT_RGBA) {
GST_ELEMENT_ERROR (mymethod, STREAM, FAILED,
("input format should be RGBA when using blur-objects property"), (NULL));
goto error;
}
return TRUE;
error:
return FALSE;
}
- 第五部分是 算法调用函数的实现,并将检测后的结果叠加在GstBuffer里面传递给下游插件;
/**
* Called when element recieves an input buffer from upstream element.
直通模式下,获取上游数据并进行算法处理后,将检测后的结果叠加在GstBuffer里面传递给下游。*/
static GstFlowReturn
gst_mymethod_transform_ip (GstBaseTransform * btrans, GstBuffer * inbuf)
{
GstMyMethod *mymethod = GST_MYMETHOD (btrans);
GstMapInfo in_map_info;
GstFlowReturn flow_ret = GST_FLOW_ERROR;
gdouble scale_ratio = 1.0;
MyMethodOutput *output;
//MyMut_info mut_info;
NvBufSurface *surface = NULL;
NvDsBatchMeta *batch_meta = NULL;
NvDsFrameMeta *frame_meta = NULL;
NvDsMetaList * l_frame = NULL;
guint i = 0;
mymethod->frame_num++;
CHECK_CUDA_STATUS (cudaSetDevice (mymethod->gpu_id),
"Unable to set cuda device");
memset (&in_map_info, 0, sizeof (in_map_info));
if (!gst_buffer_map (inbuf, &in_map_info, GST_MAP_READ)) {
g_print ("Error: Failed to map gst buffer\n");
goto error;
}
nvds_set_input_system_timestamp (inbuf, GST_ELEMENT_NAME (mymethod));
surface = (NvBufSurface *) in_map_info.data;
GST_DEBUG_OBJECT (mymethod,
"Processing Frame %" G_GUINT64_FORMAT " Surface %p\n",
mymethod->frame_num, surface);
if (CHECK_NVDS_MEMORY_AND_GPUID (mymethod, surface))
goto error;
//从inbuf里面获取nvidia的streamMeta数据结构
batch_meta = gst_buffer_get_nvds_batch_meta (inbuf);
if (batch_meta == nullptr) {
GST_ELEMENT_ERROR (mymethod, STREAM, FAILED,
("NvDsBatchMeta not found for input buffer."), (NULL));
return GST_FLOW_ERROR;
}
/* Using object crops as input to the algorithm. The objects are detected by
* the primary detector 使用裁剪的对象作为算法的输入。对象由主探测器探测*/
if(!mymethod->is_integrated)
{
if (!(surface->memType == NVBUF_MEM_CUDA_UNIFIED || surface->memType == NVBUF_MEM_CUDA_PINNED))
{
GST_ELEMENT_ERROR (mymethod, STREAM, FAILED,
("%s:need NVBUF_MEM_CUDA_UNIFIED or NVBUF_MEM_CUDA_PINNED memory for opencv",__func__), (NULL));
return GST_FLOW_ERROR;
}
}
//std::thread change_id_thread(change_id);
//change_id_thread.detach();
for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next)
{
//1.获取每一帧图片
frame_meta = (NvDsFrameMeta *) (l_frame->data);
//auto start_=std::chrono::steady_clock::now();
cv::Mat in_mat;
//2. 图片转为opencv类型
/* 2.1 Map the buffer so that it can be accessed by CPU 映射缓冲区,以便CPU可以访问它 */
if (surface->surfaceList[frame_meta->batch_id].mappedAddr.addr[0] == NULL){
if (NvBufSurfaceMap (surface, frame_meta->batch_id, 0, NVBUF_MAP_READ_WRITE) != 0){
GST_ELEMENT_ERROR (mymethod, STREAM, FAILED,
("%s:buffer map to be accessed by CPU failed", __func__), (NULL));
return GST_FLOW_ERROR;
}
}
/*2.2 Cache the mapped data for CPU access 缓存映射数据以供CPU访问*/
if(mymethod->inter_buf->memType == NVBUF_MEM_SURFACE_ARRAY)
NvBufSurfaceSyncForCpu (surface, frame_meta->batch_id, 0);
/*2.3 给in_mat赋值,修改in_mat内容,总数据流中的数据也会被修改*/
in_mat =
cv::Mat (surface->surfaceList[frame_meta->batch_id].planeParams.height[0],
surface->surfaceList[frame_meta->batch_id].planeParams.width[0], CV_8UC4,
surface->surfaceList[frame_meta->batch_id].mappedAddr.addr[0],
surface->surfaceList[frame_meta->batch_id].planeParams.pitch[0]);
cv::Mat input_mat;
cv::cvtColor (in_mat, input_mat, cv::COLOR_RGBA2BGR);
//printf("in_mat:(%d, %d, %d) =================\n", in_mat.cols, in_mat.rows, in_mat.channels());
// 调用目标检测算法
auto start_=std::chrono::steady_clock::now();
std::vector<SimpleYolo::Box> vec_obj;
SimpleYolo::BoxArray boxes;
boxes = mymethod->engine_v5s->commit(input_mat).get();
// 将检测结果添加到帧数据并传递给下游插件
attach_metadata_det(mymethod, frame_meta, scale_ratio, boxes);
auto end_=std::chrono::steady_clock::now();
std::chrono::duration<double, std::milli> elapsed = end_ - start_;
std::cout << "time_detect:" << elapsed.count() << std::endl;
/* Cache the mapped data for device access 缓存映射数据以供设备访问*/
if(mymethod->inter_buf->memType == NVBUF_MEM_SURFACE_ARRAY)
NvBufSurfaceSyncForDevice (surface, frame_meta->batch_id, 0);
//如果需要保存图片,则可以在这里进行
#ifdef WITH_OPENCV
#ifdef MYMETHOD_DEBUG
/* Use openCV to remove padding and convert RGBA to BGR. Can be skipped if
* algorithm can handle padded RGBA data. */
#if (CV_MAJOR_VERSION >= 4)
cv::cvtColor (in_mat, *mymethod->cvmat, cv::COLOR_RGBA2BGR);
#else
cv::cvtColor (in_mat, *mymethod->cvmat, CV_RGBA2BGR);
#endif
/* used to dump the converted mat to files for debug */
static guint cnt = 0;
cv::imwrite("out_" + std::to_string (cnt) + ".jpeg", *mymethod->cvmat);
cnt++;
#endif
#endif
}
flow_ret = GST_FLOW_OK;
error:
nvds_set_output_system_timestamp (inbuf, GST_ELEMENT_NAME (mymethod));
gst_buffer_unmap (inbuf, &in_map_info);
return flow_ret;
}
- 第六部分是 attach_metadata_det函数的具体实现 ,这个函数中可以设置目标框的线条、字体等信息;
/**
* 1. 将检测结果画到输出图上
*/
static void attach_metadata_det(GstMyMethod * dsexample, NvDsFrameMeta *frame_meta,
gdouble scale_ratio, SimpleYolo::BoxArray boxes)
{
gint output_num = boxes.size();
if(output_num < 1)
return;
NvDsBatchMeta *batch_meta = frame_meta->base_meta.batch_meta;
NvDsObjectMeta *object_meta = NULL;
NvDsObjectMetaList *object_meta_list = frame_meta->obj_meta_list;
static gchar font_name[] = "Serif";
GST_DEBUG_OBJECT (dsexample, "Attaching metadata %d\n", output_num);
//g_print("Attaching metadata %d\n", output_num);
//清除infer存入的数据
//nvds_clear_obj_meta_list(frame_meta, object_meta_list);
for (auto& obj : boxes) {
object_meta = nvds_acquire_obj_meta_from_pool(batch_meta);
NvOSD_RectParams & rect_params = object_meta->rect_params;
NvOSD_TextParams & text_params = object_meta->text_params;
/* Assign bounding box coordinates */
rect_params.left = obj.left;
rect_params.top = obj.top;
rect_params.width = obj.right - obj.left;
rect_params.height = obj.bottom - obj.top;
/* Semi-transparent yellow background */
rect_params.has_bg_color = 0;
rect_params.bg_color = (NvOSD_ColorParams) {1, 1, 0, 0.4};
/* Red border of width 6 */
rect_params.border_width = 3;
rect_params.border_color = (NvOSD_ColorParams) {1, 0, 0, 1};
/* Scale the bounding boxes proportionally based on how the object/frame was
* scaled during input */
rect_params.left /= scale_ratio;
rect_params.top /= scale_ratio;
rect_params.width /= scale_ratio;
rect_params.height /= scale_ratio;
object_meta->object_id = obj.class_label;
gchar strid[10];
snprintf (strid, 10, "%s", cocolabels[obj.class_label]);
g_strlcpy (object_meta->obj_label, strid, MAX_LABEL_SIZE);
/* display_text required heap allocated memory */
text_params.display_text = g_strdup (strid);
/* Display text above the left top corner of the object */
text_params.x_offset = rect_params.left;
text_params.y_offset = rect_params.top - 10;
/* Set black background for the text */
text_params.set_bg_clr = 1;
text_params.text_bg_clr = (NvOSD_ColorParams) {0, 0, 0, 1};
/* Font face, size and color */
text_params.font_params.font_name = font_name;
text_params.font_params.font_size = 11;
text_params.font_params.font_color = (NvOSD_ColorParams) {1, 1, 1, 1};
//将修改后的osd参数放到添加到帧数据里
nvds_add_obj_meta_to_frame(frame_meta, object_meta, NULL);
frame_meta->bInferDone = TRUE;
}
}
- 最后则是 插件信息设置 ,将mymethod改为自己的名字即可。
/**
* Boiler plate for registering a plugin and an element.
*/
static gboolean
mymethod_plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_mymethod_debug, "mymethod", 0,
"mymethod plugin");
return gst_element_register (plugin, "mymethod", GST_RANK_PRIMARY,
GST_TYPE_MYMETHOD);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
nvdsgst_mymethod,
DESCRIPTION, mymethod_plugin_init, DS_VERSION, LICENSE, BINARY_PACKAGE, URL)
编写cmake或者makefile进行编译
上面是整个插件的代码编写,makefile或者cmake中需要包含opencv、deepstream、cuda等需要用到的路径。具体写法这里就不再详细讲解,网上有相关的教程。
注意!!!!!!
要将编译生成的so文件复制到deepstream文件夹下的lib/gst-plugins/文件夹中去,否则deepstream框架检测不到插件。
复制过去后可以通过以下命令查看是否检测到该插件
gst-inspect-1.0 插件名称
编写deepstream app代码,将其他插件与自定义插件相连接,构成完整的pipeline。
到这里仅仅是完成了自定义插件的编写,要使用它还需要编写deepstream app代码,然后通过deepstream app代码来运行这个pipeline。
插件目录结构如下:
mymethod_lib中放自己检测算法的源码
具体怎么实现pipeline的构建,请听下回分解(工作之余抽空再写~)