Crop detections by NvBufSurfTransform function with batched buffers

• Hardware Platform (Jetson / GPU) RTX 3090
• DeepStream Version 6.3
• TensorRT Version 8.5.3
• NVIDIA GPU Driver Version (valid for GPU only) 535.129.03
• Issue Type( questions, new requirements, bugs) questions

I need to crop detections from frames. My pipeline is following:

 streammux -> pgie ->  nvtracker ->  nvvidconv -> nvvidconv_cap -> fake_sink

where nvvidconv and nvvidconv_cap are set for converting NVMM to RGB:

GstElement *nvvidconv = gst_element_factory_make("nvvideoconvert", "nvvideo-converter");
g_object_set (G_OBJECT (nvvidconv), "nvbuf-memory-type", 3, NULL);

GstElement *nvvidconv_cap = gst_element_factory_make("capsfilter", "nvvidconv_cap");

GstCaps *caps = gst_caps_from_string ("video/x-raw(memory:NVMM),format=RGB");
g_object_set (G_OBJECT (nvvidconv_cap), "caps", caps, NULL);

To crop the detection from the frame I use NvBufSurfTransform function. For batchsize = 1, I create new NvBufSurface *dst_buf for every detection and this way I can specify the size of the NvBufSurface *dst_buf as crop detection size:

#include <glib.h>
#include <gst/gst.h>
#include "gstnvdsmeta.h"
#include "nvbufsurface.h"
#include "nvds_obj_encode.h"
#include "nvbufsurftransform.h"

GstPadProbeReturn metadata_probe(GstPad *pad, GstPadProbeInfo *info, gpointer u_data)
{
    /* Get buffer. */
    GstBuffer *buf = (GstBuffer *)info->data;
    GstMapInfo inmap = GST_MAP_INFO_INIT;
    if (!gst_buffer_map(buf, &inmap, GST_MAP_READ))
    {
        glog(__FILE__, G_LOG_LEVEL_WARNING, "Input buffer mapinfo failed\n");
        return GST_FLOW_ERROR;
    }
    NvBufSurface *ip_surf = (NvBufSurface *)inmap.data;
    gst_buffer_unmap(buf, &inmap);

    // Set the transform configuration parameters
    NvBufSurfTransformConfigParams transform_config_params;
    NvBufSurfTransform_Error err;
    transform_config_params.compute_mode = NvBufSurfTransformCompute_Default;
    transform_config_params.gpu_id = 0;
    transform_config_params.cuda_stream = 0;
    NvBufSurfTransformSetSessionParams(&transform_config_params);

    NvBufSurface *dst_buf;
    NvBufSurfaceCreateParams create_params;

    NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta(buf);
    NvDsMetaList *l_frame = NULL;
    NvDsObjectMeta *obj_meta = NULL;
    NvDsMetaList *l_obj = NULL;
    gboolean medata_was_sent = FALSE;
    /* Go throught all frames in the batch. */
    for (l_frame = batch_meta->frame_meta_list; l_frame != NULL; l_frame = l_frame->next)
    {
        NvDsFrameMeta *frame_meta = (NvDsFrameMeta *)(l_frame->data);

        if (frame_meta->num_obj_meta > 0)
        {
            guint source_id = frame_meta->source_id;
            /* Go throught all detected objects in the frame. */
            for (l_obj = frame_meta->obj_meta_list; l_obj != NULL; l_obj = l_obj->next)
            {
                obj_meta = (NvDsObjectMeta *)(l_obj->data);

                NvBbox_Coords *bbox_coords = &obj_meta->detector_bbox_info.org_bbox_coords;

                NvBufSurfTransformRect src_rect;
                src_rect.top = bbox_coords->top;
                src_rect.left = bbox_coords->left;
                src_rect.width = bbox_coords->width;
                src_rect.height = bbox_coords->height;

                // Set the destination rectangle (usually same as crop size)
                NvBufSurfTransformRect dst_rect;
                dst_rect.top = 0;
                dst_rect.left = 0;
                dst_rect.width = src_rect.width;
                dst_rect.height = src_rect.height;

                // Transformation parameters
                NvBufSurfTransformParams transform_params;
                transform_params.src_rect = &src_rect;
                transform_params.dst_rect = &dst_rect;
                transform_params.transform_flag = NVBUFSURF_TRANSFORM_CROP_SRC | NVBUFSURF_TRANSFORM_CROP_DST;
                transform_params.transform_filter = NvBufSurfTransformInter_Default;

                /* Create dst buffer. */
                NvBufSurface *dst_buf;
                NvBufSurfaceCreateParams create_params;

                // Set the parameters for the buffer
                create_params.gpuId = 0;
                create_params.width = bbox_coords->width;   // Set the width of the crop region
                create_params.height = bbox_coords->height; // Set the height of the crop region
                create_params.size = 0;
                create_params.layout = NVBUF_LAYOUT_PITCH;          // Choose the appropriate layout
                create_params.colorFormat = NVBUF_COLOR_FORMAT_RGB; // Choose the appropriate color format
                create_params.memType = ip_surf->memType;           // Memory type, e.g., device memory
                // Allocate the buffer
                if (NvBufSurfaceCreate(&dst_buf, 1, &create_params) != 0)
                {
                    // Handle error
                    g_print("Error: NvBufSurfaceCreate has failed.\n");
                }
                NvBufSurfaceMemSet(dst_buf, 0, 0, 0);
                NvBufSurfaceMap(dst_buf, 0, 0, NVBUF_MAP_READ);
                NvBufSurfaceSyncForCpu(dst_buf, 0, 0);

                // Perform the transformation (crop)
                err = NvBufSurfTransform(ip_surf, dst_buf, &transform_params);
                if (err != NvBufSurfTransformError_Success)
                {
                    g_print("NvBufSurfTransform has failed: %d\n", err);
                }

                uint32_t Y_stride = dst_buf->surfaceList[0].planeParams.pitch[0];
                const uint8_t *Y = dst_buf->surfaceList[0].dataPtr;
            }
        }
    }

    return GST_PAD_PROBE_OK;
}

Where const uint8_t *Y is a pointer to the crop image. This metadata_probe function is added to the src pad of nvvidconv_cap. This solution works for me and I can save and display the crop images.

But for the case when batchsize > 1, I am not sure how to work with that. I can not create new NvBufSurface *dst_buf for every detection because src and dst buffers in NvBufSurfTransform function must have the same batchsize (as I know). My main confusions are:

  1. How to set the size of destination NvBufSurface (create_params.width and create_params.height or create_params.size )? In every frame in batch, there can be many detection with different sizes.
  2. How can I differentiate detections between frames in batch for src_rect and dst_rect arrays in NvBufSurfTransformParams? How does NvBufSurfTransform function know, which detection belongs to which frame?
  3. How to get all detection crops from the dst buffer?

You don’t need to implement that yourself. You can refer to our source code: sources\apps\sample_apps\deepstream-image-meta-test\deepstream_image_meta_test.c.
You can attach all the detected object in pgie_src_pad_buffer_probe with frameData.attachUsrMeta parameter and get the buffer in osd_sink_pad_buffer_probe with the NVDS_CROP_IMAGE_META.

Example sources\apps\sample_apps\deepstream-image-meta-test\deepstream_image_meta_test.c encodes cropped detections as jpeg images. I do not want to do that. I just want to crop each detection on GPU and then to have access to them.

There is no update from you for a period, assuming this is not an issue anymore.
Hence we are closing this topic. If need further support, please open a new one.
Thanks

If you just want to get the raw data without the ipg image, you can just set the parameter below:

        objData.saveImg = false;
        objData.attachUsrMeta = true;

Then you can get the raw data from enc_jpeg_image->outBuffer. All of this processing is done using hardware, so there is no need to worry about performance issues.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.