AravisProject / aravis

A vision library for genicam based cameras
GNU Lesser General Public License v2.1
867 stars 325 forks source link

Crash with DFK 33UX273 Camera at 200 FPS and 1000 Exposure, Works Fine with Basler QCAM-UC1440-220CE #913

Open bulverismo opened 1 month ago

bulverismo commented 1 month ago

Hi!

Describe the bug When I use a camera The Imaging Source DFK 33UX273 with 200 fps and exposure 1000 my application crashes, but when I use a Basler QCAM-UC1440-220CE camera it doesn't crash

To Reproduce I set the camera to 1000 us of exposure and set the fps to 200

Expected behavior I hope to get the images for later processing with any camera

Camera description: CAMERA THAT OCCURS THE BUG

CAMERA THAT DOESN'T OCCUR THE BUG

Platform description:

Additional context

capture loop ` def __non_triggered_capture(self): with self.lock_var_to_update: for i in range(1, 10): buffer = self._stream.timeout_pop_buffer(10000 * 1000) if buffer: if not (buffer.get_status().value_name == 'ARV_BUFFER_STATUS_SUCCESS'): continue logging.info(self._stream.get_statistics()) img = self._converter(buffer) timestamp = buffer.get_timestamp() self._stream.push_buffer(buffer) self.frame_id = (self.frame_id + 1) % (sys.maxsize - 1) if img is None: raise CameraError("Camera capture failed") return img, self.frame_id, timestamp / 1000000 logging.warning(f"Failed capturing from camera {self.camera_id}, model {self.dev}. Retrying {10 - i} times") raise CameraError("Camera capture failed") `

Log the imaging source Logs basler

EmmanuelP commented 1 month ago
                  img = self._converter(buffer)
                  if img is None:
                      raise CameraError("Camera capture failed")

Capture failed is raised when the call to converter() return a None. I don't know what converter() is supposed to do.

bulverismo commented 4 weeks ago

It is a function to convert img in raw format to numpy array

def _converter(self, buf):
        if not buf:
            return None
        pixel_format = buf.get_image_pixel_format()
        bits_per_pixel = pixel_format >> 16 & 0xff
        # Determine the appropriate ctypes data type based on bits per pixel
        if bits_per_pixel == 8:
            INTP = ctypes.POINTER(ctypes.c_uint8)
            bytes_per_pixel = 1
        elif bits_per_pixel == 16:
            INTP = ctypes.POINTER(ctypes.c_uint16)
            bytes_per_pixel = 2
        elif bits_per_pixel == 24:
            INTP = ctypes.POINTER(ctypes.c_uint8)
            bytes_per_pixel = 3
        elif bits_per_pixel == 32:
            INTP = ctypes.POINTER(ctypes.c_uint8)
            bytes_per_pixel = 4
        else:
            raise ValueError(f"Unsupported bits per pixel: {bits_per_pixel}")

        addr = buf.get_data()
        ptr = ctypes.cast(addr, INTP)

        height = buf.get_image_height()
        width = buf.get_image_width()

        # Determine the number of channels based on bytes per pixel
        if bytes_per_pixel == 1:
            im = np.ctypeslib.as_array(ptr, (height, width))
        else:
            im = np.ctypeslib.as_array(ptr, (height, width, bytes_per_pixel))

        # Copy the image to ensure it is not a view into the original buffer
        im = im.copy()

        return im
EmmanuelP commented 4 weeks ago

Thanks.

At some point in the log, the payload size returned in the image trailer packet changes from 4665600 to 187396. Do you change some features between 2 call to __non_triggered_capture ?

bulverismo commented 4 weeks ago

Yes, after initializing the camera the application starts capturing(using __non_triggered_capture) and then during the capture the application configure some features, such as exposure, gain and gamma, for example.

EmmanuelP commented 4 weeks ago

exposure, gain and gamma

Is this an exhaustive list ? Any feature that may modify the payload size ?

bulverismo commented 4 weeks ago

Full list: frame rate region exposure time exposure auto gain gamma balance ration balance white auto trigger activation trigger delay trigger

I think the only part that can change the payload size is the part where I configure the image size(which I called region in the list above).

set window size method

        # makes the x's and y's a multiple of 4
        x0 -= x0 % 4
        x1 -= x1 % 4
        y0 -= y0 % 4
        y1 -= y1 % 4

        self.cam.stop_acquisition()
        self.cam.set_region(
            x0,  # offset x
            y0,  # offset y
            max(x1 - x0, 16),  # 16*8 pixel minimal image
            max(y1 - y0, 8),
        )

        self.create_buffer()
        self.cam.start_acquisition()

create buffer method

def create_buffer(self):

    if hasattr(self, '_stream'):
        del self._stream

    self._stream = self.cam.create_stream(None, None)
    payload = self.cam.get_payload()
    for _ in range(0, 10):
        self._stream.push_buffer(Aravis.Buffer.new_allocate(payload))
EmmanuelP commented 4 weeks ago

You should also stop the acquisition thread when you want to set the region, otherwise there will still be buffer with the wrong size in the queues. Please have a look at tests/arvroitest.c, switch_roi() function.