Blackened everything
This commit is contained in:
parent
e213647217
commit
5966ce29be
57 changed files with 1938 additions and 1414 deletions
|
|
@ -1,2 +1,2 @@
|
|||
__all__ = ['AutofocusPlugin']
|
||||
from .plugin import AutofocusPlugin
|
||||
__all__ = ["AutofocusPlugin"]
|
||||
from .plugin import AutofocusPlugin
|
||||
|
|
|
|||
|
|
@ -1,18 +1,24 @@
|
|||
import numpy as np
|
||||
import logging
|
||||
|
||||
from openflexure_microscope.devel import MicroscopeViewPlugin, JsonResponse, request, jsonify
|
||||
from openflexure_microscope.devel import (
|
||||
MicroscopeViewPlugin,
|
||||
JsonResponse,
|
||||
request,
|
||||
jsonify,
|
||||
)
|
||||
|
||||
|
||||
class MeasureSharpnessAPI(MicroscopeViewPlugin):
|
||||
def post(self):
|
||||
payload = JsonResponse(request)
|
||||
return jsonify({'sharpness': self.plugin.measure_sharpness()})
|
||||
return jsonify({"sharpness": self.plugin.measure_sharpness()})
|
||||
|
||||
|
||||
class AutofocusAPI(MicroscopeViewPlugin):
|
||||
def post(self):
|
||||
payload = JsonResponse(request)
|
||||
|
||||
|
||||
# Figure out the range of z values to use
|
||||
dz = payload.param("dz", default=np.linspace(-300, 300, 7), convert=np.array)
|
||||
|
||||
|
|
@ -22,10 +28,11 @@ class AutofocusAPI(MicroscopeViewPlugin):
|
|||
# return a handle on the autofocus task
|
||||
return jsonify(task.state), 202
|
||||
|
||||
|
||||
class FastAutofocusAPI(MicroscopeViewPlugin):
|
||||
def post(self):
|
||||
payload = JsonResponse(request)
|
||||
|
||||
|
||||
# Figure out the parameters to use
|
||||
dz = payload.param("dz", default=2000, convert=int)
|
||||
backlash = payload.param("backlash", default=0, convert=int)
|
||||
|
|
@ -33,7 +40,9 @@ class FastAutofocusAPI(MicroscopeViewPlugin):
|
|||
backlash = 0
|
||||
|
||||
logging.info("Running autofocus...")
|
||||
task = self.microscope.task.start(self.plugin.fast_autofocus, dz, backlash=backlash)
|
||||
task = self.microscope.task.start(
|
||||
self.plugin.fast_autofocus, dz, backlash=backlash
|
||||
)
|
||||
|
||||
# return a handle on the autofocus task
|
||||
return jsonify(task.state), 202
|
||||
return jsonify(task.state), 202
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import threading
|
|||
import logging
|
||||
from scipy import ndimage
|
||||
|
||||
class JPEGSharpnessMonitor():
|
||||
|
||||
class JPEGSharpnessMonitor:
|
||||
def __init__(self, microscope, timeout=60):
|
||||
self.microscope = microscope
|
||||
self.camera = microscope.camera
|
||||
|
|
@ -17,32 +18,34 @@ class JPEGSharpnessMonitor():
|
|||
self.timeout = timeout
|
||||
self.keep_alive()
|
||||
self.background_thread = None
|
||||
|
||||
|
||||
def is_alive(self):
|
||||
if self.background_thread is None:
|
||||
return False
|
||||
else:
|
||||
return self.background_thread.is_alive()
|
||||
|
||||
|
||||
def should_stop(self):
|
||||
import time
|
||||
|
||||
return time.time() - self.kept_alive > self.timeout
|
||||
|
||||
|
||||
def keep_alive(self):
|
||||
import time
|
||||
|
||||
self.kept_alive = time.time()
|
||||
|
||||
|
||||
def start(self):
|
||||
"Start monitoring sharpness by looking at JPEG size"
|
||||
self.background_thread = threading.Thread(target=self._measure_jpegs)
|
||||
self.background_thread.start()
|
||||
return self
|
||||
|
||||
|
||||
def stop(self):
|
||||
"Stop the background thread"
|
||||
self.stop_event.set()
|
||||
self.background_thread.join()
|
||||
|
||||
|
||||
def _measure_jpegs(self):
|
||||
"Function that runs in a background thread to record sharpness"
|
||||
logging.info("Starting sharpness measurement in background thread")
|
||||
|
|
@ -59,7 +62,6 @@ class JPEGSharpnessMonitor():
|
|||
"""Return the size of a frame from the MJPEG stream"""
|
||||
return len(self.camera.get_frame())
|
||||
|
||||
|
||||
def focus_rel(self, dz, backlash=False, **kwargs):
|
||||
self.keep_alive()
|
||||
self.stage_times.append(time.time())
|
||||
|
|
@ -69,7 +71,7 @@ class JPEGSharpnessMonitor():
|
|||
self.stage_positions.append(self.stage.position)
|
||||
i = len(self.stage_positions) - 2
|
||||
return i, self.stage_positions[-1][2]
|
||||
|
||||
|
||||
def move_data(self, istart, istop=None):
|
||||
"Extract sharpness as a function of (interpolated) z"
|
||||
global np, logging
|
||||
|
|
@ -92,18 +94,21 @@ class JPEGSharpnessMonitor():
|
|||
"""Return the z position of the sharpest image on a given move"""
|
||||
jt, jz, js = self.move_data(index)
|
||||
return jz[np.argmax(js)]
|
||||
|
||||
|
||||
def data_dict(self):
|
||||
"""Return the gathered data as a single convenient dictionary"""
|
||||
data = {}
|
||||
for k in ['jpeg_times', 'jpeg_sizes', 'stage_times', 'stage_positions']:
|
||||
for k in ["jpeg_times", "jpeg_sizes", "stage_times", "stage_positions"]:
|
||||
data[k] = getattr(self, k)
|
||||
return data
|
||||
|
||||
|
||||
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), ...]
|
||||
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):
|
||||
|
|
@ -111,12 +116,14 @@ def sharpness_sum_lap2(rgb_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)
|
||||
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]])
|
||||
edge = np.array([[-1] * n + [1] * n])
|
||||
return np.sum(
|
||||
[np.sum(ndimage.filters.convolve(gray, W) ** 2) for W in [edge, edge.T]]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,15 +10,16 @@ from .api import MeasureSharpnessAPI, AutofocusAPI, FastAutofocusAPI
|
|||
|
||||
from openflexure_microscope.devel import MicroscopePlugin
|
||||
|
||||
|
||||
class AutofocusPlugin(MicroscopePlugin):
|
||||
"""
|
||||
Basic autofocus plugin
|
||||
"""
|
||||
|
||||
api_views = {
|
||||
'/measure_sharpness': MeasureSharpnessAPI,
|
||||
'/autofocus': AutofocusAPI,
|
||||
'/fast_autofocus': FastAutofocusAPI,
|
||||
"/measure_sharpness": MeasureSharpnessAPI,
|
||||
"/autofocus": AutofocusAPI,
|
||||
"/fast_autofocus": FastAutofocusAPI,
|
||||
}
|
||||
|
||||
### SLOW AUTOFOCUS
|
||||
|
|
@ -70,21 +71,24 @@ class AutofocusPlugin(MicroscopePlugin):
|
|||
m.focus_rel(dz)
|
||||
return m.sharpest_z_on_move(0)
|
||||
|
||||
|
||||
def fast_autofocus(self, dz=2000, backlash=None):
|
||||
"""Perform a down-up-down-up autofocus"""
|
||||
with self.monitor_sharpness() as m:
|
||||
i, z = m.focus_rel(-dz/2)
|
||||
i, z = m.focus_rel(-dz / 2)
|
||||
i, z = m.focus_rel(dz)
|
||||
fz = m.sharpest_z_on_move(i)
|
||||
if backlash is None:
|
||||
i, z = m.focus_rel(-dz) # move all the way to the start so it's consistent
|
||||
i, z = m.focus_rel(
|
||||
-dz
|
||||
) # move all the way to the start so it's consistent
|
||||
else:
|
||||
i, z = m.focus_rel(fz - z - backlash)
|
||||
m.focus_rel(fz - z)
|
||||
return m.data_dict()
|
||||
|
||||
def fast_up_down_up_autofocus(self, dz=2000, target_z=0, initial_move_up=True, mini_backlash=150):
|
||||
def fast_up_down_up_autofocus(
|
||||
self, dz=2000, target_z=0, initial_move_up=True, mini_backlash=150
|
||||
):
|
||||
"""Autofocus by measuring on the way down, and moving back up with feedback.
|
||||
|
||||
This autofocus method is very efficient, as it only passes the peak once.
|
||||
|
|
@ -124,32 +128,41 @@ class AutofocusPlugin(MicroscopePlugin):
|
|||
# Ensure the MJPEG stream has started
|
||||
self.microscope.camera.start_stream_recording()
|
||||
|
||||
df = dz #TODO: refactor so I actually use dz in the code below!
|
||||
df = dz # TODO: refactor so I actually use dz in the code below!
|
||||
if initial_move_up:
|
||||
m.focus_rel(df/2)
|
||||
m.focus_rel(df / 2)
|
||||
# move down
|
||||
i, z = m.focus_rel(-df)
|
||||
# now inspect where the sharpest point is, and estimate the sharpness
|
||||
# (JPEG size) that we should find at the start of the Z stack
|
||||
jt, jz, js = m.move_data(i)
|
||||
best_z = jz[np.argmax(js)]
|
||||
target_s = np.interp([best_z+target_z], jz[::-1], js[::-1]) #NB jz is decreasing
|
||||
target_s = np.interp(
|
||||
[best_z + target_z], jz[::-1], js[::-1]
|
||||
) # NB jz is decreasing
|
||||
|
||||
# now move to the start of the z stack
|
||||
i, z = m.focus_rel(best_z + target_z - z + mini_backlash) # takes us to the start of the stack
|
||||
i, z = m.focus_rel(
|
||||
best_z + target_z - z + mini_backlash
|
||||
) # takes us to the start of the stack
|
||||
|
||||
# We've deliberately undershot - figure out how much further we should move based on the curve
|
||||
current_js = m.jpeg_size()
|
||||
imax = np.argmax(js) # we want to crop out just the bit below the peak
|
||||
js = js[imax:] # NB z is in DECREASING order
|
||||
imax = np.argmax(js) # we want to crop out just the bit below the peak
|
||||
js = js[imax:] # NB z is in DECREASING order
|
||||
jz = jz[imax:]
|
||||
inow = np.argmax(js < current_js) # use the curve we recorded to estimate our position
|
||||
inow = np.argmax(
|
||||
js < current_js
|
||||
) # use the curve we recorded to estimate our position
|
||||
# TODO: fancy interpolation stuff
|
||||
|
||||
# So, the Z position corresponding to our current sharpness value is zs[inow]
|
||||
# That means we should move forwards, by best_z - zs[inow]
|
||||
correction_move = best_z + target_z - jz[inow]
|
||||
logging.debug("Fast autofocus scan: correcting backlash by moving {} steps".format(correction_move))
|
||||
logging.debug(
|
||||
"Fast autofocus scan: correcting backlash by moving {} steps".format(
|
||||
correction_move
|
||||
)
|
||||
)
|
||||
m.focus_rel(correction_move)
|
||||
return m.data_dict()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue