Blackened files

This commit is contained in:
Joel Collins 2019-11-06 22:07:16 +00:00
parent be5ddb76ae
commit 4e7606aa0c
24 changed files with 200 additions and 120 deletions

View file

@ -61,9 +61,7 @@ class MyPluginClass(MicroscopePlugin):
for _ in range(n_images):
# Create a data stream to capture to
output = self.microscope.camera.new_image(
temporary=False
)
output = self.microscope.camera.new_image(temporary=False)
# Capture a still image from the Pi camera, into the data stream
self.microscope.camera.capture(output.file, use_video_port=True)

View file

@ -39,10 +39,8 @@ class SnapshotAPI(MicroscopeView):
# Restart stream worker thread
self.microscope.camera.start_worker()
return Response(
self.microscope.camera.get_frame(),
mimetype="image/jpeg",
)
return Response(self.microscope.camera.get_frame(), mimetype="image/jpeg")
class StateAPI(MicroscopeView):
def get(self):
@ -240,7 +238,8 @@ def construct_blueprint(microscope_obj):
)
blueprint.add_url_rule(
"/snapshot", view_func=SnapshotAPI.as_view("snapshot", microscope=microscope_obj)
"/snapshot",
view_func=SnapshotAPI.as_view("snapshot", microscope=microscope_obj),
)
blueprint.add_url_rule(

View file

@ -134,12 +134,8 @@ def construct_blueprint(microscope_obj):
blueprint = Blueprint("task_blueprint", __name__)
blueprint.add_url_rule(
"/", view_func=TaskListAPI.as_view("task_list")
)
blueprint.add_url_rule("/", view_func=TaskListAPI.as_view("task_list"))
blueprint.add_url_rule(
"/<task_id>/", view_func=TaskAPI.as_view("task")
)
blueprint.add_url_rule("/<task_id>/", view_func=TaskAPI.as_view("task"))
return blueprint

View file

@ -120,15 +120,10 @@ class BaseCamera(metaclass=ABCMeta):
self.stream_timeout = 20
self.stream_timeout_enabled = False
self.state = {
"board": None
}
self.state = {"board": None}
# TODO: Load/save these to config
self.paths = {
"default": BASE_CAPTURE_PATH,
"temp": TEMP_CAPTURE_PATH
}
self.paths = {"default": BASE_CAPTURE_PATH, "temp": TEMP_CAPTURE_PATH}
# Capture data
self.images = []
@ -296,7 +291,7 @@ class BaseCamera(metaclass=ABCMeta):
output = CaptureObject(filepath=filepath)
# Insert a temporary tag if temporary
if temporary:
output.put_tags(['temporary'])
output.put_tags(["temporary"])
# Update capture list
self.images.append(output)
@ -339,7 +334,7 @@ class BaseCamera(metaclass=ABCMeta):
output = CaptureObject(filepath=filepath)
# Insert a temporary tag if temporary
if temporary:
output.put_tags(['temporary'])
output.put_tags(["temporary"])
# Update capture list
self.videos.append(output)

View file

@ -27,7 +27,7 @@ def pull_usercomment_dict(filepath):
try:
exif_dict = piexif.load(filepath)
except InvalidImageDataError:
logging.error("Invalid data at {}. Skipping.".format(filepath))
logging.warning("Invalid data at {}. Skipping.".format(filepath))
return None
if "Exif" in exif_dict and 37510 in exif_dict["Exif"]:
return yaml.load(exif_dict["Exif"][37510].decode())

View file

@ -16,7 +16,7 @@ import logging
# Type hinting
from typing import Tuple
from .base import BaseCamera, CaptureObject
from openflexure_microscope.camera.base import BaseCamera
# MAIN CLASS
@ -26,11 +26,9 @@ class MockStreamer(BaseCamera):
BaseCamera.__init__(self)
# Store state of PiCameraStreamer
self.state.update({
"stream_active": False,
"record_active": False,
"board": None
})
self.state.update(
{"stream_active": False, "record_active": False, "board": None}
)
# Update config properties
self.image_resolution = (1312, 976)
@ -57,7 +55,12 @@ class MockStreamer(BaseCamera):
)
draw = ImageDraw.Draw(image)
draw.text((20, 70), "Camera disconnected: {}".format(datetime.now().strftime("%d/%m/%Y, %H:%M:%S")))
draw.text(
(20, 70),
"Camera disconnected: {}".format(
datetime.now().strftime("%d/%m/%Y, %H:%M:%S")
),
)
image.save(self.stream, format="JPEG")
@ -80,12 +83,14 @@ class MockStreamer(BaseCamera):
conf_dict = BaseCamera.read_config(self)
# Include device-specific config items
conf_dict.update({
"stream_resolution": self.stream_resolution,
"image_resolution": self.image_resolution,
"numpy_resolution": self.numpy_resolution,
"jpeg_quality": self.jpeg_quality,
})
conf_dict.update(
{
"stream_resolution": self.stream_resolution,
"image_resolution": self.image_resolution,
"numpy_resolution": self.numpy_resolution,
"jpeg_quality": self.jpeg_quality,
}
)
return conf_dict
@ -179,7 +184,7 @@ class MockStreamer(BaseCamera):
with self.lock:
if isinstance(output, str):
output = open(output, 'wb')
output = open(output, "wb")
output.write(self.stream.getvalue())

