New capturing Thing

This commit is contained in:
jaknapper 2025-04-17 16:57:45 +01:00
parent 9b2ccea660
commit dee11deec1
3 changed files with 68 additions and 61 deletions

View file

@ -14,7 +14,8 @@
} }
}, },
"/background_detect/": "openflexure_microscope_server.things.background_detect:BackgroundDetectThing", "/background_detect/": "openflexure_microscope_server.things.background_detect:BackgroundDetectThing",
"/z_stack/": "openflexure_microscope_server.things.smart_scan:ZStackThing" "/z_stack/": "openflexure_microscope_server.things.z_stack:ZStackThing",
"/capturing/": "openflexure_microscope_server.things.z_stack:CapturingThing"
}, },
"settings_folder": "/var/openflexure/settings/" "settings_folder": "/var/openflexure/settings/"
} }

View file

@ -596,7 +596,7 @@ class SmartScanThing(Thing):
f"{new_pos_xyz[0]}_{new_pos_xyz[1]}", f"{new_pos_xyz[0]}_{new_pos_xyz[1]}",
) )
os.makedirs(site_folder, exist_ok=True) os.makedirs(site_folder, exist_ok=True)
self._z_stack.smart_stack( self._z_stack.run_z_stack(
images_dir=self._ongoing_scan_images_dir, images_dir=self._ongoing_scan_images_dir,
stack_dir=site_folder, stack_dir=site_folder,
) )

View file

@ -15,70 +15,15 @@ from .stage import StageDependency as StageDep
from labthings_fastapi.dependencies.invocation import ( from labthings_fastapi.dependencies.invocation import (
InvocationLogger, InvocationLogger,
) )
from labthings_fastapi.dependencies.thing import direct_thing_client_dependency
class CaptureError(RuntimeError): class CaptureError(RuntimeError):
"""An error trying to capture from Picamera""" """An error trying to capture from Picamera"""
class ZStackThing(Thing): class CapturingThing(Thing):
@thing_property
def images_to_capture(self) -> int:
"""The number of images to capture and save in a stack
Defaults to 1 unless you need to see either side of focus"""
return self.thing_settings.get("images_to_capture", 1)
@images_to_capture.setter
def images_to_capture(self, value: int) -> None:
self.thing_settings["images_to_capture"] = value
@thing_property
def stack_dz(self) -> int:
"""Space in steps between images in a z-stack
Suggested is 50 for 60-100x
100 for 40x
200 for 20x"""
return self.thing_settings.get("stack_dz", 50)
@stack_dz.setter
def stack_dz(self, value: int) -> None:
self.thing_settings["stack_dz"] = value
@thing_action @thing_action
def smart_stack(
self,
cam: CamDep,
stage: StageDep,
logger: InvocationLogger,
metadata_getter: GetThingStates,
images_dir: str,
stack_dir: str,
) -> None:
stack_dz = self.stack_dz
images_to_capture = self.images_to_capture
stack_z_range = stack_dz * (images_to_capture - 1)
stage.move_relative(z=-stack_z_range / 2)
for capture_count in range(images_to_capture):
jpeg_path = os.path.join(
stack_dir,
f"{capture_count}.jpeg",
)
self._capture_and_save(
jpeg_path=jpeg_path,
cam=cam,
logger=logger,
metadata_getter=metadata_getter,
)
# If the stack isn't complete yet, move
if capture_count + 1 < images_to_capture:
stage.move_relative(z=stack_dz)
time.sleep(0.3)
self.copy_central_image(images_dir, stack_dir, logger)
def _capture_and_save( def _capture_and_save(
self, self,
jpeg_path: str, jpeg_path: str,
@ -145,11 +90,73 @@ class ZStackThing(Thing):
except Exception as e: except Exception as e:
raise IOError(f"An error occurred while saving {jpeg_path}") from e raise IOError(f"An error occurred while saving {jpeg_path}") from e
CapturingDep = direct_thing_client_dependency(CapturingThing, "/capturing/")
class ZStackThing(Thing):
@thing_property
def images_to_capture(self) -> int:
"""The number of images to capture and save in a stack
Defaults to 1 unless you need to see either side of focus"""
return self.thing_settings.get("images_to_capture", 1)
@images_to_capture.setter
def images_to_capture(self, value: int) -> None:
self.thing_settings["images_to_capture"] = value
@thing_property
def stack_dz(self) -> int:
"""Space in steps between images in a z-stack
Suggested is 50 for 60-100x
100 for 40x
200 for 20x"""
return self.thing_settings.get("stack_dz", 50)
@stack_dz.setter
def stack_dz(self, value: int) -> None:
self.thing_settings["stack_dz"] = value
@thing_action
def run_z_stack(
self,
cam: CamDep,
stage: StageDep,
logger: InvocationLogger,
metadata_getter: GetThingStates,
capture: CapturingDep,
images_dir: str,
stack_dir: str,
) -> None:
stack_dz = self.stack_dz
images_to_capture = self.images_to_capture
stack_z_range = stack_dz * (images_to_capture - 1)
stage.move_relative(z=-stack_z_range / 2)
for capture_count in range(images_to_capture):
jpeg_path = os.path.join(
stack_dir,
f"{capture_count}.jpeg",
)
capture._capture_and_save(
jpeg_path=jpeg_path,
cam=cam,
logger=logger,
metadata_getter=metadata_getter,
)
# If the stack isn't complete yet, move
if capture_count + 1 < images_to_capture:
stage.move_relative(z=stack_dz)
time.sleep(0.3)
self.copy_central_image(images_dir, stack_dir, logger)
def copy_central_image( def copy_central_image(
self, self,
images_dir: str, images_dir: str,
stack_dir: str, stack_dir: str,
logger: InvocationLogger,
): ):
"""Gets a list of images in a folder (stack_dir), sorts them, and copies the central image """Gets a list of images in a folder (stack_dir), sorts them, and copies the central image
to images dir.""" to images dir."""
@ -157,7 +164,6 @@ class ZStackThing(Thing):
image_list.sort() image_list.sort()
central_index = (len(image_list) - 1) // 2 central_index = (len(image_list) - 1) // 2
central_image = image_list[central_index] central_image = image_list[central_index]
logger.warning(central_image)
xy_location = os.path.basename(stack_dir) xy_location = os.path.basename(stack_dir)
shutil.copy(central_image, os.path.join(images_dir, f"{xy_location}.jpeg")) shutil.copy(central_image, os.path.join(images_dir, f"{xy_location}.jpeg"))