Started updating documentation
This commit is contained in:
parent
be74b92bd7
commit
802f5ba0c2
17 changed files with 248 additions and 368 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue