UDF Writing Guide#
Overview#
An User Defined Function (UDF) is a chunk of user code that can transform video frames and/or manipulate metadata. For example, a UDF can act as filter, preprocessor, classifier or a detector. These User Defined Functions can be developed in C++ or Python. EVAM provides a GStreamer plugin - udfloader
using which users can configure and load arbitrary UDFs. These UDFs are then called once for each video frame.
Gstreamer udfloader
plugin supports loading and executing of native(C++) and python UDFs. Metadata/inference results generated by the UDFs are attached to GST
buffer as GVAJSONMeta
. A single instance of udfloader
plugin can load and execute a sequence of UDFs, thus enabling chaining multiple UDFs together.
Pipelines can also have multiple udfloader
elements.
How-To Guide for Writing UDF#
This document describes step-wise instruction for writing a User defined Function (UDF) in C++/Python to be used with the gst udfloader
element.
Ability to run arbitrary User Defined Function (UDFs) as a part of the GStreamer pipeline is one of the cardinal feature of EVAM. It enables users to adjoin any pre-processing or post-processing logic to the video analytics pipeline. Currently UDFs can be implemented using following languages.
C++ (It is also called Native UDF)
Python
The order in which the UDFs are defined in pipeline definition file is the order in which data will flow across them. Currently there is no support for demux/mux the data flow to/fro the UDFs.
Steps for Writing Native (C++) UDFs#
Every UDF writing has two major part to it.
Writing actual pre/post processing logic using EIS exposed APIs.
Adding configuration details to the
gst udfloader
element for deploying it
EIS APIs for Writing Native UDFs (C++)#
There are three APIs defined semantically to add the pre/post processing logic. These APIs must be implemented as method of a user defined class inherited from the Udf class named BaseUdf.
Initialization and DeInitialization
class DummyUdf : public BaseUdf { public: DummyUdf(config_t* config) : BaseUdf(config) { //initialization Code can be added here }; ~DummyUdf() { // Any de-initialization logic to be added here }; };
The DummyUdf in the above code snippet is the user defined class and the constructor of the class initialize the UDF’s specific data-structure. The only argument passed to this function is config which depicts configuration details mentioned in the pipeline definition file.
Processing the Actual Data
Following is the API to utilize the ingested input:
UdfRetCode process(cv::Mat& frame, cv::Mat& outputFrame, msg_envelope_t* meta) override { // Logic for processing the frame & returning the inference result. }
This function is a override method which user need to define in its UDF file.
Following are the argument details:
Argument 1(cv::Mat &frame): It represents the input frame for inference.
Argument 2(cv::Mat &outputFrame): It represents the modified frame by the user. This can be used if user need to pass a modified frame forward.
Argument 3(msg_envelope_t* meta): It represents the inference result returned by UDF. The user need to fill the msg_envelope_t structure.
Following are the return code details:
UdfRetCode: Return the appropriate macro as mentioned.
UDF_OK - UDF has processed the frame gracefully.
UDF_DROP_FRAME - The frame passed to process function need to be dropped.
UDF_ERROR - it should be returned for any kind of error in UDF.
Linking Udfloader and Custom UDFs
The initialize_udf() function need to defined as follows to create a link between UdfLoader module and respective UDF. This function ensure UdfLoader to call proper constructor and process() function of respective UDF.
extern "C" { void *initialize_udf(config_t *config) { DummyUdf *udf = new DummyUdf(config); return (void *)udf; } }
The “DummyUdf” is the class name of the user defined custom UDF.
Steps for Writing Python UDFs#
This section describes the process of writing a Python UDF. As discussed in the aforementioned scenario, it also has two aspects to it.
Writing the actual UDF.
Adding configuration details to the
gst udfloader
element for deploying it
Python APIs for writing UDF#
Initialization
In case of Python the initialization callback is also same as the native case. User must create a Udf class and the __init__() function defined in class act as a initialization routine for custom UDF. Following is the dummy constructor code:
class Udf: """Example UDF """ def __init__(self): """Constructor """ # Add the initialization code in this method.
Process Actual Data
The API used to process the actual frame is as follows:
process(self, frame, metadata): # Process the frame in this method # metadata can be used to return inference result
Argument:
frame: Image frame in numpy’s ndarray format
metadata: An empty dictionary. Inference results can be inserted in this data structure.
Return value:
This function returns three values.
1st Value : Represents if the frame Need to be dropped or Not. It is boolean in nature. In case of failure user can return True in this positional return value.
2nd Value : It represents the actual modified frame if at all it has been modified. Hence the type is numpy’s ndarray. If the frame is not modified user can return a None in this place.
3rd Value : Metadata is returned in this place. Hence the type is dict. In general user can return the passed argument as part of this function.