Segmentation fault when using nvdspreprocess

Please provide complete information as applicable to your setup.

• Hardware Platform : dGPU
• DeepStream Version: 7.0
• TensorRT Version: 8.6.1.6
**• NVIDIA GPU Driver Version: 555.58.02 **
• Issue Type: bugs

################################################################################

SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the “License”);

you may not use this file except in compliance with the License.

You may obtain a copy of the License at

Apache License, Version 2.0

Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an “AS IS” BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and

limitations under the License.

################################################################################

The values in the config file are overridden by values set through GObject

properties.

################################################################################

SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the “License”);

you may not use this file except in compliance with the License.

You may obtain a copy of the License at

Apache License, Version 2.0

Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an “AS IS” BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and

limitations under the License.

################################################################################

The values in the config file are overridden by values set through GObject

properties.

################################################################################

SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the “License”);

you may not use this file except in compliance with the License.

You may obtain a copy of the License at

Apache License, Version 2.0

Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an “AS IS” BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and

limitations under the License.

################################################################################

The values in the config file are overridden by values set through GObject

properties.

[property]
enable=1
target-unique-ids=3
operate-on-gie-id=3
# 0=NCHW, 1=NHWC, 2=CUSTOM
network-input-order=0
process-on-frame=0
# if enabled maintain the aspect ratio while scaling
maintain-aspect-ratio=1
# if enabled pad symmetrically with maintain-aspect-ratio enabled
symmetric-padding=1
# processing width/height at which image scaled
processing-width=256
processing-height=128
scaling-buf-pool-size=6
tensor-buf-pool-size=6
# tensor shape based on network-input-order
network-input-shape=8;3;128;256
# 0=RGB, 1=BGR, 2=GRAY

network-color-format=0
# 0=FP32, 1=UINT8, 2=INT8, 3=UINT32, 4=INT32, 5=FP16
tensor-data-type=0
tensor-name=x
# 0=NVBUF_MEM_DEFAULT 1=NVBUF_MEM_CUDA_PINNED 2=NVBUF_MEM_CUDA_DEVICE 3=NVBUF_MEM_CUDA_UNIFIED
scaling-pool-memory-type=0
# 0=NvBufSurfTransformCompute_Default 1=NvBufSurfTransformCompute_GPU 2=NvBufSurfTransformCompute_VIC
scaling-pool-compute-hw=0
# Scaling Interpolation method
# 0=NvBufSurfTransformInter_Nearest 1=NvBufSurfTransformInter_Bilinear 2=NvBufSurfTransformInter_Algo1
# 3=NvBufSurfTransformInter_Algo2 4=NvBufSurfTransformInter_Algo3 5=NvBufSurfTransformInter_Algo4
# 6=NvBufSurfTransformInter_Default
scaling-filter=0
custom-lib-path=/opt/nvidia/deepstream/deepstream/lib/gst-plugins/libcustom2d_preprocess.so
custom-tensor-preparation-function=CustomTensorPreparation

[user-configs]
pixel-normalization-factor=0.003921568
#mean-file=
#offsets=
#CustomTensorPreparation

[group-0]
src-ids=-1
custom-input-transformation-function=CustomTensorPreparation
process-on-all-objects=1
process-on-roi=0
roi-params-src-0=0;540;900;500;960;0;900;500;0;0;540;900;
roi-params-src-1=0;540;900;500;960;0;900;500;0;0;540;900;
roi-params-src-2=0;540;900;500;960;0;900;500;0;0;540;900;
roi-params-src-3=0;540;900;500;960;0;900;500;0;0;540;900;

This is my config for nvdspreprocess testing on my custom model. Some how setting CustomTensorPreparation on group config causing segmentation fault.
I have put some logging into CustomTensorPreparation function but it seems that this function is not called.
Nvidia example of nvdspreprocess works fine.

Let’s analyze this issue first. Can you describe in detail how you operate this?

After modification, it did run into CustomTensorPreparation. But im having a hard time getting the cropped object image out, is there any guide on this?

Or maybe is there any way to get the whole frame and do custom preprocessing base on object meta.

