From bdb307ca18038eb4d39d4a12f7f116673b1e9998 Mon Sep 17 00:00:00 2001 From: jaknapper Date: Thu, 8 May 2025 17:32:04 +0100 Subject: [PATCH 1/2] Remove verify_autofocus from autofocus --- .../things/autofocus.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/openflexure_microscope_server/things/autofocus.py b/src/openflexure_microscope_server/things/autofocus.py index a85b0d77..306a928c 100644 --- a/src/openflexure_microscope_server/things/autofocus.py +++ b/src/openflexure_microscope_server/things/autofocus.py @@ -253,22 +253,6 @@ class AutofocusThing(Thing): stage.move_absolute(z=peak_height) return heights.tolist(), sizes.tolist() - @thing_action - def verify_focus_sharpness( - self, sweep_sizes: list, camera: WrappedCamera, threshold: float = 0.95 - ): - """Take the sharpness curve of the autofocus, and the size of the current frame - to see if the autofocus completed successfully. Returns True if current sharpness - is within "leniency" number of frames from the peak of the autofocus""" - - current_sharpness = camera.grab_jpeg_size(stream_name="lores") - - peak = np.max(sweep_sizes) - base = np.min(sweep_sizes) - cutoff = threshold * (peak - base) - - return current_sharpness >= base + cutoff - @thing_property def stack_images_to_capture(self) -> int: """The number of images to capture and save in a stack From 46865b342d56c26c3a7b998696451706511ce51d Mon Sep 17 00:00:00 2001 From: jaknapper Date: Thu, 8 May 2025 17:32:38 +0100 Subject: [PATCH 2/2] Remove testing focus success, use scan data autofocus dz --- .../things/smart_scan.py | 72 +++---------------- 1 file changed, 10 insertions(+), 62 deletions(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index 2c9f1913..623dda3c 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -43,30 +43,6 @@ BackgroundDep = direct_thing_client_dependency( IMAGE_REGEX = re.compile(r"-?[0-9]+_-?[0-9]+\.jpeg$") -def unpack_autofocus(scan_data): - """Extract z, sharpness data from a move_and_measure call - - Data will start at `start_index`, i.e. `start_index` points are dropped - from the beginning of the array. - """ - jpeg_times = scan_data["jpeg_times"] - jpeg_sizes = scan_data["jpeg_sizes"] - jpeg_sizes_mb = [x / 10**3 for x in jpeg_sizes] - stage_times = scan_data["stage_times"] - stage_positions = scan_data["stage_positions"] - stage_height = [pos[2] for pos in stage_positions] - - jpeg_heights = np.interp(jpeg_times, stage_times, stage_height) - - def turningpoints(lst): - dx = np.diff(lst) - return dx[1:] * dx[:-1] < 0 - - turning = np.where(turningpoints(jpeg_heights))[0] + 1 - - return jpeg_heights[turning[0] : turning[1]], jpeg_sizes_mb[turning[0] : turning[1]] - - class NotEnoughFreeSpaceError(IOError): pass @@ -603,8 +579,16 @@ class SmartScanThing(Thing): focused = False if self._scan_data["autofocus_on"]: - focused, focused_z = self._try_autofocus(new_pos_xyz) - current_pos_xyz = (new_pos_xyz[0], new_pos_xyz[1], focused_z) + self._autofocus.looping_autofocus( + dz=self._scan_data["autofocus_dz"], start="centre" + ) + current_pos_xyz = ( + new_pos_xyz[0], + new_pos_xyz[1], + self._stage.position["z"], + ) + # Without a test for autofocus success, assume it worked + focused = True route_planner.mark_location_visited( current_pos_xyz, imaged=True, focused=focused @@ -628,42 +612,6 @@ class SmartScanThing(Thing): scan_name=self._ongoing_scan_name, ) - @_scan_running - def _try_autofocus( - self, - this_xyz: tuple[int, int, int], - ) -> bool: - """ - Try to perform autofocus and return boolean for if successful - - Args: - this_xyz is the current x,y,z position. - - Return True, focused_height on successful autofocus. - Return False, initial_height if failed after 3 tries - the position will be the initial estimate - """ - attempts = 0 - max_attempts = 3 - dz = self._scan_data["autofocus_dz"] - - while attempts < max_attempts: - attempts += 1 - - _, jpeg_sizes = self._autofocus.looping_autofocus(dz=dz, start="centre") - time.sleep(0.2) - autofocus_sharp_enough = self._autofocus.verify_focus_sharpness( - sweep_sizes=jpeg_sizes, camera=CamDep, threshold=0.92 - ) - - # If sharp enough, break and return success, otherwise go to start and try again - if autofocus_sharp_enough: - return True, self._stage.position["z"] - else: - self._stage.move_absolute(z=this_xyz[2]) - - self._scan_logger.warning("Could not autofocus after 3 attempts.") - return False, self._stage.position["z"] - @_scan_running def _return_to_starting_position(self): self._scan_logger.info("Returning to starting position.")