View file

@ -66,12 +66,14 @@ class PiCameraStreamer(BaseCamera):
) #: :py:class:`picamera.PiCamera`: Picamera object
# Store state of PiCameraStreamer
self.state.update({
"stream_active": False,
"record_active": False,
"preview_active": False,
"board": f"picamera_{self.camera.revision}",
})
self.state.update(
{
"stream_active": False,
"record_active": False,
"preview_active": False,
"board": f"picamera_{self.camera.revision}",
}
)
# Reset variable states
self.set_zoom(1.0)
@ -113,13 +115,15 @@ class PiCameraStreamer(BaseCamera):
conf_dict = BaseCamera.read_config(self)
# Include device-specific config items
conf_dict.update({
"stream_resolution": self.stream_resolution,
"image_resolution": self.image_resolution,
"numpy_resolution": self.numpy_resolution,
"jpeg_quality": self.jpeg_quality,
"picamera_settings": {},
})
conf_dict.update(
{
"stream_resolution": self.stream_resolution,
"image_resolution": self.image_resolution,
"numpy_resolution": self.numpy_resolution,
"jpeg_quality": self.jpeg_quality,
"picamera_settings": {},
}
)
# Include a subset of picamera properties
# TODO: Expand this subset so we have more metadata?
@ -333,7 +337,7 @@ class PiCameraStreamer(BaseCamera):
return output
else:
logging.error(
logging.warning(
"Cannot start a new recording\
until the current recording has stopped."
)

View file

@ -10,5 +10,14 @@ __all__ = [
"ThreadTerminationError",
]
from .pool import tasks, states, current_task, update_task_progress, cleanup_tasks, remove_task, update_task_data, taskify
from .pool import (
tasks,
states,
current_task,
update_task_progress,
cleanup_tasks,
remove_task,
update_task_data,
taskify,
)
from .thread import ThreadTerminationError

View file

@ -43,6 +43,7 @@ class TaskMaster:
# Task management
def tasks():
"""
Dictionary of tasks in default taskmaster
@ -62,6 +63,7 @@ def states():
global _default_task_master
return _default_task_master.states
def cleanup_tasks():
global _default_task_master
return _default_task_master.cleanup()
@ -74,6 +76,7 @@ def remove_task(task_id: str):
# Operations on the current task
def current_task():
current_task_thread = threading.current_thread()
if not isinstance(current_task_thread, TaskThread):
@ -97,6 +100,7 @@ def update_task_data(data: dict):
# Main "taskify" functions
def taskify(f):
"""
A decorator that wraps the passed in function

View file

@ -11,7 +11,12 @@ from openflexure_microscope.api.v1.views import MicroscopeViewPlugin
from openflexure_microscope.api.utilities import JsonResponse
# Task management
from openflexure_microscope.common.tasks import current_task, update_task_progress, update_task_data, taskify
from openflexure_microscope.common.tasks import (
current_task,
update_task_progress,
update_task_data,
taskify,
)
# Exceptions
from openflexure_microscope.exceptions import TaskDeniedException

View file

@ -7,8 +7,9 @@ import pkg_resources
import uuid
from openflexure_microscope.stage.base import BaseStage
from openflexure_microscope.stage.mock import MockStage
from openflexure_microscope.camera.base import BaseCamera
from openflexure_microscope.camera.mock import MockStreamer
from openflexure_microscope.plugins import PluginMount
from openflexure_microscope.task import TaskOrchestrator
@ -141,6 +142,22 @@ class Microscope:
logging.info("Repopulating PluginMount...")
self.attach_plugins(self.plugin_maps)
def has_real_stage(self):
if hasattr(self, "stage") and not isinstance(
self.stage, MockStage
):
return True
else:
return False
def has_real_camera(self):
if hasattr(self, "camera") and not isinstance(
self.camera, MockStreamer
):
return True
else:
return False
# Create unified state
@property
def state(self):

View file

@ -6,7 +6,7 @@ from openflexure_microscope.devel import (
JsonResponse,
request,
jsonify,
taskify
taskify,
)

View file

@ -4,7 +4,7 @@ from openflexure_microscope.devel import (
JsonResponse,
request,
jsonify,
taskify
taskify,
)
import logging

View file

@ -4,7 +4,7 @@ from openflexure_microscope.devel import (
request,
jsonify,
abort,
taskify
taskify,
)
import logging

View file

@ -1,13 +1,13 @@
import time
import numpy as np
from typing import Tuple
from functools import reduce
import uuid
import itertools
import logging
from openflexure_microscope.camera.base import generate_basename
from openflexure_microscope.devel import MicroscopePlugin
from openflexure_microscope.devel import MicroscopePlugin, update_task_progress, update_task_data
from .api import TileScanAPI
@ -53,16 +53,26 @@ class ScanPlugin(MicroscopePlugin):
api_views = {"/tile": TileScanAPI}
def __init__(self):
self.images_to_be_captured: int = 1
update_task_data({"images_to_be_captured": self.images_to_be_captured})
@property
def progress(self):
progress = (self.images_captured_so_far / self.images_to_be_captured) * 100
logging.info(progress)
return progress
def capture(
self,
basename,
scan_id,
temporary: bool = False,
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = False,
metadata: dict = {},
tags: list = [],
self,
basename,
scan_id,
temporary: bool = False,
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = False,
metadata: dict = {},
tags: list = [],
):
# Construct a tile filename
@ -97,21 +107,26 @@ class ScanPlugin(MicroscopePlugin):
output.put_tags(tags)
def tile(
self,
basename: str = None,
temporary: bool = False,
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,
fast_autofocus=False,
metadata: dict = {},
tags: list = [],
self,
basename: str = None,
temporary: bool = False,
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,
fast_autofocus=False,
metadata: dict = {},
tags: list = [],
):
# Keep task progress
# TODO: Make this line not nasty
self.images_to_be_captured = reduce((lambda x, y: x * y), grid)
self.images_captured_so_far = 0
# Generate a basename if none given
if not basename:
basename = generate_basename()
@ -123,19 +138,24 @@ class ScanPlugin(MicroscopePlugin):
initial_position = self.microscope.stage.position
# Add scan metadata
if not "time" in metadata:
if "time" not in metadata:
metadata["time"] = generate_basename()
# Check if autofocus is enabled
if autofocus_dz and hasattr(self.microscope.plugin, "default_autofocus"):
if (
autofocus_dz
and hasattr(self.microscope.plugin, "default_autofocus")
and self.microscope.has_real_stage()
and self.microscope.has_real_camera()
):
autofocus_enabled = True
else:
autofocus_enabled = False
if fast_autofocus and not hasattr(
self.microscope.plugin.default_autofocus, "monitor_sharpness"
self.microscope.plugin.default_autofocus, "monitor_sharpness"
):
logging.error(
logging.warning(
"Can't use fast autofocus in the scan - the default plugin doesn't support monitor_sharpness; maybe it is too old?"
)
fast_autofocus = False
@ -198,6 +218,9 @@ class ScanPlugin(MicroscopePlugin):
metadata=metadata,
tags=tags,
)
# Update task progress
self.images_captured_so_far += 1
update_task_progress(self.progress)
else:
logging.debug("Entering z-stack")
self.stack(
@ -218,7 +241,7 @@ class ScanPlugin(MicroscopePlugin):
next_z = self.microscope.stage.position[2]
if fast_autofocus:
next_z += (
autofocus_dz / 2
autofocus_dz / 2
) # Fast autofocus requires us to start at the top of the range
if grid[2] > 1:
next_z -= int(
@ -229,19 +252,19 @@ class ScanPlugin(MicroscopePlugin):
self.microscope.stage.move_abs(initial_position)
def stack(
self,
basename: str = None,
temporary: bool = False,
scan_id: str = None,
step_size: int = 100,
steps: int = 5,
center: bool = True,
return_to_start: bool = True,
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = False,
metadata: dict = {},
tags: list = [],
self,
basename: str = None,
temporary: bool = False,
scan_id: str = None,
step_size: int = 100,
steps: int = 5,
center: bool = True,
return_to_start: bool = True,
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = False,
metadata: dict = {},
tags: list = [],
):
# Generate a basename if none given
@ -278,6 +301,9 @@ class ScanPlugin(MicroscopePlugin):
metadata=metadata,
tags=tags,
)
# Update task progress
self.images_captured_so_far += 1
update_task_progress(self.progress)
if i != steps - 1:
logging.debug("Moving z by {}".format(step_size))

View file

@ -87,7 +87,7 @@ def load_plugin_module(plugin_path):
plugin_module = importlib.import_module(plugin_path)
plugin_name = name_from_module(plugin_path)
except Exception as e:
logging.error("Error loading plugin.")
logging.warning("Error loading plugin.")
logging.error(e)
return None, None
@ -216,7 +216,7 @@ class PluginMount(object):
)
else:
logging.error(f"Error loading plugin {plugin_map}. Moving on.")
logging.warning(f"Error loading plugin {plugin_map}. Moving on.")
class MicroscopePlugin:

View file

@ -3,7 +3,7 @@ from openflexure_microscope.devel import (
JsonResponse,
request,
jsonify,
taskify
taskify,
)
import logging

View file

@ -72,7 +72,7 @@ class ExamplePlugin(MicroscopePlugin):
time.sleep(1)
# Update task progress (if running as a task)
percent_complete = int(((t + 1)/run_time)*100)
percent_complete = int(((t + 1) / run_time) * 100)
update_task_progress(percent_complete)
return vals

View file

@ -1,3 +1,4 @@
import numpy as np
from abc import ABCMeta, abstractmethod
from openflexure_microscope.common.lock import StrictLock

View file

@ -5,6 +5,7 @@ from collections.abc import Iterable
import numpy as np
import time
class MockStage(BaseStage):
def __init__(self, port=None, **kwargs):
BaseStage.__init__(self)
@ -68,7 +69,9 @@ class MockStage(BaseStage):
else:
self._backlash = np.array([int(blsh)] * self.n_axes, dtype=np.int)
def move_rel(self, displacement: list, axis=None, backlash=True, simulate_time: bool=True):
def move_rel(
self, displacement: list, axis=None, backlash=True, simulate_time: bool = True
):
if simulate_time:
time.sleep(1)
if axis is not None:
@ -81,7 +84,7 @@ class MockStage(BaseStage):
self._position = list(np.array(self._position) + np.array(move))
def move_abs(self, final, simulate_time: bool=True, **kwargs):
def move_abs(self, final, simulate_time: bool = True, **kwargs):
if simulate_time:
time.sleep(1)

View file

@ -136,7 +136,7 @@ class Sangaboard(ExtensibleSerialInstrument):
# make sure we close the serial port cleanly (otherwise it hangs open).
self.close()
logging.error(e)
logging.error(
logging.warning(
"You may need to update the firmware running on the Sangaboard."
)
raise e
@ -164,12 +164,12 @@ class Sangaboard(ExtensibleSerialInstrument):
r"OpenFlexure Motor Board v(([\d]+)(?:\.([\d]+))+)", self.firmware
)
if not match:
logging.error(
logging.warning(
'Version string "{}" not recognised.'.format(self.firmware)
)
return False
else:
logging.error("No firmware version string was returned.")
logging.warning("No firmware version string was returned.")
return False
# Check for matching/valid version number
@ -179,7 +179,7 @@ class Sangaboard(ExtensibleSerialInstrument):
self.firmware_version = match.group(1)
if version_tuple not in Sangaboard.valid_firmwares:
logging.error(
logging.warning(
"This version of the Python module requires firmware v0.5 (with legacy support for v0.4)"
)
return False

View file

@ -1,5 +1,11 @@
import logging
from openflexure_microscope.common.tasks import tasks, states, taskify, cleanup_tasks, remove_task
from openflexure_microscope.common.tasks import (
tasks,
states,
taskify,
cleanup_tasks,
remove_task,
)
class TaskOrchestrator:
@ -14,7 +20,9 @@ class TaskOrchestrator:
@property
def tasks(self):
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.tasks().values() instead.")
logging.warning(
"TaskOrchestrator class is deprecated. Please use common.tasks.tasks().values() instead."
)
return tasks().values()
@property
@ -22,7 +30,9 @@ class TaskOrchestrator:
"""
Returns a list of dictionary representations of all tasks in the session.
"""
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.tasks() instead.")
logging.warning(
"TaskOrchestrator class is deprecated. Please use common.tasks.tasks() instead."
)
return states()
def task_from_id(self, task_id: str):
@ -41,7 +51,9 @@ class TaskOrchestrator:
function (function): The target function to run in the background
args, kwargs: Arguments that will be passed to `function` at runtime.
"""
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.taskify() instead.")
logging.warning(
"TaskOrchestrator class is deprecated. Please use common.tasks.taskify() instead."
)
return taskify(function)(*args, **kwargs)
def clean(self):
@ -49,12 +61,16 @@ class TaskOrchestrator:
Remove all inactive tasks from the task array.
This will remove tasks that either finished or never started.
"""
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.cleanup_tasks() instead.")
logging.warning(
"TaskOrchestrator class is deprecated. Please use common.tasks.cleanup_tasks() instead."
)
cleanup_tasks()
def delete(self, task_id: str):
"""
Delete a given task by ID, only if task is not currently running.
"""
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.remove_task() instead.")
logging.warning(
"TaskOrchestrator class is deprecated. Please use common.tasks.remove_task() instead."
)
remove_task(task_id)

View file

@ -30,7 +30,9 @@ class TestCaptureMethods(unittest.TestCase):
# Capture to a context (auto-deletes files when done)
with camera.new_image() as output:
camera.capture(output.file, use_video_port=use_video_port, resize=resize)
camera.capture(
output.file, use_video_port=use_video_port, resize=resize
)
# Ensure file deletion fails and returns False
self.assertFalse(output.delete_file())