From 5d8a1073b25c96a36962c26da1d4aaf9ca9ac496 Mon Sep 17 00:00:00 2001 From: jaknapper Date: Wed, 16 Apr 2025 18:29:33 +0100 Subject: [PATCH 1/5] Remove focus_change_acceptable --- .../things/smart_scan.py | 32 +++---------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index 5d853b69..ee785e43 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -651,36 +651,12 @@ class SmartScanThing(Thing): sweep_sizes=jpeg_sizes, camera=CamDep, threshold=0.92 ) - # Not sharp enough, go to start and try again - if not autofocus_sharp_enough: + # If sharp enough, break and return success, otherwise go to start and try again + if autofocus_sharp_enough: + return True + else: z = this_xyz[2] - dz / 2 if attempts < max_attempts else this_xyz[2] self._stage.move_absolute(z=z) - continue - - # No previous positions to compare against return success - if closest_xyz is None: - return True - - # Check the change in z-position is acceptable - # If the z change compared to the closest focused image exceeds - # a given fraction of the xy displacement this indicates failure - success = focus_change_acceptable( - prev_pos=closest_xyz[:2], - prev_z=closest_xyz[2], - new_pos=this_xyz[:2], - new_z=current_height, - fractional_limit=0.5, - ) - # No focus change acceptable return success - if success: - return True - - # Shifted to far move to start and try again - z = this_xyz[2] - dz / 2 if attempts < max_attempts else this_xyz[2] - self._stage.move_absolute(z=z) - self._scan_logger.info( - "The focus has shifted further than we expect: retrying." - ) self._scan_logger.warning("Could not autofocus after 3 attempts.") return False From 7cb1a3980e54578d6530cb46d23517101e5de2d2 Mon Sep 17 00:00:00 2001 From: jaknapper Date: Thu, 17 Apr 2025 17:20:40 +0100 Subject: [PATCH 2/5] Remove focus_change_acceptable test, autofocus from centre --- .../things/smart_scan.py | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index ee785e43..f0ea5f82 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -66,30 +66,6 @@ def unpack_autofocus(scan_data): return jpeg_heights[turning[0] : turning[1]], jpeg_sizes_mb[turning[0] : turning[1]] -def focus_change_acceptable(prev_pos, prev_z, new_pos, new_z, fractional_limit): - """Return true if the change in z-position is small enough. - - A large change in z-position in an indication of focus failure. - fractional_limit is the largest ratio of change in z to change in xy that's allowed - """ - - prev_xy = np.asarray(prev_pos, dtype="float64") - new_xy = np.asarray(new_pos, dtype="float64") - - dist = np.sqrt(np.sum(((new_xy - prev_xy) / 10**4) ** 2, dtype="float64")) - - focus_change = abs(new_z - prev_z) / 10**4 - if dist == 0: - print(f"Not moved between {prev_pos} and {new_pos}") - movement_ratio = 0 - else: - movement_ratio = np.divide(focus_change, dist, dtype="float64") - - if movement_ratio > fractional_limit: - return False - return True - - class NotEnoughFreeSpaceError(IOError): pass @@ -595,8 +571,7 @@ class SmartScanThing(Thing): focused = False if self._scan_data["autofocus_on"]: - closest_xyz = route_planner.closest_focus_site(new_pos_xyz[:2]) - focused = self._try_autofocus(new_pos_xyz, closest_xyz) + focused = self._try_autofocus(new_pos_xyz) route_planner.mark_location_visited( new_pos_xyz, imaged=True, focused=focused @@ -624,15 +599,12 @@ class SmartScanThing(Thing): def _try_autofocus( self, this_xyz: tuple[int, int, int], - closest_xyz: Optional[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. - closest_xyz is the (x, y, z) coordinates of the closest position, this is None - if no previous images have been taken or in focus Return True on successful autofocus. Return False if failed after 3 tries - the position will be the initial estimate @@ -644,8 +616,7 @@ class SmartScanThing(Thing): while attempts < max_attempts: attempts += 1 - _, jpeg_sizes = self._autofocus.looping_autofocus(dz=dz, start="base") - current_height = self._stage.position["z"] + _, 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 From 3bcc71304e793bae38237f85da92e58dc7639e1b Mon Sep 17 00:00:00 2001 From: jaknapper Date: Tue, 22 Apr 2025 14:33:27 +0100 Subject: [PATCH 3/5] Fixed focused path tracking, move to estimated focus before autofocus --- .../things/smart_scan.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index f0ea5f82..7d6124ab 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -363,7 +363,7 @@ class SmartScanThing(Thing): self._stage.move_absolute( x=next_point[0], y=next_point[1], - z=z_estimate - self._scan_data["autofocus_dz"] / 2, + z=z_estimate, ) return (next_point[0], next_point[1], z_estimate) @@ -553,6 +553,11 @@ class SmartScanThing(Thing): next_pos_xy, z_est = route_planner.get_next_location_and_z_estimate() new_pos_xyz = self._move_to_next_point(next_pos_xy, z_est) + current_pos_xyz = ( + new_pos_xyz[0], + new_pos_xyz[1], + self._stage.position["z"], + ) capture_image = True # If skipping background, take an image to check if it is background @@ -571,10 +576,11 @@ class SmartScanThing(Thing): focused = False if self._scan_data["autofocus_on"]: - focused = self._try_autofocus(new_pos_xyz) + focused, focused_z = self._try_autofocus(new_pos_xyz) + current_pos_xyz = (new_pos_xyz[0], new_pos_xyz[1], focused_z) route_planner.mark_location_visited( - new_pos_xyz, imaged=True, focused=focused + current_pos_xyz, imaged=True, focused=focused ) # wait for the previous capture to be saved, i.e. don't leave more than one image saving in the background @@ -606,8 +612,8 @@ class SmartScanThing(Thing): Args: this_xyz is the current x,y,z position. - Return True on successful autofocus. - Return False if failed after 3 tries - the position will be the initial estimate + 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 @@ -624,13 +630,13 @@ class SmartScanThing(Thing): # If sharp enough, break and return success, otherwise go to start and try again if autofocus_sharp_enough: - return True + return True, self._stage.position["z"] else: z = this_xyz[2] - dz / 2 if attempts < max_attempts else this_xyz[2] self._stage.move_absolute(z=z) self._scan_logger.warning("Could not autofocus after 3 attempts.") - return False + return False, self._stage.position["z"] @_scan_running def _wait_for_capture_thread(self) -> None: From 283a10c560429dd631f8343a89f9dbbc7b27a94b Mon Sep 17 00:00:00 2001 From: Joe Knapper Date: Wed, 23 Apr 2025 09:15:54 +0000 Subject: [PATCH 4/5] Always move back to expected focus after failed autofocus --- src/openflexure_microscope_server/things/smart_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index 7d6124ab..8d96927a 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -632,7 +632,7 @@ class SmartScanThing(Thing): if autofocus_sharp_enough: return True, self._stage.position["z"] else: - z = this_xyz[2] - dz / 2 if attempts < max_attempts else this_xyz[2] + z = this_xyz[2] self._stage.move_absolute(z=z) self._scan_logger.warning("Could not autofocus after 3 attempts.") From c4dc357cf35daacc78dadc737675e1324dac9575 Mon Sep 17 00:00:00 2001 From: Joe Knapper Date: Wed, 23 Apr 2025 10:57:56 +0000 Subject: [PATCH 5/5] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Julian Stirling --- src/openflexure_microscope_server/things/smart_scan.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index 8d96927a..f47cbaaf 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -632,8 +632,7 @@ class SmartScanThing(Thing): if autofocus_sharp_enough: return True, self._stage.position["z"] else: - z = this_xyz[2] - self._stage.move_absolute(z=z) + self._stage.move_absolute(z=this_xyz[2]) self._scan_logger.warning("Could not autofocus after 3 attempts.") return False, self._stage.position["z"]