Blank images after changing camera image size

Hi,
I’m seeing odd behaviour after changing the camera image size. The code is c++ and I’m using cvb 13.01.004 on an aarch64 Linux platform. The camera is a Xenics Bobcat-640. Here’s the process I’m following:

  • Connect to camera using LoadImageFile(vin_file, img_handle)
  • Set up the camera ROI by setting Cust::{Height, Width, OffsetX, OffsetY}, and other camera settings
  • Enable images using G2Grab(img_handle)
  • Process images by calling G2Wait(img_handle) and GetLinearAccess(image_handle,0,pRawImage,NULL, NULL)

That all works fine. At a certain point I need to reduce the size of the ROI and increase the frame rate, so I:

  • Call G2Freeze(img_handle, false)
  • Change the camera settings for Cust::{Height, Width, OffsetX, OffsetY}, and Std::MinimumFrameTime
  • Call IRImageSize(img_handle, IMAGERECT_CMD_RESET, dummyx, dummyy, new_img_handle)
  • Call ReleaseObject(img_handle) and img_handle=new_img_handle
  • Call G2Grab(img_handle) and start processing images again

If my new image size is Smaller than the original, then everything works smoothly. But if I switch back to the larger image size and lower frame rate, then I get no errors but every frame is completely blank (all pixels are zero). Switching back to the smaller size brings it back to working again.

As another data point, if I disconnect from the camera (Call G2Freeze(img_handle,true) and ReleaseObject(img_handle)) and then reconnect from scratch using LoadImageFile etc. and apply the new ROI values at that point then everything works OK, but it takes about 3 seconds to disconnect and reconnect.

I’ve also looked at some of the status values returned from G2GetGrabStatus() and that just shows the number of acquired frames ticking up as expected, one locked buffer, one active grab and no lost frames.

I’m kinda out of ideas as what could be causing the issue. Any suggestions gratefully received.

Thanks,

Paul

Hi @pdb

Would you please try the following code block to see whether the resizing was successful or not:

if (SUCCEEDED(res))
{
  // it worked; in order for drvImage to live up to its name it should
  // now switch roles with the result image...
  ReleaseObject(drvImage);
  drvImage = imgReset;
  imgReset = nullptr; // just to prevent people from using this later on
}

I cannot find a definition for SUCCEEDED, but the documentation says the functions return 0 on success so this is the code I’m currently using right after changing the camera settings - it includes some strictly unnecessary debugging calls but all the return codes are zero and it returns a valid NewImageHandle. And when it reports the image size and offset at the end it shows the expected new size.

	int result;
	IMG NewImageHandle = nullptr;
	cvbdim_t x=0;
	cvbdim_t y=0;
	result = IRImageSize(m_ImageHandle, IMAGERECT_CMD_RESET, x, y, NewImageHandle);
	if (result >= 0) {
		result =  ReleaseObject(m_ImageHandle);
	}
	if (result >= 0 && NewImageHandle != nullptr)
	{
		m_ImageHandle = NewImageHandle;
	} else {
		Logger::Error ("CVB error - Failed to update image size: " + std::to_string(result));
	}
	result = IRImageSize(m_ImageHandle, IMAGERECT_CMD_GET, x, y, NewImageHandle);
	if (result>=0) {
		result = IRImageOffset(m_ImageHandle, IMAGERECT_CMD_GET, x, y);
		result = IRImageOffset(m_ImageHandle, IMAGERECT_CMD_RESET, x, y);
	} else {
		Logger::Error ("CVB error - Failed to update image offsets: " + std::to_string(result));
	}

	IRImageSize(m_ImageHandle, IMAGERECT_CMD_GET, x, y, NewImageHandle);
	Logger::Info ("Image size: " + std::to_string(x) + " x " + std::to_string(y));

	IRImageOffset(m_ImageHandle, IMAGERECT_CMD_GET, x, y);
	Logger::Info ("Image offset: " + std::to_string(x) + " x " + std::to_string(y));

Thanks,
Paul

SUCCEEDED() is a macro that is defined in winerror.h as

#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)

and is a shorthand for checking that a function did return 0 (or a positive value) indicating successful operation. If you do not have it available (e. g. because you work on Linux) you can either define it as indicated above or substitute it for a simple return value check for >= 0.

