From 0e1d9cca18ff718cfb8c607dcddecc6dcf1caa4a Mon Sep 17 00:00:00 2001 From: jaknapper Date: Tue, 12 May 2026 13:15:23 +0100 Subject: [PATCH] Improvements to properties and docstrings --- .../things/autofocus.py | 22 ++++++++++--------- .../things/camera/__init__.py | 12 ++++++++++ .../things/camera/picamera.py | 5 +++++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/openflexure_microscope_server/things/autofocus.py b/src/openflexure_microscope_server/things/autofocus.py index 2b573997..84d1acad 100644 --- a/src/openflexure_microscope_server/things/autofocus.py +++ b/src/openflexure_microscope_server/things/autofocus.py @@ -265,7 +265,7 @@ class JPEGSharpnessMonitor: stage: BaseStage, camera: BaseCamera, method: SharpnessMethod = SharpnessMethod.JPEG, - record: Optional[SharpnessMethod] = None, + record: Optional[int] = None, ) -> None: """Initialise a new JPEGSharpnessMonitor. The args are injected automatically. @@ -355,7 +355,7 @@ class JPEGSharpnessMonitor: # FocusFoM metric if self.record & SharpnessMethod.FOCUS_FOM: - self._focus_foms.append(self.camera._focus_fom) + self._focus_foms.append(self.camera.focus_fom) if not self.running: break @@ -487,7 +487,7 @@ class AutofocusThing(lt.Thing): dz: int = 2000, start: Literal["centre", "base"] = "centre", sharpness_metric: SharpnessMethod = SharpnessMethod.JPEG, - record: Optional[SharpnessMethod] = None, + record: Optional[int] = None, ) -> SharpnessDataArrays: """Sweep the stage up and down, then move to the sharpest point. @@ -499,10 +499,11 @@ class AutofocusThing(lt.Thing): :param start: Starting position of the sweep. "centre" begins at -dz/2, while "base" begins from the current position. :param sharpness_metric: Sharpness metric used for evaluation. Either - JPEG file size (JPEG) or inbuilt figure of metric (FOCUS_FOM). + JPEG file size (1) or inbuilt figure of metric (2). :param record: Optional bitmask of sharpness metrics to record during - acquisition. If None, defaults to recording only the selected - sharpness_metric. + acquisition. Sharpness metrics can be selected as the sum of their int + value (see sharpness_metric). + If None, defaults to recording only the selected sharpness_metric. :returns: SharpnessDataArrays containing all recorded sharpness and stage position data for the sweep. @@ -561,7 +562,7 @@ class AutofocusThing(lt.Thing): dz: int = 2000, start: Literal["centre", "base"] = "centre", sharpness_metric: SharpnessMethod = SharpnessMethod.JPEG, - record: Optional[SharpnessMethod] = None, + record: Optional[int] = None, ) -> tuple[list[float], list[float]]: """Repeatedly autofocus the stage until it looks focused. @@ -574,10 +575,11 @@ class AutofocusThing(lt.Thing): :param start: Starting position of the sweep. "centre" begins at -dz/2, while "base" begins from the current position. :param sharpness_metric: Sharpness metric used for evaluation. Either - JPEG file size (JPEG) or inbuilt figure of metric (FOCUS_FOM). + JPEG file size (1) or inbuilt figure of metric (2). :param record: Optional bitmask of sharpness metrics to record during - acquisition. If None, defaults to recording only the selected - sharpness_metric. + acquisition. Sharpness metrics can be selected as the sum of their int + value (see sharpness_metric). + If None, defaults to recording only the selected sharpness_metric. :returns: SharpnessDataArrays containing all recorded sharpness and stage position data for the sweep. diff --git a/src/openflexure_microscope_server/things/camera/__init__.py b/src/openflexure_microscope_server/things/camera/__init__.py index b9f0f2a7..5842a2c0 100644 --- a/src/openflexure_microscope_server/things/camera/__init__.py +++ b/src/openflexure_microscope_server/things/camera/__init__.py @@ -179,6 +179,7 @@ class BaseCamera(OFMThing): mjpeg_stream = lt.outputs.MJPEGStreamDescriptor() lores_mjpeg_stream = lt.outputs.MJPEGStreamDescriptor() _memory_buffer = CameraMemoryBuffer() + supports_focus_fom: bool = False def __init__(self, thing_server_interface: lt.ThingServerInterface) -> None: """Initialise the base camera, this creates the background detectors. @@ -213,6 +214,17 @@ class BaseCamera(OFMThing): """Close hardware connection when the Thing context manager is closed.""" pass + @property + def focus_fom(self) -> int: + """Return the focus figure of merit. + + This returns a NotImplementedError if not supported by the camera. + To use, requires self.supports_focus_fom to be set to True. + """ + raise NotImplementedError( + "This camera does not support Figure of Merit focusing." + ) + @lt.property def calibration_required(self) -> bool: """Whether the camera needs calibrating. diff --git a/src/openflexure_microscope_server/things/camera/picamera.py b/src/openflexure_microscope_server/things/camera/picamera.py index c8b2ec29..4267bc14 100644 --- a/src/openflexure_microscope_server/things/camera/picamera.py +++ b/src/openflexure_microscope_server/things/camera/picamera.py @@ -380,6 +380,11 @@ class StreamingPiCamera2(BaseCamera): f"but found {hw_sensor_model}." ) + @property + def focus_fom(self) -> int: + """Return the focus figure of merit.""" + return self._focus_fom + def _on_frame_complete(self, request: Request) -> None: md = request.get_metadata() fom = md.get("FocusFoM")