diff --git a/src/openflexure_microscope_server/things/camera/__init__.py b/src/openflexure_microscope_server/things/camera/__init__.py index 076d2efd..8e095e75 100644 --- a/src/openflexure_microscope_server/things/camera/__init__.py +++ b/src/openflexure_microscope_server/things/camera/__init__.py @@ -8,7 +8,7 @@ See repository root for licensing information. from __future__ import annotations import logging -from typing import Literal, Protocol, runtime_checkable +from typing import Literal, Protocol, runtime_checkable, Optional from labthings_fastapi.thing import Thing from labthings_fastapi.decorators import thing_action, thing_property @@ -45,12 +45,14 @@ class CameraProtocol(Protocol): def capture_array( self, stream_name: Literal["main", "lores", "raw", "full"] = "main", + wait: Optional[float] = 5, ) -> NDArray: ... def capture_jpeg( self, metadata_getter: GetThingStates, resolution: Literal["lores", "main", "full"] = "main", + wait: Optional[float] = 5, ) -> JPEGBlob: """Acquire one image from the camera and return as a JPEG blob""" ... @@ -166,6 +168,7 @@ class CameraStub(BaseCamera): def capture_array( self, stream_name: Literal["main", "lores", "raw", "full"] = "main", + wait: Optional[float] = 5, ) -> NDArray: raise NotImplementedError("Cameras must not inherit from CameraStub") @@ -174,6 +177,7 @@ class CameraStub(BaseCamera): self, metadata_getter: GetThingStates, resolution: Literal["lores", "main", "full"] = "main", + wait: Optional[float] = 5, ) -> JPEGBlob: """Acquire one image from the camera and return as a JPEG blob""" raise NotImplementedError("Cameras must not inherit from CameraStub") diff --git a/src/openflexure_microscope_server/things/capture.py b/src/openflexure_microscope_server/things/capture.py index 904a5bf9..8d3dc413 100644 --- a/src/openflexure_microscope_server/things/capture.py +++ b/src/openflexure_microscope_server/things/capture.py @@ -40,6 +40,7 @@ class CaptureThing(Thing): cam, metadata_getter, stream=stream_name, + logger=logger, ) acquisition_time = time.time() self._save_capture(jpeg_path, image, metadata, logger) @@ -51,23 +52,33 @@ class CaptureThing(Thing): ) @thing_action - def capture_jpeg(self, filename: str, cam: CamDep): + def capture_jpeg(self, filename: str, cam: CamDep, logger: InvocationLogger): """Capture a JPEG (from a JPEGBlob) to disk""" - jpeg = cam.capture_jpeg(resolution="full") - jpeg.save(filename) + for capture_attempts in range(5): + try: + jpeg = cam.capture_jpeg(resolution="full", wait=5) + jpeg.save(filename) + return + except TimeoutError: + logger.warning(f'Attempt {capture_attempts+1} to capture image timed out. Do you have enough RAM?') + except Exception as e: + logger.warning(e) + raise CaptureError("An error occurred while capturing after 5 attempts") @thing_action - def _capture_array(self, cam: CamDep, metadata_getter: GetThingStates, stream: str = 'main'): + def _capture_array(self, cam: CamDep, metadata_getter: GetThingStates, logger: InvocationLogger, stream: str = 'main'): """Capture an image in memory and return it with metadata CaptureError raised if the capture fails for any reason returns tuple with numpy array of image data, and dict of metadata """ - try: - metadata = metadata_getter() - image = cam.capture_array(stream_name=stream)[..., :3] - except Exception as e: - raise CaptureError("An error occurred while capturing") from e - return image, metadata + for capture_attempts in range(5): + try: + metadata = metadata_getter() + image = cam.capture_array(stream_name=stream, wait=5)[..., :3] + return image, metadata + except TimeoutError: + logger.warning(f'Attempt {capture_attempts+1} to capture image timed out. Do you have enough RAM?') + raise CaptureError("An error occurred while capturing after 5 attempts") def _save_capture( self, diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index ea024bca..46799104 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -694,7 +694,7 @@ class SmartScanThing(Thing): self._autofocus.run_z_stack( images_dir=self._ongoing_scan_images_dir, stack_dir=site_folder, - capture_method="hires_array", + capture_method="blob", ) # increment capure counter as thread has completed