I do not understand why you set the width and height to zero? Please check whether the new size is acceptable. To ensure proper size values you should call the function with the IMAGERECT_CMD_VALIDATE action before setting the size. In this case the function returns the biggest, closest values for dx and dy. To retrieve the maximum values for dx and dy call the function with the IMAGERECT_CMD_VALIDATE action and pass 0xFFFFFFFF as dx and dy into the function. Notice that some drivers may not accept new size settings while a grab is active.

Hi MandanaS,
Thanks for the replies. According to the documentation here: https://help.commonvisionblox.com/API/C/DriverDLL/group___i_image_rect.html#IMAGERECT_CMD
the IDx and IDy parameters are outputs and the IMAGERECT_CMD_RESET command creates a new image with the standard size of the image acquisition device driver. That’s certainly the behaviour I’m seeing. After the IMAGERECT_CMD_RESET command completes successfully, x and y have been updated to the expected image dimensions I’ve told the camera to use.
That was my understanding of the reset command - to inform CVB that the camera images had changed size. All of this is done with image acquisition stopped by calling G2Freeeze().

I was just following the process described in the GenICam Driver documentation: Image Manager > CVB Technology > Acquisition device drivers and CVB Programming Interfaces > IImageRect Interface (commonvisionblox.com)

So, could I use IRImageSize with IMAGERECT_CMD_SET instead? Will that push the change down to the camera Height & Width settings via the driver?

Thanks,

Paul

Hi @pdb
The difference between set and reset is:
Set → Change the width and height in the camera.
Reset → Get the current width and height of the camera and change the CVB buffer size to these values.
We think you are usding GenICam.vin driver with your cameralink camera, can you confirm that? If you still see this behavior we need to reproduce it and that may take some time.

Hi Mandana,
It’s a GigEVision camera (Xenics Bobcat-640) but, yes, I’m using the GenICam.vin driver.

I’ve just tried changing the code around so that I just use the SET command to change size and offset, rather than changing camera properties and using RESET and I’m seeing similar behaviour (switching to larger ROI produces completely blank images).

Thanks,

Paul

Hi @pdb
I finally got to test your case. I used the following code on Ubuto18.04 with cvb 13.04.004 . I resized the camera image to a smaller size and resize it back to the original. In each step I saved the resulted image. By resizing to a bigger size the image does not get black. Would you please let me know what are your target sizes?

#include <iostream>
#include <iomanip>
#include <vector>
#include <iCVCDriver.h>
#include <iCVCImg.h>
#include <iCVGenApi.h>
#include <CVCError.h>
#include <iCVCUtilities.h>

using namespace std;

static const size_t DRIVERPATHSIZE = 256;

// see implementation below on how to access a camera via GenICam GenApi
static void genicam_access(IMG hCamera);

// entry function
int main(int argc, char* argv[])
{  
  // load the camera
  char driverPath[DRIVERPATHSIZE] = { 0 };
  TranslateFileName("%CVB%/drivers/GenICam.vin", driverPath, DRIVERPATHSIZE);
  IMG hCamera = NULL;
  IMG NewImageHandle1 = nullptr;
  IMG NewImageHandle2 = nullptr;
  
  bool success = LoadImageFile(driverPath, hCamera); 
  if(!success)
  {
    cout << "Error loading " << driverPath << " driver!" << endl;
    return 1;
  }
  cout << "Load " << driverPath << " successful." << endl;
   
  // access camera config
  if(CanNodeMapHandle(hCamera))
  {
    genicam_access(hCamera);
  }
   
  // start grab with ring buffer
  cout << "Acquire 3 images" << endl;
  cvbres_t result = G2Grab(hCamera); 
  if(result >= 0)
  {
    // acquire 10 images
    for(int i = 0; i < 3; ++i)
    {
      // wait for next image to be acquired
      // (returns immediately if unprocessed images are in the ring buffer)
      
      if(i == 0)
      {
        result = G2Wait(hCamera);
        if (result < 0)
          cout << setw(3) << i << " Error with G2Wait: " << CVC_ERROR_FROM_HRES(result) << endl;
        else {
          cout << setw(3) << i << " Acquired image @" << endl;
          cvbbool_t isWritten0 = WriteImageFile(hCamera, "/home/Original.bmp");
        }
          
      }
      else if (i==1)
      {
        //resize to smaller image
        result = G2Freeze(hCamera, false);
        cvbdim_t x1=1280;
	      cvbdim_t y1=1024;
        result = IRImageSize(hCamera, IMAGERECT_CMD_SET, x1, y1, NewImageHandle1);
        if(result >= 0 && NewImageHandle1 != nullptr){
          result =  ReleaseObject(hCamera);
          hCamera = NewImageHandle1;
        }
        
        result = G2Grab(hCamera); 
        result = G2Wait(hCamera);
        if (result < 0)
          cout << setw(3) << i << " Error with G2Wait: " << CVC_ERROR_FROM_HRES(result) << endl;
        else {
          cout << setw(3) << i << " Acquired image @" << endl;
          cvbbool_t isWritten1 = WriteImageFile(hCamera, "/home/imageSmaller.bmp");
        }
      
      }
      else if (i==2)
      {
        //resize to smaller image
        result = G2Freeze(hCamera, false);
        cvbdim_t x2=2560;
	      cvbdim_t y2=2048;
        result = IRImageSize(hCamera, IMAGERECT_CMD_SET, x2, y2, NewImageHandle2);
        if(result >= 0 && NewImageHandle2 != nullptr){
          result =  ReleaseObject(hCamera);
          hCamera = NewImageHandle2;
        }
        
        result = G2Grab(hCamera); 
        result = G2Wait(hCamera);
        if (result < 0)
          cout << setw(3) << i << " Error with G2Wait: " << CVC_ERROR_FROM_HRES(result) << endl;
        else {
          cout << setw(3) << i << " Acquired image @" << endl;
          cvbbool_t isWritten2 = WriteImageFile(hCamera, "/home/imagebigger.bmp");
        }

      }
    } 

    cout << "Stop acquisition" << endl;
    // stop the grab (kill = false: wait for ongoing frame acquisition to stop)
    result = G2Freeze(hCamera, true);
  }
  else
  {
    cout << "Error starting acquisition!" << endl;
  }

  // free camera
  cout << "Free camera" << endl;
  ReleaseObject(hCamera); 

  return 0;
}

// access a feature via CVGenApi
void genicam_access(IMG hCamera)
{
  cout << "Read camera image width: ";

  NODEMAP hNodeMap = NULL;
  cvbres_t result = NMHGetNodeMap(hCamera, hNodeMap);
  if(result >= 0)
  {
    // get width feature node
    NODE hWidth = NULL;
    result = NMGetNode(hNodeMap, "Width", hWidth);
    if(result >= 0)
    {
      // value camera dependent
      cvbint64_t widthValue = 0;
      result = NGetAsInteger(hWidth, widthValue);
      // set values via NSetAsInteger(hWidht, widthValue);
      if(result >= 0)
      {
        cout << widthValue << endl;
      }
      else
      {
        cout << "Read value error: " << CVC_ERROR_FROM_HRES(result) << endl;
      }

      ReleaseObject(hWidth);
    }
    else
    {
      cout << "Get node error: " << CVC_ERROR_FROM_HRES(result) << endl;
    }
    ReleaseObject(hNodeMap);
  }
  else
  {
    cout << "Get nodemap error: " << CVC_ERROR_FROM_HRES(result) << endl;
  }
}

1 Like

Hi Mandana,
I was switching between 512x512 and 128x128 images. Max image size is 640x512. I’ve managed to work around it by reconnecting to the camera when switching to the larger size. And I’ve had some indication from Xenics that we should expect ROI changes to take some time to apply, so I may have to live with the workaround anyway.
Unless you think there might be a difference between 13.01.004 and 13.04.004, or between x86_64 and aarch64 implementations, or between what WriteImageFile() writes out and what GetLinearAccess() provides, then this seems like it maybe a camera issue rather than CVB.
Thanks for looking into this.

Paul

1 Like

Hi @pdb

It is good to know that you found a workaround for the behavior!
Using the code that I have already posted above, I tested the scenario on an aarch64 architecture with CVB 13.04.006 and still do not see the effect you reported. The only tip I can give you now is that make sure before linearAccess functionality you refresh the hcamera:

result =  ReleaseObject(hCamera);
hCamera = NewImageHandle;
GetLinearAccess(hCamera, 0, &pBase, &xInc, &yInc);

Kind regards,
Mandana