From cf66359559034e1e72e2628cda9e894a4d574030 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 6 Feb 2020 13:35:05 +0000 Subject: [PATCH] Reimplemented serialising LST --- openflexure_microscope/camera/base.py | 4 -- openflexure_microscope/camera/pi.py | 81 +++++++++------------------ openflexure_microscope/microscope.py | 4 -- openflexure_microscope/stage/base.py | 4 -- openflexure_microscope/utilities.py | 22 ++++++++ 5 files changed, 48 insertions(+), 67 deletions(-) diff --git a/openflexure_microscope/camera/base.py b/openflexure_microscope/camera/base.py index 2dd16dc4..f74bbc0a 100644 --- a/openflexure_microscope/camera/base.py +++ b/openflexure_microscope/camera/base.py @@ -146,10 +146,6 @@ class BaseCamera(metaclass=ABCMeta): """Return the current settings as a dictionary""" return {"paths": self.paths} - def save_settings(self): - """(Optional) Save any settings to disk that need to be stored""" - return - def __enter__(self): """Create camera on context enter.""" return self diff --git a/openflexure_microscope/camera/pi.py b/openflexure_microscope/camera/pi.py index 0f23d5f3..990767d0 100644 --- a/openflexure_microscope/camera/pi.py +++ b/openflexure_microscope/camera/pi.py @@ -47,7 +47,11 @@ from .base import BaseCamera, CaptureObject from .set_picamera_gain import set_analog_gain, set_digital_gain from openflexure_microscope.paths import settings_file_path -from openflexure_microscope.utilities import serialise_array_b64 +from openflexure_microscope.utilities import ( + serialise_array_b64, + ndarray_to_json, + json_to_ndarray, +) # MAIN CLASS @@ -154,9 +158,6 @@ class PiCameraStreamer(BaseCamera): "image_resolution": self.image_resolution, "numpy_resolution": self.numpy_resolution, "jpeg_quality": self.jpeg_quality, - "picamera_lst_path": self.picamera_lst_path - if (self.picamera_lst_path and os.path.isfile(self.picamera_lst_path)) - else None, "picamera": {}, } ) @@ -170,12 +171,16 @@ class PiCameraStreamer(BaseCamera): except AttributeError: logging.debug("Unable to read PiCamera attribute {}".format(key)) - return conf_dict + # Include a serialised lens shading table + if ( + hasattr(self.camera, "lens_shading_table") + and getattr(self.camera, "lens_shading_table") is not None + ): + conf_dict["picamera"]["lens_shading_table"] = ndarray_to_json( + getattr(self.camera, "lens_shading_table") + ) - def save_settings(self): - """Save lens-shading table to disk""" - logging.info("Saving picamera_lst to {}".format(self.picamera_lst_path)) - self.save_lens_shading_table() + return conf_dict def update_settings(self, config: dict): """ @@ -209,22 +214,23 @@ class PiCameraStreamer(BaseCamera): config["picamera"], pause_for_effect=True ) + # Handle lens shading if camera supports it + if ( + hasattr(self.camera, "lens_shading_table") + and "lens_shading_table" in config["picamera"] + ): + try: + self.camera.lens_shading_table = json_to_ndarray( + config["picamera"].get("lens_shading_table") + ) + except KeyError as e: + logging.error(e) + # PiCameraStreamer parameters for key, value in config.items(): # For each provided setting if (key != "picamera") and hasattr(self, key): setattr(self, key, value) - # Handle lens shading if camera supports it - if ("picamera_lst_path" in config) and hasattr( - self.camera, "lens_shading_table" - ): - logging.debug( - "Applying lens_shading_table from file: {}".format( - config["picamera_lst_path"] - ) - ) - self.apply_lens_shading_table(config["picamera_lst_path"]) - # If stream was paused to update config, unpause if paused_stream: logging.info("Resuming stream.") @@ -293,41 +299,6 @@ class PiCameraStreamer(BaseCamera): if pause_for_effect: time.sleep(0.2) - def read_lens_shading_table(self): - """ - Read the current lens shading table as a numpy array, if it exists. Return None otherwise. - """ - if hasattr(self.camera, "lens_shading_table"): - return self.camera.lens_shading_table - else: - return None - - def save_lens_shading_table(self): - """ - Save the current lens shading table to an .npy file, if it exists. - """ - logging.debug(self.read_lens_shading_table()) - if self.read_lens_shading_table() is not None: - np.save(self.picamera_lst_path, self.read_lens_shading_table()) - else: - logging.warning("Unable to save a nonexistant lens shading table") - - def apply_lens_shading_table(self, lst_array_or_path): - """ - Apply a lens shading table from an .npy file, or numpy array. - - Args: - lst_array_or_path: Numpy array, or path to .npy file, describing the lens-shading table - """ - if isinstance(lst_array_or_path, np.ndarray): - self.camera.lens_shading_table = lst_array_or_path - elif (type(lst_array_or_path) == str) and os.path.isfile(lst_array_or_path): - self.camera.lens_shading_table = np.load(lst_array_or_path) - else: - logging.error( - "Unsupported or missing data for camera lens_shading_table. Must be numpy ndarray, or .npy file path string. Skipping." - ) - def set_zoom(self, zoom_value: float = 1.0) -> None: """ Change the camera zoom, handling re-centering and scaling. diff --git a/openflexure_microscope/microscope.py b/openflexure_microscope/microscope.py index afdb4d22..b1d8d00d 100644 --- a/openflexure_microscope/microscope.py +++ b/openflexure_microscope/microscope.py @@ -205,10 +205,6 @@ class Microscope: # Read curent config current_config = self.read_settings() # Save config to file - if self.camera: - self.camera.save_settings() - if self.stage: - self.stage.save_settings() self.settings_file.save(current_config, backup=True) @property diff --git a/openflexure_microscope/stage/base.py b/openflexure_microscope/stage/base.py index 564ba1a6..b04909b2 100644 --- a/openflexure_microscope/stage/base.py +++ b/openflexure_microscope/stage/base.py @@ -23,10 +23,6 @@ class BaseStage(metaclass=ABCMeta): """Return the current settings as a dictionary""" pass - def save_settings(self): - """(Optional) Save any settings to disk that need to be stored""" - return - @property @abstractmethod def state(self): diff --git a/openflexure_microscope/utilities.py b/openflexure_microscope/utilities.py index bd88664e..27e46212 100644 --- a/openflexure_microscope/utilities.py +++ b/openflexure_microscope/utilities.py @@ -4,6 +4,7 @@ import operator import base64 from uuid import UUID import numpy as np +import logging from collections import abc from functools import reduce from contextlib import contextmanager @@ -21,6 +22,27 @@ def serialise_array_b64(npy_arr): return b64_string, dtype, shape +def ndarray_to_json(arr: np.ndarray): + b64_string, dtype, shape = serialise_array_b64(arr) + return { + "@type": "ndarray", + "dtype": dtype, + "shape": shape, + "base64": b64_string + } + + +def json_to_ndarray(json_dict: dict): + if not json_dict.get("@type") != "ndarray": + logging.warning("No valid @type attribute found. Conversion may fail.") + for required_param in ("dtype", "shape", "base64"): + if not json_dict.get(required_param): + raise KeyError(f"Missing required key {required_param}") + + return deserialise_array_b64(json_dict.get("base64"), json_dict.get("dtype"), json_dict.get("shape")) + + + @contextmanager def set_properties(obj, **kwargs): """A context manager to set, then reset, certain properties of an object.