Convert RGBA to BGR in Surface

• Hardware Platform (Jetson / GPU) GPU
• DeepStream Version 5.0
• TensorRT Version 7.1.4
• NVIDIA GPU Driver Version (valid for GPU only) CUDA 10.2
Hi all. I am trying to export frame in surface in my pipeline. Because frame in surface is RGBA so i need to convert it to BGR or RGB. The below code convert the frame to BGR.

I saved bgr_frame into disk.

But it looks unnatural. How can i make it look natural?

The output matrix seems not constructed correctly. The input size and output size are not the same, it is better to use different buffers.
Can you refer to the source codes of gst-dsexample?
The codes are in /opt/nvidia/deepstream/deepstream-5.0/sources/gst-plugins/gst-dsexample/

The output matrix :
dsexample->cvmat =
new cv::Mat (dsexample->processing_height, dsexample->processing_width,
CV_8UC3, dsexample->host_rgb_buf, dsexample->processing_width * RGB_BYTES_PER_PIXEL);

The input matrix:
in_mat =
cv::Mat (dsexample->processing_height, dsexample->processing_width,
CV_8UC4, dsexample->inter_buf->surfaceList[0].mappedAddr.addr[0],
The conversion:
cv::cvtColor (in_mat, *dsexample->cvmat, cv::COLOR_RGBA2BGR);
cv::cvtColor (in_mat, *dsexample->cvmat, CV_RGBA2BGR);

I refer to gst-ds example to build pad_buffer_probe.
this is my function

when i save the frame, i am facing the same problem like above. where is the problem
please help me, thanks so much.

I don’t think the 4 planes jpeg file can be viewed correctly by any picture viewer. Please do the RGBA to RGB conversion first.

static GstPadProbeReturn
test_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
  GstBuffer *buf = (GstBuffer *) info->data;
  GstMapInfo in_map_info;
  memset (&in_map_info, 0, sizeof (in_map_info));

  if (gst_buffer_map (buf, &in_map_info, GST_MAP_READWRITE))
    gint idx = 0;
    NvBufSurface *surface = NULL;
    NvBufSurface surface_idx;
    NvBufSurfTransformRect src_rect, dst_rect;

    surface = (NvBufSurface *);
    surface_idx = *surface;
    surface_idx.surfaceList = &(surface->surfaceList[idx]);
    surface_idx.numFilled = surface->batchSize = 1;

    int batch_size = surface->batchSize;   = 0;
    src_rect.left  = 0;
    src_rect.width = (guint) surface->surfaceList[idx].width;
    src_rect.height= (guint) surface->surfaceList[idx].height;   = 0;
    dst_rect.left  = 0;
    dst_rect.width = (guint) surface->surfaceList[idx].width;
    dst_rect.height= (guint) surface->surfaceList[idx].height;

    NvBufSurfTransformParams nvbufsurface_params;
    nvbufsurface_params.src_rect = &src_rect;
    nvbufsurface_params.dst_rect = &dst_rect;
    nvbufsurface_params.transform_flag =  NVBUFSURF_TRANSFORM_CROP_SRC | NVBUFSURF_TRANSFORM_CROP_DST;
    nvbufsurface_params.transform_filter = NvBufSurfTransformInter_Default;

    NvBufSurfaceCreateParams nvbufsurface_create_params;
    nvbufsurface_create_params.gpuId  = surface->gpuId;
    nvbufsurface_create_params.width  = (guint) surface->surfaceList[idx].width;
    nvbufsurface_create_params.height = (guint) surface->surfaceList[idx].height;
    nvbufsurface_create_params.size = 0;
    nvbufsurface_create_params.colorFormat = NVBUF_COLOR_FORMAT_RGBA;
    nvbufsurface_create_params.layout = NVBUF_LAYOUT_PITCH;

      nvbufsurface_create_params.memType = NVBUF_MEM_DEFAULT;
      nvbufsurface_create_params.memType = NVBUF_MEM_CUDA_UNIFIED;

    if (NvBufSurfaceCreate(&surface, batch_size, &nvbufsurface_create_params) != 0){
      g_print ("NvBufSurfaceCreate failed\n");
      return GST_PAD_PROBE_DROP;
    NvBufSurfaceMemSet (surface, 0, 0, 0);

    if (NvBufSurfTransform (&surface_idx, surface, &nvbufsurface_params) != NvBufSurfTransformError_Success) {
      g_print ("NvBufSurfTransform failed with error while converting buffer\n");
      return GST_PAD_PROBE_DROP;

    NvBufSurfaceMap (surface, 0, 0, NVBUF_MAP_READ);
    NvBufSurfaceSyncForCpu (surface, 0, 0);

    cv::Mat bgr_frame = cv::Mat( cv::Size((guint) surface->surfaceList[idx].width, 
                              (guint) surface->surfaceList[idx].height), CV_8UC3 );

    cv::Mat in_mat = cv::Mat( (guint) surface->surfaceList[idx].height, 
                              (guint) surface->surfaceList[idx].width, 
                              surface->surfaceList[idx].pitch );

    cv::circle(in_mat, cv::Point(100,200), 30, cv::Scalar(255,0,255), 2.0);
    cv::cvtColor (in_mat, bgr_frame, cv::COLOR_RGBA2BGR);
    cv::imwrite("test_1111.jpg", bgr_frame);
    NvBufSurfaceUnMap(surface, 0 , 0);

    NvBufSurfaceSyncForDevice (surface, 0, 0);
  return GST_PAD_PROBE_OK;

Hi, this is my function that i modified. It can solve above problem. However, my target change data in in_mat that can show by sink. when I remove from “NvBufSurface surface_idx” to “return GST_PAD_PROBE_DROP;
}” line it can change in_mat and show change on sink, but otherwise it dont change anything.
How can i do it ?

Since there is format change and buffer size change, you can not replace the video images just with a probe function.

If just convert the video format, nvvideoconvert can do the work.

i see that i can change theo video image by
NvBufSurfaceSyncForDevice (surface, 0, 0);
but it only change on one channel if i set to CV_8UC1,

here is an example that i can change the video frame. i draw a circle.

Thank you for sharing us this sample!

but it mean my problem will not solve? you can see the circle looks unnatural.

The input buffer and output buffer are not the same, they are in different format and with different size, it is better to use different buffers.

If you want to draw something with RGB format. You can use a temporary buffer as the output buffer, after you draw on the output buffer, you need to convert the RGB back to RGBA to the original buffer. There are twice conversion :original buffer => output buffer => original buffer, and twice format change RGBA =>RGB(draw circle)=>RGBA

thanks for your suggestion.