From 6237762309f695f9d09ac068ac40a04f21a059db Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 28 Mar 2019 14:18:14 +0000 Subject: [PATCH] Combined stack and tile scans --- .../plugins/default/scan/api.py | 12 +- .../plugins/default/scan/plugin.py | 109 ++++++++++++++---- 2 files changed, 95 insertions(+), 26 deletions(-) diff --git a/openflexure_microscope/plugins/default/scan/api.py b/openflexure_microscope/plugins/default/scan/api.py index 7cc7c9f0..69d9aa56 100644 --- a/openflexure_microscope/plugins/default/scan/api.py +++ b/openflexure_microscope/plugins/default/scan/api.py @@ -10,11 +10,12 @@ class TileScanAPI(MicroscopeViewPlugin): # Get params name = payload.param('name') - step_size = payload.param('step_size', default=[2000, 1500], convert=list) - grid = payload.param('grid', default=[3, 3], convert=list) - autofocus_dz = payload.param('autofocus_dz', default=20, convert=int) + step_size = payload.param('step_size', default=[2000, 1500, 100], convert=list) + grid = payload.param('grid', default=[3, 3, 5], convert=list) + style = payload.param('style', default='raster', convert=str) + autofocus_dz = payload.param('autofocus_dz', default=50, convert=int) - use_video_port = payload.param('use_video_port', default=False, convert=bool) + use_video_port = payload.param('use_video_port', default=True, convert=bool) resize = payload.param('size', default=None) if resize: if ('width' in resize) and ('height' in resize): @@ -22,7 +23,7 @@ class TileScanAPI(MicroscopeViewPlugin): else: abort(404) - bayer = payload.param('bayer', default=True, convert=bool) + bayer = payload.param('bayer', default=False, convert=bool) metadata = payload.param('metadata', default={}, convert=dict) tags = payload.param('tags', default=[], convert=list) @@ -32,6 +33,7 @@ class TileScanAPI(MicroscopeViewPlugin): basename=name, step_size=step_size, grid=grid, + style=style, autofocus_dz=autofocus_dz, use_video_port=use_video_port, resize=resize, diff --git a/openflexure_microscope/plugins/default/scan/plugin.py b/openflexure_microscope/plugins/default/scan/plugin.py index 3fc9e27e..ffe2921e 100644 --- a/openflexure_microscope/plugins/default/scan/plugin.py +++ b/openflexure_microscope/plugins/default/scan/plugin.py @@ -2,12 +2,44 @@ import time import numpy as np from typing import Tuple import uuid +import logging from openflexure_microscope.camera.base import generate_basename from openflexure_microscope.plugins import MicroscopePlugin from .api import TileScanAPI, ZStackAPI +def construct_grid(initial, step_sizes, n_steps, style='raster'): + """ + Given an initial position, step sizes, and number of steps, + construct a 2-dimensional list of scan x-y positions. + """ + arr = [] + + for i in range(n_steps[0]): # x axis + arr.append([]) + for j in range(n_steps[1]): # y axis + # Create a coordinate array + coord = [initial[ax] + [i, j][ax]*step_sizes[ax] for ax in range(2)] + # Append coordinate array to position grid + arr[i].append(tuple(coord)) + + # Style modifiers + if style == 'snake': + for i, line in enumerate(arr): + if i % 2 != 0: + line.reverse() + + return arr + +def flatten_grid(grid): + """ + Convert a 3D list of scan positions into a flat list + of sequential positions. + """ + + grid = list(itertools.chain(*grid)) + return grid class ScanPlugin(MicroscopePlugin): """ @@ -61,9 +93,10 @@ class ScanPlugin(MicroscopePlugin): def tile( self, basename: str = None, - step_size: int = [2000, 1500], - grid: list = [3, 3], - autofocus_dz: int = 20, + step_size: int = [2000, 1500, 100], + grid: list = [3, 3, 5], + style='raster', + autofocus_dz: int = 50, use_video_port: bool = False, resize: Tuple[int, int] = None, bayer: bool = False, @@ -86,17 +119,38 @@ class ScanPlugin(MicroscopePlugin): else: autofocus_enabled = False - xdirection = 1 + # Construct an x-y grid (worry about z later) + x_y_grid = construct_grid( + initial_position, + step_size[:2], + grid[:2], + style=style + ) - with self.microscope.lock: + # Now step through each point in the x-y coordinate array + for line in x_y_grid: + # If rastering, rather than snake (or eventually spiral) + if style == 'raster': + # Return focus to initial position + logging.debug("Returning to initial z position") + self.microscope.stage.move_abs([line[0][0], line[0][1], initial_position[2]]) - for i in range(grid[0]): - for j in range(grid[1]): - - if autofocus_enabled: - self.microscope.plugin.default_autofocus.autofocus( - range(-2 * autofocus_dz, 3 * autofocus_dz, autofocus_dz)) + for x_y in line: + current_position = self.microscope.stage.position + # Move to new grid position without changing z + logging.debug("Moving to step {}".format([x_y[0], x_y[1], current_position[2]])) + self.microscope.stage.move_abs([x_y[0], x_y[1], current_position[2]]) + # Refocus + if autofocus_enabled: + # TODO: Better autofocus + logging.debug("Running autofocus") + self.microscope.plugin.default_autofocus.autofocus( + range(-3 * autofocus_dz, 4 * autofocus_dz, autofocus_dz)) + logging.debug("Finished autofocus") + time.sleep(1) # TODO: Remove + # If we're not doing a z-stack, just capture + if (grid[2] <= 1): self.capture( basename, scan_id, @@ -106,19 +160,28 @@ class ScanPlugin(MicroscopePlugin): metadata=metadata, tags=tags ) + else: + logging.debug("Entering z-stack") + self.stack( + basename=basename, + scan_id=scan_id, + step_size=step_size[2], + steps=grid[2], + center=True, + use_video_port=use_video_port, + resize=resize, + bayer=bayer, + metadata=metadata, + tags=tags + ) - if j != grid[0] - 1: - self.microscope.stage.move_rel([step_size[0] * xdirection, 0, 0]) - - xdirection *= -1 - if i != grid[1] - 1: - self.microscope.stage.move_rel([256 * xdirection, step_size[1], 0]) - - self.microscope.stage.move_abs(initial_position) + logging.debug("Returning to {}".format(initial_position)) + self.microscope.stage.move_abs(initial_position) def stack( self, basename: str = None, + scan_id: str = None, step_size: int = 100, steps: int = 5, center: bool = True, @@ -133,19 +196,21 @@ class ScanPlugin(MicroscopePlugin): basename = generate_basename() # Generate a stack ID - scan_id = uuid.uuid4().hex + if not scan_id: + scan_id = uuid.uuid4().hex # Store initial position initial_position = self.microscope.stage.position # Move to center scan if center: + logging.debug("Moving to starting position") self.microscope.stage.move_rel([0, 0, int((-step_size * steps) / 2)]) with self.microscope.lock: for i in range(steps): - + logging.debug("Capturing...") self.capture( basename, scan_id, @@ -157,6 +222,8 @@ class ScanPlugin(MicroscopePlugin): ) if i != steps - 1: + logging.debug("Moving z by {}".format(step_size)) self.microscope.stage.move_rel([0, 0, step_size]) + logging.debug("Returning to {}".format(initial_position)) self.microscope.stage.move_abs(initial_position)