Started updating documentation

This commit is contained in:
Joel Collins 2020-01-15 15:03:58 +00:00
parent be74b92bd7
commit 802f5ba0c2
17 changed files with 248 additions and 368 deletions

View file

@ -3,14 +3,14 @@
"""
Raspberry Pi camera implementation of the PiCameraStreamer class.
NOTES:
**NOTES:**
Still port used for image capture.
Preview port reserved for onboard GPU preview.
Video port:
* Splitter port 0: Image capture (if use_video_port == True)
* Splitter port 0: Image capture (if `use_video_port == True`)
* Splitter port 1: Streaming frames
* Splitter port 2: Video capture
* Splitter port 3: [Currently unused]
@ -237,6 +237,12 @@ class PiCameraStreamer(BaseCamera):
def apply_picamera_settings(
self, settings_dict: dict, pause_for_effect: bool = True
):
"""
Args:
settings_dict (dict): Dictionary of properties to apply to the :py:class:`picamera.PiCamera`: object
pause_for_effect (bool): Pause tactically to reduce risk of timing issues
"""
# Set exposure mode
if "exposure_mode" in settings_dict:
logging.debug(
@ -307,7 +313,10 @@ class PiCameraStreamer(BaseCamera):
def apply_lens_shading_table(self, lst_array_or_path):
"""
Apply a lens shading table from an .npy file, or numpy array.
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
@ -530,10 +539,13 @@ class PiCameraStreamer(BaseCamera):
Args:
output: String or file-like object to write capture data to
use_video_port (bool): Capture from the video port used for streaming. Lower resolution, faster.
fmt (str): Format of the capture.
use_video_port (bool): Capture from the video port used for streaming. Lower resolution, faster.
resize ((int, int)): Resize the captured image.
bayer (bool): Store raw bayer data in capture
Returns:
output_object (str/BytesIO): Target object.
"""
with self.lock:
@ -566,6 +578,9 @@ class PiCameraStreamer(BaseCamera):
Args:
use_video_port (bool): Capture from the video port used for streaming. Lower resolution, faster.
resize ((int, int)): Resize the captured image.
Returns:
output_array (np.ndarray): Output array of capture
"""
with self.lock:
if use_video_port:
@ -603,6 +618,9 @@ class PiCameraStreamer(BaseCamera):
Args:
use_video_port (bool): Capture from the video port used for streaming. Lower resolution, faster.
resize ((int, int)): Resize the captured image.
Returns:
output_array (np.ndarray): Output array of capture
"""
with self.lock:
if use_video_port:

View file

@ -18,6 +18,67 @@ Attributes:
"""
class OpenflexureSettingsFile:
"""
An object to handle expansion, conversion, and saving of the microscope configuration.
Args:
config_path (str): Path to the config JSON file (None falls back to default location)
expand (bool): Expand paths to valid auxillary config files.
"""
def __init__(self, config_path: str = None):
global DEFAULT_CONFIG, USER_CONFIG_FILE_PATH
# Set arguments
self.config_path = config_path or USER_CONFIG_FILE_PATH
# Initialise basic config file with defaults if it doesn't exist
initialise_file(self.config_path, populate=DEFAULT_CONFIG)
def load(self) -> dict:
"""
Loads settings from a file on-disk.
"""
# Unexpanded config dictionary (used at load/save time)
loaded_config = load_json_file(self.config_path)
logging.debug("Reading settings from disk")
return loaded_config
def save(self, config: dict, backup: bool = True):
"""
Save settings to a file on-disk.
Args:
config (dict): Dictionary of new settings
backup (bool): Back up previous settings file
"""
save_settings = config
if backup:
if os.path.isfile(self.config_path):
shutil.copyfile(self.config_path, self.config_path + ".bk")
logging.debug("Saving settings dictionary to disk")
save_json_file(self.config_path, save_settings)
def merge(self, config: dict) -> dict:
"""
Merge settings dictionary with settings loaded from file on-disk.
Args:
config (dict): Dictionary of new settings
"""
logging.debug("Merging settings with file on disk")
settings = self.load()
settings.update(config)
return settings
class JSONEncoder(json.JSONEncoder):
"""
A custom JSON encoder, with type conversions for PiCamera fractions, Numpy integers, and Numpy arrays
@ -45,62 +106,8 @@ class JSONEncoder(json.JSONEncoder):
return str(o)
# MAIN CONFIG CLASS
class OpenflexureSettingsFile:
"""
An object to handle expansion, conversion, and saving of the microscope configuration.
Args:
config_path (str): Path to the config JSON file (None falls back to default location)
expand (bool): Expand paths to valid auxillary config files.
"""
def __init__(self, config_path: str = None):
global DEFAULT_CONFIG, USER_CONFIG_FILE_PATH
# Set arguments
self.config_path = config_path or USER_CONFIG_FILE_PATH
# Initialise basic config file with defaults if it doesn't exist
initialise_file(self.config_path, populate=DEFAULT_CONFIG)
def load(self):
"""
Loads config from a file on-disk, and expands auxillary config files if available.
"""
# Unexpanded config dictionary (used at load/save time)
loaded_config = load_json_file(self.config_path)
logging.debug("Reading settings from disk")
return loaded_config
def save(self, config: dict, backup: bool = True):
"""
Save config to a file on-disk, and splits into auxillary config files if available.
"""
save_settings = config
if backup:
if os.path.isfile(self.config_path):
shutil.copyfile(self.config_path, self.config_path + ".bk")
logging.debug("Saving settings dictionary to disk")
save_json_file(self.config_path, save_settings)
def merge(self, config: dict, backup: bool = True):
logging.debug("Merging settings with file on disk")
settings = self.load()
settings.update(config)
return settings
# HANDLE BASIC LOADING AND SAVING OF SETTINGS FILES
def load_json_file(config_path) -> dict:
"""
Open a .json config file
@ -141,6 +148,12 @@ def save_json_file(config_path: str, config_dict: dict):
def create_file(config_path):
"""
Creates an empty file, and all folder structure currently nonexistant.
Args:
config_path: Path to the (possibly) new file
"""
if not os.path.exists(os.path.dirname(config_path)):
try:
os.makedirs(os.path.dirname(config_path))
@ -181,15 +194,20 @@ def settings_file_path(filename: str):
# HANDLE THE DEFAULT CONFIGURATION FILE
HERE = os.path.abspath(os.path.dirname(__file__))
#: Path of default (first-run) microscope settings
DEFAULT_CONFIG_FILE_PATH = os.path.join(HERE, "microscope_settings.default.json")
#: Path of microscope settings directory
USER_CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".openflexure")
#: Path of microscope settings directory
USER_CONFIG_FILE_PATH = os.path.join(USER_CONFIG_DIR, "microscope_settings.json")
#: Path of microscope extensions directory
USER_EXTENSIONS_PATH = os.path.join(USER_CONFIG_DIR, "microscope_extensions")
# Load the default config
with open(DEFAULT_CONFIG_FILE_PATH, "r") as default_rc:
DEFAULT_CONFIG = default_rc.read()
# Create the default user settings object
#: Default user settings object
user_settings = OpenflexureSettingsFile(config_path=USER_CONFIG_FILE_PATH)

