NvBufSurfTransform Crop Usage

Hello everyone, I am trying to apply cropping using NvBufSurfTransform on my Jetson Orin NX device with JetPack 6.2.

At the very beginning, when creating the destination surface, I am using src_rect parameters because if I try to use the values from takePhotoCropW and takePhotoCropH, I get a grey image at crop time and the cropped image does not appear. (I need help with this part.)

Then I configure the transform parameters, perform the transformation, and process the image using processV4L2Fd.

Here’s where my question and issue arise:

If I use the code like below, cropping is performed successfully, but the resulting photo has incorrect width and height values (it comes out as if it’s not cropped, with the original width and height):

Code:



             else if(takePhotoCropEnable)
             {
               NvBufSurfaceCreateParams params = {};
               params.width = src_rect.width;
               params.height = src_rect.height;
               params.memType = NVBUF_MEM_SURFACE_ARRAY;
               params.layout = NVBUF_LAYOUT_PITCH;
               params.colorFormat = NVBUF_COLOR_FORMAT_YUV420;
 
               // Create buffer
               int ret = NvBufSurfaceCreate(&dstSurface, 1, &params);
               if (ret != 0 || !dstSurface) {
                 std::cerr << "Failed to create destination surface: Error " << ret << std::endl;
                 return -1;
               }

               // Yerel bir src_rect nesnesi oluşturuyoruz
               NvBufSurfTransformRect src_rect_instance = {};
               src_rect_instance.width  = takePhotoCropW;
               src_rect_instance.height = takePhotoCropH;
               src_rect_instance.left   = takePhotoCropX;
               src_rect_instance.top    = takePhotoCropY;
 
               // Configure transform parameters
               transform_params.src_rect = &src_rect_instance;
              //  transform_params.src_rect.width = takePhotoCropW;
              //  transform_params.src_rect.height = takePhotoCropH;
              //  transform_params.src_rect.left = takePhotoCropX;
              //  transform_params.src_rect.top = takePhotoCropY;
               transform_params.transform_flag = NVBUFSURF_TRANSFORM_CROP_SRC;
               transform_params.transform_flip = NvBufSurfTransform_None;
               transform_params.transform_filter = NvBufSurfTransformInter_Algo3;
 
               // Perform transformation
               if (NvBufSurfaceFromFd(src->frameInfo->fd, (void**)&srcSurface) != 0) {
                 std::cerr << "NvBufSurfaceFromFd failed!" << std::endl;
               }
 
               NvBufSurfTransform_Error transform_error = NvBufSurfTransform(srcSurface, dstSurface, &transform_params);
               if (transform_error != NvBufSurfTransformError_Success) {
                   std::cerr << "NvBufSurfTransform failed: " << transform_error << std::endl;
                   NvBufSurfaceDestroy(dstSurface);
                   return -1;
               }
               
               // Process the buffer
               if (!processV4L2Fd(dstSurface->surfaceList[0].bufferDesc, selectedJpegQuality, takePhotoCropW, takePhotoCropH, imageWritePath)) {
                   std::cerr << "Failed to process buffer" << std::endl;
               }
 
               // Cleanup
               NvBufSurfaceDestroy(dstSurface);
             }                                            
             else
             {
               processV4L2Fd(src->frameInfo->fd, selectedJpegQuality, src->width, src->height, imageWritePath);
             }

Original Photo:

Cropped Photo (wrong width height):

If I use the code below, the cropping works, and the width/height are correct, but the remaining area shows up as green.

    else if(takePhotoCropEnable)
             {
               NvBufSurfaceCreateParams params = {};
               params.width = src_rect.width;
               params.height = src_rect.height;
               params.memType = NVBUF_MEM_SURFACE_ARRAY;
               params.layout = NVBUF_LAYOUT_PITCH;
               params.colorFormat = NVBUF_COLOR_FORMAT_YUV420;
 
               // Create buffer
               int ret = NvBufSurfaceCreate(&dstSurface, 1, &params);
               if (ret != 0 || !dstSurface) {
                 std::cerr << "Failed to create destination surface: Error " << ret << std::endl;
                 return -1;
               }

               NvBufSurfTransformRect src_rect_instance = {};
               src_rect_instance.width  = takePhotoCropW;
               src_rect_instance.height = takePhotoCropH;
               src_rect_instance.left   = takePhotoCropX;
               src_rect_instance.top    = takePhotoCropY;

               NvBufSurfTransformRect dst_rect_instance = {};
               dst_rect_instance.width  = takePhotoCropW;
               dst_rect_instance.height = takePhotoCropH;
               dst_rect_instance.left   = 0;
               dst_rect_instance.top    = 0;
 
               transform_params.src_rect = &src_rect_instance;
               transform_params.dst_rect = &dst_rect_instance;
               transform_params.transform_flag = NVBUFSURF_TRANSFORM_CROP_SRC | NVBUFSURF_TRANSFORM_CROP_DST;
               transform_params.transform_flip = NvBufSurfTransform_None;
               transform_params.transform_filter = NvBufSurfTransformInter_Algo3;
 
               if (NvBufSurfaceFromFd(src->frameInfo->fd, (void**)&srcSurface) != 0) {
                 std::cerr << "NvBufSurfaceFromFd failed!" << std::endl;
               }
 
               NvBufSurfTransform_Error transform_error = NvBufSurfTransform(srcSurface, dstSurface, &transform_params);
               if (transform_error != NvBufSurfTransformError_Success) {
                   std::cerr << "NvBufSurfTransform failed: " << transform_error << std::endl;
                   NvBufSurfaceDestroy(dstSurface);
                   return -1;
               }
               
               if (!processV4L2Fd(dstSurface->surfaceList[0].bufferDesc, selectedJpegQuality, dstSurface->surfaceList[0].width, dstSurface->surfaceList[0].height, imageWritePath)) {
                   std::cerr << "Failed to process buffer" << std::endl;
               }
 
               NvBufSurfaceDestroy(dstSurface);
             }                                            
             else
             {
               processV4L2Fd(src->frameInfo->fd, selectedJpegQuality, src->width, src->height, imageWritePath);
             }

Cropped Photo:

Here is the content of the processV4L2Fd function:

bool ArgusAPI::processV4L2Fd(int32_t fd, std::string &filePath)
{

  std::ofstream *outputFile = new std::ofstream(filePath.c_str());
  if (outputFile)
  {
    unsigned long size = m_OutputBufferSize;

#ifdef JETPACK_6_2
    unsigned char *buffer = m_OutputBuffer.get();
    m_JpegEncoder->encodeFromFd(fd, JCS_YCbCr, &buffer, size,
                                m_configuration->takePhoto.jpegQuality);
    outputFile->write((char *)buffer, size);
#endif
#if defined(JETPACK_4_6) || defined(JETPACK_4_4_1)
    unsigned char *buffer = m_OutputBuffer;
    m_JpegEncoder->encodeFromFd(fd, JCS_YCbCr, &buffer, size,
                                m_configuration->takePhoto.jpegQuality);
    outputFile->write((char *)buffer, size);
#endif

    delete outputFile;
    APP_LOG(Ekin::Logger::LogLevel::INFO_LOG, "Jpg writed as :" + filePath);
  }

  return true;
}

My goal here is to perform a successful crop using the width, height, x, and y values.
Thank you in advance for your response.

Hi,

For the camera basic functionality first needs to check the device and driver configuration.
You can reference to below program guide for the detailed information of device tree and driver implementation.

Please refer to Applications Using V4L2 IOCTL Directly by using V4L2 IOCTL to verify basic camera functionality.

Once confirm the configure and still failed below link help to get log and some information and some tips for debug.

Thanks!

Hi,

Thanks for your reply, but I believe my issue is unrelated to the camera driver or basic camera functionality. The camera works fine, and I can successfully capture images using other features.

The issue specifically occurs when using NvBufSurfTransform for cropping. I have shared both my detailed description and code in the initial message. Could you kindly review the provided code and explanation again? I believe the problem lies within the NvBufSurfTransform crop configuration, not the camera bring-up or driver layer.

I would highly appreciate your input on the transform parameters or any potential misuse in the cropping logic.

Thank you!

hello gercekerarda,

could you please try using gst pipeline to specify crop region (i.e. left, right, top, bottom)
for instance,
$ gst-launch-1.0 nvarguscamerasrc sensor-id=0 ! 'video/x-raw(memory:NVMM),width=2592, height=1944, framerate=30/1, format=NV12' ! nvvidconv left=300 right=2200 top=100 bottom=1800 ! xvimagesink

Hi, thanks for the previous reply.

even though the pipeline you posted didn’t work directly, it worked fine after I made a few adjustments to fit my camera. Since I am connected via ssh, I changed the ‘ximagesink’ and ran the pipeline below with a few edits according to my camera.

The image I get with this pipeline is exactly what I want in terms of width height and crop:

gst-launch-1.0 nvarguscamerasrc sensor-mode=9 num-buffers=1 \
! 'video/x-raw(memory:NVMM), width=3840, height=2160, format=NV12, framerate=10/1' \
! nvvidconv left=300 right=2200 top=100 bottom=1800 \
! nvjpegenc \
! filesink location=crop_test.jpg

hello gercekerarda,

BTW,
I’ve went through your code.
since you’re creating a surface with source resolution.

that’s the top-left cropped region to (1) filling, or (2) original size to that allocated surface.
please allocate another new surface for saving your crop images.
for instance,
here’s pseudo code for your reference.

    params.width = crop.width;
    params.height = crop.height;
    params.memtag = NvBufSurfaceTag_VIDEO_CONVERT;
    ret = NvBufSurf::NvAllocate(&params, 1, &dst_dma_fd);

you may see-also Topic 269071 for reference.

Thanks again for your reply Jerry,

In the part of the code you mentioned, when I create the surface using the crop parameters, I don’t get the cropped image — instead, I get a plain gray image.

hello gercekerarda,

you should refer to MMAPI, such as, 05_jpeg_encode (JPEG encode).
please run sudo apt install nvidia-l4t-jetson-multimedia-api to install the MMAPI package.

here’s sample to load 1920x1080 sources image i.e. YUV420.yuv (3.0 MB),
and, it’s adding crop options for the top-left 320x240 to save it as test.jpg file.
for instance,
$ ./jpeg_encode /home/nvidia/YUV420.yuv 1920 1080 test.jpg -crop 0 0 320 240