NvDsPreProcessStatus
CustomTensorPreparation(CustomCtx *ctx, NvDsPreProcessBatch *batch, NvDsPreProcessCustomBuf *&buf,
                        CustomTensorParams &tensorParam, NvDsPreProcessAcquirer *acquirer)
{

  NvDsPreProcessStatus status = NVDSPREPROCESS_TENSOR_NOT_READY;
  
  /** acquire a buffer from tensor pool */
  buf = acquirer->acquire();
  //float * pDst = (float*)buf->memory_ptr;
  int units = batch->units.size();
  for(int i = 0; i < units; i++)
  {
    printf("Preprocessing");
    guint64 object_id = batch->units[i].roi_meta.object_meta->object_id;
    GstBuffer *inbuf = (GstBuffer *)batch->inbuf;
    NvDsMetaList *l_frame = NULL;
    NvDsMetaList *l_obj = NULL;
    NvDsMetaList *l_user = NULL;
    //SObjectContex* pSObjectCtx = NULL;


    unsigned char* raw_data = (unsigned char*)batch->units[i].converted_frame_ptr;
    NvBufSurfaceParams *surf_params = batch->units[i].input_surf_params;
    int width = surf_params->width;
    int height = surf_params->height;
    int channels = (surf_params->colorFormat == NVBUF_COLOR_FORMAT_RGBA) ? 4 : 3;
    // Print height
    printf("width: %d\n", width);
    printf("height: %d\n", height);
    printf("channels: %d\n", channels);

    // Print pointer
    printf("Pointer: %p\n", (void*)raw_data);
    NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta(inbuf);



    for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
      l_frame = l_frame->next)
    {
      //printf("Looping l_frame");
      NvDsFrameMeta *frame_meta = (NvDsFrameMeta *)(l_frame->data);
      for (l_obj = frame_meta->obj_meta_list; l_obj != NULL;
        l_obj = l_obj->next)
      {
        //printf("Looping l_obj");
        unsigned char* host_buffer = (unsigned char*)malloc(720 * 1280 * 3);
        NvDsObjectMeta *obj_meta = (NvDsObjectMeta *)l_obj->data;
        printf("Object ID: %ld\n", obj_meta->object_id);
        float left = obj_meta->rect_params.left;
        float top = obj_meta->rect_params.top;
        float width = obj_meta->rect_params.width;
        float height = obj_meta->rect_params.height;
        printf("left: %f\n", left);
        printf("top: %f\n", top);
        printf("width: %f\n", width);
        printf("height: %f\n", height);
        //int channels = 3; // Assuming the image is RGB or BGR, adjust based on format
        printf("Begin processing \n");
        // Wrap raw data into a cv::Mat (if it's RGB/BGR format)
        cudaDeviceSynchronize(); 
        cudaMemcpy(host_buffer, raw_data, 720 * 1280 * 3, cudaMemcpyDeviceToHost);
        cv::Mat frame(720, 1280, CV_8UC3, host_buffer);
        printf("Wraped \n");
        printf("Frame shape: rows = %d, cols = %d, channels = %d\n", 
            frame.rows, frame.cols, frame.channels());
        printf("Inspecting pixel values...\n");
        for (int row = 0; row < 3; row++) { // Print first 3 rows
            for (int col = 0; col < 3; col++) { // Print first 3 columns
                cv::Vec3b pixel = frame.at<cv::Vec3b>(row, col); // Access BGR values
                printf("Pixel[%d, %d] = (B: %d, G: %d, R: %d)\n", 
                      row, col, pixel[0], pixel[1], pixel[2]);
            }
        }
        cv::imwrite("output.jpg", frame);
        printf("Saved image as output.jpg\n");
        if(obj_meta->object_id != object_id)
          continue;
        
        for (l_user = obj_meta->obj_user_meta_list; l_user != NULL;
              l_user = l_user->next){
                //NvDsUserMeta *user_meta = (NvDsUserMeta *)l_user->data;
                printf("Looping l_user");
              }
        }
      }
    }
  status = NVDSPREPROCESS_SUCCESS;
  return status;
}

This is my code trying to acess the frame by copying to host and read with cv2 but no luck. The saved frame is all black. Im new to this so please help me out.

No. You cannot get the frame based on the object meta. You can only get the frame based on the NvBufurface.

If you just want to save the original image or the object image, we recommend that you use our encapsulated interface nvds_obj_enc_process. You can refer to our deepstream-image-meta-test to learn how to use that.

If you want to get the image and custom that, you can refer to our deepstream\sources\gst-plugins\gst-dsexample\gstdsexample_optimized.cpp to learn how to get the image from the NvBufSurface.

Create a new plugin like ds-example is overly complicated for my custom preprocessing, can you suggest the most simple way to get the object image and do preprocessing using opencv. Below is my python preprocessing code

class DetPreProcess:
    def __init__(self, limit_side_len: int = 736, limit_type: str = "min"):
        self.mean = np.array([0.485, 0.456, 0.406])
        self.std = np.array([0.229, 0.224, 0.225])
        self.scale = 1 / 255.0

        self.limit_side_len = limit_side_len
        self.limit_type = limit_type

    def __call__(self, img: np.ndarray) -> Optional[np.ndarray]:
        resized_img = self.resize(img)
        if resized_img is None:
            return None

        img = self.normalize(resized_img)
        img = self.permute(img)
        img = np.expand_dims(img, axis=0).astype(np.float32)
        return img

    def normalize(self, img: np.ndarray) -> np.ndarray:
        return (img.astype("float32") * self.scale - self.mean) / self.std

    def permute(self, img: np.ndarray) -> np.ndarray:
        return img.transpose((2, 0, 1))

    def resize(self, img: np.ndarray) -> Optional[np.ndarray]:
        """resize image to a size multiple of 32 which is required by the network"""
        h, w = img.shape[:2]

        if self.limit_type == "max":
            if max(h, w) > self.limit_side_len:
                if h > w:
                    ratio = float(self.limit_side_len) / h
                else:
                    ratio = float(self.limit_side_len) / w
            else:
                ratio = 1.0
        else:
            if min(h, w) < self.limit_side_len:
                if h < w:
                    ratio = float(self.limit_side_len) / h
                else:
                    ratio = float(self.limit_side_len) / w
            else:
                ratio = 1.0

        resize_h = int(h * ratio)
        resize_w = int(w * ratio)

        resize_h = int(round(resize_h / 32) * 32)
        resize_w = int(round(resize_w / 32) * 32)

        try:
            if int(resize_w) <= 0 or int(resize_h) <= 0:
                return None
            img = cv2.resize(img, (int(resize_w), int(resize_h)))
        except Exception as exc:
            raise ResizeImgError from exc

        return img


class ResizeImgError(Exception):
    pass

It is best if i can get the cropped object from pgie though NvDsPreProcessBatch. If not then i guess i have to use NvBufSurface to get the whole frame then crop manually using object meta left top height width.

Can you elaborate on your scenario in detail? You used the preprocess plugin first, and now you need to use your python preprocess code. There’s some confusion about how you’re using it.

What i mean is im trying to convert my base python preprocessing code to work with nvdspreprocessing in C++ but i am having a hard time getting the cropped object into opencv.

Is the object stored in RoiMeta? Im so confused

OK. I got that. You want ro use the preprocess algorithm in your python code to our C++ code.

The raw image data is in the NvBufSurface. The metadata just stores some meta information. So if you want to get the raw image data, you need to get that from the NvbufSurface. You can just refer our sample code gstdsexample_optimized.cpp to learn how to get that from the NvbufSurface without actually using this plugin.

1 Like

Thanks, i will look at the example now. I printed height and width of batch->units[i].roi_meta.converted_buffer and it is the height and width of my SGIE, so i assume that the plugin already scaled my object. Is there any solution to get my object frame before it get scaled?

You can refer to our source code prepare_tensor in the nvdspreprocess_impl.cpp. This function shows how to dump raw data.

