Skip to content

Commit 3ae88c6

Browse files
committed
FEAT: integrate line scan predictor into analytical focusing
1 parent 8b55662 commit 3ae88c6

1 file changed

Lines changed: 50 additions & 8 deletions

File tree

src/eaa/task_manager/tuning/analytical_focusing.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from sciagent.message_proc import print_message
1919

2020
from eaa.tool.imaging.acquisition import AcquireImage
21+
from eaa.tool.imaging.line_scan_predictor import LineScanPredictor
2122
from eaa.tool.imaging.param_tuning import SetParameters
2223
from eaa.task_manager.tuning.base import BaseParameterTuningTaskManager
2324
from eaa.tool.imaging.registration import ImageRegistration
@@ -62,6 +63,7 @@ def __init__(
6263
run_offset_calibration: bool = True,
6364
use_linear_drift_prediction: bool = False,
6465
n_parameter_drift_points_before_prediction: int = 3,
66+
line_scan_predictor_tool: Optional[LineScanPredictor] = None,
6567
*args, **kwargs
6668
):
6769
"""Analytical scanning microscope focusing task manager driven
@@ -138,6 +140,14 @@ def __init__(
138140
n_parameter_drift_points_before_prediction : int, optional
139141
Number of parameter-drift samples to collect before using linear
140142
drift prediction for image acquisitions.
143+
line_scan_predictor_tool : LineScanPredictor, optional
144+
If provided, this tool is used instead of image registration to
145+
update scan positions after each 2D acquisition. It predicts the
146+
optimal line scan center from the reference image, reference line
147+
scan position, and current image, then shifts both line scan and
148+
image acquisition kwargs by the predicted drift so they stay in
149+
sync. Requires ``run_offset_calibration=True`` so that a 2D image
150+
is acquired before the prediction is made.
141151
"""
142152
if acquisition_tool is None:
143153
raise ValueError("`acquisition_tool` must be provided.")
@@ -177,6 +187,13 @@ def __init__(
177187
self.run_line_scan_checker = run_line_scan_checker
178188
self.run_offset_calibration = run_offset_calibration
179189
self.use_linear_drift_prediction = use_linear_drift_prediction
190+
if line_scan_predictor_tool is not None and not run_offset_calibration:
191+
raise ValueError(
192+
"`line_scan_predictor_tool` requires `run_offset_calibration=True` "
193+
"because a 2D image must be acquired before the predictor can run."
194+
)
195+
196+
self.line_scan_predictor_tool = line_scan_predictor_tool
180197
self.n_parameter_drift_points_before_prediction = (
181198
n_parameter_drift_points_before_prediction
182199
)
@@ -868,14 +885,17 @@ def rollback_and_shrink_delta(message_prefix: str) -> np.ndarray:
868885
if self.should_apply_linear_drift_prediction():
869886
self.apply_predicted_image_acquisition_position(x_current)
870887
self.run_2d_scan()
871-
line_scan_pos_offset, alignment_offset = self.find_offset()
872-
if np.any(np.isnan(line_scan_pos_offset)):
873-
x_current = rollback_and_shrink_delta("Image registration failed (NaN offset).")
874-
continue
875-
self.apply_offset_to_line_scan_kwargs(line_scan_pos_offset)
876-
self.apply_offset_to_image_acquisition_kwargs(alignment_offset)
877-
self.update_linear_drift_models(x_current)
878-
self.record_linear_drift_model_visualizations()
888+
if self.line_scan_predictor_tool is not None:
889+
self.apply_line_scan_predictor_offset()
890+
else:
891+
line_scan_pos_offset, alignment_offset = self.find_offset()
892+
if np.any(np.isnan(line_scan_pos_offset)):
893+
x_current = rollback_and_shrink_delta("Image registration failed (NaN offset).")
894+
continue
895+
self.apply_offset_to_line_scan_kwargs(line_scan_pos_offset)
896+
self.apply_offset_to_image_acquisition_kwargs(alignment_offset)
897+
self.update_linear_drift_models(x_current)
898+
self.record_linear_drift_model_visualizations()
879899
try:
880900
fwhm = self.run_line_scan()
881901
if np.isnan(fwhm):
@@ -886,6 +906,28 @@ def rollback_and_shrink_delta(message_prefix: str) -> np.ndarray:
886906
self.update_optimization_model(fwhm)
887907
return
888908

909+
def apply_line_scan_predictor_offset(self) -> None:
910+
"""Update scan positions using the line scan predictor.
911+
912+
Calls :meth:`LineScanPredictor.predict_line_scan_position` to obtain
913+
the predicted line scan center in the current image, computes the
914+
drift relative to the current line scan center, then shifts both
915+
``line_scan_kwargs`` and ``image_acquisition_kwargs`` by that drift so
916+
that the two sets of coordinates stay in sync.
917+
"""
918+
current_center = self.extract_scan_position(self.line_scan_kwargs)
919+
result = json.loads(self.line_scan_predictor_tool.predict_line_scan_position())
920+
predicted_center = np.array([result["center_y"], result["center_x"]], dtype=float)
921+
drift = predicted_center - current_center
922+
self.record_system_message(
923+
f"Line scan predictor: current center={current_center.tolist()}, "
924+
f"predicted center={predicted_center.tolist()}, "
925+
f"drift={drift.tolist()}"
926+
)
927+
# apply_offset_to_*_kwargs(o) does position -= o; passing -drift gives position += drift.
928+
self.apply_offset_to_line_scan_kwargs(-drift)
929+
self.apply_offset_to_image_acquisition_kwargs(-drift)
930+
889931
def apply_user_correction_offset(self) -> bool:
890932
message = (
891933
"Manual correction requested. Enter offset-to-subtract as 'y,x' (blank to stop): "

0 commit comments

Comments
 (0)