diff --git a/csaxs_bec/device_configs/flomni_config.yaml b/csaxs_bec/device_configs/flomni_config.yaml index 0d1ddc3..e2a20c5 100644 --- a/csaxs_bec/device_configs/flomni_config.yaml +++ b/csaxs_bec/device_configs/flomni_config.yaml @@ -399,7 +399,9 @@ cam_xeye: deviceConfig: camera_id: 1 bits_per_pixel: 24 - # channels: 3 + num_rotation_90: 3 + transpose: false + force_monochrome: true m_n_colormode: 1 enabled: true onFailure: buffer @@ -412,7 +414,9 @@ cam_ids_rgb: deviceConfig: camera_id: 203 bits_per_pixel: 24 - # channels: 3 + num_rotation_90: 3 + transpose: false + force_monochrome: true m_n_colormode: 1 enabled: true onFailure: buffer diff --git a/csaxs_bec/devices/ids_cameras/base_integration/camera.py b/csaxs_bec/devices/ids_cameras/base_integration/camera.py index d0cb22b..bc73210 100644 --- a/csaxs_bec/devices/ids_cameras/base_integration/camera.py +++ b/csaxs_bec/devices/ids_cameras/base_integration/camera.py @@ -18,6 +18,7 @@ import atexit from typing import Literal import numpy as np +import time from bec_lib.logger import bec_logger from csaxs_bec.devices.ids_cameras.base_integration.utils import check_error @@ -164,10 +165,12 @@ class Camera: m_n_colormode: Literal[0, 1, 2, 3] = 1, bits_per_pixel: int = 24, connect: bool = True, + force_monochrome: bool = False, ): self.ueye = ueye self.camera_id = camera_id self._inputs = {"m_n_colormode": m_n_colormode, "bits_per_pixel": bits_per_pixel} + self.force_monochrome = force_monochrome self._connected = False self.cam = None atexit.register(self.on_disconnect) @@ -197,14 +200,16 @@ class Camera: self.cam = IDSCameraObject(self.camera_id, **self._inputs) self._connected = True - def on_disconnect(self): - """Disconnect from the camera.""" + def on_disconnect(self, delay_after: float = 0.3): + """Disconnect from the camera and optionally wait a short time for driver cleanup.""" try: if self.cam and self.cam.h_cam: check_error(self.ueye.is_ExitCamera(self.cam.h_cam), "IDSCameraObject") self._connected = False self.cam = None - logger.info("Camera disconnected.") + if delay_after > 0: + time.sleep(delay_after) + logger.debug(f"Waited {delay_after:.2f}s after camera disconnect for cleanup.") except Exception as e: logger.info(f"Error during camera disconnection: {e}") @@ -269,6 +274,12 @@ class Camera: # If RGB image (H, W, 3), reshuffle channels from BGR → RGB if img.ndim == 3 and img.shape[2] == 3: img = img[:, :, ::-1] + if self.force_monochrome: + gray = np.dot(img[..., :3], [0.2989, 0.5870, 0.1140]).astype(np.uint8) + # expand to 3D shape (H, W, 1) for consistency with real mono cams + img = np.expand_dims(gray, axis=-1) + img = np.ascontiguousarray(img) + return img diff --git a/csaxs_bec/devices/ids_cameras/ids_camera.py b/csaxs_bec/devices/ids_cameras/ids_camera.py index 5fecb90..0691769 100644 --- a/csaxs_bec/devices/ids_cameras/ids_camera.py +++ b/csaxs_bec/devices/ids_cameras/ids_camera.py @@ -54,6 +54,7 @@ class IDSCamera(PSIDeviceBase): live_mode: bool = False, num_rotation_90: int = 0, transpose: bool = False, + force_monochrome: bool = False, **kwargs, ): """Initialize the IDS Camera. @@ -81,6 +82,7 @@ class IDSCamera(PSIDeviceBase): self._mask = np.zeros((1, 1), dtype=np.uint8) self.image.num_rotation_90 = num_rotation_90 self.image.transpose = transpose + self._force_monochrome = force_monochrome ############## Live Mode Methods ############## @@ -184,6 +186,7 @@ class IDSCamera(PSIDeviceBase): def on_connected(self): """Connect to the camera.""" + self.cam.force_monochrome = self._force_monochrome self.cam.on_connect() self.live_mode = self._inputs.get("live_mode", False) self.set_rect_roi(0, 0, self.cam.cam.width.value, self.cam.cam.height.value)