I had a look through the source code, if i want to acesss cropped object frame, it should be through batch->units[i].converted_frame_ptr right?

      if (convertFcn || convertFcnHalf) {
          /* Input needs to be pre-processed. */
          if (tensorParam.params.data_type == NvDsDataType_FP16) {
              convertFcnHalf((half*)outPtr, (unsigned char*)batch->units[i].converted_frame_ptr,
                  m_NetworkSize.width, m_NetworkSize.height, batch->pitch,
                  m_Scale, m_MeanDataBuffer.get() ? m_MeanDataBuffer->ptr<float>() : nullptr,
                  *m_PreProcessStream);
          } else {
              convertFcn((float*)outPtr, (unsigned char*)batch->units[i].converted_frame_ptr,
                  m_NetworkSize.width, m_NetworkSize.height, batch->pitch,
                  m_Scale, m_MeanDataBuffer.get() ? m_MeanDataBuffer->ptr<float>() : nullptr,
                  *m_PreProcessStream);
          }
      }

i have tried getting the object image out into a jpg to verify, but i am having a very weird result.

    NvBufSurfaceParams *surf_params = batch->units[i].roi_meta.converted_buffer;
    void* raw_data = surf_params->mappedAddr.addr;

    NvDsRoiMeta roi_meta = batch->units[i].roi_meta;
    int width = surf_params->width;
    int height = surf_params->height;
    int pitch = surf_params->pitch;
    int channels = (surf_params->colorFormat == NVBUF_COLOR_FORMAT_RGBA) ? 4 : 3;
    float x_scale = (float)roi_meta.scale_ratio_x;
    float y_scale = (float)roi_meta.scale_ratio_y;
    // Print height
    printf("width: %d\n", width);
    printf("height: %d\n", height);
    printf("pitch: %d\n", pitch);
    printf("channels: %d\n", channels);
    printf("x_scale: %f\n", x_scale);
    printf("y_scale: %f\n", y_scale);
    
    //NvBufSurfaceParams *object_buffer = batch->units[i].input_surf_params;
    printf("object width: %d\n", surf_params->width);
    printf("object height: %d\n", surf_params->height);

    void* raw_frame_data = batch->units[i].input_surf_params->mappedAddr.addr;   
    void* raw_object_data = surf_params->mappedAddr.addr; 
    // Print pointer
    printf("Pointer: %p\n", (void*)raw_frame_data);
    printf("Pointer object: %p\n", (void*)raw_object_data);
    NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta(inbuf);
    cv::Mat frame(surf_params->height, surf_params->width,CV_8UC4 ,raw_data, surf_params->pitch);
    cv::Mat image_data(surf_params->height, surf_params->width,CV_8UC4 );
    frame.copyTo(image_data);
    cv::Mat outptut_image(surf_params->height, surf_params->width,CV_8UC3 );
    cv::cvtColor(image_data,outptut_image, cv::COLOR_RGBA2BGR);
    cv::imwrite("output.jpg",outptut_image);
    printf("Converted color");

Let’s narrow it down by excluding the OpenCV code. Could you just use our code to dump the bin file first? We can use ffmpeg or other players to show the image from the bin file directly.

#if DEBUG_LIB
    static int batch_num1 = 0;
    std::ofstream outfile1("impl_in_batch_" + std::to_string(batch_num1) + ".bin");
    for (unsigned int j = 0 ; j < m_NetworkSize.height; j++) {
        outfile1.write((char*) batch->units[i].converted_frame_ptr + j*batch->pitch,
              3*m_NetworkSize.width);
    }
    outfile1.close();
    batch_num1 ++;
#endif

The image i got is all black this is the command i use to convert to png:

ffmpeg -f rawvideo -pixel_format rgb24 -video_size 160x160 -i impl_in_batch_0.bin -frames:v 1 output_image.png

I have looked into ds-example to get frame form NvBufSurfaceParams. But mappedAddr.addr[0] in my case is nil.

void* raw_object_data = batch->units[i].roi_meta.converted_buffer->mappedAddr.addr[0]; 
Pointer: (nil)

This is used for the Jetson env. Since you are using DGPU, you can directly use the converted_buffer->dataPtr pointer. You need to notice that this is the cuda buffer. You should use the cudaMemcpy API to copy the buffer from the GPU buffer to the Host Buffer first. Then you can save that or process that with OpenCV.

1 Like