View file

@ -21,26 +21,19 @@ class Microscope:
"""
A basic microscope object.
The camera and stage should already be initialised, and passed as arguments.
Attributes:
lock (:py:class:`openflexure_microscope.common.lock.CompositeLock`): Composite lock controlling thread access
to multiple pieces of hardware.
camera (:py:class:`openflexure_microscope.camera.base.BaseCamera`): Camera object
stage (:py:class:`openflexure_microscope.stage.base.BaseStage`): Stage object
task: (:py:class:`openflexure_microscope.task.TaskOrchestrator`): Threaded ask orchestrator for managing
background tasks using microscope hardware
The camera and stage objects may already be initialised, and can be passed as arguments.
"""
def __init__(self):
# Initial attributes
self.id = uuid.uuid4()
self.name = self.id
self.fov = [0, 0]
self.camera = None
self.stage = None
self.id = uuid.uuid4() #: Microscope UUID
self.name = self.id #: Microscope name (modifiable)
self.fov = [0, 0] #: Microscope field-of-view in stage motor steps
self.camera = None #: Currently connected camera object
self.stage = None #: Currently connected stage object
# Initialise with an empty composite lock
#: :py:class:`openflexure_microscope.common.lock.CompositeLock`: Composite lock for locking both camera and stage
self.lock = CompositeLock([])
# Apply settings loaded from file
@ -110,13 +103,19 @@ class Microscope:
logging.info("Reapplying settings to newly attached devices")
self.apply_settings(settings_full)
def has_real_stage(self):
def has_real_stage(self) -> bool:
"""
Check if a real (non-mock) stage is currently attached.
"""
if hasattr(self, "stage") and not isinstance(self.stage, MockStage):
return True
else:
return False
def has_real_camera(self):
"""
Check if a real (non-mock) camera is currently attached.
"""
if hasattr(self, "camera") and not isinstance(self.camera, MockStreamer):
return True
else:
@ -139,7 +138,7 @@ class Microscope:
def apply_settings(self, config: dict):
"""
Applies a config dictionary. Missing parameters will be left untouched.
Applies a settings dictionary to the microscope. Missing parameters will be left untouched.
"""
logging.debug("Microscope: Applying config: {}".format(config))