From d7e19c40e2aea301213870ce16c44a9d08d304e0 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Tue, 29 Jan 2019 13:54:47 +0000 Subject: [PATCH] Included Richards autofocus plugin in defaults --- openflexure_microscope/plugins/default/api.py | 27 ++++++++++-- .../plugins/default/focus_utils.py | 21 ++++++++++ .../plugins/default/plugin.py | 42 +++++++++++++++++-- 3 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 openflexure_microscope/plugins/default/focus_utils.py diff --git a/openflexure_microscope/plugins/default/api.py b/openflexure_microscope/plugins/default/api.py index 4bae0f2f..a97acf11 100644 --- a/openflexure_microscope/plugins/default/api.py +++ b/openflexure_microscope/plugins/default/api.py @@ -1,6 +1,9 @@ -from openflexure_microscope.api.v1.views import MicroscopeViewPlugin +import numpy as np -from flask import request, Response, escape +from openflexure_microscope.api.v1.views import MicroscopeViewPlugin +from openflexure_microscope.api.utilities import JsonPayload + +from flask import request, Response, escape, jsonify import logging @@ -16,4 +19,22 @@ class IdentifyAPI(MicroscopeViewPlugin): .. :quickref: Default plugin; Identify hardware """ data = self.microscope.plugin.default.identify() # Call a method from our plugin, using the full route - return Response(escape(data)) \ No newline at end of file + return Response(escape(data)) + +class MeasureSharpnessAPI(MicroscopeViewPlugin): + def post(self): + payload = JsonPayload(request) + return jsonify({'sharpness': self.plugin.measure_sharpness()}) + +class AutofocusAPI(MicroscopeViewPlugin): + def post(self): + payload = JsonPayload(request) + + # Figure out the range of z values to use + dz = payload.param("dz", default=np.linspace(-300,300,7), convert=np.array) + + print("Running autofocus...") + task = self.microscope.task.start(self.plugin.autofocus, dz) + + # return a handle on the autofocus task + return jsonify(task.state, 202) diff --git a/openflexure_microscope/plugins/default/focus_utils.py b/openflexure_microscope/plugins/default/focus_utils.py new file mode 100644 index 00000000..d1471627 --- /dev/null +++ b/openflexure_microscope/plugins/default/focus_utils.py @@ -0,0 +1,21 @@ +import numpy as np +from scipy import ndimage + +def decimate_to(shape, image): + """Decimate an image to reduce its size if it's too big.""" + decimation = np.max(np.ceil(np.array(image.shape, dtype=np.float)[:len(shape)]/np.array(shape))) + return image[::int(decimation), ::int(decimation), ...] + +def sharpness_sum_lap2(rgb_image): + """Return an image sharpness metric: sum(laplacian(image)**")""" + #image_bw=np.mean(decimate_to((1000,1000), rgb_image),2) + image_bw=np.mean(rgb_image, 2) + image_lap=ndimage.filters.laplace(image_bw) + return np.mean(image_lap.astype(np.float)**4) + +def sharpness_edge(image): + """Return a sharpness metric optimised for vertical lines""" + gray = np.mean(image.astype(float), 2) + n = 20 + edge = np.array([[-1]*n + [1]*n]) + return np.sum([np.sum(ndimage.filters.convolve(gray,W)**2) for W in [edge, edge.T]]) diff --git a/openflexure_microscope/plugins/default/plugin.py b/openflexure_microscope/plugins/default/plugin.py index eb66cee3..f32e1d7d 100644 --- a/openflexure_microscope/plugins/default/plugin.py +++ b/openflexure_microscope/plugins/default/plugin.py @@ -1,6 +1,11 @@ -from openflexure_microscope.plugins import MicroscopePlugin +import time +import numpy as np -from .api import IdentifyAPI +from openflexure_microscope.plugins import MicroscopePlugin +from openflexure_microscope.utilities import set_properties + +from .focus_utils import sharpness_sum_lap2 +from .api import IdentifyAPI, MeasureSharpnessAPI, AutofocusAPI class Plugin(MicroscopePlugin): @@ -10,6 +15,8 @@ class Plugin(MicroscopePlugin): api_views = { '/identify': IdentifyAPI, + '/measure_sharpness': MeasureSharpnessAPI, + '/autofocus': AutofocusAPI, } def identify(self): @@ -18,5 +25,34 @@ class Plugin(MicroscopePlugin): """ response = "My parent camera is {}, and my parent stage is {}.".format(self.microscope.camera, self.microscope.stage) - print(response) return response + + def autofocus(self, dz, settle=0.5, metric_fn=sharpness_sum_lap2): + """Perform a simple autofocus routine. + The stage is moved to z positions (relative to current position) in dz, + and at each position an image is captured and the sharpness function + evaulated. We then move back to the position where the sharpness was + highest. No interpolation is performed. + dz is assumed to be in ascending order (starting at -ve values) + """ + camera = self.microscope.camera + stage = self.microscope.stage + + with set_properties(stage, backlash=256), stage.lock, camera.lock: + sharpnesses = [] + positions = [] + camera.annotate_text = "" + + for i in stage.scan_z(dz, return_to_start=False): + positions.append(stage.position[2]) + time.sleep(settle) + sharpnesses.append(self.measure_sharpness(metric_fn)) + + newposition = positions[np.argmax(sharpnesses)] + stage.focus_rel(newposition - stage.position[2]) + + return positions, sharpnesses + + def measure_sharpness(self, metric_fn=sharpness_sum_lap2): + """Measure the sharpness of the camera's current view.""" + return metric_fn(self.microscope.camera.array(use_video_port=True, resize=(640,480)))