Complete testing of HistoScanWorkflow
This commit is contained in:
parent
1bc6b9e0c4
commit
b22fcf4b88
2 changed files with 153 additions and 6 deletions
|
|
@ -422,20 +422,22 @@ class HistoScanWorkflow(ScanWorkflow[HistoScanSettingsModel]):
|
||||||
:return: A tuple of whether an image was taken, and the z-position for focus.
|
:return: A tuple of whether an image was taken, and the z-position for focus.
|
||||||
If failed to find focus, returns for the focus z-position.
|
If failed to find focus, returns for the focus z-position.
|
||||||
"""
|
"""
|
||||||
# If skipping background, take an image to check if current field of view is background
|
|
||||||
if settings.skip_background:
|
if settings.skip_background:
|
||||||
|
# If skipping background, take an image to check if current field of view
|
||||||
|
# is background
|
||||||
image_array = self._cam.grab_as_array(stream_name="lores")
|
image_array = self._cam.grab_as_array(stream_name="lores")
|
||||||
capture_image, bg_message = self._background_detector.image_is_sample(
|
capture_image, bg_message = self._background_detector.image_is_sample(
|
||||||
image_array
|
image_array
|
||||||
)
|
)
|
||||||
del image_array
|
del image_array
|
||||||
|
|
||||||
if not capture_image:
|
if not capture_image:
|
||||||
msg = f"Skipping {xyz_pos} as it is {bg_message}."
|
# Return early if the image is background.
|
||||||
self.logger.info(msg)
|
msg = f"Skipping {xyz_pos} as it is {bg_message}."
|
||||||
return False, None
|
self.logger.info(msg)
|
||||||
|
return False, None
|
||||||
|
|
||||||
save_on_failure = settings.skip_background
|
save_on_failure = not settings.skip_background
|
||||||
|
|
||||||
focus_height: Optional[int]
|
focus_height: Optional[int]
|
||||||
focused, focus_height = self._autofocus.run_smart_stack(
|
focused, focus_height = self._autofocus.run_smart_stack(
|
||||||
|
|
@ -445,6 +447,10 @@ class HistoScanWorkflow(ScanWorkflow[HistoScanSettingsModel]):
|
||||||
# An image was captured if we are focussed or we are not skipping background.
|
# An image was captured if we are focussed or we are not skipping background.
|
||||||
imaged = focused or save_on_failure
|
imaged = focused or save_on_failure
|
||||||
|
|
||||||
|
if not imaged:
|
||||||
|
msg = f"Stack failed at {xyz_pos} treating as background."
|
||||||
|
self.logger.info(msg)
|
||||||
|
|
||||||
# run_smart_stage always returns a focus height for the sharpest image even
|
# run_smart_stage always returns a focus height for the sharpest image even
|
||||||
# if it failed to find a good focus. Set to None if not focussed.
|
# if it failed to find a good focus. Set to None if not focussed.
|
||||||
if not focused:
|
if not focused:
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from pydantic import BaseModel
|
||||||
import labthings_fastapi as lt
|
import labthings_fastapi as lt
|
||||||
from labthings_fastapi.testing import create_thing_without_server
|
from labthings_fastapi.testing import create_thing_without_server
|
||||||
|
|
||||||
|
from openflexure_microscope_server.scan_planners import SmartSpiral
|
||||||
from openflexure_microscope_server.stitching import StitchingSettings
|
from openflexure_microscope_server.stitching import StitchingSettings
|
||||||
from openflexure_microscope_server.things.autofocus import SmartStackParams
|
from openflexure_microscope_server.things.autofocus import SmartStackParams
|
||||||
from openflexure_microscope_server.things.camera_stage_mapping import csm_img_to_stage
|
from openflexure_microscope_server.things.camera_stage_mapping import csm_img_to_stage
|
||||||
|
|
@ -190,6 +191,146 @@ def test_histo_calculate_overlap(csm_matrix, overlap, expected_steps, histo_work
|
||||||
assert (dx, dy) == expected_steps
|
assert (dx, dy) == expected_steps
|
||||||
|
|
||||||
|
|
||||||
|
def test_histo_pre_scan_routine(histo_workflow, mocker):
|
||||||
|
"""Check the pre-scan routine does autofocusses."""
|
||||||
|
# Rather than create a whole Setting class, just create a mock with the value
|
||||||
|
# we need set
|
||||||
|
mock_settings = mocker.Mock()
|
||||||
|
mock_settings.smart_stack_params.autofocus_dz = 1234
|
||||||
|
# Run the function
|
||||||
|
histo_workflow.pre_scan_routine(mock_settings)
|
||||||
|
# Check the autofocus was run using the mocked slot.
|
||||||
|
assert histo_workflow._autofocus.looping_autofocus.call_count == 1
|
||||||
|
call_kwargs = histo_workflow._autofocus.looping_autofocus.call_args.kwargs
|
||||||
|
assert call_kwargs["dz"] == 1234
|
||||||
|
assert call_kwargs["start"] == "centre"
|
||||||
|
|
||||||
|
|
||||||
|
def test_histo_new_scan_planner(histo_workflow, mocker):
|
||||||
|
"""Check the pre-scan routine does autofocusses."""
|
||||||
|
# Rather than create a whole Setting class, just create a mock with the 3 values
|
||||||
|
# we need set
|
||||||
|
mock_settings = mocker.Mock()
|
||||||
|
mock_settings.dx = 666
|
||||||
|
mock_settings.dy = 999
|
||||||
|
mock_settings.max_dist = 54321
|
||||||
|
|
||||||
|
planner = histo_workflow.new_scan_planner(
|
||||||
|
mock_settings, position={"x": 1, "y": 2, "z": 3}
|
||||||
|
)
|
||||||
|
assert isinstance(planner, SmartSpiral)
|
||||||
|
# Check the settings were read
|
||||||
|
assert planner._dx == 666
|
||||||
|
assert planner._dy == 999
|
||||||
|
assert planner._max_dist == 54321
|
||||||
|
|
||||||
|
|
||||||
|
def test_histo_acquisition_with_background_detected(histo_workflow, mocker, caplog):
|
||||||
|
"""If background is detected it should return without a stack."""
|
||||||
|
# Mocking for settings as above
|
||||||
|
mock_settings = mocker.Mock()
|
||||||
|
mock_settings.skip_background = True
|
||||||
|
|
||||||
|
histo_workflow._background_detector.image_is_sample.return_value = (
|
||||||
|
False,
|
||||||
|
"totally empty",
|
||||||
|
)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.INFO):
|
||||||
|
imaged, focus_height = histo_workflow.acquisition_routine(
|
||||||
|
mock_settings, xyz_pos=(1, 2, 3)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check returns
|
||||||
|
assert imaged is False
|
||||||
|
assert focus_height is None # None meaning not focussed
|
||||||
|
# Check there is a log explaining what happened
|
||||||
|
assert len(caplog.records) == 1
|
||||||
|
assert caplog.records[0].message == "Skipping (1, 2, 3) as it is totally empty."
|
||||||
|
assert caplog.records[0].levelname == "INFO"
|
||||||
|
|
||||||
|
# And that smart stack never ran
|
||||||
|
assert histo_workflow._autofocus.run_smart_stack.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_histo_acquisition_not_skipping_background(histo_workflow, mocker, caplog):
|
||||||
|
"""Check when not skipping background an image is always saved."""
|
||||||
|
# Mocking for settings as above
|
||||||
|
mock_settings = mocker.Mock()
|
||||||
|
mock_settings.skip_background = False
|
||||||
|
|
||||||
|
# Set up background detector to say it is empty, this shouldn't stop stacking.
|
||||||
|
histo_workflow._background_detector.image_is_sample.return_value = (
|
||||||
|
False,
|
||||||
|
"totally empty",
|
||||||
|
)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.INFO):
|
||||||
|
# First set smart stack to report failure
|
||||||
|
histo_workflow._autofocus.run_smart_stack.return_value = (False, 123)
|
||||||
|
imaged, focus_height = histo_workflow.acquisition_routine(
|
||||||
|
mock_settings, xyz_pos=(1, 2, 3)
|
||||||
|
)
|
||||||
|
# Check return
|
||||||
|
assert imaged is True # Always images if not skipping background
|
||||||
|
assert focus_height is None # None meaning didn't focus
|
||||||
|
|
||||||
|
# And again reporting a successful stack
|
||||||
|
histo_workflow._autofocus.run_smart_stack.return_value = (True, 123)
|
||||||
|
imaged, focus_height = histo_workflow.acquisition_routine(
|
||||||
|
mock_settings, xyz_pos=(1, 2, 3)
|
||||||
|
)
|
||||||
|
# Check return
|
||||||
|
assert imaged is True # Always images if not skipping background
|
||||||
|
assert focus_height == 123
|
||||||
|
|
||||||
|
# Should never warn
|
||||||
|
assert len(caplog.records) == 0
|
||||||
|
|
||||||
|
# And that smart stack never ran
|
||||||
|
assert histo_workflow._autofocus.run_smart_stack.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_histo_acquisition_on_sample(histo_workflow, mocker, caplog):
|
||||||
|
"""Check when skipping background, but background not detected."""
|
||||||
|
# Mocking for settings as above
|
||||||
|
mock_settings = mocker.Mock()
|
||||||
|
mock_settings.skip_background = True
|
||||||
|
|
||||||
|
# Set up background detector to say there is sample.
|
||||||
|
histo_workflow._background_detector.image_is_sample.return_value = (True, None)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.INFO):
|
||||||
|
# First set smart stack to report failure
|
||||||
|
histo_workflow._autofocus.run_smart_stack.return_value = (False, 123)
|
||||||
|
imaged, focus_height = histo_workflow.acquisition_routine(
|
||||||
|
mock_settings, xyz_pos=(1, 2, 3)
|
||||||
|
)
|
||||||
|
# Check return:
|
||||||
|
# Don't save failed stack when skipping background, assume it is actually
|
||||||
|
# background
|
||||||
|
assert imaged is False
|
||||||
|
assert focus_height is None # None meaning didn't focus
|
||||||
|
|
||||||
|
# And again reporting a successful stack
|
||||||
|
histo_workflow._autofocus.run_smart_stack.return_value = (True, 123)
|
||||||
|
imaged, focus_height = histo_workflow.acquisition_routine(
|
||||||
|
mock_settings, xyz_pos=(1, 2, 3)
|
||||||
|
)
|
||||||
|
# Check return
|
||||||
|
assert imaged is True # Always images if not skipping background
|
||||||
|
assert focus_height == 123
|
||||||
|
|
||||||
|
# Should never warn
|
||||||
|
assert len(caplog.records) == 1
|
||||||
|
expected_message = "Stack failed at (1, 2, 3) treating as background."
|
||||||
|
assert caplog.records[0].message == expected_message
|
||||||
|
assert caplog.records[0].levelname == "INFO"
|
||||||
|
|
||||||
|
# And that smart stack never ran
|
||||||
|
assert histo_workflow._autofocus.run_smart_stack.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
def test_histo_workflow_settings_ui(histo_workflow):
|
def test_histo_workflow_settings_ui(histo_workflow):
|
||||||
"""Check that the workflow specifies the expected controls."""
|
"""Check that the workflow specifies the expected controls."""
|
||||||
ui = histo_workflow.settings_ui
|
ui = histo_workflow.settings_ui
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue