Merge branch 'purge-recentre' into 'v3'
Remove auto-recentre stage Closes #486 See merge request openflexure/openflexure-microscope-server!339
This commit is contained in:
commit
931a570b43
4 changed files with 0 additions and 160 deletions
|
|
@ -2,7 +2,6 @@
|
||||||
"things": {
|
"things": {
|
||||||
"/camera/": "openflexure_microscope_server.things.camera.picamera:StreamingPiCamera2",
|
"/camera/": "openflexure_microscope_server.things.camera.picamera:StreamingPiCamera2",
|
||||||
"/stage/": "openflexure_microscope_server.things.stage.sangaboard:SangaboardThing",
|
"/stage/": "openflexure_microscope_server.things.stage.sangaboard:SangaboardThing",
|
||||||
"/auto_recentre_stage/": "openflexure_microscope_server.things.auto_recentre_stage:RecentringThing",
|
|
||||||
"/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing",
|
"/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing",
|
||||||
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
|
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
|
||||||
"/system/": "openflexure_microscope_server.things.system:OpenFlexureSystem",
|
"/system/": "openflexure_microscope_server.things.system:OpenFlexureSystem",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
"things": {
|
"things": {
|
||||||
"/camera/": "openflexure_microscope_server.things.camera.simulation:SimulatedCamera",
|
"/camera/": "openflexure_microscope_server.things.camera.simulation:SimulatedCamera",
|
||||||
"/stage/": "openflexure_microscope_server.things.stage.dummy:DummyStage",
|
"/stage/": "openflexure_microscope_server.things.stage.dummy:DummyStage",
|
||||||
"/auto_recentre_stage/": "openflexure_microscope_server.things.auto_recentre_stage:RecentringThing",
|
|
||||||
"/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing",
|
"/autofocus/": "openflexure_microscope_server.things.autofocus:AutofocusThing",
|
||||||
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
|
"/camera_stage_mapping/": "openflexure_microscope_server.things.camera_stage_mapping:CameraStageMapper",
|
||||||
"/system/": "openflexure_microscope_server.things.system:OpenFlexureSystem",
|
"/system/": "openflexure_microscope_server.things.system:OpenFlexureSystem",
|
||||||
|
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
"""Provide functionality for automatically recentring the Stage."""
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import labthings_fastapi as lt
|
|
||||||
|
|
||||||
from .stage import StageDependency as StageDep
|
|
||||||
from openflexure_microscope_server.things.autofocus import AutofocusThing
|
|
||||||
from openflexure_microscope_server.things.camera_stage_mapping import CameraStageMapper
|
|
||||||
|
|
||||||
CSMDep = lt.deps.direct_thing_client_dependency(
|
|
||||||
CameraStageMapper, "/camera_stage_mapping/"
|
|
||||||
)
|
|
||||||
AutofocusDep = lt.deps.direct_thing_client_dependency(AutofocusThing, "/autofocus/")
|
|
||||||
|
|
||||||
|
|
||||||
class RecentringThing(lt.Thing):
|
|
||||||
"""A Thing for recentring the stage by monitoring parasitic z motion of the stage.
|
|
||||||
|
|
||||||
As the stage moves over a sphere-cap there is parasitic motion in z during xy
|
|
||||||
movement. The highest z position is the centre of motion.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@lt.thing_action
|
|
||||||
def recentre(
|
|
||||||
self,
|
|
||||||
autofocus: AutofocusDep,
|
|
||||||
stage: StageDep,
|
|
||||||
max_steps=15,
|
|
||||||
lateral_distance=5000,
|
|
||||||
):
|
|
||||||
"""Recentre the stage, based on the focal plane.
|
|
||||||
|
|
||||||
Autofocuses at multiple points around the sample to
|
|
||||||
find the overall maximum (or minimum) height, which
|
|
||||||
corresponds to the centre of the stage. This exploits the
|
|
||||||
fact that the OpenFlexure stage moves in an arc, i.e. its
|
|
||||||
height will vary with X and Y. The point where the variation
|
|
||||||
of Z with X and Y motion is smallest is the centre of its
|
|
||||||
XY travel. This routine moves in X and Y, monitoring the
|
|
||||||
Z value of the focal plane, and attempts to find the point
|
|
||||||
where Z does not vary with X and Y, which is where it stops.
|
|
||||||
|
|
||||||
max_steps: The maximum number of moves in x or y before
|
|
||||||
aborting due to a poorly positioned stage or hard to focus
|
|
||||||
sample
|
|
||||||
|
|
||||||
lateral_distance: The xy distance between areas to check.
|
|
||||||
Below 3000 becomes less reliable, as focus shouldn't shift
|
|
||||||
much between these sites, making the procedure more sensitive
|
|
||||||
to noise or a failed autofocus.
|
|
||||||
"""
|
|
||||||
max_steps = 20
|
|
||||||
dx = lateral_distance
|
|
||||||
|
|
||||||
centre = list(stage.position.values())
|
|
||||||
|
|
||||||
# A list of all the positions we've focused
|
|
||||||
focused_pos = [[], []]
|
|
||||||
|
|
||||||
autofocus.looping_autofocus()
|
|
||||||
|
|
||||||
for direction in [0, 1]:
|
|
||||||
# Start off with the current position, and moving in the positive direction
|
|
||||||
focused_pos[direction] = [list(stage.position.values())]
|
|
||||||
moves = +1
|
|
||||||
|
|
||||||
stage.move_absolute(x=centre[0], y=centre[1], z=centre[2])
|
|
||||||
steps = 0
|
|
||||||
all_heights = []
|
|
||||||
|
|
||||||
# We'll run this for x, then y
|
|
||||||
while True:
|
|
||||||
# If we're moving in the positive direction, we want the highest point
|
|
||||||
# Otherwise, we want the lowest
|
|
||||||
if moves > 0:
|
|
||||||
starting_point = np.max(
|
|
||||||
np.array(focused_pos[direction])[:, direction]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
starting_point = np.min(
|
|
||||||
np.array(focused_pos[direction])[:, direction]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Next location is an extra move in the direction we want
|
|
||||||
destination = centre
|
|
||||||
destination[direction] = starting_point + moves * dx
|
|
||||||
stage.move_absolute(
|
|
||||||
x=int(destination[0]), y=int(destination[1]), z=destination[2]
|
|
||||||
)
|
|
||||||
autofocus.looping_autofocus(autofocus, stage)
|
|
||||||
position = list(stage.position.values())
|
|
||||||
focused_pos[direction].append(position)
|
|
||||||
|
|
||||||
logging.info(focused_pos)
|
|
||||||
|
|
||||||
steps += 1
|
|
||||||
if steps > max_steps:
|
|
||||||
logging.warning(
|
|
||||||
"Couldn't find a suitable position. Roughly centre the stage and check your sample is suitable for autofocus"
|
|
||||||
)
|
|
||||||
break
|
|
||||||
|
|
||||||
if len(focused_pos[direction]) > 4:
|
|
||||||
all_heights = [x[2] for x in focused_pos[direction]]
|
|
||||||
direction_index = [x[direction] for x in focused_pos[direction]]
|
|
||||||
|
|
||||||
sorted_all_heights = [
|
|
||||||
x for y, x in sorted(zip(direction_index, all_heights))
|
|
||||||
]
|
|
||||||
|
|
||||||
sorted_lateral = sorted(direction_index)
|
|
||||||
quad_fit = np.polyfit(sorted_lateral, sorted_all_heights, 2)
|
|
||||||
quad_fit_func = np.poly1d(quad_fit)
|
|
||||||
|
|
||||||
turning = quad_fit_func.deriv()
|
|
||||||
|
|
||||||
turning_loc = -turning[0] / (turning[1])
|
|
||||||
|
|
||||||
logging.warning(sorted_all_heights)
|
|
||||||
if (
|
|
||||||
np.argmax(sorted_all_heights) != 0
|
|
||||||
and np.argmax(sorted_all_heights) != len(all_heights) - 1
|
|
||||||
):
|
|
||||||
logging.info(
|
|
||||||
f"Breaking because the highest point is at {np.argmax(sorted_all_heights)} in the list"
|
|
||||||
)
|
|
||||||
|
|
||||||
break
|
|
||||||
if turning_loc < np.min(sorted_lateral):
|
|
||||||
moves = -1
|
|
||||||
elif turning_loc > np.max(sorted_lateral):
|
|
||||||
moves = 1
|
|
||||||
|
|
||||||
# Centre value is replaced by the maximum value recorded in that axis
|
|
||||||
centre[direction] = focused_pos[direction][np.argmax(all_heights)][
|
|
||||||
direction
|
|
||||||
]
|
|
||||||
stage.move_absolute(x=centre[0], y=centre[1], z=centre[2])
|
|
||||||
autofocus.looping_autofocus()
|
|
||||||
|
|
||||||
logging.info(f"Centre of ROM is at {centre, stage.position['z']} \n")
|
|
||||||
|
|
||||||
return focused_pos
|
|
||||||
|
|
@ -76,19 +76,6 @@
|
||||||
@finished="updatePosition"
|
@finished="updatePosition"
|
||||||
@error="modalError"
|
@error="modalError"
|
||||||
/>
|
/>
|
||||||
<action-button
|
|
||||||
thing="auto_recentre_stage"
|
|
||||||
action="recentre"
|
|
||||||
submit-label="Re-centre Stage"
|
|
||||||
:can-terminate="true"
|
|
||||||
:requires-confirmation="true"
|
|
||||||
:modal-progress="true"
|
|
||||||
:confirmation-message="
|
|
||||||
'The stage will now move, and autofocus will be used to find the centre of motion. This requires a sample to be visible in the microscope. OK to proceed?'
|
|
||||||
"
|
|
||||||
@finished="updatePosition"
|
|
||||||
@error="modalError"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue