Reimplemented serialising LST
This commit is contained in:
parent
ccd5e9b891
commit
cf66359559
5 changed files with 48 additions and 67 deletions
|
|
@ -146,10 +146,6 @@ class BaseCamera(metaclass=ABCMeta):
|
||||||
"""Return the current settings as a dictionary"""
|
"""Return the current settings as a dictionary"""
|
||||||
return {"paths": self.paths}
|
return {"paths": self.paths}
|
||||||
|
|
||||||
def save_settings(self):
|
|
||||||
"""(Optional) Save any settings to disk that need to be stored"""
|
|
||||||
return
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
"""Create camera on context enter."""
|
"""Create camera on context enter."""
|
||||||
return self
|
return self
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,11 @@ from .base import BaseCamera, CaptureObject
|
||||||
from .set_picamera_gain import set_analog_gain, set_digital_gain
|
from .set_picamera_gain import set_analog_gain, set_digital_gain
|
||||||
|
|
||||||
from openflexure_microscope.paths import settings_file_path
|
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
|
# MAIN CLASS
|
||||||
|
|
@ -154,9 +158,6 @@ class PiCameraStreamer(BaseCamera):
|
||||||
"image_resolution": self.image_resolution,
|
"image_resolution": self.image_resolution,
|
||||||
"numpy_resolution": self.numpy_resolution,
|
"numpy_resolution": self.numpy_resolution,
|
||||||
"jpeg_quality": self.jpeg_quality,
|
"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": {},
|
"picamera": {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -170,12 +171,16 @@ class PiCameraStreamer(BaseCamera):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logging.debug("Unable to read PiCamera attribute {}".format(key))
|
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):
|
return conf_dict
|
||||||
"""Save lens-shading table to disk"""
|
|
||||||
logging.info("Saving picamera_lst to {}".format(self.picamera_lst_path))
|
|
||||||
self.save_lens_shading_table()
|
|
||||||
|
|
||||||
def update_settings(self, config: dict):
|
def update_settings(self, config: dict):
|
||||||
"""
|
"""
|
||||||
|
|
@ -209,22 +214,23 @@ class PiCameraStreamer(BaseCamera):
|
||||||
config["picamera"], pause_for_effect=True
|
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
|
# PiCameraStreamer parameters
|
||||||
for key, value in config.items(): # For each provided setting
|
for key, value in config.items(): # For each provided setting
|
||||||
if (key != "picamera") and hasattr(self, key):
|
if (key != "picamera") and hasattr(self, key):
|
||||||
setattr(self, key, value)
|
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 stream was paused to update config, unpause
|
||||||
if paused_stream:
|
if paused_stream:
|
||||||
logging.info("Resuming stream.")
|
logging.info("Resuming stream.")
|
||||||
|
|
@ -293,41 +299,6 @@ class PiCameraStreamer(BaseCamera):
|
||||||
if pause_for_effect:
|
if pause_for_effect:
|
||||||
time.sleep(0.2)
|
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:
|
def set_zoom(self, zoom_value: float = 1.0) -> None:
|
||||||
"""
|
"""
|
||||||
Change the camera zoom, handling re-centering and scaling.
|
Change the camera zoom, handling re-centering and scaling.
|
||||||
|
|
|
||||||
|
|
@ -205,10 +205,6 @@ class Microscope:
|
||||||
# Read curent config
|
# Read curent config
|
||||||
current_config = self.read_settings()
|
current_config = self.read_settings()
|
||||||
# Save config to file
|
# 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)
|
self.settings_file.save(current_config, backup=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,6 @@ class BaseStage(metaclass=ABCMeta):
|
||||||
"""Return the current settings as a dictionary"""
|
"""Return the current settings as a dictionary"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def save_settings(self):
|
|
||||||
"""(Optional) Save any settings to disk that need to be stored"""
|
|
||||||
return
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def state(self):
|
def state(self):
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import operator
|
||||||
import base64
|
import base64
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import logging
|
||||||
from collections import abc
|
from collections import abc
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
@ -21,6 +22,27 @@ def serialise_array_b64(npy_arr):
|
||||||
return b64_string, dtype, shape
|
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
|
@contextmanager
|
||||||
def set_properties(obj, **kwargs):
|
def set_properties(obj, **kwargs):
|
||||||
"""A context manager to set, then reset, certain properties of an object.
|
"""A context manager to set, then reset, certain properties of an object.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue