From 43cb4a2670550b955a3c5921c15bca63d307f22c Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 19 Jul 2023 17:11:14 +1000 Subject: [PATCH 001/147] Rename underscore modules --- .../{_dock_widget.py => dock_widget.py} | 2308 ++++++++--------- .../napari_lattice/{_reader.py => reader.py} | 278 +- 2 files changed, 1293 insertions(+), 1293 deletions(-) rename plugin/napari_lattice/{_dock_widget.py => dock_widget.py} (98%) rename plugin/napari_lattice/{_reader.py => reader.py} (97%) diff --git a/plugin/napari_lattice/_dock_widget.py b/plugin/napari_lattice/dock_widget.py similarity index 98% rename from plugin/napari_lattice/_dock_widget.py rename to plugin/napari_lattice/dock_widget.py index 251752c..69b5e64 100644 --- a/plugin/napari_lattice/_dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -1,1154 +1,1154 @@ -import os -import sys -import yaml -import numpy as np -from pathlib import Path -import dask.array as da -import pandas as pd -from typing import Union, Optional, Callable, Literal -from aicsimageio import AICSImage -from enum import Enum - -from magicclass.wrappers import set_design -from magicgui import magicgui -from magicclass import magicclass, field, vfield, set_options, MagicTemplate -from magicclass.utils import click -from qtpy.QtCore import Qt - -from napari.layers import Layer, Shapes -from napari.types import ImageData -from napari.utils import history - -import pyclesperanto_prototype as cle - -from napari.types import ImageData, ShapesData - -from tqdm import tqdm - -from napari_workflows import Workflow, WorkflowManager -from napari_workflows._io_yaml_v1 import load_workflow - -from lls_core import config, DeskewDirection, DeconvolutionChoice, SaveFileType, Log_Levels -from lls_core.io import LatticeData, save_img, save_img_workflow -from lls_core.utils import read_imagej_roi, get_first_last_image_and_task, modify_workflow_task, get_all_py_files, as_type, process_custom_workflow_output, check_dimensions, load_custom_py_modules -from lls_core.llsz_core import crop_volume_deskew -from lls_core.deconvolution import read_psf, pycuda_decon, skimage_decon - -from napari_lattice.ui_core import _Preview, _Deskew_Save -from napari_lattice._reader import lattice_from_napari - -# Enable Logging -import logging -logger = logging.getLogger(__name__) -logger.setLevel(logging.INFO) - -class LastDimensionOptions(Enum): - channel = "Channel" - time = "Time" - get_from_metadata = "Get from Metadata" - -@magicclass(widget_type="split") -class LLSZWidget(MagicTemplate): - - @magicclass(widget_type="split") - class LlszMenu(MagicTemplate): - open_file: bool = False - lattice: LatticeData = None - skew_dir: DeskewDirection - angle_value: float - deskew_func: Callable - - main_heading = field("

Napari Lattice: Visualization & Analysis

", widget_type="Label") - heading1 = field("Drag and drop an image file onto napari.\nOnce image has opened, initialize the\nplugin by clicking the button below.\nEnsure the image layer and voxel sizes are accurate in the prompt.\n If everything initalises properly, the button turns green.", widget_type="Label") - - @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Initialize Plugin", max_height=75, font_size=13) - @set_options(pixel_size_dx={"widget_type": "FloatSpinBox", "value": 0.1449922, "step": 0.000000001}, - pixel_size_dy={"widget_type": "FloatSpinBox", - "value": 0.1449922, "step": 0.000000001}, - pixel_size_dz={"widget_type": "FloatSpinBox", - "value": 0.3, "step": 0.000000001}, - angle={"widget_type": "FloatSpinBox", - "value": 30, "step": 0.1}, - select_device={"widget_type": "ComboBox", "choices": cle.available_device_names( - ), "value": cle.available_device_names()[0]}, - last_dimension_channel={"widget_type": "ComboBox", - "label": "Set Last dimension (channel/time)", "tooltip": "If the last dimension is initialised incorrectly, you can assign it as either channel/time"}, - merge_all_channel_layers={"widget_type": "CheckBox", "value": True, "label": "Merge all napari layers as channels", - "tooltip": "Use this option if the channels are in separate layers. napari-lattice requires all channels to be in same layer"}, - skew_dir={"widget_type": "ComboBox", "choices": DeskewDirection, "value": DeskewDirection.Y, - "label": "Direction of skew (Y or X)", "tooltip": "Skew direction when image is acquired. Ask your microscopist for details"}, - set_logging={"widget_type": "ComboBox", "choices": Log_Levels, "value": Log_Levels.INFO, - "label": "Log Level", "tooltip": "Only use for debugging. Leave it as INFO for regular operation"} - ) - def Choose_Image_Layer(self, - img_layer: Layer, - pixel_size_dx: float = 0.1449922, - pixel_size_dy: float = 0.1449922, - pixel_size_dz: float = 0.3, - angle: float = 30, - select_device: str = cle.available_device_names()[ - 0], - last_dimension_channel: LastDimensionOptions = LastDimensionOptions.get_from_metadata, - merge_all_channel_layers: bool = False, - skew_dir: DeskewDirection=DeskewDirection.Y, - set_logging: Log_Levels=Log_Levels.INFO): - - logger.setLevel(set_logging.value) - config.log_level = set_logging.value - logger.info(f"Logging set to {set_logging}") - logger.info("Using existing image layer") - - if self.parent_viewer is None: - raise Exception("This function can only be used when inside of a Napari viewer") - - # Select device for processing - cle.select_device(select_device) - - #assert skew_dir in DeskewDirection, "Skew direction not recognised. Enter either Y or X" - LLSZWidget.LlszMenu.skew_dir = skew_dir - LLSZWidget.LlszMenu.angle_value = angle - - if LLSZWidget.LlszMenu.skew_dir == DeskewDirection.Y: - LLSZWidget.LlszMenu.deskew_func = cle.deskew_y - #LLSZWidget.LlszMenu.skew_dir = DeskewDirection.Y - elif LLSZWidget.LlszMenu.skew_dir == DeskewDirection.X: - LLSZWidget.LlszMenu.deskew_func = cle.deskew_x - #LLSZWidget.LlszMenu.skew_dir = DeskewDirection.X - - # merge all napari image layers as one multidimensional image - if merge_all_channel_layers: - from napari.layers.utils.stack_utils import images_to_stack - # get list of napari layers as a list - layer_list = list(self.parent_viewer.layers) - # if more than one layer - if len(layer_list) > 1: - # convert the list of images into a stack - new_layer = images_to_stack(layer_list) - # select all current layers - self.parent_viewer.layers.select_all() - # remove selected layers - self.parent_viewer.layers.remove_selected() - # add the new composite image layer - self.parent_viewer.add_layer(new_layer) - img_layer = new_layer - - LLSZWidget.LlszMenu.lattice = lattice_from_napari( - img=img_layer, - last_dimension=None if last_dimension_channel == LastDimensionOptions.get_from_metadata else last_dimension_channel, - angle=angle, - skew=LLSZWidget.LlszMenu.skew_dir, - physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz) - ) - #LLSZWidget.LlszMenu.aics = LLSZWidget.LlszMenu.lattice.data - - # LLSZWidget.LlszMenu.dask = False # Use GPU by default - - # We initialise these variables here, but they can be changed in the deconvolution section - # list to store psf images for each channel - LLSZWidget.LlszMenu.lattice.psf = [] - LLSZWidget.LlszMenu.lattice.psf_num_iter = 10 - LLSZWidget.LlszMenu.lattice.decon_processing = DeconvolutionChoice.cpu - # list to store otf paths for each channel (Deprecated) - LLSZWidget.LlszMenu.lattice.otf_path = [] - # if not using GPU - #LLSZWidget.LlszMenu.dask = not use_GPU - - # flag for ensuring a file has been opened and plugin initialised - LLSZWidget.LlszMenu.open_file = True - - logger.info( - f"Pixel size (ZYX) in microns: {LLSZWidget.LlszMenu.lattice.dz,LLSZWidget.LlszMenu.lattice.dy,LLSZWidget.LlszMenu.lattice.dx}") - logger.info( - f"Dimensions of image layer (ZYX): {list(LLSZWidget.LlszMenu.lattice.data.shape[-3:])}") - logger.info( - f"Dimensions of deskewed image (ZYX): {LLSZWidget.LlszMenu.lattice.deskew_vol_shape}") - logger.info( - f"Deskewing angle is :{LLSZWidget.LlszMenu.lattice.angle}") - logger.info( - f"Deskew Direction :{LLSZWidget.LlszMenu.lattice.skew}") - # Add dimension labels correctly - # if channel, and not time - if LLSZWidget.LlszMenu.lattice.time == 0 and (last_dimension_channel or LLSZWidget.LlszMenu.lattice.channels > 0): - self.parent_viewer.dims.axis_labels = ('Channel', "Z", "Y", "X") - # if no channel, but has time - elif LLSZWidget.LlszMenu.lattice.channels == 0 and LLSZWidget.LlszMenu.lattice.time > 0: - self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") - # if it has channels - elif LLSZWidget.LlszMenu.lattice.channels > 1: - # If merge to stack is used, channel slider goes to the bottom - if int(self.parent_viewer.dims.dict()["range"][0][1]) == LLSZWidget.LlszMenu.lattice.channels: - self.parent_viewer.dims.axis_labels = ('Channel', "Time", "Z", "Y", "X") - else: - self.parent_viewer.dims.axis_labels = ('Time', "Channel", "Z", "Y", "X") - # if channels initialized by aicsimagio, then channels is 1 - elif LLSZWidget.LlszMenu.lattice.channels == 1 and LLSZWidget.LlszMenu.lattice.time > 1: - self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") - - logger.info(f"Initialised") - self["Choose_Image_Layer"].background_color = "green" - self["Choose_Image_Layer"].text = "Plugin Initialised" - - return - - # Pycudadecon library for deconvolution - # options={"enabled": True}, - deconvolution = vfield(bool, name="Use Deconvolution") - deconvolution.value = False - - @deconvolution.connect - def _set_decon(self): - if self.deconvolution: - logger.info("Deconvolution Activated") - LLSZWidget.LlszMenu.deconvolution.value = True - else: - logger.info("Deconvolution Disabled") - LLSZWidget.LlszMenu.deconvolution.value = False - return - - @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) - @set_options(header=dict(widget_type="Label", label="

Enter path to the PSF images

"), - psf_ch1_path={"widget_type": "FileEdit", - "label": "Channel 1:"}, - psf_ch2_path={"widget_type": "FileEdit", - "label": "Channel 2"}, - psf_ch3_path={"widget_type": "FileEdit", - "label": "Channel 3"}, - psf_ch4_path={"widget_type": "FileEdit", - "label": "Channel 4"}, - device_option={ - "widget_type": "ComboBox", "label": "Choose processing device", "choices": DeconvolutionChoice}, - no_iter={ - "widget_type": "SpinBox", "label": "No of iterations (Deconvolution)", "value": 10, "min": 1, "max": 50, "step": 1} - ) - def deconvolution_gui(self, - header, - psf_ch1_path: Path, - psf_ch2_path: Path, - psf_ch3_path: Path, - psf_ch4_path: Path, - device_option, - no_iter: int): - """GUI for Deconvolution button""" - LLSZWidget.LlszMenu.lattice.decon_processing = device_option - assert LLSZWidget.LlszMenu.deconvolution.value == True, "Deconvolution is set to False. Tick the box to activate deconvolution." - LLSZWidget.LlszMenu.lattice.psf = list(read_psf([ - psf_ch1_path, - psf_ch2_path, - psf_ch3_path, - psf_ch4_path, - ], - device_option, - lattice_class=LLSZWidget.LlszMenu.lattice - )) - LLSZWidget.LlszMenu.lattice.psf_num_iter = no_iter - self["deconvolution_gui"].background_color = "green" - self["deconvolution_gui"].text = "PSFs added" - - @magicclass(widget_type="collapsible") - class Preview: - @magicgui(header=dict(widget_type="Label", label="

Preview Deskew

"), - time=dict(label="Time:", max=2**15), - channel=dict(label="Channel:"), - call_button="Preview") - def Preview_Deskew(self, - header, - time: int, - channel: int, - img_data: ImageData): - """ - Preview deskewed data for a single timepoint and channel - - """ - _Preview(LLSZWidget, - self, - time, - channel, - img_data) - return - - # Tabbed Widget container to house all the widgets - @magicclass(widget_type="tabbed", name="Functions") - class WidgetContainer(MagicTemplate): - - @magicclass(name="Deskew", widget_type="scrollable", properties={"min_width": 100}) - class DeskewWidget(MagicTemplate): - - @magicgui(header=dict(widget_type="Label", label="

Deskew and Save

"), - time_start=dict(label="Time Start:", max=2**20), - time_end=dict(label="Time End:", value=1, max=2**20), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType, "value": SaveFileType.h5}, - save_path=dict(mode='d', label="Directory to save"), - call_button="Save") - def Deskew_Save(self, - header, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - save_as_type: str, - save_path: Path = Path(history.get_save_history()[0])): - """ Widget to Deskew and Save Data""" - _Deskew_Save(LLSZWidget, - time_start, - time_end, - ch_start, - ch_end, - save_as_type, - save_path) - return - - @magicclass(name="Crop and Deskew", widget_type="scrollable") - class CropWidget(MagicTemplate): - - # add function for previewing cropped image - @magicclass(name="Cropping Preview", widget_type="scrollable", properties={ - "min_width": 100, - "shapes_layer": Shapes - }) - class Preview_Crop_Menu(MagicTemplate): - shapes_layer: Shapes - - @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") - @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) - def activate_cropping(self): - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', - face_color=[1, 1, 1, 0], name="Cropping BBOX layer") - # TO select ROIs if needed - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.mode = "SELECT" - self["activate_cropping"].text = "Cropping layer active" - self["activate_cropping"].background_color = "green" - return - - heading2 = field("You can either import ImageJ ROI (.zip) files or manually define ROIs using the shape layer", widget_type="Label") - - @click(enabled=False) - def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])): - logger.info(f"Opening{path}") - roi_list = read_imagej_roi(path) - # convert to canvas coordinates - roi_list = (np.array(roi_list) * - LLSZWidget.LlszMenu.lattice.dy).tolist() - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', - face_color=[1, 1, 1, 0]) - return - - time_crop = field( - int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") - chan_crop = field( - int, options={"min": 0, "step": 1}, name="Channels") - heading_roi = field("If there are multiple ROIs, select the ROI before clicking button below", widget_type="Label") - #roi_idx = field(int, options={"min": 0, "step": 1}, name="ROI number") - - @click(enabled=False) - # -> LayerDataTuple: - def Crop_Preview(self, roi_layer: ShapesData): - assert roi_layer, "No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs." - # TODO: Add assertion to check if bbox layer or coordinates - time = self.time_crop.value - channel = self.chan_crop.value - - assert time < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" - assert channel < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" - - logger.info(f"Using channel {channel} and time {time}") - - vol = LLSZWidget.LlszMenu.lattice.data - vol_zyx = vol[time, channel, ...] - vol_zyx = np.array(vol_zyx) - - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape - # Create a dask array same shape as deskewed image - deskewed_volume = da.zeros(deskewed_shape) - - # Option for entering custom z start value? - z_start = 0 - z_end = deskewed_shape[0] - - # if only one roi drawn, use the first ROI for cropping - if len(roi_layer) == 1: - roi_idx = 0 - else: - assert len( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data) > 0, "Please select an ROI" - roi_idx = list( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data)[0] - - roi_choice = roi_layer[roi_idx] - # As the original image is scaled, the coordinates are in microns, so we need to convert - # roi from micron to canvas/world coordinates - roi_choice = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_choice] - logger.info(f"Previewing ROI {roi_idx}") - - # crop - if LLSZWidget.LlszMenu.deconvolution.value: - logger.info( - f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") - #psf = LLSZWidget.LlszMenu.lattice.psf[channel] - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter).astype(vol_zyx.dtype) - else: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter).astype(vol_zyx.dtype) - else: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - skew_dir=LLSZWidget.LlszMenu.skew_dir).astype(vol_zyx.dtype) - crop_roi_vol_desk = cle.pull(crop_roi_vol_desk) - - # get array back from gpu or addding cle array to napari can throw errors - - scale = (LLSZWidget.LlszMenu.lattice.new_dz, - LLSZWidget.LlszMenu.lattice.dy, - LLSZWidget.LlszMenu.lattice.dx) - self.parent_viewer.add_image( - crop_roi_vol_desk, scale=scale) - - @magicclass(name="Crop and Save Data") - class CropSaveData(MagicTemplate): - @magicgui(header=dict(widget_type="Label", label="

Crop and Save Data

"), - time_start=dict(label="Time Start:"), - time_end=dict(label="Time End:", value=1), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType}, - save_path=dict(mode='d', label="Directory to save ")) - def Crop_Save(self, - header, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - save_as_type: str, - roi_layer_list: ShapesData, - save_path: Path = Path(history.get_save_history()[0])): - - if not roi_layer_list: - logger.error( - "No coordinates found or cropping. Initialise shapes layer and draw ROIs.") - else: - assert LLSZWidget.LlszMenu.open_file, "Image not initialised" - - check_dimensions(time_start, time_end, ch_start, ch_end, - LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) - - angle = LLSZWidget.LlszMenu.lattice.angle - dx = LLSZWidget.LlszMenu.lattice.dx - dy = LLSZWidget.LlszMenu.lattice.dy - dz = LLSZWidget.LlszMenu.lattice.dz - - # get image data - img_data = LLSZWidget.LlszMenu.lattice.data - # Get shape of deskewed image - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - - logger.info("Cropping and saving files...") - - # necessary when scale is used for napari.viewer.add_image operations - roi_layer_list = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_layer_list] - - for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): - # pass arguments for save tiff, callable and function arguments - logger.info("Processing ROI ", idx) - # pass parameters for the crop_volume_deskew function - - save_img(vol=img_data, - func=crop_volume_deskew, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_name_prefix="ROI_" + - str(idx), - save_path=save_path, - save_file_type=save_as_type, - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deskewed_volume=deskewed_volume, - roi_shape=roi_layer, - angle_in_degrees=angle, - z_start=z_start, - z_end=z_end, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - LLSZWidget=LLSZWidget - ) - - logger.info( - f"Cropping and Saving Complete -> {save_path}") - return - - @magicclass(name="Workflow", widget_type="scrollable") - class WorkflowWidget: - @magicclass(name="Preview Workflow", widget_type="scrollable") - class PreviewWorkflow: - #time_preview= field(int, options={"min": 0, "step": 1}, name="Time") - #chan_preview = field(int, options={"min": 0, "step": 1}, name="Channels") - @magicgui(header=dict(widget_type="Label", label="

Preview Workflow

"), - time_preview=dict(label="Time:", max=2**20), - chan_preview=dict(label="Channel:"), - get_active_workflow=dict( - widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), - workflow_path=dict( - mode='r', label="Load custom workflow (.yaml/yml)"), - Use_Cropping=dict( - widget_type="Checkbox", label="Crop Data", value=False), - #custom_module=dict(widget_type="Checkbox",label="Load custom module (looks for *.py files in the workflow directory)",value = False), - call_button="Apply and Preview Workflow") - def Workflow_Preview(self, - header, - time_preview: int, - chan_preview: int, - get_active_workflow: bool, - Use_Cropping: bool, - roi_layer_list: ShapesData, - workflow_path: Path = Path.home()): - """ - Apply napari_workflows to the processing pipeline - User can define a pipeline which can be inspected in napari workflow inspector - and then execute it by ticking the get active workflow checkbox, - OR - Use a predefined workflow - - In both cases, if deskewing is not present as first step, it will be added on - and rest of the task will be made followers - Args: - - """ - print("Previewing deskewed channel and time with workflow") - if get_active_workflow: - # installs the workflow to napari - user_workflow = WorkflowManager.install( - self.parent_viewer).workflow - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - logger.info("Workflow loaded from napari") - else: - - try: - # Automatically scan workflow file directory for *.py files. - # If it findss one, load it as a module - - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - sys.path.append(parent_dir) - custom_py_files = get_all_py_files(parent_dir) - if len(custom_py_files) == 0: - logger.error( - f"No custom modules imported. If you'd like to use a cusotm module, place a *.py file in same folder as the workflow file {parent_dir}") - else: - modules = load_custom_py_modules( - custom_py_files) - - logger.info( - f"Custom modules imported {modules}") - user_workflow = load_workflow( - workflow_path.__str__()) - except yaml.loader.ConstructorError as e: - logger.error( - "\033[91m While loading workflow, got the following error which may mean you need to install the corresponding module in your Python environment: \033[0m") - logger.error(e) - - #user_workflow = load_workflow(workflow_path) - logger.info("Workflow loaded from file") - - assert type( - user_workflow) is Workflow, "Workflow loading error. Check if file is workflow or if required libraries are installed" - - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - #print(input_arg_first, input_arg_last, first_task_name,last_task_name ) - # get list of tasks - task_list = list(user_workflow._tasks.keys()) - logger.info(f"Workflow loaded:{user_workflow}") - # logger.info() - - # when using fields, self.time_preview.value - assert time_preview < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" - assert chan_preview < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" - - time = time_preview - channel = chan_preview - - # to access current time and channel and pass it to workflow file - config.channel = channel - config.time = time - - logger.info( - f"Processing for Time: {time} and Channel: {channel}") - - vol = LLSZWidget.LlszMenu.lattice.data - vol_zyx = vol[time, channel, ...] - vol_zyx = np.array(vol_zyx) - - task_name_start = first_task_name[0] - try: - task_name_last = last_task_name[0] - except IndexError: - task_name_last = task_name_start - - # variables to hold task name, initialize it as None - # if gpu, set otf_path, otherwise use psf - psf = None - otf_path = None - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - otf_path = "otf_path" - else: - psf = "psf" - - # if cropping, set that as first task - # get the function associated with the first task and check if its deskewing - if Use_Cropping: - # use deskewed volume for cropping function - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - if user_workflow.get_task(task_name_start)[0] not in [crop_volume_deskew]: - # if only one roi drawn, use the first ROI for cropping - if len(roi_layer_list) == 1: - roi_idx = 0 - else: # else get the user selection - assert len( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data) > 0, "Please select an ROI" - roi_idx = list( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data)[0] - - roi_choice = roi_layer_list[roi_idx] - # As the original image is scaled, the coordinates are in microns, so we need to convert - # roi to from micron to canvas/world coordinates - roi_choice = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_choice] - logger.info(f"Previewing ROI {roi_idx}") - if LLSZWidget.LlszMenu.deconvolution.value: - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - otf_path=otf_path, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - skew_dir=LLSZWidget.LlszMenu.skew_dir) - else: - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - skew_dir=LLSZWidget.LlszMenu.skew_dir) - - # Set input of the workflow to be crop_deskewing, i.e., the original first operation will now have crop_deskew_image as an input (becoming second instead) - user_workflow.set( - input_arg_first, "crop_deskew_image") - else: - user_workflow.set(input_arg_first, vol_zyx) - # Not cropping; If deskew not in workflow, append to start - elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = LLSZWidget.LlszMenu.lattice.psf[channel] - input_arg_first_decon, input_arg_last_decon, first_task_name_decon, last_task_name_decon = get_first_last_image_and_task( - user_workflow) - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) - # user_workflow.set(input_arg_first_decon,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # user_workflow.set(input_arg_first_decon,"deconvolution") - - user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, - "deconvolution", - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - linear_interpolation=True) - - # user_workflow.set("change_bitdepth",as_type,"deskew_image",vol_zyx) - # Set input of the workflow to be from deskewing output with same bit depth as original volume - # user_workflow.set(input_arg_first,"change_bitdepth") - - else: - user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, - vol_zyx, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - linear_interpolation=True) - # Set input of the workflow to be from deskewing - # user_workflow.set(input_arg_first,"deskew_image") - - user_workflow.set( - "change_bitdepth", as_type, "deskew_image", vol_zyx) - # Set input of the workflow to be from deskewing with same bit depth as original volume - user_workflow.set( - input_arg_first, "change_bitdepth") - - else: - # if deskew already in workflow, just check if deconvolution needs to be added - # repitition of above (maybe create a function?) - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = LLSZWidget.LlszMenu.lattice.psf[channel] - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - - # set input to subsequent task as deconvolution output - user_workflow.set( - input_arg_first, "deconvolution") - - logger.info("Workflow to be executed:") - logger.info(user_workflow) - # Execute workflow - processed_vol = user_workflow.get(task_name_last) - - # check if a measurement table (usually a dictionary or list) or a tuple with different data types - # The function below saves the tables and adds any images to napari window - if type(processed_vol) in [dict, list, tuple]: - if (len(processed_vol) > 1): - df = pd.DataFrame() - for idx, i in enumerate(processed_vol): - df_temp = process_custom_workflow_output( - i, parent_dir, idx, LLSZWidget, self, channel, time, preview=True) - final_df = pd.concat([df, df_temp]) - # append dataframes from every loop and have table command outside loop? - # TODO: Figure out why table is not displaying - from napari_spreadsheet import _widget - table_viewer = _widget.TableViewerWidget( - show=True) - table_viewer.add_spreadsheet(final_df) - # widgets.Table(value=final_df).show() - - else: - # add image to napari window - # TODO: check if its an image napari supports? - process_custom_workflow_output( - processed_vol, parent_dir, 0, LLSZWidget, self, channel, time) - - print("Workflow complete") - pass - - @magicgui(header=dict(widget_type="Label", label="

Apply Workflow and Save Output

"), - time_start=dict(label="Time Start:", max=2**20), - time_end=dict(label="Time End:", - value=1, max=2**20), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - Use_Cropping=dict( - widget_type="Checkbox", label="Crop Data", value=False), - get_active_workflow=dict( - widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), - workflow_path=dict( - mode='r', label="Load custom workflow (.yaml/yml)"), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType}, - save_path=dict( - mode='d', label="Directory to save "), - #custom_module=dict(widget_type="Checkbox",label="Load custom module (same dir as workflow)",value = False), - call_button="Apply Workflow and Save Result") - def Apply_Workflow_and_Save(self, - header, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - Use_Cropping, - roi_layer_list: ShapesData, - get_active_workflow: bool = False, - workflow_path: Path = Path.home(), - save_as_type: str = SaveFileType.tiff, - save_path: Path = Path(history.get_save_history()[0])): - """ - Apply a user-defined analysis workflow using napari-workflows - - Args: - time_start (int): Start Time - time_end (int): End Time - ch_start (int): Start Channel - ch_end (int): End Channel - Use_Cropping (_type_): Use cropping based on ROIs in the shapes layer - roi_layer_list (ShapesData): Shapes layer to use for cropping; can be a list of shapes - get_active_workflow (bool, optional): Gets active workflow in napari. Defaults to False. - workflow_path (Path, optional): User can also choose a custom workflow defined in a yaml file. - save_path (Path, optional): Path to save resulting data - """ - assert LLSZWidget.LlszMenu.open_file, "Image not initialised" - - check_dimensions(time_start, time_end, ch_start, ch_end, - LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) - - # Get parameters - angle = LLSZWidget.LlszMenu.lattice.angle - dx = LLSZWidget.LlszMenu.lattice.dx - dy = LLSZWidget.LlszMenu.lattice.dy - dz = LLSZWidget.LlszMenu.lattice.dz - - if get_active_workflow: - # installs the workflow to napari - user_workflow = WorkflowManager.install( - self.parent_viewer).workflow - print("Workflow installed") - else: - # Automatically scan workflow file directory for *.py files. - # If it findss one, load it as a module - import importlib - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - sys.path.append(parent_dir) - custom_py_files = get_all_py_files(parent_dir) - if len(custom_py_files) == 0: - print( - f"No custom modules imported. If you'd like to use a cusotm module, place a *.py file in same folder as the workflow file {parent_dir}") - else: - modules = map( - importlib.import_module, custom_py_files) - print(f"Custom modules imported {modules}") - user_workflow = load_workflow(workflow_path) - - assert type( - user_workflow) is Workflow, "Workflow file is not a napari workflow object. Check file! You can use workflow inspector if needed" - - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - print(input_arg_first, input_arg_last, - first_task_name, last_task_name) - # get list of tasks - task_list = list(user_workflow._tasks.keys()) - print("Workflow loaded:") - print(user_workflow) - - vol = LLSZWidget.LlszMenu.lattice.data - - #vol_zyx= vol[time,channel,...] - - task_name_start = first_task_name[0] - - try: - task_name_last = last_task_name[0] - except IndexError: - task_name_last = task_name_start - - # variables to hold task name, initialize it as None - # if gpu, set otf_path, otherwise use psf - psf = None - otf_path = None - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - #otf_path = "otf_path" - psf_arg = "psf" - psf = LLSZWidget.LlszMenu.lattice.psf - else: - psf_arg = "psf" - psf = LLSZWidget.LlszMenu.lattice.psf - # if cropping, set that as first task - - if Use_Cropping: - # convert Roi pixel coordinates to canvas coordinates - # necessary only when scale is used for napari.viewer.add_image operations - roi_layer_list = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_layer_list] - - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - roi = "roi" - volume = "volume" - # Check if decon ticked, if so set as first and crop as second? - - # Create workflow for cropping and deskewing - # volume and roi used will be set dynamically - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=volume, - deskewed_volume=deskewed_volume, - roi_shape=roi, - angle_in_degrees=angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - z_start=z_start, - z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - psf=psf_arg, - skew_dir=LLSZWidget.LlszMenu.skew_dir) - - # change the first task so it accepts "crop_deskew as input" - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="crop_deskew_image", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - - for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): - print("Processing ROI ", idx) - user_workflow.set(roi, roi_layer) - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=volume, - first_task="crop_deskew_image", - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - #roi_layer = roi_layer, - save_name_prefix="ROI_" + \ - str(idx), - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - # IF just deskewing and its not in the tasks, add that as first task - elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): - input = "input" - # add task to the workflow - user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, - input_image=input, - angle_in_degrees=angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - linear_interpolation=True) - # Set input of the workflow to be from deskewing - # change workflow task starts from is "deskew_image" and - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deskew_image", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = "psf" - otf_path = "otf_path" - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=input, - psf=psf_arg, - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=input, - psf=psf_arg, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # modify the user workflow so "deconvolution" is accepted - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - task_name_start = first_task_name[0] - - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=input, - first_task=task_name_start, - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - # If deskewing is already as a task, then set the first argument to input so we can modify that later - else: - # if deskewing is already first task, then check if deconvolution needed - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = "psf" - otf_path = "otf_path" - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=input, - psf=psf_arg, - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=input, - psf=psf_arg, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # modify the user workflow so "deconvolution" is accepted - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - task_name_start = first_task_name[0] - - # we pass first argument as input - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=input_arg_first, - first_task=task_name_start, - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - print("Workflow complete") - return - - pass - -def _napari_lattice_widget_wrapper() -> LLSZWidget: - # split widget type enables a resizable widget - #max_height = 50 - # Important to have this or napari won't recognize the classes and magicclass qidgets - widget = LLSZWidget() - # aligning collapsible widgets at the top instead of having them centered vertically - widget._widget._layout.setAlignment(Qt.AlignTop) - - # widget._widget._layout.setWidgetResizable(True) - return widget +import os +import sys +import yaml +import numpy as np +from pathlib import Path +import dask.array as da +import pandas as pd +from typing import Union, Optional, Callable, Literal +from aicsimageio import AICSImage +from enum import Enum + +from magicclass.wrappers import set_design +from magicgui import magicgui +from magicclass import magicclass, field, vfield, set_options, MagicTemplate +from magicclass.utils import click +from qtpy.QtCore import Qt + +from napari.layers import Layer, Shapes +from napari.types import ImageData +from napari.utils import history + +import pyclesperanto_prototype as cle + +from napari.types import ImageData, ShapesData + +from tqdm import tqdm + +from napari_workflows import Workflow, WorkflowManager +from napari_workflows._io_yaml_v1 import load_workflow + +from lls_core import config, DeskewDirection, DeconvolutionChoice, SaveFileType, Log_Levels +from lls_core.io import LatticeData, save_img, save_img_workflow +from lls_core.utils import read_imagej_roi, get_first_last_image_and_task, modify_workflow_task, get_all_py_files, as_type, process_custom_workflow_output, check_dimensions, load_custom_py_modules +from lls_core.llsz_core import crop_volume_deskew +from lls_core.deconvolution import read_psf, pycuda_decon, skimage_decon + +from napari_lattice.ui_core import _Preview, _Deskew_Save +from napari_lattice._reader import lattice_from_napari + +# Enable Logging +import logging +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +class LastDimensionOptions(Enum): + channel = "Channel" + time = "Time" + get_from_metadata = "Get from Metadata" + +@magicclass(widget_type="split") +class LLSZWidget(MagicTemplate): + + @magicclass(widget_type="split") + class LlszMenu(MagicTemplate): + open_file: bool = False + lattice: LatticeData = None + skew_dir: DeskewDirection + angle_value: float + deskew_func: Callable + + main_heading = field("

Napari Lattice: Visualization & Analysis

", widget_type="Label") + heading1 = field("Drag and drop an image file onto napari.\nOnce image has opened, initialize the\nplugin by clicking the button below.\nEnsure the image layer and voxel sizes are accurate in the prompt.\n If everything initalises properly, the button turns green.", widget_type="Label") + + @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Initialize Plugin", max_height=75, font_size=13) + @set_options(pixel_size_dx={"widget_type": "FloatSpinBox", "value": 0.1449922, "step": 0.000000001}, + pixel_size_dy={"widget_type": "FloatSpinBox", + "value": 0.1449922, "step": 0.000000001}, + pixel_size_dz={"widget_type": "FloatSpinBox", + "value": 0.3, "step": 0.000000001}, + angle={"widget_type": "FloatSpinBox", + "value": 30, "step": 0.1}, + select_device={"widget_type": "ComboBox", "choices": cle.available_device_names( + ), "value": cle.available_device_names()[0]}, + last_dimension_channel={"widget_type": "ComboBox", + "label": "Set Last dimension (channel/time)", "tooltip": "If the last dimension is initialised incorrectly, you can assign it as either channel/time"}, + merge_all_channel_layers={"widget_type": "CheckBox", "value": True, "label": "Merge all napari layers as channels", + "tooltip": "Use this option if the channels are in separate layers. napari-lattice requires all channels to be in same layer"}, + skew_dir={"widget_type": "ComboBox", "choices": DeskewDirection, "value": DeskewDirection.Y, + "label": "Direction of skew (Y or X)", "tooltip": "Skew direction when image is acquired. Ask your microscopist for details"}, + set_logging={"widget_type": "ComboBox", "choices": Log_Levels, "value": Log_Levels.INFO, + "label": "Log Level", "tooltip": "Only use for debugging. Leave it as INFO for regular operation"} + ) + def Choose_Image_Layer(self, + img_layer: Layer, + pixel_size_dx: float = 0.1449922, + pixel_size_dy: float = 0.1449922, + pixel_size_dz: float = 0.3, + angle: float = 30, + select_device: str = cle.available_device_names()[ + 0], + last_dimension_channel: LastDimensionOptions = LastDimensionOptions.get_from_metadata, + merge_all_channel_layers: bool = False, + skew_dir: DeskewDirection=DeskewDirection.Y, + set_logging: Log_Levels=Log_Levels.INFO): + + logger.setLevel(set_logging.value) + config.log_level = set_logging.value + logger.info(f"Logging set to {set_logging}") + logger.info("Using existing image layer") + + if self.parent_viewer is None: + raise Exception("This function can only be used when inside of a Napari viewer") + + # Select device for processing + cle.select_device(select_device) + + #assert skew_dir in DeskewDirection, "Skew direction not recognised. Enter either Y or X" + LLSZWidget.LlszMenu.skew_dir = skew_dir + LLSZWidget.LlszMenu.angle_value = angle + + if LLSZWidget.LlszMenu.skew_dir == DeskewDirection.Y: + LLSZWidget.LlszMenu.deskew_func = cle.deskew_y + #LLSZWidget.LlszMenu.skew_dir = DeskewDirection.Y + elif LLSZWidget.LlszMenu.skew_dir == DeskewDirection.X: + LLSZWidget.LlszMenu.deskew_func = cle.deskew_x + #LLSZWidget.LlszMenu.skew_dir = DeskewDirection.X + + # merge all napari image layers as one multidimensional image + if merge_all_channel_layers: + from napari.layers.utils.stack_utils import images_to_stack + # get list of napari layers as a list + layer_list = list(self.parent_viewer.layers) + # if more than one layer + if len(layer_list) > 1: + # convert the list of images into a stack + new_layer = images_to_stack(layer_list) + # select all current layers + self.parent_viewer.layers.select_all() + # remove selected layers + self.parent_viewer.layers.remove_selected() + # add the new composite image layer + self.parent_viewer.add_layer(new_layer) + img_layer = new_layer + + LLSZWidget.LlszMenu.lattice = lattice_from_napari( + img=img_layer, + last_dimension=None if last_dimension_channel == LastDimensionOptions.get_from_metadata else last_dimension_channel, + angle=angle, + skew=LLSZWidget.LlszMenu.skew_dir, + physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz) + ) + #LLSZWidget.LlszMenu.aics = LLSZWidget.LlszMenu.lattice.data + + # LLSZWidget.LlszMenu.dask = False # Use GPU by default + + # We initialise these variables here, but they can be changed in the deconvolution section + # list to store psf images for each channel + LLSZWidget.LlszMenu.lattice.psf = [] + LLSZWidget.LlszMenu.lattice.psf_num_iter = 10 + LLSZWidget.LlszMenu.lattice.decon_processing = DeconvolutionChoice.cpu + # list to store otf paths for each channel (Deprecated) + LLSZWidget.LlszMenu.lattice.otf_path = [] + # if not using GPU + #LLSZWidget.LlszMenu.dask = not use_GPU + + # flag for ensuring a file has been opened and plugin initialised + LLSZWidget.LlszMenu.open_file = True + + logger.info( + f"Pixel size (ZYX) in microns: {LLSZWidget.LlszMenu.lattice.dz,LLSZWidget.LlszMenu.lattice.dy,LLSZWidget.LlszMenu.lattice.dx}") + logger.info( + f"Dimensions of image layer (ZYX): {list(LLSZWidget.LlszMenu.lattice.data.shape[-3:])}") + logger.info( + f"Dimensions of deskewed image (ZYX): {LLSZWidget.LlszMenu.lattice.deskew_vol_shape}") + logger.info( + f"Deskewing angle is :{LLSZWidget.LlszMenu.lattice.angle}") + logger.info( + f"Deskew Direction :{LLSZWidget.LlszMenu.lattice.skew}") + # Add dimension labels correctly + # if channel, and not time + if LLSZWidget.LlszMenu.lattice.time == 0 and (last_dimension_channel or LLSZWidget.LlszMenu.lattice.channels > 0): + self.parent_viewer.dims.axis_labels = ('Channel', "Z", "Y", "X") + # if no channel, but has time + elif LLSZWidget.LlszMenu.lattice.channels == 0 and LLSZWidget.LlszMenu.lattice.time > 0: + self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") + # if it has channels + elif LLSZWidget.LlszMenu.lattice.channels > 1: + # If merge to stack is used, channel slider goes to the bottom + if int(self.parent_viewer.dims.dict()["range"][0][1]) == LLSZWidget.LlszMenu.lattice.channels: + self.parent_viewer.dims.axis_labels = ('Channel', "Time", "Z", "Y", "X") + else: + self.parent_viewer.dims.axis_labels = ('Time', "Channel", "Z", "Y", "X") + # if channels initialized by aicsimagio, then channels is 1 + elif LLSZWidget.LlszMenu.lattice.channels == 1 and LLSZWidget.LlszMenu.lattice.time > 1: + self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") + + logger.info(f"Initialised") + self["Choose_Image_Layer"].background_color = "green" + self["Choose_Image_Layer"].text = "Plugin Initialised" + + return + + # Pycudadecon library for deconvolution + # options={"enabled": True}, + deconvolution = vfield(bool, name="Use Deconvolution") + deconvolution.value = False + + @deconvolution.connect + def _set_decon(self): + if self.deconvolution: + logger.info("Deconvolution Activated") + LLSZWidget.LlszMenu.deconvolution.value = True + else: + logger.info("Deconvolution Disabled") + LLSZWidget.LlszMenu.deconvolution.value = False + return + + @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) + @set_options(header=dict(widget_type="Label", label="

Enter path to the PSF images

"), + psf_ch1_path={"widget_type": "FileEdit", + "label": "Channel 1:"}, + psf_ch2_path={"widget_type": "FileEdit", + "label": "Channel 2"}, + psf_ch3_path={"widget_type": "FileEdit", + "label": "Channel 3"}, + psf_ch4_path={"widget_type": "FileEdit", + "label": "Channel 4"}, + device_option={ + "widget_type": "ComboBox", "label": "Choose processing device", "choices": DeconvolutionChoice}, + no_iter={ + "widget_type": "SpinBox", "label": "No of iterations (Deconvolution)", "value": 10, "min": 1, "max": 50, "step": 1} + ) + def deconvolution_gui(self, + header, + psf_ch1_path: Path, + psf_ch2_path: Path, + psf_ch3_path: Path, + psf_ch4_path: Path, + device_option, + no_iter: int): + """GUI for Deconvolution button""" + LLSZWidget.LlszMenu.lattice.decon_processing = device_option + assert LLSZWidget.LlszMenu.deconvolution.value == True, "Deconvolution is set to False. Tick the box to activate deconvolution." + LLSZWidget.LlszMenu.lattice.psf = list(read_psf([ + psf_ch1_path, + psf_ch2_path, + psf_ch3_path, + psf_ch4_path, + ], + device_option, + lattice_class=LLSZWidget.LlszMenu.lattice + )) + LLSZWidget.LlszMenu.lattice.psf_num_iter = no_iter + self["deconvolution_gui"].background_color = "green" + self["deconvolution_gui"].text = "PSFs added" + + @magicclass(widget_type="collapsible") + class Preview: + @magicgui(header=dict(widget_type="Label", label="

Preview Deskew

"), + time=dict(label="Time:", max=2**15), + channel=dict(label="Channel:"), + call_button="Preview") + def Preview_Deskew(self, + header, + time: int, + channel: int, + img_data: ImageData): + """ + Preview deskewed data for a single timepoint and channel + + """ + _Preview(LLSZWidget, + self, + time, + channel, + img_data) + return + + # Tabbed Widget container to house all the widgets + @magicclass(widget_type="tabbed", name="Functions") + class WidgetContainer(MagicTemplate): + + @magicclass(name="Deskew", widget_type="scrollable", properties={"min_width": 100}) + class DeskewWidget(MagicTemplate): + + @magicgui(header=dict(widget_type="Label", label="

Deskew and Save

"), + time_start=dict(label="Time Start:", max=2**20), + time_end=dict(label="Time End:", value=1, max=2**20), + ch_start=dict(label="Channel Start:"), + ch_end=dict(label="Channel End:", value=1), + save_as_type={ + "label": "Save as filetype:", "choices": SaveFileType, "value": SaveFileType.h5}, + save_path=dict(mode='d', label="Directory to save"), + call_button="Save") + def Deskew_Save(self, + header, + time_start: int, + time_end: int, + ch_start: int, + ch_end: int, + save_as_type: str, + save_path: Path = Path(history.get_save_history()[0])): + """ Widget to Deskew and Save Data""" + _Deskew_Save(LLSZWidget, + time_start, + time_end, + ch_start, + ch_end, + save_as_type, + save_path) + return + + @magicclass(name="Crop and Deskew", widget_type="scrollable") + class CropWidget(MagicTemplate): + + # add function for previewing cropped image + @magicclass(name="Cropping Preview", widget_type="scrollable", properties={ + "min_width": 100, + "shapes_layer": Shapes + }) + class Preview_Crop_Menu(MagicTemplate): + shapes_layer: Shapes + + @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") + @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) + def activate_cropping(self): + LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', + face_color=[1, 1, 1, 0], name="Cropping BBOX layer") + # TO select ROIs if needed + LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.mode = "SELECT" + self["activate_cropping"].text = "Cropping layer active" + self["activate_cropping"].background_color = "green" + return + + heading2 = field("You can either import ImageJ ROI (.zip) files or manually define ROIs using the shape layer", widget_type="Label") + + @click(enabled=False) + def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])): + logger.info(f"Opening{path}") + roi_list = read_imagej_roi(path) + # convert to canvas coordinates + roi_list = (np.array(roi_list) * + LLSZWidget.LlszMenu.lattice.dy).tolist() + LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', + face_color=[1, 1, 1, 0]) + return + + time_crop = field( + int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") + chan_crop = field( + int, options={"min": 0, "step": 1}, name="Channels") + heading_roi = field("If there are multiple ROIs, select the ROI before clicking button below", widget_type="Label") + #roi_idx = field(int, options={"min": 0, "step": 1}, name="ROI number") + + @click(enabled=False) + # -> LayerDataTuple: + def Crop_Preview(self, roi_layer: ShapesData): + assert roi_layer, "No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs." + # TODO: Add assertion to check if bbox layer or coordinates + time = self.time_crop.value + channel = self.chan_crop.value + + assert time < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" + assert channel < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" + + logger.info(f"Using channel {channel} and time {time}") + + vol = LLSZWidget.LlszMenu.lattice.data + vol_zyx = vol[time, channel, ...] + vol_zyx = np.array(vol_zyx) + + deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape + # Create a dask array same shape as deskewed image + deskewed_volume = da.zeros(deskewed_shape) + + # Option for entering custom z start value? + z_start = 0 + z_end = deskewed_shape[0] + + # if only one roi drawn, use the first ROI for cropping + if len(roi_layer) == 1: + roi_idx = 0 + else: + assert len( + LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data) > 0, "Please select an ROI" + roi_idx = list( + LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data)[0] + + roi_choice = roi_layer[roi_idx] + # As the original image is scaled, the coordinates are in microns, so we need to convert + # roi from micron to canvas/world coordinates + roi_choice = [ + x/LLSZWidget.LlszMenu.lattice.dy for x in roi_choice] + logger.info(f"Previewing ROI {roi_idx}") + + # crop + if LLSZWidget.LlszMenu.deconvolution.value: + logger.info( + f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") + #psf = LLSZWidget.LlszMenu.lattice.psf[channel] + if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, + deskewed_volume=deskewed_volume, + roi_shape=roi_choice, + angle_in_degrees=LLSZWidget.LlszMenu.angle_value, + voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, + voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, + voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + z_start=z_start, + z_end=z_end, + deconvolution=LLSZWidget.LlszMenu.deconvolution.value, + decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + psf=LLSZWidget.LlszMenu.lattice.psf[channel], + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter).astype(vol_zyx.dtype) + else: + crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, + deskewed_volume=deskewed_volume, + roi_shape=roi_choice, + angle_in_degrees=LLSZWidget.LlszMenu.angle_value, + voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, + voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, + voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + z_start=z_start, + z_end=z_end, + deconvolution=LLSZWidget.LlszMenu.deconvolution.value, + decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + psf=LLSZWidget.LlszMenu.lattice.psf[channel], + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter).astype(vol_zyx.dtype) + else: + crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, + deskewed_volume=deskewed_volume, + roi_shape=roi_choice, + angle_in_degrees=LLSZWidget.LlszMenu.angle_value, + voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, + voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, + voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + z_start=z_start, + z_end=z_end, + skew_dir=LLSZWidget.LlszMenu.skew_dir).astype(vol_zyx.dtype) + crop_roi_vol_desk = cle.pull(crop_roi_vol_desk) + + # get array back from gpu or addding cle array to napari can throw errors + + scale = (LLSZWidget.LlszMenu.lattice.new_dz, + LLSZWidget.LlszMenu.lattice.dy, + LLSZWidget.LlszMenu.lattice.dx) + self.parent_viewer.add_image( + crop_roi_vol_desk, scale=scale) + + @magicclass(name="Crop and Save Data") + class CropSaveData(MagicTemplate): + @magicgui(header=dict(widget_type="Label", label="

Crop and Save Data

"), + time_start=dict(label="Time Start:"), + time_end=dict(label="Time End:", value=1), + ch_start=dict(label="Channel Start:"), + ch_end=dict(label="Channel End:", value=1), + save_as_type={ + "label": "Save as filetype:", "choices": SaveFileType}, + save_path=dict(mode='d', label="Directory to save ")) + def Crop_Save(self, + header, + time_start: int, + time_end: int, + ch_start: int, + ch_end: int, + save_as_type: str, + roi_layer_list: ShapesData, + save_path: Path = Path(history.get_save_history()[0])): + + if not roi_layer_list: + logger.error( + "No coordinates found or cropping. Initialise shapes layer and draw ROIs.") + else: + assert LLSZWidget.LlszMenu.open_file, "Image not initialised" + + check_dimensions(time_start, time_end, ch_start, ch_end, + LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) + + angle = LLSZWidget.LlszMenu.lattice.angle + dx = LLSZWidget.LlszMenu.lattice.dx + dy = LLSZWidget.LlszMenu.lattice.dy + dz = LLSZWidget.LlszMenu.lattice.dz + + # get image data + img_data = LLSZWidget.LlszMenu.lattice.data + # Get shape of deskewed image + deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape + deskewed_volume = da.zeros(deskewed_shape) + z_start = 0 + z_end = deskewed_shape[0] + + logger.info("Cropping and saving files...") + + # necessary when scale is used for napari.viewer.add_image operations + roi_layer_list = [ + x/LLSZWidget.LlszMenu.lattice.dy for x in roi_layer_list] + + for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): + # pass arguments for save tiff, callable and function arguments + logger.info("Processing ROI ", idx) + # pass parameters for the crop_volume_deskew function + + save_img(vol=img_data, + func=crop_volume_deskew, + time_start=time_start, + time_end=time_end, + channel_start=ch_start, + channel_end=ch_end, + save_name_prefix="ROI_" + + str(idx), + save_path=save_path, + save_file_type=save_as_type, + save_name=LLSZWidget.LlszMenu.lattice.save_name, + dx=dx, + dy=dy, + dz=dz, + angle=angle, + deskewed_volume=deskewed_volume, + roi_shape=roi_layer, + angle_in_degrees=angle, + z_start=z_start, + z_end=z_end, + voxel_size_x=dx, + voxel_size_y=dy, + voxel_size_z=dz, + LLSZWidget=LLSZWidget + ) + + logger.info( + f"Cropping and Saving Complete -> {save_path}") + return + + @magicclass(name="Workflow", widget_type="scrollable") + class WorkflowWidget: + @magicclass(name="Preview Workflow", widget_type="scrollable") + class PreviewWorkflow: + #time_preview= field(int, options={"min": 0, "step": 1}, name="Time") + #chan_preview = field(int, options={"min": 0, "step": 1}, name="Channels") + @magicgui(header=dict(widget_type="Label", label="

Preview Workflow

"), + time_preview=dict(label="Time:", max=2**20), + chan_preview=dict(label="Channel:"), + get_active_workflow=dict( + widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), + workflow_path=dict( + mode='r', label="Load custom workflow (.yaml/yml)"), + Use_Cropping=dict( + widget_type="Checkbox", label="Crop Data", value=False), + #custom_module=dict(widget_type="Checkbox",label="Load custom module (looks for *.py files in the workflow directory)",value = False), + call_button="Apply and Preview Workflow") + def Workflow_Preview(self, + header, + time_preview: int, + chan_preview: int, + get_active_workflow: bool, + Use_Cropping: bool, + roi_layer_list: ShapesData, + workflow_path: Path = Path.home()): + """ + Apply napari_workflows to the processing pipeline + User can define a pipeline which can be inspected in napari workflow inspector + and then execute it by ticking the get active workflow checkbox, + OR + Use a predefined workflow + + In both cases, if deskewing is not present as first step, it will be added on + and rest of the task will be made followers + Args: + + """ + print("Previewing deskewed channel and time with workflow") + if get_active_workflow: + # installs the workflow to napari + user_workflow = WorkflowManager.install( + self.parent_viewer).workflow + parent_dir = workflow_path.resolve( + ).parents[0].__str__()+os.sep + logger.info("Workflow loaded from napari") + else: + + try: + # Automatically scan workflow file directory for *.py files. + # If it findss one, load it as a module + + parent_dir = workflow_path.resolve( + ).parents[0].__str__()+os.sep + sys.path.append(parent_dir) + custom_py_files = get_all_py_files(parent_dir) + if len(custom_py_files) == 0: + logger.error( + f"No custom modules imported. If you'd like to use a cusotm module, place a *.py file in same folder as the workflow file {parent_dir}") + else: + modules = load_custom_py_modules( + custom_py_files) + + logger.info( + f"Custom modules imported {modules}") + user_workflow = load_workflow( + workflow_path.__str__()) + except yaml.loader.ConstructorError as e: + logger.error( + "\033[91m While loading workflow, got the following error which may mean you need to install the corresponding module in your Python environment: \033[0m") + logger.error(e) + + #user_workflow = load_workflow(workflow_path) + logger.info("Workflow loaded from file") + + assert type( + user_workflow) is Workflow, "Workflow loading error. Check if file is workflow or if required libraries are installed" + + input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + user_workflow) + #print(input_arg_first, input_arg_last, first_task_name,last_task_name ) + # get list of tasks + task_list = list(user_workflow._tasks.keys()) + logger.info(f"Workflow loaded:{user_workflow}") + # logger.info() + + # when using fields, self.time_preview.value + assert time_preview < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" + assert chan_preview < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" + + time = time_preview + channel = chan_preview + + # to access current time and channel and pass it to workflow file + config.channel = channel + config.time = time + + logger.info( + f"Processing for Time: {time} and Channel: {channel}") + + vol = LLSZWidget.LlszMenu.lattice.data + vol_zyx = vol[time, channel, ...] + vol_zyx = np.array(vol_zyx) + + task_name_start = first_task_name[0] + try: + task_name_last = last_task_name[0] + except IndexError: + task_name_last = task_name_start + + # variables to hold task name, initialize it as None + # if gpu, set otf_path, otherwise use psf + psf = None + otf_path = None + + if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + otf_path = "otf_path" + else: + psf = "psf" + + # if cropping, set that as first task + # get the function associated with the first task and check if its deskewing + if Use_Cropping: + # use deskewed volume for cropping function + deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape + deskewed_volume = da.zeros(deskewed_shape) + z_start = 0 + z_end = deskewed_shape[0] + if user_workflow.get_task(task_name_start)[0] not in [crop_volume_deskew]: + # if only one roi drawn, use the first ROI for cropping + if len(roi_layer_list) == 1: + roi_idx = 0 + else: # else get the user selection + assert len( + LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data) > 0, "Please select an ROI" + roi_idx = list( + LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data)[0] + + roi_choice = roi_layer_list[roi_idx] + # As the original image is scaled, the coordinates are in microns, so we need to convert + # roi to from micron to canvas/world coordinates + roi_choice = [ + x/LLSZWidget.LlszMenu.lattice.dy for x in roi_choice] + logger.info(f"Previewing ROI {roi_idx}") + if LLSZWidget.LlszMenu.deconvolution.value: + user_workflow.set("crop_deskew_image", crop_volume_deskew, + original_volume=vol_zyx, + deskewed_volume=deskewed_volume, + roi_shape=roi_choice, + angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, + voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, + voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, + voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + z_start=z_start, + z_end=z_end, + deconvolution=LLSZWidget.LlszMenu.deconvolution.value, + decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + otf_path=otf_path, + psf=LLSZWidget.LlszMenu.lattice.psf[channel], + skew_dir=LLSZWidget.LlszMenu.skew_dir) + else: + user_workflow.set("crop_deskew_image", crop_volume_deskew, + original_volume=vol_zyx, + deskewed_volume=deskewed_volume, + roi_shape=roi_choice, + angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, + voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, + voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, + voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + z_start=z_start, + z_end=z_end, + skew_dir=LLSZWidget.LlszMenu.skew_dir) + + # Set input of the workflow to be crop_deskewing, i.e., the original first operation will now have crop_deskew_image as an input (becoming second instead) + user_workflow.set( + input_arg_first, "crop_deskew_image") + else: + user_workflow.set(input_arg_first, vol_zyx) + # Not cropping; If deskew not in workflow, append to start + elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): + # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + if LLSZWidget.LlszMenu.deconvolution.value: + psf = LLSZWidget.LlszMenu.lattice.psf[channel] + input_arg_first_decon, input_arg_last_decon, first_task_name_decon, last_task_name_decon = get_first_last_image_and_task( + user_workflow) + + if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + user_workflow.set("deconvolution", + pycuda_decon, + image=vol_zyx, + psf=LLSZWidget.LlszMenu.lattice.psf[channel], + dzdata=LLSZWidget.LlszMenu.lattice.dz, + dxdata=LLSZWidget.LlszMenu.lattice.dx, + dzpsf=LLSZWidget.LlszMenu.lattice.dz, + dxpsf=LLSZWidget.LlszMenu.lattice.dx, + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) + # user_workflow.set(input_arg_first_decon,"deconvolution") + else: + user_workflow.set("deconvolution", + skimage_decon, + vol_zyx=vol_zyx, + psf=LLSZWidget.LlszMenu.lattice.psf[channel], + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest') + # user_workflow.set(input_arg_first_decon,"deconvolution") + + user_workflow.set("deskew_image", + LLSZWidget.LlszMenu.deskew_func, + "deconvolution", + angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, + voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, + voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, + voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + linear_interpolation=True) + + # user_workflow.set("change_bitdepth",as_type,"deskew_image",vol_zyx) + # Set input of the workflow to be from deskewing output with same bit depth as original volume + # user_workflow.set(input_arg_first,"change_bitdepth") + + else: + user_workflow.set("deskew_image", + LLSZWidget.LlszMenu.deskew_func, + vol_zyx, + angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, + voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, + voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, + voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + linear_interpolation=True) + # Set input of the workflow to be from deskewing + # user_workflow.set(input_arg_first,"deskew_image") + + user_workflow.set( + "change_bitdepth", as_type, "deskew_image", vol_zyx) + # Set input of the workflow to be from deskewing with same bit depth as original volume + user_workflow.set( + input_arg_first, "change_bitdepth") + + else: + # if deskew already in workflow, just check if deconvolution needs to be added + # repitition of above (maybe create a function?) + # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + if LLSZWidget.LlszMenu.deconvolution.value: + psf = LLSZWidget.LlszMenu.lattice.psf[channel] + input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + user_workflow) + + if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + user_workflow.set("deconvolution", + pycuda_decon, + image=vol_zyx, + psf=LLSZWidget.LlszMenu.lattice.psf[channel], + dzdata=LLSZWidget.LlszMenu.lattice.dz, + dxdata=LLSZWidget.LlszMenu.lattice.dx, + dzpsf=LLSZWidget.LlszMenu.lattice.dz, + dxpsf=LLSZWidget.LlszMenu.lattice.dx, + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) + # user_workflow.set(input_arg_first,"deconvolution") + else: + user_workflow.set("deconvolution", + skimage_decon, + vol_zyx=vol_zyx, + psf=LLSZWidget.LlszMenu.lattice.psf[channel], + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest') + + # set input to subsequent task as deconvolution output + user_workflow.set( + input_arg_first, "deconvolution") + + logger.info("Workflow to be executed:") + logger.info(user_workflow) + # Execute workflow + processed_vol = user_workflow.get(task_name_last) + + # check if a measurement table (usually a dictionary or list) or a tuple with different data types + # The function below saves the tables and adds any images to napari window + if type(processed_vol) in [dict, list, tuple]: + if (len(processed_vol) > 1): + df = pd.DataFrame() + for idx, i in enumerate(processed_vol): + df_temp = process_custom_workflow_output( + i, parent_dir, idx, LLSZWidget, self, channel, time, preview=True) + final_df = pd.concat([df, df_temp]) + # append dataframes from every loop and have table command outside loop? + # TODO: Figure out why table is not displaying + from napari_spreadsheet import _widget + table_viewer = _widget.TableViewerWidget( + show=True) + table_viewer.add_spreadsheet(final_df) + # widgets.Table(value=final_df).show() + + else: + # add image to napari window + # TODO: check if its an image napari supports? + process_custom_workflow_output( + processed_vol, parent_dir, 0, LLSZWidget, self, channel, time) + + print("Workflow complete") + pass + + @magicgui(header=dict(widget_type="Label", label="

Apply Workflow and Save Output

"), + time_start=dict(label="Time Start:", max=2**20), + time_end=dict(label="Time End:", + value=1, max=2**20), + ch_start=dict(label="Channel Start:"), + ch_end=dict(label="Channel End:", value=1), + Use_Cropping=dict( + widget_type="Checkbox", label="Crop Data", value=False), + get_active_workflow=dict( + widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), + workflow_path=dict( + mode='r', label="Load custom workflow (.yaml/yml)"), + save_as_type={ + "label": "Save as filetype:", "choices": SaveFileType}, + save_path=dict( + mode='d', label="Directory to save "), + #custom_module=dict(widget_type="Checkbox",label="Load custom module (same dir as workflow)",value = False), + call_button="Apply Workflow and Save Result") + def Apply_Workflow_and_Save(self, + header, + time_start: int, + time_end: int, + ch_start: int, + ch_end: int, + Use_Cropping, + roi_layer_list: ShapesData, + get_active_workflow: bool = False, + workflow_path: Path = Path.home(), + save_as_type: str = SaveFileType.tiff, + save_path: Path = Path(history.get_save_history()[0])): + """ + Apply a user-defined analysis workflow using napari-workflows + + Args: + time_start (int): Start Time + time_end (int): End Time + ch_start (int): Start Channel + ch_end (int): End Channel + Use_Cropping (_type_): Use cropping based on ROIs in the shapes layer + roi_layer_list (ShapesData): Shapes layer to use for cropping; can be a list of shapes + get_active_workflow (bool, optional): Gets active workflow in napari. Defaults to False. + workflow_path (Path, optional): User can also choose a custom workflow defined in a yaml file. + save_path (Path, optional): Path to save resulting data + """ + assert LLSZWidget.LlszMenu.open_file, "Image not initialised" + + check_dimensions(time_start, time_end, ch_start, ch_end, + LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) + + # Get parameters + angle = LLSZWidget.LlszMenu.lattice.angle + dx = LLSZWidget.LlszMenu.lattice.dx + dy = LLSZWidget.LlszMenu.lattice.dy + dz = LLSZWidget.LlszMenu.lattice.dz + + if get_active_workflow: + # installs the workflow to napari + user_workflow = WorkflowManager.install( + self.parent_viewer).workflow + print("Workflow installed") + else: + # Automatically scan workflow file directory for *.py files. + # If it findss one, load it as a module + import importlib + parent_dir = workflow_path.resolve( + ).parents[0].__str__()+os.sep + sys.path.append(parent_dir) + custom_py_files = get_all_py_files(parent_dir) + if len(custom_py_files) == 0: + print( + f"No custom modules imported. If you'd like to use a cusotm module, place a *.py file in same folder as the workflow file {parent_dir}") + else: + modules = map( + importlib.import_module, custom_py_files) + print(f"Custom modules imported {modules}") + user_workflow = load_workflow(workflow_path) + + assert type( + user_workflow) is Workflow, "Workflow file is not a napari workflow object. Check file! You can use workflow inspector if needed" + + input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + user_workflow) + print(input_arg_first, input_arg_last, + first_task_name, last_task_name) + # get list of tasks + task_list = list(user_workflow._tasks.keys()) + print("Workflow loaded:") + print(user_workflow) + + vol = LLSZWidget.LlszMenu.lattice.data + + #vol_zyx= vol[time,channel,...] + + task_name_start = first_task_name[0] + + try: + task_name_last = last_task_name[0] + except IndexError: + task_name_last = task_name_start + + # variables to hold task name, initialize it as None + # if gpu, set otf_path, otherwise use psf + psf = None + otf_path = None + + if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + #otf_path = "otf_path" + psf_arg = "psf" + psf = LLSZWidget.LlszMenu.lattice.psf + else: + psf_arg = "psf" + psf = LLSZWidget.LlszMenu.lattice.psf + # if cropping, set that as first task + + if Use_Cropping: + # convert Roi pixel coordinates to canvas coordinates + # necessary only when scale is used for napari.viewer.add_image operations + roi_layer_list = [ + x/LLSZWidget.LlszMenu.lattice.dy for x in roi_layer_list] + + deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape + deskewed_volume = da.zeros(deskewed_shape) + z_start = 0 + z_end = deskewed_shape[0] + roi = "roi" + volume = "volume" + # Check if decon ticked, if so set as first and crop as second? + + # Create workflow for cropping and deskewing + # volume and roi used will be set dynamically + user_workflow.set("crop_deskew_image", crop_volume_deskew, + original_volume=volume, + deskewed_volume=deskewed_volume, + roi_shape=roi, + angle_in_degrees=angle, + voxel_size_x=dx, + voxel_size_y=dy, + voxel_size_z=dz, + z_start=z_start, + z_end=z_end, + deconvolution=LLSZWidget.LlszMenu.deconvolution.value, + decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + psf=psf_arg, + skew_dir=LLSZWidget.LlszMenu.skew_dir) + + # change the first task so it accepts "crop_deskew as input" + new_task = modify_workflow_task( + old_arg=input_arg_first, task_key=task_name_start, new_arg="crop_deskew_image", workflow=user_workflow) + user_workflow.set(task_name_start, new_task) + + for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): + print("Processing ROI ", idx) + user_workflow.set(roi, roi_layer) + save_img_workflow(vol=vol, + workflow=user_workflow, + input_arg=volume, + first_task="crop_deskew_image", + last_task=task_name_last, + time_start=time_start, + time_end=time_end, + channel_start=ch_start, + channel_end=ch_end, + save_file_type=save_as_type, + save_path=save_path, + #roi_layer = roi_layer, + save_name_prefix="ROI_" + \ + str(idx), + save_name=LLSZWidget.LlszMenu.lattice.save_name, + dx=dx, + dy=dy, + dz=dz, + angle=angle, + deconvolution=LLSZWidget.LlszMenu.deconvolution.value, + decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + otf_path=otf_path, + psf_arg=psf_arg, + psf=psf) + + # IF just deskewing and its not in the tasks, add that as first task + elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): + input = "input" + # add task to the workflow + user_workflow.set("deskew_image", + LLSZWidget.LlszMenu.deskew_func, + input_image=input, + angle_in_degrees=angle, + voxel_size_x=dx, + voxel_size_y=dy, + voxel_size_z=dz, + linear_interpolation=True) + # Set input of the workflow to be from deskewing + # change workflow task starts from is "deskew_image" and + new_task = modify_workflow_task( + old_arg=input_arg_first, task_key=task_name_start, new_arg="deskew_image", workflow=user_workflow) + user_workflow.set(task_name_start, new_task) + + # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + if LLSZWidget.LlszMenu.deconvolution.value: + psf = "psf" + otf_path = "otf_path" + input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + user_workflow) + + if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + user_workflow.set("deconvolution", + pycuda_decon, + image=input, + psf=psf_arg, + dzdata=LLSZWidget.LlszMenu.lattice.dz, + dxdata=LLSZWidget.LlszMenu.lattice.dx, + dzpsf=LLSZWidget.LlszMenu.lattice.dz, + dxpsf=LLSZWidget.LlszMenu.lattice.dx, + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) + # user_workflow.set(input_arg_first,"deconvolution") + else: + user_workflow.set("deconvolution", + skimage_decon, + vol_zyx=input, + psf=psf_arg, + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest') + # modify the user workflow so "deconvolution" is accepted + new_task = modify_workflow_task( + old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) + user_workflow.set(task_name_start, new_task) + input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + user_workflow) + task_name_start = first_task_name[0] + + save_img_workflow(vol=vol, + workflow=user_workflow, + input_arg=input, + first_task=task_name_start, + last_task=task_name_last, + time_start=time_start, + time_end=time_end, + channel_start=ch_start, + channel_end=ch_end, + save_file_type=save_as_type, + save_path=save_path, + save_name=LLSZWidget.LlszMenu.lattice.save_name, + dx=dx, + dy=dy, + dz=dz, + angle=angle, + deconvolution=LLSZWidget.LlszMenu.deconvolution.value, + decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + otf_path=otf_path, + psf_arg=psf_arg, + psf=psf) + + # If deskewing is already as a task, then set the first argument to input so we can modify that later + else: + # if deskewing is already first task, then check if deconvolution needed + # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + if LLSZWidget.LlszMenu.deconvolution.value: + psf = "psf" + otf_path = "otf_path" + input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + user_workflow) + + if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + user_workflow.set("deconvolution", + pycuda_decon, + image=input, + psf=psf_arg, + dzdata=LLSZWidget.LlszMenu.lattice.dz, + dxdata=LLSZWidget.LlszMenu.lattice.dx, + dzpsf=LLSZWidget.LlszMenu.lattice.dz, + dxpsf=LLSZWidget.LlszMenu.lattice.dx, + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) + # user_workflow.set(input_arg_first,"deconvolution") + else: + user_workflow.set("deconvolution", + skimage_decon, + vol_zyx=input, + psf=psf_arg, + num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest') + # modify the user workflow so "deconvolution" is accepted + new_task = modify_workflow_task( + old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) + user_workflow.set(task_name_start, new_task) + input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + user_workflow) + task_name_start = first_task_name[0] + + # we pass first argument as input + save_img_workflow(vol=vol, + workflow=user_workflow, + input_arg=input_arg_first, + first_task=task_name_start, + last_task=task_name_last, + time_start=time_start, + time_end=time_end, + channel_start=ch_start, + channel_end=ch_end, + save_file_type=save_as_type, + save_path=save_path, + save_name=LLSZWidget.LlszMenu.lattice.save_name, + dx=dx, + dy=dy, + dz=dz, + angle=angle, + deconvolution=LLSZWidget.LlszMenu.deconvolution.value, + decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + otf_path=otf_path, + psf_arg=psf_arg, + psf=psf) + + print("Workflow complete") + return + + pass + +def _napari_lattice_widget_wrapper() -> LLSZWidget: + # split widget type enables a resizable widget + #max_height = 50 + # Important to have this or napari won't recognize the classes and magicclass qidgets + widget = LLSZWidget() + # aligning collapsible widgets at the top instead of having them centered vertically + widget._widget._layout.setAlignment(Qt.AlignTop) + + # widget._widget._layout.setWidgetResizable(True) + return widget diff --git a/plugin/napari_lattice/_reader.py b/plugin/napari_lattice/reader.py similarity index 97% rename from plugin/napari_lattice/_reader.py rename to plugin/napari_lattice/reader.py index 895f14f..0344956 100644 --- a/plugin/napari_lattice/_reader.py +++ b/plugin/napari_lattice/reader.py @@ -1,140 +1,140 @@ -""" -reader plugin for h5 saved using np2bdv -https://github.com/nvladimus/npy2bdv -#TODO: pass pyramidal layer to napari -##use ilevel parameter in read_view to access different subsamples/pyramids -#pass a list of images with different resolution for pyramid; use is_pyramid=True flag in napari.add_image -, however pyramidal support for 3D not available yet -""" -from __future__ import annotations - -import dask.array as da -import dask.delayed as delayed -import os -import numpy as np -from napari.layers import image, Layer -from napari.layers._data_protocols import LayerDataProtocol -from aicsimageio.types import ArrayLike -from aicsimageio.dimensions import Dimensions -from aicsimageio.aics_image import AICSImage - -from typing_extensions import Literal -from typing import Any, Optional, cast, Tuple - -from lls_core.io import img_from_array -from lls_core.lattice_data import lattice_from_aics, LatticeData - -def lattice_from_napari( - img: Layer, - last_dimension: Optional[Literal["channel", "time"]], - **kwargs: Any -) -> LatticeData: - """ - Factory function for generating a LatticeData from a Napari Image - - Arguments: - kwargs: Extra arguments to pass to the LatticeData constructor - """ - - img_data_aics: AICSImage - - if 'aicsimage' in img.metadata.keys(): - img_data_aics = img.metadata['aicsimage'] - else: - if not last_dimension: - raise ValueError("Either the Napari image must have dimensional metadata, or last_dimension must be provided") - img_data_aics = img_from_array(cast(ArrayLike, img.data), last_dimension=last_dimension, physical_pixel_sizes=kwargs.get("physical_pixel_sizes")) - - save_name: str - if img.source.path is None: - # remove colon (:) and any leading spaces - save_name = img.name.replace(":", "").strip() - # replace any group of spaces with "_" - save_name = '_'.join(save_name.split()) - else: - file_name_noext = os.path.basename(img.source.path) - file_name = os.path.splitext(file_name_noext)[0] - # remove colon (:) and any leading spaces - save_name = file_name.replace(":", "").strip() - # replace any group of spaces with "_" - save_name = '_'.join(save_name.split()) - - return lattice_from_aics(img_data_aics, save_name=save_name, **kwargs) - -def napari_get_reader(path: list[str] | str): - """Check if file ends with h5 and returns reader function if true - Parameters - ---------- - path : str or list of str - Path to file, or list of paths. - Returns - ------- - function - """ - if isinstance(path, list): - # reader plugins may be handed single path, or a list of paths. - # if it is a list, we are only going to open first file - path = path[0] - - # if we know we cannot read the file, we immediately return None. - if not path.endswith(".h5"): - return None - - # otherwise we return the *function* that can read ``path``. - return bdv_h5_reader - - -def bdv_h5_reader(path): - """Take a path and returns a list of LayerData tuples.""" - - os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" - - #print(path) - import npy2bdv - h5_file = npy2bdv.npy2bdv.BdvEditor(path) - - img = [] - - #get dimensions of first image - first_timepoint = h5_file.read_view(time=0,channel=0) - - #Threshold to figure out when to use out-of-memory loading/dask - #Got the idea from napari-aicsimageio - #https://github.com/AllenCellModeling/napari-aicsimageio/blob/22934757c2deda30c13f39ec425343182fa91a89/napari_aicsimageio/core.py#L222 - mem_threshold_bytes = 4e9 - mem_per_threshold = 0.3 - - from psutil import virtual_memory - - file_size = os.path.getsize(path) - avail_mem = virtual_memory().available - - #if file size <30% of available memory and <4GB, open - if file_size<=mem_per_threshold*avail_mem and file_size LatticeData: + """ + Factory function for generating a LatticeData from a Napari Image + + Arguments: + kwargs: Extra arguments to pass to the LatticeData constructor + """ + + img_data_aics: AICSImage + + if 'aicsimage' in img.metadata.keys(): + img_data_aics = img.metadata['aicsimage'] + else: + if not last_dimension: + raise ValueError("Either the Napari image must have dimensional metadata, or last_dimension must be provided") + img_data_aics = img_from_array(cast(ArrayLike, img.data), last_dimension=last_dimension, physical_pixel_sizes=kwargs.get("physical_pixel_sizes")) + + save_name: str + if img.source.path is None: + # remove colon (:) and any leading spaces + save_name = img.name.replace(":", "").strip() + # replace any group of spaces with "_" + save_name = '_'.join(save_name.split()) + else: + file_name_noext = os.path.basename(img.source.path) + file_name = os.path.splitext(file_name_noext)[0] + # remove colon (:) and any leading spaces + save_name = file_name.replace(":", "").strip() + # replace any group of spaces with "_" + save_name = '_'.join(save_name.split()) + + return lattice_from_aics(img_data_aics, save_name=save_name, **kwargs) + +def napari_get_reader(path: list[str] | str): + """Check if file ends with h5 and returns reader function if true + Parameters + ---------- + path : str or list of str + Path to file, or list of paths. + Returns + ------- + function + """ + if isinstance(path, list): + # reader plugins may be handed single path, or a list of paths. + # if it is a list, we are only going to open first file + path = path[0] + + # if we know we cannot read the file, we immediately return None. + if not path.endswith(".h5"): + return None + + # otherwise we return the *function* that can read ``path``. + return bdv_h5_reader + + +def bdv_h5_reader(path): + """Take a path and returns a list of LayerData tuples.""" + + os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" + + #print(path) + import npy2bdv + h5_file = npy2bdv.npy2bdv.BdvEditor(path) + + img = [] + + #get dimensions of first image + first_timepoint = h5_file.read_view(time=0,channel=0) + + #Threshold to figure out when to use out-of-memory loading/dask + #Got the idea from napari-aicsimageio + #https://github.com/AllenCellModeling/napari-aicsimageio/blob/22934757c2deda30c13f39ec425343182fa91a89/napari_aicsimageio/core.py#L222 + mem_threshold_bytes = 4e9 + mem_per_threshold = 0.3 + + from psutil import virtual_memory + + file_size = os.path.getsize(path) + avail_mem = virtual_memory().available + + #if file size <30% of available memory and <4GB, open + if file_size<=mem_per_threshold*avail_mem and file_size Date: Fri, 21 Jul 2023 18:19:50 +1000 Subject: [PATCH 002/147] Clean up global variables and types in dock_widget --- core/lls_core/config.py | 2 +- core/lls_core/lattice_data.py | 8 +- core/lls_core/llsz_core.py | 39 ++- plugin/napari_lattice/dock_widget.py | 491 ++++++++++++++------------- plugin/napari_lattice/napari.yaml | 6 +- plugin/tests/test_dock_widget.py | 2 +- 6 files changed, 288 insertions(+), 260 deletions(-) diff --git a/core/lls_core/config.py b/core/lls_core/config.py index 8460167..c63a3ae 100644 --- a/core/lls_core/config.py +++ b/core/lls_core/config.py @@ -2,5 +2,5 @@ #https://docs.python.org/3/faq/programming.html?highlight=global#how-do-i-share-global-variables-across-modules channel = 0 time = 0 -#configure default logging level for use in packages; inherit from _dock_widget or __main__.py +#configure default logging level for use in packages; inherit from dock_widget or __main__.py log_level = 10 \ No newline at end of file diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 45efa05..c3066f0 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -47,6 +47,10 @@ class LatticeData: #: The filename of this data when it is saved save_name: str + # Dimensions of the deskewed output + deskew_vol_shape: Tuple[int, ...] = field(init=False) + deskew_affine_transform: cle.AffineTransform3D = field(init=False) + #: Geometry of the light path skew: DeskewDirection = DeskewDirection.Y angle: float = 30.0 @@ -58,10 +62,6 @@ class LatticeData: new_dz: Optional[float] = None - # Dimensions of the deskewed output - deskew_vol_shape: Optional[Tuple[int, ...]] = None - deskew_affine_transform: Optional[cle.AffineTransform3D] = None - # PSF data that should be refactored into another class eventually psf: Optional[List[NDArray]] = None psf_num_iter: Optional[int] = None diff --git a/core/lls_core/llsz_core.py b/core/lls_core/llsz_core.py index 92a59f1..106b25f 100644 --- a/core/lls_core/llsz_core.py +++ b/core/lls_core/llsz_core.py @@ -6,7 +6,8 @@ from dask.array.core import Array as DaskArray import dask.array as da from resource_backed_dask_array import ResourceBackedDaskArray -from typing import Optional, Union, TYPE_CHECKING +from typing import Any, Optional, Union, TYPE_CHECKING, overload, Literal, Tuple +from typing_extensions import Unpack, TypedDict, Required from pyclesperanto_prototype._tier8._affine_transform_deskew_3d import ( affine_transform_deskew_3d, ) @@ -32,10 +33,36 @@ cle._tier0._pycl.OCLArray, ] +class CommonArgs(TypedDict, total=False): + original_volume: Required[ArrayLike] + deskewed_volume: Union[ ArrayLike, None ] + roi_shape: Union[list, NDArray, None] + angle_in_degrees: float + voxel_size_x: float + voxel_size_y: float + voxel_size_z: float + z_start: int + z_end: int + deconvolution: bool + decon_processing: Optional[str] + psf: Union[Psf, None] + num_iter: int + linear_interpolation: bool + skew_dir: DeskewDirection + +@overload +def crop_volume_deskew(*, debug: Literal[True], get_deskew_and_decon: bool = False, **kwargs: Unpack[CommonArgs]) -> Tuple[NDArray, NDArray]: + ... +@overload +def crop_volume_deskew(*, debug: Literal[False] = False, get_deskew_and_decon: Literal[True], **kwargs: Unpack[CommonArgs]) -> Tuple[NDArray, NDArray]: + ... +@overload +def crop_volume_deskew(*, debug: Literal[False] = False, get_deskew_and_decon: Literal[False] = False, **kwargs: Unpack[CommonArgs]) -> NDArray: + ... def crop_volume_deskew( original_volume: ArrayLike, deskewed_volume: Union[ ArrayLike, None ] = None, - roi_shape: Union[Shapes, list, NDArray, None] = None, + roi_shape: Union[list, NDArray, None] = None, angle_in_degrees: float = 30, voxel_size_x: float = 1, voxel_size_y: float = 1, @@ -84,15 +111,15 @@ def crop_volume_deskew( # if shapes layer, get first one # TODO: test this - if is_napari_shape(roi_shape): - shape = roi_shape.data[0] + # if is_napari_shape(roi_shape): + # shape = roi_shape.data[0] # if its a list and each element has a shape of 4, its a list of rois - elif type(roi_shape) is list and len(roi_shape[0]) == 4: + if isinstance(roi_shape, list) and len(roi_shape[0]) == 4: # TODO:change to accept any roi by passing index shape = roi_shape[0] # len(roi_shape) >= 1: # if its a array or list with shape of 4, its a single ROI - elif len(roi_shape) == 4 and type(roi_shape) in (np.ndarray, list): + elif len(roi_shape) == 4 and isinstance(roi_shape, (np.ndarray, list)): shape = roi_shape assert len(shape) == 4, print("Shape must be an array of shape 4") diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 69b5e64..9b5ef7c 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -4,14 +4,17 @@ import numpy as np from pathlib import Path import dask.array as da +from dask.array.core import Array as DaskArray import pandas as pd from typing import Union, Optional, Callable, Literal +from typing_extensions import NoReturn, Never, TypeVar, overload, Any from aicsimageio import AICSImage from enum import Enum from magicclass.wrappers import set_design from magicgui import magicgui -from magicclass import magicclass, field, vfield, set_options, MagicTemplate +from magicclass import magicclass, field, vfield, set_options, LlszTemplate +from magicclass._gui._base import check_override from magicclass.utils import click from qtpy.QtCore import Qt @@ -35,7 +38,7 @@ from lls_core.deconvolution import read_psf, pycuda_decon, skimage_decon from napari_lattice.ui_core import _Preview, _Deskew_Save -from napari_lattice._reader import lattice_from_napari +from napari_lattice.reader import lattice_from_napari # Enable Logging import logging @@ -47,16 +50,23 @@ class LastDimensionOptions(Enum): time = "Time" get_from_metadata = "Get from Metadata" +class LlszTemplate(LlszTemplate): + @property + def llsz_parent(self) -> "LLSZWidget": + return self.find_ancestor(LLSZWidget) + @magicclass(widget_type="split") -class LLSZWidget(MagicTemplate): +class LLSZWidget(LlszTemplate): + open_file: bool = False + lattice: LatticeData = None + skew_dir: DeskewDirection + angle_value: float + deskew_func: Callable + deconvolution: bool = False + shapes_layer: Shapes @magicclass(widget_type="split") - class LlszMenu(MagicTemplate): - open_file: bool = False - lattice: LatticeData = None - skew_dir: DeskewDirection - angle_value: float - deskew_func: Callable + class LlszMenu(LlszTemplate): main_heading = field("

Napari Lattice: Visualization & Analysis

", widget_type="Label") heading1 = field("Drag and drop an image file onto napari.\nOnce image has opened, initialize the\nplugin by clicking the button below.\nEnsure the image layer and voxel sizes are accurate in the prompt.\n If everything initalises properly, the button turns green.", widget_type="Label") @@ -105,15 +115,15 @@ def Choose_Image_Layer(self, cle.select_device(select_device) #assert skew_dir in DeskewDirection, "Skew direction not recognised. Enter either Y or X" - LLSZWidget.LlszMenu.skew_dir = skew_dir - LLSZWidget.LlszMenu.angle_value = angle - - if LLSZWidget.LlszMenu.skew_dir == DeskewDirection.Y: - LLSZWidget.LlszMenu.deskew_func = cle.deskew_y - #LLSZWidget.LlszMenu.skew_dir = DeskewDirection.Y - elif LLSZWidget.LlszMenu.skew_dir == DeskewDirection.X: - LLSZWidget.LlszMenu.deskew_func = cle.deskew_x - #LLSZWidget.LlszMenu.skew_dir = DeskewDirection.X + self.llsz_parent.skew_dir = skew_dir + self.llsz_parent.angle_value = angle + + if self.llsz_parent.skew_dir == DeskewDirection.Y: + self.llsz_parent.deskew_func = cle.deskew_y + #skew_dir = DeskewDirection.Y + elif self.llsz_parent.skew_dir == DeskewDirection.X: + self.llsz_parent.deskew_func = cle.deskew_x + #self.llsz_parent.skew_dir = DeskewDirection.X # merge all napari image layers as one multidimensional image if merge_all_channel_layers: @@ -132,56 +142,56 @@ def Choose_Image_Layer(self, self.parent_viewer.add_layer(new_layer) img_layer = new_layer - LLSZWidget.LlszMenu.lattice = lattice_from_napari( + self.llsz_parent.lattice = lattice_from_napari( img=img_layer, last_dimension=None if last_dimension_channel == LastDimensionOptions.get_from_metadata else last_dimension_channel, angle=angle, - skew=LLSZWidget.LlszMenu.skew_dir, + skew=self.llsz_parent.skew_dir, physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz) ) - #LLSZWidget.LlszMenu.aics = LLSZWidget.LlszMenu.lattice.data + #self.llsz_parent.aics = self.llsz_parent.lattice.data - # LLSZWidget.LlszMenu.dask = False # Use GPU by default + # self.llsz_parent.dask = False # Use GPU by default # We initialise these variables here, but they can be changed in the deconvolution section # list to store psf images for each channel - LLSZWidget.LlszMenu.lattice.psf = [] - LLSZWidget.LlszMenu.lattice.psf_num_iter = 10 - LLSZWidget.LlszMenu.lattice.decon_processing = DeconvolutionChoice.cpu + self.llsz_parent.lattice.psf = [] + self.llsz_parent.lattice.psf_num_iter = 10 + self.llsz_parent.lattice.decon_processing = DeconvolutionChoice.cpu # list to store otf paths for each channel (Deprecated) - LLSZWidget.LlszMenu.lattice.otf_path = [] + self.llsz_parent.lattice.otf_path = [] # if not using GPU - #LLSZWidget.LlszMenu.dask = not use_GPU + #self.llsz_parent.dask = not use_GPU # flag for ensuring a file has been opened and plugin initialised - LLSZWidget.LlszMenu.open_file = True + self.llsz_parent.open_file = True logger.info( - f"Pixel size (ZYX) in microns: {LLSZWidget.LlszMenu.lattice.dz,LLSZWidget.LlszMenu.lattice.dy,LLSZWidget.LlszMenu.lattice.dx}") + f"Pixel size (ZYX) in microns: {self.llsz_parent.lattice.dz,self.llsz_parent.lattice.dy,self.llsz_parent.lattice.dx}") logger.info( - f"Dimensions of image layer (ZYX): {list(LLSZWidget.LlszMenu.lattice.data.shape[-3:])}") + f"Dimensions of image layer (ZYX): {list(self.llsz_parent.lattice.data.shape[-3:])}") logger.info( - f"Dimensions of deskewed image (ZYX): {LLSZWidget.LlszMenu.lattice.deskew_vol_shape}") + f"Dimensions of deskewed image (ZYX): {self.llsz_parent.lattice.deskew_vol_shape}") logger.info( - f"Deskewing angle is :{LLSZWidget.LlszMenu.lattice.angle}") + f"Deskewing angle is: {self.llsz_parent.lattice.angle}") logger.info( - f"Deskew Direction :{LLSZWidget.LlszMenu.lattice.skew}") + f"Deskew Direction: {self.llsz_parent.lattice.skew}") # Add dimension labels correctly # if channel, and not time - if LLSZWidget.LlszMenu.lattice.time == 0 and (last_dimension_channel or LLSZWidget.LlszMenu.lattice.channels > 0): + if self.llsz_parent.lattice.time == 0 and (last_dimension_channel or self.llsz_parent.lattice.channels > 0): self.parent_viewer.dims.axis_labels = ('Channel', "Z", "Y", "X") # if no channel, but has time - elif LLSZWidget.LlszMenu.lattice.channels == 0 and LLSZWidget.LlszMenu.lattice.time > 0: + elif self.llsz_parent.lattice.channels == 0 and self.llsz_parent.lattice.time > 0: self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") # if it has channels - elif LLSZWidget.LlszMenu.lattice.channels > 1: + elif self.llsz_parent.lattice.channels > 1: # If merge to stack is used, channel slider goes to the bottom - if int(self.parent_viewer.dims.dict()["range"][0][1]) == LLSZWidget.LlszMenu.lattice.channels: + if int(self.parent_viewer.dims.dict()["range"][0][1]) == self.llsz_parent.lattice.channels: self.parent_viewer.dims.axis_labels = ('Channel', "Time", "Z", "Y", "X") else: self.parent_viewer.dims.axis_labels = ('Time', "Channel", "Z", "Y", "X") # if channels initialized by aicsimagio, then channels is 1 - elif LLSZWidget.LlszMenu.lattice.channels == 1 and LLSZWidget.LlszMenu.lattice.time > 1: + elif self.llsz_parent.lattice.channels == 1 and self.llsz_parent.lattice.time > 1: self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") logger.info(f"Initialised") @@ -199,11 +209,10 @@ def Choose_Image_Layer(self, def _set_decon(self): if self.deconvolution: logger.info("Deconvolution Activated") - LLSZWidget.LlszMenu.deconvolution.value = True + self.llsz_parent.deconvolution = True else: logger.info("Deconvolution Disabled") - LLSZWidget.LlszMenu.deconvolution.value = False - return + self.llsz_parent.deconvolution = False @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) @set_options(header=dict(widget_type="Label", label="

Enter path to the PSF images

"), @@ -221,26 +230,27 @@ def _set_decon(self): "widget_type": "SpinBox", "label": "No of iterations (Deconvolution)", "value": 10, "min": 1, "max": 50, "step": 1} ) def deconvolution_gui(self, - header, + header: str, psf_ch1_path: Path, psf_ch2_path: Path, psf_ch3_path: Path, psf_ch4_path: Path, - device_option, + device_option: DeconvolutionChoice, no_iter: int): """GUI for Deconvolution button""" - LLSZWidget.LlszMenu.lattice.decon_processing = device_option - assert LLSZWidget.LlszMenu.deconvolution.value == True, "Deconvolution is set to False. Tick the box to activate deconvolution." - LLSZWidget.LlszMenu.lattice.psf = list(read_psf([ + self.llsz_parent.lattice.decon_processing = device_option + if not self.llsz_parent.deconvolution: + raise Exception("Deconvolution is set to False. Tick the box to activate deconvolution.") + self.llsz_parent.lattice.psf = list(read_psf([ psf_ch1_path, psf_ch2_path, psf_ch3_path, psf_ch4_path, ], device_option, - lattice_class=LLSZWidget.LlszMenu.lattice + lattice_class=self.llsz_parent.lattice )) - LLSZWidget.LlszMenu.lattice.psf_num_iter = no_iter + self.llsz_parent.lattice.psf_num_iter = no_iter self["deconvolution_gui"].background_color = "green" self["deconvolution_gui"].text = "PSFs added" @@ -251,7 +261,7 @@ class Preview: channel=dict(label="Channel:"), call_button="Preview") def Preview_Deskew(self, - header, + header: str, time: int, channel: int, img_data: ImageData): @@ -264,14 +274,13 @@ def Preview_Deskew(self, time, channel, img_data) - return # Tabbed Widget container to house all the widgets @magicclass(widget_type="tabbed", name="Functions") - class WidgetContainer(MagicTemplate): + class WidgetContainer(LlszTemplate): @magicclass(name="Deskew", widget_type="scrollable", properties={"min_width": 100}) - class DeskewWidget(MagicTemplate): + class DeskewWidget(LlszTemplate): @magicgui(header=dict(widget_type="Label", label="

Deskew and Save

"), time_start=dict(label="Time Start:", max=2**20), @@ -283,7 +292,7 @@ class DeskewWidget(MagicTemplate): save_path=dict(mode='d', label="Directory to save"), call_button="Save") def Deskew_Save(self, - header, + header: str, time_start: int, time_end: int, ch_start: int, @@ -301,67 +310,64 @@ def Deskew_Save(self, return @magicclass(name="Crop and Deskew", widget_type="scrollable") - class CropWidget(MagicTemplate): + class CropWidget(LlszTemplate): # add function for previewing cropped image @magicclass(name="Cropping Preview", widget_type="scrollable", properties={ "min_width": 100, "shapes_layer": Shapes }) - class Preview_Crop_Menu(MagicTemplate): - shapes_layer: Shapes + class Preview_Crop_Menu(LlszTemplate): @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) def activate_cropping(self): - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', - face_color=[1, 1, 1, 0], name="Cropping BBOX layer") + if self.parent_viewer is None: + raise Exception("This function can only be used when inside of a Napari viewer") + self.llsz_parent.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', + face_color=[1, 1, 1, 0], name="Cropping BBOX layer") # TO select ROIs if needed - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.mode = "SELECT" + self.llsz_parent.shapes_layer.mode = "SELECT" self["activate_cropping"].text = "Cropping layer active" self["activate_cropping"].background_color = "green" - return heading2 = field("You can either import ImageJ ROI (.zip) files or manually define ROIs using the shape layer", widget_type="Label") @click(enabled=False) - def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])): + def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])) -> None: logger.info(f"Opening{path}") - roi_list = read_imagej_roi(path) + roi_list = read_imagej_roi(str(path)) # convert to canvas coordinates - roi_list = (np.array(roi_list) * - LLSZWidget.LlszMenu.lattice.dy).tolist() - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', - face_color=[1, 1, 1, 0]) - return + self.find_ancestor(LLSZWidget) + roi_list = (np.array(roi_list) * self.llsz_parent.lattice.dy).tolist() + self.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', face_color=[1, 1, 1, 0]) - time_crop = field( - int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") - chan_crop = field( - int, options={"min": 0, "step": 1}, name="Channels") + time_crop = field(int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") + chan_crop = field(int, options={"min": 0, "step": 1}, name="Channels") heading_roi = field("If there are multiple ROIs, select the ROI before clicking button below", widget_type="Label") #roi_idx = field(int, options={"min": 0, "step": 1}, name="ROI number") @click(enabled=False) # -> LayerDataTuple: def Crop_Preview(self, roi_layer: ShapesData): - assert roi_layer, "No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs." + if not roi_layer: + raise Exception("No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs.") # TODO: Add assertion to check if bbox layer or coordinates time = self.time_crop.value channel = self.chan_crop.value - - assert time < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" - assert channel < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" - + if time >= self.llsz_parent.lattice.time: + raise ValueError("Time is out of range") + if time >= self.llsz_parent.lattice.time: + raise ValueError("Channel is out of range") logger.info(f"Using channel {channel} and time {time}") - vol = LLSZWidget.LlszMenu.lattice.data + vol = self.llsz_parent.lattice.data vol_zyx = vol[time, channel, ...] vol_zyx = np.array(vol_zyx) - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape + deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape # Create a dask array same shape as deskewed image - deskewed_volume = da.zeros(deskewed_shape) + deskewed_volume: DaskArray = da.zeros(deskewed_shape) # Option for entering custom z start value? z_start = 0 @@ -370,75 +376,74 @@ def Crop_Preview(self, roi_layer: ShapesData): # if only one roi drawn, use the first ROI for cropping if len(roi_layer) == 1: roi_idx = 0 - else: - assert len( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data) > 0, "Please select an ROI" - roi_idx = list( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data)[0] + elif len(self.shapes_layer.selected_data) == 0: + raise Exception("Please select an ROI") + + roi_idx = list(self.shapes_layer.selected_data)[0] - roi_choice = roi_layer[roi_idx] # As the original image is scaled, the coordinates are in microns, so we need to convert # roi from micron to canvas/world coordinates - roi_choice = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_choice] + roi_choice = [x/self.llsz_parent.lattice.dy for x in roi_layer[roi_idx]] logger.info(f"Previewing ROI {roi_idx}") # crop - if LLSZWidget.LlszMenu.deconvolution.value: + if self.llsz_parent.deconvolution: + if not self.llsz_parent.lattice.psf or not self.llsz_parent.lattice.psf_num_iter or not self.llsz_parent.lattice.decon_processing: + raise Exception("PSF fields should be set by this point!") logger.info( f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") - #psf = LLSZWidget.LlszMenu.lattice.psf[channel] - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + #psf = self.llsz_parent.lattice.psf[channel] + if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, deskewed_volume=deskewed_volume, roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + angle_in_degrees=self.llsz_parent.angle_value, + voxel_size_x=self.llsz_parent.lattice.dx, + voxel_size_y=self.llsz_parent.lattice.dy, + voxel_size_z=self.llsz_parent.lattice.dz, z_start=z_start, z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter).astype(vol_zyx.dtype) + deconvolution=self.llsz_parent.deconvolution, + decon_processing=self.llsz_parent.lattice.decon_processing.value, + psf=self.llsz_parent.lattice.psf[channel], + num_iter=self.llsz_parent.lattice.psf_num_iter).astype(vol_zyx.dtype) else: crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, deskewed_volume=deskewed_volume, roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + angle_in_degrees=self.llsz_parent.angle_value, + voxel_size_x=self.llsz_parent.lattice.dx, + voxel_size_y=self.llsz_parent.lattice.dy, + voxel_size_z=self.llsz_parent.lattice.dz, z_start=z_start, z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter).astype(vol_zyx.dtype) + deconvolution=self.llsz_parent.deconvolution, + decon_processing=self.llsz_parent.lattice.decon_processing.value, + psf=self.llsz_parent.lattice.psf[channel], + num_iter=self.llsz_parent.lattice.psf_num_iter).astype(vol_zyx.dtype) else: crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, deskewed_volume=deskewed_volume, roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + angle_in_degrees=self.llsz_parent.angle_value, + voxel_size_x=self.llsz_parent.lattice.dx, + voxel_size_y=self.llsz_parent.lattice.dy, + voxel_size_z=self.llsz_parent.lattice.dz, z_start=z_start, z_end=z_end, - skew_dir=LLSZWidget.LlszMenu.skew_dir).astype(vol_zyx.dtype) + skew_dir=self.llsz_parent.skew_dir).astype(vol_zyx.dtype) crop_roi_vol_desk = cle.pull(crop_roi_vol_desk) # get array back from gpu or addding cle array to napari can throw errors - scale = (LLSZWidget.LlszMenu.lattice.new_dz, - LLSZWidget.LlszMenu.lattice.dy, - LLSZWidget.LlszMenu.lattice.dx) + scale = (self.llsz_parent.lattice.new_dz, + self.llsz_parent.lattice.dy, + self.llsz_parent.lattice.dx) self.parent_viewer.add_image( crop_roi_vol_desk, scale=scale) @magicclass(name="Crop and Save Data") - class CropSaveData(MagicTemplate): + class CropSaveData(LlszTemplate): @magicgui(header=dict(widget_type="Label", label="

Crop and Save Data

"), time_start=dict(label="Time Start:"), time_end=dict(label="Time End:", value=1), @@ -448,12 +453,12 @@ class CropSaveData(MagicTemplate): "label": "Save as filetype:", "choices": SaveFileType}, save_path=dict(mode='d', label="Directory to save ")) def Crop_Save(self, - header, + header: str, time_start: int, time_end: int, ch_start: int, ch_end: int, - save_as_type: str, + save_as_type: SaveFileType, roi_layer_list: ShapesData, save_path: Path = Path(history.get_save_history()[0])): @@ -461,20 +466,20 @@ def Crop_Save(self, logger.error( "No coordinates found or cropping. Initialise shapes layer and draw ROIs.") else: - assert LLSZWidget.LlszMenu.open_file, "Image not initialised" + if not self.llsz_parent.open_file: + raise Exception("Image not initialised") - check_dimensions(time_start, time_end, ch_start, ch_end, - LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) + check_dimensions(time_start, time_end, ch_start, ch_end, self.llsz_parent.lattice.channels, self.llsz_parent.lattice.time) - angle = LLSZWidget.LlszMenu.lattice.angle - dx = LLSZWidget.LlszMenu.lattice.dx - dy = LLSZWidget.LlszMenu.lattice.dy - dz = LLSZWidget.LlszMenu.lattice.dz + angle = self.llsz_parent.lattice.angle + dx = self.llsz_parent.lattice.dx + dy = self.llsz_parent.lattice.dy + dz = self.llsz_parent.lattice.dz # get image data - img_data = LLSZWidget.LlszMenu.lattice.data + img_data = self.llsz_parent.lattice.data # Get shape of deskewed image - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape + deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape deskewed_volume = da.zeros(deskewed_shape) z_start = 0 z_end = deskewed_shape[0] @@ -482,8 +487,7 @@ def Crop_Save(self, logger.info("Cropping and saving files...") # necessary when scale is used for napari.viewer.add_image operations - roi_layer_list = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_layer_list] + roi_layer_list = ShapesData([x/self.llsz_parent.lattice.dy for x in roi_layer_list]) for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): # pass arguments for save tiff, callable and function arguments @@ -500,7 +504,7 @@ def Crop_Save(self, str(idx), save_path=save_path, save_file_type=save_as_type, - save_name=LLSZWidget.LlszMenu.lattice.save_name, + save_name=self.llsz_parent.lattice.save_name, dx=dx, dy=dy, dz=dz, @@ -513,17 +517,17 @@ def Crop_Save(self, voxel_size_x=dx, voxel_size_y=dy, voxel_size_z=dz, - LLSZWidget=LLSZWidget + LLSZWidget=parent ) logger.info( f"Cropping and Saving Complete -> {save_path}") - return @magicclass(name="Workflow", widget_type="scrollable") - class WorkflowWidget: + class WorkflowWidget(LlszTemplate): + @magicclass(name="Preview Workflow", widget_type="scrollable") - class PreviewWorkflow: + class PreviewWorkflow(LlszTemplate): #time_preview= field(int, options={"min": 0, "step": 1}, name="Time") #chan_preview = field(int, options={"min": 0, "step": 1}, name="Channels") @magicgui(header=dict(widget_type="Label", label="

Preview Workflow

"), @@ -538,7 +542,7 @@ class PreviewWorkflow: #custom_module=dict(widget_type="Checkbox",label="Load custom module (looks for *.py files in the workflow directory)",value = False), call_button="Apply and Preview Workflow") def Workflow_Preview(self, - header, + header: str, time_preview: int, chan_preview: int, get_active_workflow: bool, @@ -560,13 +564,11 @@ def Workflow_Preview(self, print("Previewing deskewed channel and time with workflow") if get_active_workflow: # installs the workflow to napari - user_workflow = WorkflowManager.install( - self.parent_viewer).workflow + user_workflow = WorkflowManager.install(self.parent_viewer).workflow parent_dir = workflow_path.resolve( ).parents[0].__str__()+os.sep logger.info("Workflow loaded from napari") else: - try: # Automatically scan workflow file directory for *.py files. # If it findss one, load it as a module @@ -587,17 +589,15 @@ def Workflow_Preview(self, user_workflow = load_workflow( workflow_path.__str__()) except yaml.loader.ConstructorError as e: - logger.error( - "\033[91m While loading workflow, got the following error which may mean you need to install the corresponding module in your Python environment: \033[0m") - logger.error(e) + raise Exception("\033[91m While loading workflow, got the following error which may mean you need to install the corresponding module in your Python environment: \033[0m") from e #user_workflow = load_workflow(workflow_path) logger.info("Workflow loaded from file") - assert type( - user_workflow) is Workflow, "Workflow loading error. Check if file is workflow or if required libraries are installed" + if not isinstance(user_workflow, Workflow): + raise Exception("Workflow loading error. Check if file is workflow or if required libraries are installed") - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + input_arg_first, _, first_task_name, last_task_name = get_first_last_image_and_task( user_workflow) #print(input_arg_first, input_arg_last, first_task_name,last_task_name ) # get list of tasks @@ -606,8 +606,10 @@ def Workflow_Preview(self, # logger.info() # when using fields, self.time_preview.value - assert time_preview < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" - assert chan_preview < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" + if time_preview >= self.llsz_parent.lattice.time: + raise ValueError("Time is out of range") + if chan_preview >= self.llsz_parent.lattice.time: + raise ValueError("Channel is out of range") time = time_preview channel = chan_preview @@ -619,7 +621,7 @@ def Workflow_Preview(self, logger.info( f"Processing for Time: {time} and Channel: {channel}") - vol = LLSZWidget.LlszMenu.lattice.data + vol = self.llsz_parent.lattice.data vol_zyx = vol[time, channel, ...] vol_zyx = np.array(vol_zyx) @@ -634,7 +636,7 @@ def Workflow_Preview(self, psf = None otf_path = None - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: otf_path = "otf_path" else: psf = "psf" @@ -643,7 +645,7 @@ def Workflow_Preview(self, # get the function associated with the first task and check if its deskewing if Use_Cropping: # use deskewed volume for cropping function - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape + deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape deskewed_volume = da.zeros(deskewed_shape) z_start = 0 z_end = deskewed_shape[0] @@ -652,45 +654,44 @@ def Workflow_Preview(self, if len(roi_layer_list) == 1: roi_idx = 0 else: # else get the user selection - assert len( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data) > 0, "Please select an ROI" - roi_idx = list( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data)[0] + if len(self.llsz_parent.shapes_layer.selected_data) <= 0: + raise Exception("Please select an ROI") + roi_idx = list(self.llsz_parent.shapes_layer.selected_data)[0] roi_choice = roi_layer_list[roi_idx] # As the original image is scaled, the coordinates are in microns, so we need to convert # roi to from micron to canvas/world coordinates roi_choice = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_choice] + x/self.llsz_parent.lattice.dy for x in roi_choice] logger.info(f"Previewing ROI {roi_idx}") - if LLSZWidget.LlszMenu.deconvolution.value: + if self.llsz_parent.deconvolution: user_workflow.set("crop_deskew_image", crop_volume_deskew, original_volume=vol_zyx, deskewed_volume=deskewed_volume, roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + angle_in_degrees=self.llsz_parent.lattice.angle, + voxel_size_x=self.llsz_parent.lattice.dx, + voxel_size_y=self.llsz_parent.lattice.dy, + voxel_size_z=self.llsz_parent.lattice.dz, z_start=z_start, z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + deconvolution=self.llsz_parent.deconvolution, + decon_processing=self.llsz_parent.lattice.decon_processing, otf_path=otf_path, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - skew_dir=LLSZWidget.LlszMenu.skew_dir) + psf=self.llsz_parent.lattice.psf[channel], + skew_dir=self.llsz_parent.skew_dir) else: user_workflow.set("crop_deskew_image", crop_volume_deskew, original_volume=vol_zyx, deskewed_volume=deskewed_volume, roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + angle_in_degrees=self.llsz_parent.lattice.angle, + voxel_size_x=self.llsz_parent.lattice.dx, + voxel_size_y=self.llsz_parent.lattice.dy, + voxel_size_z=self.llsz_parent.lattice.dz, z_start=z_start, z_end=z_end, - skew_dir=LLSZWidget.LlszMenu.skew_dir) + skew_dir=self.llsz_parent.skew_dir) # Set input of the workflow to be crop_deskewing, i.e., the original first operation will now have crop_deskew_image as an input (becoming second instead) user_workflow.set( @@ -700,40 +701,40 @@ def Workflow_Preview(self, # Not cropping; If deskew not in workflow, append to start elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = LLSZWidget.LlszMenu.lattice.psf[channel] + if self.llsz_parent.deconvolution.value: + psf = self.llsz_parent.lattice.psf[channel] input_arg_first_decon, input_arg_last_decon, first_task_name_decon, last_task_name_decon = get_first_last_image_and_task( user_workflow) - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: user_workflow.set("deconvolution", pycuda_decon, image=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) + psf=self.llsz_parent.lattice.psf[channel], + dzdata=self.llsz_parent.lattice.dz, + dxdata=self.llsz_parent.lattice.dx, + dzpsf=self.llsz_parent.lattice.dz, + dxpsf=self.llsz_parent.lattice.dx, + num_iter=self.llsz_parent.lattice.psf_num_iter) # user_workflow.set(input_arg_first_decon,"deconvolution") else: user_workflow.set("deconvolution", skimage_decon, vol_zyx=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, + psf=self.llsz_parent.lattice.psf[channel], + num_iter=self.llsz_parent.lattice.psf_num_iter, clip=False, filter_epsilon=0, boundary='nearest') # user_workflow.set(input_arg_first_decon,"deconvolution") user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, + self.llsz_parent.deskew_func, "deconvolution", - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + angle_in_degrees=self.llsz_parent.lattice.angle, + voxel_size_x=self.llsz_parent.lattice.dx, + voxel_size_y=self.llsz_parent.lattice.dy, + voxel_size_z=self.llsz_parent.lattice.dz, linear_interpolation=True) # user_workflow.set("change_bitdepth",as_type,"deskew_image",vol_zyx) @@ -742,12 +743,12 @@ def Workflow_Preview(self, else: user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, + self.llsz_parent.deskew_func, vol_zyx, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, + angle_in_degrees=self.llsz_parent.lattice.angle, + voxel_size_x=self.llsz_parent.lattice.dx, + voxel_size_y=self.llsz_parent.lattice.dy, + voxel_size_z=self.llsz_parent.lattice.dz, linear_interpolation=True) # Set input of the workflow to be from deskewing # user_workflow.set(input_arg_first,"deskew_image") @@ -762,28 +763,28 @@ def Workflow_Preview(self, # if deskew already in workflow, just check if deconvolution needs to be added # repitition of above (maybe create a function?) # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = LLSZWidget.LlszMenu.lattice.psf[channel] + if self.llsz_parent.deconvolution.value: + psf = self.llsz_parent.lattice.psf[channel] input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( user_workflow) - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: user_workflow.set("deconvolution", pycuda_decon, image=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) + psf=self.llsz_parent.lattice.psf[channel], + dzdata=self.llsz_parent.lattice.dz, + dxdata=self.llsz_parent.lattice.dx, + dzpsf=self.llsz_parent.lattice.dz, + dxpsf=self.llsz_parent.lattice.dx, + num_iter=self.llsz_parent.lattice.psf_num_iter) # user_workflow.set(input_arg_first,"deconvolution") else: user_workflow.set("deconvolution", skimage_decon, vol_zyx=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, + psf=self.llsz_parent.lattice.psf[channel], + num_iter=self.llsz_parent.lattice.psf_num_iter, clip=False, filter_epsilon=0, boundary='nearest') @@ -867,16 +868,17 @@ def Apply_Workflow_and_Save(self, workflow_path (Path, optional): User can also choose a custom workflow defined in a yaml file. save_path (Path, optional): Path to save resulting data """ - assert LLSZWidget.LlszMenu.open_file, "Image not initialised" + if not self.llsz_parent.open_file: + raise Exception("Image not initialised") check_dimensions(time_start, time_end, ch_start, ch_end, - LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) + self.llsz_parent.lattice.channels, self.llsz_parent.lattice.time) # Get parameters - angle = LLSZWidget.LlszMenu.lattice.angle - dx = LLSZWidget.LlszMenu.lattice.dx - dy = LLSZWidget.LlszMenu.lattice.dy - dz = LLSZWidget.LlszMenu.lattice.dz + angle = self.llsz_parent.lattice.angle + dx = self.llsz_parent.lattice.dx + dy = self.llsz_parent.lattice.dy + dz = self.llsz_parent.lattice.dz if get_active_workflow: # installs the workflow to napari @@ -898,11 +900,11 @@ def Apply_Workflow_and_Save(self, modules = map( importlib.import_module, custom_py_files) print(f"Custom modules imported {modules}") - user_workflow = load_workflow(workflow_path) - - assert type( - user_workflow) is Workflow, "Workflow file is not a napari workflow object. Check file! You can use workflow inspector if needed" + user_workflow = load_workflow(str(workflow_path)) + if not isinstance(user_workflow, Workflow): + raise Exception("Workflow file is not a napari workflow object. Check file! You can use workflow inspector if needed") + input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( user_workflow) print(input_arg_first, input_arg_last, @@ -912,7 +914,7 @@ def Apply_Workflow_and_Save(self, print("Workflow loaded:") print(user_workflow) - vol = LLSZWidget.LlszMenu.lattice.data + vol = self.llsz_parent.lattice.data #vol_zyx= vol[time,channel,...] @@ -928,22 +930,21 @@ def Apply_Workflow_and_Save(self, psf = None otf_path = None - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: #otf_path = "otf_path" psf_arg = "psf" - psf = LLSZWidget.LlszMenu.lattice.psf + psf = self.llsz_parent.lattice.psf else: psf_arg = "psf" - psf = LLSZWidget.LlszMenu.lattice.psf + psf = self.llsz_parent.lattice.psf # if cropping, set that as first task if Use_Cropping: # convert Roi pixel coordinates to canvas coordinates # necessary only when scale is used for napari.viewer.add_image operations - roi_layer_list = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_layer_list] + roi_layer_list = [x/self.llsz_parent.lattice.dy for x in roi_layer_list] - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape + deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape deskewed_volume = da.zeros(deskewed_shape) z_start = 0 z_end = deskewed_shape[0] @@ -963,10 +964,10 @@ def Apply_Workflow_and_Save(self, voxel_size_z=dz, z_start=z_start, z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + deconvolution=self.llsz_parent.deconvolution.value, + decon_processing=self.llsz_parent.lattice.decon_processing, psf=psf_arg, - skew_dir=LLSZWidget.LlszMenu.skew_dir) + skew_dir=self.llsz_parent.skew_dir) # change the first task so it accepts "crop_deskew as input" new_task = modify_workflow_task( @@ -990,13 +991,13 @@ def Apply_Workflow_and_Save(self, #roi_layer = roi_layer, save_name_prefix="ROI_" + \ str(idx), - save_name=LLSZWidget.LlszMenu.lattice.save_name, + save_name=self.llsz_parent.lattice.save_name, dx=dx, dy=dy, dz=dz, angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + deconvolution=self.llsz_parent.deconvolution.value, + decon_processing=self.llsz_parent.lattice.decon_processing, otf_path=otf_path, psf_arg=psf_arg, psf=psf) @@ -1006,7 +1007,7 @@ def Apply_Workflow_and_Save(self, input = "input" # add task to the workflow user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, + self.llsz_parent.deskew_func, input_image=input, angle_in_degrees=angle, voxel_size_x=dx, @@ -1020,29 +1021,29 @@ def Apply_Workflow_and_Save(self, user_workflow.set(task_name_start, new_task) # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: + if self.llsz_parent.deconvolution.value: psf = "psf" otf_path = "otf_path" input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( user_workflow) - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: user_workflow.set("deconvolution", pycuda_decon, image=input, psf=psf_arg, - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) + dzdata=self.llsz_parent.lattice.dz, + dxdata=self.llsz_parent.lattice.dx, + dzpsf=self.llsz_parent.lattice.dz, + dxpsf=self.llsz_parent.lattice.dx, + num_iter=self.llsz_parent.lattice.psf_num_iter) # user_workflow.set(input_arg_first,"deconvolution") else: user_workflow.set("deconvolution", skimage_decon, vol_zyx=input, psf=psf_arg, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, + num_iter=self.llsz_parent.lattice.psf_num_iter, clip=False, filter_epsilon=0, boundary='nearest') @@ -1065,13 +1066,13 @@ def Apply_Workflow_and_Save(self, channel_end=ch_end, save_file_type=save_as_type, save_path=save_path, - save_name=LLSZWidget.LlszMenu.lattice.save_name, + save_name=self.llsz_parent.lattice.save_name, dx=dx, dy=dy, dz=dz, angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + deconvolution=self.llsz_parent.deconvolution.value, + decon_processing=self.llsz_parent.lattice.decon_processing, otf_path=otf_path, psf_arg=psf_arg, psf=psf) @@ -1080,29 +1081,29 @@ def Apply_Workflow_and_Save(self, else: # if deskewing is already first task, then check if deconvolution needed # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: + if self.llsz_parent.deconvolution.value: psf = "psf" otf_path = "otf_path" input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( user_workflow) - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: user_workflow.set("deconvolution", pycuda_decon, image=input, psf=psf_arg, - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) + dzdata=self.llsz_parent.lattice.dz, + dxdata=self.llsz_parent.lattice.dx, + dzpsf=self.llsz_parent.lattice.dz, + dxpsf=self.llsz_parent.lattice.dx, + num_iter=self.llsz_parent.lattice.psf_num_iter) # user_workflow.set(input_arg_first,"deconvolution") else: user_workflow.set("deconvolution", skimage_decon, vol_zyx=input, psf=psf_arg, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, + num_iter=self.llsz_parent.lattice.psf_num_iter, clip=False, filter_epsilon=0, boundary='nearest') @@ -1126,13 +1127,13 @@ def Apply_Workflow_and_Save(self, channel_end=ch_end, save_file_type=save_as_type, save_path=save_path, - save_name=LLSZWidget.LlszMenu.lattice.save_name, + save_name=self.llsz_parent.lattice.save_name, dx=dx, dy=dy, dz=dz, angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, + deconvolution=self.llsz_parent.deconvolution.value, + decon_processing=self.llsz_parent.lattice.decon_processing, otf_path=otf_path, psf_arg=psf_arg, psf=psf) diff --git a/plugin/napari_lattice/napari.yaml b/plugin/napari_lattice/napari.yaml index 9f0bc22..b83b0f0 100644 --- a/plugin/napari_lattice/napari.yaml +++ b/plugin/napari_lattice/napari.yaml @@ -2,9 +2,9 @@ name: napari-lattice display_name: Lattice Lightsheet Analysis contributions: commands: - - id: napari-lattice._dock_widget + - id: napari-lattice.dock_widget title: Create napari_lattice widget - python_name: napari_lattice._dock_widget:_napari_lattice_widget_wrapper + python_name: napari_lattice.dock_widget:_napari_lattice_widget_wrapper # ~~ Reader ~~ - id: napari-lattice.get_reader @@ -19,7 +19,7 @@ contributions: # python_name: napari_lattice.use_workflow:_workflow_widget widgets: - - command: napari-lattice._dock_widget + - command: napari-lattice.dock_widget display_name: Lattice Lightsheet Analysis #- command: napari-lattice.crop_deskew diff --git a/plugin/tests/test_dock_widget.py b/plugin/tests/test_dock_widget.py index 58b8b09..7d8e9ca 100644 --- a/plugin/tests/test_dock_widget.py +++ b/plugin/tests/test_dock_widget.py @@ -1,5 +1,5 @@ from __future__ import annotations -from napari_lattice._dock_widget import _napari_lattice_widget_wrapper +from napari_lattice.dock_widget import _napari_lattice_widget_wrapper import numpy as np from typing import Callable, TYPE_CHECKING from magicclass.testing import check_function_gui_buildable, FunctionGuiTester From 7e3a78f6f72f783c767b49af316b6e55bb73da04 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 26 Jul 2023 16:48:41 +1000 Subject: [PATCH 003/147] Clean up dock_widget --- plugin/napari_lattice/dock_widget.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 9b5ef7c..0ec2ec2 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -6,15 +6,12 @@ import dask.array as da from dask.array.core import Array as DaskArray import pandas as pd -from typing import Union, Optional, Callable, Literal -from typing_extensions import NoReturn, Never, TypeVar, overload, Any -from aicsimageio import AICSImage +from typing import Callable from enum import Enum from magicclass.wrappers import set_design from magicgui import magicgui from magicclass import magicclass, field, vfield, set_options, LlszTemplate -from magicclass._gui._base import check_override from magicclass.utils import click from qtpy.QtCore import Qt @@ -58,7 +55,7 @@ def llsz_parent(self) -> "LLSZWidget": @magicclass(widget_type="split") class LLSZWidget(LlszTemplate): open_file: bool = False - lattice: LatticeData = None + lattice: LatticeData skew_dir: DeskewDirection angle_value: float deskew_func: Callable From e3742c77508f0ab9d2316cd69ff4f56077a22d00 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 27 Jul 2023 10:40:06 +1000 Subject: [PATCH 004/147] Fix missing imports in deconvolution --- core/lls_core/deconvolution.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/core/lls_core/deconvolution.py b/core/lls_core/deconvolution.py index 60e4e8f..4222c84 100644 --- a/core/lls_core/deconvolution.py +++ b/core/lls_core/deconvolution.py @@ -1,12 +1,18 @@ from pathlib import Path + +from resource_backed_dask_array import ResourceBackedDaskArray from lls_core import DeconvolutionChoice from lls_core.lattice_data import LatticeData import logging import importlib.util -from typing import Collection, Iterable -from aicsimageio import AICSImage +from typing import Collection, Iterable, Optional +from aicsimageio.aics_image import AICSImage +from aicsimageio.types import ArrayLike from aicspylibczi import CziFile from numpy.typing import NDArray +import numpy as np +from dask.array.core import Array as DaskArray + from lls_core.utils import pad_image_nearest_multiple @@ -69,13 +75,13 @@ def read_psf(psf_paths: Collection[Path], # libraries are better designed.. Atleast until RL deconvolution is available in pyclesperant # Talley Lamberts pycudadecon is a great library and highly optimised. def pycuda_decon( - image, - otf_path=None, - dzdata=0.3, - dxdata=0.1449922, - dzpsf=0.3, - dxpsf=0.1449922, - psf=None, + image: NDArray, + otf_path: Optional[str]=None, + dzdata: float=0.3, + dxdata: float=0.1449922, + dzpsf: float=0.3, + dxpsf: float=0.1449922, + psf: Optional[ArrayLike]=None, num_iter: int = 10, cropping: bool = False, ): @@ -83,7 +89,7 @@ def pycuda_decon( pycudadecon can return cropped images, so we pad the images with dimensions that are a multiple of 64 Args: - image (np.array): _description_ + image : _description_ otf_path : (path to the generated otf file, if available. Otherwise psf needs to be provided) dzdata : (pixel size in z in microns) dxdata : (pixel size in xy in microns) @@ -97,10 +103,7 @@ def pycuda_decon( assert image.ndim == 3, f"Image needs to be 3D. Got {image.ndim}" # if dask array, convert to numpy array - if type(image) in [ - da.core.Array, - ResourceBackedDaskArray, - ]: + if isinstance(image, DaskArray): image = np.array(image) orig_img_shape = image.shape From 248a6452abaf75b51d0e25838ee8a0058864f774 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 28 Jul 2023 18:34:33 +1000 Subject: [PATCH 005/147] More dock_widget cleanup --- plugin/napari_lattice/dock_widget.py | 36 ++++++++++++++-------------- pyrightconfig.json | 5 ++-- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 0ec2ec2..cad7bb7 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -11,13 +11,14 @@ from magicclass.wrappers import set_design from magicgui import magicgui -from magicclass import magicclass, field, vfield, set_options, LlszTemplate +from magicclass import magicclass, field, vfield, set_options, MagicTemplate from magicclass.utils import click from qtpy.QtCore import Qt from napari.layers import Layer, Shapes from napari.types import ImageData from napari.utils import history +from napari import Viewer import pyclesperanto_prototype as cle @@ -47,10 +48,17 @@ class LastDimensionOptions(Enum): time = "Time" get_from_metadata = "Get from Metadata" -class LlszTemplate(LlszTemplate): +class LlszTemplate(MagicTemplate): @property def llsz_parent(self) -> "LLSZWidget": return self.find_ancestor(LLSZWidget) + + @property + def parent_viewer(self) -> Viewer: + viewer = super().parent_viewer + if viewer is None: + raise Exception("This function can only be used when inside of a Napari viewer") + return super().parent_viewer @magicclass(widget_type="split") class LLSZWidget(LlszTemplate): @@ -105,9 +113,6 @@ def Choose_Image_Layer(self, logger.info(f"Logging set to {set_logging}") logger.info("Using existing image layer") - if self.parent_viewer is None: - raise Exception("This function can only be used when inside of a Napari viewer") - # Select device for processing cle.select_device(select_device) @@ -195,8 +200,6 @@ def Choose_Image_Layer(self, self["Choose_Image_Layer"].background_color = "green" self["Choose_Image_Layer"].text = "Plugin Initialised" - return - # Pycudadecon library for deconvolution # options={"enabled": True}, deconvolution = vfield(bool, name="Use Deconvolution") @@ -304,7 +307,6 @@ def Deskew_Save(self, ch_end, save_as_type, save_path) - return @magicclass(name="Crop and Deskew", widget_type="scrollable") class CropWidget(LlszTemplate): @@ -319,8 +321,6 @@ class Preview_Crop_Menu(LlszTemplate): @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) def activate_cropping(self): - if self.parent_viewer is None: - raise Exception("This function can only be used when inside of a Napari viewer") self.llsz_parent.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', face_color=[1, 1, 1, 0], name="Cropping BBOX layer") # TO select ROIs if needed @@ -337,7 +337,7 @@ def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])) -> # convert to canvas coordinates self.find_ancestor(LLSZWidget) roi_list = (np.array(roi_list) * self.llsz_parent.lattice.dy).tolist() - self.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', face_color=[1, 1, 1, 0]) + self.llsz_parent.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', face_color=[1, 1, 1, 0]) time_crop = field(int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") chan_crop = field(int, options={"min": 0, "step": 1}, name="Channels") @@ -373,10 +373,10 @@ def Crop_Preview(self, roi_layer: ShapesData): # if only one roi drawn, use the first ROI for cropping if len(roi_layer) == 1: roi_idx = 0 - elif len(self.shapes_layer.selected_data) == 0: + elif len(self.llsz_parent.shapes_layer.selected_data) == 0: raise Exception("Please select an ROI") - roi_idx = list(self.shapes_layer.selected_data)[0] + roi_idx = list(self.llsz_parent.shapes_layer.selected_data)[0] # As the original image is scaled, the coordinates are in microns, so we need to convert # roi from micron to canvas/world coordinates @@ -514,7 +514,7 @@ def Crop_Save(self, voxel_size_x=dx, voxel_size_y=dy, voxel_size_z=dz, - LLSZWidget=parent + LLSZWidget=self.llsz_parent ) logger.info( @@ -661,7 +661,7 @@ def Workflow_Preview(self, roi_choice = [ x/self.llsz_parent.lattice.dy for x in roi_choice] logger.info(f"Previewing ROI {roi_idx}") - if self.llsz_parent.deconvolution: + if self.llsz_parent.deconvolution and self.llsz_parent.lattice.psf is not None: user_workflow.set("crop_deskew_image", crop_volume_deskew, original_volume=vol_zyx, deskewed_volume=deskewed_volume, @@ -760,7 +760,7 @@ def Workflow_Preview(self, # if deskew already in workflow, just check if deconvolution needs to be added # repitition of above (maybe create a function?) # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if self.llsz_parent.deconvolution.value: + if self.llsz_parent.deconvolution: psf = self.llsz_parent.lattice.psf[channel] input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( user_workflow) @@ -840,12 +840,12 @@ def Workflow_Preview(self, #custom_module=dict(widget_type="Checkbox",label="Load custom module (same dir as workflow)",value = False), call_button="Apply Workflow and Save Result") def Apply_Workflow_and_Save(self, - header, + header: str, time_start: int, time_end: int, ch_start: int, ch_end: int, - Use_Cropping, + Use_Cropping: bool, roi_layer_list: ShapesData, get_active_workflow: bool = False, workflow_path: Path = Path.home(), diff --git a/pyrightconfig.json b/pyrightconfig.json index 6703ee2..bf4441a 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -7,5 +7,6 @@ "reportUnknownParameterType": false, "reportUntypedFunctionDecorator": false, "reportMissingTypeArgument": false, - "reportPrivateUsage": false -} \ No newline at end of file + "reportPrivateUsage": false, + "reportPrivateImportUsage": false +} From 695b0ea221cdfd286be02a8950defd32fbe8dec4 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 2 Aug 2023 17:22:24 +1000 Subject: [PATCH 006/147] Type signature tidying, and remove an unused function --- core/lls_core/__init__.py | 10 +-- core/lls_core/utils.py | 116 ++------------------------- plugin/napari_lattice/dock_widget.py | 10 ++- 3 files changed, 16 insertions(+), 120 deletions(-) diff --git a/core/lls_core/__init__.py b/core/lls_core/__init__.py index 320f378..b292cad 100644 --- a/core/lls_core/__init__.py +++ b/core/lls_core/__init__.py @@ -1,23 +1,19 @@ __version__ = "0.2.6" - +from strenum import StrEnum from enum import Enum # Initialize configuration options -#Deskew Direction -from pyclesperanto_prototype._tier8._affine_transform_deskew_3d import DeskewDirection - - #Choice of Deconvolution -class DeconvolutionChoice(Enum): +class DeconvolutionChoice(StrEnum): cuda_gpu = "cuda_gpu" opencl_gpu = "opencl_gpu" cpu = "cpu" #Choice of File extension to save -class SaveFileType(Enum): +class SaveFileType(StrEnum): h5 = "h5" tiff = "tiff" diff --git a/core/lls_core/utils.py b/core/lls_core/utils.py index 84c014f..db9b34d 100644 --- a/core/lls_core/utils.py +++ b/core/lls_core/utils.py @@ -6,7 +6,7 @@ from os import devnull, path import os from typing_extensions import Any, TYPE_CHECKING, TypeGuard -from typing import List, Tuple, Union, Collection +from typing import Iterable, List, Optional, Sequence, Tuple, Union, Collection from numpy.typing import NDArray import pandas as pd @@ -18,7 +18,8 @@ from tifffile import imsave from . import config, DeskewDirection -from aicsimageio.types import ArrayLike + +from lls_core.types import ArrayLike if TYPE_CHECKING: from xml.etree.ElementTree import Element @@ -334,9 +335,9 @@ def as_type(img, ref_vol): return img -def process_custom_workflow_output(workflow_output, - save_dir=None, - idx=None, +def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike], + save_dir: Optional[str]=None, + idx: Optional[str]=None, LLSZWidget=None, widget_class=None, channel=0, @@ -376,109 +377,6 @@ def process_custom_workflow_output(workflow_output, else: return workflow_output - -def _process_custom_workflow_output_batch(ref_vol, - no_elements, - array_element_type, - channel_range, - images_array, - save_path, - time_point, - ch, - save_name_prefix, - save_name, - dx=None, - dy=None, - new_dz=None - ): - # create columns index for the list - if list in array_element_type: - row_idx = [] - - # Iterate through the dict or list output from workflow and add columns for Channel and timepoint - for i in range(no_elements): - for j in channel_range: - if type(images_array[j, i]) in [dict]: - # images_array[j,i].update({"Channel/Time":"C"+str(j)+"T"+str(time_point)}) - images_array[j, i].update({"Channel": "C"+str(j)}) - images_array[j, i].update({"Time": "T"+str(time_point)}) - elif type(images_array[j, i]) in [list]: - row_idx.append("C"+str(j)+"T"+str(time_point)) - # row_idx.append("C"+str(j)) - # row_idx.append("T"+str(time_point)) - - for element in range(no_elements): - if(array_element_type[element]) in [dict]: - # convert to pandas dataframe - output_dict_pd = [pd.DataFrame(i) - for i in images_array[:, element]] - - output_dict_pd = pd.concat(output_dict_pd) - # set index to the channel/time - output_dict_pd = output_dict_pd.set_index(["Time", "Channel"]) - - # Save path - dict_save_path = os.path.join( - save_path, "Measurement_"+save_name_prefix) - if not(os.path.exists(dict_save_path)): - os.mkdir(dict_save_path) - - #dict_save_path = os.path.join(dict_save_path,"C" + str(ch) + "T" + str(time_point)+"_"+str(element) + "_measurement.csv") - dict_save_path = os.path.join( - dict_save_path, "Summary_measurement_"+save_name_prefix+"_"+str(element)+"_.csv") - # Opens csv and appends it if file already exists; not efficient. - if os.path.exists(dict_save_path): - output_dict_pd_existing = pd.read_csv( - dict_save_path, index_col=["Time", "Channel"]) - output_dict_summary = pd.concat( - (output_dict_pd_existing, output_dict_pd)) - output_dict_summary.to_csv(dict_save_path) - else: - output_dict_pd.to_csv(dict_save_path) - - # TODO:modify this so one file saved for measurement - elif(array_element_type[element]) in [list]: - - output_list_pd = pd.DataFrame( - np.vstack(images_array[:, element]), index=row_idx) - # Save path - list_save_path = os.path.join( - save_path, "Measurement_"+save_name_prefix) - if not(os.path.exists(list_save_path)): - os.mkdir(list_save_path) - list_save_path = os.path.join(list_save_path, "C" + str(ch) + "T" + str( - time_point)+"_"+save_name_prefix+"_"+str(element) + "_measurement.csv") - output_list_pd.to_csv(list_save_path) - - elif(array_element_type[element]) in [np.ndarray, cle._tier0._pycl.OCLArray, da.core.Array]: - - # Save path - img_save_path = os.path.join( - save_path, "Measurement_"+save_name_prefix) - if not(os.path.exists(img_save_path)): - os.mkdir(img_save_path) - - im_final = np.stack(images_array[:, element]).astype(ref_vol.dtype) - final_name = os.path.join(img_save_path, save_name_prefix + "_"+str(element) + "_T" + str( - time_point) + "_" + save_name + ".tif") - # "C" + str(ch) + - #OmeTiffWriter.save(images_array, final_name, physical_pixel_sizes=aics_image_pixel_sizes) - # if only one image with no channel, then dimension will 1,z,y,x, so swap 0 and 1 - if len(im_final.shape) == 4: - # was 1,2,but when stacking images, dimension is CZYX - im_final = np.swapaxes(im_final, 0, 1) - # adding extra dimension for T - im_final = im_final[np.newaxis, ...] - elif len(im_final.shape) > 4: # if - # if image with multiple channels, , it will be 1,c,z,y,x - im_final = np.swapaxes(im_final, 1, 2) - # imagej=True; ImageJ hyperstack axes must be in TZCYXS order - imsave(final_name, im_final, bigtiff=True, imagej=True, resolution=(1./dx, 1./dy), - metadata={'spacing': new_dz, 'unit': 'um', 'axes': 'TZCYX'}) # imagej=True - im_final = None - return - - def pad_image_nearest_multiple(img: NDArray, nearest_multiple: int) -> NDArray: """pad an Image to the nearest multiple of provided number @@ -500,7 +398,7 @@ def pad_image_nearest_multiple(img: NDArray, nearest_multiple: int) -> NDArray: return padded_img -def check_dimensions(user_time_start: int, user_time_end, user_channel_start: int, user_channel_end: int, total_channels: int, total_time: int): +def check_dimensions(user_time_start: int, user_time_end: int, user_channel_start: int, user_channel_end: int, total_channels: int, total_time: int): if total_time == 1 or total_time == 2: max_time = 1 diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index cad7bb7..f0a15c1 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -350,6 +350,8 @@ def Crop_Preview(self, roi_layer: ShapesData): if not roi_layer: raise Exception("No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs.") # TODO: Add assertion to check if bbox layer or coordinates + + # Slice out the image of interest to preview time = self.time_crop.value channel = self.chan_crop.value if time >= self.llsz_parent.lattice.time: @@ -1018,7 +1020,7 @@ def Apply_Workflow_and_Save(self, user_workflow.set(task_name_start, new_task) # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if self.llsz_parent.deconvolution.value: + if self.llsz_parent.deconvolution: psf = "psf" otf_path = "otf_path" input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( @@ -1068,7 +1070,7 @@ def Apply_Workflow_and_Save(self, dy=dy, dz=dz, angle=angle, - deconvolution=self.llsz_parent.deconvolution.value, + deconvolution=self.llsz_parent.deconvolution, decon_processing=self.llsz_parent.lattice.decon_processing, otf_path=otf_path, psf_arg=psf_arg, @@ -1078,7 +1080,7 @@ def Apply_Workflow_and_Save(self, else: # if deskewing is already first task, then check if deconvolution needed # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if self.llsz_parent.deconvolution.value: + if self.llsz_parent.deconvolution: psf = "psf" otf_path = "otf_path" input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( @@ -1129,7 +1131,7 @@ def Apply_Workflow_and_Save(self, dy=dy, dz=dz, angle=angle, - deconvolution=self.llsz_parent.deconvolution.value, + deconvolution=self.llsz_parent.deconvolution, decon_processing=self.llsz_parent.lattice.decon_processing, otf_path=otf_path, psf_arg=psf_arg, From 9752c68dd59c3d0fb03a3b54ce85299e34e0dbf8 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 4 Aug 2023 12:10:21 +1000 Subject: [PATCH 007/147] Refactor workflow augmentation --- core/lls_core/__init__.py | 1 + core/lls_core/io.py | 1 - core/lls_core/types.py | 11 + core/lls_core/utils.py | 180 ++------- core/lls_core/workflow.py | 198 +++++++++ plugin/napari_lattice/dock_widget.py | 577 ++++++++++++--------------- pyrightconfig.json | 3 +- 7 files changed, 487 insertions(+), 484 deletions(-) create mode 100644 core/lls_core/types.py create mode 100644 core/lls_core/workflow.py diff --git a/core/lls_core/__init__.py b/core/lls_core/__init__.py index b292cad..9d9bfb6 100644 --- a/core/lls_core/__init__.py +++ b/core/lls_core/__init__.py @@ -3,6 +3,7 @@ from strenum import StrEnum from enum import Enum +from pyclesperanto_prototype._tier8._affine_transform_deskew_3d import DeskewDirection # Initialize configuration options diff --git a/core/lls_core/io.py b/core/lls_core/io.py index ca4fdbb..78b25fe 100644 --- a/core/lls_core/io.py +++ b/core/lls_core/io.py @@ -615,4 +615,3 @@ def save_img_workflow(vol, writer_list[writer_idx].write_xml() # close the writers (applies for both tiff and h5) writer_list[writer_idx].close() - diff --git a/core/lls_core/types.py b/core/lls_core/types.py new file mode 100644 index 0000000..aa2b889 --- /dev/null +++ b/core/lls_core/types.py @@ -0,0 +1,11 @@ +from typing import Union +from typing_extensions import TypeGuard, Any, TypeAlias +from dask.array.core import Array as DaskArray +from numpy.typing import NDArray +from pyopencl.array import Array as OCLArray +import numpy as np + +ArrayLike: TypeAlias = Union[DaskArray, NDArray, OCLArray] + +def is_arraylike(arr: Any) -> TypeGuard[ArrayLike]: + return isinstance(arr, (DaskArray, np.ndarray, OCLArray)) diff --git a/core/lls_core/utils.py b/core/lls_core/utils.py index db9b34d..bd82f9b 100644 --- a/core/lls_core/utils.py +++ b/core/lls_core/utils.py @@ -1,33 +1,27 @@ from __future__ import annotations -import numpy as np from collections import defaultdict from contextlib import contextmanager, redirect_stderr, redirect_stdout from os import devnull, path -import os -from typing_extensions import Any, TYPE_CHECKING, TypeGuard -from typing import Iterable, List, Optional, Sequence, Tuple, Union, Collection -from numpy.typing import NDArray - -import pandas as pd -import dask.array as da +from typing import Collection, List, Tuple, Union +import numpy as np import pyclesperanto_prototype as cle -from read_roi import read_roi_zip -from read_roi import read_roi_file - -from tifffile import imsave -from . import config, DeskewDirection - from lls_core.types import ArrayLike +from numpy.typing import NDArray +from read_roi import read_roi_file, read_roi_zip +from typing_extensions import TYPE_CHECKING, Any, TypeGuard + +from . import DeskewDirection, config if TYPE_CHECKING: from xml.etree.ElementTree import Element + from napari.layers import Shapes - from napari_workflows import Workflow # Enable Logging import logging + logger = logging.getLogger(__name__) logger.setLevel(config.log_level) @@ -105,7 +99,9 @@ def get_deskewed_shape(volume: ArrayLike, tuple: Shape of deskewed volume in zyx np.array: Affine transform for deskewing """ - from pyclesperanto_prototype._tier8._affine_transform import _determine_translation_and_bounding_box + from pyclesperanto_prototype._tier8._affine_transform import ( + _determine_translation_and_bounding_box, + ) deskew_transform = cle.AffineTransform3D() @@ -238,145 +234,6 @@ def read_imagej_roi(roi_zip_path: str): ij_roi[k], ".Recognised as type ", ij_roi[k]['type']) return roi_list -# Functions to deal with cle workflow -# TODO: Clean up this function - - -def get_first_last_image_and_task(user_workflow: Workflow): - """Get images and tasks for first and last entry - Args: - user_workflow (Workflow): _description_ - Returns: - list: name of first input image, last input image, first task, last task - """ - - # get image with no preprocessing step (first image) - input_arg_first = user_workflow.roots()[0] - # get last image - input_arg_last = user_workflow.leafs()[0] - # get name of preceding image as that is the input to last task - img_source = user_workflow.sources_of(input_arg_last)[0] - first_task_name = [] - last_task_name = [] - - # loop through workflow keys and get key that has - for key in user_workflow._tasks.keys(): - for task in user_workflow._tasks[key]: - if task == input_arg_first: - first_task_name.append(key) - elif task == img_source: - last_task_name.append(key) - - return input_arg_first, img_source, first_task_name, last_task_name - - -def modify_workflow_task(old_arg, task_key: str, new_arg, workflow): - """_Modify items in a workflow task - Workflow is not modified, only a new task with updated arg is returned - Args: - old_arg (_type_): The argument in the workflow that needs to be modified - new_arg (_type_): New argument - task_key (str): Name of the task within the workflow - workflow (napari-workflow): Workflow - - Returns: - tuple: Modified task with name task_key - """ - task = workflow._tasks[task_key] - # convert tuple to list for modification - task_list = list(task) - try: - item_index = task_list.index(old_arg) - except ValueError: - print(old_arg, " not found in workflow file") - task_list[item_index] = new_arg - modified_task = tuple(task_list) - return modified_task - -def load_custom_py_modules(custom_py_files): - from importlib import reload, import_module - import sys - test_first_module_import = import_module(custom_py_files[0]) - if test_first_module_import not in sys.modules: - modules = map(import_module, custom_py_files) - else: - modules = map(reload, custom_py_files) - return modules - - -# TODO: CHANGE so user can select modules? Safer -def get_all_py_files(directory: str) -> list[str]: - """get all py files within directory and return as a list of filenames - Args: - directory: Directory with .py files - """ - from os.path import dirname, basename, isfile, join - import glob - - modules = glob.glob(join(dirname(directory), "*.py")) - all = [basename(f)[:-3] for f in modules if isfile(f) - and not f.endswith('__init__.py')] - print(f"Files found are: {all}") - - return all - - -def as_type(img, ref_vol): - """return image same dtype as ref_vol - - Args: - img (_type_): _description_ - ref_vol (_type_): _description_ - - Returns: - _type_: _description_ - """ - img.astype(ref_vol.dtype) - return img - - -def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike], - save_dir: Optional[str]=None, - idx: Optional[str]=None, - LLSZWidget=None, - widget_class=None, - channel=0, - time=0, - preview: bool = True): - """Check the output from a custom workflow; - saves tables and images separately - - Args: - workflow_output (_type_): _description_ - save_dir (_type_): _description_ - idx (_type_): _description_ - LLSZWidget (_type_): _description_ - widget_class (_type_): _description_ - channel (_type_): _description_ - time (_type_): _description_ - """ - if type(workflow_output) in [dict, list]: - # create function for tthis dataframe bit - df = pd.DataFrame(workflow_output) - if preview: - save_path = path.join( - save_dir, "lattice_measurement_"+str(idx)+".csv") - print(f"Detected a dictionary as output, saving preview at", save_path) - df.to_csv(save_path, index=False) - return df - - else: - return df - elif type(workflow_output) in [np.ndarray, cle._tier0._pycl.OCLArray, da.core.Array]: - if preview: - suffix_name = str(idx)+"_c" + str(channel) + "_t" + str(time) - scale = (LLSZWidget.LlszMenu.lattice.new_dz, - LLSZWidget.LlszMenu.lattice.dy, LLSZWidget.LlszMenu.lattice.dx) - widget_class.parent_viewer.add_image( - workflow_output, name="Workflow_preview_" + suffix_name, scale=scale) - else: - return workflow_output - def pad_image_nearest_multiple(img: NDArray, nearest_multiple: int) -> NDArray: """pad an Image to the nearest multiple of provided number @@ -471,3 +328,16 @@ def crop_psf(psf_img: np.ndarray, threshold: float = 3e-3): psf_crop = psf_img[min_z:max_z, min_y:max_y, min_x:max_x] return psf_crop + +def as_type(img, ref_vol): + """return image same dtype as ref_vol + + Args: + img (_type_): _description_ + ref_vol (_type_): _description_ + + Returns: + _type_: _description_ + """ + img.astype(ref_vol.dtype) + return img diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py new file mode 100644 index 0000000..cb44d39 --- /dev/null +++ b/core/lls_core/workflow.py @@ -0,0 +1,198 @@ +""" +Functions related to manipulating Napari Workflows +""" +from __future__ import annotations + +from os import path +from pathlib import Path +from typing import List, Optional, Tuple, Union + +import dask.array as da +import numpy as np +import pandas as pd +import pyclesperanto_prototype as cle +from lls_core.types import ArrayLike +from typing_extensions import TYPE_CHECKING + +if TYPE_CHECKING: + from napari_workflows import Workflow + +import logging +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +def get_first_last_image_and_task(user_workflow: Workflow) -> Tuple[str, str, str, str]: + """ + Get images and tasks for first and last entry + Returns: + Tuple of (name of first input image, name of last input image, name of first task, name of last task) + """ + + # get image with no preprocessing step (first image) + input_arg_first = user_workflow.roots()[0] + # get last image + input_arg_last = user_workflow.leafs()[0] + # get name of preceding image as that is the input to last task + img_source = user_workflow.sources_of(input_arg_last)[0] + first_task_name = [] + last_task_name = [] + + # loop through workflow keys and get key that has + for key in user_workflow._tasks.keys(): + for task in user_workflow._tasks[key]: + if task == input_arg_first: + first_task_name.append(key) + elif task == img_source: + last_task_name.append(key) + + return input_arg_first, img_source, first_task_name[0], last_task_name[0] if len(last_task_name) > 0 else first_task_name[0] + + +def modify_workflow_task(old_arg: str, task_key: str, new_arg: str, workflow: Workflow) -> tuple: + """ + Modify items in a workflow task + Workflow is not modified, only a new task with updated arg is returned + Args: + old_arg: The argument in the workflow that needs to be modified + new_arg: New argument + task_key: Name of the task within the workflow + workflow: Workflow + + Returns: + tuple: Modified task with name task_key + """ + task = workflow._tasks[task_key] + # convert tuple to list for modification + task_list = list(task) + try: + item_index = task_list.index(old_arg) + except ValueError: + raise Exception(f"{old_arg} not found in workflow file") + + task_list[item_index] = new_arg + modified_task = tuple(task_list) + return modified_task + +def replace_first_arg(workflow: Workflow, new_arg: str, old_arg: Optional[str] = None): + """ + Replaces an argument in the first task of a workflow with some other value + Args: + old_arg: If provided, the name of an argument to replace, otherwise it defaults to the + first workflow arg + """ + img_arg_first, _, first_task_name, _ = get_first_last_image_and_task(workflow) + if old_arg is None: + old_arg = img_arg_first + workflow.set( + first_task_name, + modify_workflow_task( + old_arg=old_arg, + task_key=first_task_name, + new_arg=new_arg, + workflow=workflow + ) + ) + +def load_custom_py_modules(custom_py_files): + import sys + from importlib import import_module, reload + test_first_module_import = import_module(custom_py_files[0]) + if test_first_module_import not in sys.modules: + modules = map(import_module, custom_py_files) + else: + modules = map(reload, custom_py_files) + return modules + + +# TODO: CHANGE so user can select modules? Safer +def get_all_py_files(directory: str) -> list[str]: + """get all py files within directory and return as a list of filenames + Args: + directory: Directory with .py files + """ + import glob + from os.path import basename, dirname, isfile, join + + modules = glob.glob(join(dirname(directory), "*.py")) + all = [basename(f)[:-3] for f in modules if isfile(f) + and not f.endswith('__init__.py')] + print(f"Files found are: {all}") + + return all + +def import_script(script: Path): + """ + Imports a Python script given its path + """ + import importlib.util + import sys + module_name = script.stem + spec = importlib.util.spec_from_file_location(module_name, script) + if spec is None or spec.loader is None: + raise Exception(f"Failed to import {script}!") + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + +def import_workflow_modules(workflow: Path): + """ + Imports all the Python files that might be used in a given custom workflow + + Args: + workflow: Path to the workflow YAML file + """ + counter = 0 + for script in workflow.parent.glob("*.py"): + if script.stem == "__init__": + # Skip __init__.py + continue + import_script(script) + counter += 1 + + if counter == 0: + logger.warn(f"No custom modules imported. If you'd like to use a custom module, place a *.py file in same folder as the workflow file {workflow.parent}") + else: + logger.info(f"{counter} custom modules imported") + + +def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike], + save_dir: Optional[str]=None, + idx: Optional[str]=None, + LLSZWidget=None, + widget_class=None, + channel=0, + time=0, + preview: bool = True): + """Check the output from a custom workflow; + saves tables and images separately + + Args: + workflow_output (_type_): _description_ + save_dir (_type_): _description_ + idx (_type_): _description_ + LLSZWidget (_type_): _description_ + widget_class (_type_): _description_ + channel (_type_): _description_ + time (_type_): _description_ + """ + if isinstance(workflow_output, (dict, list)): + # create function for tthis dataframe bit + df = pd.DataFrame(workflow_output) + if preview: + save_path = path.join( + save_dir, "lattice_measurement_"+str(idx)+".csv") + print(f"Detected a dictionary as output, saving preview at", save_path) + df.to_csv(save_path, index=False) + return df + + else: + return df + elif isinstance(workflow_output, (np.ndarray, cle._tier0._pycl.OCLArray, da.core.Array)): + if preview: + suffix_name = str(idx)+"_c" + str(channel) + "_t" + str(time) + scale = (LLSZWidget.LlszMenu.lattice.new_dz, + LLSZWidget.LlszMenu.lattice.dy, LLSZWidget.LlszMenu.lattice.dx) + widget_class.parent_viewer.add_image( + workflow_output, name="Workflow_preview_" + suffix_name, scale=scale) + else: + return workflow_output diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index f0a15c1..a11f53b 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -4,9 +4,8 @@ import numpy as np from pathlib import Path import dask.array as da -from dask.array.core import Array as DaskArray import pandas as pd -from typing import Callable +from typing import allable, Iterable, Optional, Union from enum import Enum from magicclass.wrappers import set_design @@ -29,15 +28,19 @@ from napari_workflows import Workflow, WorkflowManager from napari_workflows._io_yaml_v1 import load_workflow -from lls_core import config, DeskewDirection, DeconvolutionChoice, SaveFileType, Log_Levels +from lls_core import config, DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection from lls_core.io import LatticeData, save_img, save_img_workflow -from lls_core.utils import read_imagej_roi, get_first_last_image_and_task, modify_workflow_task, get_all_py_files, as_type, process_custom_workflow_output, check_dimensions, load_custom_py_modules +from lls_core.types import ArrayLike +from lls_core.workflow import get_first_last_image_and_task, modify_workflow_task, get_all_py_files, process_custom_workflow_output, load_custom_py_modules, import_workflow_modules, replace_first_arg +from lls_core.utils import check_dimensions, read_imagej_roi, as_type from lls_core.llsz_core import crop_volume_deskew from lls_core.deconvolution import read_psf, pycuda_decon, skimage_decon from napari_lattice.ui_core import _Preview, _Deskew_Save from napari_lattice.reader import lattice_from_napari +from copy import copy + # Enable Logging import logging logger = logging.getLogger(__name__) @@ -345,7 +348,6 @@ def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])) -> #roi_idx = field(int, options={"min": 0, "step": 1}, name="ROI number") @click(enabled=False) - # -> LayerDataTuple: def Crop_Preview(self, roi_layer: ShapesData): if not roi_layer: raise Exception("No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs.") @@ -360,22 +362,8 @@ def Crop_Preview(self, roi_layer: ShapesData): raise ValueError("Channel is out of range") logger.info(f"Using channel {channel} and time {time}") - vol = self.llsz_parent.lattice.data - vol_zyx = vol[time, channel, ...] - vol_zyx = np.array(vol_zyx) - - deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape - # Create a dask array same shape as deskewed image - deskewed_volume: DaskArray = da.zeros(deskewed_shape) - - # Option for entering custom z start value? - z_start = 0 - z_end = deskewed_shape[0] - # if only one roi drawn, use the first ROI for cropping - if len(roi_layer) == 1: - roi_idx = 0 - elif len(self.llsz_parent.shapes_layer.selected_data) == 0: + if len(self.llsz_parent.shapes_layer.selected_data) == 0: raise Exception("Please select an ROI") roi_idx = list(self.llsz_parent.shapes_layer.selected_data)[0] @@ -386,58 +374,46 @@ def Crop_Preview(self, roi_layer: ShapesData): logger.info(f"Previewing ROI {roi_idx}") # crop + + # Set the deconvolution options if self.llsz_parent.deconvolution: if not self.llsz_parent.lattice.psf or not self.llsz_parent.lattice.psf_num_iter or not self.llsz_parent.lattice.decon_processing: - raise Exception("PSF fields should be set by this point!") + raise Exception( + "PSF fields should be set by this point!") logger.info( f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") - #psf = self.llsz_parent.lattice.psf[channel] - if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=self.llsz_parent.angle_value, - voxel_size_x=self.llsz_parent.lattice.dx, - voxel_size_y=self.llsz_parent.lattice.dy, - voxel_size_z=self.llsz_parent.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=self.llsz_parent.deconvolution, - decon_processing=self.llsz_parent.lattice.decon_processing.value, - psf=self.llsz_parent.lattice.psf[channel], - num_iter=self.llsz_parent.lattice.psf_num_iter).astype(vol_zyx.dtype) - else: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=self.llsz_parent.angle_value, - voxel_size_x=self.llsz_parent.lattice.dx, - voxel_size_y=self.llsz_parent.lattice.dy, - voxel_size_z=self.llsz_parent.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=self.llsz_parent.deconvolution, - decon_processing=self.llsz_parent.lattice.decon_processing.value, - psf=self.llsz_parent.lattice.psf[channel], - num_iter=self.llsz_parent.lattice.psf_num_iter).astype(vol_zyx.dtype) + decon_kwargs = dict( + decon_processing=self.llsz_parent.lattice.decon_processing.value, + psf=self.llsz_parent.lattice.psf[channel], + num_iter=self.llsz_parent.lattice.psf_num_iter + ) else: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=self.llsz_parent.angle_value, - voxel_size_x=self.llsz_parent.lattice.dx, - voxel_size_y=self.llsz_parent.lattice.dy, - voxel_size_z=self.llsz_parent.lattice.dz, - z_start=z_start, - z_end=z_end, - skew_dir=self.llsz_parent.skew_dir).astype(vol_zyx.dtype) - crop_roi_vol_desk = cle.pull(crop_roi_vol_desk) + decon_kwargs = dict() + + crop_roi_vol_desk = cle.pull( + crop_volume_deskew( + original_volume=np.array(self.llsz_parent.lattice.data[time, channel, ...]), + roi_shape=roi_choice, + angle_in_degrees=self.llsz_parent.angle_value, + voxel_size_x=self.llsz_parent.lattice.dx, + voxel_size_y=self.llsz_parent.lattice.dy, + voxel_size_z=self.llsz_parent.lattice.dz, + deconvolution=self.llsz_parent.deconvolution, + # Option for entering custom z start value? + z_start=0, + z_end=self.llsz_parent.lattice.deskew_vol_shape[0], + skew_dir=self.llsz_parent.skew_dir, + **decon_kwargs + ).astype(self.llsz_parent.lattice.data.dtype) + ) # get array back from gpu or addding cle array to napari can throw errors - scale = (self.llsz_parent.lattice.new_dz, - self.llsz_parent.lattice.dy, - self.llsz_parent.lattice.dx) + scale = ( + self.llsz_parent.lattice.new_dz, + self.llsz_parent.lattice.dy, + self.llsz_parent.lattice.dx + ) self.parent_viewer.add_image( crop_roi_vol_desk, scale=scale) @@ -549,65 +525,22 @@ def Workflow_Preview(self, roi_layer_list: ShapesData, workflow_path: Path = Path.home()): """ - Apply napari_workflows to the processing pipeline - User can define a pipeline which can be inspected in napari workflow inspector - and then execute it by ticking the get active workflow checkbox, - OR - Use a predefined workflow - - In both cases, if deskewing is not present as first step, it will be added on - and rest of the task will be made followers - Args: - + Apply napari_workflows to the processing pipeline + User can define a pipeline which can be inspected in napari workflow inspector + and then execute it by ticking the get active workflow checkbox, + OR + Use a predefined workflow + + In both cases, if deskewing is not present as first step, it will be added on + and rest of the task will be made followers """ - print("Previewing deskewed channel and time with workflow") - if get_active_workflow: - # installs the workflow to napari - user_workflow = WorkflowManager.install(self.parent_viewer).workflow - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - logger.info("Workflow loaded from napari") - else: - try: - # Automatically scan workflow file directory for *.py files. - # If it findss one, load it as a module - - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - sys.path.append(parent_dir) - custom_py_files = get_all_py_files(parent_dir) - if len(custom_py_files) == 0: - logger.error( - f"No custom modules imported. If you'd like to use a cusotm module, place a *.py file in same folder as the workflow file {parent_dir}") - else: - modules = load_custom_py_modules( - custom_py_files) - - logger.info( - f"Custom modules imported {modules}") - user_workflow = load_workflow( - workflow_path.__str__()) - except yaml.loader.ConstructorError as e: - raise Exception("\033[91m While loading workflow, got the following error which may mean you need to install the corresponding module in your Python environment: \033[0m") from e - - #user_workflow = load_workflow(workflow_path) - logger.info("Workflow loaded from file") - - if not isinstance(user_workflow, Workflow): - raise Exception("Workflow loading error. Check if file is workflow or if required libraries are installed") - - input_arg_first, _, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - #print(input_arg_first, input_arg_last, first_task_name,last_task_name ) - # get list of tasks - task_list = list(user_workflow._tasks.keys()) - logger.info(f"Workflow loaded:{user_workflow}") - # logger.info() + logger.info("Previewing deskewed channel and time with workflow") + user_workflow = get_workflow(self.parent_viewer if get_active_workflow else workflow_path) # when using fields, self.time_preview.value if time_preview >= self.llsz_parent.lattice.time: raise ValueError("Time is out of range") - if chan_preview >= self.llsz_parent.lattice.time: + if chan_preview >= self.llsz_parent.lattice.channels: raise ValueError("Channel is out of range") time = time_preview @@ -617,180 +550,7 @@ def Workflow_Preview(self, config.channel = channel config.time = time - logger.info( - f"Processing for Time: {time} and Channel: {channel}") - - vol = self.llsz_parent.lattice.data - vol_zyx = vol[time, channel, ...] - vol_zyx = np.array(vol_zyx) - - task_name_start = first_task_name[0] - try: - task_name_last = last_task_name[0] - except IndexError: - task_name_last = task_name_start - - # variables to hold task name, initialize it as None - # if gpu, set otf_path, otherwise use psf - psf = None - otf_path = None - - if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - otf_path = "otf_path" - else: - psf = "psf" - - # if cropping, set that as first task - # get the function associated with the first task and check if its deskewing - if Use_Cropping: - # use deskewed volume for cropping function - deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - if user_workflow.get_task(task_name_start)[0] not in [crop_volume_deskew]: - # if only one roi drawn, use the first ROI for cropping - if len(roi_layer_list) == 1: - roi_idx = 0 - else: # else get the user selection - if len(self.llsz_parent.shapes_layer.selected_data) <= 0: - raise Exception("Please select an ROI") - roi_idx = list(self.llsz_parent.shapes_layer.selected_data)[0] - - roi_choice = roi_layer_list[roi_idx] - # As the original image is scaled, the coordinates are in microns, so we need to convert - # roi to from micron to canvas/world coordinates - roi_choice = [ - x/self.llsz_parent.lattice.dy for x in roi_choice] - logger.info(f"Previewing ROI {roi_idx}") - if self.llsz_parent.deconvolution and self.llsz_parent.lattice.psf is not None: - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=self.llsz_parent.lattice.angle, - voxel_size_x=self.llsz_parent.lattice.dx, - voxel_size_y=self.llsz_parent.lattice.dy, - voxel_size_z=self.llsz_parent.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=self.llsz_parent.deconvolution, - decon_processing=self.llsz_parent.lattice.decon_processing, - otf_path=otf_path, - psf=self.llsz_parent.lattice.psf[channel], - skew_dir=self.llsz_parent.skew_dir) - else: - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=self.llsz_parent.lattice.angle, - voxel_size_x=self.llsz_parent.lattice.dx, - voxel_size_y=self.llsz_parent.lattice.dy, - voxel_size_z=self.llsz_parent.lattice.dz, - z_start=z_start, - z_end=z_end, - skew_dir=self.llsz_parent.skew_dir) - - # Set input of the workflow to be crop_deskewing, i.e., the original first operation will now have crop_deskew_image as an input (becoming second instead) - user_workflow.set( - input_arg_first, "crop_deskew_image") - else: - user_workflow.set(input_arg_first, vol_zyx) - # Not cropping; If deskew not in workflow, append to start - elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if self.llsz_parent.deconvolution.value: - psf = self.llsz_parent.lattice.psf[channel] - input_arg_first_decon, input_arg_last_decon, first_task_name_decon, last_task_name_decon = get_first_last_image_and_task( - user_workflow) - - if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=vol_zyx, - psf=self.llsz_parent.lattice.psf[channel], - dzdata=self.llsz_parent.lattice.dz, - dxdata=self.llsz_parent.lattice.dx, - dzpsf=self.llsz_parent.lattice.dz, - dxpsf=self.llsz_parent.lattice.dx, - num_iter=self.llsz_parent.lattice.psf_num_iter) - # user_workflow.set(input_arg_first_decon,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=vol_zyx, - psf=self.llsz_parent.lattice.psf[channel], - num_iter=self.llsz_parent.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # user_workflow.set(input_arg_first_decon,"deconvolution") - - user_workflow.set("deskew_image", - self.llsz_parent.deskew_func, - "deconvolution", - angle_in_degrees=self.llsz_parent.lattice.angle, - voxel_size_x=self.llsz_parent.lattice.dx, - voxel_size_y=self.llsz_parent.lattice.dy, - voxel_size_z=self.llsz_parent.lattice.dz, - linear_interpolation=True) - - # user_workflow.set("change_bitdepth",as_type,"deskew_image",vol_zyx) - # Set input of the workflow to be from deskewing output with same bit depth as original volume - # user_workflow.set(input_arg_first,"change_bitdepth") - - else: - user_workflow.set("deskew_image", - self.llsz_parent.deskew_func, - vol_zyx, - angle_in_degrees=self.llsz_parent.lattice.angle, - voxel_size_x=self.llsz_parent.lattice.dx, - voxel_size_y=self.llsz_parent.lattice.dy, - voxel_size_z=self.llsz_parent.lattice.dz, - linear_interpolation=True) - # Set input of the workflow to be from deskewing - # user_workflow.set(input_arg_first,"deskew_image") - - user_workflow.set( - "change_bitdepth", as_type, "deskew_image", vol_zyx) - # Set input of the workflow to be from deskewing with same bit depth as original volume - user_workflow.set( - input_arg_first, "change_bitdepth") - - else: - # if deskew already in workflow, just check if deconvolution needs to be added - # repitition of above (maybe create a function?) - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if self.llsz_parent.deconvolution: - psf = self.llsz_parent.lattice.psf[channel] - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=vol_zyx, - psf=self.llsz_parent.lattice.psf[channel], - dzdata=self.llsz_parent.lattice.dz, - dxdata=self.llsz_parent.lattice.dx, - dzpsf=self.llsz_parent.lattice.dz, - dxpsf=self.llsz_parent.lattice.dx, - num_iter=self.llsz_parent.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=vol_zyx, - psf=self.llsz_parent.lattice.psf[channel], - num_iter=self.llsz_parent.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - - # set input to subsequent task as deconvolution output - user_workflow.set( - input_arg_first, "deconvolution") + logger.info(f"Processing for Time: {time} and Channel: {channel}") logger.info("Workflow to be executed:") logger.info(user_workflow) @@ -821,7 +581,6 @@ def Workflow_Preview(self, processed_vol, parent_dir, 0, LLSZWidget, self, channel, time) print("Workflow complete") - pass @magicgui(header=dict(widget_type="Label", label="

Apply Workflow and Save Output

"), time_start=dict(label="Time Start:", max=2**20), @@ -879,44 +638,14 @@ def Apply_Workflow_and_Save(self, dy = self.llsz_parent.lattice.dy dz = self.llsz_parent.lattice.dz - if get_active_workflow: - # installs the workflow to napari - user_workflow = WorkflowManager.install( - self.parent_viewer).workflow - print("Workflow installed") - else: - # Automatically scan workflow file directory for *.py files. - # If it findss one, load it as a module - import importlib - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - sys.path.append(parent_dir) - custom_py_files = get_all_py_files(parent_dir) - if len(custom_py_files) == 0: - print( - f"No custom modules imported. If you'd like to use a cusotm module, place a *.py file in same folder as the workflow file {parent_dir}") - else: - modules = map( - importlib.import_module, custom_py_files) - print(f"Custom modules imported {modules}") - user_workflow = load_workflow(str(workflow_path)) - - if not isinstance(user_workflow, Workflow): - raise Exception("Workflow file is not a napari workflow object. Check file! You can use workflow inspector if needed") + user_workflow = get_workflow(self.parent_viewer if get_active_workflow else workflow_path) input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( user_workflow) - print(input_arg_first, input_arg_last, - first_task_name, last_task_name) - # get list of tasks - task_list = list(user_workflow._tasks.keys()) - print("Workflow loaded:") - print(user_workflow) + logger.info(f"{input_arg_first=}, {input_arg_last=}, {first_task_name=}, {last_task_name=}") + logger.info(f"Workflow loaded: {user_workflow}") vol = self.llsz_parent.lattice.data - - #vol_zyx= vol[time,channel,...] - task_name_start = first_task_name[0] try: @@ -1140,7 +869,6 @@ def Apply_Workflow_and_Save(self, print("Workflow complete") return - pass def _napari_lattice_widget_wrapper() -> LLSZWidget: # split widget type enables a resizable widget @@ -1152,3 +880,198 @@ def _napari_lattice_widget_wrapper() -> LLSZWidget: # widget._widget._layout.setWidgetResizable(True) return widget + +def get_workflow(source: Union[Path, Viewer]) -> Workflow: + """ + Gets a user defined workflow object, either from a viewer or from a file + + Args: + source: Either the path to a workflow file, or a Napari viewer from which to extract the workflow + """ + if isinstance(source, Viewer): + # installs the workflow to napari + user_workflow = WorkflowManager.install(source).workflow + logger.info("Workflow installed") + else: + import_workflow_modules(source) + user_workflow = load_workflow(str(source)) + + if not isinstance(user_workflow, Workflow): + raise Exception("Workflow file is not a napari workflow object. Check file! You can use workflow inspector if needed") + + logger.info(f"Workflow loaded: {user_workflow}") + return user_workflow + +def augment_workflow( + workflow: Workflow, + crop: bool, + roi_layer_list: ShapesData, + lattice: LatticeData, + deconvolution: bool, + times: range, + channels: range + ) -> Iterable[Workflow]: + """ + Yields copies of the input workflow, modified with the addition of deskewing and optionally, + cropping and deconvolution + """ + user_workflow = copy(workflow) + _, _, first_task_name, _ = get_first_last_image_and_task(workflow) + + for loop_time_idx, time_point in enumerate(times): + output_array = [] + data_table = [] + for loop_ch_idx, ch in enumerate(channels): + + if crop: + yield from make_crop_workflows( + user_workflow=user_workflow, + roi_layer_list=roi_layer_list, + lattice=lattice, + deconvolution=deconvolution + ) + + # save_img_workflow(vol=vol, + # workflow=user_workflow, + # input_arg=volume, + # first_task="crop_deskew_image", + # last_task=task_name_last, + # time_start=time_start, + # time_end=time_end, + # channel_start=ch_start, + # channel_end=ch_end, + # save_file_type=save_as_type, + # save_path=save_path, + # #roi_layer = roi_layer, + # save_name_prefix="ROI_" + \ + # str(idx), + # save_name=self.llsz_parent.lattice.save_name, + # dx=dx, + # dy=dy, + # dz=dz, + # angle=angle, + # deconvolution=self.llsz_parent.deconvolution.value, + # decon_processing=self.llsz_parent.lattice.decon_processing, + # otf_path=otf_path, + # psf_arg=psf_arg, + # psf=psf) + else: + INPUT_ARG = "input" + + # IF just deskewing and its not in the tasks, add that as first task + if user_workflow.get_task(first_task_name)[0] not in (cle.deskew_y, cle.deskew_x): + # add task to the workflow + user_workflow.set( + "deskew_image", + lattice.deskew_func, + input_image=INPUT_ARG, + angle_in_degrees=lattice.angle, + voxel_size_x=lattice.dx, + voxel_size_y=lattice.dy, + voxel_size_z=lattice.dz, + linear_interpolation=True + ) + # Set input of the workflow to be from deskewing + # change workflow task starts from is "deskew_image" and + replace_first_arg(user_workflow, new_arg="deskew_image") + + # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + if deconvolution: + PSF_ARG = "psf" + + if lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + user_workflow.set( + "deconvolution", + pycuda_decon, + image=INPUT_ARG, + psf=PSF_ARG, + dzdata=lattice.dz, + dxdata=lattice.dx, + dzpsf=lattice.dz, + dxpsf=lattice.dx, + num_iter=lattice.psf_num_iter + ) + # user_workflow.set(input_arg_first,"deconvolution") + else: + user_workflow.set( + "deconvolution", + skimage_decon, + vol_zyx=INPUT_ARG, + psf=PSF_ARG, + num_iter=lattice.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest' + ) + # modify the user workflow so "deconvolution" is accepted + replace_first_arg(user_workflow, new_arg="deconvolution") + + yield workflow + + # save_img_workflow(vol=vol, + # workflow=user_workflow, + # input_arg=INPUT_ARG, + # first_task=task_name_start, + # last_task=task_name_last, + # time_start=time_start, + # time_end=time_end, + # channel_start=ch_start, + # channel_end=ch_end, + # save_file_type=save_as_type, + # save_path=save_path, + # save_name=self.llsz_parent.lattice.save_name, + # dx=dx, + # dy=dy, + # dz=dz, + # angle=angle, + # deconvolution=self.llsz_parent.deconvolution, + # decon_processing=self.llsz_parent.lattice.decon_processing, + # otf_path=OTF_PATH_ARG, + # psf_arg=psf_arg, + # psf=PSF_ARG) + + +def make_crop_workflows( + user_workflow: Workflow, + roi_layer_list: ShapesData, + lattice: LatticeData, + deconvolution: bool +) -> Iterable[Workflow]: + """ + Yields a copy of `user_workflow` for each region of interest, with deskewing, cropping and deconvolution steps added on to the start + """ + + # Convert Roi pixel coordinates to canvas coordinates + # necessary only when scale is used for napari.viewer.add_image operations + + # Here we generate a workflow for each ROI + for idx, roi_layer in enumerate(tqdm([x/lattice for x in roi_layer_list], desc="ROI:", position=0)): + # Check if decon ticked, if so set as first and crop as second? + + # Create workflow for cropping and deskewing + # volume and roi used will be set dynamically + current_workflow = copy(user_workflow) + current_workflow.set( + "crop_deskew_image", + crop_volume_deskew, + original_volume="volume", + roi_shape="roi", + angle_in_degrees=lattice.angle, + voxel_size_x=lattice.dx, + voxel_size_y=lattice.dy, + voxel_size_z=lattice.dy, + z_start=0, + z_end=lattice.deskew_vol_shape[0], + deconvolution=deconvolution, + decon_processing=lattice.decon_processing, + psf="psf", + skew_dir=lattice.skew_dir + ) + + # change the first task so it accepts "crop_deskew as input" + replace_first_arg(current_workflow, new_arg="crop_deskew_image") + + logging.info(f"Processing ROI {idx}") + current_workflow.set("roi", roi_layer) + + yield current_workflow diff --git a/pyrightconfig.json b/pyrightconfig.json index bf4441a..86c8d98 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -8,5 +8,6 @@ "reportUntypedFunctionDecorator": false, "reportMissingTypeArgument": false, "reportPrivateUsage": false, - "reportPrivateImportUsage": false + "reportPrivateImportUsage": false, + "typeCheckingMode": "strict" } From 5db912095f28a516e7795e800cf34b39d33f8d02 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 4 Aug 2023 19:22:26 +1000 Subject: [PATCH 008/147] Object oriented system --- core/lls_core/deconvolution.py | 2 +- core/lls_core/lattice_data.py | 305 +++++++++++++++++++++++++-- core/lls_core/llsz_core.py | 6 +- core/lls_core/workflow.py | 172 +++++++++++++++ plugin/napari_lattice/dock_widget.py | 227 ++------------------ 5 files changed, 474 insertions(+), 238 deletions(-) diff --git a/core/lls_core/deconvolution.py b/core/lls_core/deconvolution.py index d27fe27..628577b 100644 --- a/core/lls_core/deconvolution.py +++ b/core/lls_core/deconvolution.py @@ -78,7 +78,7 @@ def read_psf(psf_paths: Collection[Path], # libraries are better designed.. Atleast until RL deconvolution is available in pyclesperant # Talley Lamberts pycudadecon is a great library and highly optimised. def pycuda_decon( - image: NDArray, + image: ArrayLike, otf_path: Optional[str]=None, dzdata: float=0.3, dxdata: float=0.1449922, diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index c3066f0..8eebbff 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -7,17 +7,30 @@ from numpy.typing import NDArray from dataclasses import dataclass import math +from dask.array.core import Array as DaskArray +import dask as da +from itertools import groupby +import tifffile -from typing import Any, List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar +from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Union -from aicsimageio.types import ArrayLike, PhysicalPixelSizes +from aicsimageio.types import PhysicalPixelSizes import pyclesperanto_prototype as cle +from tqdm import tqdm -from lls_core import DeskewDirection, DeconvolutionChoice +from lls_core import DeskewDirection, DeconvolutionChoice, SaveFileType +from lls_core.deconvolution import pycuda_decon, skimage_decon +from lls_core.llsz_core import crop_volume_deskew from lls_core.utils import get_deskewed_shape +from lls_core.types import ArrayLike if TYPE_CHECKING: import pyclesperanto_prototype as cle + from napari.types import ShapesData + +import logging + +logger = logging.getLogger(__name__) T = TypeVar("T") def raise_if_none(obj: Optional[T], message: str) -> T: @@ -25,6 +38,34 @@ def raise_if_none(obj: Optional[T], message: str) -> T: raise TypeError(message) return obj +@dataclass +class ProcessedVolume: + """ + A slice of the image processing result + """ + time_index: int + time: int + channel_index: int + channel: int + data: ArrayLike + + roi_index: Optional[int] = None + +def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] = None, channel: Optional[int] = None, time: Optional[int] = None) -> str: + """ + Generates a filename for this result + """ + components: List[str] = [] + if prefix is not None: + components.append(prefix) + if roi_index is not None: + components.append(f"ROI_{roi_index}") + if channel is not None: + components.append(f"C{channel}") + if time is not None: + components.append(f"T{time}") + return "_".join(components) + @dataclass class DefinedPixelSizes: """ @@ -35,11 +76,34 @@ class DefinedPixelSizes: Y: float = 0.14 Z: float = 0.3 +@dataclass +class DeconvolutionParams: + """ + Parameters for the optional deconvolution step + """ + decon_processing: DeconvolutionChoice = DeconvolutionChoice.cpu + psf: List[NDArray] = [] + psf_num_iter: int = 10 + otf_path: List = [] + # Background value to subtract + background: Union[float, Literal["auto","second_last"]] = 0 + +@dataclass +class CropParams: + """ + Parameters for the optional cropping step + """ + roi_layer_list: ShapesData + z_start: int = 0 + z_end: int = 1 + @dataclass class LatticeData: """ Holds data and metadata for a given image in a consistent format """ + # TODO: add defaults here, rather than in the CLI + #: 3-5D array data: ArrayLike dims: Dimensions @@ -55,24 +119,23 @@ class LatticeData: skew: DeskewDirection = DeskewDirection.Y angle: float = 30.0 - decon_processing: Optional[DeconvolutionChoice] = None - #: Pixel size in microns physical_pixel_sizes: DefinedPixelSizes = field(default_factory=DefinedPixelSizes) - new_dz: Optional[float] = None + new_dz: float = field(init=False) - # PSF data that should be refactored into another class eventually - psf: Optional[List[NDArray]] = None - psf_num_iter: Optional[int] = None - otf_path: Optional[List] = None + # If this is None, then deconvolution is disabled + deconvolution: Optional[DeconvolutionParams] = None - #: Number of time points - time: int = 0 - #: Number of channels - channels: int = 0 + # If this is None, then cropping is disabled + crop: Optional[CropParams] = None + + # Time and channel to process + time_range: range = field(init=False) + channel_range: range = field(init=False) + + save_type: SaveFileType = SaveFileType.h5 - # TODO: add defaults here, rather than in the CLI # Hack to ensure that .skew_dir behaves identically to .skew @property def skew_dir(self) -> DeskewDirection: @@ -125,16 +188,218 @@ def set_angle(self, angle: float) -> None: def set_skew(self, skew: DeskewDirection) -> None: self.skew = skew + @property + def cropping_enabled(self) -> bool: + "True if cropping should be performed" + return self.crop is not None + + @property + def deconv_enabled(self) -> bool: + "True if deconvolution should be performed" + return self.deconvolution is not None + + @property + def time(self) -> int: + """Number of time points""" + return self.dims.T + + @property + def channels(self) -> int: + """Number of channels""" + return self.dims.C + def __post_init__(self): # set new z voxel size - if self.skew == DeskewDirection.Y or self.skew == DeskewDirection.X: - self.new_dz = math.sin(self.angle * math.pi / 180.0) * self.dz + self.new_dz = math.sin(self.angle * math.pi / 180.0) * self.dz # process the file to get shape of final deskewed image self.deskew_vol_shape, self.deskew_affine_transform = get_deskewed_shape(self.data, self.angle, self.dx, self.dy, self.dz) - print(f"Channels: {self.channels}, Time: {self.time}") + self.time_range = range(self.time + 1) + self.channel_range = range(self.channels + 1) + logging.log(f"Channels: {self.channels}, Time: {self.time}") print("If channel and time need to be swapped, you can enforce this by choosing 'Last dimension is channel' when initialising the plugin") + def slice_data(self, time: int, channel: int) -> ArrayLike: + if time > self.time: + raise ValueError("time is out of range") + if channel > self.channels: + raise ValueError("channel is out of range") + + if len(self.dims.shape) == 3: + return self.data + elif len(self.dims.shape) == 4: + return self.data[time, :, :, :] + elif len(self.dims.shape) == 5: + return self.data[time, channel, :, :, :] + + raise Exception("Lattice data must be 3-5 dimensions") + + def iter_slices(self) -> Iterable[Tuple[int, int, int, int, ArrayLike]]: + """ + Yields array slices for each time and channel of interest. + + Returns: + An iterable of tuples. Each tuple contains (time_index, time, channel_index, channel, slice) + """ + for time_idx, time in enumerate(self.time_range): + for ch_idx, ch in enumerate(self.channel_range): + yield time_idx, time, ch_idx, ch, self.slice_data(time=time, channel=ch) + + def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int): + """ + Checks for a slice with incomplete data, caused by incomplete acquisition + """ + import numpy as np + if not isinstance(volume, DaskArray): + return volume + orig_shape = volume.shape + raw_vol = volume.compute() + if raw_vol.shape != orig_shape: + logger.warn(f"Time {time_point}, channel {channel} is incomplete. Actual shape {orig_shape}, got {raw_vol.shape}") + z_diff, y_diff, x_diff = np.subtract(orig_shape, raw_vol.shape) + logger.info(f"Padding with{z_diff,y_diff,x_diff}") + raw_vol = np.pad(raw_vol, ((0, z_diff), (0, y_diff), (0, x_diff))) + if raw_vol.shape != orig_shape: + raise Exception(f"Shape of last timepoint still doesn't match. Got {raw_vol.shape}") + return raw_vol + + @property + def deskewed_volume(self) -> DaskArray: + return da.zeros(self.deskew_vol_shape) + + def process_crop(self) -> Iterable[ProcessedVolume]: + """ + Yields processed image slices with cropping enabled + """ + if self.crop is None: + raise Exception("This function can only be called when crop is set") + + # We have an extra level of iteration for the crop path: iterating over each ROI + for roi_index, roi in enumerate(tqdm(self.crop.roi_layer_list, desc="ROI:", position=0)): + # pass arguments for save tiff, callable and function arguments + logger.info("Processing ROI ", roi_index) + + deconv_args: dict[Any, Any] = {} + if self.deconvolution is not None: + deconv_args = dict( + num_iter = self.deconvolution.psf_num_iter, + psf = self.deconvolution.psf, + decon_processing=self.deconvolution.decon_processing + ) + + for time_idx, time, ch_idx, ch, data in self.iter_slices(): + yield ProcessedVolume( + data = crop_volume_deskew( + original_volume=data, + deconvolution=self.deconv_enabled, + get_deskew_and_decon=False, + debug=False, + roi_shape=roi, + linear_interpolation=True, + voxel_size_x=self.dx, + voxel_size_y=self.dy, + voxel_size_z=self.dy, + angle_in_degrees=self.angle, + deskewed_volume=self.deskewed_volume, + z_start=self.crop.z_start, + z_end=self.crop.z_end, + **deconv_args + ), + channel=ch, + channel_index=ch_idx, + time=time, + time_index=time_idx, + roi_index=roi_index + ) + def process_non_crop(self) -> Iterable[ProcessedVolume]: + """ + Yields processed image slices without cropping + """ + for time_idx, time, ch_idx, ch, data in self.iter_slices(): + if self.deconvolution is not None: + if self.deconvolution.decon_processing == DeconvolutionChoice.cuda_gpu: + data = pycuda_decon( + image=data, + psf=self.deconvolution.psf[ch], + dzdata=self.dz, + dxdata=self.dx, + dzpsf=self.dz, + dxpsf=self.dx, + num_iter=self.deconvolution.psf_num_iter + ) + else: + data = skimage_decon( + vol_zyx=data, + psf=self.deconvolution.psf[ch], + num_iter=self.deconvolution.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest' + ) + + yield ProcessedVolume( + data = cle.pull_zyx(self.deskew_func( + input_image=data, + angle_in_degrees=self.angle, + linear_interpolation=True, + voxel_size_x=self.dx, + voxel_size_y=self.dy, + voxel_size_z=self.dz + )), + channel=ch, + channel_index=ch_idx, + time=time, + time_index=time_idx + ) + + def process(self) -> Iterable[ProcessedVolume]: + if self.cropping_enabled: + yield from self.process_crop() + else: + yield from self.process_non_crop() + + def save_image(self, slices: Iterable[ProcessedVolume]): + """ + Saves result slices (ie generated by `process()` to disk) + """ + # TODO: refactor this into a class system, one for each format + import numpy as np + from pathlib import Path + import npy2bdv + + for roi, roi_results in groupby(slices, key=lambda it: it.roi_index): + if self.save_type == SaveFileType.h5: + bdv_writer = npy2bdv.BdvWriter( + make_filename_prefix(prefix=self.save_name, roi_index=roi), + compression='gzip', + nchannels=len(self.channel_range), + subsamp=( + (1, 1, 1), (1, 2, 2), (2, 4, 4)), + overwrite=False + ) + for result in roi_results: + bdv_writer.append_view( + result.data, + time=result.time, + channel=result.channel, + voxel_size_xyz=(self.dx, self.dy, self.new_dz), + voxel_units='um' + ) + elif self.save_type == SaveFileType.tiff: + # For each time point, we write a separate TIFF + for time, results in groupby(roi_results, key=lambda it: it.time): + result_list = list(results) + first_result = result_list[0] + images_array = np.swapaxes(np.expand_dims([result.data for result in result_list], axis=0), 1, 2) + tifffile.imwrite( + file = Path(make_filename_prefix(channel=first_result.channel, time=time, roi_index=roi)).with_suffix("tiff"), + data = images_array, + bigtiff=True, + resolution=(1./self.dx, 1./self.dy, "MICROMETER"), + metadata={'spacing': self.new_dz, 'unit': 'um', 'axes': 'TZCYX'}, + imagej=True + ) + def lattice_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None), **kwargs: Any) -> LatticeData: # Note: The reason we copy all of these fields rather than just storing the AICSImage is because that class is mostly immutable and so not suitable @@ -147,8 +412,6 @@ def lattice_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = return LatticeData( data = img.dask_data, dims = img.dims, - time = img.dims.T, - channels = img.dims.C, physical_pixel_sizes = pixel_sizes, **kwargs ) @@ -203,4 +466,4 @@ def lattice_fom_array(arr: ArrayLike, last_dimension: Optional[Literal["channel" last_dimension: See img_from_array """ aics = img_from_array(arr, last_dimension) - return lattice_from_aics(aics, **kwargs) \ No newline at end of file + return lattice_from_aics(aics, **kwargs) diff --git a/core/lls_core/llsz_core.py b/core/lls_core/llsz_core.py index ba100e8..479d728 100644 --- a/core/lls_core/llsz_core.py +++ b/core/lls_core/llsz_core.py @@ -1,6 +1,5 @@ from __future__ import annotations -from aicsimageio.types import ArrayLike import numpy as np import pyclesperanto_prototype as cle from dask.array.core import Array as DaskArray @@ -17,6 +16,7 @@ from lls_core.utils import calculate_crop_bbox, check_subclass, is_napari_shape, pad_image_nearest_multiple from lls_core import config, DeskewDirection, DeconvolutionChoice from lls_core.deconvolution import pycuda_decon +from lls_core.types import ArrayLike # Enable Logging import logging @@ -46,7 +46,7 @@ class CommonArgs(TypedDict, total=False): z_start: int z_end: int deconvolution: bool - decon_processing: Optional[str] + decon_processing: Optional[DeconvolutionChoice] psf: Union[Psf, None] num_iter: int linear_interpolation: bool @@ -73,7 +73,7 @@ def crop_volume_deskew( z_end: int = 1, debug: bool = False, deconvolution: bool = False, - decon_processing: Optional[str]=None, + decon_processing: Optional[DeconvolutionChoice]=None, psf: Union[Psf, None]=None, num_iter: int = 10, linear_interpolation: bool=True, diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index cb44d39..7ccd6a8 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -11,6 +11,7 @@ import numpy as np import pandas as pd import pyclesperanto_prototype as cle +from lls_core.llsz_core import crop_volume_deskew from lls_core.types import ArrayLike from typing_extensions import TYPE_CHECKING @@ -196,3 +197,174 @@ def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike] workflow_output, name="Workflow_preview_" + suffix_name, scale=scale) else: return workflow_output + +def augment_workflow( + workflow: Workflow, + lattice: LatticeData, + times: range, + channels: range + ) -> Iterable[Workflow]: + """ + Yields copies of the input workflow, modified with the addition of deskewing and optionally, + cropping and deconvolution + """ + user_workflow = copy(workflow) + _, _, first_task_name, _ = get_first_last_image_and_task(workflow) + + for loop_time_idx, time_point in enumerate(times): + output_array = [] + data_table = [] + for loop_ch_idx, ch in enumerate(channels): + + if crop: + yield from make_crop_workflows( + user_workflow=user_workflow, + roi_layer_list=roi_layer_list, + lattice=lattice, + deconvolution=deconvolution + ) + + # save_img_workflow(vol=vol, + # workflow=user_workflow, + # input_arg=volume, + # first_task="crop_deskew_image", + # last_task=task_name_last, + # time_start=time_start, + # time_end=time_end, + # channel_start=ch_start, + # channel_end=ch_end, + # save_file_type=save_as_type, + # save_path=save_path, + # #roi_layer = roi_layer, + # save_name_prefix="ROI_" + \ + # str(idx), + # save_name=self.llsz_parent.lattice.save_name, + # dx=dx, + # dy=dy, + # dz=dz, + # angle=angle, + # deconvolution=self.llsz_parent.deconvolution.value, + # decon_processing=self.llsz_parent.lattice.decon_processing, + # otf_path=otf_path, + # psf_arg=psf_arg, + # psf=psf) + else: + INPUT_ARG = "input" + + # IF just deskewing and its not in the tasks, add that as first task + if user_workflow.get_task(first_task_name)[0] not in (cle.deskew_y, cle.deskew_x): + # add task to the workflow + user_workflow.set( + "deskew_image", + lattice.deskew_func, + input_image=INPUT_ARG, + angle_in_degrees=lattice.angle, + voxel_size_x=lattice.dx, + voxel_size_y=lattice.dy, + voxel_size_z=lattice.dz, + linear_interpolation=True + ) + # Set input of the workflow to be from deskewing + # change workflow task starts from is "deskew_image" and + replace_first_arg(user_workflow, new_arg="deskew_image") + + # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + if deconvolution: + PSF_ARG = "psf" + + if lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + user_workflow.set( + "deconvolution", + pycuda_decon, + image=INPUT_ARG, + psf=PSF_ARG, + dzdata=lattice.dz, + dxdata=lattice.dx, + dzpsf=lattice.dz, + dxpsf=lattice.dx, + num_iter=lattice.psf_num_iter + ) + # user_workflow.set(input_arg_first,"deconvolution") + else: + user_workflow.set( + "deconvolution", + skimage_decon, + vol_zyx=INPUT_ARG, + psf=PSF_ARG, + num_iter=lattice.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest' + ) + # modify the user workflow so "deconvolution" is accepted + replace_first_arg(user_workflow, new_arg="deconvolution") + + yield workflow + + # save_img_workflow(vol=vol, + # workflow=user_workflow, + # input_arg=INPUT_ARG, + # first_task=task_name_start, + # last_task=task_name_last, + # time_start=time_start, + # time_end=time_end, + # channel_start=ch_start, + # channel_end=ch_end, + # save_file_type=save_as_type, + # save_path=save_path, + # save_name=self.llsz_parent.lattice.save_name, + # dx=dx, + # dy=dy, + # dz=dz, + # angle=angle, + # deconvolution=self.llsz_parent.deconvolution, + # decon_processing=self.llsz_parent.lattice.decon_processing, + # otf_path=OTF_PATH_ARG, + # psf_arg=psf_arg, + # psf=PSF_ARG) + + +def make_crop_workflows( + user_workflow: Workflow, + roi_layer_list: ShapesData, + lattice: LatticeData, + deconvolution: bool +) -> Iterable[Workflow]: + """ + Yields a copy of `user_workflow` for each region of interest, with deskewing, cropping and deconvolution steps added on to the start + """ + + # Convert Roi pixel coordinates to canvas coordinates + # necessary only when scale is used for napari.viewer.add_image operations + + # Here we generate a workflow for each ROI + for idx, roi_layer in enumerate(tqdm([x/lattice for x in roi_layer_list], desc="ROI:", position=0)): + # Check if decon ticked, if so set as first and crop as second? + + # Create workflow for cropping and deskewing + # volume and roi used will be set dynamically + current_workflow = copy(user_workflow) + current_workflow.set( + "crop_deskew_image", + crop_volume_deskew, + original_volume="volume", + roi_shape="roi", + angle_in_degrees=lattice.angle, + voxel_size_x=lattice.dx, + voxel_size_y=lattice.dy, + voxel_size_z=lattice.dy, + z_start=0, + z_end=lattice.deskew_vol_shape[0], + deconvolution=deconvolution, + decon_processing=lattice.decon_processing, + psf="psf", + skew_dir=lattice.skew_dir + ) + + # change the first task so it accepts "crop_deskew as input" + replace_first_arg(current_workflow, new_arg="crop_deskew_image") + + logging.info(f"Processing ROI {idx}") + current_workflow.set("roi", roi_layer) + + yield current_workflow diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index a11f53b..d578119 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -30,6 +30,7 @@ from lls_core import config, DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection from lls_core.io import LatticeData, save_img, save_img_workflow +from lls_core.lattice_data import DeconvolutionParams from lls_core.types import ArrayLike from lls_core.workflow import get_first_last_image_and_task, modify_workflow_task, get_all_py_files, process_custom_workflow_output, load_custom_py_modules, import_workflow_modules, replace_first_arg from lls_core.utils import check_dimensions, read_imagej_roi, as_type @@ -67,10 +68,7 @@ def parent_viewer(self) -> Viewer: class LLSZWidget(LlszTemplate): open_file: bool = False lattice: LatticeData - skew_dir: DeskewDirection - angle_value: float - deskew_func: Callable - deconvolution: bool = False + deconv: DeconvolutionParams = DeconvolutionParams() shapes_layer: Shapes @magicclass(widget_type="split") @@ -119,17 +117,6 @@ def Choose_Image_Layer(self, # Select device for processing cle.select_device(select_device) - #assert skew_dir in DeskewDirection, "Skew direction not recognised. Enter either Y or X" - self.llsz_parent.skew_dir = skew_dir - self.llsz_parent.angle_value = angle - - if self.llsz_parent.skew_dir == DeskewDirection.Y: - self.llsz_parent.deskew_func = cle.deskew_y - #skew_dir = DeskewDirection.Y - elif self.llsz_parent.skew_dir == DeskewDirection.X: - self.llsz_parent.deskew_func = cle.deskew_x - #self.llsz_parent.skew_dir = DeskewDirection.X - # merge all napari image layers as one multidimensional image if merge_all_channel_layers: from napari.layers.utils.stack_utils import images_to_stack @@ -151,23 +138,10 @@ def Choose_Image_Layer(self, img=img_layer, last_dimension=None if last_dimension_channel == LastDimensionOptions.get_from_metadata else last_dimension_channel, angle=angle, - skew=self.llsz_parent.skew_dir, - physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz) + skew=skew_dir, + physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz), + # deconvolution = DeconvolutionParams() ) - #self.llsz_parent.aics = self.llsz_parent.lattice.data - - # self.llsz_parent.dask = False # Use GPU by default - - # We initialise these variables here, but they can be changed in the deconvolution section - # list to store psf images for each channel - self.llsz_parent.lattice.psf = [] - self.llsz_parent.lattice.psf_num_iter = 10 - self.llsz_parent.lattice.decon_processing = DeconvolutionChoice.cpu - # list to store otf paths for each channel (Deprecated) - self.llsz_parent.lattice.otf_path = [] - # if not using GPU - #self.llsz_parent.dask = not use_GPU - # flag for ensuring a file has been opened and plugin initialised self.llsz_parent.open_file = True @@ -212,10 +186,11 @@ def Choose_Image_Layer(self, def _set_decon(self): if self.deconvolution: logger.info("Deconvolution Activated") - self.llsz_parent.deconvolution = True + # Enable deconvolutio by using the saved parameters + self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv else: logger.info("Deconvolution Disabled") - self.llsz_parent.deconvolution = False + self.llsz_parent.lattice.deconvolution = None @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) @set_options(header=dict(widget_type="Label", label="

Enter path to the PSF images

"), @@ -241,10 +216,11 @@ def deconvolution_gui(self, device_option: DeconvolutionChoice, no_iter: int): """GUI for Deconvolution button""" - self.llsz_parent.lattice.decon_processing = device_option - if not self.llsz_parent.deconvolution: + # Force deconvolution to be true if we do this + if not self.llsz_parent.lattice.deconvolution: raise Exception("Deconvolution is set to False. Tick the box to activate deconvolution.") - self.llsz_parent.lattice.psf = list(read_psf([ + self.llsz_parent.deconv.decon_processing = device_option + self.llsz_parent.deconv.psf = list(read_psf([ psf_ch1_path, psf_ch2_path, psf_ch3_path, @@ -253,7 +229,7 @@ def deconvolution_gui(self, device_option, lattice_class=self.llsz_parent.lattice )) - self.llsz_parent.lattice.psf_num_iter = no_iter + self.llsz_parent.deconv.psf_num_iter = no_iter self["deconvolution_gui"].background_color = "green" self["deconvolution_gui"].text = "PSFs added" @@ -475,8 +451,7 @@ def Crop_Save(self, time_end=time_end, channel_start=ch_start, channel_end=ch_end, - save_name_prefix="ROI_" + - str(idx), + save_name_prefix="ROI_" + str(idx), save_path=save_path, save_file_type=save_as_type, save_name=self.llsz_parent.lattice.save_name, @@ -901,177 +876,3 @@ def get_workflow(source: Union[Path, Viewer]) -> Workflow: logger.info(f"Workflow loaded: {user_workflow}") return user_workflow - -def augment_workflow( - workflow: Workflow, - crop: bool, - roi_layer_list: ShapesData, - lattice: LatticeData, - deconvolution: bool, - times: range, - channels: range - ) -> Iterable[Workflow]: - """ - Yields copies of the input workflow, modified with the addition of deskewing and optionally, - cropping and deconvolution - """ - user_workflow = copy(workflow) - _, _, first_task_name, _ = get_first_last_image_and_task(workflow) - - for loop_time_idx, time_point in enumerate(times): - output_array = [] - data_table = [] - for loop_ch_idx, ch in enumerate(channels): - - if crop: - yield from make_crop_workflows( - user_workflow=user_workflow, - roi_layer_list=roi_layer_list, - lattice=lattice, - deconvolution=deconvolution - ) - - # save_img_workflow(vol=vol, - # workflow=user_workflow, - # input_arg=volume, - # first_task="crop_deskew_image", - # last_task=task_name_last, - # time_start=time_start, - # time_end=time_end, - # channel_start=ch_start, - # channel_end=ch_end, - # save_file_type=save_as_type, - # save_path=save_path, - # #roi_layer = roi_layer, - # save_name_prefix="ROI_" + \ - # str(idx), - # save_name=self.llsz_parent.lattice.save_name, - # dx=dx, - # dy=dy, - # dz=dz, - # angle=angle, - # deconvolution=self.llsz_parent.deconvolution.value, - # decon_processing=self.llsz_parent.lattice.decon_processing, - # otf_path=otf_path, - # psf_arg=psf_arg, - # psf=psf) - else: - INPUT_ARG = "input" - - # IF just deskewing and its not in the tasks, add that as first task - if user_workflow.get_task(first_task_name)[0] not in (cle.deskew_y, cle.deskew_x): - # add task to the workflow - user_workflow.set( - "deskew_image", - lattice.deskew_func, - input_image=INPUT_ARG, - angle_in_degrees=lattice.angle, - voxel_size_x=lattice.dx, - voxel_size_y=lattice.dy, - voxel_size_z=lattice.dz, - linear_interpolation=True - ) - # Set input of the workflow to be from deskewing - # change workflow task starts from is "deskew_image" and - replace_first_arg(user_workflow, new_arg="deskew_image") - - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if deconvolution: - PSF_ARG = "psf" - - if lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set( - "deconvolution", - pycuda_decon, - image=INPUT_ARG, - psf=PSF_ARG, - dzdata=lattice.dz, - dxdata=lattice.dx, - dzpsf=lattice.dz, - dxpsf=lattice.dx, - num_iter=lattice.psf_num_iter - ) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set( - "deconvolution", - skimage_decon, - vol_zyx=INPUT_ARG, - psf=PSF_ARG, - num_iter=lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest' - ) - # modify the user workflow so "deconvolution" is accepted - replace_first_arg(user_workflow, new_arg="deconvolution") - - yield workflow - - # save_img_workflow(vol=vol, - # workflow=user_workflow, - # input_arg=INPUT_ARG, - # first_task=task_name_start, - # last_task=task_name_last, - # time_start=time_start, - # time_end=time_end, - # channel_start=ch_start, - # channel_end=ch_end, - # save_file_type=save_as_type, - # save_path=save_path, - # save_name=self.llsz_parent.lattice.save_name, - # dx=dx, - # dy=dy, - # dz=dz, - # angle=angle, - # deconvolution=self.llsz_parent.deconvolution, - # decon_processing=self.llsz_parent.lattice.decon_processing, - # otf_path=OTF_PATH_ARG, - # psf_arg=psf_arg, - # psf=PSF_ARG) - - -def make_crop_workflows( - user_workflow: Workflow, - roi_layer_list: ShapesData, - lattice: LatticeData, - deconvolution: bool -) -> Iterable[Workflow]: - """ - Yields a copy of `user_workflow` for each region of interest, with deskewing, cropping and deconvolution steps added on to the start - """ - - # Convert Roi pixel coordinates to canvas coordinates - # necessary only when scale is used for napari.viewer.add_image operations - - # Here we generate a workflow for each ROI - for idx, roi_layer in enumerate(tqdm([x/lattice for x in roi_layer_list], desc="ROI:", position=0)): - # Check if decon ticked, if so set as first and crop as second? - - # Create workflow for cropping and deskewing - # volume and roi used will be set dynamically - current_workflow = copy(user_workflow) - current_workflow.set( - "crop_deskew_image", - crop_volume_deskew, - original_volume="volume", - roi_shape="roi", - angle_in_degrees=lattice.angle, - voxel_size_x=lattice.dx, - voxel_size_y=lattice.dy, - voxel_size_z=lattice.dy, - z_start=0, - z_end=lattice.deskew_vol_shape[0], - deconvolution=deconvolution, - decon_processing=lattice.decon_processing, - psf="psf", - skew_dir=lattice.skew_dir - ) - - # change the first task so it accepts "crop_deskew as input" - replace_first_arg(current_workflow, new_arg="crop_deskew_image") - - logging.info(f"Processing ROI {idx}") - current_workflow.set("roi", roi_layer) - - yield current_workflow From e9f3866dfa119ec47399c496bb420afd88aa7603 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 10 Aug 2023 18:35:29 +1000 Subject: [PATCH 009/147] Move to Pydantic, and add field validations for LatticeData and similar --- core/lls_core/lattice_data.py | 142 ++++++++++++++++++--------- core/pyproject.toml | 1 + plugin/napari_lattice/dock_widget.py | 2 + plugin/napari_lattice/reader.py | 3 +- 4 files changed, 100 insertions(+), 48 deletions(-) diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 8eebbff..857701e 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -1,11 +1,10 @@ from __future__ import annotations # class for initializing lattice data and setting metadata # TODO: handle scenes -from dataclasses import dataclass, field +from pydantic import BaseModel, Field, NonNegativeInt, NonNegativeFloat, root_validator, validator from aicsimageio.aics_image import AICSImage from aicsimageio.dimensions import Dimensions from numpy.typing import NDArray -from dataclasses import dataclass import math from dask.array.core import Array as DaskArray import dask as da @@ -38,18 +37,16 @@ def raise_if_none(obj: Optional[T], message: str) -> T: raise TypeError(message) return obj -@dataclass -class ProcessedVolume: +class ProcessedVolume(BaseModel): """ A slice of the image processing result """ - time_index: int - time: int - channel_index: int - channel: int + time_index: NonNegativeInt + time: NonNegativeInt + channel_index: NonNegativeInt + channel: NonNegativeInt data: ArrayLike - - roi_index: Optional[int] = None + roi_index: Optional[NonNegativeInt] = None def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] = None, channel: Optional[int] = None, time: Optional[int] = None) -> str: """ @@ -66,63 +63,66 @@ def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] components.append(f"T{time}") return "_".join(components) -@dataclass -class DefinedPixelSizes: +class DefinedPixelSizes(BaseModel): """ Like PhysicalPixelSizes, but it's a dataclass, and none of its fields are None """ - X: float = 0.14 - Y: float = 0.14 - Z: float = 0.3 + X: NonNegativeFloat = 0.14 + Y: NonNegativeFloat = 0.14 + Z: NonNegativeFloat = 0.3 -@dataclass -class DeconvolutionParams: +class DeconvolutionParams(BaseModel): """ Parameters for the optional deconvolution step """ decon_processing: DeconvolutionChoice = DeconvolutionChoice.cpu psf: List[NDArray] = [] - psf_num_iter: int = 10 + psf_num_iter: NonNegativeInt = 10 + # TODO: handle this otf_path: List = [] # Background value to subtract - background: Union[float, Literal["auto","second_last"]] = 0 + background: Union[float, Literal["auto", "second_last"]] = 0 -@dataclass -class CropParams: +class CropParams(BaseModel): """ Parameters for the optional cropping step """ roi_layer_list: ShapesData - z_start: int = 0 + z_start: NonNegativeInt = 0 z_end: int = 1 -@dataclass -class LatticeData: +class LatticeData(BaseModel): """ Holds data and metadata for a given image in a consistent format """ - # TODO: add defaults here, rather than in the CLI - #: 3-5D array + #: A 3-5D array containing the image data data: ArrayLike + + #: Dimensions of `data` dims: Dimensions #: The filename of this data when it is saved save_name: str - # Dimensions of the deskewed output - deskew_vol_shape: Tuple[int, ...] = field(init=False) - deskew_affine_transform: cle.AffineTransform3D = field(init=False) + #: Dimensions of the deskewed output + deskew_vol_shape: Tuple[int, ...] + + deskew_affine_transform: cle.AffineTransform3D + + #: The range of times to process + time_range: range + + #: The range of channels to process + channel_range: range #: Geometry of the light path skew: DeskewDirection = DeskewDirection.Y angle: float = 30.0 #: Pixel size in microns - physical_pixel_sizes: DefinedPixelSizes = field(default_factory=DefinedPixelSizes) - - new_dz: float = field(init=False) + physical_pixel_sizes: DefinedPixelSizes = Field(default_factory=DefinedPixelSizes) # If this is None, then deconvolution is disabled deconvolution: Optional[DeconvolutionParams] = None @@ -130,10 +130,7 @@ class LatticeData: # If this is None, then cropping is disabled crop: Optional[CropParams] = None - # Time and channel to process - time_range: range = field(init=False) - channel_range: range = field(init=False) - + #: The data type to save the result as save_type: SaveFileType = SaveFileType.h5 # Hack to ensure that .skew_dir behaves identically to .skew @@ -207,17 +204,70 @@ def time(self) -> int: def channels(self) -> int: """Number of channels""" return self.dims.C + + @root_validator + def set_deskew(cls, values: dict) -> dict: + """ + Sets the default deskew shape values if the user has not provided them + """ + # process the file to get shape of final deskewed image + if values['deskew_vol_shape'] is None: + if values['deskew_affine_transform'] is None: + # If neither has been set, calculate them ourselves + values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(values["data"], values["angle"], values["dx"], values["dy"], values["dz"]) + else: + raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") + return values + + @validator("time_range") + def default_time_range(cls, v: Any, values: dict) -> range: + """ + Sets the default time range if undefined + """ + if v is None: + return range(values["dims"].T + 1) + return v + + @validator("time_range") + def disjoint_time_range(cls, v: range, values: dict): + """ + Validates that the time range is within the range of channels in our array + """ + max_time = values["dims"].T + if v.start < 0: + raise ValueError("The lowest valid start value is 0") + if v.stop > max_time: + raise ValueError(f"The highest valid time value is the length of the time axis, which is {max_time}") + return v + + @validator("channel_range") + def default_channel_range(cls, v: Any, values: dict) -> range: + """ + Sets the default channel range if undefined + """ + if v is None: + return range(values["dims"].C + 1) + return v - def __post_init__(self): - # set new z voxel size - self.new_dz = math.sin(self.angle * math.pi / 180.0) * self.dz + @validator("channel_range") + def disjoint_channel_range(cls, v: range, values: dict): + """ + Validates that the channel range is within the range of channels in our array + """ + max_channel = values["dims"].T + if v.start < 0: + raise ValueError("The lowest valid start value is 0") + if v.stop > max_channel: + raise ValueError(f"The highest valid channel value is the length of the channel axis, which is {max_channel}") + return v - # process the file to get shape of final deskewed image - self.deskew_vol_shape, self.deskew_affine_transform = get_deskewed_shape(self.data, self.angle, self.dx, self.dy, self.dz) - self.time_range = range(self.time + 1) - self.channel_range = range(self.channels + 1) - logging.log(f"Channels: {self.channels}, Time: {self.time}") - print("If channel and time need to be swapped, you can enforce this by choosing 'Last dimension is channel' when initialising the plugin") + @property + def new_dz(self): + return math.sin(self.angle * math.pi / 180.0) * self.dz + + def __post_init__(self): + logger.info(f"Channels: {self.channels}, Time: {self.time}") + logger.info("If channel and time need to be swapped, you can enforce this by choosing 'Last dimension is channel' when initialising the plugin") def slice_data(self, time: int, channel: int) -> ArrayLike: if time > self.time: @@ -363,6 +413,7 @@ def save_image(self, slices: Iterable[ProcessedVolume]): Saves result slices (ie generated by `process()` to disk) """ # TODO: refactor this into a class system, one for each format + # TODO: make this a method on a ProcessedData class import numpy as np from pathlib import Path import npy2bdv @@ -456,7 +507,6 @@ def img_from_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", " arr = np.swapaxes(arr, 0, 1) return AICSImage(image=arr, dim_order=dim_order, **kwargs) - def lattice_fom_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> LatticeData: """ Creates a `LatticeData` from an array diff --git a/core/pyproject.toml b/core/pyproject.toml index 3d6f3a4..ab5b93f 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -46,6 +46,7 @@ dependencies = [ "read-roi", "gputools", "pyclesperanto_prototype>=0.20.0", + "pydantic~=1.0", "npy2bdv", "redlionfish", # Older tifffile are not compatible with aicsimageio, see: https://github.com/AllenCellModeling/aicsimageio/issues/518 diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index d578119..56ae441 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -325,6 +325,7 @@ def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])) -> @click(enabled=False) def Crop_Preview(self, roi_layer: ShapesData): + if not roi_layer: raise Exception("No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs.") # TODO: Add assertion to check if bbox layer or coordinates @@ -384,6 +385,7 @@ def Crop_Preview(self, roi_layer: ShapesData): ) # get array back from gpu or addding cle array to napari can throw errors + image = next(self.llsz_parent.lattice.process()) scale = ( self.llsz_parent.lattice.new_dz, diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index 0ae8eef..9f8775c 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -82,7 +82,6 @@ def napari_get_reader(path: list[str] | str): # otherwise we return the *function* that can read ``path``. return bdv_h5_reader - def bdv_h5_reader(path): """Take a path and returns a list of LayerData tuples.""" @@ -136,4 +135,4 @@ def bdv_h5_reader(path): add_kwargs = {} layer_type = "image" # optional, default is "image" - return [(images, add_kwargs, layer_type)] \ No newline at end of file + return [(images, add_kwargs, layer_type)] From ba63f3902d50b4b26dba36f240c3e0ec6f07f9ee Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 11 Aug 2023 08:09:07 +1000 Subject: [PATCH 010/147] Refactor process() to return a ProcessedSlices object --- core/lls_core/lattice_data.py | 112 +++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 48 deletions(-) diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 857701e..075716a 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -37,6 +37,55 @@ def raise_if_none(obj: Optional[T], message: str) -> T: raise TypeError(message) return obj +class ProcessedSlices(BaseModel): + #: Iterable of result slices. + #: Note that this is a finite iterator that can only be iterated once + slices: Iterable[ProcessedVolume] + + #: The "parent" LatticeData that was used to create this result + lattice_data: LatticeData + + def save_image(self): + """ + Saves result slices to disk + """ + # TODO: refactor this into a class system, one for each format + import numpy as np + from pathlib import Path + import npy2bdv + + for roi, roi_results in groupby(self.slices, key=lambda it: it.roi_index): + if self.lattice_data.save_type == SaveFileType.h5: + bdv_writer = npy2bdv.BdvWriter( + make_filename_prefix(prefix=self.lattice_data.save_name, roi_index=roi), + compression='gzip', + nchannels=len(self.lattice_data.channel_range), + subsamp=((1, 1, 1), (1, 2, 2), (2, 4, 4)), + overwrite=False + ) + for result in roi_results: + bdv_writer.append_view( + result.data, + time=result.time, + channel=result.channel, + voxel_size_xyz=(self.lattice_data.dx, self.lattice_data.dy, self.lattice_data.new_dz), + voxel_units='um' + ) + elif self.lattice_data.save_type == SaveFileType.tiff: + # For each time point, we write a separate TIFF + for time, results in groupby(roi_results, key=lambda it: it.time): + result_list = list(results) + first_result = result_list[0] + images_array = np.swapaxes(np.expand_dims([result.data for result in result_list], axis=0), 1, 2) + tifffile.imwrite( + file = Path(make_filename_prefix(channel=first_result.channel, time=time, roi_index=roi)).with_suffix("tiff"), + data = images_array, + bigtiff=True, + resolution=(1./self.lattice_data.dx, 1./self.lattice_data.dy, "MICROMETER"), + metadata={'spacing': self.lattice_data.new_dz, 'unit': 'um', 'axes': 'TZCYX'}, + imagej=True + ) + class ProcessedVolume(BaseModel): """ A slice of the image processing result @@ -317,7 +366,7 @@ def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, chann def deskewed_volume(self) -> DaskArray: return da.zeros(self.deskew_vol_shape) - def process_crop(self) -> Iterable[ProcessedVolume]: + def _process_crop(self) -> Iterable[ProcessedVolume]: """ Yields processed image slices with cropping enabled """ @@ -361,7 +410,7 @@ def process_crop(self) -> Iterable[ProcessedVolume]: time_index=time_idx, roi_index=roi_index ) - def process_non_crop(self) -> Iterable[ProcessedVolume]: + def _process_non_crop(self) -> Iterable[ProcessedVolume]: """ Yields processed image slices without cropping """ @@ -402,54 +451,21 @@ def process_non_crop(self) -> Iterable[ProcessedVolume]: time_index=time_idx ) - def process(self) -> Iterable[ProcessedVolume]: - if self.cropping_enabled: - yield from self.process_crop() - else: - yield from self.process_non_crop() - - def save_image(self, slices: Iterable[ProcessedVolume]): + def process(self) -> ProcessedSlices: """ - Saves result slices (ie generated by `process()` to disk) + Execute the processing and return the result. + This is the main public API for processing """ - # TODO: refactor this into a class system, one for each format - # TODO: make this a method on a ProcessedData class - import numpy as np - from pathlib import Path - import npy2bdv - - for roi, roi_results in groupby(slices, key=lambda it: it.roi_index): - if self.save_type == SaveFileType.h5: - bdv_writer = npy2bdv.BdvWriter( - make_filename_prefix(prefix=self.save_name, roi_index=roi), - compression='gzip', - nchannels=len(self.channel_range), - subsamp=( - (1, 1, 1), (1, 2, 2), (2, 4, 4)), - overwrite=False - ) - for result in roi_results: - bdv_writer.append_view( - result.data, - time=result.time, - channel=result.channel, - voxel_size_xyz=(self.dx, self.dy, self.new_dz), - voxel_units='um' - ) - elif self.save_type == SaveFileType.tiff: - # For each time point, we write a separate TIFF - for time, results in groupby(roi_results, key=lambda it: it.time): - result_list = list(results) - first_result = result_list[0] - images_array = np.swapaxes(np.expand_dims([result.data for result in result_list], axis=0), 1, 2) - tifffile.imwrite( - file = Path(make_filename_prefix(channel=first_result.channel, time=time, roi_index=roi)).with_suffix("tiff"), - data = images_array, - bigtiff=True, - resolution=(1./self.dx, 1./self.dy, "MICROMETER"), - metadata={'spacing': self.new_dz, 'unit': 'um', 'axes': 'TZCYX'}, - imagej=True - ) + if self.cropping_enabled: + return ProcessedSlices( + lattice_data=self, + slices=self._process_crop() + ) + else: + return ProcessedSlices( + lattice_data=self, + slices=self._process_non_crop() + ) def lattice_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None), **kwargs: Any) -> LatticeData: # Note: The reason we copy all of these fields rather than just storing the AICSImage is because that class is mostly immutable and so not suitable From c02ea408c88fbc73d524f1da13e77b4f6ae919e0 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 11 Aug 2023 10:47:12 +1000 Subject: [PATCH 011/147] Fix breaking plugin errors --- core/lls_core/deconvolution.py | 8 ++++++-- core/lls_core/lattice_data.py | 7 ++++--- core/lls_core/types.py | 4 +++- core/lls_core/workflow.py | 8 ++++---- plugin/napari_lattice/dock_widget.py | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/core/lls_core/deconvolution.py b/core/lls_core/deconvolution.py index 628577b..4206f84 100644 --- a/core/lls_core/deconvolution.py +++ b/core/lls_core/deconvolution.py @@ -1,12 +1,13 @@ +from __future__ import annotations + from pathlib import Path from resource_backed_dask_array import ResourceBackedDaskArray from lls_core import DeconvolutionChoice -from lls_core.lattice_data import LatticeData import pyclesperanto_prototype as cle import logging import importlib.util -from typing import Collection, Iterable,Union,Literal, Optional +from typing import Collection, Iterable,Union,Literal, Optional, TYPE_CHECKING from aicsimageio.aics_image import AICSImage from aicspylibczi import CziFile from numpy.typing import NDArray @@ -19,6 +20,9 @@ from lls_core.utils import pad_image_nearest_multiple from lls_core.types import ArrayLike, is_arraylike +if TYPE_CHECKING: + from lls_core.lattice_data import LatticeData + logger = logging.getLogger(__name__) def read_psf(psf_paths: Collection[Path], diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 075716a..4fc7b0c 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -4,12 +4,13 @@ from pydantic import BaseModel, Field, NonNegativeInt, NonNegativeFloat, root_validator, validator from aicsimageio.aics_image import AICSImage from aicsimageio.dimensions import Dimensions -from numpy.typing import NDArray +# from numpy.typing import NDArray import math from dask.array.core import Array as DaskArray import dask as da from itertools import groupby import tifffile +from pydantic_numpy import NDArray from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Union @@ -86,7 +87,7 @@ def save_image(self): imagej=True ) -class ProcessedVolume(BaseModel): +class ProcessedVolume(BaseModel, arbitrary_types_allowed=True): """ A slice of the image processing result """ @@ -141,7 +142,7 @@ class CropParams(BaseModel): z_start: NonNegativeInt = 0 z_end: int = 1 -class LatticeData(BaseModel): +class LatticeData(BaseModel, arbitrary_types_allowed=True): """ Holds data and metadata for a given image in a consistent format """ diff --git a/core/lls_core/types.py b/core/lls_core/types.py index aa2b889..077586f 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -1,9 +1,11 @@ from typing import Union from typing_extensions import TypeGuard, Any, TypeAlias from dask.array.core import Array as DaskArray -from numpy.typing import NDArray +# from numpy.typing import NDArray from pyopencl.array import Array as OCLArray import numpy as np +import pydantic_numpy.dtype as pnd +from pydantic_numpy import NDArray, NDArrayFp32, NumpyModel ArrayLike: TypeAlias = Union[DaskArray, NDArray, OCLArray] diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index 7ccd6a8..a861d47 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -211,10 +211,10 @@ def augment_workflow( user_workflow = copy(workflow) _, _, first_task_name, _ = get_first_last_image_and_task(workflow) - for loop_time_idx, time_point in enumerate(times): - output_array = [] - data_table = [] - for loop_ch_idx, ch in enumerate(channels): + # for loop_time_idx, time_point in enumerate(times): + # output_array = [] + # data_table = [] + # for loop_ch_idx, ch in enumerate(channels): if crop: yield from make_crop_workflows( diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 56ae441..e6e65dc 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -5,7 +5,7 @@ from pathlib import Path import dask.array as da import pandas as pd -from typing import allable, Iterable, Optional, Union +from typing import Callable, Iterable, Optional, Union from enum import Enum from magicclass.wrappers import set_design From eee538fe6f7794f88bf370be4c7da7c3c657cf92 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 16 Aug 2023 20:01:06 +1000 Subject: [PATCH 012/147] Initial Restructure of GUI --- core/lls_core/lattice_data.py | 17 +++- plugin/napari_lattice/dock_widget.py | 125 ++++++++++++++++++++++----- 2 files changed, 115 insertions(+), 27 deletions(-) diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 4fc7b0c..06487f4 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -38,6 +38,15 @@ def raise_if_none(obj: Optional[T], message: str) -> T: raise TypeError(message) return obj +class DefaultMixin(BaseModel): + """ + Adds a method for retrieving default values from a BaseModel + """ + + @classmethod + def get_default(cls, field_name: str): + return cls.__fields__[field_name].get_default() + class ProcessedSlices(BaseModel): #: Iterable of result slices. #: Note that this is a finite iterator that can only be iterated once @@ -113,7 +122,7 @@ def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] components.append(f"T{time}") return "_".join(components) -class DefinedPixelSizes(BaseModel): +class DefinedPixelSizes(DefaultMixin): """ Like PhysicalPixelSizes, but it's a dataclass, and none of its fields are None @@ -142,7 +151,7 @@ class CropParams(BaseModel): z_start: NonNegativeInt = 0 z_end: int = 1 -class LatticeData(BaseModel, arbitrary_types_allowed=True): +class LatticeData(DefaultMixin, arbitrary_types_allowed=True): """ Holds data and metadata for a given image in a consistent format """ @@ -261,8 +270,8 @@ def set_deskew(cls, values: dict) -> dict: Sets the default deskew shape values if the user has not provided them """ # process the file to get shape of final deskewed image - if values['deskew_vol_shape'] is None: - if values['deskew_affine_transform'] is None: + if values.get('deskew_vol_shape') is None: + if values.get('deskew_affine_transform') is None: # If neither has been set, calculate them ourselves values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(values["data"], values["angle"], values["dx"], values["dy"], values["dz"]) else: diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index e6e65dc..afdd6c6 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -5,12 +5,15 @@ from pathlib import Path import dask.array as da import pandas as pd -from typing import Callable, Iterable, Optional, Union +from typing import Any, Callable, Iterable, Literal, Optional, Sequence, Tuple, Union, List from enum import Enum from magicclass.wrappers import set_design from magicgui import magicgui -from magicclass import magicclass, field, vfield, set_options, MagicTemplate +from magicclass import magicclass, field, vfield, set_options, MagicTemplate, FieldGroup +from magicclass.fields import MagicValueField +from magicclass.widgets import CollapsibleContainer, CheckBox, RangeSlider +from magicclass.widgets.containers import ContainerWidget, _VCollapsibleContainer, wrap_container from magicclass.utils import click from qtpy.QtCore import Qt @@ -30,7 +33,7 @@ from lls_core import config, DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection from lls_core.io import LatticeData, save_img, save_img_workflow -from lls_core.lattice_data import DeconvolutionParams +from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes from lls_core.types import ArrayLike from lls_core.workflow import get_first_last_image_and_task, modify_workflow_task, get_all_py_files, process_custom_workflow_output, load_custom_py_modules, import_workflow_modules, replace_first_arg from lls_core.utils import check_dimensions, read_imagej_roi, as_type @@ -47,11 +50,6 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -class LastDimensionOptions(Enum): - channel = "Channel" - time = "Time" - get_from_metadata = "Get from Metadata" - class LlszTemplate(MagicTemplate): @property def llsz_parent(self) -> "LLSZWidget": @@ -64,6 +62,48 @@ def parent_viewer(self) -> Viewer: raise Exception("This function can only be used when inside of a Napari viewer") return super().parent_viewer +@magicclass +class DeconvolutionWidget(MagicTemplate): + """ + A counterpart to the DeconvolutionParams Pydantic class + """ + decon_processing = vfield(DeconvolutionChoice, label="Processing Algorithm") + psf = vfield(List[Path], label = "PSFs") + psf_num_iter = vfield(int, label = "Number of Iterations") + +@magicclass +class CroppingWidget(LlszTemplate): + """ + A counterpart to the CropParams Pydantic class + """ + # roi_layer_list: ShapesData + z_start = vfield(int).with_options(value=0, max=10, min=0) + z_end = vfield(int).with_options(value=0, max=10, min=0, label="Z End") + shapes= vfield(ShapesData)#Optional[Shapes] = None + _shapes_layer: Optional[Shapes] = None + + # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") + # @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) + def activate_cropping(self): + self._shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', + face_color=[1, 1, 1, 0], name="Cropping BBOX layer") + # TO select ROIs if needed + self._shapes_layer.mode = "SELECT" + self["activate_cropping"].text = "Cropping layer active" + self["activate_cropping"].background_color = "green" + + def _make_crop_params(self) -> CropParams: + return CropParams( + z_start=self.z_start, + z_end=self.z_end, + roi_layer_list=self.shapes +) + +class LastDimensionOptions(Enum): + XYZTC = "XYZTC" + XYZCT = "XYZCT" + Metadata = "Get from Metadata" + @magicclass(widget_type="split") class LLSZWidget(LlszTemplate): open_file: bool = False @@ -76,6 +116,59 @@ class LlszMenu(LlszTemplate): main_heading = field("

Napari Lattice: Visualization & Analysis

", widget_type="Label") heading1 = field("Drag and drop an image file onto napari.\nOnce image has opened, initialize the\nplugin by clicking the button below.\nEnsure the image layer and voxel sizes are accurate in the prompt.\n If everything initalises properly, the button turns green.", widget_type="Label") + # Pycudadecon library for deconvolution + # options={"enabled": True}, + + img_layer = vfield(Layer).with_options(label = "Image Layer") + pixel_sizes = vfield(Tuple[float, float, float]).with_options( + value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), + label="Pixel Sizes (XYZ)" + ) + angle: MagicValueField[float] = vfield(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") + device = vfield(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") + merge_all_channels = vfield(False).with_options(label="Merge all Channels") + dimension_order = vfield(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") + skew_dir = vfield(DeskewDirection.Y).with_options(label = "Skew Direction") + set_logging = vfield(Log_Levels.INFO).with_options(label="Logging Level") + + deconvolution = vfield(bool, name="Use Deconvolution") + deconvolution.value = False + deconv_widget = DeconvolutionWidget() + deconv_widget.enabled = False + deconv_widget.visible = False + + @deconvolution.connect + def _set_decon(self) -> None: + if self.deconvolution: + logger.info("Deconvolution Activated") + # Enable deconvolutio by using the saved parameters + # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv + self.deconv_widget.enabled = True + self.deconv_widget.visible = True + else: + logger.info("Deconvolution Disabled") + # self.llsz_parent.lattice.deconvolution = None + self.deconv_widget.enabled = False + self.deconv_widget.visible = False + + cropping_enabled = vfield(bool, name="Use Cropping", options={"value": False}) + crop_widget = CroppingWidget() + crop_widget.enabled = False + crop_widget.visible = False + + @cropping_enabled.connect + def _set_crop(self) -> None: + if self.cropping_enabled: + logger.info("Cropping Activated") + # Enable deconvolutio by using the saved parameters + # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv + self.crop_widget.enabled = True + self.crop_widget.visible = True + else: + logger.info("Deconvolution Disabled") + # self.llsz_parent.lattice.deconvolution = None + self.crop_widget.enabled = False + self.crop_widget.visible = False @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Initialize Plugin", max_height=75, font_size=13) @set_options(pixel_size_dx={"widget_type": "FloatSpinBox", "value": 0.1449922, "step": 0.000000001}, @@ -104,7 +197,7 @@ def Choose_Image_Layer(self, angle: float = 30, select_device: str = cle.available_device_names()[ 0], - last_dimension_channel: LastDimensionOptions = LastDimensionOptions.get_from_metadata, + last_dimension_channel: LastDimensionOptions = "", merge_all_channel_layers: bool = False, skew_dir: DeskewDirection=DeskewDirection.Y, set_logging: Log_Levels=Log_Levels.INFO): @@ -177,20 +270,6 @@ def Choose_Image_Layer(self, self["Choose_Image_Layer"].background_color = "green" self["Choose_Image_Layer"].text = "Plugin Initialised" - # Pycudadecon library for deconvolution - # options={"enabled": True}, - deconvolution = vfield(bool, name="Use Deconvolution") - deconvolution.value = False - - @deconvolution.connect - def _set_decon(self): - if self.deconvolution: - logger.info("Deconvolution Activated") - # Enable deconvolutio by using the saved parameters - self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv - else: - logger.info("Deconvolution Disabled") - self.llsz_parent.lattice.deconvolution = None @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) @set_options(header=dict(widget_type="Label", label="

Enter path to the PSF images

"), From 48b971aa4de17503db55ebd6099e3d285eec902f Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 17 Aug 2023 17:02:28 +1000 Subject: [PATCH 013/147] Finish draft of one-page GUI --- plugin/napari_lattice/dock_widget.py | 1601 ++++++++++++++------------ 1 file changed, 842 insertions(+), 759 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index afdd6c6..ce1e034 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -6,7 +6,7 @@ import dask.array as da import pandas as pd from typing import Any, Callable, Iterable, Literal, Optional, Sequence, Tuple, Union, List -from enum import Enum +from enum import Enum, auto from magicclass.wrappers import set_design from magicgui import magicgui @@ -50,6 +50,10 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +class WorkflowSource(Enum): + ActiveWorkflow = auto() + CustomPath = auto() + class LlszTemplate(MagicTemplate): @property def llsz_parent(self) -> "LLSZWidget": @@ -77,9 +81,15 @@ class CroppingWidget(LlszTemplate): A counterpart to the CropParams Pydantic class """ # roi_layer_list: ShapesData - z_start = vfield(int).with_options(value=0, max=10, min=0) - z_end = vfield(int).with_options(value=0, max=10, min=0, label="Z End") - shapes= vfield(ShapesData)#Optional[Shapes] = None + shapes= vfield(ShapesData, label = "ROIs")#Optional[Shapes] = None + z_range = vfield(Tuple[int, int]).with_options( + label = "Z Range", + value = (0, 1), + options = dict( + min = 0, + max = 1 + ) + ) _shapes_layer: Optional[Shapes] = None # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") @@ -97,7 +107,15 @@ def _make_crop_params(self) -> CropParams: z_start=self.z_start, z_end=self.z_end, roi_layer_list=self.shapes -) + ) + +@magicclass +class WorkflowWidget(LlszTemplate): + """ + Handles the workflow related parameters + """ + workflow_source = vfield(WorkflowSource).with_options(label = "Workflow Source") + workflow_path = vfield(Path).with_options(label = "Workflow Path", visible=False) class LastDimensionOptions(Enum): XYZTC = "XYZTC" @@ -115,7 +133,7 @@ class LLSZWidget(LlszTemplate): class LlszMenu(LlszTemplate): main_heading = field("

Napari Lattice: Visualization & Analysis

", widget_type="Label") - heading1 = field("Drag and drop an image file onto napari.\nOnce image has opened, initialize the\nplugin by clicking the button below.\nEnsure the image layer and voxel sizes are accurate in the prompt.\n If everything initalises properly, the button turns green.", widget_type="Label") + heading1 = field("Drag and drop an image file onto napari.", widget_type="Label") # Pycudadecon library for deconvolution # options={"enabled": True}, @@ -130,9 +148,34 @@ class LlszMenu(LlszTemplate): dimension_order = vfield(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") skew_dir = vfield(DeskewDirection.Y).with_options(label = "Skew Direction") set_logging = vfield(Log_Levels.INFO).with_options(label="Logging Level") + time_range = vfield(Tuple[int, int]).with_options( + label="Time Export Range", + value=(0, 10), + options = dict( + min=0, + max=10, + ) + ) + channel_range = vfield(Tuple[int, int]).with_options( + label="Channel Range", + value=(0, 10), + options = dict( + min=0, + max=10, + ) + ) + save_type = vfield(SaveFileType).with_options( + label = "Save Format" + ) + save_path = vfield(Path).with_options( + label = "Save Directory", + value = Path(history.get_save_history()[0]) + ) - deconvolution = vfield(bool, name="Use Deconvolution") - deconvolution.value = False + ### + # Deconvolution + ### + deconvolution = vfield(bool, name="Use Deconvolution").with_options(value=False) deconv_widget = DeconvolutionWidget() deconv_widget.enabled = False deconv_widget.visible = False @@ -151,7 +194,11 @@ def _set_decon(self) -> None: self.deconv_widget.enabled = False self.deconv_widget.visible = False - cropping_enabled = vfield(bool, name="Use Cropping", options={"value": False}) + ### + # Cropping + ### + + cropping_enabled = vfield(bool, name="Use Cropping").with_options(value=False) crop_widget = CroppingWidget() crop_widget.enabled = False crop_widget.visible = False @@ -170,760 +217,796 @@ def _set_crop(self) -> None: self.crop_widget.enabled = False self.crop_widget.visible = False - @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Initialize Plugin", max_height=75, font_size=13) - @set_options(pixel_size_dx={"widget_type": "FloatSpinBox", "value": 0.1449922, "step": 0.000000001}, - pixel_size_dy={"widget_type": "FloatSpinBox", - "value": 0.1449922, "step": 0.000000001}, - pixel_size_dz={"widget_type": "FloatSpinBox", - "value": 0.3, "step": 0.000000001}, - angle={"widget_type": "FloatSpinBox", - "value": 30, "step": 0.1}, - select_device={"widget_type": "ComboBox", "choices": cle.available_device_names( - ), "value": cle.available_device_names()[0]}, - last_dimension_channel={"widget_type": "ComboBox", - "label": "Set Last dimension (channel/time)", "tooltip": "If the last dimension is initialised incorrectly, you can assign it as either channel/time"}, - merge_all_channel_layers={"widget_type": "CheckBox", "value": True, "label": "Merge all napari layers as channels", - "tooltip": "Use this option if the channels are in separate layers. napari-lattice requires all channels to be in same layer"}, - skew_dir={"widget_type": "ComboBox", "choices": DeskewDirection, "value": DeskewDirection.Y, - "label": "Direction of skew (Y or X)", "tooltip": "Skew direction when image is acquired. Ask your microscopist for details"}, - set_logging={"widget_type": "ComboBox", "choices": Log_Levels, "value": Log_Levels.INFO, - "label": "Log Level", "tooltip": "Only use for debugging. Leave it as INFO for regular operation"} - ) - def Choose_Image_Layer(self, - img_layer: Layer, - pixel_size_dx: float = 0.1449922, - pixel_size_dy: float = 0.1449922, - pixel_size_dz: float = 0.3, - angle: float = 30, - select_device: str = cle.available_device_names()[ - 0], - last_dimension_channel: LastDimensionOptions = "", - merge_all_channel_layers: bool = False, - skew_dir: DeskewDirection=DeskewDirection.Y, - set_logging: Log_Levels=Log_Levels.INFO): - - logger.setLevel(set_logging.value) - config.log_level = set_logging.value - logger.info(f"Logging set to {set_logging}") - logger.info("Using existing image layer") - - # Select device for processing - cle.select_device(select_device) - - # merge all napari image layers as one multidimensional image - if merge_all_channel_layers: - from napari.layers.utils.stack_utils import images_to_stack - # get list of napari layers as a list - layer_list = list(self.parent_viewer.layers) - # if more than one layer - if len(layer_list) > 1: - # convert the list of images into a stack - new_layer = images_to_stack(layer_list) - # select all current layers - self.parent_viewer.layers.select_all() - # remove selected layers - self.parent_viewer.layers.remove_selected() - # add the new composite image layer - self.parent_viewer.add_layer(new_layer) - img_layer = new_layer - - self.llsz_parent.lattice = lattice_from_napari( - img=img_layer, - last_dimension=None if last_dimension_channel == LastDimensionOptions.get_from_metadata else last_dimension_channel, - angle=angle, - skew=skew_dir, - physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz), - # deconvolution = DeconvolutionParams() - ) - # flag for ensuring a file has been opened and plugin initialised - self.llsz_parent.open_file = True - - logger.info( - f"Pixel size (ZYX) in microns: {self.llsz_parent.lattice.dz,self.llsz_parent.lattice.dy,self.llsz_parent.lattice.dx}") - logger.info( - f"Dimensions of image layer (ZYX): {list(self.llsz_parent.lattice.data.shape[-3:])}") - logger.info( - f"Dimensions of deskewed image (ZYX): {self.llsz_parent.lattice.deskew_vol_shape}") - logger.info( - f"Deskewing angle is: {self.llsz_parent.lattice.angle}") - logger.info( - f"Deskew Direction: {self.llsz_parent.lattice.skew}") - # Add dimension labels correctly - # if channel, and not time - if self.llsz_parent.lattice.time == 0 and (last_dimension_channel or self.llsz_parent.lattice.channels > 0): - self.parent_viewer.dims.axis_labels = ('Channel', "Z", "Y", "X") - # if no channel, but has time - elif self.llsz_parent.lattice.channels == 0 and self.llsz_parent.lattice.time > 0: - self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") - # if it has channels - elif self.llsz_parent.lattice.channels > 1: - # If merge to stack is used, channel slider goes to the bottom - if int(self.parent_viewer.dims.dict()["range"][0][1]) == self.llsz_parent.lattice.channels: - self.parent_viewer.dims.axis_labels = ('Channel', "Time", "Z", "Y", "X") - else: - self.parent_viewer.dims.axis_labels = ('Time', "Channel", "Z", "Y", "X") - # if channels initialized by aicsimagio, then channels is 1 - elif self.llsz_parent.lattice.channels == 1 and self.llsz_parent.lattice.time > 1: - self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") - - logger.info(f"Initialised") - self["Choose_Image_Layer"].background_color = "green" - self["Choose_Image_Layer"].text = "Plugin Initialised" - - - @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) - @set_options(header=dict(widget_type="Label", label="

Enter path to the PSF images

"), - psf_ch1_path={"widget_type": "FileEdit", - "label": "Channel 1:"}, - psf_ch2_path={"widget_type": "FileEdit", - "label": "Channel 2"}, - psf_ch3_path={"widget_type": "FileEdit", - "label": "Channel 3"}, - psf_ch4_path={"widget_type": "FileEdit", - "label": "Channel 4"}, - device_option={ - "widget_type": "ComboBox", "label": "Choose processing device", "choices": DeconvolutionChoice}, - no_iter={ - "widget_type": "SpinBox", "label": "No of iterations (Deconvolution)", "value": 10, "min": 1, "max": 50, "step": 1} - ) - def deconvolution_gui(self, - header: str, - psf_ch1_path: Path, - psf_ch2_path: Path, - psf_ch3_path: Path, - psf_ch4_path: Path, - device_option: DeconvolutionChoice, - no_iter: int): - """GUI for Deconvolution button""" - # Force deconvolution to be true if we do this - if not self.llsz_parent.lattice.deconvolution: - raise Exception("Deconvolution is set to False. Tick the box to activate deconvolution.") - self.llsz_parent.deconv.decon_processing = device_option - self.llsz_parent.deconv.psf = list(read_psf([ - psf_ch1_path, - psf_ch2_path, - psf_ch3_path, - psf_ch4_path, - ], - device_option, - lattice_class=self.llsz_parent.lattice - )) - self.llsz_parent.deconv.psf_num_iter = no_iter - self["deconvolution_gui"].background_color = "green" - self["deconvolution_gui"].text = "PSFs added" - - @magicclass(widget_type="collapsible") - class Preview: - @magicgui(header=dict(widget_type="Label", label="

Preview Deskew

"), - time=dict(label="Time:", max=2**15), - channel=dict(label="Channel:"), - call_button="Preview") - def Preview_Deskew(self, - header: str, - time: int, - channel: int, - img_data: ImageData): - """ - Preview deskewed data for a single timepoint and channel - - """ - _Preview(LLSZWidget, - self, - time, - channel, - img_data) + ### + # Workflow + ### + workflow_enabled = vfield(bool, name="Use Workflow").with_options(value=False) + workflow_widget = WorkflowWidget() + workflow_widget.enabled = False + workflow_widget.visible = False + + @workflow_enabled.connect + def _set_workflow(self) -> None: + if self.workflow_enabled: + logger.info("Workflow Activated") + # Enable deconvolutio by using the saved parameters + # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv + self.workflow_widget.enabled = True + self.workflow_widget.visible = True + else: + logger.info("Deconvolution Disabled") + # self.llsz_parent.lattice.deconvolution = None + self.workflow_widget.enabled = False + self.workflow_widget.visible = False + + # @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Initialize Plugin", max_height=75, font_size=13) + # @set_options(pixel_size_dx={"widget_type": "FloatSpinBox", "value": 0.1449922, "step": 0.000000001}, + # pixel_size_dy={"widget_type": "FloatSpinBox", + # "value": 0.1449922, "step": 0.000000001}, + # pixel_size_dz={"widget_type": "FloatSpinBox", + # "value": 0.3, "step": 0.000000001}, + # angle={"widget_type": "FloatSpinBox", + # "value": 30, "step": 0.1}, + # select_device={"widget_type": "ComboBox", "choices": cle.available_device_names( + # ), "value": cle.available_device_names()[0]}, + # last_dimension_channel={"widget_type": "ComboBox", + # "label": "Set Last dimension (channel/time)", "tooltip": "If the last dimension is initialised incorrectly, you can assign it as either channel/time"}, + # merge_all_channel_layers={"widget_type": "CheckBox", "value": True, "label": "Merge all napari layers as channels", + # "tooltip": "Use this option if the channels are in separate layers. napari-lattice requires all channels to be in same layer"}, + # skew_dir={"widget_type": "ComboBox", "choices": DeskewDirection, "value": DeskewDirection.Y, + # "label": "Direction of skew (Y or X)", "tooltip": "Skew direction when image is acquired. Ask your microscopist for details"}, + # set_logging={"widget_type": "ComboBox", "choices": Log_Levels, "value": Log_Levels.INFO, + # "label": "Log Level", "tooltip": "Only use for debugging. Leave it as INFO for regular operation"} + # ) + # def Choose_Image_Layer(self, + # img_layer: Layer, + # pixel_size_dx: float = 0.1449922, + # pixel_size_dy: float = 0.1449922, + # pixel_size_dz: float = 0.3, + # angle: float = 30, + # select_device: str = cle.available_device_names()[ + # 0], + # last_dimension_channel: LastDimensionOptions = "", + # merge_all_channel_layers: bool = False, + # skew_dir: DeskewDirection=DeskewDirection.Y, + # set_logging: Log_Levels=Log_Levels.INFO): + + # logger.setLevel(set_logging.value) + # config.log_level = set_logging.value + # logger.info(f"Logging set to {set_logging}") + # logger.info("Using existing image layer") + + # # Select device for processing + # cle.select_device(select_device) + + # # merge all napari image layers as one multidimensional image + # if merge_all_channel_layers: + # from napari.layers.utils.stack_utils import images_to_stack + # # get list of napari layers as a list + # layer_list = list(self.parent_viewer.layers) + # # if more than one layer + # if len(layer_list) > 1: + # # convert the list of images into a stack + # new_layer = images_to_stack(layer_list) + # # select all current layers + # self.parent_viewer.layers.select_all() + # # remove selected layers + # self.parent_viewer.layers.remove_selected() + # # add the new composite image layer + # self.parent_viewer.add_layer(new_layer) + # img_layer = new_layer + + # self.llsz_parent.lattice = lattice_from_napari( + # img=img_layer, + # last_dimension=None if last_dimension_channel == LastDimensionOptions.get_from_metadata else last_dimension_channel, + # angle=angle, + # skew=skew_dir, + # physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz), + # # deconvolution = DeconvolutionParams() + # ) + # # flag for ensuring a file has been opened and plugin initialised + # self.llsz_parent.open_file = True + + # logger.info( + # f"Pixel size (ZYX) in microns: {self.llsz_parent.lattice.dz,self.llsz_parent.lattice.dy,self.llsz_parent.lattice.dx}") + # logger.info( + # f"Dimensions of image layer (ZYX): {list(self.llsz_parent.lattice.data.shape[-3:])}") + # logger.info( + # f"Dimensions of deskewed image (ZYX): {self.llsz_parent.lattice.deskew_vol_shape}") + # logger.info( + # f"Deskewing angle is: {self.llsz_parent.lattice.angle}") + # logger.info( + # f"Deskew Direction: {self.llsz_parent.lattice.skew}") + # # Add dimension labels correctly + # # if channel, and not time + # if self.llsz_parent.lattice.time == 0 and (last_dimension_channel or self.llsz_parent.lattice.channels > 0): + # self.parent_viewer.dims.axis_labels = ('Channel', "Z", "Y", "X") + # # if no channel, but has time + # elif self.llsz_parent.lattice.channels == 0 and self.llsz_parent.lattice.time > 0: + # self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") + # # if it has channels + # elif self.llsz_parent.lattice.channels > 1: + # # If merge to stack is used, channel slider goes to the bottom + # if int(self.parent_viewer.dims.dict()["range"][0][1]) == self.llsz_parent.lattice.channels: + # self.parent_viewer.dims.axis_labels = ('Channel', "Time", "Z", "Y", "X") + # else: + # self.parent_viewer.dims.axis_labels = ('Time', "Channel", "Z", "Y", "X") + # # if channels initialized by aicsimagio, then channels is 1 + # elif self.llsz_parent.lattice.channels == 1 and self.llsz_parent.lattice.time > 1: + # self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") + + # logger.info(f"Initialised") + # self["Choose_Image_Layer"].background_color = "green" + # self["Choose_Image_Layer"].text = "Plugin Initialised" + + + # @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) + # @set_options(header=dict(widget_type="Label", label="

Enter path to the PSF images

"), + # psf_ch1_path={"widget_type": "FileEdit", + # "label": "Channel 1:"}, + # psf_ch2_path={"widget_type": "FileEdit", + # "label": "Channel 2"}, + # psf_ch3_path={"widget_type": "FileEdit", + # "label": "Channel 3"}, + # psf_ch4_path={"widget_type": "FileEdit", + # "label": "Channel 4"}, + # device_option={ + # "widget_type": "ComboBox", "label": "Choose processing device", "choices": DeconvolutionChoice}, + # no_iter={ + # "widget_type": "SpinBox", "label": "No of iterations (Deconvolution)", "value": 10, "min": 1, "max": 50, "step": 1} + # ) + # def deconvolution_gui(self, + # header: str, + # psf_ch1_path: Path, + # psf_ch2_path: Path, + # psf_ch3_path: Path, + # psf_ch4_path: Path, + # device_option: DeconvolutionChoice, + # no_iter: int): + # """GUI for Deconvolution button""" + # # Force deconvolution to be true if we do this + # if not self.llsz_parent.lattice.deconvolution: + # raise Exception("Deconvolution is set to False. Tick the box to activate deconvolution.") + # self.llsz_parent.deconv.decon_processing = device_option + # self.llsz_parent.deconv.psf = list(read_psf([ + # psf_ch1_path, + # psf_ch2_path, + # psf_ch3_path, + # psf_ch4_path, + # ], + # device_option, + # lattice_class=self.llsz_parent.lattice + # )) + # self.llsz_parent.deconv.psf_num_iter = no_iter + # self["deconvolution_gui"].background_color = "green" + # self["deconvolution_gui"].text = "PSFs added" + + @set_options(header=dict(widget_type="Label", label="

Preview Deskew

"), + time=dict(label="Time:", max=2**15), + channel=dict(label="Channel:"), + call_button="Preview" + ) + @set_design(text="Preview") + def preview(self, header:str, time: int, channel: int): + pass + + @set_design(text="Save") + def save(self): + pass + + + # @magicclass(widget_type="collapsible") + # class Preview: + # @magicgui(header=dict(widget_type="Label", label="

Preview Deskew

"), + # time=dict(label="Time:", max=2**15), + # channel=dict(label="Channel:"), + # call_button="Preview") + # def Preview_Deskew(self, + # header: str, + # time: int, + # channel: int, + # img_data: ImageData): + # """ + # Preview deskewed data for a single timepoint and channel + + # """ + # _Preview(LLSZWidget, + # self, + # time, + # channel, + # img_data) # Tabbed Widget container to house all the widgets - @magicclass(widget_type="tabbed", name="Functions") - class WidgetContainer(LlszTemplate): - - @magicclass(name="Deskew", widget_type="scrollable", properties={"min_width": 100}) - class DeskewWidget(LlszTemplate): - - @magicgui(header=dict(widget_type="Label", label="

Deskew and Save

"), - time_start=dict(label="Time Start:", max=2**20), - time_end=dict(label="Time End:", value=1, max=2**20), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType, "value": SaveFileType.h5}, - save_path=dict(mode='d', label="Directory to save"), - call_button="Save") - def Deskew_Save(self, - header: str, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - save_as_type: str, - save_path: Path = Path(history.get_save_history()[0])): - """ Widget to Deskew and Save Data""" - _Deskew_Save(LLSZWidget, - time_start, - time_end, - ch_start, - ch_end, - save_as_type, - save_path) - - @magicclass(name="Crop and Deskew", widget_type="scrollable") - class CropWidget(LlszTemplate): + # @magicclass(widget_type="tabbed", name="Functions") + # class WidgetContainer(LlszTemplate): + + # @magicclass(name="Deskew", widget_type="scrollable", properties={"min_width": 100}) + # class DeskewWidget(LlszTemplate): + + # @magicgui(header=dict(widget_type="Label", label="

Deskew and Save

"), + # time_start=dict(label="Time Start:", max=2**20), + # time_end=dict(label="Time End:", value=1, max=2**20), + # ch_start=dict(label="Channel Start:"), + # ch_end=dict(label="Channel End:", value=1), + # save_as_type={ + # "label": "Save as filetype:", "choices": SaveFileType, "value": SaveFileType.h5}, + # save_path=dict(mode='d', label="Directory to save"), + # call_button="Save") + # def Deskew_Save(self, + # header: str, + # time_start: int, + # time_end: int, + # ch_start: int, + # ch_end: int, + # save_as_type: str, + # save_path: Path = Path(history.get_save_history()[0])): + # """ Widget to Deskew and Save Data""" + # _Deskew_Save(LLSZWidget, + # time_start, + # time_end, + # ch_start, + # ch_end, + # save_as_type, + # save_path) + + # @magicclass(name="Crop and Deskew", widget_type="scrollable") + # class CropWidget(LlszTemplate): - # add function for previewing cropped image - @magicclass(name="Cropping Preview", widget_type="scrollable", properties={ - "min_width": 100, - "shapes_layer": Shapes - }) - class Preview_Crop_Menu(LlszTemplate): - - @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") - @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) - def activate_cropping(self): - self.llsz_parent.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', - face_color=[1, 1, 1, 0], name="Cropping BBOX layer") - # TO select ROIs if needed - self.llsz_parent.shapes_layer.mode = "SELECT" - self["activate_cropping"].text = "Cropping layer active" - self["activate_cropping"].background_color = "green" - - heading2 = field("You can either import ImageJ ROI (.zip) files or manually define ROIs using the shape layer", widget_type="Label") - - @click(enabled=False) - def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])) -> None: - logger.info(f"Opening{path}") - roi_list = read_imagej_roi(str(path)) - # convert to canvas coordinates - self.find_ancestor(LLSZWidget) - roi_list = (np.array(roi_list) * self.llsz_parent.lattice.dy).tolist() - self.llsz_parent.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', face_color=[1, 1, 1, 0]) - - time_crop = field(int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") - chan_crop = field(int, options={"min": 0, "step": 1}, name="Channels") - heading_roi = field("If there are multiple ROIs, select the ROI before clicking button below", widget_type="Label") - #roi_idx = field(int, options={"min": 0, "step": 1}, name="ROI number") - - @click(enabled=False) - def Crop_Preview(self, roi_layer: ShapesData): - - if not roi_layer: - raise Exception("No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs.") - # TODO: Add assertion to check if bbox layer or coordinates - - # Slice out the image of interest to preview - time = self.time_crop.value - channel = self.chan_crop.value - if time >= self.llsz_parent.lattice.time: - raise ValueError("Time is out of range") - if time >= self.llsz_parent.lattice.time: - raise ValueError("Channel is out of range") - logger.info(f"Using channel {channel} and time {time}") - - # if only one roi drawn, use the first ROI for cropping - if len(self.llsz_parent.shapes_layer.selected_data) == 0: - raise Exception("Please select an ROI") - - roi_idx = list(self.llsz_parent.shapes_layer.selected_data)[0] - - # As the original image is scaled, the coordinates are in microns, so we need to convert - # roi from micron to canvas/world coordinates - roi_choice = [x/self.llsz_parent.lattice.dy for x in roi_layer[roi_idx]] - logger.info(f"Previewing ROI {roi_idx}") - - # crop - - # Set the deconvolution options - if self.llsz_parent.deconvolution: - if not self.llsz_parent.lattice.psf or not self.llsz_parent.lattice.psf_num_iter or not self.llsz_parent.lattice.decon_processing: - raise Exception( - "PSF fields should be set by this point!") - logger.info( - f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") - decon_kwargs = dict( - decon_processing=self.llsz_parent.lattice.decon_processing.value, - psf=self.llsz_parent.lattice.psf[channel], - num_iter=self.llsz_parent.lattice.psf_num_iter - ) - else: - decon_kwargs = dict() - - crop_roi_vol_desk = cle.pull( - crop_volume_deskew( - original_volume=np.array(self.llsz_parent.lattice.data[time, channel, ...]), - roi_shape=roi_choice, - angle_in_degrees=self.llsz_parent.angle_value, - voxel_size_x=self.llsz_parent.lattice.dx, - voxel_size_y=self.llsz_parent.lattice.dy, - voxel_size_z=self.llsz_parent.lattice.dz, - deconvolution=self.llsz_parent.deconvolution, - # Option for entering custom z start value? - z_start=0, - z_end=self.llsz_parent.lattice.deskew_vol_shape[0], - skew_dir=self.llsz_parent.skew_dir, - **decon_kwargs - ).astype(self.llsz_parent.lattice.data.dtype) - ) - - # get array back from gpu or addding cle array to napari can throw errors - image = next(self.llsz_parent.lattice.process()) - - scale = ( - self.llsz_parent.lattice.new_dz, - self.llsz_parent.lattice.dy, - self.llsz_parent.lattice.dx - ) - self.parent_viewer.add_image( - crop_roi_vol_desk, scale=scale) - - @magicclass(name="Crop and Save Data") - class CropSaveData(LlszTemplate): - @magicgui(header=dict(widget_type="Label", label="

Crop and Save Data

"), - time_start=dict(label="Time Start:"), - time_end=dict(label="Time End:", value=1), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType}, - save_path=dict(mode='d', label="Directory to save ")) - def Crop_Save(self, - header: str, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - save_as_type: SaveFileType, - roi_layer_list: ShapesData, - save_path: Path = Path(history.get_save_history()[0])): - - if not roi_layer_list: - logger.error( - "No coordinates found or cropping. Initialise shapes layer and draw ROIs.") - else: - if not self.llsz_parent.open_file: - raise Exception("Image not initialised") - - check_dimensions(time_start, time_end, ch_start, ch_end, self.llsz_parent.lattice.channels, self.llsz_parent.lattice.time) - - angle = self.llsz_parent.lattice.angle - dx = self.llsz_parent.lattice.dx - dy = self.llsz_parent.lattice.dy - dz = self.llsz_parent.lattice.dz - - # get image data - img_data = self.llsz_parent.lattice.data - # Get shape of deskewed image - deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - - logger.info("Cropping and saving files...") - - # necessary when scale is used for napari.viewer.add_image operations - roi_layer_list = ShapesData([x/self.llsz_parent.lattice.dy for x in roi_layer_list]) - - for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): - # pass arguments for save tiff, callable and function arguments - logger.info("Processing ROI ", idx) - # pass parameters for the crop_volume_deskew function - - save_img(vol=img_data, - func=crop_volume_deskew, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_name_prefix="ROI_" + str(idx), - save_path=save_path, - save_file_type=save_as_type, - save_name=self.llsz_parent.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deskewed_volume=deskewed_volume, - roi_shape=roi_layer, - angle_in_degrees=angle, - z_start=z_start, - z_end=z_end, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - LLSZWidget=self.llsz_parent - ) - - logger.info( - f"Cropping and Saving Complete -> {save_path}") - - @magicclass(name="Workflow", widget_type="scrollable") - class WorkflowWidget(LlszTemplate): - - @magicclass(name="Preview Workflow", widget_type="scrollable") - class PreviewWorkflow(LlszTemplate): - #time_preview= field(int, options={"min": 0, "step": 1}, name="Time") - #chan_preview = field(int, options={"min": 0, "step": 1}, name="Channels") - @magicgui(header=dict(widget_type="Label", label="

Preview Workflow

"), - time_preview=dict(label="Time:", max=2**20), - chan_preview=dict(label="Channel:"), - get_active_workflow=dict( - widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), - workflow_path=dict( - mode='r', label="Load custom workflow (.yaml/yml)"), - Use_Cropping=dict( - widget_type="Checkbox", label="Crop Data", value=False), - #custom_module=dict(widget_type="Checkbox",label="Load custom module (looks for *.py files in the workflow directory)",value = False), - call_button="Apply and Preview Workflow") - def Workflow_Preview(self, - header: str, - time_preview: int, - chan_preview: int, - get_active_workflow: bool, - Use_Cropping: bool, - roi_layer_list: ShapesData, - workflow_path: Path = Path.home()): - """ - Apply napari_workflows to the processing pipeline - User can define a pipeline which can be inspected in napari workflow inspector - and then execute it by ticking the get active workflow checkbox, - OR - Use a predefined workflow - - In both cases, if deskewing is not present as first step, it will be added on - and rest of the task will be made followers - """ - logger.info("Previewing deskewed channel and time with workflow") - user_workflow = get_workflow(self.parent_viewer if get_active_workflow else workflow_path) - - # when using fields, self.time_preview.value - if time_preview >= self.llsz_parent.lattice.time: - raise ValueError("Time is out of range") - if chan_preview >= self.llsz_parent.lattice.channels: - raise ValueError("Channel is out of range") - - time = time_preview - channel = chan_preview - - # to access current time and channel and pass it to workflow file - config.channel = channel - config.time = time - - logger.info(f"Processing for Time: {time} and Channel: {channel}") - - logger.info("Workflow to be executed:") - logger.info(user_workflow) - # Execute workflow - processed_vol = user_workflow.get(task_name_last) - - # check if a measurement table (usually a dictionary or list) or a tuple with different data types - # The function below saves the tables and adds any images to napari window - if type(processed_vol) in [dict, list, tuple]: - if (len(processed_vol) > 1): - df = pd.DataFrame() - for idx, i in enumerate(processed_vol): - df_temp = process_custom_workflow_output( - i, parent_dir, idx, LLSZWidget, self, channel, time, preview=True) - final_df = pd.concat([df, df_temp]) - # append dataframes from every loop and have table command outside loop? - # TODO: Figure out why table is not displaying - from napari_spreadsheet import _widget - table_viewer = _widget.TableViewerWidget( - show=True) - table_viewer.add_spreadsheet(final_df) - # widgets.Table(value=final_df).show() - - else: - # add image to napari window - # TODO: check if its an image napari supports? - process_custom_workflow_output( - processed_vol, parent_dir, 0, LLSZWidget, self, channel, time) - - print("Workflow complete") - - @magicgui(header=dict(widget_type="Label", label="

Apply Workflow and Save Output

"), - time_start=dict(label="Time Start:", max=2**20), - time_end=dict(label="Time End:", - value=1, max=2**20), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - Use_Cropping=dict( - widget_type="Checkbox", label="Crop Data", value=False), - get_active_workflow=dict( - widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), - workflow_path=dict( - mode='r', label="Load custom workflow (.yaml/yml)"), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType}, - save_path=dict( - mode='d', label="Directory to save "), - #custom_module=dict(widget_type="Checkbox",label="Load custom module (same dir as workflow)",value = False), - call_button="Apply Workflow and Save Result") - def Apply_Workflow_and_Save(self, - header: str, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - Use_Cropping: bool, - roi_layer_list: ShapesData, - get_active_workflow: bool = False, - workflow_path: Path = Path.home(), - save_as_type: str = SaveFileType.tiff, - save_path: Path = Path(history.get_save_history()[0])): - """ - Apply a user-defined analysis workflow using napari-workflows - - Args: - time_start (int): Start Time - time_end (int): End Time - ch_start (int): Start Channel - ch_end (int): End Channel - Use_Cropping (_type_): Use cropping based on ROIs in the shapes layer - roi_layer_list (ShapesData): Shapes layer to use for cropping; can be a list of shapes - get_active_workflow (bool, optional): Gets active workflow in napari. Defaults to False. - workflow_path (Path, optional): User can also choose a custom workflow defined in a yaml file. - save_path (Path, optional): Path to save resulting data - """ - if not self.llsz_parent.open_file: - raise Exception("Image not initialised") - - check_dimensions(time_start, time_end, ch_start, ch_end, - self.llsz_parent.lattice.channels, self.llsz_parent.lattice.time) - - # Get parameters - angle = self.llsz_parent.lattice.angle - dx = self.llsz_parent.lattice.dx - dy = self.llsz_parent.lattice.dy - dz = self.llsz_parent.lattice.dz - - user_workflow = get_workflow(self.parent_viewer if get_active_workflow else workflow_path) + # # add function for previewing cropped image + # @magicclass(name="Cropping Preview", widget_type="scrollable", properties={ + # "min_width": 100, + # "shapes_layer": Shapes + # }) + # class Preview_Crop_Menu(LlszTemplate): + + # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") + # @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) + # def activate_cropping(self): + # self.llsz_parent.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', + # face_color=[1, 1, 1, 0], name="Cropping BBOX layer") + # # TO select ROIs if needed + # self.llsz_parent.shapes_layer.mode = "SELECT" + # self["activate_cropping"].text = "Cropping layer active" + # self["activate_cropping"].background_color = "green" + + # heading2 = field("You can either import ImageJ ROI (.zip) files or manually define ROIs using the shape layer", widget_type="Label") + + # @click(enabled=False) + # def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])) -> None: + # logger.info(f"Opening{path}") + # roi_list = read_imagej_roi(str(path)) + # # convert to canvas coordinates + # self.find_ancestor(LLSZWidget) + # roi_list = (np.array(roi_list) * self.llsz_parent.lattice.dy).tolist() + # self.llsz_parent.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', face_color=[1, 1, 1, 0]) + + # time_crop = field(int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") + # chan_crop = field(int, options={"min": 0, "step": 1}, name="Channels") + # heading_roi = field("If there are multiple ROIs, select the ROI before clicking button below", widget_type="Label") + # #roi_idx = field(int, options={"min": 0, "step": 1}, name="ROI number") + + # @click(enabled=False) + # def Crop_Preview(self, roi_layer: ShapesData): + + # if not roi_layer: + # raise Exception("No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs.") + # # TODO: Add assertion to check if bbox layer or coordinates + + # # Slice out the image of interest to preview + # time = self.time_crop.value + # channel = self.chan_crop.value + # if time >= self.llsz_parent.lattice.time: + # raise ValueError("Time is out of range") + # if time >= self.llsz_parent.lattice.time: + # raise ValueError("Channel is out of range") + # logger.info(f"Using channel {channel} and time {time}") + + # # if only one roi drawn, use the first ROI for cropping + # if len(self.llsz_parent.shapes_layer.selected_data) == 0: + # raise Exception("Please select an ROI") + + # roi_idx = list(self.llsz_parent.shapes_layer.selected_data)[0] + + # # As the original image is scaled, the coordinates are in microns, so we need to convert + # # roi from micron to canvas/world coordinates + # roi_choice = [x/self.llsz_parent.lattice.dy for x in roi_layer[roi_idx]] + # logger.info(f"Previewing ROI {roi_idx}") + + # # crop + + # # Set the deconvolution options + # if self.llsz_parent.deconvolution: + # if not self.llsz_parent.lattice.psf or not self.llsz_parent.lattice.psf_num_iter or not self.llsz_parent.lattice.decon_processing: + # raise Exception( + # "PSF fields should be set by this point!") + # logger.info( + # f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") + # decon_kwargs = dict( + # decon_processing=self.llsz_parent.lattice.decon_processing.value, + # psf=self.llsz_parent.lattice.psf[channel], + # num_iter=self.llsz_parent.lattice.psf_num_iter + # ) + # else: + # decon_kwargs = dict() + + # crop_roi_vol_desk = cle.pull( + # crop_volume_deskew( + # original_volume=np.array(self.llsz_parent.lattice.data[time, channel, ...]), + # roi_shape=roi_choice, + # angle_in_degrees=self.llsz_parent.angle_value, + # voxel_size_x=self.llsz_parent.lattice.dx, + # voxel_size_y=self.llsz_parent.lattice.dy, + # voxel_size_z=self.llsz_parent.lattice.dz, + # deconvolution=self.llsz_parent.deconvolution, + # # Option for entering custom z start value? + # z_start=0, + # z_end=self.llsz_parent.lattice.deskew_vol_shape[0], + # skew_dir=self.llsz_parent.skew_dir, + # **decon_kwargs + # ).astype(self.llsz_parent.lattice.data.dtype) + # ) + + # # get array back from gpu or addding cle array to napari can throw errors + # image = next(self.llsz_parent.lattice.process()) + + # scale = ( + # self.llsz_parent.lattice.new_dz, + # self.llsz_parent.lattice.dy, + # self.llsz_parent.lattice.dx + # ) + # self.parent_viewer.add_image( + # crop_roi_vol_desk, scale=scale) + + # @magicclass(name="Crop and Save Data") + # class CropSaveData(LlszTemplate): + # @magicgui(header=dict(widget_type="Label", label="

Crop and Save Data

"), + # time_start=dict(label="Time Start:"), + # time_end=dict(label="Time End:", value=1), + # ch_start=dict(label="Channel Start:"), + # ch_end=dict(label="Channel End:", value=1), + # save_as_type={ + # "label": "Save as filetype:", "choices": SaveFileType}, + # save_path=dict(mode='d', label="Directory to save ")) + # def Crop_Save(self, + # header: str, + # time_start: int, + # time_end: int, + # ch_start: int, + # ch_end: int, + # save_as_type: SaveFileType, + # roi_layer_list: ShapesData, + # save_path: Path = Path(history.get_save_history()[0])): + + # if not roi_layer_list: + # logger.error( + # "No coordinates found or cropping. Initialise shapes layer and draw ROIs.") + # else: + # if not self.llsz_parent.open_file: + # raise Exception("Image not initialised") + + # check_dimensions(time_start, time_end, ch_start, ch_end, self.llsz_parent.lattice.channels, self.llsz_parent.lattice.time) + + # angle = self.llsz_parent.lattice.angle + # dx = self.llsz_parent.lattice.dx + # dy = self.llsz_parent.lattice.dy + # dz = self.llsz_parent.lattice.dz + + # # get image data + # img_data = self.llsz_parent.lattice.data + # # Get shape of deskewed image + # deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape + # deskewed_volume = da.zeros(deskewed_shape) + # z_start = 0 + # z_end = deskewed_shape[0] + + # logger.info("Cropping and saving files...") + + # # necessary when scale is used for napari.viewer.add_image operations + # roi_layer_list = ShapesData([x/self.llsz_parent.lattice.dy for x in roi_layer_list]) + + # for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): + # # pass arguments for save tiff, callable and function arguments + # logger.info("Processing ROI ", idx) + # # pass parameters for the crop_volume_deskew function + + # save_img(vol=img_data, + # func=crop_volume_deskew, + # time_start=time_start, + # time_end=time_end, + # channel_start=ch_start, + # channel_end=ch_end, + # save_name_prefix="ROI_" + str(idx), + # save_path=save_path, + # save_file_type=save_as_type, + # save_name=self.llsz_parent.lattice.save_name, + # dx=dx, + # dy=dy, + # dz=dz, + # angle=angle, + # deskewed_volume=deskewed_volume, + # roi_shape=roi_layer, + # angle_in_degrees=angle, + # z_start=z_start, + # z_end=z_end, + # voxel_size_x=dx, + # voxel_size_y=dy, + # voxel_size_z=dz, + # LLSZWidget=self.llsz_parent + # ) + + # logger.info( + # f"Cropping and Saving Complete -> {save_path}") + + # @magicclass(name="Workflow", widget_type="scrollable") + # class WorkflowWidget(LlszTemplate): + + # @magicclass(name="Preview Workflow", widget_type="scrollable") + # class PreviewWorkflow(LlszTemplate): + # #time_preview= field(int, options={"min": 0, "step": 1}, name="Time") + # #chan_preview = field(int, options={"min": 0, "step": 1}, name="Channels") + # @magicgui(header=dict(widget_type="Label", label="

Preview Workflow

"), + # time_preview=dict(label="Time:", max=2**20), + # chan_preview=dict(label="Channel:"), + # get_active_workflow=dict( + # widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), + # workflow_path=dict( + # mode='r', label="Load custom workflow (.yaml/yml)"), + # Use_Cropping=dict( + # widget_type="Checkbox", label="Crop Data", value=False), + # #custom_module=dict(widget_type="Checkbox",label="Load custom module (looks for *.py files in the workflow directory)",value = False), + # call_button="Apply and Preview Workflow") + # def Workflow_Preview(self, + # header: str, + # time_preview: int, + # chan_preview: int, + # get_active_workflow: bool, + # Use_Cropping: bool, + # roi_layer_list: ShapesData, + # workflow_path: Path = Path.home()): + # """ + # Apply napari_workflows to the processing pipeline + # User can define a pipeline which can be inspected in napari workflow inspector + # and then execute it by ticking the get active workflow checkbox, + # OR + # Use a predefined workflow + + # In both cases, if deskewing is not present as first step, it will be added on + # and rest of the task will be made followers + # """ + # logger.info("Previewing deskewed channel and time with workflow") + # user_workflow = get_workflow(self.parent_viewer if get_active_workflow else workflow_path) + + # # when using fields, self.time_preview.value + # if time_preview >= self.llsz_parent.lattice.time: + # raise ValueError("Time is out of range") + # if chan_preview >= self.llsz_parent.lattice.channels: + # raise ValueError("Channel is out of range") + + # time = time_preview + # channel = chan_preview + + # # to access current time and channel and pass it to workflow file + # config.channel = channel + # config.time = time + + # logger.info(f"Processing for Time: {time} and Channel: {channel}") + + # logger.info("Workflow to be executed:") + # logger.info(user_workflow) + # # Execute workflow + # processed_vol = user_workflow.get(task_name_last) + + # # check if a measurement table (usually a dictionary or list) or a tuple with different data types + # # The function below saves the tables and adds any images to napari window + # if type(processed_vol) in [dict, list, tuple]: + # if (len(processed_vol) > 1): + # df = pd.DataFrame() + # for idx, i in enumerate(processed_vol): + # df_temp = process_custom_workflow_output( + # i, parent_dir, idx, LLSZWidget, self, channel, time, preview=True) + # final_df = pd.concat([df, df_temp]) + # # append dataframes from every loop and have table command outside loop? + # # TODO: Figure out why table is not displaying + # from napari_spreadsheet import _widget + # table_viewer = _widget.TableViewerWidget( + # show=True) + # table_viewer.add_spreadsheet(final_df) + # # widgets.Table(value=final_df).show() + + # else: + # # add image to napari window + # # TODO: check if its an image napari supports? + # process_custom_workflow_output( + # processed_vol, parent_dir, 0, LLSZWidget, self, channel, time) + + # print("Workflow complete") + + # @magicgui(header=dict(widget_type="Label", label="

Apply Workflow and Save Output

"), + # time_start=dict(label="Time Start:", max=2**20), + # time_end=dict(label="Time End:", + # value=1, max=2**20), + # ch_start=dict(label="Channel Start:"), + # ch_end=dict(label="Channel End:", value=1), + # Use_Cropping=dict( + # widget_type="Checkbox", label="Crop Data", value=False), + # get_active_workflow=dict( + # widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), + # workflow_path=dict( + # mode='r', label="Load custom workflow (.yaml/yml)"), + # save_as_type={ + # "label": "Save as filetype:", "choices": SaveFileType}, + # save_path=dict( + # mode='d', label="Directory to save "), + # #custom_module=dict(widget_type="Checkbox",label="Load custom module (same dir as workflow)",value = False), + # call_button="Apply Workflow and Save Result") + # def Apply_Workflow_and_Save(self, + # header: str, + # time_start: int, + # time_end: int, + # ch_start: int, + # ch_end: int, + # Use_Cropping: bool, + # roi_layer_list: ShapesData, + # get_active_workflow: bool = False, + # workflow_path: Path = Path.home(), + # save_as_type: str = SaveFileType.tiff, + # save_path: Path = Path(history.get_save_history()[0])): + # """ + # Apply a user-defined analysis workflow using napari-workflows + + # Args: + # time_start (int): Start Time + # time_end (int): End Time + # ch_start (int): Start Channel + # ch_end (int): End Channel + # Use_Cropping (_type_): Use cropping based on ROIs in the shapes layer + # roi_layer_list (ShapesData): Shapes layer to use for cropping; can be a list of shapes + # get_active_workflow (bool, optional): Gets active workflow in napari. Defaults to False. + # workflow_path (Path, optional): User can also choose a custom workflow defined in a yaml file. + # save_path (Path, optional): Path to save resulting data + # """ + # if not self.llsz_parent.open_file: + # raise Exception("Image not initialised") + + # check_dimensions(time_start, time_end, ch_start, ch_end, + # self.llsz_parent.lattice.channels, self.llsz_parent.lattice.time) + + # # Get parameters + # angle = self.llsz_parent.lattice.angle + # dx = self.llsz_parent.lattice.dx + # dy = self.llsz_parent.lattice.dy + # dz = self.llsz_parent.lattice.dz + + # user_workflow = get_workflow(self.parent_viewer if get_active_workflow else workflow_path) - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - logger.info(f"{input_arg_first=}, {input_arg_last=}, {first_task_name=}, {last_task_name=}") - logger.info(f"Workflow loaded: {user_workflow}") - - vol = self.llsz_parent.lattice.data - task_name_start = first_task_name[0] - - try: - task_name_last = last_task_name[0] - except IndexError: - task_name_last = task_name_start - - # variables to hold task name, initialize it as None - # if gpu, set otf_path, otherwise use psf - psf = None - otf_path = None - - if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - #otf_path = "otf_path" - psf_arg = "psf" - psf = self.llsz_parent.lattice.psf - else: - psf_arg = "psf" - psf = self.llsz_parent.lattice.psf - # if cropping, set that as first task - - if Use_Cropping: - # convert Roi pixel coordinates to canvas coordinates - # necessary only when scale is used for napari.viewer.add_image operations - roi_layer_list = [x/self.llsz_parent.lattice.dy for x in roi_layer_list] - - deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - roi = "roi" - volume = "volume" - # Check if decon ticked, if so set as first and crop as second? - - # Create workflow for cropping and deskewing - # volume and roi used will be set dynamically - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=volume, - deskewed_volume=deskewed_volume, - roi_shape=roi, - angle_in_degrees=angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - z_start=z_start, - z_end=z_end, - deconvolution=self.llsz_parent.deconvolution.value, - decon_processing=self.llsz_parent.lattice.decon_processing, - psf=psf_arg, - skew_dir=self.llsz_parent.skew_dir) - - # change the first task so it accepts "crop_deskew as input" - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="crop_deskew_image", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - - for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): - print("Processing ROI ", idx) - user_workflow.set(roi, roi_layer) - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=volume, - first_task="crop_deskew_image", - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - #roi_layer = roi_layer, - save_name_prefix="ROI_" + \ - str(idx), - save_name=self.llsz_parent.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=self.llsz_parent.deconvolution.value, - decon_processing=self.llsz_parent.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - # IF just deskewing and its not in the tasks, add that as first task - elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): - input = "input" - # add task to the workflow - user_workflow.set("deskew_image", - self.llsz_parent.deskew_func, - input_image=input, - angle_in_degrees=angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - linear_interpolation=True) - # Set input of the workflow to be from deskewing - # change workflow task starts from is "deskew_image" and - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deskew_image", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if self.llsz_parent.deconvolution: - psf = "psf" - otf_path = "otf_path" - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=input, - psf=psf_arg, - dzdata=self.llsz_parent.lattice.dz, - dxdata=self.llsz_parent.lattice.dx, - dzpsf=self.llsz_parent.lattice.dz, - dxpsf=self.llsz_parent.lattice.dx, - num_iter=self.llsz_parent.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=input, - psf=psf_arg, - num_iter=self.llsz_parent.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # modify the user workflow so "deconvolution" is accepted - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - task_name_start = first_task_name[0] - - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=input, - first_task=task_name_start, - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - save_name=self.llsz_parent.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=self.llsz_parent.deconvolution, - decon_processing=self.llsz_parent.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - # If deskewing is already as a task, then set the first argument to input so we can modify that later - else: - # if deskewing is already first task, then check if deconvolution needed - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if self.llsz_parent.deconvolution: - psf = "psf" - otf_path = "otf_path" - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=input, - psf=psf_arg, - dzdata=self.llsz_parent.lattice.dz, - dxdata=self.llsz_parent.lattice.dx, - dzpsf=self.llsz_parent.lattice.dz, - dxpsf=self.llsz_parent.lattice.dx, - num_iter=self.llsz_parent.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=input, - psf=psf_arg, - num_iter=self.llsz_parent.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # modify the user workflow so "deconvolution" is accepted - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - task_name_start = first_task_name[0] - - # we pass first argument as input - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=input_arg_first, - first_task=task_name_start, - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - save_name=self.llsz_parent.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=self.llsz_parent.deconvolution, - decon_processing=self.llsz_parent.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - print("Workflow complete") - return + # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + # user_workflow) + # logger.info(f"{input_arg_first=}, {input_arg_last=}, {first_task_name=}, {last_task_name=}") + # logger.info(f"Workflow loaded: {user_workflow}") + + # vol = self.llsz_parent.lattice.data + # task_name_start = first_task_name[0] + + # try: + # task_name_last = last_task_name[0] + # except IndexError: + # task_name_last = task_name_start + + # # variables to hold task name, initialize it as None + # # if gpu, set otf_path, otherwise use psf + # psf = None + # otf_path = None + + # if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + # #otf_path = "otf_path" + # psf_arg = "psf" + # psf = self.llsz_parent.lattice.psf + # else: + # psf_arg = "psf" + # psf = self.llsz_parent.lattice.psf + # # if cropping, set that as first task + + # if Use_Cropping: + # # convert Roi pixel coordinates to canvas coordinates + # # necessary only when scale is used for napari.viewer.add_image operations + # roi_layer_list = [x/self.llsz_parent.lattice.dy for x in roi_layer_list] + + # deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape + # deskewed_volume = da.zeros(deskewed_shape) + # z_start = 0 + # z_end = deskewed_shape[0] + # roi = "roi" + # volume = "volume" + # # Check if decon ticked, if so set as first and crop as second? + + # # Create workflow for cropping and deskewing + # # volume and roi used will be set dynamically + # user_workflow.set("crop_deskew_image", crop_volume_deskew, + # original_volume=volume, + # deskewed_volume=deskewed_volume, + # roi_shape=roi, + # angle_in_degrees=angle, + # voxel_size_x=dx, + # voxel_size_y=dy, + # voxel_size_z=dz, + # z_start=z_start, + # z_end=z_end, + # deconvolution=self.llsz_parent.deconvolution.value, + # decon_processing=self.llsz_parent.lattice.decon_processing, + # psf=psf_arg, + # skew_dir=self.llsz_parent.skew_dir) + + # # change the first task so it accepts "crop_deskew as input" + # new_task = modify_workflow_task( + # old_arg=input_arg_first, task_key=task_name_start, new_arg="crop_deskew_image", workflow=user_workflow) + # user_workflow.set(task_name_start, new_task) + + # for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): + # print("Processing ROI ", idx) + # user_workflow.set(roi, roi_layer) + # save_img_workflow(vol=vol, + # workflow=user_workflow, + # input_arg=volume, + # first_task="crop_deskew_image", + # last_task=task_name_last, + # time_start=time_start, + # time_end=time_end, + # channel_start=ch_start, + # channel_end=ch_end, + # save_file_type=save_as_type, + # save_path=save_path, + # #roi_layer = roi_layer, + # save_name_prefix="ROI_" + \ + # str(idx), + # save_name=self.llsz_parent.lattice.save_name, + # dx=dx, + # dy=dy, + # dz=dz, + # angle=angle, + # deconvolution=self.llsz_parent.deconvolution.value, + # decon_processing=self.llsz_parent.lattice.decon_processing, + # otf_path=otf_path, + # psf_arg=psf_arg, + # psf=psf) + + # # IF just deskewing and its not in the tasks, add that as first task + # elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): + # input = "input" + # # add task to the workflow + # user_workflow.set("deskew_image", + # self.llsz_parent.deskew_func, + # input_image=input, + # angle_in_degrees=angle, + # voxel_size_x=dx, + # voxel_size_y=dy, + # voxel_size_z=dz, + # linear_interpolation=True) + # # Set input of the workflow to be from deskewing + # # change workflow task starts from is "deskew_image" and + # new_task = modify_workflow_task( + # old_arg=input_arg_first, task_key=task_name_start, new_arg="deskew_image", workflow=user_workflow) + # user_workflow.set(task_name_start, new_task) + + # # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + # if self.llsz_parent.deconvolution: + # psf = "psf" + # otf_path = "otf_path" + # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + # user_workflow) + + # if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + # user_workflow.set("deconvolution", + # pycuda_decon, + # image=input, + # psf=psf_arg, + # dzdata=self.llsz_parent.lattice.dz, + # dxdata=self.llsz_parent.lattice.dx, + # dzpsf=self.llsz_parent.lattice.dz, + # dxpsf=self.llsz_parent.lattice.dx, + # num_iter=self.llsz_parent.lattice.psf_num_iter) + # # user_workflow.set(input_arg_first,"deconvolution") + # else: + # user_workflow.set("deconvolution", + # skimage_decon, + # vol_zyx=input, + # psf=psf_arg, + # num_iter=self.llsz_parent.lattice.psf_num_iter, + # clip=False, + # filter_epsilon=0, + # boundary='nearest') + # # modify the user workflow so "deconvolution" is accepted + # new_task = modify_workflow_task( + # old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) + # user_workflow.set(task_name_start, new_task) + # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + # user_workflow) + # task_name_start = first_task_name[0] + + # save_img_workflow(vol=vol, + # workflow=user_workflow, + # input_arg=input, + # first_task=task_name_start, + # last_task=task_name_last, + # time_start=time_start, + # time_end=time_end, + # channel_start=ch_start, + # channel_end=ch_end, + # save_file_type=save_as_type, + # save_path=save_path, + # save_name=self.llsz_parent.lattice.save_name, + # dx=dx, + # dy=dy, + # dz=dz, + # angle=angle, + # deconvolution=self.llsz_parent.deconvolution, + # decon_processing=self.llsz_parent.lattice.decon_processing, + # otf_path=otf_path, + # psf_arg=psf_arg, + # psf=psf) + + # # If deskewing is already as a task, then set the first argument to input so we can modify that later + # else: + # # if deskewing is already first task, then check if deconvolution needed + # # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + # if self.llsz_parent.deconvolution: + # psf = "psf" + # otf_path = "otf_path" + # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + # user_workflow) + + # if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + # user_workflow.set("deconvolution", + # pycuda_decon, + # image=input, + # psf=psf_arg, + # dzdata=self.llsz_parent.lattice.dz, + # dxdata=self.llsz_parent.lattice.dx, + # dzpsf=self.llsz_parent.lattice.dz, + # dxpsf=self.llsz_parent.lattice.dx, + # num_iter=self.llsz_parent.lattice.psf_num_iter) + # # user_workflow.set(input_arg_first,"deconvolution") + # else: + # user_workflow.set("deconvolution", + # skimage_decon, + # vol_zyx=input, + # psf=psf_arg, + # num_iter=self.llsz_parent.lattice.psf_num_iter, + # clip=False, + # filter_epsilon=0, + # boundary='nearest') + # # modify the user workflow so "deconvolution" is accepted + # new_task = modify_workflow_task( + # old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) + # user_workflow.set(task_name_start, new_task) + # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( + # user_workflow) + # task_name_start = first_task_name[0] + + # # we pass first argument as input + # save_img_workflow(vol=vol, + # workflow=user_workflow, + # input_arg=input_arg_first, + # first_task=task_name_start, + # last_task=task_name_last, + # time_start=time_start, + # time_end=time_end, + # channel_start=ch_start, + # channel_end=ch_end, + # save_file_type=save_as_type, + # save_path=save_path, + # save_name=self.llsz_parent.lattice.save_name, + # dx=dx, + # dy=dy, + # dz=dz, + # angle=angle, + # deconvolution=self.llsz_parent.deconvolution, + # decon_processing=self.llsz_parent.lattice.decon_processing, + # otf_path=otf_path, + # psf_arg=psf_arg, + # psf=psf) + + # print("Workflow complete") + # return def _napari_lattice_widget_wrapper() -> LLSZWidget: From 2370be80b45f6fba8bf3151ecd827a010a7ed66a Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 22 Aug 2023 09:41:37 +1000 Subject: [PATCH 014/147] First pass of preview working in the GUI --- core/lls_core/lattice_data.py | 170 ++++---- plugin/napari_lattice/dock_widget.py | 593 +++++++++++++++++++-------- plugin/napari_lattice/reader.py | 6 +- 3 files changed, 520 insertions(+), 249 deletions(-) diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 06487f4..02c00d7 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -13,6 +13,8 @@ from pydantic_numpy import NDArray from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Union +from typing_extensions import Self +from pathlib import Path from aicsimageio.types import PhysicalPixelSizes import pyclesperanto_prototype as cle @@ -23,6 +25,7 @@ from lls_core.llsz_core import crop_volume_deskew from lls_core.utils import get_deskewed_shape from lls_core.types import ArrayLike +from napari_workflows import Workflow if TYPE_CHECKING: import pyclesperanto_prototype as cle @@ -47,6 +50,17 @@ class DefaultMixin(BaseModel): def get_default(cls, field_name: str): return cls.__fields__[field_name].get_default() +class ProcessedVolume(BaseModel, arbitrary_types_allowed=True): + """ + A slice of the image processing result + """ + time_index: NonNegativeInt + time: NonNegativeInt + channel_index: NonNegativeInt + channel: NonNegativeInt + data: ArrayLike + roi_index: Optional[NonNegativeInt] = None + class ProcessedSlices(BaseModel): #: Iterable of result slices. #: Note that this is a finite iterator that can only be iterated once @@ -96,17 +110,6 @@ def save_image(self): imagej=True ) -class ProcessedVolume(BaseModel, arbitrary_types_allowed=True): - """ - A slice of the image processing result - """ - time_index: NonNegativeInt - time: NonNegativeInt - channel_index: NonNegativeInt - channel: NonNegativeInt - data: ArrayLike - roi_index: Optional[NonNegativeInt] = None - def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] = None, channel: Optional[int] = None, time: Optional[int] = None) -> str: """ Generates a filename for this result @@ -131,7 +134,7 @@ class DefinedPixelSizes(DefaultMixin): Y: NonNegativeFloat = 0.14 Z: NonNegativeFloat = 0.3 -class DeconvolutionParams(BaseModel): +class DeconvolutionParams(BaseModel, arbitrary_types_allowed=True): """ Parameters for the optional deconvolution step """ @@ -139,17 +142,17 @@ class DeconvolutionParams(BaseModel): psf: List[NDArray] = [] psf_num_iter: NonNegativeInt = 10 # TODO: handle this - otf_path: List = [] + # otf_path: List = [] # Background value to subtract background: Union[float, Literal["auto", "second_last"]] = 0 -class CropParams(BaseModel): +class CropParams(BaseModel, arbitrary_types_allowed=True): """ Parameters for the optional cropping step """ roi_layer_list: ShapesData z_start: NonNegativeInt = 0 - z_end: int = 1 + z_end: NonNegativeInt = 1 class LatticeData(DefaultMixin, arbitrary_types_allowed=True): """ @@ -162,19 +165,10 @@ class LatticeData(DefaultMixin, arbitrary_types_allowed=True): #: Dimensions of `data` dims: Dimensions - #: The filename of this data when it is saved - save_name: str - #: Dimensions of the deskewed output - deskew_vol_shape: Tuple[int, ...] + deskew_vol_shape: Tuple[int, ...] = Field(init_var=False) - deskew_affine_transform: cle.AffineTransform3D - - #: The range of times to process - time_range: range - - #: The range of channels to process - channel_range: range + deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False) #: Geometry of the light path skew: DeskewDirection = DeskewDirection.Y @@ -183,15 +177,82 @@ class LatticeData(DefaultMixin, arbitrary_types_allowed=True): #: Pixel size in microns physical_pixel_sizes: DefinedPixelSizes = Field(default_factory=DefinedPixelSizes) - # If this is None, then deconvolution is disabled + #: If this is None, then deconvolution is disabled deconvolution: Optional[DeconvolutionParams] = None - # If this is None, then cropping is disabled + #: If this is None, then cropping is disabled crop: Optional[CropParams] = None + + #: If defined, this is a workflow to add lightsheet processing onto + workflow: Optional[Workflow] = None + + #: The directory where this data will be saved + save_dir: Path + + #: The file name to save this as, without the directory name or file extension + save_name: str + + #: The range of times to process + time_range: range + + #: The range of channels to process + channel_range: range #: The data type to save the result as save_type: SaveFileType = SaveFileType.h5 + @validator("time_range") + def default_time_range(cls, v: Any, values: dict) -> range: + """ + Sets the default time range if undefined + """ + if v is None: + return range(values["dims"].T + 1) + return v + + @validator("time_range") + def disjoint_time_range(cls, v: range, values: dict): + """ + Validates that the time range is within the range of channels in our array + """ + max_time = values["dims"].T + if v.start < 0: + raise ValueError("The lowest valid start value is 0") + if v.stop > max_time: + raise ValueError(f"The highest valid time value is the length of the time axis, which is {max_time}") + return v + + @validator("channel_range") + def default_channel_range(cls, v: Any, values: dict) -> range: + """ + Sets the default channel range if undefined + """ + if v is None: + return range(values["dims"].C + 1) + return v + + @validator("channel_range") + def disjoint_channel_range(cls, v: range, values: dict): + """ + Validates that the channel range is within the range of channels in our array + """ + max_channel = values["dims"].T + if v.start < 0: + raise ValueError("The lowest valid start value is 0") + if v.stop > max_channel: + raise ValueError(f"The highest valid channel value is the length of the channel axis, which is {max_channel}") + return v + + @validator("channel_range") + def channel_range_subset(cls, v: range, values: dict): + if min(v) < 0 or max(v) > values["dims"].C: + raise ValueError("The output channel range must be a subset of the total available channels") + + @validator("time_range") + def time_range_subset(cls, v: range, values: dict): + if min(v) < 0 or max(v) > values["dims"].T: + raise ValueError("The output time range must be a subset of the total available time points") + # Hack to ensure that .skew_dir behaves identically to .skew @property def skew_dir(self) -> DeskewDirection: @@ -264,7 +325,7 @@ def channels(self) -> int: """Number of channels""" return self.dims.C - @root_validator + @root_validator(pre=True) def set_deskew(cls, values: dict) -> dict: """ Sets the default deskew shape values if the user has not provided them @@ -273,53 +334,11 @@ def set_deskew(cls, values: dict) -> dict: if values.get('deskew_vol_shape') is None: if values.get('deskew_affine_transform') is None: # If neither has been set, calculate them ourselves - values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(values["data"], values["angle"], values["dx"], values["dy"], values["dz"]) + values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(values["data"], values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) else: raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") return values - @validator("time_range") - def default_time_range(cls, v: Any, values: dict) -> range: - """ - Sets the default time range if undefined - """ - if v is None: - return range(values["dims"].T + 1) - return v - - @validator("time_range") - def disjoint_time_range(cls, v: range, values: dict): - """ - Validates that the time range is within the range of channels in our array - """ - max_time = values["dims"].T - if v.start < 0: - raise ValueError("The lowest valid start value is 0") - if v.stop > max_time: - raise ValueError(f"The highest valid time value is the length of the time axis, which is {max_time}") - return v - - @validator("channel_range") - def default_channel_range(cls, v: Any, values: dict) -> range: - """ - Sets the default channel range if undefined - """ - if v is None: - return range(values["dims"].C + 1) - return v - - @validator("channel_range") - def disjoint_channel_range(cls, v: range, values: dict): - """ - Validates that the channel range is within the range of channels in our array - """ - max_channel = values["dims"].T - if v.start < 0: - raise ValueError("The lowest valid start value is 0") - if v.stop > max_channel: - raise ValueError(f"The highest valid channel value is the length of the channel axis, which is {max_channel}") - return v - @property def new_dz(self): return math.sin(self.angle * math.pi / 180.0) * self.dz @@ -425,9 +444,11 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: Yields processed image slices without cropping """ for time_idx, time, ch_idx, ch, data in self.iter_slices(): + if isinstance(data, DaskArray): + data = data.compute() if self.deconvolution is not None: if self.deconvolution.decon_processing == DeconvolutionChoice.cuda_gpu: - data = pycuda_decon( + data= pycuda_decon( image=data, psf=self.deconvolution.psf[ch], dzdata=self.dz, @@ -437,7 +458,7 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: num_iter=self.deconvolution.psf_num_iter ) else: - data = skimage_decon( + data= skimage_decon( vol_zyx=data, psf=self.deconvolution.psf[ch], num_iter=self.deconvolution.psf_num_iter, @@ -466,6 +487,7 @@ def process(self) -> ProcessedSlices: Execute the processing and return the result. This is the main public API for processing """ + ProcessedSlices.update_forward_refs() if self.cropping_enabled: return ProcessedSlices( lattice_data=self, diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index ce1e034..41882bf 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -5,14 +5,14 @@ from pathlib import Path import dask.array as da import pandas as pd -from typing import Any, Callable, Iterable, Literal, Optional, Sequence, Tuple, Union, List +from typing import Any, Callable, Iterable, Literal, Optional, Sequence, Tuple, Union, List, TypeVar, Type, cast from enum import Enum, auto from magicclass.wrappers import set_design -from magicgui import magicgui -from magicclass import magicclass, field, vfield, set_options, MagicTemplate, FieldGroup -from magicclass.fields import MagicValueField -from magicclass.widgets import CollapsibleContainer, CheckBox, RangeSlider +from magicgui.widgets import Widget +from magicclass import magicclass as original_magicclass, field, vfield, set_options, MagicTemplate, FieldGroup +from magicclass.fields import MagicValueField, MagicField +from magicclass.widgets import CollapsibleContainer, CheckBox, RangeSlider, ComboBox from magicclass.widgets.containers import ContainerWidget, _VCollapsibleContainer, wrap_container from magicclass.utils import click from qtpy.QtCore import Qt @@ -31,91 +31,126 @@ from napari_workflows import Workflow, WorkflowManager from napari_workflows._io_yaml_v1 import load_workflow -from lls_core import config, DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection -from lls_core.io import LatticeData, save_img, save_img_workflow -from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes -from lls_core.types import ArrayLike -from lls_core.workflow import get_first_last_image_and_task, modify_workflow_task, get_all_py_files, process_custom_workflow_output, load_custom_py_modules, import_workflow_modules, replace_first_arg -from lls_core.utils import check_dimensions, read_imagej_roi, as_type -from lls_core.llsz_core import crop_volume_deskew -from lls_core.deconvolution import read_psf, pycuda_decon, skimage_decon +from lls_core import DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection +from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes, LatticeData -from napari_lattice.ui_core import _Preview, _Deskew_Save from napari_lattice.reader import lattice_from_napari +from pydantic import ValidationError -from copy import copy +from strenum import StrEnum # Enable Logging import logging + +from lls_core.workflow import import_workflow_modules +from itertools import groupby logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -class WorkflowSource(Enum): - ActiveWorkflow = auto() - CustomPath = auto() +MagicClassType = TypeVar("MagicClassType") +def magicclass(*args, **kwargs) -> Callable[[MagicClassType], MagicClassType]: + return original_magicclass(*args, **kwargs) + +class WorkflowSource(StrEnum): + ActiveWorkflow = "Active Workflow" + CustomPath = "Custom Path" + +class BackgroundSource(StrEnum): + Auto = "Automatic" + SecondLast = "Second Last" + Custom = "Custom" + +def enable_field(parent: MagicTemplate, field: MagicField, enabled: bool = True) -> None: + """ + Enable the widget associated with a field + + Args: + parent: The parent magicclass that contains the field + field: The field to enable/disable + enabled: If False, disable the field instead of enabling it + """ + real_field = getattr(parent, field.name) + if not isinstance(real_field, Widget): + raise Exception("Define your fields with field() not vfield()!") + real_field.visible = enabled + real_field.enabled = enabled + +EnabledHandlerType = TypeVar("EnabledHandlerType") +def enable_if(fields: List[MagicField]): + """ + Makes an event handler that should be used via `fields_enabled.connect(make_enabled_handler())`. + Args: + condition: A function that takes an instance of the class and returns True if the fields should be enabled + fields: A list of fields to be dynamically enabled or disabled + Example: + :: + @some_field.connect + @enable_if( + [some_field] + ) + def _enable_fields(self) -> bool: + return some_field.value + """ + # Ideally we could use subclassing to add both the vfield and this event handler, but there + # seems to be a bug preventing this: https://github.com/hanjinliu/magic-class/issues/113. + + # Start by disabling all the fields + + def _decorator(fn: Callable[[EnabledHandlerType], bool])-> Callable[[EnabledHandlerType], None]: + for field in fields: + field.visible = False + field.enabled = False + + def make_handler(fn: Callable) -> Callable[[EnabledHandlerType], None]: + def handler(parent: EnabledHandlerType): + enable = fn(parent) + if enable: + logger.info(f"{parent.__class__} Activated") + else: + logger.info(f"{parent.__class__} Deactivated") + for field in fields: + enable_field(parent, field, enabled=enable) + return handler + + return make_handler(fn) + + return _decorator + +# class HideableContents(MagicTemplate): +# # fields_enabled = vfield(False, label="Enabled") + +# # @fields_enabled.connect +# def _enabled_changed(self) -> None: +# if self.fields_enabled: +# logger.info(f"{self.__class__} Activated") +# for child in self.__magicclass_children__: +# child.visible = True +# child.enabled = True +# else: +# logger.info(f"{self.__class__} Deactivated") +# for child in self.__magicclass_children__: +# child.visible = False +# child.enabled = False class LlszTemplate(MagicTemplate): @property def llsz_parent(self) -> "LLSZWidget": return self.find_ancestor(LLSZWidget) - @property - def parent_viewer(self) -> Viewer: - viewer = super().parent_viewer - if viewer is None: - raise Exception("This function can only be used when inside of a Napari viewer") - return super().parent_viewer - -@magicclass -class DeconvolutionWidget(MagicTemplate): - """ - A counterpart to the DeconvolutionParams Pydantic class - """ - decon_processing = vfield(DeconvolutionChoice, label="Processing Algorithm") - psf = vfield(List[Path], label = "PSFs") - psf_num_iter = vfield(int, label = "Number of Iterations") - -@magicclass -class CroppingWidget(LlszTemplate): - """ - A counterpart to the CropParams Pydantic class - """ - # roi_layer_list: ShapesData - shapes= vfield(ShapesData, label = "ROIs")#Optional[Shapes] = None - z_range = vfield(Tuple[int, int]).with_options( - label = "Z Range", - value = (0, 1), - options = dict( - min = 0, - max = 1 - ) - ) - _shapes_layer: Optional[Shapes] = None - - # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") - # @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) - def activate_cropping(self): - self._shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', - face_color=[1, 1, 1, 0], name="Cropping BBOX layer") - # TO select ROIs if needed - self._shapes_layer.mode = "SELECT" - self["activate_cropping"].text = "Cropping layer active" - self["activate_cropping"].background_color = "green" - - def _make_crop_params(self) -> CropParams: - return CropParams( - z_start=self.z_start, - z_end=self.z_end, - roi_layer_list=self.shapes - ) - -@magicclass -class WorkflowWidget(LlszTemplate): - """ - Handles the workflow related parameters - """ - workflow_source = vfield(WorkflowSource).with_options(label = "Workflow Source") - workflow_path = vfield(Path).with_options(label = "Workflow Path", visible=False) +def parent_viewer(mc: MagicTemplate) -> Viewer: + viewer = mc.parent_viewer + if viewer is None: + raise Exception("This function can only be used when inside of a Napari viewer") + return mc.parent_viewer + +T = TypeVar("T") +def get_child(parent: MagicTemplate, child_cls: Type[T]) -> T: + if isinstance(child_cls, MagicTemplate): + return child_cls + for child in parent.__magicclass_children__: + if isinstance(child, child_cls): + return child + raise Exception("Child couldn't be found!") class LastDimensionOptions(Enum): XYZTC = "XYZTC" @@ -125,10 +160,53 @@ class LastDimensionOptions(Enum): @magicclass(widget_type="split") class LLSZWidget(LlszTemplate): open_file: bool = False - lattice: LatticeData - deconv: DeconvolutionParams = DeconvolutionParams() shapes_layer: Shapes + def _check_validity(self) -> bool: + """ + Returns True if the model is valid + """ + try: + self._make_model() + return True + except: + return False + + def _make_model_friendly(self) -> LatticeData: + """ + Generates a LatticeData, but returns validation errors in a user friendly way + """ + try: + return self._make_model() + except ValidationError as e: + message = [f"

{e.model}

"] + for error in e.errors(): + header = ", ".join([str(it).capitalize() for it in error['loc']]) + message.append(f"

{header}

{error['msg']}") + raise Exception("\n".join(message)) + + def _make_model(self) -> LatticeData: + deskew = get_child(self, self.LlszMenu.WidgetContainer.DeskewWidget) + output = get_child(self, self.LlszMenu.WidgetContainer.OutputWidget) + deconv = get_child(self, self.LlszMenu.WidgetContainer.DeconvolutionWidget) + crop = get_child(self, self.LlszMenu.WidgetContainer.CroppingWidget) + workflow = get_child(self, self.LlszMenu.WidgetContainer.WorkflowWidget) + + return lattice_from_napari( + img=deskew.img_layer.value, + last_dimension=deskew.dimension_order.value, + angle=deskew.angle.value, + skew = deskew.skew_dir.value, + physical_pixel_sizes=deskew.pixel_sizes.value, + save_dir = output.save_path.value, + channel_range=range(output.channel_range.value[0], output.channel_range.value[1]), + time_range=range(output.time_range.value[0], output.time_range.value[1]), + save_type=output.save_type.value, + deconvolution = deconv._make_model(), + workflow = workflow._make_model(), + crop = crop._make_model() + ) + @magicclass(widget_type="split") class LlszMenu(LlszTemplate): @@ -137,107 +215,260 @@ class LlszMenu(LlszTemplate): # Pycudadecon library for deconvolution # options={"enabled": True}, - img_layer = vfield(Layer).with_options(label = "Image Layer") - pixel_sizes = vfield(Tuple[float, float, float]).with_options( - value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), - label="Pixel Sizes (XYZ)" - ) - angle: MagicValueField[float] = vfield(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") - device = vfield(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") - merge_all_channels = vfield(False).with_options(label="Merge all Channels") - dimension_order = vfield(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") - skew_dir = vfield(DeskewDirection.Y).with_options(label = "Skew Direction") - set_logging = vfield(Log_Levels.INFO).with_options(label="Logging Level") - time_range = vfield(Tuple[int, int]).with_options( - label="Time Export Range", - value=(0, 10), - options = dict( - min=0, - max=10, - ) - ) - channel_range = vfield(Tuple[int, int]).with_options( - label="Channel Range", - value=(0, 10), - options = dict( - min=0, - max=10, - ) - ) - save_type = vfield(SaveFileType).with_options( - label = "Save Format" - ) - save_path = vfield(Path).with_options( - label = "Save Directory", - value = Path(history.get_save_history()[0]) - ) + # Tabbed Widget container to house all the widgets + @magicclass(widget_type="tabbed", name="Functions") + class WidgetContainer(LlszTemplate): - ### - # Deconvolution - ### - deconvolution = vfield(bool, name="Use Deconvolution").with_options(value=False) - deconv_widget = DeconvolutionWidget() - deconv_widget.enabled = False - deconv_widget.visible = False - - @deconvolution.connect - def _set_decon(self) -> None: - if self.deconvolution: - logger.info("Deconvolution Activated") - # Enable deconvolutio by using the saved parameters - # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv - self.deconv_widget.enabled = True - self.deconv_widget.visible = True - else: - logger.info("Deconvolution Disabled") - # self.llsz_parent.lattice.deconvolution = None - self.deconv_widget.enabled = False - self.deconv_widget.visible = False + @magicclass(name="1. Deskew") + class DeskewWidget: + img_layer = field(List[Layer]).with_options(label = "Image Layer", layout="vertical", value=[]) + pixel_sizes = field(Tuple[float, float, float]).with_options( + value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), + label="Pixel Sizes (XYZ)" + ) + angle = field(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") + device = field(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") + merge_all_channels = field(False).with_options(label="Merge all Channels") + dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") + skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") + + @magicclass(name="2. Deconvolution") + class DeconvolutionWidget: + """ + A counterpart to the DeconvolutionParams Pydantic class + """ + fields_enabled = field(False, label="Enabled") + decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") + psf = field(List[Path], label = "PSFs") + psf_num_iter = field(int, label = "Number of Iterations") + background = field(ComboBox).with_choices( + [it.value for it in BackgroundSource] + ).with_options(label="Background") + background_custom = field(float).with_options( + visible=False, + label="Custom Background" + ) - ### - # Cropping - ### + # @background.connect + # def _show_custom_background(self): + # self.background_custom.visible = self.background == BackgroundSource.Custom + + @background.connect + @enable_if( + [background_custom] + ) + def _enable_custom_background(self) -> bool: + return self.background.value == BackgroundSource.Custom + + # @fields_enabled.connect + # def _enable_fields(self) -> bool: + # self.decon_processing.visible = self.fields_enabled + + @fields_enabled.connect + @enable_if( + fields = [ + decon_processing, + psf, + psf_num_iter, + background + ] + ) + def _enable_fields(self) -> bool: + return self.fields_enabled.value + + def _make_model(self) -> Optional[DeconvolutionParams]: + if not self.fields_enabled.value: + return None + return DeconvolutionParams( + decon_processing=self.decon_processing.value, + background=self.background.value + ) + + @magicclass(name = "3. Crop") + class CroppingWidget: + """ + A counterpart to the CropParams Pydantic class + """ + fields_enabled = field(False, label="Enabled") + shapes= field(ShapesData, label = "ROIs")#Optional[Shapes] = None + z_range = field(Tuple[int, int]).with_options( + label = "Z Range", + value = (0, 1), + options = dict( + min = 0, + max = 1 + ), + ) + + @fields_enabled.connect + @enable_if([shapes, z_range]) + def _enable_workflow(self) -> bool: + return self.fields_enabled.value + + # roi_layer_list: ShapesData + # @magicclass(visible=False) + # class Fields(MagicTemplate): + # shapes= vfield(ShapesData, label = "ROIs")#Optional[Shapes] = None + # z_range = vfield(Tuple[int, int]).with_options( + # label = "Z Range", + # value = (0, 1), + # options = dict( + # min = 0, + # max = 1 + # ), + # ) + # _shapes_layer: Optional[Shapes] = None + + # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") + # @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) + # @set_design(text="New Cropping Layer") + # def activate_cropping(self): + # self._shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', + # face_color=[1, 1, 1, 0], name="Cropping BBOX layer") + # # TO select ROIs if needed + # self._shapes_layer.mode = "SELECT" + # self["activate_cropping"].text = "Cropping layer active" + # self["activate_cropping"].background_color = "green" + + def _make_model(self) -> Optional[CropParams]: + child = get_child(self, CropFields) + return CropParams( + z_start=child.z_range[0], + z_end=child.z_range[1], + roi_layer_list=child.shapes + ) + + @magicclass(name="4. Workflow") + class WorkflowWidget: + """ + Handles the workflow related parameters + """ + fields_enabled = field(False, label="Enabled") + workflow_source = field(ComboBox).with_options(label = "Workflow Source").with_choices([it.value for it in WorkflowSource]) + workflow_path = field(Path).with_options(label = "Workflow Path", visible=False) + + @fields_enabled.connect + @enable_if([workflow_source]) + def _enable_workflow(self) -> bool: + return self.fields_enabled.value + + @fields_enabled.connect + @enable_if([workflow_path]) + def _workflow_path(self) -> bool: + return self.workflow_source.value == WorkflowSource.CustomPath + + def _make_model(self) -> Optional[Workflow]: + if not self.fields_enabled.value: + return None + child = get_child(self, WorkflowFields) + if child.workflow_source == WorkflowSource.ActiveWorkflow: + return WorkflowManager.install(self.parent_viewer).workflow + else: + import_workflow_modules(child.workflow_path) + return load_workflow(str(child.workflow_path)) + + @magicclass(name="5. Output") + class OutputWidget: + set_logging = field(Log_Levels.INFO).with_options(label="Logging Level") + time_range = field(Tuple[int, int]).with_options( + label="Time Export Range", + value=(0, 1), + options = dict( + min=0, + max=100, + ) + ) + channel_range = field(Tuple[int, int]).with_options( + label="Channel Range", + value=(0, 1), + options = dict( + min=0, + max=100, + ) + ) + save_type = field(SaveFileType).with_options( + label = "Save Format" + ) + save_path = field(Path).with_options( + label = "Save Directory", + value = Path(history.get_save_history()[0]) + ) + save_name = field(str).with_options( + label = "Save Prefix" + ) - cropping_enabled = vfield(bool, name="Use Cropping").with_options(value=False) - crop_widget = CroppingWidget() - crop_widget.enabled = False - crop_widget.visible = False - - @cropping_enabled.connect - def _set_crop(self) -> None: - if self.cropping_enabled: - logger.info("Cropping Activated") - # Enable deconvolutio by using the saved parameters - # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv - self.crop_widget.enabled = True - self.crop_widget.visible = True - else: - logger.info("Deconvolution Disabled") - # self.llsz_parent.lattice.deconvolution = None - self.crop_widget.enabled = False - self.crop_widget.visible = False + # @DeskewWidget.img_layer.connect + # def _img_changed(self): + # deskew = get_child(self, self.DeskewWidget) + # output = get_child(self, self.OutputWidget) + # output.channel_range.options["max"] = deskew.img_layer.value._dims_displayed ### - # Workflow + # Deconvolution ### - workflow_enabled = vfield(bool, name="Use Workflow").with_options(value=False) - workflow_widget = WorkflowWidget() - workflow_widget.enabled = False - workflow_widget.visible = False - - @workflow_enabled.connect - def _set_workflow(self) -> None: - if self.workflow_enabled: - logger.info("Workflow Activated") - # Enable deconvolutio by using the saved parameters - # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv - self.workflow_widget.enabled = True - self.workflow_widget.visible = True - else: - logger.info("Deconvolution Disabled") - # self.llsz_parent.lattice.deconvolution = None - self.workflow_widget.enabled = False - self.workflow_widget.visible = False + # deconvolution = vfield(bool, name="Use Deconvolution").with_options(value=False) + # deconv_widget = DeconvolutionWidget() + # deconv_widget.enabled = False + # deconv_widget.visible = False + + # @deconvolution.connect + # def _set_decon(self) -> None: + # if self.deconvolution: + # logger.info("Deconvolution Activated") + # # Enable deconvolutio by using the saved parameters + # # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv + # self.deconv_widget.enabled = True + # self.deconv_widget.visible = True + # else: + # logger.info("Deconvolution Disabled") + # # self.llsz_parent.lattice.deconvolution = None + # self.deconv_widget.enabled = False + # self.deconv_widget.visible = False + + # ### + # # Cropping + # ### + + # cropping_enabled = vfield(bool, name="Use Cropping").with_options(value=False) + # crop_widget = CroppingWidget() + # crop_widget.enabled = False + # crop_widget.visible = False + + # @cropping_enabled.connect + # def _set_crop(self) -> None: + # if self.cropping_enabled: + # logger.info("Cropping Activated") + # # Enable deconvolutio by using the saved parameters + # # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv + # self.crop_widget.enabled = True + # self.crop_widget.visible = True + # else: + # logger.info("Deconvolution Disabled") + # # self.llsz_parent.lattice.deconvolution = None + # self.crop_widget.enabled = False + # self.crop_widget.visible = False + + # ### + # # Workflow + # ### + # workflow_enabled = vfield(bool, name="Use Workflow").with_options(value=False) + # workflow_widget = WorkflowWidget() + # workflow_widget.enabled = False + # workflow_widget.visible = False + + # @workflow_enabled.connect + # def _set_workflow(self) -> None: + # if self.workflow_enabled: + # logger.info("Workflow Activated") + # # Enable deconvolutio by using the saved parameters + # # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv + # self.workflow_widget.enabled = True + # self.workflow_widget.visible = True + # else: + # logger.info("Deconvolution Disabled") + # # self.llsz_parent.lattice.deconvolution = None + # self.workflow_widget.enabled = False + # self.workflow_widget.visible = False # @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Initialize Plugin", max_height=75, font_size=13) # @set_options(pixel_size_dx={"widget_type": "FloatSpinBox", "value": 0.1449922, "step": 0.000000001}, @@ -388,11 +619,25 @@ def _set_workflow(self) -> None: ) @set_design(text="Preview") def preview(self, header:str, time: int, channel: int): - pass + # We only need to process one time point for the preview, + # so we made a copy using a subset of the times + lattice = self._make_model_friendly().copy(update=dict( + time_range = range(time, time+1), + channel_range = range(time, time+1), + )) + for slice in lattice.process().slices: + scale = ( + lattice.new_dz, + lattice.dy, + lattice.dx + ) + self.parent_viewer.add_image(slice.data, scale=scale) + max_z = np.argmax(np.sum(slice.data, axis=(1, 2))) + self.parent_viewer.dims.set_current_step(0, max_z) @set_design(text="Save") def save(self): - pass + self._make_model_friendly() # @magicclass(widget_type="collapsible") diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index 9f8775c..9dca5a8 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -7,6 +7,7 @@ , however pyramidal support for 3D not available yet """ from __future__ import annotations +from pathlib import Path import dask.array as da import dask.delayed as delayed @@ -14,7 +15,6 @@ import numpy as np from napari.layers import image, Layer from napari.layers._data_protocols import LayerDataProtocol -from aicsimageio.types import ArrayLike from aicsimageio.dimensions import Dimensions from aicsimageio.aics_image import AICSImage @@ -22,6 +22,7 @@ from typing import Any, Optional, cast, Tuple from lls_core.lattice_data import lattice_from_aics, LatticeData, img_from_array +from lls_core.types import ArrayLike def lattice_from_napari( img: Layer, @@ -35,6 +36,9 @@ def lattice_from_napari( kwargs: Extra arguments to pass to the LatticeData constructor """ + if not isinstance(img, Layer): + raise Exception("img must be a napari layer object") + img_data_aics: AICSImage if 'aicsimage' in img.metadata.keys(): From 1e4162748fe7546aad0cd07f6b5ccdb4aa7e3bc1 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 1 Sep 2023 19:07:17 +1000 Subject: [PATCH 015/147] Implemented traffic light system for first two field sets --- core/lls_core/lattice_data.py | 80 ++++--- plugin/napari_lattice/disableable.py | 34 +++ plugin/napari_lattice/dock_widget.py | 346 ++++++--------------------- plugin/napari_lattice/fields.py | 288 ++++++++++++++++++++++ plugin/napari_lattice/green.png | Bin 0 -> 12406 bytes plugin/napari_lattice/grey.png | Bin 0 -> 8880 bytes plugin/napari_lattice/icons.py | 5 + plugin/napari_lattice/red.png | Bin 0 -> 10457 bytes 8 files changed, 440 insertions(+), 313 deletions(-) create mode 100644 plugin/napari_lattice/disableable.py create mode 100644 plugin/napari_lattice/fields.py create mode 100644 plugin/napari_lattice/green.png create mode 100644 plugin/napari_lattice/grey.png create mode 100644 plugin/napari_lattice/icons.py create mode 100644 plugin/napari_lattice/red.png diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 02c00d7..54768ae 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -154,11 +154,41 @@ class CropParams(BaseModel, arbitrary_types_allowed=True): z_start: NonNegativeInt = 0 z_end: NonNegativeInt = 1 -class LatticeData(DefaultMixin, arbitrary_types_allowed=True): - """ - Holds data and metadata for a given image in a consistent format - """ +class OutputParams(DefaultMixin, arbitrary_types_allowed=True): + #: The directory where this data will be saved + save_dir: Path + #: The file name to save this as, without the directory name or file extension + save_name: str + + #: The range of times to process + time_range: range + + #: The range of channels to process + channel_range: range + + #: The data type to save the result as + save_type: SaveFileType = SaveFileType.h5 + + @validator("time_range") + def default_time_range(cls, v: Any, values: dict) -> range: + """ + Sets the default time range if undefined + """ + if v is None: + return range(values["dims"].T + 1) + return v + + @validator("channel_range") + def default_channel_range(cls, v: Any, values: dict) -> range: + """ + Sets the default channel range if undefined + """ + if v is None: + return range(values["dims"].C + 1) + return v + +class DeskewParams(DefaultMixin, arbitrary_types_allowed=True): #: A 3-5D array containing the image data data: ArrayLike @@ -177,6 +207,15 @@ class LatticeData(DefaultMixin, arbitrary_types_allowed=True): #: Pixel size in microns physical_pixel_sizes: DefinedPixelSizes = Field(default_factory=DefinedPixelSizes) + +class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): + """ + Holds data and metadata for a given image in a consistent format + """ + + # Note: originally the save-related fields were included via composition and not inheritance + # (similar to how `crop` and `workflow` are handled), but this was impractical for implementing validations + #: If this is None, then deconvolution is disabled deconvolution: Optional[DeconvolutionParams] = None @@ -186,30 +225,6 @@ class LatticeData(DefaultMixin, arbitrary_types_allowed=True): #: If defined, this is a workflow to add lightsheet processing onto workflow: Optional[Workflow] = None - #: The directory where this data will be saved - save_dir: Path - - #: The file name to save this as, without the directory name or file extension - save_name: str - - #: The range of times to process - time_range: range - - #: The range of channels to process - channel_range: range - - #: The data type to save the result as - save_type: SaveFileType = SaveFileType.h5 - - @validator("time_range") - def default_time_range(cls, v: Any, values: dict) -> range: - """ - Sets the default time range if undefined - """ - if v is None: - return range(values["dims"].T + 1) - return v - @validator("time_range") def disjoint_time_range(cls, v: range, values: dict): """ @@ -222,15 +237,6 @@ def disjoint_time_range(cls, v: range, values: dict): raise ValueError(f"The highest valid time value is the length of the time axis, which is {max_time}") return v - @validator("channel_range") - def default_channel_range(cls, v: Any, values: dict) -> range: - """ - Sets the default channel range if undefined - """ - if v is None: - return range(values["dims"].C + 1) - return v - @validator("channel_range") def disjoint_channel_range(cls, v: range, values: dict): """ diff --git a/plugin/napari_lattice/disableable.py b/plugin/napari_lattice/disableable.py new file mode 100644 index 0000000..fbd3e9f --- /dev/null +++ b/plugin/napari_lattice/disableable.py @@ -0,0 +1,34 @@ +from tkinter import BaseWidget +from qtpy import QtWidgets as QtW, QtCore +from qtpy.QtCore import Qt, QEvent, QSize +from magicgui.application import use_app +from magicgui.widgets import Widget +from magicgui.widgets._concrete import _LabeledWidget +from magicgui.backends._qtpy.widgets import ( + QBaseWidget, + Container as ContainerBase, + MainWindow as MainWindowBase, +) + +class _Disableable(ContainerBase): + def __init__(self, layout="vertical", scrollable: bool = False, **kwargs): + BaseWidget.__init__(self, QtW.QWidget) + + if layout == "horizontal": + self._layout: QtW.QLayout = QtW.QHBoxLayout() + else: + self._layout = QtW.QVBoxLayout() + + self._stacked_widget = QtW.QStackedWidget(self._qwidget) + self._stacked_widget.setContentsMargins(0, 0, 0, 0) + self._inner_qwidget = QtW.QWidget(self._qwidget) + self._qwidget.setLayout(self._layout) + self._layout.addWidget(self._stacked_widget) + self._layout.addWidget(self._inner_qwidget) + + def _mgui_insert_widget(self, position: int, widget: Widget): + self._stacked_widget.insertWidget(position, widget.native) + + def _mgui_remove_widget(self, widget: Widget): + self._stacked_widget.removeWidget(widget.native) + widget.native.setParent(None) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 41882bf..8beb1a4 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -10,21 +10,19 @@ from magicclass.wrappers import set_design from magicgui.widgets import Widget -from magicclass import magicclass as original_magicclass, field, vfield, set_options, MagicTemplate, FieldGroup +from magicclass import magicclass, field, vfield, set_options, MagicTemplate, FieldGroup, Icon from magicclass.fields import MagicValueField, MagicField from magicclass.widgets import CollapsibleContainer, CheckBox, RangeSlider, ComboBox from magicclass.widgets.containers import ContainerWidget, _VCollapsibleContainer, wrap_container from magicclass.utils import click from qtpy.QtCore import Qt +from qtpy.QtGui import QIcon +from qtpy.QtWidgets import QTabWidget from napari.layers import Layer, Shapes from napari.types import ImageData -from napari.utils import history from napari import Viewer - -import pyclesperanto_prototype as cle - -from napari.types import ImageData, ShapesData +from napari_lattice.icons import RED, GREEN, GREY from tqdm import tqdm @@ -34,88 +32,26 @@ from lls_core import DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes, LatticeData -from napari_lattice.reader import lattice_from_napari from pydantic import ValidationError +from napari_lattice.reader import lattice_from_napari +from napari_lattice.fields import DeskewFields, CroppingFields, DeconvolutionFields, WorkflowFields, OutputFields +from napari_lattice.icons import GREY from strenum import StrEnum +from lls_core.workflow import import_workflow_modules # Enable Logging import logging -from lls_core.workflow import import_workflow_modules -from itertools import groupby logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -MagicClassType = TypeVar("MagicClassType") -def magicclass(*args, **kwargs) -> Callable[[MagicClassType], MagicClassType]: - return original_magicclass(*args, **kwargs) - -class WorkflowSource(StrEnum): - ActiveWorkflow = "Active Workflow" - CustomPath = "Custom Path" - -class BackgroundSource(StrEnum): - Auto = "Automatic" - SecondLast = "Second Last" - Custom = "Custom" - -def enable_field(parent: MagicTemplate, field: MagicField, enabled: bool = True) -> None: - """ - Enable the widget associated with a field - - Args: - parent: The parent magicclass that contains the field - field: The field to enable/disable - enabled: If False, disable the field instead of enabling it - """ - real_field = getattr(parent, field.name) - if not isinstance(real_field, Widget): - raise Exception("Define your fields with field() not vfield()!") - real_field.visible = enabled - real_field.enabled = enabled - -EnabledHandlerType = TypeVar("EnabledHandlerType") -def enable_if(fields: List[MagicField]): - """ - Makes an event handler that should be used via `fields_enabled.connect(make_enabled_handler())`. - Args: - condition: A function that takes an instance of the class and returns True if the fields should be enabled - fields: A list of fields to be dynamically enabled or disabled - Example: - :: - @some_field.connect - @enable_if( - [some_field] - ) - def _enable_fields(self) -> bool: - return some_field.value - """ - # Ideally we could use subclassing to add both the vfield and this event handler, but there - # seems to be a bug preventing this: https://github.com/hanjinliu/magic-class/issues/113. - - # Start by disabling all the fields - - def _decorator(fn: Callable[[EnabledHandlerType], bool])-> Callable[[EnabledHandlerType], None]: - for field in fields: - field.visible = False - field.enabled = False - - def make_handler(fn: Callable) -> Callable[[EnabledHandlerType], None]: - def handler(parent: EnabledHandlerType): - enable = fn(parent) - if enable: - logger.info(f"{parent.__class__} Activated") - else: - logger.info(f"{parent.__class__} Deactivated") - for field in fields: - enable_field(parent, field, enabled=enable) - return handler - - return make_handler(fn) - - return _decorator +# MagicClassType = TypeVar("MagicClassType") +# def magicclass(*args, **kwargs) -> Callable[[MagicClassType], MagicClassType]: +# return original_magicclass(*args, **kwargs) +def strikeout(text: str) -> str: + return '\u0336'.join(text) + '\u0336' # class HideableContents(MagicTemplate): # # fields_enabled = vfield(False, label="Enabled") @@ -132,6 +68,14 @@ def handler(parent: EnabledHandlerType): # child.visible = False # child.enabled = False +def validate_tab(parent: MagicTemplate, fields: FieldGroup, index: int): + tab_widget: QTabWidget = parent._widget._tab_widget + try: + fields._make_model() + tab_widget.setTabIcon(index, QIcon(GREEN)) + except ValidationError: + tab_widget.setTabIcon(index, QIcon(RED)) + class LlszTemplate(MagicTemplate): @property def llsz_parent(self) -> "LLSZWidget": @@ -142,20 +86,7 @@ def parent_viewer(mc: MagicTemplate) -> Viewer: if viewer is None: raise Exception("This function can only be used when inside of a Napari viewer") return mc.parent_viewer - -T = TypeVar("T") -def get_child(parent: MagicTemplate, child_cls: Type[T]) -> T: - if isinstance(child_cls, MagicTemplate): - return child_cls - for child in parent.__magicclass_children__: - if isinstance(child, child_cls): - return child - raise Exception("Child couldn't be found!") - -class LastDimensionOptions(Enum): - XYZTC = "XYZTC" - XYZCT = "XYZCT" - Metadata = "Get from Metadata" + @magicclass(widget_type="split") class LLSZWidget(LlszTemplate): @@ -186,11 +117,11 @@ def _make_model_friendly(self) -> LatticeData: raise Exception("\n".join(message)) def _make_model(self) -> LatticeData: - deskew = get_child(self, self.LlszMenu.WidgetContainer.DeskewWidget) - output = get_child(self, self.LlszMenu.WidgetContainer.OutputWidget) - deconv = get_child(self, self.LlszMenu.WidgetContainer.DeconvolutionWidget) - crop = get_child(self, self.LlszMenu.WidgetContainer.CroppingWidget) - workflow = get_child(self, self.LlszMenu.WidgetContainer.WorkflowWidget) + deskew = self.LlszMenu.WidgetContainer.DeskewWidget + output = self.LlszMenu.WidgetContainer.OutputWidget + deconv = self.LlszMenu.WidgetContainer.DeconvolutionWidget + crop = self.LlszMenu.WidgetContainer.CroppingWidget + workflow = self.LlszMenu.WidgetContainer.WorkflowWidget return lattice_from_napari( img=deskew.img_layer.value, @@ -216,189 +147,52 @@ class LlszMenu(LlszTemplate): # options={"enabled": True}, # Tabbed Widget container to house all the widgets - @magicclass(widget_type="tabbed", name="Functions") + @magicclass(widget_type="tabbed", name="Functions", labels=False) class WidgetContainer(LlszTemplate): - @magicclass(name="1. Deskew") - class DeskewWidget: - img_layer = field(List[Layer]).with_options(label = "Image Layer", layout="vertical", value=[]) - pixel_sizes = field(Tuple[float, float, float]).with_options( - value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), - label="Pixel Sizes (XYZ)" - ) - angle = field(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") - device = field(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") - merge_all_channels = field(False).with_options(label="Merge all Channels") - dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") - skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") - - @magicclass(name="2. Deconvolution") - class DeconvolutionWidget: - """ - A counterpart to the DeconvolutionParams Pydantic class - """ - fields_enabled = field(False, label="Enabled") - decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") - psf = field(List[Path], label = "PSFs") - psf_num_iter = field(int, label = "Number of Iterations") - background = field(ComboBox).with_choices( - [it.value for it in BackgroundSource] - ).with_options(label="Background") - background_custom = field(float).with_options( - visible=False, - label="Custom Background" - ) - - # @background.connect - # def _show_custom_background(self): - # self.background_custom.visible = self.background == BackgroundSource.Custom - - @background.connect - @enable_if( - [background_custom] - ) - def _enable_custom_background(self) -> bool: - return self.background.value == BackgroundSource.Custom - - # @fields_enabled.connect - # def _enable_fields(self) -> bool: - # self.decon_processing.visible = self.fields_enabled - - @fields_enabled.connect - @enable_if( - fields = [ - decon_processing, - psf, - psf_num_iter, - background - ] - ) - def _enable_fields(self) -> bool: - return self.fields_enabled.value - - def _make_model(self) -> Optional[DeconvolutionParams]: - if not self.fields_enabled.value: - return None - return DeconvolutionParams( - decon_processing=self.decon_processing.value, - background=self.background.value - ) - - @magicclass(name = "3. Crop") - class CroppingWidget: - """ - A counterpart to the CropParams Pydantic class - """ - fields_enabled = field(False, label="Enabled") - shapes= field(ShapesData, label = "ROIs")#Optional[Shapes] = None - z_range = field(Tuple[int, int]).with_options( - label = "Z Range", - value = (0, 1), - options = dict( - min = 0, - max = 1 - ), - ) - - @fields_enabled.connect - @enable_if([shapes, z_range]) - def _enable_workflow(self) -> bool: - return self.fields_enabled.value - - # roi_layer_list: ShapesData - # @magicclass(visible=False) - # class Fields(MagicTemplate): - # shapes= vfield(ShapesData, label = "ROIs")#Optional[Shapes] = None - # z_range = vfield(Tuple[int, int]).with_options( - # label = "Z Range", - # value = (0, 1), - # options = dict( - # min = 0, - # max = 1 - # ), - # ) - # _shapes_layer: Optional[Shapes] = None - - # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") - # @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) - # @set_design(text="New Cropping Layer") - # def activate_cropping(self): - # self._shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', - # face_color=[1, 1, 1, 0], name="Cropping BBOX layer") - # # TO select ROIs if needed - # self._shapes_layer.mode = "SELECT" - # self["activate_cropping"].text = "Cropping layer active" - # self["activate_cropping"].background_color = "green" - - def _make_model(self) -> Optional[CropParams]: - child = get_child(self, CropFields) - return CropParams( - z_start=child.z_range[0], - z_end=child.z_range[1], - roi_layer_list=child.shapes - ) - - @magicclass(name="4. Workflow") - class WorkflowWidget: - """ - Handles the workflow related parameters - """ - fields_enabled = field(False, label="Enabled") - workflow_source = field(ComboBox).with_options(label = "Workflow Source").with_choices([it.value for it in WorkflowSource]) - workflow_path = field(Path).with_options(label = "Workflow Path", visible=False) - - @fields_enabled.connect - @enable_if([workflow_source]) - def _enable_workflow(self) -> bool: - return self.fields_enabled.value - - @fields_enabled.connect - @enable_if([workflow_path]) - def _workflow_path(self) -> bool: - return self.workflow_source.value == WorkflowSource.CustomPath - - def _make_model(self) -> Optional[Workflow]: - if not self.fields_enabled.value: - return None - child = get_child(self, WorkflowFields) - if child.workflow_source == WorkflowSource.ActiveWorkflow: - return WorkflowManager.install(self.parent_viewer).workflow - else: - import_workflow_modules(child.workflow_path) - return load_workflow(str(child.workflow_path)) - - @magicclass(name="5. Output") - class OutputWidget: - set_logging = field(Log_Levels.INFO).with_options(label="Logging Level") - time_range = field(Tuple[int, int]).with_options( - label="Time Export Range", - value=(0, 1), - options = dict( - min=0, - max=100, - ) - ) - channel_range = field(Tuple[int, int]).with_options( - label="Channel Range", - value=(0, 1), - options = dict( - min=0, - max=100, - ) - ) - save_type = field(SaveFileType).with_options( - label = "Save Format" - ) - save_path = field(Path).with_options( - label = "Save Directory", - value = Path(history.get_save_history()[0]) - ) - save_name = field(str).with_options( - label = "Save Prefix" - ) - + def __post_init__(self): + tab_widget: QTabWidget= self._widget._tab_widget + for i in range(5): + tab_widget.setTabIcon(i, QIcon(GREY)) + + deskew_fields = DeskewFields(name = "1. Deskew") + @deskew_fields.connect + def _deskew_changed(self): + validate_tab(self, self.deskew_fields, 0) + + deconv_fields = DeconvolutionFields(name = "2. Deconvolution") + @deconv_fields.connect + def _deconv_changed(self): + validate_tab(self, self.deconv_fields, 1) + + cropping_fields = CroppingFields(name = "3. Crop") + @cropping_fields.connect + def _cropping_changed(self): + validate_tab(self, 2) + + workflow_fields = WorkflowFields(name = "4. Workflow") + @workflow_fields.connect + def _workflow_changed(self): + validate_tab(self, 3) + + output_fields = OutputFields(name = "5. Output") + @output_fields.connect + def _output_changed(self): + validate_tab(self, 4) + + # @magicclass(name="1. Deskew") + # class DeskewWidget(MagicTemplate): + # img_layer = field(List[Layer]).with_options(label = "Image Layer", layout="vertical", value=[]) + # pixel_sizes = field(Tuple[float, float, float]).with_options( + # value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), + # label="Pixel Sizes (XYZ)" + # ) + # angle = field(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") + # device = field(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") + # merge_all_channels = field(False).with_options(label="Merge all Channels") + # dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") + # skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") - # @DeskewWidget.img_layer.connect # def _img_changed(self): # deskew = get_child(self, self.DeskewWidget) # output = get_child(self, self.OutputWidget) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py new file mode 100644 index 0000000..e6d6489 --- /dev/null +++ b/plugin/napari_lattice/fields.py @@ -0,0 +1,288 @@ +from pathlib import Path +from magicclass import FieldGroup, field, MagicTemplate +from magicclass.widgets import Widget, ComboBox +from magicclass.fields import MagicField +from typing import Callable, List, Optional, Tuple, TypeVar + +from strenum import StrEnum +from lls_core import DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection +from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes, LatticeData, OutputParams, DeskewParams +from napari.layers import Layer +from enum import Enum +import pyclesperanto_prototype as cle +from napari_workflows import Workflow, WorkflowManager +from napari.types import ImageData, ShapesData +from napari.utils import history +from abc import ABC + +from napari_lattice.icons import RED, GREEN, GREY + +# FieldGroups that the users interact with to input data + +import logging + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + + +class WorkflowSource(StrEnum): + ActiveWorkflow = "Active Workflow" + CustomPath = "Custom Path" + +class BackgroundSource(StrEnum): + Auto = "Automatic" + SecondLast = "Second Last" + Custom = "Custom" + + +def enable_field(parent: MagicTemplate, field: MagicField, enabled: bool = True) -> None: + """ + Enable the widget associated with a field + + Args: + parent: The parent magicclass that contains the field + field: The field to enable/disable + enabled: If False, disable the field instead of enabling it + """ + real_field = getattr(parent, field.name) + if not isinstance(real_field, Widget): + raise Exception("Define your fields with field() not vfield()!") + real_field.visible = enabled + real_field.enabled = enabled + + +EnabledHandlerType = TypeVar("EnabledHandlerType") +def enable_if(fields: List[MagicField]): + """ + Makes an event handler that should be used via `fields_enabled.connect(make_enabled_handler())`. + Args: + condition: A function that takes an instance of the class and returns True if the fields should be enabled + fields: A list of fields to be dynamically enabled or disabled + Example: + :: + @some_field.connect + @enable_if( + [some_field] + ) + def _enable_fields(self) -> bool: + return some_field.value + """ + # Ideally we could use subclassing to add both the vfield and this event handler, but there + # seems to be a bug preventing this: https://github.com/hanjinliu/magic-class/issues/113. + + # Start by disabling all the fields + + def _decorator(fn: Callable[[EnabledHandlerType], bool])-> Callable[[EnabledHandlerType], None]: + for field in fields: + field.visible = False + field.enabled = False + + def make_handler(fn: Callable) -> Callable[[EnabledHandlerType], None]: + def handler(parent: EnabledHandlerType): + enable = fn(parent) + if enable: + logger.info(f"{parent.__class__} Activated") + else: + logger.info(f"{parent.__class__} Deactivated") + for field in fields: + enable_field(parent, field, enabled=enable) + return handler + + return make_handler(fn) + + return _decorator + + +class LastDimensionOptions(Enum): + XYZTC = "XYZTC" + XYZCT = "XYZCT" + Metadata = "Get from Metadata" + +# class NapariFields(FieldGroup, ABC): +# def __init__(self, layout: str = "vertical", labels: bool = False, name: str | None = None, **kwargs): +# super().__init__(layout, labels, name, **kwargs) + +class DeskewFields(FieldGroup): + img_layer = field(List[Layer]).with_options(label = "Image Layer", layout="vertical", value=[]) + pixel_sizes = field(Tuple[float, float, float]).with_options( + value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), + label="Pixel Sizes (XYZ)" + ) + angle = field(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") + device = field(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") + merge_all_channels = field(False).with_options(label="Merge all Channels") + dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") + skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") + + def _make_model(self) -> DeskewParams: + return DeskewParams( + img=self.img_layer.value, + last_dimension=self.dimension_order.value, + angle=self.angle.value, + skew = self.skew_dir.value, + physical_pixel_sizes=self.pixel_sizes.value, + ) + +class DeconvolutionFields(FieldGroup): + """ + A counterpart to the DeconvolutionParams Pydantic class + """ + fields_enabled = field(False, label="Enabled") + decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") + psf = field(List[Path], label = "PSFs") + psf_num_iter = field(int, label = "Number of Iterations") + background = field(ComboBox).with_choices( + [it.value for it in BackgroundSource] + ).with_options(label="Background") + background_custom = field(float).with_options( + visible=False, + label="Custom Background" + ) + + # @background.connect + # def _show_custom_background(self): + # self.background_custom.visible = self.background == BackgroundSource.Custom + + @background.connect + @enable_if( + [background_custom] + ) + def _enable_custom_background(self) -> bool: + return self.background.value == BackgroundSource.Custom + + # @fields_enabled.connect + # def _enable_fields(self) -> bool: + # self.decon_processing.visible = self.fields_enabled + + @fields_enabled.connect + @enable_if( + fields = [ + decon_processing, + psf, + psf_num_iter, + background + ] + ) + def _enable_fields(self) -> bool: + return self.fields_enabled.value + + def _make_model(self) -> Optional[DeconvolutionParams]: + if not self.fields_enabled.value: + return None + return DeconvolutionParams( + decon_processing=self.decon_processing.value, + background=self.background.value + ) + +class CroppingFields(FieldGroup): + """ + A counterpart to the CropParams Pydantic class + """ + fields_enabled = field(False, label="Enabled") + shapes= field(ShapesData, label = "ROIs")#Optional[Shapes] = None + z_range = field(Tuple[int, int]).with_options( + label = "Z Range", + value = (0, 1), + options = dict( + min = 0, + max = 1 + ), + ) + + @fields_enabled.connect + @enable_if([shapes, z_range]) + def _enable_workflow(self) -> bool: + return self.fields_enabled.value + + # roi_layer_list: ShapesData + # @magicclass(visible=False) + # class Fields(MagicTemplate): + # shapes= vfield(ShapesData, label = "ROIs")#Optional[Shapes] = None + # z_range = vfield(Tuple[int, int]).with_options( + # label = "Z Range", + # value = (0, 1), + # options = dict( + # min = 0, + # max = 1 + # ), + # ) + # _shapes_layer: Optional[Shapes] = None + + # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") + # @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) + # @set_design(text="New Cropping Layer") + # def activate_cropping(self): + # self._shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', + # face_color=[1, 1, 1, 0], name="Cropping BBOX layer") + # # TO select ROIs if needed + # self._shapes_layer.mode = "SELECT" + # self["activate_cropping"].text = "Cropping layer active" + # self["activate_cropping"].background_color = "green" + + def _make_model(self) -> Optional[CropParams]: + return CropParams( + z_start=self.z_range.value[0], + z_end=self.z_range.value[1], + roi_layer_list=self.shapes.value + ) + +class WorkflowFields(FieldGroup): + """ + Handles the workflow related parameters + """ + fields_enabled = field(False, label="Enabled") + workflow_source = field(ComboBox).with_options(label = "Workflow Source").with_choices([it.value for it in WorkflowSource]) + workflow_path = field(Path).with_options(label = "Workflow Path", visible=False) + + @fields_enabled.connect + @enable_if([workflow_source]) + def _enable_workflow(self) -> bool: + return self.fields_enabled.value + + @fields_enabled.connect + @enable_if([workflow_path]) + def _workflow_path(self) -> bool: + return self.workflow_source.value == WorkflowSource.CustomPath + + def _make_model(self) -> Optional[Workflow]: + if not self.fields_enabled.value: + return None + child = get_child(self, WorkflowFields) + if child.workflow_source == WorkflowSource.ActiveWorkflow: + return WorkflowManager.install(self.parent_viewer).workflow + else: + import_workflow_modules(child.workflow_path) + return load_workflow(str(child.workflow_path)) + +# @magicclass(name="5. Output") +class OutputFields(FieldGroup): + set_logging = field(Log_Levels.INFO).with_options(label="Logging Level") + time_range = field(Tuple[int, int]).with_options( + label="Time Export Range", + value=(0, 1), + options = dict( + min=0, + max=100, + ) + ) + channel_range = field(Tuple[int, int]).with_options( + label="Channel Range", + value=(0, 1), + options = dict( + min=0, + max=100, + ) + ) + save_type = field(SaveFileType).with_options( + label = "Save Format" + ) + save_path = field(Path).with_options( + label = "Save Directory", + value = Path(history.get_save_history()[0]) + ) + save_name = field(str).with_options( + label = "Save Prefix" + ) + + +# @DeskewWidget.img_layer.connect diff --git a/plugin/napari_lattice/green.png b/plugin/napari_lattice/green.png new file mode 100644 index 0000000000000000000000000000000000000000..8addaf353491501b5e8e13b2714f2be7cd8bdbdc GIT binary patch literal 12406 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 literal 0 HcmV?d00001 diff --git a/plugin/napari_lattice/grey.png b/plugin/napari_lattice/grey.png new file mode 100644 index 0000000000000000000000000000000000000000..d3218dbcc6ac35b7938b7ad34d53411fe492682e GIT binary patch literal 8880 zcma)iXIN9+wsk0>L^`2&6hROIgc@4t(yKI)9*P74Nu>9xh=9_I5Tq)-DpI8*NKpZi zjv`1e(gnWgd(L~#x#zj}`F`vqYmG7InsctPW}dy1hlcu^lpt0R005xWx}|1}f4BR6 zk&)nEmoj{1001D9o2doXLQfawi1HAzcS1QJMEpFw@HhY5w32x0?-JP0DV)(0Cz`OCtf8*kh~uZ@4y3rwdeNpKzd?e zehR#Q{KD{`e@{brx&KhG?h3pXdWPJpC^UjwQbbZjlvfeNEsu6`h8e4A{KFVOQs8yP zV!dDxh_A1&h%ZzGg?536$;!$?M8zTE;=*`_Fvi~#YwsuQiMjgQ#a}+u5Ew_an-|s% z<;nfqr@aH}K30L37msuQGcpgaznyzx{*gVt4iG`e)u4 zQ-3c6#2A4=-A6kj)O`@1*sFhPfnN=#ibmLD5o-8Lfmd8q94ag-D=aEzDh7o~O2H%~ zg~UWfMgJ!2p`6^D{r{UR0h17eNs8miuzzRc<>6$Hwg3O&|6Op{Z}wk7;idV1M)WTm zRTL71#s`Pb0s1G{e?eNRs)lHkvl|kRVT?6ZxV6+(rNm^Vq=dyq#Qt!9?;Fg}%@2XJ zP;>J@cw+DxQ{a`A`X8X>{{sG5+XR2K@C*Fq@b}Kbv^?xx5aw=9Sl7RO{cBzq;py@} zf#3Usy#HI^w>{nPJ1YKX{XaGq|HAMIii$%1Z5_-1#(p0fZuvh_g57e%U{PrQKU065 zBol=9U!%WoXizfojEzXUv>OboGx0SCe0`KwPxAH&r0{KUDklzRUpW(?v{wo3b zKZJkl1bDl@=I|W`->M*g_bUA0?{0_i#Onx+?`07}NIn37Aj}P~i2qUo03h(>{*w_Q zH6w7J5>z!rv!=Q_BZ|S{lU+f4)YD?IcUAAR<@TLF{qk#-eu_-3c;4Mx1q>y)gQx&M zc`EGg5Sfbyb;VbFM$Tup6;`m@O$$`~x8Dh_hnvFOV|oearkeawJ-^f8EM(cW=&^V`b@X{>N$0a-Ys2G- zv8(kl6QLuEaBxopXM)nsF9%uE1nMBNfOu==yhE3ITSk7I>`hKb$ z6nsB2KhsKouOGV(YsrPrvP;;g9;mdrw6CzwtNQ7+t$}=waH>n|Ord}{gv%lEyt4-W zJBi%umL&!N5V`vMBJe9x@&N$Y>a^69P5pAl@46&^g*_d5t~>4Be0h24F*6&8Dh_-z zjxIk7)8;mc_!4icCLl2O@Md!AZGduDs20YT$PQ3!Ap*!~HgNUW7Fgb2)MNRaTI08aahVu86ck6?oUX$0N{*rA;FA+K9qrcowg&5)UMPzNxK4It#( z4zEFnFmAOGS8LS$0>)LRyQOJ7ji_&e`)o%XVvmtv2+L zuRuh*&yi%@GK8RfHke_WvF6&0?v`Rt2uQWw;ch?&WvX}5^4L%#KuTb42RxrQwBatW zlHOoj4DfCDIywS~%$}2|zcv?8DIqiy$gPf>*8!`$vg3C60hkW(Q2b}5KE=SFyLMci zMF-pOk&#O*)5mQDJFd>QA-7wOe}KG>ikd^!Y7Xy647RI;r(FTJv_F`qPtPaoq!P{@ zg$^hf?)(Z)8DZ}%++oScbC9dZ%dHlx(*)S6(#8uZ6`e}}UpF_Mkpi|mmmmZ)Q4O~F zex0o9pMTv{rHx_*<>iKj@nmH7`7S9D_N68a*F0YLp)mtbHxg*w0k6;Fx??ngW$ZV_NI9{4Un#d z5Bhc@G{HskgnpuRffA_C7cw_e8-x7QC#9`go7)rNI_vE!ksK<+u!%_=TaxOp60e8n zSHKp}cQP~H*K8x5G1r`u#jE``R|2%_|8?{h|TBKEszEG*m+9*>$DT zFyp->?^{AIhF`f8?eW2kHJ;%nb5-FP)2nrM&b0^i{WEi{j(vsoT?s0P^*Sw!)YJr)8C`sXSc zeEhkiuy-Z`9NPb-7!`VDA(`w_A+Ea0FvW{U+alk>{JqZ@I*c5s0zP6%(NbVZ;#p7C zWhe0Kph&$>Hd9RJ3|>yos?6YMJ`<1F{V@9qkmlTob5V=8e2uSnv41YpM~T_sGKzLf zf_-`V?fK?2en9v69WcTCbMm}!WOU3c_n!k3;^i%ZguxX3Q&ob>yBUV+B;R)PD2CQ) zq$1COaSP_oQu(P$W{W(6SZXq>ZWI)ny)sqe_U4jNHYAzwyMGAjf~sh4B4Dndnq6ez z4AQkXaOU52@5JP_EKvV=AX+*tci{4FJJDUvj=%y^QHacQ#E2%ChifVHuqK2l!ZrqH zNs>RBo*#9O#!SR1fp3$0{VRnKaYTnm_R@g*G=zSgkR*q;l15_itdZuS35gNeXsiW^*|Xb zC#*et^L)LSF21-T^DTBxc$rIB!TqWazNKMfDNpG|tX>t6r7{?ad^M%24;Y0Jlik|t z_&RgWtYS;#2YL&Mq#a|sPTpOY`F55%-!*Z6h*s5R+@>OIq5Tr%e)+&*w?0T1Vx4bs zaobLzB1HI;iTjypJSa!SG^d0=Dea|$2H!nFcNGg4GFKkNz1&z`SCw$?1DPv)@SC9; z;o|AvsA*k>W-s@@ILwuS7ov|sD9SFuoI6y}yVeC3itF;_K9#h}Ov3v`+2(u?)RzsY zTAbv;Qlp;@hDl3hYIDiyO1~=kKGA)G;FxyK>%7ezrN=!$6eED`-fqq0m^}|89J@Tr zia%)p9Y@i*&c`O9zIFiy#k(k3i>wRA1I%g@?sE!;6RtF@NMCkZhT4 z3W51_X=Prla#%H_0%6xf>sj+%?LQ}KHxom zw@ce<#S=7vaXo*BwLqF&F(*lJyMFgB(CJ43GB)BRK}v+diX85mZru#DB1a-kmUxP0 zkcqUL?D0;5CGp6ndCg4onFoA~U~FY$%XZ|pL7oYq=7N!O+fw%{TG$3YRI>B%(xkkk zXkRIH8`4s>|1nN;HZg8LlSTOBZqjDGHCY<;y1;D3GuR>}Q$9CIoU8{wc#|lOYAb}H zx`FcRIoU#$xlqS!72y%8C6%F)8QFT^`eX$qjz>2DY~?j zL#TMmt0>jB8zubmhZo)Q@!-#VuY=GM8j|Sj6Q$^?cgB1fEgm^8q%_-SWAz zL{$${E$P)O`1DQJSadqaq;RXuo%6WSV|s{;5!^zF{^j5f@m^4Xa^H3< zf}koe!@sQ)^dX=yxLzqNqGXwKj5#qEdc@|Jp7qAyoz=T0b63kF*l?Y6T?Ks9 zxMcz6pgj2yYTV%gAEFHNO)|Q|6BM2KYiz@;&s(q?e$zIZf>uA8-v}TaoR^{!UK;y? zP%>uY!)a=Ih~2(%n8`N43ZCaAr&3M&g8pu=luv`W*L+L|q^q>bNW=D1F_T_apUq8B zE;gAll&5{lQEj3YOrp9twswzFlMttpET|nVwY0kiG^H5r0|=`EC?_#ZDwsswhvClF zPA*ZF0dk~ZnmF0juxF=*%PH=Y>CYIvzIt?gDmscR5%JdMY7p{HZG?bxB7L{hQ?9QcBuK9m{LlpukfYo!CUXH9g4dZ&#zj7|ug4L}Hluup| zMliU_HkH{0lp*iAz($l;jbYl2-J>;cS~$rabGYYey#9nZS3O znKmz%B&M=PRT+R)ox@4#%eaW5=MpYPrN&j}GS6T67PJ6sZz~@-fvE<`{eKcxDde7uh0Kq6{Gi%YKZm}JVw+L)v}66*>|?1c)DvgQcB z`mV$q$u1-kfuK`(bEOr&E?D?o04aQ9HE*)xN3efph0ne9w$%!&=wx@5rk5|g?0*#K zP`pU006b^M+@YN!J;11SY`b!EXOR zMvX=b%HpOm-rHJJSyRP7gXb(NWTi3WBhSU@jh*>HnSU#}zD;ofdB8(PSDEzd~R6`QK2!y$OuZLf$M}jQbE2Q_h-+O2r zY3KXuYOOh6u}S%GJvT+qlAK=LSVUp&gV+>e|44UHpMT`Zk!%@hZ(M(7!-bhl z(R1-YldFb^8^k{p#84k(-bCdX+AkNz-OwK6#g99D{o=Vm(Br&u_nBa^BKnH2)XX`WpFp*V4sG|^6bJdF%PMul zO~R+F#!qdph{@6tAes`@8$@-vdB{Xo5LvO<+ zl8TaI>K^Wi9ZF<5|kg)X;F@5(N}XtoQj1wdtHnB;FN-G_F)uIAU0+rFL9uAY=18_WxEW)>v1`emdK^d-M-!m8HEZ6YwcXz zriG6%kFVYZmYnOnuaKXr%7q9&T#oQ2h(l)V4L7|IULD>LH^{ zK)LHqo8-Zsa!f7~HWQzp5K+voT>FeOz#Rvs%&YZ9V(`3;m7E?T>OlWBe1Y>N6Bd zE8NR`_RZtu_=)tIug-K+D96MDlYEs*1yuot!j*BpSp7ACIJE9-l;2b03hLB4J&uoD z0xl$0anB-GU0umi3#HRg=xnTX)zUU(X99B@osK#!t$= zWI`ZG0y5a|-*MKFnrfG;I-rl*q5W*RN7Q|4je!x$yksczy7^4EK~EXIBN#0TlFxeJ zt<-AYYQ*3i!_e@2Mq?d4GxixrxFES>O|rfddfEr1D+i=}hSpt}tf>SZey9KIi(Ot5+fC6YsRXZ`alzD8duD4S4LFeq=g0 z^F1>uq)r%=kgzqMvg>`}0aje2KQ>lgP%!rCJ}cM=Rm+AY?^Q;?vRGD3zY=S#Nv-h( z%I+dd5*=T9X&x)0@3f{FiMczqrX9*2DclKYo1pCyWYeJ)fvPta9MDHNnH46hbBt1) z(6nchb_0w-*>c`(4QnE+l4YF*#`SIh<10i~y8V3d>2!4%0^h<>qXaP+|F&jpdJ-C( zIEky6(EnOaq9epK^+I{uA&(ztIZglNQ$pZW&9FT4T>g`Dfp!De43P5Sq^@yvWx3m@ z*CS1vSHBmo-zLZu-*5RGomc)l8K6`H1JxM85B|+(WGA^;p?>%AYP*QXe=^#*ndFwUTG51PsV49 zk$t{5NfcUEX9_jJENk*b)1q6a8(yszLLZONA3-My2K)>;w*z}q%s&jDL zC-%jMoww5p;|w)YqL(_0Ym@vGUZ&_eYBohkIhSW~yXEktni7=`n#PzX7%aMXj9a|s zC=XPDE(o*!!eTU?fE$K(uJm6VyUo_;i1!XNQC{Ic~)eK=JGl3E?Oi#AAYKmh#N9={x z(H@~S@jZ902mOd-`VMi{XFL5SdhJ7Dn$H9+{XG6A$nk^XSkQ z7{SfA^at6;y(Gp879vCC0{5+^Aj)zx&a+pBF$pyTW|gY)8aob=!IEjZQGMY%NMHM7YvBQgT-vw82oQ>LYS4w7v#K*qifO=*F`Jnw=6TWmO+#WLqjsHPQh!|#Hz~U zMownV&`bn}66|qDX<*V^kK_G!1PGTtLm9Q#Amw)_{uJafB#uw%hvRyDsgcZ@1T)bG z?ZIu_(7>#fO#YB0mwqXpuv4{LWd3~fJknloxtTTn@~9eGaAi`Dc`-#Kt8nDB$qEfD1zy??cFg$wwBM zpRt=BBvTCy{u1f-=S~a&`1S5f%Ra>*K-4H!%_s_#VJ069cVE#Avc>i7Ia-qr5(kyU z3xZZFvMy(h^xRFX#EG1_D=i?Rg{Zkw)NHli52B?_wvmBjJ|H{3-j8IesgxnDdh4F~ zQSXpW7N=mQlABQ`3TVcsMEdy;#hweLMcc{v8~{v5a2-c`O81sYWnT%ZKXQ{-O3h!6 zPOt%WO+}C95ms__vSnz_-tnL6D)l>t0RbHndbTV6Hg__mFu&xxkSuIGTh9dU`U$$VktTUv*^SCnVwL zKvO&_fV4WgrXne;PfF^VnZFnCHUI~?x&pc$eMw^je-sMJ@FznA5$qtcikVg@fHqYC z-=oklPpV~gvL{s(`Nr&TB?z;y{bxLcVyVzf78mI#GK0L2ABVP&|5Kc&uB=379FUeijJG>ms`Fe{3CYF32qsu(B0epdkCYa3h;)Eq?i? zmUVQrZ69Toidg;k+j7Cuwou^n*%T|7S&u81~R?33>+cEgsHus!TBt%u?zUsg}0ifGIOTH3(F-P!W@*26^; zkI=!ImNYyXn!z;a{-6w}8ke27Ru!?^jA!hf_^0WwT6bp&I-~^vfMT~(@ewFd<;0fk zBkuekVRT7}0#g$Bdxt>qb;o?}h5B3+;AaszhTS}Gwst}mD{^esdzHDWOB=m_P>AiE ze_qXA`|QkOj^H>jtGH^OcMu=-=naB7XIiBP(q6po!#DMVT6y<(q$E3OdxQ=jz!Q5d zeu`OqXZ&Jbrr;RGM%iz5k~5ZgA&0ZSB=GQ~a)-%*bxM}^Ig%+_@(6w%RCJQIsS?0% zcZOrd({l838t8|4L!x=H!{aTDds~%7m8vyr&j{fwOz(FAt94fJJ?eP>OIN2nA4M?$ z^sPh9CV~2~uQkc{b{3!d;B-QOT~=|d9W08gH;e-`GfLDbAX={)*z|={omK~2>d>fj z#TMsBGJ3@(J?@~v4YEhAU{d7+b|J49Qh!LJCzIumBjLq~V^ z!^8tO6kC=ChAPM8!$2Qixhh!>kgus3D&__f>}9`|N^f@Cgs&H^lhEMAI0VDv{NO_T z$(uAsZs(y>)B&mXoCf=M-w)O&=8ewP8_f&F9BvT|zYcjUA&Yx;q`CJH{p{0=&ZJi? zy39!j9$MYLIIbx}O^5~ClvqIx2gh4@TRV9^svLaXa{fi?mu(LvQv&40uXQAV38t{8OkB5Zg$Y(i9o{1w&5bL`+GrjZQTGw5-OfH=uv)i|IYZvw zzcX!M0?1CEoC$%~1huWMXmdaKct&?ATd)As;atrpbB!AlxmC-9;~KB9gpg8tVOfbF z&C~;a7iYF^x!LLdKNN9fmn0srd3Nfwx%SVQNd^1H-`po*NzUz==c``Ij8jyumY(mi zo0JDR+ivZ;1jzgcY(GC1zNh?oO7eE^5gLx&Sm za6l7z=?Pzi%h@u>?h&)md`B#ytWf=A&Wh|034l=W1TTBEjSlrhz?;UJ$5)v@m-Vzw z&wmKXCam}OH{qIBFGhWy@W?2w3)l( zFFtww%P;q=AObz|FFh80-d1eyk8~E7B{vgIJ4*cnhqR!Mw<8kHdHBx1PG1t4g4lp! UF+D23|7fD6uCG?9Vi)oM0N#rN(f|Me literal 0 HcmV?d00001 diff --git a/plugin/napari_lattice/icons.py b/plugin/napari_lattice/icons.py new file mode 100644 index 0000000..75b82fb --- /dev/null +++ b/plugin/napari_lattice/icons.py @@ -0,0 +1,5 @@ +import pkg_resources + +GREEN = pkg_resources.resource_filename(__name__, "green.png") +GREY = pkg_resources.resource_filename(__name__, "grey.png") +RED = pkg_resources.resource_filename(__name__, "red.png") diff --git a/plugin/napari_lattice/red.png b/plugin/napari_lattice/red.png new file mode 100644 index 0000000000000000000000000000000000000000..1ca32a4d2da939e14f7e48e7eb3bda266c8b1276 GIT binary patch literal 10457 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 R{=Si@rlh4{{b>0cWnRw literal 0 HcmV?d00001 From d43bce7ce64dbce1ff2f32463839ae87f9813200 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 4 Sep 2023 18:36:31 +1000 Subject: [PATCH 016/147] Refactor field validation into mixin --- core/lls_core/cmds/__main__.py | 9 ++++-- core/lls_core/lattice_data.py | 44 ++++++++++++++------------- plugin/napari_lattice/dock_widget.py | 17 ++--------- plugin/napari_lattice/fields.py | 45 ++++++++++++++++++++++++---- plugin/napari_lattice/reader.py | 13 ++++---- plugin/pyproject.toml | 2 +- 6 files changed, 83 insertions(+), 47 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 66ae8d7..76e41a3 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -9,7 +9,7 @@ import sys import re from lls_core.io import save_img, save_img_workflow -from lls_core.lattice_data import lattice_from_aics +from lls_core.lattice_data import lattice_params_from_aics, LatticeData from lls_core.utils import read_imagej_roi, get_all_py_files, get_first_last_image_and_task, modify_workflow_task, check_dimensions from lls_core.llsz_core import crop_volume_deskew from aicsimageio import AICSImage @@ -363,7 +363,12 @@ def main(argv: Sequence[str] = sys.argv[1:]): print(f"Scene {scene} not valid") # Initialize Lattice class - lattice = lattice_from_aics(aics_img, angle=deskew_angle, skew=skew_dir, save_name=save_name, physical_pixel_sizes=PhysicalPixelSizes(dx, dy, dz)) + lattice = LatticeData( + **lattice_params_from_aics(aics_img, physical_pixel_sizes=PhysicalPixelSizes(dx, dy, dz)), + angle=deskew_angle, + skew=skew_dir, + save_name=save_name + ) if time_start is None or time_end is None: time_start, time_end = 0, lattice.time - 1 diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 54768ae..b4b64d5 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -13,7 +13,7 @@ from pydantic_numpy import NDArray from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Union -from typing_extensions import Self +from typing_extensions import TypedDict from pathlib import Path from aicsimageio.types import PhysicalPixelSizes @@ -207,6 +207,20 @@ class DeskewParams(DefaultMixin, arbitrary_types_allowed=True): #: Pixel size in microns physical_pixel_sizes: DefinedPixelSizes = Field(default_factory=DefinedPixelSizes) + @root_validator(pre=True) + def set_deskew(cls, values: dict) -> dict: + """ + Sets the default deskew shape values if the user has not provided them + """ + # process the file to get shape of final deskewed image + if values.get('deskew_vol_shape') is None: + if values.get('deskew_affine_transform') is None: + # If neither has been set, calculate them ourselves + values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(values["data"], values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) + else: + raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") + return values + class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): """ @@ -330,20 +344,6 @@ def time(self) -> int: def channels(self) -> int: """Number of channels""" return self.dims.C - - @root_validator(pre=True) - def set_deskew(cls, values: dict) -> dict: - """ - Sets the default deskew shape values if the user has not provided them - """ - # process the file to get shape of final deskewed image - if values.get('deskew_vol_shape') is None: - if values.get('deskew_affine_transform') is None: - # If neither has been set, calculate them ourselves - values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(values["data"], values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) - else: - raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") - return values @property def new_dz(self): @@ -505,7 +505,12 @@ def process(self) -> ProcessedSlices: slices=self._process_non_crop() ) -def lattice_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None), **kwargs: Any) -> LatticeData: +class AicsLatticeParams(TypedDict): + data: DaskArray + dims: Dimensions + physical_pixel_sizes: DefinedPixelSizes + +def lattice_params_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None)) -> AicsLatticeParams: # Note: The reason we copy all of these fields rather than just storing the AICSImage is because that class is mostly immutable and so not suitable pixel_sizes = DefinedPixelSizes( @@ -514,11 +519,10 @@ def lattice_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = Z = physical_pixel_sizes[2] or img.physical_pixel_sizes.Z or LatticeData.physical_pixel_sizes.Z ) - return LatticeData( + return AicsLatticeParams( data = img.dask_data, dims = img.dims, physical_pixel_sizes = pixel_sizes, - **kwargs ) def img_from_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> AICSImage: @@ -561,7 +565,7 @@ def img_from_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", " arr = np.swapaxes(arr, 0, 1) return AICSImage(image=arr, dim_order=dim_order, **kwargs) -def lattice_fom_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> LatticeData: +def lattice_fom_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> AicsLatticeParams: """ Creates a `LatticeData` from an array @@ -570,4 +574,4 @@ def lattice_fom_array(arr: ArrayLike, last_dimension: Optional[Literal["channel" last_dimension: See img_from_array """ aics = img_from_array(arr, last_dimension) - return lattice_from_aics(aics, **kwargs) + return lattice_params_from_aics(aics, **kwargs) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 8beb1a4..51f64c3 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -33,7 +33,7 @@ from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes, LatticeData from pydantic import ValidationError -from napari_lattice.reader import lattice_from_napari +from napari_lattice.reader import lattice_params_from_napari from napari_lattice.fields import DeskewFields, CroppingFields, DeconvolutionFields, WorkflowFields, OutputFields from napari_lattice.icons import GREY @@ -68,6 +68,7 @@ def strikeout(text: str) -> str: # child.visible = False # child.enabled = False + def validate_tab(parent: MagicTemplate, fields: FieldGroup, index: int): tab_widget: QTabWidget = parent._widget._tab_widget try: @@ -103,19 +104,6 @@ def _check_validity(self) -> bool: except: return False - def _make_model_friendly(self) -> LatticeData: - """ - Generates a LatticeData, but returns validation errors in a user friendly way - """ - try: - return self._make_model() - except ValidationError as e: - message = [f"

{e.model}

"] - for error in e.errors(): - header = ", ".join([str(it).capitalize() for it in error['loc']]) - message.append(f"

{header}

{error['msg']}") - raise Exception("\n".join(message)) - def _make_model(self) -> LatticeData: deskew = self.LlszMenu.WidgetContainer.DeskewWidget output = self.LlszMenu.WidgetContainer.OutputWidget @@ -123,6 +111,7 @@ def _make_model(self) -> LatticeData: crop = self.LlszMenu.WidgetContainer.CroppingWidget workflow = self.LlszMenu.WidgetContainer.WorkflowWidget + # TODO: fix return lattice_from_napari( img=deskew.img_layer.value, last_dimension=deskew.dimension_order.value, diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index e6d6489..8b53ee7 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -1,8 +1,9 @@ from pathlib import Path from magicclass import FieldGroup, field, MagicTemplate -from magicclass.widgets import Widget, ComboBox +from magicclass.widgets import Widget, ComboBox, Label from magicclass.fields import MagicField from typing import Callable, List, Optional, Tuple, TypeVar +from pydantic import BaseModel, ValidationError from strenum import StrEnum from lls_core import DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection @@ -16,6 +17,7 @@ from abc import ABC from napari_lattice.icons import RED, GREEN, GREY +from napari_lattice.reader import lattice_params_from_napari # FieldGroups that the users interact with to input data @@ -24,6 +26,22 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +def _get_friendly_validations(model: FieldGroup) -> str: + """ + Generates a LatticeData, but returns validation errors in a user friendly way + """ + try: + model._make_model() + return "" + except ValidationError as e: + message = [] + # message = [f"

{e.model}

"] + for error in e.errors(): + header = ", ".join([str(it).capitalize() for it in error['loc']]) + message.append(f"
  • {header} {error['msg']}
  • ") + joined = '\n'.join(message) + return f"
      {joined}
    " + class WorkflowSource(StrEnum): ActiveWorkflow = "Active Workflow" @@ -102,7 +120,19 @@ class LastDimensionOptions(Enum): # def __init__(self, layout: str = "vertical", labels: bool = False, name: str | None = None, **kwargs): # super().__init__(layout, labels, name, **kwargs) -class DeskewFields(FieldGroup): +class NapariFieldGroup: + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.changed.connect(self._validate) + + def _validate(self): + self.errors.value = _get_friendly_validations(self) + + def _make_model(self) -> BaseModel: + raise NotImplementedError() + + +class DeskewFields(NapariFieldGroup, FieldGroup): img_layer = field(List[Layer]).with_options(label = "Image Layer", layout="vertical", value=[]) pixel_sizes = field(Tuple[float, float, float]).with_options( value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), @@ -113,14 +143,17 @@ class DeskewFields(FieldGroup): merge_all_channels = field(False).with_options(label="Merge all Channels") dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") + errors = field(Label).with_options(label="Errors") def _make_model(self) -> DeskewParams: return DeskewParams( - img=self.img_layer.value, - last_dimension=self.dimension_order.value, + **lattice_params_from_napari( + img=self.img_layer.value, + last_dimension=self.dimension_order.value, + physical_pixel_sizes=self.pixel_sizes.value, + ), angle=self.angle.value, skew = self.skew_dir.value, - physical_pixel_sizes=self.pixel_sizes.value, ) class DeconvolutionFields(FieldGroup): @@ -188,12 +221,14 @@ class CroppingFields(FieldGroup): max = 1 ), ) + errors = field(Label) @fields_enabled.connect @enable_if([shapes, z_range]) def _enable_workflow(self) -> bool: return self.fields_enabled.value + # roi_layer_list: ShapesData # @magicclass(visible=False) # class Fields(MagicTemplate): diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index 9dca5a8..1d37956 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -21,14 +21,17 @@ from typing_extensions import Literal from typing import Any, Optional, cast, Tuple -from lls_core.lattice_data import lattice_from_aics, LatticeData, img_from_array +from lls_core.lattice_data import lattice_params_from_aics, img_from_array, AicsLatticeParams, PhysicalPixelSizes from lls_core.types import ArrayLike -def lattice_from_napari( +class NapariImageParams(AicsLatticeParams): + save_name: str + +def lattice_params_from_napari( img: Layer, last_dimension: Optional[Literal["channel", "time"]], - **kwargs: Any -) -> LatticeData: + physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None) +) -> NapariImageParams: """ Factory function for generating a LatticeData from a Napari Image @@ -62,7 +65,7 @@ def lattice_from_napari( # replace any group of spaces with "_" save_name = '_'.join(save_name.split()) - return lattice_from_aics(img_data_aics, save_name=save_name, **kwargs) + return NapariImageParams(save_name=save_name, **lattice_params_from_aics(img_data_aics, physical_pixel_sizes=physical_pixel_sizes)) def napari_get_reader(path: list[str] | str): """Check if file ends with h5 and returns reader function if true diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index 2b80c70..ba29fff 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -41,7 +41,7 @@ dependencies = [ "dask-image", "dask[distributed]", # We need this Python 3.8 fix: https://github.com/hanjinliu/magic-class/pull/108 - "magic-class>=0.7.4", + "magic-class>=0.7.5", "magicgui", "pyopencl", "read-roi", From 73f248edcbced209218c8252ee76d2b252cb303b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 5 Sep 2023 13:40:05 +1000 Subject: [PATCH 017/147] Metaclass attempt --- core/lls_core/lattice_data.py | 2 +- plugin/napari_lattice/dock_widget.py | 40 ++++++------ plugin/napari_lattice/fields.py | 92 ++++++++++++++++++++++++---- plugin/napari_lattice/reader.py | 2 +- 4 files changed, 101 insertions(+), 35 deletions(-) diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index b4b64d5..600b6e5 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -544,7 +544,7 @@ def img_from_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", " dim_order="ZYX" else: if last_dimension not in ["channel", "time"]: - raise ValueError("last_dimension must be either channel or time") + raise ValueError("last_dimension must be either channel or time when convertin") if len(arr.shape) == 4: if last_dimension == "channel": dim_order = "CZYX" diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 51f64c3..8a1836b 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -105,11 +105,11 @@ def _check_validity(self) -> bool: return False def _make_model(self) -> LatticeData: - deskew = self.LlszMenu.WidgetContainer.DeskewWidget - output = self.LlszMenu.WidgetContainer.OutputWidget - deconv = self.LlszMenu.WidgetContainer.DeconvolutionWidget - crop = self.LlszMenu.WidgetContainer.CroppingWidget - workflow = self.LlszMenu.WidgetContainer.WorkflowWidget + # deskew = self.LlszMenu.WidgetContainer.DeskewWidget + # output = self.LlszMenu.WidgetContainer.OutputWidget + # deconv = self.LlszMenu.WidgetContainer.DeconvolutionWidget + # crop = self.LlszMenu.WidgetContainer.CroppingWidget + # workflow = self.LlszMenu.WidgetContainer.WorkflowWidget # TODO: fix return lattice_from_napari( @@ -145,29 +145,29 @@ def __post_init__(self): tab_widget.setTabIcon(i, QIcon(GREY)) deskew_fields = DeskewFields(name = "1. Deskew") - @deskew_fields.connect - def _deskew_changed(self): - validate_tab(self, self.deskew_fields, 0) + # @deskew_fields.connect + # def _deskew_changed(self): + # validate_tab(self, self.deskew_fields, 0) deconv_fields = DeconvolutionFields(name = "2. Deconvolution") - @deconv_fields.connect - def _deconv_changed(self): - validate_tab(self, self.deconv_fields, 1) + # @deconv_fields.connect + # def _deconv_changed(self): + # validate_tab(self, self.deconv_fields, 1) cropping_fields = CroppingFields(name = "3. Crop") - @cropping_fields.connect - def _cropping_changed(self): - validate_tab(self, 2) + # @cropping_fields.connect + # def _cropping_changed(self): + # validate_tab(self, 2) workflow_fields = WorkflowFields(name = "4. Workflow") - @workflow_fields.connect - def _workflow_changed(self): - validate_tab(self, 3) + # @workflow_fields.connect + # def _workflow_changed(self): + # validate_tab(self, 3) output_fields = OutputFields(name = "5. Output") - @output_fields.connect - def _output_changed(self): - validate_tab(self, 4) + # @output_fields.connect + # def _output_changed(self): + # validate_tab(self, 4) # @magicclass(name="1. Deskew") # class DeskewWidget(MagicTemplate): diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 8b53ee7..e4d1e48 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -2,7 +2,8 @@ from magicclass import FieldGroup, field, MagicTemplate from magicclass.widgets import Widget, ComboBox, Label from magicclass.fields import MagicField -from typing import Callable, List, Optional, Tuple, TypeVar +from typing import Callable, List, Optional, Protocol, Tuple, TypeVar, Union, cast +from typing_extensions import Protocol, Self from pydantic import BaseModel, ValidationError from strenum import StrEnum @@ -15,9 +16,13 @@ from napari.types import ImageData, ShapesData from napari.utils import history from abc import ABC +from qtpy.QtWidgets import QTabWidget from napari_lattice.icons import RED, GREEN, GREY from napari_lattice.reader import lattice_params_from_napari +from napari_lattice.utils import get_viewer + +from napari.layers import Image # FieldGroups that the users interact with to input data @@ -41,6 +46,8 @@ def _get_friendly_validations(model: FieldGroup) -> str: message.append(f"
  • {header} {error['msg']}
  • ") joined = '\n'.join(message) return f"
      {joined}
    " + except Exception as e: + return str(e) class WorkflowSource(StrEnum): @@ -120,35 +127,90 @@ class LastDimensionOptions(Enum): # def __init__(self, layout: str = "vertical", labels: bool = False, name: str | None = None, **kwargs): # super().__init__(layout, labels, name, **kwargs) -class NapariFieldGroup: +class NapariFieldGroupCompatible(Protocol): + errors: MagicField[Label] + +class FieldGroupMeta(type(Protocol), type(FieldGroup)): + pass + +class NapariFieldGroup(NapariFieldGroupCompatible, Protocol): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self = cast(FieldGroup, self) self.changed.connect(self._validate) - def _validate(self): + def _get_parent_tab_widget(self: Union[FieldGroup, Self]) -> QTabWidget: + from qtpy.QtWidgets import QWidget + parent = cast(QWidget, self.parent) + return parent.parentWidget() + + def _get_tab_index(self: Union[FieldGroup, Self]) -> int: + from magicclass.widgets import Container + widget = cast(Container, self._widget) + return self._get_parent_tab_widget().indexOf(widget._qwidget) + + def _set_valid(self, valid: bool): + from qtpy.QtGui import QIcon + tab_parent = self._get_parent_tab_widget() + index = self._get_tab_index() + + if valid: + tab_parent.setTabIcon(index, QIcon(GREEN)) + else: + tab_parent.setTabIcon(index, QIcon(RED)) + + def _validate(self: Union[FieldGroup, Self]): self.errors.value = _get_friendly_validations(self) + self._set_valid(False if self.errors.value else True) def _make_model(self) -> BaseModel: raise NotImplementedError() -class DeskewFields(NapariFieldGroup, FieldGroup): - img_layer = field(List[Layer]).with_options(label = "Image Layer", layout="vertical", value=[]) +class DeskewFields(FieldGroup, NapariFieldGroup, metaclass=FieldGroupMeta): + + def _get_dimension_options(self, _) -> List[str]: + """ + Returns the list of dimension order options that might be possible for the current image stack + """ + default = ["Get from Metadata"] + try: + merged = self._merge_layers() + except Exception: + return default + ndims = len(merged._dims_order) + if ndims == 3: + return ["ZYX"] + default + elif ndims == 4: + return ["TZYX", "CZYX"] + default + elif ndims == 5: + return ["TCZYX", "CTZYX"] + default + else: + raise Exception("Only 3-5 dimensional arrays are supported") + + img_layer = field(List[Image], widget_type='Select').with_options(label = "Image Layer").with_choices(get_viewer().layers) pixel_sizes = field(Tuple[float, float, float]).with_options( value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), label="Pixel Sizes (XYZ)" ) angle = field(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") device = field(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") - merge_all_channels = field(False).with_options(label="Merge all Channels") - dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") + # merge_all_channels = field(False).with_options(label="Merge all Channels") + dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices(_get_dimension_options).with_options(label="Dimension Order") skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") errors = field(Label).with_options(label="Errors") + def _merge_layers(self) -> Image: + """ + Returns a single image merged from all the selected layers + """ + from napari.layers.utils.stack_utils import images_to_stack + return images_to_stack(self.img_layer.value) + def _make_model(self) -> DeskewParams: return DeskewParams( **lattice_params_from_napari( - img=self.img_layer.value, + img=self._merge_layers(), last_dimension=self.dimension_order.value, physical_pixel_sizes=self.pixel_sizes.value, ), @@ -156,7 +218,7 @@ def _make_model(self) -> DeskewParams: skew = self.skew_dir.value, ) -class DeconvolutionFields(FieldGroup): +class DeconvolutionFields(NapariFieldGroup, FieldGroup, metaclass=FieldGroupMeta): """ A counterpart to the DeconvolutionParams Pydantic class """ @@ -171,6 +233,8 @@ class DeconvolutionFields(FieldGroup): visible=False, label="Custom Background" ) + errors = field(Label).with_options(label="Errors") + # @background.connect # def _show_custom_background(self): @@ -207,7 +271,7 @@ def _make_model(self) -> Optional[DeconvolutionParams]: background=self.background.value ) -class CroppingFields(FieldGroup): +class CroppingFields(FieldGroup, NapariFieldGroup, metaclass=FieldGroupMeta): """ A counterpart to the CropParams Pydantic class """ @@ -221,7 +285,7 @@ class CroppingFields(FieldGroup): max = 1 ), ) - errors = field(Label) + errors = field(Label).with_options(label="Errors") @fields_enabled.connect @enable_if([shapes, z_range]) @@ -261,13 +325,14 @@ def _make_model(self) -> Optional[CropParams]: roi_layer_list=self.shapes.value ) -class WorkflowFields(FieldGroup): +class WorkflowFields(FieldGroup, NapariFieldGroup, metaclass=FieldGroupMeta): """ Handles the workflow related parameters """ fields_enabled = field(False, label="Enabled") workflow_source = field(ComboBox).with_options(label = "Workflow Source").with_choices([it.value for it in WorkflowSource]) workflow_path = field(Path).with_options(label = "Workflow Path", visible=False) + errors = field(Label).with_options(label="Errors") @fields_enabled.connect @enable_if([workflow_source]) @@ -290,7 +355,7 @@ def _make_model(self) -> Optional[Workflow]: return load_workflow(str(child.workflow_path)) # @magicclass(name="5. Output") -class OutputFields(FieldGroup): +class OutputFields(FieldGroup, NapariFieldGroup, metaclass=FieldGroupMeta): set_logging = field(Log_Levels.INFO).with_options(label="Logging Level") time_range = field(Tuple[int, int]).with_options( label="Time Export Range", @@ -318,6 +383,7 @@ class OutputFields(FieldGroup): save_name = field(str).with_options( label = "Save Prefix" ) + errors = field(Label).with_options(label="Errors") # @DeskewWidget.img_layer.connect diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index 1d37956..eae7371 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -49,7 +49,7 @@ def lattice_params_from_napari( else: if not last_dimension: raise ValueError("Either the Napari image must have dimensional metadata, or last_dimension must be provided") - img_data_aics = img_from_array(cast(ArrayLike, img.data), last_dimension=last_dimension, physical_pixel_sizes=kwargs.get("physical_pixel_sizes")) + img_data_aics = img_from_array(cast(ArrayLike, img.data), last_dimension=last_dimension, physical_pixel_sizes=physical_pixel_sizes) save_name: str if img.source.path is None: From af02a256b951df7afdcba9b65ccb03667f0c9b21 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 5 Sep 2023 21:28:09 +1000 Subject: [PATCH 018/147] Code cleanup, dimension stacking, use mixin for validation logic --- core/lls_core/lattice_data.py | 46 +++++++++++++-------------- plugin/napari_lattice/dock_widget.py | 19 +++++------ plugin/napari_lattice/fields.py | 47 ++++++++++++++++++---------- plugin/napari_lattice/reader.py | 9 +++--- plugin/napari_lattice/utils.py | 7 +++++ 5 files changed, 75 insertions(+), 53 deletions(-) create mode 100644 plugin/napari_lattice/utils.py diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 600b6e5..ec6c86c 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -26,10 +26,10 @@ from lls_core.utils import get_deskewed_shape from lls_core.types import ArrayLike from napari_workflows import Workflow +from napari.types import ShapesData if TYPE_CHECKING: import pyclesperanto_prototype as cle - from napari.types import ShapesData import logging @@ -525,7 +525,7 @@ def lattice_params_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixel physical_pixel_sizes = pixel_sizes, ) -def img_from_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> AICSImage: +def img_from_array(arr: ArrayLike, dimension_order: Optional[str] = None, **kwargs: Any) -> AICSImage: """ Creates an AICSImage from an array without metadata @@ -534,36 +534,36 @@ def img_from_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", " last_dimension: How to handle the dimension order kwargs: Additional arguments to pass to the AICSImage constructor """ - dim_order: str + # dim_order: str if len(arr.shape) < 3 or len(arr.shape) > 5: raise ValueError("Array dimensions must be in the range [3, 5]") # if aicsimageio tiffreader assigns last dim as time when it should be channel, user can override this - if len(arr.shape) == 3: - dim_order="ZYX" - else: - if last_dimension not in ["channel", "time"]: - raise ValueError("last_dimension must be either channel or time when convertin") - if len(arr.shape) == 4: - if last_dimension == "channel": - dim_order = "CZYX" - elif last_dimension == "time": - dim_order = "TZYX" - elif len(arr.shape) == 5: - if last_dimension == "channel": - dim_order = "CTZYX" - elif last_dimension == "time": - dim_order = "TCZYX" - else: - raise ValueError() - - img = AICSImage(image=arr, dim_order=dim_order, **kwargs) + # if len(arr.shape) == 3: + # dim_order="ZYX" + # else: + # if last_dimension not in ["channel", "time"]: + # raise ValueError("last_dimension must be either channel or time when convertin") + # if len(arr.shape) == 4: + # if last_dimension == "channel": + # dim_order = "CZYX" + # elif last_dimension == "time": + # dim_order = "TZYX" + # elif len(arr.shape) == 5: + # if last_dimension == "channel": + # dim_order = "CTZYX" + # elif last_dimension == "time": + # dim_order = "TCZYX" + # else: + # raise ValueError() + + img = AICSImage(image=arr, dim_order=dimension_order, **kwargs) # if last axes of "aicsimage data" shape is not equal to time, then swap channel and time if img.data.shape[0] != img.dims.T or img.data.shape[1] != img.dims.C: arr = np.swapaxes(arr, 0, 1) - return AICSImage(image=arr, dim_order=dim_order, **kwargs) + return AICSImage(image=arr, dim_order=dimension_order, **kwargs) def lattice_fom_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> AicsLatticeParams: """ diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 8a1836b..7238828 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -50,8 +50,6 @@ # def magicclass(*args, **kwargs) -> Callable[[MagicClassType], MagicClassType]: # return original_magicclass(*args, **kwargs) -def strikeout(text: str) -> str: - return '\u0336'.join(text) + '\u0336' # class HideableContents(MagicTemplate): # # fields_enabled = vfield(False, label="Enabled") @@ -69,13 +67,13 @@ def strikeout(text: str) -> str: # child.enabled = False -def validate_tab(parent: MagicTemplate, fields: FieldGroup, index: int): - tab_widget: QTabWidget = parent._widget._tab_widget - try: - fields._make_model() - tab_widget.setTabIcon(index, QIcon(GREEN)) - except ValidationError: - tab_widget.setTabIcon(index, QIcon(RED)) +# def validate_tab(parent: MagicTemplate, fields: FieldGroup, index: int): +# tab_widget: QTabWidget = parent._widget._tab_widget +# try: +# fields._make_model() +# tab_widget.setTabIcon(index, QIcon(GREEN)) +# except ValidationError: +# tab_widget.setTabIcon(index, QIcon(RED)) class LlszTemplate(MagicTemplate): @property @@ -135,6 +133,7 @@ class LlszMenu(LlszTemplate): # Pycudadecon library for deconvolution # options={"enabled": True}, + # Tabbed Widget container to house all the widgets @magicclass(widget_type="tabbed", name="Functions", labels=False) class WidgetContainer(LlszTemplate): @@ -143,6 +142,8 @@ def __post_init__(self): tab_widget: QTabWidget= self._widget._tab_widget for i in range(5): tab_widget.setTabIcon(i, QIcon(GREY)) + for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: + field._validate() deskew_fields = DeskewFields(name = "1. Deskew") # @deskew_fields.connect diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index e4d1e48..4099495 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -130,14 +130,19 @@ class LastDimensionOptions(Enum): class NapariFieldGroupCompatible(Protocol): errors: MagicField[Label] -class FieldGroupMeta(type(Protocol), type(FieldGroup)): - pass - -class NapariFieldGroup(NapariFieldGroupCompatible, Protocol): +class NapariFieldGroup: def __init__(self, *args, **kwargs): + from magicgui.widgets import Container super().__init__(*args, **kwargs) self = cast(FieldGroup, self) - self.changed.connect(self._validate) + # self._widget._mgui_bind_parent_change_callback(self._validate) + self.changed.connect(self._validate, unique=False) + # super(Container, self).connect(self._validate) + # self.connect(self._validate) + + # def _on_value_change(self, *args, **kwargs) -> None: + # super()._on_value_change(*args, **kwargs) + # self._validate() def _get_parent_tab_widget(self: Union[FieldGroup, Self]) -> QTabWidget: from qtpy.QtWidgets import QWidget @@ -161,13 +166,15 @@ def _set_valid(self, valid: bool): def _validate(self: Union[FieldGroup, Self]): self.errors.value = _get_friendly_validations(self) - self._set_valid(False if self.errors.value else True) + valid = not bool(self.errors.value) + self.errors.visible = not valid + self._set_valid(valid) def _make_model(self) -> BaseModel: raise NotImplementedError() -class DeskewFields(FieldGroup, NapariFieldGroup, metaclass=FieldGroupMeta): +class DeskewFields(NapariFieldGroup, FieldGroup): def _get_dimension_options(self, _) -> List[str]: """ @@ -200,6 +207,10 @@ def _get_dimension_options(self, _) -> List[str]: skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") errors = field(Label).with_options(label="Errors") + @img_layer.connect + def _img_changed(self): + self.dimension_order.reset_choices() + def _merge_layers(self) -> Image: """ Returns a single image merged from all the selected layers @@ -211,14 +222,14 @@ def _make_model(self) -> DeskewParams: return DeskewParams( **lattice_params_from_napari( img=self._merge_layers(), - last_dimension=self.dimension_order.value, + dimension_order=None if self.dimension_order.value == "Get from Metadata" else self.dimension_order.value, physical_pixel_sizes=self.pixel_sizes.value, ), angle=self.angle.value, skew = self.skew_dir.value, ) -class DeconvolutionFields(NapariFieldGroup, FieldGroup, metaclass=FieldGroupMeta): +class DeconvolutionFields(NapariFieldGroup, FieldGroup): """ A counterpart to the DeconvolutionParams Pydantic class """ @@ -271,7 +282,7 @@ def _make_model(self) -> Optional[DeconvolutionParams]: background=self.background.value ) -class CroppingFields(FieldGroup, NapariFieldGroup, metaclass=FieldGroupMeta): +class CroppingFields(FieldGroup, NapariFieldGroup): """ A counterpart to the CropParams Pydantic class """ @@ -319,13 +330,15 @@ def _enable_workflow(self) -> bool: # self["activate_cropping"].background_color = "green" def _make_model(self) -> Optional[CropParams]: - return CropParams( - z_start=self.z_range.value[0], - z_end=self.z_range.value[1], - roi_layer_list=self.shapes.value - ) + if self.fields_enabled.value: + return CropParams( + z_start=self.z_range.value[0], + z_end=self.z_range.value[1], + roi_layer_list=self.shapes.value + ) + return None -class WorkflowFields(FieldGroup, NapariFieldGroup, metaclass=FieldGroupMeta): +class WorkflowFields(FieldGroup, NapariFieldGroup): """ Handles the workflow related parameters """ @@ -355,7 +368,7 @@ def _make_model(self) -> Optional[Workflow]: return load_workflow(str(child.workflow_path)) # @magicclass(name="5. Output") -class OutputFields(FieldGroup, NapariFieldGroup, metaclass=FieldGroupMeta): +class OutputFields(FieldGroup, NapariFieldGroup): set_logging = field(Log_Levels.INFO).with_options(label="Logging Level") time_range = field(Tuple[int, int]).with_options( label="Time Export Range", diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index eae7371..0775172 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -29,7 +29,8 @@ class NapariImageParams(AicsLatticeParams): def lattice_params_from_napari( img: Layer, - last_dimension: Optional[Literal["channel", "time"]], + dimension_order: Optional[str], + # last_dimension: Optional[Literal["channel", "time"]], physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None) ) -> NapariImageParams: """ @@ -47,9 +48,9 @@ def lattice_params_from_napari( if 'aicsimage' in img.metadata.keys(): img_data_aics = img.metadata['aicsimage'] else: - if not last_dimension: - raise ValueError("Either the Napari image must have dimensional metadata, or last_dimension must be provided") - img_data_aics = img_from_array(cast(ArrayLike, img.data), last_dimension=last_dimension, physical_pixel_sizes=physical_pixel_sizes) + if not dimension_order: + raise ValueError("Either the Napari image must have dimensional metadata, or a dimension order must be provided") + img_data_aics = img_from_array(cast(ArrayLike, img.data), dimension_order=dimension_order, physical_pixel_sizes=physical_pixel_sizes) save_name: str if img.source.path is None: diff --git a/plugin/napari_lattice/utils.py b/plugin/napari_lattice/utils.py new file mode 100644 index 0000000..1967c3e --- /dev/null +++ b/plugin/napari_lattice/utils.py @@ -0,0 +1,7 @@ +from napari.viewer import current_viewer, Viewer + +def get_viewer() -> Viewer: + viewer = current_viewer() + if viewer is None: + raise Exception("No viewer present!") + return viewer From 28e62b91e78bf5663fe1370c6407bc3770f5c092 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 6 Sep 2023 12:06:41 +1000 Subject: [PATCH 019/147] Implement "disabled", greyed-out steps, fix some bugs in the Lattice model generation --- plugin/napari_lattice/dock_widget.py | 24 ++++----- plugin/napari_lattice/fields.py | 75 ++++++++++++++++++---------- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 7238828..4bba9e3 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -34,7 +34,7 @@ from pydantic import ValidationError from napari_lattice.reader import lattice_params_from_napari -from napari_lattice.fields import DeskewFields, CroppingFields, DeconvolutionFields, WorkflowFields, OutputFields +from napari_lattice.fields import DeskewFields, CroppingFields, DeconvolutionFields, WorkflowFields, OutputFields, exception_to_html from napari_lattice.icons import GREY from strenum import StrEnum @@ -110,19 +110,12 @@ def _make_model(self) -> LatticeData: # workflow = self.LlszMenu.WidgetContainer.WorkflowWidget # TODO: fix - return lattice_from_napari( - img=deskew.img_layer.value, - last_dimension=deskew.dimension_order.value, - angle=deskew.angle.value, - skew = deskew.skew_dir.value, - physical_pixel_sizes=deskew.pixel_sizes.value, - save_dir = output.save_path.value, - channel_range=range(output.channel_range.value[0], output.channel_range.value[1]), - time_range=range(output.time_range.value[0], output.time_range.value[1]), - save_type=output.save_type.value, - deconvolution = deconv._make_model(), - workflow = workflow._make_model(), - crop = crop._make_model() + return LatticeData( + **self.LlszMenu.WidgetContainer.deskew_fields._make_model().dict(), + **self.LlszMenu.WidgetContainer.output_fields._make_model().dict(), + workflow=self.LlszMenu.WidgetContainer.workflow_fields._make_model(), + deconvolution=self.LlszMenu.WidgetContainer.deconv_fields._make_model(), + crop=self.LlszMenu.WidgetContainer.cropping_fields._make_model() ) @magicclass(widget_type="split") @@ -405,10 +398,11 @@ def __post_init__(self): def preview(self, header:str, time: int, channel: int): # We only need to process one time point for the preview, # so we made a copy using a subset of the times - lattice = self._make_model_friendly().copy(update=dict( + lattice = self._make_model().copy(update=dict( time_range = range(time, time+1), channel_range = range(time, time+1), )) + for slice in lattice.process().slices: scale = ( lattice.new_dz, diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 4099495..251d8f9 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -2,7 +2,7 @@ from magicclass import FieldGroup, field, MagicTemplate from magicclass.widgets import Widget, ComboBox, Label from magicclass.fields import MagicField -from typing import Callable, List, Optional, Protocol, Tuple, TypeVar, Union, cast +from typing import Any, Callable, List, Optional, Protocol, Tuple, TypeVar, Union, cast from typing_extensions import Protocol, Self from pydantic import BaseModel, ValidationError @@ -31,24 +31,29 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -def _get_friendly_validations(model: FieldGroup) -> str: +def exception_to_html(e: BaseException) -> str: """ - Generates a LatticeData, but returns validation errors in a user friendly way + Converts an exception to HTML for reporting back to the user """ - try: - model._make_model() - return "" - except ValidationError as e: + if isinstance(e, ValidationError): message = [] - # message = [f"

    {e.model}

    "] for error in e.errors(): header = ", ".join([str(it).capitalize() for it in error['loc']]) message.append(f"
  • {header} {error['msg']}
  • ") joined = '\n'.join(message) return f"
      {joined}
    " - except Exception as e: + else: return str(e) +def get_friendly_validations(model: FieldGroup) -> str: + """ + Generates a BaseModel, but returns validation errors in a user friendly way + """ + try: + model._make_model() + return "" + except BaseException as e: + return exception_to_html(e) class WorkflowSource(StrEnum): ActiveWorkflow = "Active Workflow" @@ -127,12 +132,22 @@ class LastDimensionOptions(Enum): # def __init__(self, layout: str = "vertical", labels: bool = False, name: str | None = None, **kwargs): # super().__init__(layout, labels, name, **kwargs) -class NapariFieldGroupCompatible(Protocol): - errors: MagicField[Label] +# class NapariFieldGroupCompatible(Protocol): +# from magicclass.widgets import Container +# from qtpy.QtWidgets import QWidget +# errors: MagicField[Label] +# parent: QWidget +# _widget: Container class NapariFieldGroup: - def __init__(self, *args, **kwargs): - from magicgui.widgets import Container + # This implementation is a bit ugly. This is a mixin that can only be used on a `FieldGroup`. + # However, it can't inherit from FieldGroup because then the metaclass would look for fields in this + # class definition, find none, and then make an empty GUI page when this is rendered. + # It also can't inherit from a FieldGroup-like Protocol as mypy suggests for mixin classes + # (https://mypy.readthedocs.io/en/latest/more_types.html#mixin-classes) because it doesn't + # implement the attributes of a FieldGroup. Ideally this could be a Protocol subclass as well + # to make it remain abstract, but the Protocol metaclass interferes with the FieldGroup metaclass + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self = cast(FieldGroup, self) # self._widget._mgui_bind_parent_change_callback(self._validate) @@ -144,28 +159,27 @@ def __init__(self, *args, **kwargs): # super()._on_value_change(*args, **kwargs) # self._validate() - def _get_parent_tab_widget(self: Union[FieldGroup, Self]) -> QTabWidget: - from qtpy.QtWidgets import QWidget - parent = cast(QWidget, self.parent) - return parent.parentWidget() + def _get_parent_tab_widget(self: Any) -> QTabWidget: + return self.parent.parentWidget() - def _get_tab_index(self: Union[FieldGroup, Self]) -> int: - from magicclass.widgets import Container - widget = cast(Container, self._widget) - return self._get_parent_tab_widget().indexOf(widget._qwidget) + def _get_tab_index(self: Any) -> int: + return self._get_parent_tab_widget().indexOf(self._widget._qwidget) - def _set_valid(self, valid: bool): + def _set_valid(self: Any, valid: bool): from qtpy.QtGui import QIcon tab_parent = self._get_parent_tab_widget() index = self._get_tab_index() - if valid: + if hasattr(self, "fields_enabled") and not self.fields_enabled.value: + # Special case for "diabled" sections + tab_parent.setTabIcon(index, QIcon(GREY)) + elif valid: tab_parent.setTabIcon(index, QIcon(GREEN)) else: tab_parent.setTabIcon(index, QIcon(RED)) - def _validate(self: Union[FieldGroup, Self]): - self.errors.value = _get_friendly_validations(self) + def _validate(self: Any): + self.errors.value = get_friendly_validations(self) valid = not bool(self.errors.value) self.errors.visible = not valid self._set_valid(valid) @@ -216,6 +230,8 @@ def _merge_layers(self) -> Image: Returns a single image merged from all the selected layers """ from napari.layers.utils.stack_utils import images_to_stack + if len(self.img_layer.value) == 0: + raise Exception("At least one image layer must be selected.") return images_to_stack(self.img_layer.value) def _make_model(self) -> DeskewParams: @@ -398,5 +414,14 @@ class OutputFields(FieldGroup, NapariFieldGroup): ) errors = field(Label).with_options(label="Errors") + def _make_model(self) -> OutputParams: + return OutputParams( + channel_range=range(self.channel_range.value[0], self.channel_range.value[1]), + time_range=range(self.time_range.value[0], self.time_range.value[1]), + save_dir=self.save_path.value, + save_name=self.save_name.value, + save_type=self.save_type.value + ) + # @DeskewWidget.img_layer.connect From 39f798bb1c92ce1baf64856ed6a5be58fb0c9589 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 7 Sep 2023 11:55:32 +1000 Subject: [PATCH 020/147] Add new tab icons, filter down to only images in the layer select, make errors red --- core/lls_core/workflow.py | 2 +- plugin/napari_lattice/circle-regular.svg | 1 + plugin/napari_lattice/fields.py | 51 ++++++++++++++++-------- plugin/napari_lattice/icons.py | 6 +-- plugin/napari_lattice/invalid.svg | 8 ++++ plugin/napari_lattice/utils.py | 16 ++++++++ plugin/napari_lattice/valid.svg | 8 ++++ 7 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 plugin/napari_lattice/circle-regular.svg create mode 100644 plugin/napari_lattice/invalid.svg create mode 100644 plugin/napari_lattice/valid.svg diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index a861d47..63ffe06 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -135,7 +135,7 @@ def import_script(script: Path): sys.modules[module_name] = module spec.loader.exec_module(module) -def import_workflow_modules(workflow: Path): +def import_workflow_modules(workflow: Path) -> None: """ Imports all the Python files that might be used in a given custom workflow diff --git a/plugin/napari_lattice/circle-regular.svg b/plugin/napari_lattice/circle-regular.svg new file mode 100644 index 0000000..13e8dda --- /dev/null +++ b/plugin/napari_lattice/circle-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 251d8f9..8138466 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -1,15 +1,15 @@ from pathlib import Path from magicclass import FieldGroup, field, MagicTemplate -from magicclass.widgets import Widget, ComboBox, Label +from magicclass.widgets import Widget, ComboBox, Label, Select from magicclass.fields import MagicField -from typing import Any, Callable, List, Optional, Protocol, Tuple, TypeVar, Union, cast +from typing import Any, Callable, List, Optional, Protocol, Tuple, TypeVar, Union, cast, TYPE_CHECKING from typing_extensions import Protocol, Self from pydantic import BaseModel, ValidationError from strenum import StrEnum from lls_core import DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes, LatticeData, OutputParams, DeskewParams -from napari.layers import Layer +from napari.layers import Shapes from enum import Enum import pyclesperanto_prototype as cle from napari_workflows import Workflow, WorkflowManager @@ -20,10 +20,11 @@ from napari_lattice.icons import RED, GREEN, GREY from napari_lattice.reader import lattice_params_from_napari -from napari_lattice.utils import get_viewer +from napari_lattice.utils import get_viewer, get_layers from napari.layers import Image + # FieldGroups that the users interact with to input data import logging @@ -152,6 +153,14 @@ def __init__(self, *args: Any, **kwargs: Any): self = cast(FieldGroup, self) # self._widget._mgui_bind_parent_change_callback(self._validate) self.changed.connect(self._validate, unique=False) + + # Style the error label. + # We have to check this is a QLabel because in theory this might run in a non-QT backend + errors = self.errors.native + from qtpy.QtWidgets import QLabel + if isinstance(errors, QLabel): + errors.setStyleSheet("color: red;") + errors.setWordWrap(True) # super(Container, self).connect(self._validate) # self.connect(self._validate) @@ -184,10 +193,9 @@ def _validate(self: Any): self.errors.visible = not valid self._set_valid(valid) - def _make_model(self) -> BaseModel: + def _make_model(self): raise NotImplementedError() - class DeskewFields(NapariFieldGroup, FieldGroup): def _get_dimension_options(self, _) -> List[str]: @@ -209,7 +217,7 @@ def _get_dimension_options(self, _) -> List[str]: else: raise Exception("Only 3-5 dimensional arrays are supported") - img_layer = field(List[Image], widget_type='Select').with_options(label = "Image Layer").with_choices(get_viewer().layers) + img_layer = field(List[Image], widget_type='Select').with_options(label = "Image Layer").with_choices(lambda _x, _y: get_layers(Image)) pixel_sizes = field(Tuple[float, float, float]).with_options( value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), label="Pixel Sizes (XYZ)" @@ -293,17 +301,24 @@ def _enable_fields(self) -> bool: def _make_model(self) -> Optional[DeconvolutionParams]: if not self.fields_enabled.value: return None + if self.background.value == BackgroundSource.Custom: + background = self.background_custom.value + elif self.background.value == BackgroundSource.Auto: + background = "auto" + else: + background = "second_last" return DeconvolutionParams( decon_processing=self.decon_processing.value, - background=self.background.value + background=background, + psf_num_iter=self.psf_num_iter.value ) -class CroppingFields(FieldGroup, NapariFieldGroup): +class CroppingFields(NapariFieldGroup, FieldGroup): """ A counterpart to the CropParams Pydantic class """ fields_enabled = field(False, label="Enabled") - shapes= field(ShapesData, label = "ROIs")#Optional[Shapes] = None + shapes= field(List[Shapes], widget_type="Select", label = "ROIs").with_options(choices=lambda _x, _y: get_layers(Shapes)) z_range = field(Tuple[int, int]).with_options( label = "Z Range", value = (0, 1), @@ -346,15 +361,16 @@ def _enable_workflow(self) -> bool: # self["activate_cropping"].background_color = "green" def _make_model(self) -> Optional[CropParams]: + import numpy as np if self.fields_enabled.value: return CropParams( z_start=self.z_range.value[0], z_end=self.z_range.value[1], - roi_layer_list=self.shapes.value + roi_layer_list=ShapesData([np.array(shape.data) for shape in self.shapes.value]) ) return None -class WorkflowFields(FieldGroup, NapariFieldGroup): +class WorkflowFields(NapariFieldGroup, FieldGroup): """ Handles the workflow related parameters """ @@ -374,17 +390,18 @@ def _workflow_path(self) -> bool: return self.workflow_source.value == WorkflowSource.CustomPath def _make_model(self) -> Optional[Workflow]: + from lls_core.workflow import import_workflow_modules + from napari_workflows._io_yaml_v1 import load_workflow if not self.fields_enabled.value: return None - child = get_child(self, WorkflowFields) - if child.workflow_source == WorkflowSource.ActiveWorkflow: + if self.workflow_source.value == WorkflowSource.ActiveWorkflow: return WorkflowManager.install(self.parent_viewer).workflow else: - import_workflow_modules(child.workflow_path) - return load_workflow(str(child.workflow_path)) + import_workflow_modules(self.workflow_path.value) + return load_workflow(str(self.workflow_path.value)) # @magicclass(name="5. Output") -class OutputFields(FieldGroup, NapariFieldGroup): +class OutputFields(NapariFieldGroup, FieldGroup): set_logging = field(Log_Levels.INFO).with_options(label="Logging Level") time_range = field(Tuple[int, int]).with_options( label="Time Export Range", diff --git a/plugin/napari_lattice/icons.py b/plugin/napari_lattice/icons.py index 75b82fb..7c01abe 100644 --- a/plugin/napari_lattice/icons.py +++ b/plugin/napari_lattice/icons.py @@ -1,5 +1,5 @@ import pkg_resources -GREEN = pkg_resources.resource_filename(__name__, "green.png") -GREY = pkg_resources.resource_filename(__name__, "grey.png") -RED = pkg_resources.resource_filename(__name__, "red.png") +GREEN = pkg_resources.resource_filename(__name__, "valid.svg") +GREY = pkg_resources.resource_filename(__name__, "circle-regular.svg") +RED = pkg_resources.resource_filename(__name__, "invalid.svg") diff --git a/plugin/napari_lattice/invalid.svg b/plugin/napari_lattice/invalid.svg new file mode 100644 index 0000000..e1d7dcc --- /dev/null +++ b/plugin/napari_lattice/invalid.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugin/napari_lattice/utils.py b/plugin/napari_lattice/utils.py index 1967c3e..25778c7 100644 --- a/plugin/napari_lattice/utils.py +++ b/plugin/napari_lattice/utils.py @@ -1,7 +1,23 @@ from napari.viewer import current_viewer, Viewer +from napari.layers import Layer +from typing_extensions import TypeVar +from typing import Sequence, Type def get_viewer() -> Viewer: + """ + Returns the current viewer, throwing an exception if one doesn't exist + """ viewer = current_viewer() if viewer is None: raise Exception("No viewer present!") return viewer + +LayerType = TypeVar("LayerType", bound=Layer) +def get_layers(type: Type[LayerType]) -> Sequence[LayerType]: + """ + Returns all layers in the current napari viewer of a given `Layer` subtype. + For example, if you pass `napari.layers.Image`, it will return a list of + Image layers + """ + viewer = get_viewer() + return [layer for layer in viewer.layers if isinstance(layer, type)] diff --git a/plugin/napari_lattice/valid.svg b/plugin/napari_lattice/valid.svg new file mode 100644 index 0000000..81ad09b --- /dev/null +++ b/plugin/napari_lattice/valid.svg @@ -0,0 +1,8 @@ + + + + + + + + From 23224b1c93d6cac84487f6e3c42e869d37a9f534 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 11 Sep 2023 12:36:55 +1000 Subject: [PATCH 021/147] Rework the dimension merging --- core/lls_core/lattice_data.py | 23 +++--- core/lls_core/utils.py | 11 ++- core/pyproject.toml | 3 +- plugin/napari_lattice/fields.py | 136 +++++++++++++++++++++++++------- plugin/napari_lattice/reader.py | 88 +++++++++++++-------- 5 files changed, 190 insertions(+), 71 deletions(-) diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index ec6c86c..f568462 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -13,7 +13,7 @@ from pydantic_numpy import NDArray from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Union -from typing_extensions import TypedDict +from typing_extensions import TypedDict, Self from pathlib import Path from aicsimageio.types import PhysicalPixelSizes @@ -35,12 +35,6 @@ logger = logging.getLogger(__name__) -T = TypeVar("T") -def raise_if_none(obj: Optional[T], message: str) -> T: - if obj is None: - raise TypeError(message) - return obj - class DefaultMixin(BaseModel): """ Adds a method for retrieving default values from a BaseModel @@ -130,10 +124,20 @@ class DefinedPixelSizes(DefaultMixin): Like PhysicalPixelSizes, but it's a dataclass, and none of its fields are None """ - X: NonNegativeFloat = 0.14 - Y: NonNegativeFloat = 0.14 + X: NonNegativeFloat = 0.1499219272808386 + Y: NonNegativeFloat = 0.1499219272808386 Z: NonNegativeFloat = 0.3 + @classmethod + def from_physical(cls, pixels: PhysicalPixelSizes) -> Self: + from lls_core.utils import raise_if_none + + return DefinedPixelSizes( + X=raise_if_none(pixels.X, "All pixels must be defined"), + Y=raise_if_none(pixels.Y, "All pixels must be defined"), + Z=raise_if_none(pixels.Z, "All pixels must be defined"), + ) + class DeconvolutionParams(BaseModel, arbitrary_types_allowed=True): """ Parameters for the optional deconvolution step @@ -213,6 +217,7 @@ def set_deskew(cls, values: dict) -> dict: Sets the default deskew shape values if the user has not provided them """ # process the file to get shape of final deskewed image + data = values["data"] if values.get('deskew_vol_shape') is None: if values.get('deskew_affine_transform') is None: # If neither has been set, calculate them ourselves diff --git a/core/lls_core/utils.py b/core/lls_core/utils.py index bd82f9b..e0025c9 100644 --- a/core/lls_core/utils.py +++ b/core/lls_core/utils.py @@ -3,7 +3,7 @@ from collections import defaultdict from contextlib import contextmanager, redirect_stderr, redirect_stdout from os import devnull, path -from typing import Collection, List, Tuple, Union +from typing import Collection, List, Optional, Tuple, TypeVar, Union import numpy as np import pyclesperanto_prototype as cle @@ -341,3 +341,12 @@ def as_type(img, ref_vol): """ img.astype(ref_vol.dtype) return img + +T = TypeVar("T") +def raise_if_none(obj: Optional[T], message: str) -> T: + """ + Asserts that `obj` is not None + """ + if obj is None: + raise TypeError(message) + return obj diff --git a/core/pyproject.toml b/core/pyproject.toml index ab5b93f..04c5c87 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -53,7 +53,8 @@ dependencies = [ "tifffile>=2023.3.15", #>=2022.8.12 "fsspec>=2022.8.2", "napari-workflows>=0.2.8", - "resource-backed-dask-array>=0.1.0" + "resource-backed-dask-array>=0.1.0", + "xarray[parallel]" ] [project.urls] diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 8138466..b69deac 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -1,3 +1,4 @@ +from __future__ import annotations from pathlib import Path from magicclass import FieldGroup, field, MagicTemplate from magicclass.widgets import Widget, ComboBox, Label, Select @@ -7,6 +8,7 @@ from pydantic import BaseModel, ValidationError from strenum import StrEnum +from enum import auto from lls_core import DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes, LatticeData, OutputParams, DeskewParams from napari.layers import Shapes @@ -24,6 +26,9 @@ from napari.layers import Image +if TYPE_CHECKING: + from xarray import DataArray + # FieldGroups that the users interact with to input data @@ -111,11 +116,11 @@ def _decorator(fn: Callable[[EnabledHandlerType], bool])-> Callable[[EnabledHand def make_handler(fn: Callable) -> Callable[[EnabledHandlerType], None]: def handler(parent: EnabledHandlerType): enable = fn(parent) - if enable: - logger.info(f"{parent.__class__} Activated") - else: - logger.info(f"{parent.__class__} Deactivated") for field in fields: + if enable: + logger.info(f"{field.name} Activated") + else: + logger.info(f"{field.name} Deactivated") enable_field(parent, field, enabled=enable) return handler @@ -123,6 +128,9 @@ def handler(parent: EnabledHandlerType): return _decorator +class StackAlong(StrEnum): + CHANNEL = "Channel" + TIME = "Time" class LastDimensionOptions(Enum): XYZTC = "XYZTC" @@ -203,12 +211,10 @@ def _get_dimension_options(self, _) -> List[str]: Returns the list of dimension order options that might be possible for the current image stack """ default = ["Get from Metadata"] - try: - merged = self._merge_layers() - except Exception: + ndims = max([len(layer.data.shape) for layer in self.img_layer.value], default=None) + if ndims is None: return default - ndims = len(merged._dims_order) - if ndims == 3: + elif ndims == 3: return ["ZYX"] + default elif ndims == 4: return ["TZYX", "CZYX"] + default @@ -217,38 +223,112 @@ def _get_dimension_options(self, _) -> List[str]: else: raise Exception("Only 3-5 dimensional arrays are supported") - img_layer = field(List[Image], widget_type='Select').with_options(label = "Image Layer").with_choices(lambda _x, _y: get_layers(Image)) + img_layer = field(List[Image], widget_type='Select').with_options( + label="Image Layer(s) to Deskew", + tooltip="All the image layers you select will be stacked into one image and then deskewed. To select multiple layers, hold Command (MacOS) or Control (Windows, Linux)." + ).with_choices(lambda _x, _y: get_layers(Image)) + stack_along = field( + str + ).with_choices( + [it.value for it in StackAlong] + ).with_options( + label="Stack Along", + tooltip="The direction along which to stack multiple selected layers.", + value=StackAlong.CHANNEL + ) pixel_sizes = field(Tuple[float, float, float]).with_options( - value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), - label="Pixel Sizes (XYZ)" + label="Pixel Sizes: XYZ (µm)", + tooltip="The size of each pixel in microns. The first field selects the X pixel size, then Y, then Z." + ) + angle = field(LatticeData.get_default("angle")).with_options( + value=LatticeData.get_default("angle"), + label="Skew Angle (°)", + tooltip="The angle to deskew the image, in degrees" + ) + device = field(str).with_choices(cle.available_device_names()).with_options( + label="Graphics Device", + tooltip="The GPU that will be used to perform the processing" ) - angle = field(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") - device = field(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") # merge_all_channels = field(False).with_options(label="Merge all Channels") - dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices(_get_dimension_options).with_options(label="Dimension Order") - skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") + dimension_order = field( + str + ).with_choices( + _get_dimension_options + ).with_options( + label="Dimension Order", + tooltip="Specifies the order of dimensions in the input images. For example, if your image is a 4D array with multiple channels along the first axis, you will specify CZYX.", + value=LastDimensionOptions.Metadata.value + ) + skew_dir = field(DeskewDirection.Y).with_options( + label="Skew Direction", + tooltip="The axis along which to deskew" + ) errors = field(Label).with_options(label="Errors") + def __init__(self, *args: Any, **kwargs: Any): + super().__init__(*args, **kwargs) + from qtpy.QtWidgets import QDoubleSpinBox + from magicgui.widgets import TupleEdit + + # Enormous hack to set the precision + # A better method has been requested here: https://github.com/pyapp-kit/magicgui/issues/581#issuecomment-1709467219 + if isinstance(self.pixel_sizes, TupleEdit): + for subwidget in self.pixel_sizes._list: + if isinstance(subwidget.native, QDoubleSpinBox): + subwidget.native.setDecimals(10) + # We have to re-set the default value after changing the precision, because it's already been rounded up + # Also, we have to block emitting the changed signal at construction time + with self.changed.blocked(): + self.pixel_sizes.value = ( + DefinedPixelSizes.get_default("X"), + DefinedPixelSizes.get_default("Y"), + DefinedPixelSizes.get_default("Z") + ) + @img_layer.connect - def _img_changed(self): + def _img_changed(self) -> None: + # Recalculate the dimension options self.dimension_order.reset_choices() - def _merge_layers(self) -> Image: + @img_layer.connect + @enable_if([stack_along]) + def _hide_stack_along(self): + return len(self.img_layer.value) > 1 + + def _merge_layers(self) -> DataArray: """ - Returns a single image merged from all the selected layers + Returns a single image array merged from all the selected layers """ - from napari.layers.utils.stack_utils import images_to_stack - if len(self.img_layer.value) == 0: - raise Exception("At least one image layer must be selected.") - return images_to_stack(self.img_layer.value) + from xarray import DataArray, concat + layers = [DataArray(it, dims=self.dimension_order.value.split()) for it in self.img_layer.value] + if len(layers) == 0: + raise Exception("At least one image layer must be selected") + elif len(layers) == 1: + return layers[0] + else: + dim = "C" if self.stack_along.value == StackAlong.CHANNEL else "T" + return concat(layers, dim=dim) + + # if len(self.img_layer.value) == 0: + # raise Exception("At least one image layer must be selected.") + # return images_to_stack(self.img_layer.value) def _make_model(self) -> DeskewParams: - return DeskewParams( - **lattice_params_from_napari( - img=self._merge_layers(), + from aicsimageio.types import PhysicalPixelSizes + params = lattice_params_from_napari( + imgs=self.img_layer.value, dimension_order=None if self.dimension_order.value == "Get from Metadata" else self.dimension_order.value, - physical_pixel_sizes=self.pixel_sizes.value, - ), + physical_pixel_sizes=PhysicalPixelSizes( + X = self.pixel_sizes.value[0], + Y = self.pixel_sizes.value[1], + Z = self.pixel_sizes.value[2] + ), + stack_along="C" if self.stack_along.value == StackAlong.CHANNEL else "T" + ) + return DeskewParams( + data=params["data"], + dims=params["dims"], + physical_pixel_sizes=params["physical_pixel_sizes"], angle=self.angle.value, skew = self.skew_dir.value, ) diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index 0775172..1c7cee2 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -13,60 +13,84 @@ import dask.delayed as delayed import os import numpy as np -from napari.layers import image, Layer +from napari.layers import Layer, Image from napari.layers._data_protocols import LayerDataProtocol from aicsimageio.dimensions import Dimensions from aicsimageio.aics_image import AICSImage from typing_extensions import Literal -from typing import Any, Optional, cast, Tuple +from typing import Any, Optional, cast, Tuple, Collection -from lls_core.lattice_data import lattice_params_from_aics, img_from_array, AicsLatticeParams, PhysicalPixelSizes +from lls_core.lattice_data import DefinedPixelSizes, lattice_params_from_aics, img_from_array, AicsLatticeParams, PhysicalPixelSizes from lls_core.types import ArrayLike class NapariImageParams(AicsLatticeParams): save_name: str def lattice_params_from_napari( - img: Layer, + imgs: Collection[Image], dimension_order: Optional[str], - # last_dimension: Optional[Literal["channel", "time"]], - physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None) + physical_pixel_sizes: PhysicalPixelSizes, + stack_along: str ) -> NapariImageParams: """ Factory function for generating a LatticeData from a Napari Image - - Arguments: - kwargs: Extra arguments to pass to the LatticeData constructor """ + from xarray import DataArray, concat - if not isinstance(img, Layer): - raise Exception("img must be a napari layer object") - - img_data_aics: AICSImage + if len(imgs) < 1: + raise ValueError("At least one image must be provided.") - if 'aicsimage' in img.metadata.keys(): - img_data_aics = img.metadata['aicsimage'] - else: - if not dimension_order: + save_name: str + pixel_sizes: set[PhysicalPixelSizes] = {physical_pixel_sizes} + save_names = [] + + # The pixel sizes according to the AICS metadata, if any + final_imgs: list[DataArray] = [] + + for img in imgs: + if img.source.path is None: + # remove colon (:) and any leading spaces + save_name = img.name.replace(":", "").strip() + # replace any group of spaces with "_" + save_name = '_'.join(save_name.split()) + else: + file_name_noext = os.path.basename(img.source.path) + file_name = os.path.splitext(file_name_noext)[0] + # remove colon (:) and any leading spaces + save_name = file_name.replace(":", "").strip() + # replace any group of spaces with "_" + save_name = '_'.join(save_name.split()) + + save_names.append(save_name) + + if 'aicsimage' in img.metadata.keys(): + img_data_aics: AICSImage = img.metadata['aicsimage'] + # Only process pixel sizes that are not none + if all(img_data_aics.physical_pixel_sizes): + pixel_sizes.add(img_data_aics.physical_pixel_sizes) + # if pixel_size_metadata is not None and pixel_sizes != img_data_aics.physical_pixel_sizes: + # raise Exception(f"Two or more layers that you have tried to merge have different pixel sizes according to their metadata! A previous image has size {physical_pixel_sizes}, whereas {img.name} has size {img_data_aics.physical_pixel_sizes}.") + # else: + # pixel_size_metadata = img_data_aics.physical_pixel_sizes + + calculated_order = img_data_aics.dims.order + elif dimension_order is None: raise ValueError("Either the Napari image must have dimensional metadata, or a dimension order must be provided") - img_data_aics = img_from_array(cast(ArrayLike, img.data), dimension_order=dimension_order, physical_pixel_sizes=physical_pixel_sizes) + else: + calculated_order = list(dimension_order) - save_name: str - if img.source.path is None: - # remove colon (:) and any leading spaces - save_name = img.name.replace(":", "").strip() - # replace any group of spaces with "_" - save_name = '_'.join(save_name.split()) + final_imgs.append(DataArray(img.data, dims=calculated_order)) + + if len(pixel_sizes) > 1: + raise Exception(f"Two or more layers that you have tried to merge have different pixel sizes according to their metadata! {pixel_sizes}") + elif len(pixel_sizes) == 1: + final_pixel_size = DefinedPixelSizes.from_physical(pixel_sizes.pop()) else: - file_name_noext = os.path.basename(img.source.path) - file_name = os.path.splitext(file_name_noext)[0] - # remove colon (:) and any leading spaces - save_name = file_name.replace(":", "").strip() - # replace any group of spaces with "_" - save_name = '_'.join(save_name.split()) - - return NapariImageParams(save_name=save_name, **lattice_params_from_aics(img_data_aics, physical_pixel_sizes=physical_pixel_sizes)) + final_pixel_size = DefinedPixelSizes.from_physical(physical_pixel_sizes) + + final_img = concat(final_imgs, dim=stack_along) + return NapariImageParams(save_name=save_names[0], physical_pixel_sizes=final_pixel_size, data=final_img, dims=final_img.shape) def napari_get_reader(path: list[str] | str): """Check if file ends with h5 and returns reader function if true From 17e10b61121e72d2d58db7cf509047330a37c997 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 11 Sep 2023 18:22:08 +1000 Subject: [PATCH 022/147] Cleanup old code --- core/lls_core/lattice_data.py | 75 ++- plugin/napari_lattice/dock_widget.py | 974 +-------------------------- plugin/napari_lattice/fields.py | 153 ++--- plugin/napari_lattice/reader.py | 8 +- 4 files changed, 138 insertions(+), 1072 deletions(-) diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index f568462..3ec8bbb 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -27,6 +27,7 @@ from lls_core.types import ArrayLike from napari_workflows import Workflow from napari.types import ShapesData +from xarray import DataArray if TYPE_CHECKING: import pyclesperanto_prototype as cle @@ -75,7 +76,7 @@ def save_image(self): for roi, roi_results in groupby(self.slices, key=lambda it: it.roi_index): if self.lattice_data.save_type == SaveFileType.h5: bdv_writer = npy2bdv.BdvWriter( - make_filename_prefix(prefix=self.lattice_data.save_name, roi_index=roi), + make_filename_prefix(prefix=self.lattice_data.save_name, roi_index=roi) + ".h5", compression='gzip', nchannels=len(self.lattice_data.channel_range), subsamp=((1, 1, 1), (1, 2, 2), (2, 4, 4)), @@ -180,7 +181,7 @@ def default_time_range(cls, v: Any, values: dict) -> range: Sets the default time range if undefined """ if v is None: - return range(values["dims"].T + 1) + return range(values["data"].sizes["T"] + 1) return v @validator("channel_range") @@ -189,20 +190,12 @@ def default_channel_range(cls, v: Any, values: dict) -> range: Sets the default channel range if undefined """ if v is None: - return range(values["dims"].C + 1) + return range(values["data"].sizes["C"] + 1) return v class DeskewParams(DefaultMixin, arbitrary_types_allowed=True): #: A 3-5D array containing the image data - data: ArrayLike - - #: Dimensions of `data` - dims: Dimensions - - #: Dimensions of the deskewed output - deskew_vol_shape: Tuple[int, ...] = Field(init_var=False) - - deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False) + data: DataArray #: Geometry of the light path skew: DeskewDirection = DeskewDirection.Y @@ -211,17 +204,42 @@ class DeskewParams(DefaultMixin, arbitrary_types_allowed=True): #: Pixel size in microns physical_pixel_sizes: DefinedPixelSizes = Field(default_factory=DefinedPixelSizes) + #: Dimensions of the deskewed output + deskew_vol_shape: Tuple[int, ...] = Field(init_var=False, default=None) + + deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False, default=None) + + @property + def dims(self): + return self.data.dims + + @validator("data", pre=True) + def reshaping(cls, v: Any): + # This allows a user to pass in any array-like object and have it + # converted and reshaped appropriately + array = DataArray(v) + if not set(array.dims).issuperset({"X", "Y", "Z"}): + raise ValueError("The input array must at least have XYZ coordinates") + if "T" not in array.dims: + array = array.expand_dims("T") + if "C" not in array.dims: + array = array.expand_dims("C") + return array.transpose("T", "C", "Z", "Y", "X") + + def get_3d_slice(self) -> DataArray: + return self.data.sel(C=0, T=0) + @root_validator(pre=True) def set_deskew(cls, values: dict) -> dict: """ Sets the default deskew shape values if the user has not provided them """ # process the file to get shape of final deskewed image - data = values["data"] + data: DataArray = cls.reshaping(values["data"]) if values.get('deskew_vol_shape') is None: if values.get('deskew_affine_transform') is None: # If neither has been set, calculate them ourselves - values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(values["data"], values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) + values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(data.sel(C=0, T=0).to_numpy(), values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) else: raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") return values @@ -249,7 +267,7 @@ def disjoint_time_range(cls, v: range, values: dict): """ Validates that the time range is within the range of channels in our array """ - max_time = values["dims"].T + max_time = values["data"].sizes["T"] if v.start < 0: raise ValueError("The lowest valid start value is 0") if v.stop > max_time: @@ -261,7 +279,7 @@ def disjoint_channel_range(cls, v: range, values: dict): """ Validates that the channel range is within the range of channels in our array """ - max_channel = values["dims"].T + max_channel = values["data"].sizes["C"] if v.start < 0: raise ValueError("The lowest valid start value is 0") if v.stop > max_channel: @@ -270,13 +288,15 @@ def disjoint_channel_range(cls, v: range, values: dict): @validator("channel_range") def channel_range_subset(cls, v: range, values: dict): - if min(v) < 0 or max(v) > values["dims"].C: + if min(v) < 0 or max(v) > values["data"].sizes["C"]: raise ValueError("The output channel range must be a subset of the total available channels") + return v @validator("time_range") def time_range_subset(cls, v: range, values: dict): - if min(v) < 0 or max(v) > values["dims"].T: - raise ValueError("The output time range must be a subset of the total available time points") + if min(v) < 0 or max(v) > values["data"].sizes["T"]: + raise ValueError("The output time range must be a subset of the total available time points") + return v # Hack to ensure that .skew_dir behaves identically to .skew @property @@ -343,12 +363,12 @@ def deconv_enabled(self) -> bool: @property def time(self) -> int: """Number of time points""" - return self.dims.T + return self.data.sizes["T"] @property def channels(self) -> int: """Number of channels""" - return self.dims.C + return self.data.sizes["C"] @property def new_dz(self): @@ -358,17 +378,19 @@ def __post_init__(self): logger.info(f"Channels: {self.channels}, Time: {self.time}") logger.info("If channel and time need to be swapped, you can enforce this by choosing 'Last dimension is channel' when initialising the plugin") - def slice_data(self, time: int, channel: int) -> ArrayLike: + def slice_data(self, time: int, channel: int) -> DataArray: if time > self.time: raise ValueError("time is out of range") if channel > self.channels: raise ValueError("channel is out of range") - if len(self.dims.shape) == 3: + return self.data.sel(T=time, C=channel) + + if len(self.data.shape) == 3: return self.data - elif len(self.dims.shape) == 4: + elif len(self.data.shape) == 4: return self.data[time, :, :, :] - elif len(self.dims.shape) == 5: + elif len(self.data.shape) == 5: return self.data[time, channel, :, :, :] raise Exception("Lattice data must be 3-5 dimensions") @@ -511,8 +533,7 @@ def process(self) -> ProcessedSlices: ) class AicsLatticeParams(TypedDict): - data: DaskArray - dims: Dimensions + data: DataArray physical_pixel_sizes: DefinedPixelSizes def lattice_params_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None)) -> AicsLatticeParams: diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 4bba9e3..398827f 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -1,80 +1,32 @@ -import os -import sys -import yaml -import numpy as np +# Enable Logging +import logging from pathlib import Path -import dask.array as da -import pandas as pd -from typing import Any, Callable, Iterable, Literal, Optional, Sequence, Tuple, Union, List, TypeVar, Type, cast -from enum import Enum, auto +from typing import Union +import numpy as np +from lls_core.lattice_data import LatticeData +from lls_core.workflow import import_workflow_modules +from magicclass import MagicTemplate, field, magicclass, set_options from magicclass.wrappers import set_design -from magicgui.widgets import Widget -from magicclass import magicclass, field, vfield, set_options, MagicTemplate, FieldGroup, Icon -from magicclass.fields import MagicValueField, MagicField -from magicclass.widgets import CollapsibleContainer, CheckBox, RangeSlider, ComboBox -from magicclass.widgets.containers import ContainerWidget, _VCollapsibleContainer, wrap_container -from magicclass.utils import click -from qtpy.QtCore import Qt -from qtpy.QtGui import QIcon -from qtpy.QtWidgets import QTabWidget - -from napari.layers import Layer, Shapes -from napari.types import ImageData from napari import Viewer -from napari_lattice.icons import RED, GREEN, GREY - -from tqdm import tqdm - +from napari.layers import Shapes +from napari_lattice.fields import ( + CroppingFields, + DeconvolutionFields, + DeskewFields, + OutputFields, + WorkflowFields, +) +from napari_lattice.icons import GREY from napari_workflows import Workflow, WorkflowManager from napari_workflows._io_yaml_v1 import load_workflow - -from lls_core import DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection -from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes, LatticeData - -from pydantic import ValidationError -from napari_lattice.reader import lattice_params_from_napari -from napari_lattice.fields import DeskewFields, CroppingFields, DeconvolutionFields, WorkflowFields, OutputFields, exception_to_html -from napari_lattice.icons import GREY - -from strenum import StrEnum - -from lls_core.workflow import import_workflow_modules -# Enable Logging -import logging +from qtpy.QtCore import Qt +from qtpy.QtGui import QIcon +from qtpy.QtWidgets import QTabWidget logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -# MagicClassType = TypeVar("MagicClassType") -# def magicclass(*args, **kwargs) -> Callable[[MagicClassType], MagicClassType]: -# return original_magicclass(*args, **kwargs) - -# class HideableContents(MagicTemplate): -# # fields_enabled = vfield(False, label="Enabled") - -# # @fields_enabled.connect -# def _enabled_changed(self) -> None: -# if self.fields_enabled: -# logger.info(f"{self.__class__} Activated") -# for child in self.__magicclass_children__: -# child.visible = True -# child.enabled = True -# else: -# logger.info(f"{self.__class__} Deactivated") -# for child in self.__magicclass_children__: -# child.visible = False -# child.enabled = False - - -# def validate_tab(parent: MagicTemplate, fields: FieldGroup, index: int): -# tab_widget: QTabWidget = parent._widget._tab_widget -# try: -# fields._make_model() -# tab_widget.setTabIcon(index, QIcon(GREEN)) -# except ValidationError: -# tab_widget.setTabIcon(index, QIcon(RED)) - class LlszTemplate(MagicTemplate): @property def llsz_parent(self) -> "LLSZWidget": @@ -86,7 +38,6 @@ def parent_viewer(mc: MagicTemplate) -> Viewer: raise Exception("This function can only be used when inside of a Napari viewer") return mc.parent_viewer - @magicclass(widget_type="split") class LLSZWidget(LlszTemplate): open_file: bool = False @@ -103,16 +54,19 @@ def _check_validity(self) -> bool: return False def _make_model(self) -> LatticeData: - # deskew = self.LlszMenu.WidgetContainer.DeskewWidget - # output = self.LlszMenu.WidgetContainer.OutputWidget - # deconv = self.LlszMenu.WidgetContainer.DeconvolutionWidget - # crop = self.LlszMenu.WidgetContainer.CroppingWidget - # workflow = self.LlszMenu.WidgetContainer.WorkflowWidget - - # TODO: fix + deskew_args = self.LlszMenu.WidgetContainer.deskew_fields._get_kwargs() + output_args = self.LlszMenu.WidgetContainer.output_fields._make_model() return LatticeData( - **self.LlszMenu.WidgetContainer.deskew_fields._make_model().dict(), - **self.LlszMenu.WidgetContainer.output_fields._make_model().dict(), + data=deskew_args["data"], + angle=deskew_args["angle"], + channel_range=output_args.channel_range, + time_range=output_args.time_range, + save_dir=output_args.save_dir, + # We let the user specify a prefix, but if they don't, we can use the default + save_name=output_args.save_name or deskew_args["save_name"] , + save_type=output_args.save_type, + physical_pixel_sizes=deskew_args["physical_pixel_sizes"], + skew=deskew_args["skew"], workflow=self.LlszMenu.WidgetContainer.workflow_fields._make_model(), deconvolution=self.LlszMenu.WidgetContainer.deconv_fields._make_model(), crop=self.LlszMenu.WidgetContainer.cropping_fields._make_model() @@ -123,9 +77,6 @@ class LlszMenu(LlszTemplate): main_heading = field("

    Napari Lattice: Visualization & Analysis

    ", widget_type="Label") heading1 = field("Drag and drop an image file onto napari.", widget_type="Label") - # Pycudadecon library for deconvolution - # options={"enabled": True}, - # Tabbed Widget container to house all the widgets @magicclass(widget_type="tabbed", name="Functions", labels=False) @@ -139,255 +90,10 @@ def __post_init__(self): field._validate() deskew_fields = DeskewFields(name = "1. Deskew") - # @deskew_fields.connect - # def _deskew_changed(self): - # validate_tab(self, self.deskew_fields, 0) - deconv_fields = DeconvolutionFields(name = "2. Deconvolution") - # @deconv_fields.connect - # def _deconv_changed(self): - # validate_tab(self, self.deconv_fields, 1) - cropping_fields = CroppingFields(name = "3. Crop") - # @cropping_fields.connect - # def _cropping_changed(self): - # validate_tab(self, 2) - workflow_fields = WorkflowFields(name = "4. Workflow") - # @workflow_fields.connect - # def _workflow_changed(self): - # validate_tab(self, 3) - output_fields = OutputFields(name = "5. Output") - # @output_fields.connect - # def _output_changed(self): - # validate_tab(self, 4) - - # @magicclass(name="1. Deskew") - # class DeskewWidget(MagicTemplate): - # img_layer = field(List[Layer]).with_options(label = "Image Layer", layout="vertical", value=[]) - # pixel_sizes = field(Tuple[float, float, float]).with_options( - # value=(DefinedPixelSizes.get_default("X"), DefinedPixelSizes.get_default("Y"), DefinedPixelSizes.get_default("Z")), - # label="Pixel Sizes (XYZ)" - # ) - # angle = field(LatticeData.get_default("angle")).with_options(value=LatticeData.get_default("angle"), label="Skew Angle") - # device = field(str).with_choices(cle.available_device_names()).with_options(label="Graphics Device") - # merge_all_channels = field(False).with_options(label="Merge all Channels") - # dimension_order = field(str).with_options(value=LastDimensionOptions.Metadata.value).with_choices([it.value for it in LastDimensionOptions]).with_options(label="Dimension Order") - # skew_dir = field(DeskewDirection.Y).with_options(label = "Skew Direction") - - # def _img_changed(self): - # deskew = get_child(self, self.DeskewWidget) - # output = get_child(self, self.OutputWidget) - # output.channel_range.options["max"] = deskew.img_layer.value._dims_displayed - ### - # Deconvolution - ### - # deconvolution = vfield(bool, name="Use Deconvolution").with_options(value=False) - # deconv_widget = DeconvolutionWidget() - # deconv_widget.enabled = False - # deconv_widget.visible = False - - # @deconvolution.connect - # def _set_decon(self) -> None: - # if self.deconvolution: - # logger.info("Deconvolution Activated") - # # Enable deconvolutio by using the saved parameters - # # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv - # self.deconv_widget.enabled = True - # self.deconv_widget.visible = True - # else: - # logger.info("Deconvolution Disabled") - # # self.llsz_parent.lattice.deconvolution = None - # self.deconv_widget.enabled = False - # self.deconv_widget.visible = False - - # ### - # # Cropping - # ### - - # cropping_enabled = vfield(bool, name="Use Cropping").with_options(value=False) - # crop_widget = CroppingWidget() - # crop_widget.enabled = False - # crop_widget.visible = False - - # @cropping_enabled.connect - # def _set_crop(self) -> None: - # if self.cropping_enabled: - # logger.info("Cropping Activated") - # # Enable deconvolutio by using the saved parameters - # # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv - # self.crop_widget.enabled = True - # self.crop_widget.visible = True - # else: - # logger.info("Deconvolution Disabled") - # # self.llsz_parent.lattice.deconvolution = None - # self.crop_widget.enabled = False - # self.crop_widget.visible = False - - # ### - # # Workflow - # ### - # workflow_enabled = vfield(bool, name="Use Workflow").with_options(value=False) - # workflow_widget = WorkflowWidget() - # workflow_widget.enabled = False - # workflow_widget.visible = False - - # @workflow_enabled.connect - # def _set_workflow(self) -> None: - # if self.workflow_enabled: - # logger.info("Workflow Activated") - # # Enable deconvolutio by using the saved parameters - # # self.llsz_parent.lattice.deconvolution = self.llsz_parent.deconv - # self.workflow_widget.enabled = True - # self.workflow_widget.visible = True - # else: - # logger.info("Deconvolution Disabled") - # # self.llsz_parent.lattice.deconvolution = None - # self.workflow_widget.enabled = False - # self.workflow_widget.visible = False - - # @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Initialize Plugin", max_height=75, font_size=13) - # @set_options(pixel_size_dx={"widget_type": "FloatSpinBox", "value": 0.1449922, "step": 0.000000001}, - # pixel_size_dy={"widget_type": "FloatSpinBox", - # "value": 0.1449922, "step": 0.000000001}, - # pixel_size_dz={"widget_type": "FloatSpinBox", - # "value": 0.3, "step": 0.000000001}, - # angle={"widget_type": "FloatSpinBox", - # "value": 30, "step": 0.1}, - # select_device={"widget_type": "ComboBox", "choices": cle.available_device_names( - # ), "value": cle.available_device_names()[0]}, - # last_dimension_channel={"widget_type": "ComboBox", - # "label": "Set Last dimension (channel/time)", "tooltip": "If the last dimension is initialised incorrectly, you can assign it as either channel/time"}, - # merge_all_channel_layers={"widget_type": "CheckBox", "value": True, "label": "Merge all napari layers as channels", - # "tooltip": "Use this option if the channels are in separate layers. napari-lattice requires all channels to be in same layer"}, - # skew_dir={"widget_type": "ComboBox", "choices": DeskewDirection, "value": DeskewDirection.Y, - # "label": "Direction of skew (Y or X)", "tooltip": "Skew direction when image is acquired. Ask your microscopist for details"}, - # set_logging={"widget_type": "ComboBox", "choices": Log_Levels, "value": Log_Levels.INFO, - # "label": "Log Level", "tooltip": "Only use for debugging. Leave it as INFO for regular operation"} - # ) - # def Choose_Image_Layer(self, - # img_layer: Layer, - # pixel_size_dx: float = 0.1449922, - # pixel_size_dy: float = 0.1449922, - # pixel_size_dz: float = 0.3, - # angle: float = 30, - # select_device: str = cle.available_device_names()[ - # 0], - # last_dimension_channel: LastDimensionOptions = "", - # merge_all_channel_layers: bool = False, - # skew_dir: DeskewDirection=DeskewDirection.Y, - # set_logging: Log_Levels=Log_Levels.INFO): - - # logger.setLevel(set_logging.value) - # config.log_level = set_logging.value - # logger.info(f"Logging set to {set_logging}") - # logger.info("Using existing image layer") - - # # Select device for processing - # cle.select_device(select_device) - - # # merge all napari image layers as one multidimensional image - # if merge_all_channel_layers: - # from napari.layers.utils.stack_utils import images_to_stack - # # get list of napari layers as a list - # layer_list = list(self.parent_viewer.layers) - # # if more than one layer - # if len(layer_list) > 1: - # # convert the list of images into a stack - # new_layer = images_to_stack(layer_list) - # # select all current layers - # self.parent_viewer.layers.select_all() - # # remove selected layers - # self.parent_viewer.layers.remove_selected() - # # add the new composite image layer - # self.parent_viewer.add_layer(new_layer) - # img_layer = new_layer - - # self.llsz_parent.lattice = lattice_from_napari( - # img=img_layer, - # last_dimension=None if last_dimension_channel == LastDimensionOptions.get_from_metadata else last_dimension_channel, - # angle=angle, - # skew=skew_dir, - # physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz), - # # deconvolution = DeconvolutionParams() - # ) - # # flag for ensuring a file has been opened and plugin initialised - # self.llsz_parent.open_file = True - - # logger.info( - # f"Pixel size (ZYX) in microns: {self.llsz_parent.lattice.dz,self.llsz_parent.lattice.dy,self.llsz_parent.lattice.dx}") - # logger.info( - # f"Dimensions of image layer (ZYX): {list(self.llsz_parent.lattice.data.shape[-3:])}") - # logger.info( - # f"Dimensions of deskewed image (ZYX): {self.llsz_parent.lattice.deskew_vol_shape}") - # logger.info( - # f"Deskewing angle is: {self.llsz_parent.lattice.angle}") - # logger.info( - # f"Deskew Direction: {self.llsz_parent.lattice.skew}") - # # Add dimension labels correctly - # # if channel, and not time - # if self.llsz_parent.lattice.time == 0 and (last_dimension_channel or self.llsz_parent.lattice.channels > 0): - # self.parent_viewer.dims.axis_labels = ('Channel', "Z", "Y", "X") - # # if no channel, but has time - # elif self.llsz_parent.lattice.channels == 0 and self.llsz_parent.lattice.time > 0: - # self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") - # # if it has channels - # elif self.llsz_parent.lattice.channels > 1: - # # If merge to stack is used, channel slider goes to the bottom - # if int(self.parent_viewer.dims.dict()["range"][0][1]) == self.llsz_parent.lattice.channels: - # self.parent_viewer.dims.axis_labels = ('Channel', "Time", "Z", "Y", "X") - # else: - # self.parent_viewer.dims.axis_labels = ('Time', "Channel", "Z", "Y", "X") - # # if channels initialized by aicsimagio, then channels is 1 - # elif self.llsz_parent.lattice.channels == 1 and self.llsz_parent.lattice.time > 1: - # self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") - - # logger.info(f"Initialised") - # self["Choose_Image_Layer"].background_color = "green" - # self["Choose_Image_Layer"].text = "Plugin Initialised" - - - # @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) - # @set_options(header=dict(widget_type="Label", label="

    Enter path to the PSF images

    "), - # psf_ch1_path={"widget_type": "FileEdit", - # "label": "Channel 1:"}, - # psf_ch2_path={"widget_type": "FileEdit", - # "label": "Channel 2"}, - # psf_ch3_path={"widget_type": "FileEdit", - # "label": "Channel 3"}, - # psf_ch4_path={"widget_type": "FileEdit", - # "label": "Channel 4"}, - # device_option={ - # "widget_type": "ComboBox", "label": "Choose processing device", "choices": DeconvolutionChoice}, - # no_iter={ - # "widget_type": "SpinBox", "label": "No of iterations (Deconvolution)", "value": 10, "min": 1, "max": 50, "step": 1} - # ) - # def deconvolution_gui(self, - # header: str, - # psf_ch1_path: Path, - # psf_ch2_path: Path, - # psf_ch3_path: Path, - # psf_ch4_path: Path, - # device_option: DeconvolutionChoice, - # no_iter: int): - # """GUI for Deconvolution button""" - # # Force deconvolution to be true if we do this - # if not self.llsz_parent.lattice.deconvolution: - # raise Exception("Deconvolution is set to False. Tick the box to activate deconvolution.") - # self.llsz_parent.deconv.decon_processing = device_option - # self.llsz_parent.deconv.psf = list(read_psf([ - # psf_ch1_path, - # psf_ch2_path, - # psf_ch3_path, - # psf_ch4_path, - # ], - # device_option, - # lattice_class=self.llsz_parent.lattice - # )) - # self.llsz_parent.deconv.psf_num_iter = no_iter - # self["deconvolution_gui"].background_color = "green" - # self["deconvolution_gui"].text = "PSFs added" @set_options(header=dict(widget_type="Label", label="

    Preview Deskew

    "), time=dict(label="Time:", max=2**15), @@ -415,622 +121,8 @@ def preview(self, header:str, time: int, channel: int): @set_design(text="Save") def save(self): - self._make_model_friendly() - - - # @magicclass(widget_type="collapsible") - # class Preview: - # @magicgui(header=dict(widget_type="Label", label="

    Preview Deskew

    "), - # time=dict(label="Time:", max=2**15), - # channel=dict(label="Channel:"), - # call_button="Preview") - # def Preview_Deskew(self, - # header: str, - # time: int, - # channel: int, - # img_data: ImageData): - # """ - # Preview deskewed data for a single timepoint and channel - - # """ - # _Preview(LLSZWidget, - # self, - # time, - # channel, - # img_data) - - # Tabbed Widget container to house all the widgets - # @magicclass(widget_type="tabbed", name="Functions") - # class WidgetContainer(LlszTemplate): - - # @magicclass(name="Deskew", widget_type="scrollable", properties={"min_width": 100}) - # class DeskewWidget(LlszTemplate): - - # @magicgui(header=dict(widget_type="Label", label="

    Deskew and Save

    "), - # time_start=dict(label="Time Start:", max=2**20), - # time_end=dict(label="Time End:", value=1, max=2**20), - # ch_start=dict(label="Channel Start:"), - # ch_end=dict(label="Channel End:", value=1), - # save_as_type={ - # "label": "Save as filetype:", "choices": SaveFileType, "value": SaveFileType.h5}, - # save_path=dict(mode='d', label="Directory to save"), - # call_button="Save") - # def Deskew_Save(self, - # header: str, - # time_start: int, - # time_end: int, - # ch_start: int, - # ch_end: int, - # save_as_type: str, - # save_path: Path = Path(history.get_save_history()[0])): - # """ Widget to Deskew and Save Data""" - # _Deskew_Save(LLSZWidget, - # time_start, - # time_end, - # ch_start, - # ch_end, - # save_as_type, - # save_path) - - # @magicclass(name="Crop and Deskew", widget_type="scrollable") - # class CropWidget(LlszTemplate): - - # # add function for previewing cropped image - # @magicclass(name="Cropping Preview", widget_type="scrollable", properties={ - # "min_width": 100, - # "shapes_layer": Shapes - # }) - # class Preview_Crop_Menu(LlszTemplate): - - # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") - # @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) - # def activate_cropping(self): - # self.llsz_parent.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', - # face_color=[1, 1, 1, 0], name="Cropping BBOX layer") - # # TO select ROIs if needed - # self.llsz_parent.shapes_layer.mode = "SELECT" - # self["activate_cropping"].text = "Cropping layer active" - # self["activate_cropping"].background_color = "green" - - # heading2 = field("You can either import ImageJ ROI (.zip) files or manually define ROIs using the shape layer", widget_type="Label") - - # @click(enabled=False) - # def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])) -> None: - # logger.info(f"Opening{path}") - # roi_list = read_imagej_roi(str(path)) - # # convert to canvas coordinates - # self.find_ancestor(LLSZWidget) - # roi_list = (np.array(roi_list) * self.llsz_parent.lattice.dy).tolist() - # self.llsz_parent.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', face_color=[1, 1, 1, 0]) - - # time_crop = field(int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") - # chan_crop = field(int, options={"min": 0, "step": 1}, name="Channels") - # heading_roi = field("If there are multiple ROIs, select the ROI before clicking button below", widget_type="Label") - # #roi_idx = field(int, options={"min": 0, "step": 1}, name="ROI number") - - # @click(enabled=False) - # def Crop_Preview(self, roi_layer: ShapesData): - - # if not roi_layer: - # raise Exception("No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs.") - # # TODO: Add assertion to check if bbox layer or coordinates - - # # Slice out the image of interest to preview - # time = self.time_crop.value - # channel = self.chan_crop.value - # if time >= self.llsz_parent.lattice.time: - # raise ValueError("Time is out of range") - # if time >= self.llsz_parent.lattice.time: - # raise ValueError("Channel is out of range") - # logger.info(f"Using channel {channel} and time {time}") - - # # if only one roi drawn, use the first ROI for cropping - # if len(self.llsz_parent.shapes_layer.selected_data) == 0: - # raise Exception("Please select an ROI") - - # roi_idx = list(self.llsz_parent.shapes_layer.selected_data)[0] - - # # As the original image is scaled, the coordinates are in microns, so we need to convert - # # roi from micron to canvas/world coordinates - # roi_choice = [x/self.llsz_parent.lattice.dy for x in roi_layer[roi_idx]] - # logger.info(f"Previewing ROI {roi_idx}") - - # # crop - - # # Set the deconvolution options - # if self.llsz_parent.deconvolution: - # if not self.llsz_parent.lattice.psf or not self.llsz_parent.lattice.psf_num_iter or not self.llsz_parent.lattice.decon_processing: - # raise Exception( - # "PSF fields should be set by this point!") - # logger.info( - # f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") - # decon_kwargs = dict( - # decon_processing=self.llsz_parent.lattice.decon_processing.value, - # psf=self.llsz_parent.lattice.psf[channel], - # num_iter=self.llsz_parent.lattice.psf_num_iter - # ) - # else: - # decon_kwargs = dict() - - # crop_roi_vol_desk = cle.pull( - # crop_volume_deskew( - # original_volume=np.array(self.llsz_parent.lattice.data[time, channel, ...]), - # roi_shape=roi_choice, - # angle_in_degrees=self.llsz_parent.angle_value, - # voxel_size_x=self.llsz_parent.lattice.dx, - # voxel_size_y=self.llsz_parent.lattice.dy, - # voxel_size_z=self.llsz_parent.lattice.dz, - # deconvolution=self.llsz_parent.deconvolution, - # # Option for entering custom z start value? - # z_start=0, - # z_end=self.llsz_parent.lattice.deskew_vol_shape[0], - # skew_dir=self.llsz_parent.skew_dir, - # **decon_kwargs - # ).astype(self.llsz_parent.lattice.data.dtype) - # ) - - # # get array back from gpu or addding cle array to napari can throw errors - # image = next(self.llsz_parent.lattice.process()) - - # scale = ( - # self.llsz_parent.lattice.new_dz, - # self.llsz_parent.lattice.dy, - # self.llsz_parent.lattice.dx - # ) - # self.parent_viewer.add_image( - # crop_roi_vol_desk, scale=scale) - - # @magicclass(name="Crop and Save Data") - # class CropSaveData(LlszTemplate): - # @magicgui(header=dict(widget_type="Label", label="

    Crop and Save Data

    "), - # time_start=dict(label="Time Start:"), - # time_end=dict(label="Time End:", value=1), - # ch_start=dict(label="Channel Start:"), - # ch_end=dict(label="Channel End:", value=1), - # save_as_type={ - # "label": "Save as filetype:", "choices": SaveFileType}, - # save_path=dict(mode='d', label="Directory to save ")) - # def Crop_Save(self, - # header: str, - # time_start: int, - # time_end: int, - # ch_start: int, - # ch_end: int, - # save_as_type: SaveFileType, - # roi_layer_list: ShapesData, - # save_path: Path = Path(history.get_save_history()[0])): - - # if not roi_layer_list: - # logger.error( - # "No coordinates found or cropping. Initialise shapes layer and draw ROIs.") - # else: - # if not self.llsz_parent.open_file: - # raise Exception("Image not initialised") - - # check_dimensions(time_start, time_end, ch_start, ch_end, self.llsz_parent.lattice.channels, self.llsz_parent.lattice.time) - - # angle = self.llsz_parent.lattice.angle - # dx = self.llsz_parent.lattice.dx - # dy = self.llsz_parent.lattice.dy - # dz = self.llsz_parent.lattice.dz - - # # get image data - # img_data = self.llsz_parent.lattice.data - # # Get shape of deskewed image - # deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape - # deskewed_volume = da.zeros(deskewed_shape) - # z_start = 0 - # z_end = deskewed_shape[0] - - # logger.info("Cropping and saving files...") - - # # necessary when scale is used for napari.viewer.add_image operations - # roi_layer_list = ShapesData([x/self.llsz_parent.lattice.dy for x in roi_layer_list]) - - # for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): - # # pass arguments for save tiff, callable and function arguments - # logger.info("Processing ROI ", idx) - # # pass parameters for the crop_volume_deskew function - - # save_img(vol=img_data, - # func=crop_volume_deskew, - # time_start=time_start, - # time_end=time_end, - # channel_start=ch_start, - # channel_end=ch_end, - # save_name_prefix="ROI_" + str(idx), - # save_path=save_path, - # save_file_type=save_as_type, - # save_name=self.llsz_parent.lattice.save_name, - # dx=dx, - # dy=dy, - # dz=dz, - # angle=angle, - # deskewed_volume=deskewed_volume, - # roi_shape=roi_layer, - # angle_in_degrees=angle, - # z_start=z_start, - # z_end=z_end, - # voxel_size_x=dx, - # voxel_size_y=dy, - # voxel_size_z=dz, - # LLSZWidget=self.llsz_parent - # ) - - # logger.info( - # f"Cropping and Saving Complete -> {save_path}") - - # @magicclass(name="Workflow", widget_type="scrollable") - # class WorkflowWidget(LlszTemplate): - - # @magicclass(name="Preview Workflow", widget_type="scrollable") - # class PreviewWorkflow(LlszTemplate): - # #time_preview= field(int, options={"min": 0, "step": 1}, name="Time") - # #chan_preview = field(int, options={"min": 0, "step": 1}, name="Channels") - # @magicgui(header=dict(widget_type="Label", label="

    Preview Workflow

    "), - # time_preview=dict(label="Time:", max=2**20), - # chan_preview=dict(label="Channel:"), - # get_active_workflow=dict( - # widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), - # workflow_path=dict( - # mode='r', label="Load custom workflow (.yaml/yml)"), - # Use_Cropping=dict( - # widget_type="Checkbox", label="Crop Data", value=False), - # #custom_module=dict(widget_type="Checkbox",label="Load custom module (looks for *.py files in the workflow directory)",value = False), - # call_button="Apply and Preview Workflow") - # def Workflow_Preview(self, - # header: str, - # time_preview: int, - # chan_preview: int, - # get_active_workflow: bool, - # Use_Cropping: bool, - # roi_layer_list: ShapesData, - # workflow_path: Path = Path.home()): - # """ - # Apply napari_workflows to the processing pipeline - # User can define a pipeline which can be inspected in napari workflow inspector - # and then execute it by ticking the get active workflow checkbox, - # OR - # Use a predefined workflow - - # In both cases, if deskewing is not present as first step, it will be added on - # and rest of the task will be made followers - # """ - # logger.info("Previewing deskewed channel and time with workflow") - # user_workflow = get_workflow(self.parent_viewer if get_active_workflow else workflow_path) - - # # when using fields, self.time_preview.value - # if time_preview >= self.llsz_parent.lattice.time: - # raise ValueError("Time is out of range") - # if chan_preview >= self.llsz_parent.lattice.channels: - # raise ValueError("Channel is out of range") - - # time = time_preview - # channel = chan_preview - - # # to access current time and channel and pass it to workflow file - # config.channel = channel - # config.time = time - - # logger.info(f"Processing for Time: {time} and Channel: {channel}") - - # logger.info("Workflow to be executed:") - # logger.info(user_workflow) - # # Execute workflow - # processed_vol = user_workflow.get(task_name_last) - - # # check if a measurement table (usually a dictionary or list) or a tuple with different data types - # # The function below saves the tables and adds any images to napari window - # if type(processed_vol) in [dict, list, tuple]: - # if (len(processed_vol) > 1): - # df = pd.DataFrame() - # for idx, i in enumerate(processed_vol): - # df_temp = process_custom_workflow_output( - # i, parent_dir, idx, LLSZWidget, self, channel, time, preview=True) - # final_df = pd.concat([df, df_temp]) - # # append dataframes from every loop and have table command outside loop? - # # TODO: Figure out why table is not displaying - # from napari_spreadsheet import _widget - # table_viewer = _widget.TableViewerWidget( - # show=True) - # table_viewer.add_spreadsheet(final_df) - # # widgets.Table(value=final_df).show() - - # else: - # # add image to napari window - # # TODO: check if its an image napari supports? - # process_custom_workflow_output( - # processed_vol, parent_dir, 0, LLSZWidget, self, channel, time) - - # print("Workflow complete") - - # @magicgui(header=dict(widget_type="Label", label="

    Apply Workflow and Save Output

    "), - # time_start=dict(label="Time Start:", max=2**20), - # time_end=dict(label="Time End:", - # value=1, max=2**20), - # ch_start=dict(label="Channel Start:"), - # ch_end=dict(label="Channel End:", value=1), - # Use_Cropping=dict( - # widget_type="Checkbox", label="Crop Data", value=False), - # get_active_workflow=dict( - # widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), - # workflow_path=dict( - # mode='r', label="Load custom workflow (.yaml/yml)"), - # save_as_type={ - # "label": "Save as filetype:", "choices": SaveFileType}, - # save_path=dict( - # mode='d', label="Directory to save "), - # #custom_module=dict(widget_type="Checkbox",label="Load custom module (same dir as workflow)",value = False), - # call_button="Apply Workflow and Save Result") - # def Apply_Workflow_and_Save(self, - # header: str, - # time_start: int, - # time_end: int, - # ch_start: int, - # ch_end: int, - # Use_Cropping: bool, - # roi_layer_list: ShapesData, - # get_active_workflow: bool = False, - # workflow_path: Path = Path.home(), - # save_as_type: str = SaveFileType.tiff, - # save_path: Path = Path(history.get_save_history()[0])): - # """ - # Apply a user-defined analysis workflow using napari-workflows - - # Args: - # time_start (int): Start Time - # time_end (int): End Time - # ch_start (int): Start Channel - # ch_end (int): End Channel - # Use_Cropping (_type_): Use cropping based on ROIs in the shapes layer - # roi_layer_list (ShapesData): Shapes layer to use for cropping; can be a list of shapes - # get_active_workflow (bool, optional): Gets active workflow in napari. Defaults to False. - # workflow_path (Path, optional): User can also choose a custom workflow defined in a yaml file. - # save_path (Path, optional): Path to save resulting data - # """ - # if not self.llsz_parent.open_file: - # raise Exception("Image not initialised") - - # check_dimensions(time_start, time_end, ch_start, ch_end, - # self.llsz_parent.lattice.channels, self.llsz_parent.lattice.time) - - # # Get parameters - # angle = self.llsz_parent.lattice.angle - # dx = self.llsz_parent.lattice.dx - # dy = self.llsz_parent.lattice.dy - # dz = self.llsz_parent.lattice.dz - - # user_workflow = get_workflow(self.parent_viewer if get_active_workflow else workflow_path) - - # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - # user_workflow) - # logger.info(f"{input_arg_first=}, {input_arg_last=}, {first_task_name=}, {last_task_name=}") - # logger.info(f"Workflow loaded: {user_workflow}") - - # vol = self.llsz_parent.lattice.data - # task_name_start = first_task_name[0] - - # try: - # task_name_last = last_task_name[0] - # except IndexError: - # task_name_last = task_name_start - - # # variables to hold task name, initialize it as None - # # if gpu, set otf_path, otherwise use psf - # psf = None - # otf_path = None - - # if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - # #otf_path = "otf_path" - # psf_arg = "psf" - # psf = self.llsz_parent.lattice.psf - # else: - # psf_arg = "psf" - # psf = self.llsz_parent.lattice.psf - # # if cropping, set that as first task - - # if Use_Cropping: - # # convert Roi pixel coordinates to canvas coordinates - # # necessary only when scale is used for napari.viewer.add_image operations - # roi_layer_list = [x/self.llsz_parent.lattice.dy for x in roi_layer_list] - - # deskewed_shape = self.llsz_parent.lattice.deskew_vol_shape - # deskewed_volume = da.zeros(deskewed_shape) - # z_start = 0 - # z_end = deskewed_shape[0] - # roi = "roi" - # volume = "volume" - # # Check if decon ticked, if so set as first and crop as second? - - # # Create workflow for cropping and deskewing - # # volume and roi used will be set dynamically - # user_workflow.set("crop_deskew_image", crop_volume_deskew, - # original_volume=volume, - # deskewed_volume=deskewed_volume, - # roi_shape=roi, - # angle_in_degrees=angle, - # voxel_size_x=dx, - # voxel_size_y=dy, - # voxel_size_z=dz, - # z_start=z_start, - # z_end=z_end, - # deconvolution=self.llsz_parent.deconvolution.value, - # decon_processing=self.llsz_parent.lattice.decon_processing, - # psf=psf_arg, - # skew_dir=self.llsz_parent.skew_dir) - - # # change the first task so it accepts "crop_deskew as input" - # new_task = modify_workflow_task( - # old_arg=input_arg_first, task_key=task_name_start, new_arg="crop_deskew_image", workflow=user_workflow) - # user_workflow.set(task_name_start, new_task) - - # for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): - # print("Processing ROI ", idx) - # user_workflow.set(roi, roi_layer) - # save_img_workflow(vol=vol, - # workflow=user_workflow, - # input_arg=volume, - # first_task="crop_deskew_image", - # last_task=task_name_last, - # time_start=time_start, - # time_end=time_end, - # channel_start=ch_start, - # channel_end=ch_end, - # save_file_type=save_as_type, - # save_path=save_path, - # #roi_layer = roi_layer, - # save_name_prefix="ROI_" + \ - # str(idx), - # save_name=self.llsz_parent.lattice.save_name, - # dx=dx, - # dy=dy, - # dz=dz, - # angle=angle, - # deconvolution=self.llsz_parent.deconvolution.value, - # decon_processing=self.llsz_parent.lattice.decon_processing, - # otf_path=otf_path, - # psf_arg=psf_arg, - # psf=psf) - - # # IF just deskewing and its not in the tasks, add that as first task - # elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): - # input = "input" - # # add task to the workflow - # user_workflow.set("deskew_image", - # self.llsz_parent.deskew_func, - # input_image=input, - # angle_in_degrees=angle, - # voxel_size_x=dx, - # voxel_size_y=dy, - # voxel_size_z=dz, - # linear_interpolation=True) - # # Set input of the workflow to be from deskewing - # # change workflow task starts from is "deskew_image" and - # new_task = modify_workflow_task( - # old_arg=input_arg_first, task_key=task_name_start, new_arg="deskew_image", workflow=user_workflow) - # user_workflow.set(task_name_start, new_task) - - # # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - # if self.llsz_parent.deconvolution: - # psf = "psf" - # otf_path = "otf_path" - # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - # user_workflow) - - # if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - # user_workflow.set("deconvolution", - # pycuda_decon, - # image=input, - # psf=psf_arg, - # dzdata=self.llsz_parent.lattice.dz, - # dxdata=self.llsz_parent.lattice.dx, - # dzpsf=self.llsz_parent.lattice.dz, - # dxpsf=self.llsz_parent.lattice.dx, - # num_iter=self.llsz_parent.lattice.psf_num_iter) - # # user_workflow.set(input_arg_first,"deconvolution") - # else: - # user_workflow.set("deconvolution", - # skimage_decon, - # vol_zyx=input, - # psf=psf_arg, - # num_iter=self.llsz_parent.lattice.psf_num_iter, - # clip=False, - # filter_epsilon=0, - # boundary='nearest') - # # modify the user workflow so "deconvolution" is accepted - # new_task = modify_workflow_task( - # old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) - # user_workflow.set(task_name_start, new_task) - # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - # user_workflow) - # task_name_start = first_task_name[0] - - # save_img_workflow(vol=vol, - # workflow=user_workflow, - # input_arg=input, - # first_task=task_name_start, - # last_task=task_name_last, - # time_start=time_start, - # time_end=time_end, - # channel_start=ch_start, - # channel_end=ch_end, - # save_file_type=save_as_type, - # save_path=save_path, - # save_name=self.llsz_parent.lattice.save_name, - # dx=dx, - # dy=dy, - # dz=dz, - # angle=angle, - # deconvolution=self.llsz_parent.deconvolution, - # decon_processing=self.llsz_parent.lattice.decon_processing, - # otf_path=otf_path, - # psf_arg=psf_arg, - # psf=psf) - - # # If deskewing is already as a task, then set the first argument to input so we can modify that later - # else: - # # if deskewing is already first task, then check if deconvolution needed - # # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - # if self.llsz_parent.deconvolution: - # psf = "psf" - # otf_path = "otf_path" - # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - # user_workflow) - - # if self.llsz_parent.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - # user_workflow.set("deconvolution", - # pycuda_decon, - # image=input, - # psf=psf_arg, - # dzdata=self.llsz_parent.lattice.dz, - # dxdata=self.llsz_parent.lattice.dx, - # dzpsf=self.llsz_parent.lattice.dz, - # dxpsf=self.llsz_parent.lattice.dx, - # num_iter=self.llsz_parent.lattice.psf_num_iter) - # # user_workflow.set(input_arg_first,"deconvolution") - # else: - # user_workflow.set("deconvolution", - # skimage_decon, - # vol_zyx=input, - # psf=psf_arg, - # num_iter=self.llsz_parent.lattice.psf_num_iter, - # clip=False, - # filter_epsilon=0, - # boundary='nearest') - # # modify the user workflow so "deconvolution" is accepted - # new_task = modify_workflow_task( - # old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) - # user_workflow.set(task_name_start, new_task) - # input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - # user_workflow) - # task_name_start = first_task_name[0] - - # # we pass first argument as input - # save_img_workflow(vol=vol, - # workflow=user_workflow, - # input_arg=input_arg_first, - # first_task=task_name_start, - # last_task=task_name_last, - # time_start=time_start, - # time_end=time_end, - # channel_start=ch_start, - # channel_end=ch_end, - # save_file_type=save_as_type, - # save_path=save_path, - # save_name=self.llsz_parent.lattice.save_name, - # dx=dx, - # dy=dy, - # dz=dz, - # angle=angle, - # deconvolution=self.llsz_parent.deconvolution, - # decon_processing=self.llsz_parent.lattice.decon_processing, - # otf_path=otf_path, - # psf_arg=psf_arg, - # psf=psf) - - # print("Workflow complete") - # return - + lattice = self._make_model() + lattice.process().save_image() def _napari_lattice_widget_wrapper() -> LLSZWidget: # split widget type enables a resizable widget diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index b69deac..f95251c 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -1,38 +1,38 @@ -from __future__ import annotations -from pathlib import Path -from magicclass import FieldGroup, field, MagicTemplate -from magicclass.widgets import Widget, ComboBox, Label, Select -from magicclass.fields import MagicField -from typing import Any, Callable, List, Optional, Protocol, Tuple, TypeVar, Union, cast, TYPE_CHECKING -from typing_extensions import Protocol, Self -from pydantic import BaseModel, ValidationError +# FieldGroups that the users interact with to input data -from strenum import StrEnum -from enum import auto -from lls_core import DeconvolutionChoice, SaveFileType, Log_Levels, DeskewDirection -from lls_core.lattice_data import CropParams, DeconvolutionParams, DefinedPixelSizes, LatticeData, OutputParams, DeskewParams -from napari.layers import Shapes +import logging from enum import Enum +from pathlib import Path +from typing import Any, Callable, List, Optional, Tuple, TypeVar, cast + import pyclesperanto_prototype as cle -from napari_workflows import Workflow, WorkflowManager -from napari.types import ImageData, ShapesData +from lls_core import ( + DeconvolutionChoice, + DeskewDirection, + Log_Levels, + SaveFileType, +) +from lls_core.lattice_data import ( + CropParams, + DeconvolutionParams, + DefinedPixelSizes, + DeskewParams, + LatticeData, + OutputParams, +) +from magicclass import FieldGroup, MagicTemplate, field +from magicclass.fields import MagicField +from magicclass.widgets import ComboBox, Label, Widget +from napari.layers import Image, Shapes +from napari.types import ShapesData from napari.utils import history -from abc import ABC +from napari_lattice.icons import GREEN, GREY, RED +from napari_lattice.reader import NapariImageParams, lattice_params_from_napari +from napari_lattice.utils import get_layers +from napari_workflows import Workflow, WorkflowManager +from pydantic import ValidationError from qtpy.QtWidgets import QTabWidget - -from napari_lattice.icons import RED, GREEN, GREY -from napari_lattice.reader import lattice_params_from_napari -from napari_lattice.utils import get_viewer, get_layers - -from napari.layers import Image - -if TYPE_CHECKING: - from xarray import DataArray - - -# FieldGroups that the users interact with to input data - -import logging +from strenum import StrEnum logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -137,17 +137,6 @@ class LastDimensionOptions(Enum): XYZCT = "XYZCT" Metadata = "Get from Metadata" -# class NapariFields(FieldGroup, ABC): -# def __init__(self, layout: str = "vertical", labels: bool = False, name: str | None = None, **kwargs): -# super().__init__(layout, labels, name, **kwargs) - -# class NapariFieldGroupCompatible(Protocol): -# from magicclass.widgets import Container -# from qtpy.QtWidgets import QWidget -# errors: MagicField[Label] -# parent: QWidget -# _widget: Container - class NapariFieldGroup: # This implementation is a bit ugly. This is a mixin that can only be used on a `FieldGroup`. # However, it can't inherit from FieldGroup because then the metaclass would look for fields in this @@ -204,6 +193,10 @@ def _validate(self: Any): def _make_model(self): raise NotImplementedError() +class DeskewKwargs(NapariImageParams): + angle: float + skew: DeskewDirection + class DeskewFields(NapariFieldGroup, FieldGroup): def _get_dimension_options(self, _) -> List[str]: @@ -267,8 +260,8 @@ def _get_dimension_options(self, _) -> List[str]: def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) - from qtpy.QtWidgets import QDoubleSpinBox from magicgui.widgets import TupleEdit + from qtpy.QtWidgets import QDoubleSpinBox # Enormous hack to set the precision # A better method has been requested here: https://github.com/pyapp-kit/magicgui/issues/581#issuecomment-1709467219 @@ -287,34 +280,21 @@ def __init__(self, *args: Any, **kwargs: Any): @img_layer.connect def _img_changed(self) -> None: - # Recalculate the dimension options + # Recalculate the dimension options whenever the image changes self.dimension_order.reset_choices() @img_layer.connect @enable_if([stack_along]) def _hide_stack_along(self): + # Hide the "Stack Along" option if we only have one image return len(self.img_layer.value) > 1 - def _merge_layers(self) -> DataArray: + def _get_kwargs(self) -> DeskewKwargs: """ - Returns a single image array merged from all the selected layers + Returns the LatticeData fields that the Deskew tab can provide """ - from xarray import DataArray, concat - layers = [DataArray(it, dims=self.dimension_order.value.split()) for it in self.img_layer.value] - if len(layers) == 0: - raise Exception("At least one image layer must be selected") - elif len(layers) == 1: - return layers[0] - else: - dim = "C" if self.stack_along.value == StackAlong.CHANNEL else "T" - return concat(layers, dim=dim) - - # if len(self.img_layer.value) == 0: - # raise Exception("At least one image layer must be selected.") - # return images_to_stack(self.img_layer.value) - - def _make_model(self) -> DeskewParams: from aicsimageio.types import PhysicalPixelSizes + DeskewParams.update_forward_refs() params = lattice_params_from_napari( imgs=self.img_layer.value, dimension_order=None if self.dimension_order.value == "Get from Metadata" else self.dimension_order.value, @@ -325,14 +305,21 @@ def _make_model(self) -> DeskewParams: ), stack_along="C" if self.stack_along.value == StackAlong.CHANNEL else "T" ) - return DeskewParams( - data=params["data"], - dims=params["dims"], - physical_pixel_sizes=params["physical_pixel_sizes"], + return DeskewKwargs( + **params, angle=self.angle.value, skew = self.skew_dir.value, ) + def _make_model(self) -> DeskewParams: + kwargs = self._get_kwargs() + return DeskewParams( + data=kwargs["data"], + physical_pixel_sizes=kwargs["physical_pixel_sizes"], + angle=kwargs["angle"], + skew = kwargs["skew"] + ) + class DeconvolutionFields(NapariFieldGroup, FieldGroup): """ A counterpart to the DeconvolutionParams Pydantic class @@ -350,11 +337,6 @@ class DeconvolutionFields(NapariFieldGroup, FieldGroup): ) errors = field(Label).with_options(label="Errors") - - # @background.connect - # def _show_custom_background(self): - # self.background_custom.visible = self.background == BackgroundSource.Custom - @background.connect @enable_if( [background_custom] @@ -362,10 +344,6 @@ class DeconvolutionFields(NapariFieldGroup, FieldGroup): def _enable_custom_background(self) -> bool: return self.background.value == BackgroundSource.Custom - # @fields_enabled.connect - # def _enable_fields(self) -> bool: - # self.decon_processing.visible = self.fields_enabled - @fields_enabled.connect @enable_if( fields = [ @@ -414,32 +392,6 @@ class CroppingFields(NapariFieldGroup, FieldGroup): def _enable_workflow(self) -> bool: return self.fields_enabled.value - - # roi_layer_list: ShapesData - # @magicclass(visible=False) - # class Fields(MagicTemplate): - # shapes= vfield(ShapesData, label = "ROIs")#Optional[Shapes] = None - # z_range = vfield(Tuple[int, int]).with_options( - # label = "Z Range", - # value = (0, 1), - # options = dict( - # min = 0, - # max = 1 - # ), - # ) - # _shapes_layer: Optional[Shapes] = None - - # @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") - # @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) - # @set_design(text="New Cropping Layer") - # def activate_cropping(self): - # self._shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', - # face_color=[1, 1, 1, 0], name="Cropping BBOX layer") - # # TO select ROIs if needed - # self._shapes_layer.mode = "SELECT" - # self["activate_cropping"].text = "Cropping layer active" - # self["activate_cropping"].background_color = "green" - def _make_model(self) -> Optional[CropParams]: import numpy as np if self.fields_enabled.value: @@ -519,6 +471,3 @@ def _make_model(self) -> OutputParams: save_name=self.save_name.value, save_type=self.save_type.value ) - - -# @DeskewWidget.img_layer.connect diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index 1c7cee2..dac3452 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -41,6 +41,10 @@ def lattice_params_from_napari( if len(imgs) < 1: raise ValueError("At least one image must be provided.") + if len(set(len(it.data.shape) for it in imgs)) > 1: + size_message = ",".join(f"{img.name}: {len(img.data.shape)}" for img in imgs) + raise ValueError(f"The input images have multiple different dimensions, which napari lattice doesn't support: {size_message}") + save_name: str pixel_sizes: set[PhysicalPixelSizes] = {physical_pixel_sizes} save_names = [] @@ -74,11 +78,11 @@ def lattice_params_from_napari( # else: # pixel_size_metadata = img_data_aics.physical_pixel_sizes - calculated_order = img_data_aics.dims.order + calculated_order = tuple(img_data_aics.dims.order) elif dimension_order is None: raise ValueError("Either the Napari image must have dimensional metadata, or a dimension order must be provided") else: - calculated_order = list(dimension_order) + calculated_order = tuple(dimension_order) final_imgs.append(DataArray(img.data, dims=calculated_order)) From df6513a928605aca9ff8e175ceaf52cabfba45c2 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 12 Sep 2023 17:42:37 +1000 Subject: [PATCH 023/147] Use pydantic-cli for CLI --- core/lls_core/cmds/__main__.py | 797 ++------------------------- core/lls_core/lattice_data.py | 155 ++++-- plugin/napari_lattice/dock_widget.py | 2 +- plugin/napari_lattice/fields.py | 7 +- 4 files changed, 135 insertions(+), 826 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 76e41a3..205293f 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -3,775 +3,40 @@ # Example for deskewing files in a folder # python lattice_processing.py --input /home/pradeep/to_deskew --output /home/pradeep/output_save/ --processing deskew -import argparse -import os -import glob -import sys -import re -from lls_core.io import save_img, save_img_workflow -from lls_core.lattice_data import lattice_params_from_aics, LatticeData -from lls_core.utils import read_imagej_roi, get_all_py_files, get_first_last_image_and_task, modify_workflow_task, check_dimensions -from lls_core.llsz_core import crop_volume_deskew -from aicsimageio import AICSImage -from aicsimageio.types import PhysicalPixelSizes -import pyclesperanto_prototype as cle -from tqdm import tqdm -import dask.array as da -from napari_workflows._io_yaml_v1 import load_workflow from pathlib import Path -import yaml -from typing import Sequence, TYPE_CHECKING - -from lls_core.deconvolution import read_psf -from lls_core import DeskewDirection, DeconvolutionChoice, SaveFileType -from enum import Enum - -from napari_workflows import Workflow - - -# define parser class so as to print help message -class ArgParser(argparse.ArgumentParser): - def error(self, message): - sys.stderr.write('error: %s\n' % message) - self.print_help() - sys.exit(2) - - -class ProcessingOptions(Enum): - deskew = "deskew" - crop = "crop" - workflow = "workflow" - workflow_crop = "workflow_crop" - - -def make_parser(): - """ Parse input arguments""" - parser = argparse.ArgumentParser(description="Lattice Data Analysis") - parser.add_argument('--input', type=str, nargs=1, help="Enter input file", - required=False) # only false if using config file - parser.add_argument('--output', type=str, nargs=1, help="Enter save folder", - required=False) # only false if using config file - parser.add_argument('--skew_direction', type=DeskewDirection, nargs=1, - help="Enter the direction of skew (default is Y)", - action="store", - choices=("Y", "X"), - default=DeskewDirection.Y) - parser.add_argument('--deskew_angle', type=float, nargs=1, - help="Enter the deskew angle (default is 30)", - default=30.0) - parser.add_argument('--processing', type=ProcessingOptions, nargs=1, - help="Enter the processing option: deskew, crop, workflow or workflow_crop", required=False, - action="store", - choices=(ProcessingOptions.deskew, ProcessingOptions.crop, - ProcessingOptions.workflow, ProcessingOptions.workflow_crop), - default=None) - parser.add_argument('--deconvolution', type=DeconvolutionChoice, nargs=1, - help="Specify the device to use for deconvolution. Options are cpu or cuda_gpu", - action="store") - parser.add_argument('--deconvolution_num_iter', type=int, nargs=1, - help="Enter the number of iterations to run Richardson-Lucy deconvolution (default is 10)") - parser.add_argument('--deconvolution_psf', type=str, nargs="+", - help="Enter paths to psf file/s separated by commas or you can enter each path with double quotes") # use + for nargs for flexible no of args - parser.add_argument('--roi_file', type=str, nargs=1, - help="Enter the path to the ROI file for performing cropping (only valid for -processing where crop or workflow_crop is specified") - parser.add_argument('--voxel_sizes', type=float, nargs=3, - help="Enter the voxel sizes as (dz,dy,dx). Make sure they are in brackets", - default=(0.3, 0.1499219272808386, 0.1499219272808386)) - parser.add_argument('--file_extension', type=str, nargs=1, - help="If choosing a folder, enter the extension of the files (make sure you enter it with the dot at the start, i.e., .czi or .tif), else .czi and .tif files will be used") - parser.add_argument('--time_range', type=int, nargs=2, - help="Enter time range to extract, default will be entire timeseries if no range is specified. For example, 0 9 will extract first 10 timepoints", - default=(None, None)) - parser.add_argument('--channel_range', type=int, nargs=2, - help="Enter channel range to extract, default will be all channels if no range is specified. For example, 0 1 will extract first two channels.", - default=(None, None)) - parser.add_argument('--workflow_path', type=str, nargs=1, - help="Enter path to the workflow file '.yml", default=[None]) - parser.add_argument('--output_file_type', type=SaveFileType, nargs=1, - help="Save as either tiff or h5, defaults to h5", - action="store", - choices=(SaveFileType.tiff, SaveFileType.h5), - default=[SaveFileType.h5]), - parser.add_argument('--channel', type=bool, nargs=1, - help="If input is a tiff file and there are channel dimensions but no time dimensions, choose as True", - default=False) - parser.add_argument('--config', type=str, nargs=1, - help="Location of config file, all other arguments will be ignored and overwriten by those in the yaml file", default=None) - parser.add_argument('--roi_number', type=int, nargs=1, - help="Process an individual ROI, loop all if unset", default=None) - parser.add_argument('--set_logging', type=str, nargs=1, - help="Set logging level [INFO,DEBUG]", default=["INFO"]) - return parser - -def main(argv: Sequence[str] = sys.argv[1:]): - parser = make_parser() - args = parser.parse_args(argv) - - # Enable Logging - import logging - logger = logging.getLogger(__name__) - - # Setting empty strings for psf paths - psf_ch1_path = "" - psf_ch2_path = "" - psf_ch3_path = "" - psf_ch4_path = "" - - # IF using a config file, set a lot of parameters here - # the rest are scattered throughout the code when needed - # could be worth bringing everything up top - print(args.config) - - if args.config: - try: - with open(args.config[0], 'r') as con: - try: - processing_parameters = yaml.safe_load(con) - - except Exception as exc: - print(exc) - - except FileNotFoundError as exc: - exit(f"Config yml file {args.config[0]} not found, please specify") - - if not processing_parameters: - logging.error(f"Config file not loaded properly") - # this is how I propose setting the command line variables - # If they're in the config, get from there. If not - # Look in command line. If not there, then exit - if 'input' in processing_parameters: - input_path = processing_parameters['input'] - elif args.input is not None: - input_path = args.input[0] - else: - exit("Input not set") - print("Processing file %s" % input_path) - - if 'output' in processing_parameters: - output_path = processing_parameters['output'] - elif args.output is not None: - output_path = args.output[0] + os.sep - else: - exit("Output not set") - - # If requried setting not in config file = use defaults from argparse - # get method for a dictionary will get a value if specified, if not, will use value from args as default value - - dz, dy, dx = processing_parameters.get('voxel_sizes', args.voxel_sizes) - channel_dimension = processing_parameters.get('channel', args.channel) - skew_dir = processing_parameters.get( - 'skew_direction', DeskewDirection.Y) - deskew_angle = processing_parameters.get('deskew_angle', 30.0) - processing = ProcessingOptions[processing_parameters.get( - 'processing', None).lower()] - time_start, time_end = processing_parameters.get( - 'time_range', (None, None)) - channel_start, channel_end = processing_parameters.get( - 'channel_range', (None, None)) - output_file_type = SaveFileType[processing_parameters.get( - 'output_file_type', args.output_file_type).lower()] - - # to allow for either/or CLI/config file Todo for rest of parameters? - if 'roi_number' in processing_parameters: - roi_to_process = processing_parameters.get('roi_number') - elif args.roi_number is not None: - roi_to_process = args.roi_number[0] - else: - roi_to_process = None - - log_level = processing_parameters.get('--set_logging', "INFO") - workflow_path = processing_parameters.get('workflow_path', None) - if workflow_path is not None: - workflow_path = Path(workflow_path) - - logging.basicConfig(level=log_level.upper()) - logging.info(f"Logging set to {log_level.upper()}") - - if not processing: - logging.error("Processing option not set.") - exit() - - deconvolution = processing_parameters.get('deconvolution', None) - if deconvolution is not None: - deconvolution = DeconvolutionChoice[deconvolution.lower()] - psf_arg = "psf" - deconvolution_num_iter = processing_parameters.get( - 'deconvolution_num_iter', 10) - psf_paths = processing_parameters.get('deconvolution_psf') - logging.debug(psf_paths) - if not psf_paths: - logging.error("PSF paths not set option not set.") - exit() - else: - psf_ch1_path = psf_paths[0].replace(",", "").strip() - psf_ch2_path = psf_paths[1].replace(",", "").strip() - psf_ch3_path = psf_paths[2].replace(",", "").strip() - psf_ch4_path = psf_paths[3].replace(",", "").strip() - - else: - deconvolution = False - - roi_file: str - if processing == ProcessingOptions.crop or processing == ProcessingOptions.workflow_crop: - if 'roi_file' in processing_parameters: - roi_file = processing_parameters.get('roi_file', False) - elif args.roi_file is not None: - roi_file = args.roi_file[0] - else: - exit("Specify roi file") - assert os.path.exists(roi_file), "Cannot find " + roi_file - print("Processing using roi file %s" % roi_file) - - assert os.path.exists(input_path), "Cannot find input " + input_path - assert os.path.exists(output_path), "Cannot find output " + output_path - - file_extension = processing_parameters.get( - 'file_extension', [".czi", ".tif", ".tiff"]) - - # setting (some - see above) parameters from CLI - else: # if not using config file - input_path = args.input[0] - output_path = args.output[0] + os.sep - dz, dy, dx = args.voxel_sizes - deskew_angle = args.deskew_angle - channel_dimension = args.channel - time_start, time_end = args.time_range - channel_start, channel_end = args.channel_range - skew_dir = args.skew_direction - processing = args.processing[0] - output_file_type = args.output_file_type[0] - roi_to_process = args.roi_number - workflow_path = args.workflow_path[0] - - log_level = args.set_logging[0] - logging.basicConfig(level=log_level.upper()) - logging.info(f"Logging set to {log_level.upper()}") - - if roi_to_process: - logging.info(f"Processing ROI {roi_to_process}") - - if not processing: - logging.error("Processing option not set.") - exit() - - # deconvolution - if args.deconvolution: - deconvolution = args.deconvolution[0] - psf_arg = "psf" - if args.deconvolution_psf: - psf_paths = re.split(';|,', args.deconvolution_psf[0]) - logging.debug(psf_paths) - try: - psf_ch1_path = psf_paths[0] - psf_ch2_path = psf_paths[1] - psf_ch3_path = psf_paths[2] - psf_ch4_path = psf_paths[3] - except IndexError: - pass - else: - logging.error("PSF paths not set option not set.") - exit() - # num of iter default is 10 if nothing specified - if args.deconvolution_num_iter: - deconvolution_num_iter = args.deconvolution_num_iter - else: - deconvolution_num_iter = 10 - else: - deconvolution = False - - # output file type to save - if not output_file_type: - output_file_type = SaveFileType.h5 - - # Get - if processing == ProcessingOptions.crop or processing == ProcessingOptions.workflow_crop: - assert args.roi_file, "Specify roi_file (ImageJ/FIJI ROI Zip file)" - roi_file = args.roi_file[0] - if os.path.isfile(roi_file): # if file make sure it is a zip file or roi file - - roi_file_extension = os.path.splitext(roi_file)[1] - assert roi_file_extension == ".zip" or roi_file_extension == ".roi", "ROI file is not a zip or .roi file" - - # Check if input and output paths exist - assert os.path.exists(input_path), "Cannot find input " + input_path - assert os.path.exists(output_path), "Cannot find output " + output_path - - if not args.file_extension: - file_extension = [".czi", ".tif", ".tiff"] - else: - file_extension = args.file_extension - - # Initialise list of images and ROIs - img_list = [] - roi_list = [] - logging.debug(f"Deconvolution is set to {deconvolution} and option is ") - - logging.debug(f"Output file type is {output_file_type}") - # If input_path a directory, get a list of images - if os.path.isdir(input_path): - for file_type in file_extension: - img_list.extend(glob.glob(input_path + os.sep + '*' + file_type)) - print("List of images: ", img_list) - elif os.path.isfile(input_path) and (os.path.splitext(input_path))[1] in file_extension: - # if a single file, just add filename to the image list - img_list.append(input_path) - else: - sys.exit("Do not recognise " + input_path + " as directory or file") - - # If cropping, get list of roi files with matching image names - if processing == ProcessingOptions.crop or processing == ProcessingOptions.workflow_crop: - if os.path.isdir(roi_file): - for img in img_list: - img_name = os.path.basename(os.path.splitext(img)[0]) - roi_temp = roi_file + os.sep + img_name + ".zip" - - if os.path.exists(roi_temp): - roi_list.append(roi_temp) - else: - sys.exit("Cannot find ROI file for " + img) - - print("List of ROIs: ", roi_list) - elif os.path.isfile(roi_file): - roi_list.append(roi_file) - assert len(roi_list) == len( - img_list), "Image and ROI lists do not match" - else: - # add list of empty strings so that it can run through for loop - no_files = len(img_list) - roi_list = [""] * no_files - - # loop through the list of images and rois - for img, roi_path in zip(img_list, roi_list): - save_name = os.path.splitext(os.path.basename(img))[0] - - print("Processing Image " + img) - if processing == ProcessingOptions.crop or processing == ProcessingOptions.workflow_crop: - print("Processing ROI " + roi_path) - aics_img = AICSImage(img) - - # check if scene valid; if not it iterates through all scenes - len_scenes = len(aics_img.scenes) - for scene in range(len_scenes): - aics_img.set_scene(scene) - test = aics_img.get_image_dask_data("YX", T=0, C=0, Z=0) - try: - test_max = test.max().compute() - if test_max: - print(f"Scene {scene} is valid") - break - except Exception: - print(f"Scene {scene} not valid") - - # Initialize Lattice class - lattice = LatticeData( - **lattice_params_from_aics(aics_img, physical_pixel_sizes=PhysicalPixelSizes(dx, dy, dz)), - angle=deskew_angle, - skew=skew_dir, - save_name=save_name +from typing import List + +from lls_core.lattice_data import ( + CropParams, + DeconvolutionParams, + DeskewParams, + LatticeData, + OutputParams, +) +from pydantic_cli import run_and_exit, DefaultConfig +from pydantic import Field + +class CliParams(DeskewParams, CropParams, OutputParams, DeconvolutionParams): + # The idea of this class is to re-use the logic from the Pydantic + # models, namely the validation, defaults etc, but to flatten them + # into one model so that pydantic-cli can handle it, and to override + # certain field definitions with CLI-specific data types + save_name: str = Field(default=None, description=OutputParams.get_description("save_name")) + image: Path = Field(description="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi") + roi_list: List[Path] = Field(default=[], description="A list of paths pointing to regions of interest to crop to, in ImageJ format.") + psf: List[Path] = Field(default=[], description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.") + + def to_lattice(self) -> LatticeData: + return LatticeData( + **self.dict() ) - if time_start is None or time_end is None: - time_start, time_end = 0, lattice.time - 1 - - if channel_start is None or channel_end is None: - channel_start, channel_end = 0, lattice.channels - 1 - - # Verify dimensions - check_dimensions(time_start, time_end, channel_start, - channel_end, lattice.channels, lattice.time) - print("dimensions verified") - # If deconvolution, set the parameters in the lattice class - if deconvolution: - lattice.decon_processing = deconvolution - lattice.psf_num_iter = deconvolution_num_iter - logging.debug(f"Num of iterations decon, {lattice.psf_num_iter}") - logging.info("Performing Deconvolution") - lattice.psf = [] - lattice.otf_path = [] - # Remove empty values and convert to Path - lattice.psf = list(read_psf([Path(it) for it in [psf_ch1_path, psf_ch2_path, psf_ch3_path, psf_ch4_path] if it is not None], decon_option=lattice.decon_processing, lattice_class=lattice)) - - else: - lattice.decon_processing = None - - # Override pixel values by reading metadata if file is czi - if os.path.splitext(img)[1] == ".czi": - dz, dy, dx = lattice.dz, lattice.dy, lattice.dx - logging.info(f"Pixel values from metadata (zyx): {dz},{dy},{dx}") - - # Setup workflows based on user input - if processing == ProcessingOptions.workflow or processing == ProcessingOptions.workflow_crop: - # load workflow from path - # if args.config and 'workflow_path' in processing_parameters: - #workflow_path = Path(processing_parameters['workflow_path']) - # else: - #workflow_path = Path(workflow_path) - - # load custom modules (*.py) in same directory as workflow file - import importlib - parent_dir = Path(workflow_path).resolve().parents[0].__str__() + os.sep - - sys.path.append(parent_dir) - custom_py_files = get_all_py_files(parent_dir) - - if len(custom_py_files) > 0: - modules = map(importlib.import_module, custom_py_files) - logging.info(f"Custom modules imported {modules}") - - # workflow has to be reloaded for each image and reinitialised - user_workflow = load_workflow(workflow_path.__str__()) - if not isinstance(user_workflow, Workflow): - raise ValueError("Workflow file is not a napari workflow object. Check file!") - - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - logging.debug(input_arg_first, input_arg_last, - first_task_name, last_task_name) - - # get list of tasks - task_list = list(user_workflow._tasks.keys()) - - print("Workflow loaded:") - logging.info(user_workflow) - - task_name_start = first_task_name[0] - try: - task_name_last = last_task_name[0] - except IndexError: - task_name_last = task_name_start - - # if workflow involves cropping, assign first task as crop_volume_deskew - if processing == ProcessingOptions.workflow_crop: - deskewed_shape = lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - roi = "roi" - volume = "volume" - # Create workflow for cropping and deskewing - # volume and roi used will be set dynamically - user_workflow.set("crop_deskew", crop_volume_deskew, - original_volume=volume, - deskewed_volume=deskewed_volume, - roi_shape=roi, - angle_in_degrees=deskew_angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - z_start=z_start, - z_end=z_end, - skew_dir=lattice.skew) - # change the first task so it accepts "crop_deskew as input" - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="crop_deskew", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - - elif processing == ProcessingOptions.workflow: - # Verify if deskewing function is in workflow; if not, add as first task - if user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): - custom_workflow = True - input = "input" - - # add task to the workflow - user_workflow.set("deskew_image", lattice.deskew_func, - input_image=input, - angle_in_degrees=deskew_angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - linear_interpolation=True) - # Set input of the workflow to be from deskewing - # change the first task so it accepts "deskew_image" as input - new_task = modify_workflow_task(old_arg=input_arg_first, task_key=task_name_start, - new_arg="deskew_image", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - else: - custom_workflow = False - - img_data = lattice.data - - # Create save directory for each image - save_path = output_path + os.sep + \ - os.path.basename(os.path.splitext(img)[0]) + os.sep - - if not os.path.exists(save_path): - try: - os.mkdir(save_path) - except FileExistsError: - # this is sometimes caused when running parallel jobs - # can safely be ignored (I hope) - pass - - logging.info(f"Saving at {save_path}") - - # Deskewing only - - if processing == ProcessingOptions.deskew: - # deconvolution - if lattice.decon_processing: - save_img(vol=img_data, - func=lattice.deskew_func, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_path=save_path, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle, - terminal=True, - lattice=lattice, - angle_in_degrees=deskew_angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - linear_interpolation=True - ) - - else: - save_img(vol=img_data, - func=lattice.deskew_func, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_path=save_path, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle, - angle_in_degrees=deskew_angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz - ) - - # Crop and deskew - elif processing == ProcessingOptions.crop or processing == ProcessingOptions.workflow_crop: - print(roi_path) - roi_img = read_imagej_roi(roi_path) - - deskewed_shape = lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - - # Can modify for entering custom z values - z_start = 0 - z_end = deskewed_shape[0] - - # if roi number is specified, roi_img will be a list containing only one roi. - if roi_to_process is not None: - # just do one ROI - assert roi_to_process < len( - roi_img), f"ROI specified is {roi_to_process}, which is less than total ROIs ({len(roi_img)})" - logging.info(f"Processing single ROI: {roi_to_process}") - # If only one ROI, single loop - roi_img = [roi_img[roi_to_process]] - - # loop through rois in the roi list - for idx, roi_layer in enumerate(tqdm(roi_img, desc="ROI:", position=0)): - - if roi_to_process is not None: - roi_label = str(roi_to_process) - else: - roi_label = str(idx) - - print("Processing ROI " + str(idx) + - " of " + str(len(roi_img))) - deskewed_shape = lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - - # Can modify for entering custom z values - z_start = 0 - z_end = deskewed_shape[0] - - if processing == ProcessingOptions.crop: - # deconvolution - if lattice.decon_processing: - save_img(img_data, - func=crop_volume_deskew, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_name_prefix="ROI_" + roi_label + "_", - save_path=save_path, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle, - terminal=True, - lattice=lattice, - deskewed_volume=deskewed_volume, - roi_shape=roi_layer, - angle_in_degrees=deskew_angle, - z_start=z_start, - z_end=z_end, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - ) - else: - print("SHOULD BE DOING THIS") - print(save_path) - print(save_name) - save_img(img_data, - func=crop_volume_deskew, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_name_prefix="ROI_" + roi_label + "_", - save_path=save_path, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle, - deskewed_volume=deskewed_volume, - roi_shape=roi_layer, - angle_in_degrees=deskew_angle, - z_start=z_start, - z_end=z_end, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - ) - - elif processing == ProcessingOptions.workflow_crop: - # deconvolution - user_workflow.set(roi, roi_layer) - - if lattice.decon_processing: - save_img_workflow(vol=img_data, - workflow=user_workflow, - input_arg=volume, - first_task="crop_deskew", - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_path=save_path, - save_name_prefix="ROI_" + roi_label, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle, - deconvolution=True, - decon_processing=lattice.decon_processing, - psf=lattice.psf, - psf_arg=psf_arg) - else: - save_img_workflow(vol=img_data, - workflow=user_workflow, - input_arg=volume, - first_task="crop_deskew", - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_path=save_path, - save_file_type=output_file_type, - save_name_prefix="ROI_" + roi_label, - save_name=save_name, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle, - deconvolution=False) - - elif processing == ProcessingOptions.workflow: - # if deskew_image task set above manually - if custom_workflow: - if lattice.decon_processing: - save_img_workflow(vol=img_data, - workflow=user_workflow, - input_arg=input, - first_task="deskew_image", - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_path=save_path, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle, - deconvolution=True, - decon_processing=lattice.decon_processing, - psf=lattice.psf, - psf_arg=psf_arg) - else: - save_img_workflow(vol=img_data, - workflow=user_workflow, - input_arg=input, - first_task="deskew_image", - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_path=save_path, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle) - else: - if lattice.decon_processing: - save_img_workflow(vol=img_data, - workflow=user_workflow, - input_arg=input_arg_first, - first_task=first_task_name, - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_path=save_path, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle, - deconvolution=True, - decon_processing=lattice.decon_processing, - psf=lattice.psf, - psf_arg=psf_arg) - else: - save_img_workflow(vol=img_data, - workflow=user_workflow, - input_arg=input_arg_first, - first_task=first_task_name, - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=channel_start, - channel_end=channel_end, - save_path=save_path, - save_name=save_name, - save_file_type=output_file_type, - dx=dx, - dy=dy, - dz=dz, - angle=deskew_angle) + class Config(DefaultConfig): + CLI_JSON_ENABLE = True +def _main(params: CliParams) -> int: + params.to_lattice().process().save_image() + return 0 if __name__ == '__main__': - main() + run_and_exit(CliParams, _main) diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py index 3ec8bbb..7455ca1 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/lattice_data.py @@ -1,9 +1,9 @@ from __future__ import annotations # class for initializing lattice data and setting metadata # TODO: handle scenes +from os import getcwd from pydantic import BaseModel, Field, NonNegativeInt, NonNegativeFloat, root_validator, validator from aicsimageio.aics_image import AICSImage -from aicsimageio.dimensions import Dimensions # from numpy.typing import NDArray import math from dask.array.core import Array as DaskArray @@ -12,7 +12,7 @@ import tifffile from pydantic_numpy import NDArray -from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Union +from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union from typing_extensions import TypedDict, Self from pathlib import Path @@ -31,20 +31,31 @@ if TYPE_CHECKING: import pyclesperanto_prototype as cle + from enum import Enum import logging logger = logging.getLogger(__name__) -class DefaultMixin(BaseModel): +def enum_choices(enum: Type[Enum]) -> str: """ - Adds a method for retrieving default values from a BaseModel + Returns a human readable list of enum choices + """ + return "{" + ", ".join([it.name for it in enum]) + "}" + +class FieldAccessMixin(BaseModel): + """ + Adds methods to a BaseModel for accessing useful field information """ @classmethod def get_default(cls, field_name: str): return cls.__fields__[field_name].get_default() + @classmethod + def get_description(cls, field_name: str) -> str: + return cls.__fields__[field_name].field_info.description + class ProcessedVolume(BaseModel, arbitrary_types_allowed=True): """ A slice of the image processing result @@ -120,7 +131,7 @@ def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] components.append(f"T{time}") return "_".join(components) -class DefinedPixelSizes(DefaultMixin): +class DefinedPixelSizes(FieldAccessMixin): """ Like PhysicalPixelSizes, but it's a dataclass, and none of its fields are None @@ -143,37 +154,58 @@ class DeconvolutionParams(BaseModel, arbitrary_types_allowed=True): """ Parameters for the optional deconvolution step """ - decon_processing: DeconvolutionChoice = DeconvolutionChoice.cpu - psf: List[NDArray] = [] - psf_num_iter: NonNegativeInt = 10 - # TODO: handle this - # otf_path: List = [] - # Background value to subtract - background: Union[float, Literal["auto", "second_last"]] = 0 + decon_processing: DeconvolutionChoice = Field( + default=DeconvolutionChoice.cpu, + description=f"Hardware to use to perform the deconvolution. Choices: {enum_choices(DeconvolutionChoice)}") + psf: List[NDArray] = Field( + default=[], + description="List of Point Spread Functions to use for deconvolution. Each of which should be a 3D array." + ) + psf_num_iter: NonNegativeInt = Field( + default=10, + description="Number of iterations to perform in deconvolution" + ) + background: Union[float, Literal["auto", "second_last"]] = Field( + default=0, + description='Background value to subtract for deconvolution. Only used when decon_processing is set to GPU. This can either be a literal number, "auto" which uses the median of the last slice, or "second_last" which uses the median of the last slice.' + ) class CropParams(BaseModel, arbitrary_types_allowed=True): """ Parameters for the optional cropping step """ - roi_layer_list: ShapesData - z_start: NonNegativeInt = 0 - z_end: NonNegativeInt = 1 + roi_list: ShapesData = Field( + description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex." + ) + z_range: Tuple[NonNegativeInt, NonNegativeInt] = Field( + default=None, + description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." + ) -class OutputParams(DefaultMixin, arbitrary_types_allowed=True): - #: The directory where this data will be saved - save_dir: Path +class OutputParams(FieldAccessMixin, arbitrary_types_allowed=True): + save_dir: Path = Field( + default = Path.cwd(), + description="The directory where the output data will be saved" + ) - #: The file name to save this as, without the directory name or file extension - save_name: str + save_name: str = Field( + description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." + ) - #: The range of times to process - time_range: range + time_range: range = Field( + default = None, + description="The range of times to process. This defaults to all time points in the image array." + ) - #: The range of channels to process - channel_range: range + channel_range: range = Field( + default = None, + description="The range of channels to process. This defaults to all channels in the image array." + ) - #: The data type to save the result as - save_type: SaveFileType = SaveFileType.h5 + save_type: SaveFileType = Field( + default=SaveFileType.h5, + description=f"The data type to save the result as. This will also be used to determine the file extension of the output files. Choices: {enum_choices(SaveFileType)}" + ) @validator("time_range") def default_time_range(cls, v: Any, values: dict) -> range: @@ -193,27 +225,38 @@ def default_channel_range(cls, v: Any, values: dict) -> range: return range(values["data"].sizes["C"] + 1) return v -class DeskewParams(DefaultMixin, arbitrary_types_allowed=True): - #: A 3-5D array containing the image data - data: DataArray +class DeskewParams(FieldAccessMixin, arbitrary_types_allowed=True): + image: DataArray = Field( + description="A 3-5D array containing the image data" + ) - #: Geometry of the light path - skew: DeskewDirection = DeskewDirection.Y - angle: float = 30.0 + skew: DeskewDirection = Field( + default=DeskewDirection.Y, + description=f"Axis along which to deskew the image. Choices: {enum_choices(DeskewDirection)}" + ) + angle: float = Field( + default=30.0, + description="Angle of deskewing, in degrees" + ) - #: Pixel size in microns - physical_pixel_sizes: DefinedPixelSizes = Field(default_factory=DefinedPixelSizes) + physical_pixel_sizes: DefinedPixelSizes = Field( + default_factory=DefinedPixelSizes, + description="Pixel size of the microscope, in microns" + ) - #: Dimensions of the deskewed output - deskew_vol_shape: Tuple[int, ...] = Field(init_var=False, default=None) + deskew_vol_shape: Tuple[int, ...] = Field( + init_var=False, + default=None, + description="Dimensions of the deskewed output. This is set automatically based on other input parameters, and doesn't need to be provided by the user." + ) - deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False, default=None) + deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False, default=None, description="Deskewing transformation function. This is set automatically based on other input parameters, and doesn't need to be provided by the user.") @property def dims(self): - return self.data.dims + return self.image.dims - @validator("data", pre=True) + @validator("image", pre=True) def reshaping(cls, v: Any): # This allows a user to pass in any array-like object and have it # converted and reshaped appropriately @@ -227,7 +270,7 @@ def reshaping(cls, v: Any): return array.transpose("T", "C", "Z", "Y", "X") def get_3d_slice(self) -> DataArray: - return self.data.sel(C=0, T=0) + return self.image.sel(C=0, T=0) @root_validator(pre=True) def set_deskew(cls, values: dict) -> dict: @@ -244,7 +287,6 @@ def set_deskew(cls, values: dict) -> dict: raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") return values - class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): """ Holds data and metadata for a given image in a consistent format @@ -259,8 +301,10 @@ class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): #: If this is None, then cropping is disabled crop: Optional[CropParams] = None - #: If defined, this is a workflow to add lightsheet processing onto - workflow: Optional[Workflow] = None + workflow: Optional[Workflow] = Field( + default=None, + description="If defined, this is a workflow to add lightsheet processing onto" + ) @validator("time_range") def disjoint_time_range(cls, v: range, values: dict): @@ -363,12 +407,12 @@ def deconv_enabled(self) -> bool: @property def time(self) -> int: """Number of time points""" - return self.data.sizes["T"] + return self.image.sizes["T"] @property def channels(self) -> int: """Number of channels""" - return self.data.sizes["C"] + return self.image.sizes["C"] @property def new_dz(self): @@ -384,14 +428,14 @@ def slice_data(self, time: int, channel: int) -> DataArray: if channel > self.channels: raise ValueError("channel is out of range") - return self.data.sel(T=time, C=channel) + return self.image.sel(T=time, C=channel) - if len(self.data.shape) == 3: - return self.data - elif len(self.data.shape) == 4: - return self.data[time, :, :, :] - elif len(self.data.shape) == 5: - return self.data[time, channel, :, :, :] + if len(self.image.shape) == 3: + return self.image + elif len(self.image.shape) == 4: + return self.image[time, :, :, :] + elif len(self.image.shape) == 5: + return self.image[time, channel, :, :, :] raise Exception("Lattice data must be 3-5 dimensions") @@ -436,7 +480,7 @@ def _process_crop(self) -> Iterable[ProcessedVolume]: raise Exception("This function can only be called when crop is set") # We have an extra level of iteration for the crop path: iterating over each ROI - for roi_index, roi in enumerate(tqdm(self.crop.roi_layer_list, desc="ROI:", position=0)): + for roi_index, roi in enumerate(tqdm(self.crop.roi_list, desc="ROI:", position=0)): # pass arguments for save tiff, callable and function arguments logger.info("Processing ROI ", roi_index) @@ -462,8 +506,8 @@ def _process_crop(self) -> Iterable[ProcessedVolume]: voxel_size_z=self.dy, angle_in_degrees=self.angle, deskewed_volume=self.deskewed_volume, - z_start=self.crop.z_start, - z_end=self.crop.z_end, + z_start=self.crop.z_range[0], + z_end=self.crop.z_range[1], **deconv_args ), channel=ch, @@ -484,6 +528,7 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: data= pycuda_decon( image=data, psf=self.deconvolution.psf[ch], + background=self.deconvolution.background, dzdata=self.dz, dxdata=self.dx, dzpsf=self.dz, diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 398827f..4194dcd 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -57,7 +57,7 @@ def _make_model(self) -> LatticeData: deskew_args = self.LlszMenu.WidgetContainer.deskew_fields._get_kwargs() output_args = self.LlszMenu.WidgetContainer.output_fields._make_model() return LatticeData( - data=deskew_args["data"], + image=deskew_args["data"], angle=deskew_args["angle"], channel_range=output_args.channel_range, time_range=output_args.time_range, diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index f95251c..19d671f 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -314,7 +314,7 @@ def _get_kwargs(self) -> DeskewKwargs: def _make_model(self) -> DeskewParams: kwargs = self._get_kwargs() return DeskewParams( - data=kwargs["data"], + image=kwargs["data"], physical_pixel_sizes=kwargs["physical_pixel_sizes"], angle=kwargs["angle"], skew = kwargs["skew"] @@ -396,9 +396,8 @@ def _make_model(self) -> Optional[CropParams]: import numpy as np if self.fields_enabled.value: return CropParams( - z_start=self.z_range.value[0], - z_end=self.z_range.value[1], - roi_layer_list=ShapesData([np.array(shape.data) for shape in self.shapes.value]) + z_range=self.z_range.value, + roi_list=ShapesData([np.array(shape.data) for shape in self.shapes.value]) ) return None From 652a6de91f9ac4dba7ead1bbe7cf5f84ec47f703 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 14 Sep 2023 12:19:08 +1000 Subject: [PATCH 024/147] Refactor submodels into different files --- core/lls_core/__init__.py | 5 - core/lls_core/cmds/__main__.py | 165 +++++++++++-- core/lls_core/deconvolution.py | 2 +- core/lls_core/io.py | 2 +- core/lls_core/models/crop.py | 18 ++ core/lls_core/models/deconvolution.py | 34 +++ core/lls_core/models/deskew.py | 97 ++++++++ core/lls_core/{ => models}/lattice_data.py | 257 ++++++--------------- core/lls_core/models/output.py | 49 ++++ core/lls_core/models/utils.py | 24 ++ core/lls_core/types.py | 5 +- core/lls_core/workflow.py | 9 +- core/tests/test_deskew.py | 2 +- plugin/napari_lattice/dock_widget.py | 6 +- plugin/napari_lattice/fields.py | 6 +- plugin/napari_lattice/reader.py | 2 +- 16 files changed, 458 insertions(+), 225 deletions(-) create mode 100644 core/lls_core/models/crop.py create mode 100644 core/lls_core/models/deconvolution.py create mode 100644 core/lls_core/models/deskew.py rename core/lls_core/{ => models}/lattice_data.py (69%) create mode 100644 core/lls_core/models/output.py create mode 100644 core/lls_core/models/utils.py diff --git a/core/lls_core/__init__.py b/core/lls_core/__init__.py index 9d9bfb6..eb2eddf 100644 --- a/core/lls_core/__init__.py +++ b/core/lls_core/__init__.py @@ -13,11 +13,6 @@ class DeconvolutionChoice(StrEnum): opencl_gpu = "opencl_gpu" cpu = "cpu" -#Choice of File extension to save -class SaveFileType(StrEnum): - h5 = "h5" - tiff = "tiff" - #CONFIGURE LOGGING using a dictionary (can also be done with yaml file) import logging.config LOGGING_CONFIG = { diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 205293f..c0a0f1b 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -1,42 +1,161 @@ # lattice_processing.py + # Run processing on command line instead of napari. # Example for deskewing files in a folder # python lattice_processing.py --input /home/pradeep/to_deskew --output /home/pradeep/output_save/ --processing deskew from pathlib import Path -from typing import List +from typing import Any, Dict, List, Optional, Tuple -from lls_core.lattice_data import ( +from lls_core.models.lattice_data import ( CropParams, - DeconvolutionParams, - DeskewParams, LatticeData, OutputParams, ) -from pydantic_cli import run_and_exit, DefaultConfig -from pydantic import Field - -class CliParams(DeskewParams, CropParams, OutputParams, DeconvolutionParams): - # The idea of this class is to re-use the logic from the Pydantic - # models, namely the validation, defaults etc, but to flatten them - # into one model so that pydantic-cli can handle it, and to override - # certain field definitions with CLI-specific data types +from lls_core.models.deskew import ( + DefinedPixelSizes, + skew, + angle, + physical_pixel_sizes, +) +from lls_core.models.deconvolution import ( + Background, + DeconvolutionParams, + psf_num_iter, + decon_processing, + background +) +from lls_core.models.output import ( + SaveFileType, + save_dir, + save_type, + channel_range, + time_range +) +from napari_workflows import Workflow +from lls_core.models.crop import z_range +from pydantic_cli import run_and_exit, DefaultConfig, pydantic_class_to_parser +from pydantic import DirectoryPath, Field, NonNegativeInt, validator, FilePath, BaseModel, root_validator +from lls_core import DeconvolutionChoice, DeskewDirection + +class + +class CliParams(BaseModel): + # We can't directly use LatticeData because it uses nested models which aren't + # supported by pydantic-cli, and also we need to modify some types to support the CLI e.g. by allowing filepath inputs + + class Config(DefaultConfig): + CLI_JSON_ENABLE = True + arbitrary_types_allowed=True + + # Deskew + image: FilePath = Field(description="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi") + skew: DeskewDirection = skew + angle: float = angle + physical_pixel_sizes: Optional[DefinedPixelSizes] = physical_pixel_sizes + + # Deconvolution + deconvolution: bool = False + decon_processing: DeconvolutionChoice = decon_processing + psf: List[FilePath] = Field(default=[], description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.") + psf_num_iter = psf_num_iter + background: Background = background + + # Crop + roi_list: List[FilePath] = Field(default=[], description="A list of paths pointing to regions of interest to crop to, in ImageJ format.") + z_range: Tuple[NonNegativeInt, NonNegativeInt] = z_range + + # Output + # This one needs overriding to accommodate the default value save_name: str = Field(default=None, description=OutputParams.get_description("save_name")) - image: Path = Field(description="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi") - roi_list: List[Path] = Field(default=[], description="A list of paths pointing to regions of interest to crop to, in ImageJ format.") - psf: List[Path] = Field(default=[], description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.") + save_dir: DirectoryPath = save_dir + save_type: SaveFileType = save_type + channel_range: range = channel_range + time_range: range = time_range + + # Workflow + workflow: Optional[FilePath] = Field(default = None, description="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow.") + + @validator("save_name", pre=True) + def default_save_name(cls, v: Any, values: Dict): + # Use the input file path to provide a default output filename + if v is None and values.get("image", None): + path: Path = values["image"] + return path.stem + "_deskewed" + return v + + @root_validator(pre=True) + def metadata_from_path(cls, values: Dict) -> Dict: + + return values def to_lattice(self) -> LatticeData: + from aicsimageio import AICSImage + + crop = None + if len(self.roi_list) > 0: + crop = CropParams( + roi_list=[AICSImage(it).data for it in self.roi_list], + z_range=self.z_range + ) + + deconvolution = None + if self.deconvolution: + deconvolution = DeconvolutionParams( + decon_processing=self.decon_processing, + psf = [AICSImage(it).data for it in self.psf], + psf_num_iter = self.psf_num_iter, + ) + + workflow = None + if self.workflow is not None: + from lls_core.workflow import workflow_from_path + workflow = workflow_from_path(self.workflow) + return LatticeData( - **self.dict() - ) + image=AICSImage(self.image).xarray_dask_data, + angle=self.angle, + skew=self.skew, + physical_pixel_sizes=self.physical_pixel_sizes, - class Config(DefaultConfig): - CLI_JSON_ENABLE = True + crop=crop, + deconvolution=deconvolution, + workflow=workflow, + + save_type=self.save_type, + channel_range=self.channel_range, + time_range=self.time_range, + save_dir=self.save_dir, + save_name=self.save_name + ) -def _main(params: CliParams) -> int: - params.to_lattice().process().save_image() - return 0 +def make_parser(): + parser = pydantic_class_to_parser(CliParams, default_value_override={}) + parser.add_argument("--yaml-config", required=False, help="Path to configuration YAML file") + return parser if __name__ == '__main__': - run_and_exit(CliParams, _main) + parser = make_parser() + args = parser.parse_args() + arg_dict = vars(args) + + json_path = arg_dict.pop("json-config", None) + json_config = {} + if json_path: + import json + with open(json_path) as fp: + json_config = json.load(fp) + + yaml_path = arg_dict.pop("yaml-config", None) + yaml_config = {} + if yaml_path: + import yaml + with open(yaml_path) as fp: + json_config = yaml.safe_load(fp) + + config = CliParams( + **json_config, + **yaml_config, + **arg_dict + ) + config.to_lattice().process().save_image() diff --git a/core/lls_core/deconvolution.py b/core/lls_core/deconvolution.py index 4206f84..3a209d6 100644 --- a/core/lls_core/deconvolution.py +++ b/core/lls_core/deconvolution.py @@ -21,7 +21,7 @@ from lls_core.types import ArrayLike, is_arraylike if TYPE_CHECKING: - from lls_core.lattice_data import LatticeData + from lls_core.models.lattice_data import LatticeData logger = logging.getLogger(__name__) diff --git a/core/lls_core/io.py b/core/lls_core/io.py index 78b25fe..008b9a3 100644 --- a/core/lls_core/io.py +++ b/core/lls_core/io.py @@ -23,7 +23,7 @@ from lls_core.llsz_core import crop_volume_deskew from lls_core.deconvolution import skimage_decon, pycuda_decon from lls_core import config, DeconvolutionChoice, SaveFileType -from lls_core.lattice_data import LatticeData +from lls_core.models.lattice_data import LatticeData import os import numpy as np diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py new file mode 100644 index 0000000..822bc98 --- /dev/null +++ b/core/lls_core/models/crop.py @@ -0,0 +1,18 @@ + +from typing import Tuple +from pydantic import BaseModel, Field, NonNegativeInt +from napari.types import ShapesData + + +z_range: Tuple[NonNegativeInt, NonNegativeInt] = Field( + default=None, + description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." +) +class CropParams(BaseModel, arbitrary_types_allowed=True): + """ + Parameters for the optional cropping step + """ + roi_list: ShapesData = Field( + description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex." + ) + z_range: Tuple[NonNegativeInt, NonNegativeInt] = z_range diff --git a/core/lls_core/models/deconvolution.py b/core/lls_core/models/deconvolution.py new file mode 100644 index 0000000..993f29e --- /dev/null +++ b/core/lls_core/models/deconvolution.py @@ -0,0 +1,34 @@ + +from pydantic import BaseModel, Field, NonNegativeInt +from pydantic_numpy import NDArray + +from typing import List, Literal, Union + + +from lls_core import DeconvolutionChoice +from lls_core.models.utils import enum_choices + +decon_processing: DeconvolutionChoice = Field( + default=DeconvolutionChoice.cpu, + description=f"Hardware to use to perform the deconvolution. Choices: {enum_choices(DeconvolutionChoice)}") +psf: List[NDArray] = Field( + default=[], + description="List of Point Spread Functions to use for deconvolution. Each of which should be a 3D array." +) +psf_num_iter: NonNegativeInt = Field( + default=10, + description="Number of iterations to perform in deconvolution" +) +Background = Union[float, Literal["auto", "second_last"]] +background: Background = Field( + default=0, + description='Background value to subtract for deconvolution. Only used when decon_processing is set to GPU. This can either be a literal number, "auto" which uses the median of the last slice, or "second_last" which uses the median of the last slice.' +) +class DeconvolutionParams(BaseModel, arbitrary_types_allowed=True): + """ + Parameters for the optional deconvolution step + """ + decon_processing: DeconvolutionChoice = decon_processing + psf: List[NDArray] = psf + psf_num_iter: NonNegativeInt = psf_num_iter + background: Union[float, Literal["auto", "second_last"]] = background diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py new file mode 100644 index 0000000..dc40c0c --- /dev/null +++ b/core/lls_core/models/deskew.py @@ -0,0 +1,97 @@ +from __future__ import annotations +# class for initializing lattice data and setting metadata +# TODO: handle scenes +from pydantic import Field, NonNegativeFloat, validator, root_validator +# from numpy.typing import NDArray + +from typing import Any, Tuple +from typing_extensions import Self, TYPE_CHECKING + +import pyclesperanto_prototype as cle + +from lls_core import DeskewDirection +from xarray import DataArray + +from lls_core.models.utils import FieldAccessMixin, enum_choices + +if TYPE_CHECKING: + from aicsimageio.types import PhysicalPixelSizes + +class DefinedPixelSizes(FieldAccessMixin): + """ + Like PhysicalPixelSizes, but it's a dataclass, and + none of its fields are None + """ + X: NonNegativeFloat = 0.1499219272808386 + Y: NonNegativeFloat = 0.1499219272808386 + Z: NonNegativeFloat = 0.3 + + @classmethod + def from_physical(cls, pixels: PhysicalPixelSizes) -> Self: + from lls_core.utils import raise_if_none + + return DefinedPixelSizes( + X=raise_if_none(pixels.X, "All pixels must be defined"), + Y=raise_if_none(pixels.Y, "All pixels must be defined"), + Z=raise_if_none(pixels.Z, "All pixels must be defined"), + ) + + +class DeskewParams(FieldAccessMixin, arbitrary_types_allowed=True): + image: DataArray = Field( + description="A 3-5D array containing the image data" + ) + skew: DeskewDirection = Field( + default=DeskewDirection.Y, + description=f"Axis along which to deskew the image. Choices: {enum_choices(DeskewDirection)}" + ) + angle: float = Field( + default=30.0, + description="Angle of deskewing, in degrees" + ) + physical_pixel_sizes: DefinedPixelSizes = Field( + default_factory=DefinedPixelSizes, + description="Pixel size of the microscope, in microns" +) + deskew_vol_shape: Tuple[int, ...] = Field( + init_var=False, + default=None, + description="Dimensions of the deskewed output. This is set automatically based on other input parameters, and doesn't need to be provided by the user." + ) + + deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False, default=None, description="Deskewing transformation function. This is set automatically based on other input parameters, and doesn't need to be provided by the user.") + + @property + def dims(self): + return self.image.dims + + @validator("image", pre=True) + def reshaping(cls, v: Any): + # This allows a user to pass in any array-like object and have it + # converted and reshaped appropriately + array = DataArray(v) + if not set(array.dims).issuperset({"X", "Y", "Z"}): + raise ValueError("The input array must at least have XYZ coordinates") + if "T" not in array.dims: + array = array.expand_dims("T") + if "C" not in array.dims: + array = array.expand_dims("C") + return array.transpose("T", "C", "Z", "Y", "X") + + def get_3d_slice(self) -> DataArray: + return self.image.sel(C=0, T=0) + + @root_validator(pre=True) + def set_deskew(cls, values: dict) -> dict: + """ + Sets the default deskew shape values if the user has not provided them + """ + # process the file to get shape of final deskewed image + data: DataArray = cls.reshaping(values["data"]) + if values.get('deskew_vol_shape') is None: + if values.get('deskew_affine_transform') is None: + # If neither has been set, calculate them ourselves + values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(data.sel(C=0, T=0).to_numpy(), values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) + else: + raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") + return values diff --git a/core/lls_core/lattice_data.py b/core/lls_core/models/lattice_data.py similarity index 69% rename from core/lls_core/lattice_data.py rename to core/lls_core/models/lattice_data.py index 7455ca1..e08dc41 100644 --- a/core/lls_core/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -1,8 +1,7 @@ from __future__ import annotations # class for initializing lattice data and setting metadata # TODO: handle scenes -from os import getcwd -from pydantic import BaseModel, Field, NonNegativeInt, NonNegativeFloat, root_validator, validator +from pydantic import BaseModel, DirectoryPath, Field, NonNegativeInt, validator from aicsimageio.aics_image import AICSImage # from numpy.typing import NDArray import math @@ -10,52 +9,36 @@ import dask as da from itertools import groupby import tifffile -from pydantic_numpy import NDArray -from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union -from typing_extensions import TypedDict, Self -from pathlib import Path +from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, Union, overload +from typing_extensions import TypedDict, Unpack, NotRequired, get_args from aicsimageio.types import PhysicalPixelSizes import pyclesperanto_prototype as cle from tqdm import tqdm -from lls_core import DeskewDirection, DeconvolutionChoice, SaveFileType +from lls_core import DeskewDirection, DeconvolutionChoice from lls_core.deconvolution import pycuda_decon, skimage_decon from lls_core.llsz_core import crop_volume_deskew -from lls_core.utils import get_deskewed_shape +from lls_core.models.crop import CropParams +from lls_core.models.deconvolution import DeconvolutionParams +from lls_core.models.output import OutputParams, SaveFileType from lls_core.types import ArrayLike +from lls_core.models.deskew import DeskewParams from napari_workflows import Workflow -from napari.types import ShapesData from xarray import DataArray if TYPE_CHECKING: import pyclesperanto_prototype as cle from enum import Enum + from pathlib import Path + from lls_core.models.deskew import DefinedPixelSizes + from aicsimageio.types import ImageLike import logging logger = logging.getLogger(__name__) -def enum_choices(enum: Type[Enum]) -> str: - """ - Returns a human readable list of enum choices - """ - return "{" + ", ".join([it.name for it in enum]) + "}" - -class FieldAccessMixin(BaseModel): - """ - Adds methods to a BaseModel for accessing useful field information - """ - - @classmethod - def get_default(cls, field_name: str): - return cls.__fields__[field_name].get_default() - - @classmethod - def get_description(cls, field_name: str) -> str: - return cls.__fields__[field_name].field_info.description - class ProcessedVolume(BaseModel, arbitrary_types_allowed=True): """ A slice of the image processing result @@ -131,161 +114,28 @@ def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] components.append(f"T{time}") return "_".join(components) -class DefinedPixelSizes(FieldAccessMixin): - """ - Like PhysicalPixelSizes, but it's a dataclass, and - none of its fields are None - """ - X: NonNegativeFloat = 0.1499219272808386 - Y: NonNegativeFloat = 0.1499219272808386 - Z: NonNegativeFloat = 0.3 - - @classmethod - def from_physical(cls, pixels: PhysicalPixelSizes) -> Self: - from lls_core.utils import raise_if_none - - return DefinedPixelSizes( - X=raise_if_none(pixels.X, "All pixels must be defined"), - Y=raise_if_none(pixels.Y, "All pixels must be defined"), - Z=raise_if_none(pixels.Z, "All pixels must be defined"), - ) - -class DeconvolutionParams(BaseModel, arbitrary_types_allowed=True): - """ - Parameters for the optional deconvolution step - """ - decon_processing: DeconvolutionChoice = Field( - default=DeconvolutionChoice.cpu, - description=f"Hardware to use to perform the deconvolution. Choices: {enum_choices(DeconvolutionChoice)}") - psf: List[NDArray] = Field( - default=[], - description="List of Point Spread Functions to use for deconvolution. Each of which should be a 3D array." - ) - psf_num_iter: NonNegativeInt = Field( - default=10, - description="Number of iterations to perform in deconvolution" - ) - background: Union[float, Literal["auto", "second_last"]] = Field( - default=0, - description='Background value to subtract for deconvolution. Only used when decon_processing is set to GPU. This can either be a literal number, "auto" which uses the median of the last slice, or "second_last" which uses the median of the last slice.' - ) - -class CropParams(BaseModel, arbitrary_types_allowed=True): - """ - Parameters for the optional cropping step - """ - roi_list: ShapesData = Field( - description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex." - ) - z_range: Tuple[NonNegativeInt, NonNegativeInt] = Field( - default=None, - description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." - ) - -class OutputParams(FieldAccessMixin, arbitrary_types_allowed=True): - save_dir: Path = Field( - default = Path.cwd(), - description="The directory where the output data will be saved" - ) - - save_name: str = Field( - description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." - ) - - time_range: range = Field( - default = None, - description="The range of times to process. This defaults to all time points in the image array." - ) - - channel_range: range = Field( - default = None, - description="The range of channels to process. This defaults to all channels in the image array." - ) - - save_type: SaveFileType = Field( - default=SaveFileType.h5, - description=f"The data type to save the result as. This will also be used to determine the file extension of the output files. Choices: {enum_choices(SaveFileType)}" - ) - - @validator("time_range") - def default_time_range(cls, v: Any, values: dict) -> range: - """ - Sets the default time range if undefined - """ - if v is None: - return range(values["data"].sizes["T"] + 1) - return v - - @validator("channel_range") - def default_channel_range(cls, v: Any, values: dict) -> range: - """ - Sets the default channel range if undefined - """ - if v is None: - return range(values["data"].sizes["C"] + 1) - return v - -class DeskewParams(FieldAccessMixin, arbitrary_types_allowed=True): - image: DataArray = Field( - description="A 3-5D array containing the image data" - ) - skew: DeskewDirection = Field( - default=DeskewDirection.Y, - description=f"Axis along which to deskew the image. Choices: {enum_choices(DeskewDirection)}" - ) - angle: float = Field( - default=30.0, - description="Angle of deskewing, in degrees" - ) +workflow: Optional[Workflow] = Field( + default=None, + description="If defined, this is a workflow to add lightsheet processing onto" +) - physical_pixel_sizes: DefinedPixelSizes = Field( - default_factory=DefinedPixelSizes, - description="Pixel size of the microscope, in microns" - ) - - deskew_vol_shape: Tuple[int, ...] = Field( - init_var=False, - default=None, - description="Dimensions of the deskewed output. This is set automatically based on other input parameters, and doesn't need to be provided by the user." - ) +class CommonOutputArgs(TypedDict): + # Arguments + save_dir: NotRequired[DirectoryPath] + save_name: NotRequired[str] + save_type: SaveFileType + time_range: range + channel_range: range - deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False, default=None, description="Deskewing transformation function. This is set automatically based on other input parameters, and doesn't need to be provided by the user.") +class CommonDeskewArgs(TypedDict): + skew: DeskewDirection + angle: float - @property - def dims(self): - return self.image.dims - - @validator("image", pre=True) - def reshaping(cls, v: Any): - # This allows a user to pass in any array-like object and have it - # converted and reshaped appropriately - array = DataArray(v) - if not set(array.dims).issuperset({"X", "Y", "Z"}): - raise ValueError("The input array must at least have XYZ coordinates") - if "T" not in array.dims: - array = array.expand_dims("T") - if "C" not in array.dims: - array = array.expand_dims("C") - return array.transpose("T", "C", "Z", "Y", "X") - - def get_3d_slice(self) -> DataArray: - return self.image.sel(C=0, T=0) - - @root_validator(pre=True) - def set_deskew(cls, values: dict) -> dict: - """ - Sets the default deskew shape values if the user has not provided them - """ - # process the file to get shape of final deskewed image - data: DataArray = cls.reshaping(values["data"]) - if values.get('deskew_vol_shape') is None: - if values.get('deskew_affine_transform') is None: - # If neither has been set, calculate them ourselves - values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(data.sel(C=0, T=0).to_numpy(), values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) - else: - raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") - return values +class CommonLatticeArgs(CommonDeskewArgs, CommonOutputArgs): + deconvolution: Optional[DeconvolutionParams] + crop: Optional[CropParams] + workflow: Optional[Workflow] class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): """ @@ -295,16 +145,57 @@ class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): # Note: originally the save-related fields were included via composition and not inheritance # (similar to how `crop` and `workflow` are handled), but this was impractical for implementing validations + @overload + def make( + self, + image: Union[ImageLike, AICSImage], + physical_pixel_sizes: Optional[DefinedPixelSizes], + **kwargs: Unpack[CommonLatticeArgs] + ): + ... + @overload + def make( + self, + image: ArrayLike, + physical_pixel_sizes: DefinedPixelSizes, + **kwargs: Unpack[CommonLatticeArgs] + ): + ... + def make( + self, + image: Any, + physical_pixel_sizes: Optional[DefinedPixelSizes] = None, + **kwargs: Unpack[CommonLatticeArgs] + ): + if isinstance(image, get_args(ImageLike)): + image = AICSImage(image) + + meta_pixels = None + if isinstance(image, AICSImage): + if all(image.physical_pixel_sizes): + meta_pixels = DefinedPixelSizes.from_physical(image.physical_pixel_sizes) + image = image.xarray_dask_data + + image = DataArray(image) + + combined_pixels = physical_pixel_sizes or meta_pixels + if combined_pixels is None: + raise ValueError("Pixel sizes") + + LatticeData( + image=image, + physical_pixel_sizes=combined_pixels, + **kwargs + ) + + #: If this is None, then deconvolution is disabled deconvolution: Optional[DeconvolutionParams] = None #: If this is None, then cropping is disabled crop: Optional[CropParams] = None - workflow: Optional[Workflow] = Field( - default=None, - description="If defined, this is a workflow to add lightsheet processing onto" - ) + workflow: Optional[Workflow] = workflow @validator("time_range") def disjoint_time_range(cls, v: range, values: dict): diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py new file mode 100644 index 0000000..10eacc7 --- /dev/null +++ b/core/lls_core/models/output.py @@ -0,0 +1,49 @@ +from typing import Any, NotRequired, TypedDict +from pydantic import Field, validator, DirectoryPath +from strenum import StrEnum +from os import getcwd + +from lls_core.models.utils import FieldAccessMixin, enum_choices + +#Choice of File extension to save +class SaveFileType(StrEnum): + h5 = "h5" + tiff = "tiff" + +class OutputParams(FieldAccessMixin, arbitrary_types_allowed=True): + save_dir: DirectoryPath = Field( + default = getcwd(), + description="The directory where the output data will be saved" + ) + save_name: str = Field( + description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." + ) + save_type: SaveFileType = Field( + default=SaveFileType.h5, + description=f"The data type to save the result as. This will also be used to determine the file extension of the output files. Choices: {enum_choices(SaveFileType)}" +) + time_range: range = Field( + default = None, + description="The range of times to process. This defaults to all time points in the image array." + ) + channel_range: range = Field( + description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." +) + + @validator("time_range") + def default_time_range(cls, v: Any, values: dict) -> range: + """ + Sets the default time range if undefined + """ + if v is None: + return range(values["data"].sizes["T"] + 1) + return v + + @validator("channel_range") + def default_channel_range(cls, v: Any, values: dict) -> range: + """ + Sets the default channel range if undefined + """ + if v is None: + return range(values["data"].sizes["C"] + 1) + return v diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py new file mode 100644 index 0000000..4bb912e --- /dev/null +++ b/core/lls_core/models/utils.py @@ -0,0 +1,24 @@ + +from typing import Type +from enum import Enum +from pydantic import BaseModel + + +def enum_choices(enum: Type[Enum]) -> str: + """ + Returns a human readable list of enum choices + """ + return "{" + ", ".join([it.name for it in enum]) + "}" + +class FieldAccessMixin(BaseModel): + """ + Adds methods to a BaseModel for accessing useful field information + """ + + @classmethod + def get_default(cls, field_name: str): + return cls.__fields__[field_name].get_default() + + @classmethod + def get_description(cls, field_name: str) -> str: + return cls.__fields__[field_name].field_info.description diff --git a/core/lls_core/types.py b/core/lls_core/types.py index 077586f..f71844f 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -6,8 +6,9 @@ import numpy as np import pydantic_numpy.dtype as pnd from pydantic_numpy import NDArray, NDArrayFp32, NumpyModel +from xarray import DataArray -ArrayLike: TypeAlias = Union[DaskArray, NDArray, OCLArray] +ArrayLike: TypeAlias = Union[DaskArray, NDArray, OCLArray, DataArray] def is_arraylike(arr: Any) -> TypeGuard[ArrayLike]: - return isinstance(arr, (DaskArray, np.ndarray, OCLArray)) + return isinstance(arr, (DaskArray, np.ndarray, OCLArray, DataArray)) diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index 63ffe06..4183e9d 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -135,7 +135,7 @@ def import_script(script: Path): sys.modules[module_name] = module spec.loader.exec_module(module) -def import_workflow_modules(workflow: Path) -> None: +def _import_workflow_modules(workflow: Path) -> None: """ Imports all the Python files that might be used in a given custom workflow @@ -155,6 +155,13 @@ def import_workflow_modules(workflow: Path) -> None: else: logger.info(f"{counter} custom modules imported") +def workflow_from_path(workflow: Path) -> Workflow: + """ + Imports the dependency modules for a workflow, and loads it from disk + """ + from napari_workflows._io_yaml_v1 import load_workflow + _import_workflow_modules(workflow) + return load_workflow(str(workflow)) def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike], save_dir: Optional[str]=None, diff --git a/core/tests/test_deskew.py b/core/tests/test_deskew.py index 8cd06c9..1b30dc7 100644 --- a/core/tests/test_deskew.py +++ b/core/tests/test_deskew.py @@ -1,7 +1,7 @@ #filename and function name should start with "test_" when using pytest import pyclesperanto_prototype as cle import numpy as np -from lls_core.lattice_data import lattice_fom_array +from lls_core.models.lattice_data import lattice_fom_array def test_deskew(): diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 4194dcd..c5a6560 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -4,8 +4,8 @@ from typing import Union import numpy as np -from lls_core.lattice_data import LatticeData -from lls_core.workflow import import_workflow_modules +from lls_core.models.lattice_data import LatticeData +from lls_core.workflow import _import_workflow_modules from magicclass import MagicTemplate, field, magicclass, set_options from magicclass.wrappers import set_design from napari import Viewer @@ -147,7 +147,7 @@ def get_workflow(source: Union[Path, Viewer]) -> Workflow: user_workflow = WorkflowManager.install(source).workflow logger.info("Workflow installed") else: - import_workflow_modules(source) + _import_workflow_modules(source) user_workflow = load_workflow(str(source)) if not isinstance(user_workflow, Workflow): diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 19d671f..537a1a1 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -12,7 +12,7 @@ Log_Levels, SaveFileType, ) -from lls_core.lattice_data import ( +from lls_core.models.lattice_data import ( CropParams, DeconvolutionParams, DefinedPixelSizes, @@ -421,15 +421,13 @@ def _workflow_path(self) -> bool: return self.workflow_source.value == WorkflowSource.CustomPath def _make_model(self) -> Optional[Workflow]: - from lls_core.workflow import import_workflow_modules + from lls_core.workflow import _import_workflow_modules from napari_workflows._io_yaml_v1 import load_workflow if not self.fields_enabled.value: return None if self.workflow_source.value == WorkflowSource.ActiveWorkflow: return WorkflowManager.install(self.parent_viewer).workflow else: - import_workflow_modules(self.workflow_path.value) - return load_workflow(str(self.workflow_path.value)) # @magicclass(name="5. Output") class OutputFields(NapariFieldGroup, FieldGroup): diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index dac3452..9d47be2 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -21,7 +21,7 @@ from typing_extensions import Literal from typing import Any, Optional, cast, Tuple, Collection -from lls_core.lattice_data import DefinedPixelSizes, lattice_params_from_aics, img_from_array, AicsLatticeParams, PhysicalPixelSizes +from lls_core.models.lattice_data import DefinedPixelSizes, lattice_params_from_aics, img_from_array, AicsLatticeParams, PhysicalPixelSizes from lls_core.types import ArrayLike class NapariImageParams(AicsLatticeParams): From a0e4bb126c36b9a693db091f1993f1793031db76 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 00:35:18 +1000 Subject: [PATCH 025/147] Rewrite to use typer, all in on pydantic validators --- core/lls_core/cmds/__main__.py | 391 ++++++++++++++++---------- core/lls_core/models/crop.py | 52 +++- core/lls_core/models/deconvolution.py | 98 +++++-- core/lls_core/models/deskew.py | 14 +- core/lls_core/models/lattice_data.py | 40 ++- core/lls_core/models/output.py | 24 +- core/lls_core/models/utils.py | 14 +- core/lls_core/types.py | 15 +- core/pyproject.toml | 8 + 9 files changed, 444 insertions(+), 212 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index c0a0f1b..9698fcc 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -4,158 +4,249 @@ # Example for deskewing files in a folder # python lattice_processing.py --input /home/pradeep/to_deskew --output /home/pradeep/output_save/ --processing deskew +from enum import auto +import json from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple - -from lls_core.models.lattice_data import ( - CropParams, - LatticeData, - OutputParams, -) -from lls_core.models.deskew import ( - DefinedPixelSizes, - skew, - angle, - physical_pixel_sizes, -) -from lls_core.models.deconvolution import ( - Background, - DeconvolutionParams, - psf_num_iter, - decon_processing, - background -) -from lls_core.models.output import ( - SaveFileType, - save_dir, - save_type, - channel_range, - time_range -) -from napari_workflows import Workflow -from lls_core.models.crop import z_range -from pydantic_cli import run_and_exit, DefaultConfig, pydantic_class_to_parser -from pydantic import DirectoryPath, Field, NonNegativeInt, validator, FilePath, BaseModel, root_validator +from typing import Any, Dict, List, Literal, Optional, Tuple +from typing_extensions import Annotated +from strenum import StrEnum + +from lls_core.models.lattice_data import LatticeData +from lls_core.models.deskew import DefinedPixelSizes, DeskewParams +from lls_core.models.deconvolution import DeconvolutionParams +from lls_core.models.crop import CropParams +from lls_core.models.output import OutputParams from lls_core import DeconvolutionChoice, DeskewDirection +from lls_core.types import image_like_to_image +import typer + +from lls_core.models.output import SaveFileType +from toolz.dicttoolz import merge + +# class + +# class CliParams(BaseModel): +# # We can't directly use LatticeData because it uses nested models which aren't +# # supported by pydantic-cli, and also we need to modify some types to support the CLI e.g. by allowing filepath inputs + +# class Config(DefaultConfig): +# CLI_JSON_ENABLE = True +# arbitrary_types_allowed=True + +# # Deskew +# image: FilePath = Field(description="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi") +# skew: DeskewDirection = skew +# angle: float = angle +# physical_pixel_sizes: Optional[DefinedPixelSizes] = physical_pixel_sizes + +# # Deconvolution +# deconvolution: bool = False +# decon_processing: DeconvolutionChoice = decon_processing +# psf: List[FilePath] = Field(default=[], description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.") +# psf_num_iter = psf_num_iter +# background: Background = background + +# # Crop +# roi_list: List[FilePath] = Field(default=[], description="A list of paths pointing to regions of interest to crop to, in ImageJ format.") +# z_range: Tuple[NonNegativeInt, NonNegativeInt] = z_range + +# # Output +# # This one needs overriding to accommodate the default value +# save_name: str = Field(default=None, description=OutputParams.get_description("save_name")) +# save_dir: DirectoryPath = save_dir +# save_type: SaveFileType = save_type +# channel_range: range = channel_range +# time_range: range = time_range + +# # Workflow +# workflow: Optional[FilePath] = Field(default = None, description="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow.") + +# @validator("save_name", pre=True) +# def default_save_name(cls, v: Any, values: Dict): +# # Use the input file path to provide a default output filename +# if v is None and values.get("image", None): +# path: Path = values["image"] +# return path.stem + "_deskewed" +# return v + +# @root_validator(pre=True) +# def metadata_from_path(cls, values: Dict) -> Dict: + +# return values + +# def to_lattice(self) -> LatticeData: +# from aicsimageio import AICSImage + +# crop = None +# if len(self.roi_list) > 0: +# crop = CropParams( +# roi_list=[AICSImage(it).data for it in self.roi_list], +# z_range=self.z_range +# ) + +# deconvolution = None +# if self.deconvolution: +# deconvolution = DeconvolutionParams( +# decon_processing=self.decon_processing, +# psf = [AICSImage(it).data for it in self.psf], +# psf_num_iter = self.psf_num_iter, +# ) + +# workflow = None +# if self.workflow is not None: +# from lls_core.workflow import workflow_from_path +# workflow = workflow_from_path(self.workflow) + +# return LatticeData( +# image=AICSImage(self.image).xarray_dask_data, +# angle=self.angle, +# skew=self.skew, +# physical_pixel_sizes=self.physical_pixel_sizes, + +# crop=crop, +# deconvolution=deconvolution, +# workflow=workflow, + +# save_type=self.save_type, +# channel_range=self.channel_range, +# time_range=self.time_range, +# save_dir=self.save_dir, +# save_name=self.save_name +# ) + +# def make_parser(): +# parser = pydantic_class_to_parser(CliParams, default_value_override={}) +# parser.add_argument("--yaml-config", required=False, help="Path to configuration YAML file") +# return parser +class CliDeskewDirection(StrEnum): + X = auto() + Y = auto() + +def main( + image: Path = typer.Argument(help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi"), + skew: CliDeskewDirection = typer.Option( + default=DeskewParams.get_default("skew").name, + help=DeskewParams.get_description("skew") + ),# DeskewParams.make_typer_field("skew"), + angle: float = DeskewParams.make_typer_field("angle") , + pixel_sizes: Tuple[float, float, float] = typer.Option( + ( + LatticeData.get_default("physical_pixel_sizes").X, + LatticeData.get_default("physical_pixel_sizes").Y, + LatticeData.get_default("physical_pixel_sizes").Z, + ), help=DeskewParams.get_description("physical_pixel_sizes") + ". This takes three arguments, corresponding to the X Y and Z pixel dimensions respectively" + ), + + rois: List[Path] = typer.Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), + # Ideally this and other range values would be defined as Tuples, but these seem to be broken: https://github.com/tiangolo/typer/discussions/667 + z_start: Optional[int] = typer.Option(None, help="The index of the first Z slice to use. All prior Z slices will be discarded."), + z_end: Optional[int] = typer.Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded."), + + enable_deconvolution: Annotated[bool, typer.Option("--deconvolution/--disable-deconvolution")] = False, + decon_processing: DeconvolutionChoice = DeconvolutionParams.make_typer_field("decon_processing"), + psf: Annotated[List[Path], typer.Option(help="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.")] = [], + psf_num_iter: int = DeconvolutionParams.make_typer_field("psf_num_iter"), + background: str = DeconvolutionParams.make_typer_field("background"), + + time_start: Optional[int] = typer.Option(None, help="Index of the first time slice to use (inclusive)"), + time_end: Optional[int] = typer.Option(None, help="Index of the first time slice to use (exclusive)"), + + channel_start: Optional[int] = typer.Option(None, help="Index of the first channel slice to use (inclusive)"), + channel_end: Optional[int] = typer.Option(None, help="Index of the first channel slice to use (exclusive)"), + + save_dir: Path = OutputParams.make_typer_field("save_dir"), + save_name: Optional[str] = OutputParams.make_typer_field("save_name"), + save_type: SaveFileType = OutputParams.make_typer_field("save_type"), + + workflow: Optional[Path] = typer.Option(None, help="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow."), + json_config: Optional[Path] = typer.Option(None), + yaml_config: Optional[Path] = typer.Option(None) +): + cli_args = dict( + image=image, + angle=angle, + skew=DeskewDirection[skew.name], + physical_pixel_sizes=DefinedPixelSizes( + X=pixel_sizes[0], + Y=pixel_sizes[1], + Z=pixel_sizes[2], + ), + crop=None if len(rois) == 0 else dict( + roi_list=rois, + z_range=(z_start, z_end) + ), + deconvolution=None if not enable_deconvolution else dict( + decon_processing = decon_processing, + psf = psf, + psf_num_iter = psf_num_iter, + background = background + ), + workflow=None, + + time_range=(time_start, time_end), + channel_range=(channel_start, channel_end), + save_dir=save_dir, + save_name=save_name, + save_type=save_type + ) -class - -class CliParams(BaseModel): - # We can't directly use LatticeData because it uses nested models which aren't - # supported by pydantic-cli, and also we need to modify some types to support the CLI e.g. by allowing filepath inputs - - class Config(DefaultConfig): - CLI_JSON_ENABLE = True - arbitrary_types_allowed=True - - # Deskew - image: FilePath = Field(description="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi") - skew: DeskewDirection = skew - angle: float = angle - physical_pixel_sizes: Optional[DefinedPixelSizes] = physical_pixel_sizes - - # Deconvolution - deconvolution: bool = False - decon_processing: DeconvolutionChoice = decon_processing - psf: List[FilePath] = Field(default=[], description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.") - psf_num_iter = psf_num_iter - background: Background = background - - # Crop - roi_list: List[FilePath] = Field(default=[], description="A list of paths pointing to regions of interest to crop to, in ImageJ format.") - z_range: Tuple[NonNegativeInt, NonNegativeInt] = z_range - - # Output - # This one needs overriding to accommodate the default value - save_name: str = Field(default=None, description=OutputParams.get_description("save_name")) - save_dir: DirectoryPath = save_dir - save_type: SaveFileType = save_type - channel_range: range = channel_range - time_range: range = time_range - - # Workflow - workflow: Optional[FilePath] = Field(default = None, description="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow.") - - @validator("save_name", pre=True) - def default_save_name(cls, v: Any, values: Dict): - # Use the input file path to provide a default output filename - if v is None and values.get("image", None): - path: Path = values["image"] - return path.stem + "_deskewed" - return v - - @root_validator(pre=True) - def metadata_from_path(cls, values: Dict) -> Dict: - - return values - - def to_lattice(self) -> LatticeData: - from aicsimageio import AICSImage - - crop = None - if len(self.roi_list) > 0: - crop = CropParams( - roi_list=[AICSImage(it).data for it in self.roi_list], - z_range=self.z_range - ) - - deconvolution = None - if self.deconvolution: - deconvolution = DeconvolutionParams( - decon_processing=self.decon_processing, - psf = [AICSImage(it).data for it in self.psf], - psf_num_iter = self.psf_num_iter, - ) - - workflow = None - if self.workflow is not None: - from lls_core.workflow import workflow_from_path - workflow = workflow_from_path(self.workflow) - - return LatticeData( - image=AICSImage(self.image).xarray_dask_data, - angle=self.angle, - skew=self.skew, - physical_pixel_sizes=self.physical_pixel_sizes, - - crop=crop, - deconvolution=deconvolution, - workflow=workflow, - - save_type=self.save_type, - channel_range=self.channel_range, - time_range=self.time_range, - save_dir=self.save_dir, - save_name=self.save_name - ) - -def make_parser(): - parser = pydantic_class_to_parser(CliParams, default_value_override={}) - parser.add_argument("--yaml-config", required=False, help="Path to configuration YAML file") - return parser - -if __name__ == '__main__': - parser = make_parser() - args = parser.parse_args() - arg_dict = vars(args) - - json_path = arg_dict.pop("json-config", None) - json_config = {} - if json_path: + json_args = {} + if json_config is not None: import json - with open(json_path) as fp: - json_config = json.load(fp) - - yaml_path = arg_dict.pop("yaml-config", None) - yaml_config = {} - if yaml_path: - import yaml - with open(yaml_path) as fp: - json_config = yaml.safe_load(fp) - - config = CliParams( - **json_config, - **yaml_config, - **arg_dict + with json_config.open() as fp: + json_args = json.load(fp) + + yaml_args = {} + if yaml_config is not None: + with yaml_config.open() as fp: + from yaml import safe_load + yaml_args = safe_load(fp) + + return LatticeData.parse_obj(merge(yaml_args, json_args, cli_args)) + + # img_data = image_like_to_image(image) + + # crop = None + # if len(rois) > 0: + # crop = CropParams.from_img_metadata( + # img_data, + # roi_list=[image_like_to_image(it) for it in rois], + # z_range=z_range + # ) + + # decon = None + # if enable_deconvolution: + # decon = DeconvolutionParams( + # decon_processing=decon_processing, + # # background= + # ) + + LatticeData( ) - config.to_lattice().process().save_image() + +if __name__ == '__main__': + typer.run(main) + # parser = make_parser() + # args = parser.parse_args() + # arg_dict = vars(args) + + # json_path = arg_dict.pop("json-config", None) + # json_config = {} + # if json_path: + # import json + # with open(json_path) as fp: + # json_config = json.load(fp) + + # yaml_path = arg_dict.pop("yaml-config", None) + # yaml_config = {} + # if yaml_path: + # import yaml + # with open(yaml_path) as fp: + # json_config = yaml.safe_load(fp) + + # config = CliParams( + # **json_config, + # **yaml_config, + # **arg_dict + # ) + # config.to_lattice().process().save_image() diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index 822bc98..1751f97 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -1,18 +1,50 @@ - -from typing import Tuple -from pydantic import BaseModel, Field, NonNegativeInt +from typing import List, Optional, Tuple, TYPE_CHECKING, Union +from typing_extensions import Self +from pydantic import Field, NonNegativeInt from napari.types import ShapesData +from xarray import DataArray +from lls_core.models.utils import FieldAccessMixin +from lls_core.types import image_like_to_image, ImageLike + +if TYPE_CHECKING: + from lls_core.types import ArrayLike + from aicsimageio import AICSImage + -z_range: Tuple[NonNegativeInt, NonNegativeInt] = Field( - default=None, - description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." -) -class CropParams(BaseModel, arbitrary_types_allowed=True): +class CropParams(FieldAccessMixin, arbitrary_types_allowed=True): """ Parameters for the optional cropping step """ - roi_list: ShapesData = Field( + roi_list: List[DataArray] = Field( description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex." ) - z_range: Tuple[NonNegativeInt, NonNegativeInt] = z_range + z_range: Tuple[NonNegativeInt, NonNegativeInt] = Field( + default=None, + description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." + ) + # @classmethod + # def make( + # cls, + # roi_list: List[Union[ArrayLike, ImageLike]], + # z_range: Tuple[NonNegativeInt, NonNegativeInt] + # ) -> Self: + # return CropParams( + # roi_list=[image_like_to_image(it) for it in roi_list], + # z_range=z_range + # ) + + # @classmethod + # def from_img_metadata( + # cls, + # img: DataArray, + # roi_list: List[Union[ArrayLike, ImageLike]], + # z_range: Optional[Tuple[NonNegativeInt, NonNegativeInt]] + # ) -> Self: + # if z_range is None: + # z_range = (0, img.sizes["Z"]) + + # return CropParams.make( + # roi_list=roi_list, + # z_range=z_range + # ) diff --git a/core/lls_core/models/deconvolution.py b/core/lls_core/models/deconvolution.py index 993f29e..87fb22f 100644 --- a/core/lls_core/models/deconvolution.py +++ b/core/lls_core/models/deconvolution.py @@ -1,34 +1,82 @@ -from pydantic import BaseModel, Field, NonNegativeInt -from pydantic_numpy import NDArray +from pydantic import Field, NonNegativeInt, validator -from typing import List, Literal, Union +from typing import Any, List, Literal, Optional, Union, TYPE_CHECKING +from typing_extensions import Self, TypedDict, Unpack +from xarray import DataArray from lls_core import DeconvolutionChoice -from lls_core.models.utils import enum_choices - -decon_processing: DeconvolutionChoice = Field( - default=DeconvolutionChoice.cpu, - description=f"Hardware to use to perform the deconvolution. Choices: {enum_choices(DeconvolutionChoice)}") -psf: List[NDArray] = Field( - default=[], - description="List of Point Spread Functions to use for deconvolution. Each of which should be a 3D array." -) -psf_num_iter: NonNegativeInt = Field( - default=10, - description="Number of iterations to perform in deconvolution" -) +from lls_core.models.utils import enum_choices, FieldAccessMixin + +from lls_core.types import image_like_to_image, ImageLike + Background = Union[float, Literal["auto", "second_last"]] -background: Background = Field( - default=0, - description='Background value to subtract for deconvolution. Only used when decon_processing is set to GPU. This can either be a literal number, "auto" which uses the median of the last slice, or "second_last" which uses the median of the last slice.' -) -class DeconvolutionParams(BaseModel, arbitrary_types_allowed=True): +class MakeKwargs(TypedDict, total=False): + decon_processing: Union[DeconvolutionChoice, str] + psf: List[ImageLike] + psf_num_iter: int + background: str + +class DeconvolutionParams(FieldAccessMixin, arbitrary_types_allowed=True): """ Parameters for the optional deconvolution step """ - decon_processing: DeconvolutionChoice = decon_processing - psf: List[NDArray] = psf - psf_num_iter: NonNegativeInt = psf_num_iter - background: Union[float, Literal["auto", "second_last"]] = background + decon_processing: DeconvolutionChoice = Field( + default=DeconvolutionChoice.cpu, + description=f"Hardware to use to perform the deconvolution. Choices: {enum_choices(DeconvolutionChoice)}" + ) + psf: List[DataArray] = Field( + default=[], + description="List of Point Spread Functions to use for deconvolution. Each of which should be a 3D array." + ) + psf_num_iter: NonNegativeInt = Field( + default=10, + description="Number of iterations to perform in deconvolution" + ) + background: Background = Field( + default=0, + description='Background value to subtract for deconvolution. Only used when decon_processing is set to GPU. This can either be a literal number, "auto" which uses the median of the last slice, or "second_last" which uses the median of the last slice.' + ) + + @validator("decon_processing", pre=True) + def convert_decon(cls, v: Any): + if isinstance(v, str): + return DeconvolutionChoice[v] + return v + + convert_image = validator("psf", pre=True, each_item=True, allow_reuse=True)(image_like_to_image) + + # @classmethod + # def make( + # cls, + # **kwargs: Unpack[MakeKwargs] + # ) -> Self: + # dest= { } + # decon_processing = kwargs.pop("decon_processing", None) + # if isinstance(decon_processing, DeconvolutionChoice): + # dest["decon_processing"] = decon_processing + # elif decon_processing is not None: + # dest["decon_processing"] = DeconvolutionChoice(decon_processing) + + # DeconvolutionParams(**dest) + # dest + # 1 + "a" + + # DeconvolutionParams(**kwargs) + + # background_processed: Background + # try: + # background_processed = float(background) + # except Exception: + # if background == "auto" or background == "second_last": + # background_processed = background + # else: + # raise Exception("Invalid background option") + + # return DeconvolutionParams( + # decon_processing=decon_processing, + # background=background_processed, + # psf = [image_like_to_image(it) for it in psf], + # psf_num_iter=psf_num_iter + # ) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index dc40c0c..a074141 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -4,7 +4,7 @@ from pydantic import Field, NonNegativeFloat, validator, root_validator # from numpy.typing import NDArray -from typing import Any, Tuple +from typing import Any, Literal, Tuple from typing_extensions import Self, TYPE_CHECKING import pyclesperanto_prototype as cle @@ -13,10 +13,14 @@ from xarray import DataArray from lls_core.models.utils import FieldAccessMixin, enum_choices +from lls_core.types import image_like_to_image +from lls_core.utils import get_deskewed_shape if TYPE_CHECKING: from aicsimageio.types import PhysicalPixelSizes +# DeskewDirection = Literal["X", "Y"] + class DefinedPixelSizes(FieldAccessMixin): """ Like PhysicalPixelSizes, but it's a dataclass, and @@ -65,11 +69,13 @@ class DeskewParams(FieldAccessMixin, arbitrary_types_allowed=True): def dims(self): return self.image.dims + # convert_image = validator("image", pre=True, allow_reuse=True)(image_like_to_image) + @validator("image", pre=True) def reshaping(cls, v: Any): # This allows a user to pass in any array-like object and have it # converted and reshaped appropriately - array = DataArray(v) + array = image_like_to_image(v) if not set(array.dims).issuperset({"X", "Y", "Z"}): raise ValueError("The input array must at least have XYZ coordinates") if "T" not in array.dims: @@ -87,11 +93,11 @@ def set_deskew(cls, values: dict) -> dict: Sets the default deskew shape values if the user has not provided them """ # process the file to get shape of final deskewed image - data: DataArray = cls.reshaping(values["data"]) + data: DataArray = cls.reshaping(values["image"]) if values.get('deskew_vol_shape') is None: if values.get('deskew_affine_transform') is None: # If neither has been set, calculate them ourselves - values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(data.sel(C=0, T=0).to_numpy(), values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) + values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(data.isel(C=0, T=0).to_numpy(), values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) else: raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") return values diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index e08dc41..2b3f7e8 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -1,7 +1,8 @@ from __future__ import annotations +from os import PathLike # class for initializing lattice data and setting metadata # TODO: handle scenes -from pydantic import BaseModel, DirectoryPath, Field, NonNegativeInt, validator +from pydantic import BaseModel, DirectoryPath, Field, NonNegativeInt, root_validator, validator from aicsimageio.aics_image import AICSImage # from numpy.typing import NDArray import math @@ -27,11 +28,10 @@ from lls_core.models.deskew import DeskewParams from napari_workflows import Workflow from xarray import DataArray +from pathlib import Path if TYPE_CHECKING: import pyclesperanto_prototype as cle - from enum import Enum - from pathlib import Path from lls_core.models.deskew import DefinedPixelSizes from aicsimageio.types import ImageLike @@ -123,7 +123,6 @@ def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] class CommonOutputArgs(TypedDict): # Arguments save_dir: NotRequired[DirectoryPath] - save_name: NotRequired[str] save_type: SaveFileType time_range: range channel_range: range @@ -146,25 +145,31 @@ class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): # (similar to how `crop` and `workflow` are handled), but this was impractical for implementing validations @overload + @classmethod def make( - self, + cls, image: Union[ImageLike, AICSImage], - physical_pixel_sizes: Optional[DefinedPixelSizes], + physical_pixel_sizes: Optional[DefinedPixelSizes] = None, + save_name: Optional[str] = None, **kwargs: Unpack[CommonLatticeArgs] ): ... @overload + @classmethod def make( - self, + cls, image: ArrayLike, physical_pixel_sizes: DefinedPixelSizes, + save_name: str, **kwargs: Unpack[CommonLatticeArgs] ): ... + @classmethod def make( - self, + cls, image: Any, physical_pixel_sizes: Optional[DefinedPixelSizes] = None, + save_name: Optional[str] = None, **kwargs: Unpack[CommonLatticeArgs] ): if isinstance(image, get_args(ImageLike)): @@ -185,10 +190,10 @@ def make( LatticeData( image=image, physical_pixel_sizes=combined_pixels, + save_name=save_name, **kwargs ) - #: If this is None, then deconvolution is disabled deconvolution: Optional[DeconvolutionParams] = None @@ -197,12 +202,21 @@ def make( workflow: Optional[Workflow] = workflow + @root_validator(pre=True) + def default_save_name(cls, values: dict): + # This needs to be a root validator to ensure it runs before the + # reshaping validator. We can't override that either since it's + # a field validator and can't modify save_name + if values.get("save_name", None) is None and isinstance(values["image"], PathLike): + values["save_name"] = Path(values["image"]).stem + return values + @validator("time_range") def disjoint_time_range(cls, v: range, values: dict): """ Validates that the time range is within the range of channels in our array """ - max_time = values["data"].sizes["T"] + max_time = values["image"].sizes["T"] if v.start < 0: raise ValueError("The lowest valid start value is 0") if v.stop > max_time: @@ -214,7 +228,7 @@ def disjoint_channel_range(cls, v: range, values: dict): """ Validates that the channel range is within the range of channels in our array """ - max_channel = values["data"].sizes["C"] + max_channel = values["image"].sizes["C"] if v.start < 0: raise ValueError("The lowest valid start value is 0") if v.stop > max_channel: @@ -223,13 +237,13 @@ def disjoint_channel_range(cls, v: range, values: dict): @validator("channel_range") def channel_range_subset(cls, v: range, values: dict): - if min(v) < 0 or max(v) > values["data"].sizes["C"]: + if min(v) < 0 or max(v) > values["image"].sizes["C"]: raise ValueError("The output channel range must be a subset of the total available channels") return v @validator("time_range") def time_range_subset(cls, v: range, values: dict): - if min(v) < 0 or max(v) > values["data"].sizes["T"]: + if min(v) < 0 or max(v) > values["image"].sizes["T"]: raise ValueError("The output time range must be a subset of the total available time points") return v diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 10eacc7..16b0aab 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -1,4 +1,4 @@ -from typing import Any, NotRequired, TypedDict +from typing import Any from pydantic import Field, validator, DirectoryPath from strenum import StrEnum from os import getcwd @@ -30,20 +30,30 @@ class OutputParams(FieldAccessMixin, arbitrary_types_allowed=True): description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." ) - @validator("time_range") - def default_time_range(cls, v: Any, values: dict) -> range: + @validator("time_range", pre=True) + def parse_time_range(cls, v: Any, values: dict) -> Any: """ Sets the default time range if undefined """ + default_start = 0 + default_end = values["image"].sizes["T"] if v is None: - return range(values["data"].sizes["T"] + 1) + return range(default_start, default_end) + elif isinstance(v, tuple) and len(v) == 2: + # Allow 2-tuples to be used as input for this field + return range(v[0] or default_start, v[1] or default_end) return v - @validator("channel_range") - def default_channel_range(cls, v: Any, values: dict) -> range: + @validator("channel_range", pre=True) + def parse_channel_range(cls, v: Any, values: dict) -> Any: """ Sets the default channel range if undefined """ + default_start = 0 + default_end = values["image"].sizes["C"] if v is None: - return range(values["data"].sizes["C"] + 1) + return range(default_start, default_end) + elif isinstance(v, tuple) and len(v) == 2: + # Allow 2-tuples to be used as input for this field + return range(v[0] or default_start, v[1] or default_end) return v diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py index 4bb912e..35f407d 100644 --- a/core/lls_core/models/utils.py +++ b/core/lls_core/models/utils.py @@ -1,7 +1,9 @@ -from typing import Type +from typing import Any, Type from enum import Enum from pydantic import BaseModel +from typer import Option +from typer.models import OptionInfo def enum_choices(enum: Type[Enum]) -> str: @@ -16,9 +18,17 @@ class FieldAccessMixin(BaseModel): """ @classmethod - def get_default(cls, field_name: str): + def get_default(cls, field_name: str) -> Any: return cls.__fields__[field_name].get_default() @classmethod def get_description(cls, field_name: str) -> str: return cls.__fields__[field_name].field_info.description + + @classmethod + def make_typer_field(cls, field_name: str, extra_description: str = "") -> Any: + field = cls.__fields__[field_name] + return Option( + default = field.get_default(), + help=field.field_info.description + extra_description + ) diff --git a/core/lls_core/types.py b/core/lls_core/types.py index f71844f..7249aab 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -4,11 +4,24 @@ # from numpy.typing import NDArray from pyopencl.array import Array as OCLArray import numpy as np -import pydantic_numpy.dtype as pnd from pydantic_numpy import NDArray, NDArrayFp32, NumpyModel from xarray import DataArray +from aicsimageio import AICSImage +from os import PathLike, fspath ArrayLike: TypeAlias = Union[DaskArray, NDArray, OCLArray, DataArray] def is_arraylike(arr: Any) -> TypeGuard[ArrayLike]: return isinstance(arr, (DaskArray, np.ndarray, OCLArray, DataArray)) + +ImageLike: TypeAlias = Union[PathLike, AICSImage, ArrayLike] +def image_like_to_image(img: ImageLike) -> DataArray: + """ + Converts an image in one of many formats to a DataArray + """ + if isinstance(img, PathLike): + img = AICSImage(fspath(img)) + if isinstance(img, AICSImage): + return img.xarray_dask_data + else: + return DataArray(img) diff --git a/core/pyproject.toml b/core/pyproject.toml index 04c5c87..167125f 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -79,3 +79,11 @@ psf = [ [project.scripts] lls-pipeline = "lls_core.cmds.__main__:main" + +[tool.mypy] +plugins = [ + "pydantic.mypy" +] + +[tool.pydantic-mypy] +init_typed = false From 4a9155062f85a600f24d26ab86fe8584c4ac1721 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 00:39:15 +1000 Subject: [PATCH 026/147] Clean up commented/unused code --- core/lls_core/cmds/__main__.py | 143 +------------------------- core/lls_core/models/crop.py | 35 +------ core/lls_core/models/deconvolution.py | 38 +------ core/lls_core/models/deskew.py | 3 +- core/lls_core/models/lattice_data.py | 55 +--------- 5 files changed, 7 insertions(+), 267 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 9698fcc..a7be6a1 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -5,119 +5,21 @@ # python lattice_processing.py --input /home/pradeep/to_deskew --output /home/pradeep/output_save/ --processing deskew from enum import auto -import json from pathlib import Path -from typing import Any, Dict, List, Literal, Optional, Tuple +from typing import List, Optional, Tuple from typing_extensions import Annotated from strenum import StrEnum from lls_core.models.lattice_data import LatticeData from lls_core.models.deskew import DefinedPixelSizes, DeskewParams from lls_core.models.deconvolution import DeconvolutionParams -from lls_core.models.crop import CropParams from lls_core.models.output import OutputParams from lls_core import DeconvolutionChoice, DeskewDirection -from lls_core.types import image_like_to_image import typer from lls_core.models.output import SaveFileType from toolz.dicttoolz import merge -# class - -# class CliParams(BaseModel): -# # We can't directly use LatticeData because it uses nested models which aren't -# # supported by pydantic-cli, and also we need to modify some types to support the CLI e.g. by allowing filepath inputs - -# class Config(DefaultConfig): -# CLI_JSON_ENABLE = True -# arbitrary_types_allowed=True - -# # Deskew -# image: FilePath = Field(description="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi") -# skew: DeskewDirection = skew -# angle: float = angle -# physical_pixel_sizes: Optional[DefinedPixelSizes] = physical_pixel_sizes - -# # Deconvolution -# deconvolution: bool = False -# decon_processing: DeconvolutionChoice = decon_processing -# psf: List[FilePath] = Field(default=[], description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.") -# psf_num_iter = psf_num_iter -# background: Background = background - -# # Crop -# roi_list: List[FilePath] = Field(default=[], description="A list of paths pointing to regions of interest to crop to, in ImageJ format.") -# z_range: Tuple[NonNegativeInt, NonNegativeInt] = z_range - -# # Output -# # This one needs overriding to accommodate the default value -# save_name: str = Field(default=None, description=OutputParams.get_description("save_name")) -# save_dir: DirectoryPath = save_dir -# save_type: SaveFileType = save_type -# channel_range: range = channel_range -# time_range: range = time_range - -# # Workflow -# workflow: Optional[FilePath] = Field(default = None, description="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow.") - -# @validator("save_name", pre=True) -# def default_save_name(cls, v: Any, values: Dict): -# # Use the input file path to provide a default output filename -# if v is None and values.get("image", None): -# path: Path = values["image"] -# return path.stem + "_deskewed" -# return v - -# @root_validator(pre=True) -# def metadata_from_path(cls, values: Dict) -> Dict: - -# return values - -# def to_lattice(self) -> LatticeData: -# from aicsimageio import AICSImage - -# crop = None -# if len(self.roi_list) > 0: -# crop = CropParams( -# roi_list=[AICSImage(it).data for it in self.roi_list], -# z_range=self.z_range -# ) - -# deconvolution = None -# if self.deconvolution: -# deconvolution = DeconvolutionParams( -# decon_processing=self.decon_processing, -# psf = [AICSImage(it).data for it in self.psf], -# psf_num_iter = self.psf_num_iter, -# ) - -# workflow = None -# if self.workflow is not None: -# from lls_core.workflow import workflow_from_path -# workflow = workflow_from_path(self.workflow) - -# return LatticeData( -# image=AICSImage(self.image).xarray_dask_data, -# angle=self.angle, -# skew=self.skew, -# physical_pixel_sizes=self.physical_pixel_sizes, - -# crop=crop, -# deconvolution=deconvolution, -# workflow=workflow, - -# save_type=self.save_type, -# channel_range=self.channel_range, -# time_range=self.time_range, -# save_dir=self.save_dir, -# save_name=self.save_name -# ) - -# def make_parser(): -# parser = pydantic_class_to_parser(CliParams, default_value_override={}) -# parser.add_argument("--yaml-config", required=False, help="Path to configuration YAML file") -# return parser class CliDeskewDirection(StrEnum): X = auto() Y = auto() @@ -204,49 +106,6 @@ def main( return LatticeData.parse_obj(merge(yaml_args, json_args, cli_args)) - # img_data = image_like_to_image(image) - - # crop = None - # if len(rois) > 0: - # crop = CropParams.from_img_metadata( - # img_data, - # roi_list=[image_like_to_image(it) for it in rois], - # z_range=z_range - # ) - - # decon = None - # if enable_deconvolution: - # decon = DeconvolutionParams( - # decon_processing=decon_processing, - # # background= - # ) - - LatticeData( - ) if __name__ == '__main__': typer.run(main) - # parser = make_parser() - # args = parser.parse_args() - # arg_dict = vars(args) - - # json_path = arg_dict.pop("json-config", None) - # json_config = {} - # if json_path: - # import json - # with open(json_path) as fp: - # json_config = json.load(fp) - - # yaml_path = arg_dict.pop("yaml-config", None) - # yaml_config = {} - # if yaml_path: - # import yaml - # with open(yaml_path) as fp: - # json_config = yaml.safe_load(fp) - - # config = CliParams( - # **json_config, - # **yaml_config, - # **arg_dict - # ) - # config.to_lattice().process().save_image() diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index 1751f97..3000181 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -1,15 +1,7 @@ -from typing import List, Optional, Tuple, TYPE_CHECKING, Union -from typing_extensions import Self +from typing import List, Tuple from pydantic import Field, NonNegativeInt -from napari.types import ShapesData from xarray import DataArray from lls_core.models.utils import FieldAccessMixin -from lls_core.types import image_like_to_image, ImageLike - -if TYPE_CHECKING: - from lls_core.types import ArrayLike - from aicsimageio import AICSImage - class CropParams(FieldAccessMixin, arbitrary_types_allowed=True): @@ -23,28 +15,3 @@ class CropParams(FieldAccessMixin, arbitrary_types_allowed=True): default=None, description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." ) - # @classmethod - # def make( - # cls, - # roi_list: List[Union[ArrayLike, ImageLike]], - # z_range: Tuple[NonNegativeInt, NonNegativeInt] - # ) -> Self: - # return CropParams( - # roi_list=[image_like_to_image(it) for it in roi_list], - # z_range=z_range - # ) - - # @classmethod - # def from_img_metadata( - # cls, - # img: DataArray, - # roi_list: List[Union[ArrayLike, ImageLike]], - # z_range: Optional[Tuple[NonNegativeInt, NonNegativeInt]] - # ) -> Self: - # if z_range is None: - # z_range = (0, img.sizes["Z"]) - - # return CropParams.make( - # roi_list=roi_list, - # z_range=z_range - # ) diff --git a/core/lls_core/models/deconvolution.py b/core/lls_core/models/deconvolution.py index 87fb22f..c2b360c 100644 --- a/core/lls_core/models/deconvolution.py +++ b/core/lls_core/models/deconvolution.py @@ -1,8 +1,8 @@ from pydantic import Field, NonNegativeInt, validator -from typing import Any, List, Literal, Optional, Union, TYPE_CHECKING -from typing_extensions import Self, TypedDict, Unpack +from typing import Any, List, Literal, Union +from typing_extensions import TypedDict from xarray import DataArray @@ -46,37 +46,3 @@ def convert_decon(cls, v: Any): return v convert_image = validator("psf", pre=True, each_item=True, allow_reuse=True)(image_like_to_image) - - # @classmethod - # def make( - # cls, - # **kwargs: Unpack[MakeKwargs] - # ) -> Self: - # dest= { } - # decon_processing = kwargs.pop("decon_processing", None) - # if isinstance(decon_processing, DeconvolutionChoice): - # dest["decon_processing"] = decon_processing - # elif decon_processing is not None: - # dest["decon_processing"] = DeconvolutionChoice(decon_processing) - - # DeconvolutionParams(**dest) - # dest - # 1 + "a" - - # DeconvolutionParams(**kwargs) - - # background_processed: Background - # try: - # background_processed = float(background) - # except Exception: - # if background == "auto" or background == "second_last": - # background_processed = background - # else: - # raise Exception("Invalid background option") - - # return DeconvolutionParams( - # decon_processing=decon_processing, - # background=background_processed, - # psf = [image_like_to_image(it) for it in psf], - # psf_num_iter=psf_num_iter - # ) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index a074141..99af70b 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -2,9 +2,8 @@ # class for initializing lattice data and setting metadata # TODO: handle scenes from pydantic import Field, NonNegativeFloat, validator, root_validator -# from numpy.typing import NDArray -from typing import Any, Literal, Tuple +from typing import Any, Tuple from typing_extensions import Self, TYPE_CHECKING import pyclesperanto_prototype as cle diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 2b3f7e8..af7012b 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -4,15 +4,14 @@ # TODO: handle scenes from pydantic import BaseModel, DirectoryPath, Field, NonNegativeInt, root_validator, validator from aicsimageio.aics_image import AICSImage -# from numpy.typing import NDArray import math from dask.array.core import Array as DaskArray import dask as da from itertools import groupby import tifffile -from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple, Union, overload -from typing_extensions import TypedDict, Unpack, NotRequired, get_args +from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple +from typing_extensions import TypedDict, NotRequired from aicsimageio.types import PhysicalPixelSizes import pyclesperanto_prototype as cle @@ -144,56 +143,6 @@ class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): # Note: originally the save-related fields were included via composition and not inheritance # (similar to how `crop` and `workflow` are handled), but this was impractical for implementing validations - @overload - @classmethod - def make( - cls, - image: Union[ImageLike, AICSImage], - physical_pixel_sizes: Optional[DefinedPixelSizes] = None, - save_name: Optional[str] = None, - **kwargs: Unpack[CommonLatticeArgs] - ): - ... - @overload - @classmethod - def make( - cls, - image: ArrayLike, - physical_pixel_sizes: DefinedPixelSizes, - save_name: str, - **kwargs: Unpack[CommonLatticeArgs] - ): - ... - @classmethod - def make( - cls, - image: Any, - physical_pixel_sizes: Optional[DefinedPixelSizes] = None, - save_name: Optional[str] = None, - **kwargs: Unpack[CommonLatticeArgs] - ): - if isinstance(image, get_args(ImageLike)): - image = AICSImage(image) - - meta_pixels = None - if isinstance(image, AICSImage): - if all(image.physical_pixel_sizes): - meta_pixels = DefinedPixelSizes.from_physical(image.physical_pixel_sizes) - image = image.xarray_dask_data - - image = DataArray(image) - - combined_pixels = physical_pixel_sizes or meta_pixels - if combined_pixels is None: - raise ValueError("Pixel sizes") - - LatticeData( - image=image, - physical_pixel_sizes=combined_pixels, - save_name=save_name, - **kwargs - ) - #: If this is None, then deconvolution is disabled deconvolution: Optional[DeconvolutionParams] = None From 2d20bdfec7bcd30e245f1cb67d587e11da781018 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 10:24:01 +1000 Subject: [PATCH 027/147] Fix missing imports --- core/lls_core/models/lattice_data.py | 1 - core/pyproject.toml | 10 ++++++---- plugin/napari_lattice/fields.py | 21 +++++++++++---------- plugin/pyproject.toml | 6 +++++- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index af7012b..fdc36a2 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -32,7 +32,6 @@ if TYPE_CHECKING: import pyclesperanto_prototype as cle from lls_core.models.deskew import DefinedPixelSizes - from aicsimageio.types import ImageLike import logging diff --git a/core/pyproject.toml b/core/pyproject.toml index 1b2bfd8..558c1ce 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -43,18 +43,16 @@ dependencies = [ "dask[distributed]", "pyopencl", "read-roi", - "gputools", "pyclesperanto_prototype>=0.20.0", "pydantic~=1.0", + "pydantic_numpy", "npy2bdv", - "redlionfish", # Older tifffile are not compatible with aicsimageio, see: https://github.com/AllenCellModeling/aicsimageio/issues/518 "tifffile>=2023.3.15", #>=2022.8.12 "fsspec>=2022.8.2", "pyclesperanto_prototype>=0.20.0", "napari-workflows>=0.2.8", - "xarray[parallel]", "npy2bdv", "numpy", "pandas", @@ -62,9 +60,13 @@ dependencies = [ "read-roi", "resource-backed-dask-array>=0.1.0", "scikit-image", + "StrEnum", + "toolz", "tifffile", "tqdm", - "typing_extensions" + "typer", + "typing_extensions", + "xarray[parallel]", ] [project.urls] diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 537a1a1..e4ced2a 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -10,7 +10,6 @@ DeconvolutionChoice, DeskewDirection, Log_Levels, - SaveFileType, ) from lls_core.models.lattice_data import ( CropParams, @@ -20,6 +19,7 @@ LatticeData, OutputParams, ) +from lls_core.workflow import workflow_from_path from magicclass import FieldGroup, MagicTemplate, field from magicclass.fields import MagicField from magicclass.widgets import ComboBox, Label, Widget @@ -30,7 +30,6 @@ from napari_lattice.reader import NapariImageParams, lattice_params_from_napari from napari_lattice.utils import get_layers from napari_workflows import Workflow, WorkflowManager -from pydantic import ValidationError from qtpy.QtWidgets import QTabWidget from strenum import StrEnum @@ -41,6 +40,7 @@ def exception_to_html(e: BaseException) -> str: """ Converts an exception to HTML for reporting back to the user """ + from pydantic import ValidationError if isinstance(e, ValidationError): message = [] for error in e.errors(): @@ -244,14 +244,14 @@ def _get_dimension_options(self, _) -> List[str]: ) # merge_all_channels = field(False).with_options(label="Merge all Channels") dimension_order = field( - str - ).with_choices( - _get_dimension_options - ).with_options( - label="Dimension Order", - tooltip="Specifies the order of dimensions in the input images. For example, if your image is a 4D array with multiple channels along the first axis, you will specify CZYX.", - value=LastDimensionOptions.Metadata.value - ) + str + ).with_choices( + _get_dimension_options + ).with_options( + label="Dimension Order", + tooltip="Specifies the order of dimensions in the input images. For example, if your image is a 4D array with multiple channels along the first axis, you will specify CZYX.", + value=LastDimensionOptions.Metadata.value + ) skew_dir = field(DeskewDirection.Y).with_options( label="Skew Direction", tooltip="The axis along which to deskew" @@ -428,6 +428,7 @@ def _make_model(self) -> Optional[Workflow]: if self.workflow_source.value == WorkflowSource.ActiveWorkflow: return WorkflowManager.install(self.parent_viewer).workflow else: + return workflow_from_path(self.workflow_path.value) # @magicclass(name="5. Output") class OutputFields(NapariFieldGroup, FieldGroup): diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index ae61c0b..f8c6b95 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -41,9 +41,9 @@ dependencies = [ # This isn't used directly, but we need to pin this version "fsspec>=2022.8.2", # We need this Python 3.8 fix: https://github.com/hanjinliu/magic-class/pull/108 + "lls_core", "magic-class>=0.7.5", "magicgui", - "lls_core", "napari-aicsimageio>=0.7.2", "napari-spreadsheet", "napari-workflow-inspector", @@ -52,12 +52,16 @@ dependencies = [ "npy2bdv", "numpy", "pandas", + "pkg_resources", "psutil", "pyclesperanto_prototype>=0.20.0", + "pydantic", "qtpy", "tqdm", "typing_extensions", "pyyaml", + "StrEnum", + "xarray" ] [project.urls] From 5395f5ba1e0329c7069059bd25d830592230c68e Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 10:27:34 +1000 Subject: [PATCH 028/147] pydantic-numpy --- core/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pyproject.toml b/core/pyproject.toml index 558c1ce..067d5bb 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -45,7 +45,7 @@ dependencies = [ "read-roi", "pyclesperanto_prototype>=0.20.0", "pydantic~=1.0", - "pydantic_numpy", + "pydantic-numpy", "npy2bdv", # Older tifffile are not compatible with aicsimageio, see: https://github.com/AllenCellModeling/aicsimageio/issues/518 From 3f844c97f5820a97e4c4be87f8205407f200f0d3 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 10:33:47 +1000 Subject: [PATCH 029/147] Use older pydantic-numpy --- core/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pyproject.toml b/core/pyproject.toml index 067d5bb..974733c 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -45,7 +45,7 @@ dependencies = [ "read-roi", "pyclesperanto_prototype>=0.20.0", "pydantic~=1.0", - "pydantic-numpy", + "pydantic-numpy~=2.0", "npy2bdv", # Older tifffile are not compatible with aicsimageio, see: https://github.com/AllenCellModeling/aicsimageio/issues/518 From a24341753ea8282a5ce2316ff572ae41c2140cf0 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 11:00:56 +1000 Subject: [PATCH 030/147] Remove pydantic-numpy --- core/lls_core/types.py | 2 +- core/pyproject.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/lls_core/types.py b/core/lls_core/types.py index 7249aab..26691bb 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -4,7 +4,7 @@ # from numpy.typing import NDArray from pyopencl.array import Array as OCLArray import numpy as np -from pydantic_numpy import NDArray, NDArrayFp32, NumpyModel +from numpy.typing import NDArray from xarray import DataArray from aicsimageio import AICSImage from os import PathLike, fspath diff --git a/core/pyproject.toml b/core/pyproject.toml index 974733c..5911e88 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -45,7 +45,6 @@ dependencies = [ "read-roi", "pyclesperanto_prototype>=0.20.0", "pydantic~=1.0", - "pydantic-numpy~=2.0", "npy2bdv", # Older tifffile are not compatible with aicsimageio, see: https://github.com/AllenCellModeling/aicsimageio/issues/518 From d9695f03d05c385d6258b497e3c0650ca0b9ef06 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 11:18:40 +1000 Subject: [PATCH 031/147] Pyright settings in the pyproject.toml --- core/pyproject.toml | 10 ++++++++++ plugin/pyproject.toml | 10 ++++++++++ pyrightconfig.json | 13 ------------- 3 files changed, 20 insertions(+), 13 deletions(-) delete mode 100644 pyrightconfig.json diff --git a/core/pyproject.toml b/core/pyproject.toml index 5911e88..efc5f38 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -104,6 +104,16 @@ init_typed = false typeCheckingMode = "off" reportUndefinedVariable = "error" reportMissingImports = "none" +reportMissingTypeStubs = false +reportUnknownVariableType = false +reportUnknownArgumentType = false +reportUnknownLambdaType = false +reportUnknownMemberType = false +reportUnknownParameterType = false +reportUntypedFunctionDecorator = false +reportMissingTypeArgument = false +reportPrivateUsage = false +reportPrivateImportUsage = false [tool.fawltydeps] ignore_unused = [ diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index f8c6b95..a7e9a18 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -107,3 +107,13 @@ output_format = "human_detailed" typeCheckingMode = "off" reportUndefinedVariable = "error" reportMissingImports = "none" +reportMissingTypeStubs = false +reportUnknownVariableType = false +reportUnknownArgumentType = false +reportUnknownLambdaType = false +reportUnknownMemberType = false +reportUnknownParameterType = false +reportUntypedFunctionDecorator = false +reportMissingTypeArgument = false +reportPrivateUsage = false +reportPrivateImportUsage = false diff --git a/pyrightconfig.json b/pyrightconfig.json deleted file mode 100644 index 86c8d98..0000000 --- a/pyrightconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "reportMissingTypeStubs": false, - "reportUnknownVariableType": false, - "reportUnknownArgumentType": false, - "reportUnknownLambdaType": false, - "reportUnknownMemberType": false, - "reportUnknownParameterType": false, - "reportUntypedFunctionDecorator": false, - "reportMissingTypeArgument": false, - "reportPrivateUsage": false, - "reportPrivateImportUsage": false, - "typeCheckingMode": "strict" -} From 1b0b0f33fcc537f712f41f497bd6af0e978a19e2 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 15:15:05 +1000 Subject: [PATCH 032/147] Rework workflow handling, move sample data into package --- core/lls_core/cmds/__main__.py | 40 +-- core/lls_core/models/lattice_data.py | 110 ++++++-- core/lls_core/sample/LLS7_t1_ch1.czi | Bin 0 -> 6877600 bytes core/lls_core/sample/LLS7_t1_ch3.czi | Bin 0 -> 7065344 bytes core/lls_core/sample/LLS7_t2_ch1.czi | Bin 0 -> 6967744 bytes core/lls_core/sample/LLS7_t2_ch3.czi | Bin 0 -> 7337184 bytes .../lls_core/sample}/RBC_lattice.tif | Bin .../lls_core/sample}/RBC_tiny.czi | Bin core/lls_core/sample/README.txt | 4 + core/lls_core/sample/__init__.py | 9 + .../lls_core/sample}/config/README.md | 0 .../lls_core/sample}/config/config.yaml | 0 .../lls_core/sample}/config/config.yml | 0 core/lls_core/sample/multich_multi_time.tif | Bin 0 -> 3356511 bytes .../sample}/psfs/zeiss_simulated/488.czi | Bin .../sample}/psfs/zeiss_simulated/488.tif | Bin .../sample}/psfs/zeiss_simulated/561.czi | Bin .../sample}/psfs/zeiss_simulated/561.tif | Bin .../sample}/psfs/zeiss_simulated/640.czi | Bin .../sample}/psfs/zeiss_simulated/640.tif | Bin .../sample}/psfs/zeiss_simulated/README.md | 0 .../psfs/zeiss_simulated/description.txt | 6 +- core/lls_core/workflow.py | 244 +++++++++--------- core/tests/test_arg_parser.py | 13 +- 24 files changed, 259 insertions(+), 167 deletions(-) create mode 100755 core/lls_core/sample/LLS7_t1_ch1.czi create mode 100755 core/lls_core/sample/LLS7_t1_ch3.czi create mode 100755 core/lls_core/sample/LLS7_t2_ch1.czi create mode 100755 core/lls_core/sample/LLS7_t2_ch3.czi rename {sample_data => core/lls_core/sample}/RBC_lattice.tif (100%) rename {sample_data => core/lls_core/sample}/RBC_tiny.czi (100%) create mode 100755 core/lls_core/sample/README.txt create mode 100644 core/lls_core/sample/__init__.py rename {sample_data => core/lls_core/sample}/config/README.md (100%) rename {sample_data => core/lls_core/sample}/config/config.yaml (100%) rename {sample_data => core/lls_core/sample}/config/config.yml (100%) create mode 100755 core/lls_core/sample/multich_multi_time.tif rename {sample_data => core/lls_core/sample}/psfs/zeiss_simulated/488.czi (100%) rename {sample_data => core/lls_core/sample}/psfs/zeiss_simulated/488.tif (100%) rename {sample_data => core/lls_core/sample}/psfs/zeiss_simulated/561.czi (100%) rename {sample_data => core/lls_core/sample}/psfs/zeiss_simulated/561.tif (100%) rename {sample_data => core/lls_core/sample}/psfs/zeiss_simulated/640.czi (100%) rename {sample_data => core/lls_core/sample}/psfs/zeiss_simulated/640.tif (100%) rename {sample_data => core/lls_core/sample}/psfs/zeiss_simulated/README.md (100%) rename {sample_data => core/lls_core/sample}/psfs/zeiss_simulated/description.txt (98%) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index a7be6a1..22677a9 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -15,7 +15,7 @@ from lls_core.models.deconvolution import DeconvolutionParams from lls_core.models.output import OutputParams from lls_core import DeconvolutionChoice, DeskewDirection -import typer +from typer import Typer, Argument, Option from lls_core.models.output import SaveFileType from toolz.dicttoolz import merge @@ -24,14 +24,17 @@ class CliDeskewDirection(StrEnum): X = auto() Y = auto() +app = Typer(add_completion=False) + +@app.command() def main( - image: Path = typer.Argument(help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi"), - skew: CliDeskewDirection = typer.Option( + image: Path = Argument(help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi"), + skew: CliDeskewDirection = Option( default=DeskewParams.get_default("skew").name, help=DeskewParams.get_description("skew") ),# DeskewParams.make_typer_field("skew"), angle: float = DeskewParams.make_typer_field("angle") , - pixel_sizes: Tuple[float, float, float] = typer.Option( + pixel_sizes: Tuple[float, float, float] = Option( ( LatticeData.get_default("physical_pixel_sizes").X, LatticeData.get_default("physical_pixel_sizes").Y, @@ -39,30 +42,30 @@ def main( ), help=DeskewParams.get_description("physical_pixel_sizes") + ". This takes three arguments, corresponding to the X Y and Z pixel dimensions respectively" ), - rois: List[Path] = typer.Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), + rois: List[Path] = Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), # Ideally this and other range values would be defined as Tuples, but these seem to be broken: https://github.com/tiangolo/typer/discussions/667 - z_start: Optional[int] = typer.Option(None, help="The index of the first Z slice to use. All prior Z slices will be discarded."), - z_end: Optional[int] = typer.Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded."), + z_start: Optional[int] = Option(None, help="The index of the first Z slice to use. All prior Z slices will be discarded."), + z_end: Optional[int] = Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded."), - enable_deconvolution: Annotated[bool, typer.Option("--deconvolution/--disable-deconvolution")] = False, + enable_deconvolution: Annotated[bool, Option("--deconvolution/--disable-deconvolution")] = False, decon_processing: DeconvolutionChoice = DeconvolutionParams.make_typer_field("decon_processing"), - psf: Annotated[List[Path], typer.Option(help="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.")] = [], + psf: Annotated[List[Path], Option(help="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.")] = [], psf_num_iter: int = DeconvolutionParams.make_typer_field("psf_num_iter"), background: str = DeconvolutionParams.make_typer_field("background"), - time_start: Optional[int] = typer.Option(None, help="Index of the first time slice to use (inclusive)"), - time_end: Optional[int] = typer.Option(None, help="Index of the first time slice to use (exclusive)"), + time_start: Optional[int] = Option(None, help="Index of the first time slice to use (inclusive)"), + time_end: Optional[int] = Option(None, help="Index of the first time slice to use (exclusive)"), - channel_start: Optional[int] = typer.Option(None, help="Index of the first channel slice to use (inclusive)"), - channel_end: Optional[int] = typer.Option(None, help="Index of the first channel slice to use (exclusive)"), + channel_start: Optional[int] = Option(None, help="Index of the first channel slice to use (inclusive)"), + channel_end: Optional[int] = Option(None, help="Index of the first channel slice to use (exclusive)"), save_dir: Path = OutputParams.make_typer_field("save_dir"), save_name: Optional[str] = OutputParams.make_typer_field("save_name"), save_type: SaveFileType = OutputParams.make_typer_field("save_type"), - workflow: Optional[Path] = typer.Option(None, help="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow."), - json_config: Optional[Path] = typer.Option(None), - yaml_config: Optional[Path] = typer.Option(None) + workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow."), + json_config: Optional[Path] = Option(None), + yaml_config: Optional[Path] = Option(None) ): cli_args = dict( image=image, @@ -83,7 +86,7 @@ def main( psf_num_iter = psf_num_iter, background = background ), - workflow=None, + workflow=workflow, time_range=(time_start, time_end), channel_range=(channel_start, channel_end), @@ -106,6 +109,5 @@ def main( return LatticeData.parse_obj(merge(yaml_args, json_args, cli_args)) - if __name__ == '__main__': - typer.run(main) + app() diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index fdc36a2..4a9ea64 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -11,7 +11,7 @@ import tifffile from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple -from typing_extensions import TypedDict, NotRequired +from typing_extensions import TypedDict, NotRequired, Generic, TypeVar from aicsimageio.types import PhysicalPixelSizes import pyclesperanto_prototype as cle @@ -32,22 +32,35 @@ if TYPE_CHECKING: import pyclesperanto_prototype as cle from lls_core.models.deskew import DefinedPixelSizes + from numpy.typing import NDArray import logging logger = logging.getLogger(__name__) -class ProcessedVolume(BaseModel, arbitrary_types_allowed=True): - """ - A slice of the image processing result - """ +T = TypeVar("T") +S = TypeVar("S") +class SlicedData(BaseModel, Generic[T]): + data: T time_index: NonNegativeInt time: NonNegativeInt channel_index: NonNegativeInt channel: NonNegativeInt - data: ArrayLike roi_index: Optional[NonNegativeInt] = None + def copy_with_data(self, data: S) -> SlicedData[S]: + """ + Return a modified version of this with new inner data + """ + from typing_extensions import cast + return cast( + SlicedData[S], + self.copy(update={ + "data": data + }) + ) +ProcessedVolume = SlicedData[ArrayLike] + class ProcessedSlices(BaseModel): #: Iterable of result slices. #: Note that this is a finite iterator that can only be iterated once @@ -292,7 +305,7 @@ def slice_data(self, time: int, channel: int) -> DataArray: raise Exception("Lattice data must be 3-5 dimensions") - def iter_slices(self) -> Iterable[Tuple[int, int, int, int, ArrayLike]]: + def iter_slices(self) -> Iterable[SlicedData[ArrayLike]]: """ Yields array slices for each time and channel of interest. @@ -301,7 +314,48 @@ def iter_slices(self) -> Iterable[Tuple[int, int, int, int, ArrayLike]]: """ for time_idx, time in enumerate(self.time_range): for ch_idx, ch in enumerate(self.channel_range): - yield time_idx, time, ch_idx, ch, self.slice_data(time=time, channel=ch) + yield SlicedData( + data=self.slice_data(time=time, channel=ch), + time_index=time_idx, + time= time, + channel_index=ch_idx, + channel=ch, + ) + + def iter_sublattices(self, update_with: dict = {}) -> Iterable[SlicedData[LatticeData]]: + """ + Yields copies of the current LatticeData, one for each slice. + These copies can then be processed separately. + Args: + update_with: dictionary of arguments to update the generated lattices with + """ + for subarray in self.iter_slices(): + yield subarray.copy_with_data( + self.copy(update={ "image": subarray, + **update_with + }) + ) + + def generate_workflows( + self, + ) -> Iterable[SlicedData[Workflow]]: + """ + Yields copies of the input workflow, modified with the addition of deskewing and optionally, + cropping and deconvolution + """ + if self.workflow is None: + return + + from copy import copy + # We make a copy of the lattice for each slice, each of which has no associated workflow + for lattice_slice in self.iter_sublattices(update_with={"workflow": None}): + user_workflow = copy(self.workflow) + user_workflow.set( + "deskew_image", + LatticeData.process, + lattice_slice.data + ) + yield lattice_slice.copy_with_data(user_workflow) def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int): """ @@ -373,14 +427,15 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: """ Yields processed image slices without cropping """ - for time_idx, time, ch_idx, ch, data in self.iter_slices(): - if isinstance(data, DaskArray): - data = data.compute() + for slice in self.iter_slices(): + data: ArrayLike = slice.data + if isinstance(slice.data, DaskArray): + data = slice.data.compute() if self.deconvolution is not None: if self.deconvolution.decon_processing == DeconvolutionChoice.cuda_gpu: data= pycuda_decon( image=data, - psf=self.deconvolution.psf[ch], + psf=self.deconvolution.psf[slice.channel], background=self.deconvolution.background, dzdata=self.dz, dxdata=self.dx, @@ -391,35 +446,46 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: else: data= skimage_decon( vol_zyx=data, - psf=self.deconvolution.psf[ch], + psf=self.deconvolution.psf[slice.channel], num_iter=self.deconvolution.psf_num_iter, clip=False, filter_epsilon=0, boundary='nearest' ) - yield ProcessedVolume( - data = cle.pull_zyx(self.deskew_func( + yield slice.copy_with_data( + cle.pull_zyx(self.deskew_func( input_image=data, angle_in_degrees=self.angle, linear_interpolation=True, voxel_size_x=self.dx, voxel_size_y=self.dy, voxel_size_z=self.dz - )), - channel=ch, - channel_index=ch_idx, - time=time, - time_index=time_idx + )) ) - def process(self) -> ProcessedSlices: """ Execute the processing and return the result. This is the main public API for processing """ ProcessedSlices.update_forward_refs() - if self.cropping_enabled: + + if self.workflow is not None: + outputs = [] + for workflow in self.generate_workflows(): + for leaf in workflow.data.leafs(): + outputs.append( + workflow.copy_with_data( + workflow.data.get(leaf) + ) + ) + + return ProcessedSlices( + slices = outputs, + lattice_data=self + ) + + elif self.cropping_enabled: return ProcessedSlices( lattice_data=self, slices=self._process_crop() diff --git a/core/lls_core/sample/LLS7_t1_ch1.czi b/core/lls_core/sample/LLS7_t1_ch1.czi new file mode 100755 index 0000000000000000000000000000000000000000..5aa11594867ac885baa612f94d21e9a0ee8f72b8 GIT binary patch literal 6877600 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%0DfYn0CsZ7s)QX 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 z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF 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 z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ 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 z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ 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 literal 0 HcmV?d00001 diff --git a/core/lls_core/sample/LLS7_t1_ch3.czi b/core/lls_core/sample/LLS7_t1_ch3.czi new file mode 100755 index 0000000000000000000000000000000000000000..07d160f58867e535e1f669975c09cb47ddacb8fc GIT binary patch literal 7065344 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 z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b z2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b2{Z{b3H*QT z*lTWPS9WF5{x`uLa65~@2P{nN6Jb30D#zK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U 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 z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0zX0Ezkl=p ze)qHgX8zxQ`u=x+Lw}tB0RjXF5FkK+009C72oNAZfB*pk1pa9PzxIB{nCJhOdnaAJcKXSUBX+vf%+ zHaKx+`#iIKZg66Q6KA&1Gu!6|CpI{7X8Sy|eQt1KgA-@A&okTSXRuE}fB*pk1PBly zK!5-N0t5&UAVA>H64?JX)%e}tyqmWDN1!HcK79W9_s~D#r`@f)?Nzvk|J^}9(cGKSAL^tg-+h;fJ4Ni2^KC^vx)861jH|;aqXE*H)PIS{gvwe2c-rz(x z?K9hFH|-5hbkjbweRk8{;6yj=Guvl3?F~+J(>}9(cGKSAL^tg-+h;fbKfV9piQoNa z&upLeAAy0K;PdbHIlpe71PBlyK!5-N0t5(rBLV;awSV{jf6edL@3;ST^g9iA@;jN2 z9v?pMzen#?bkly9Jo&s=QIqz&{DJrL1;XJ6{@%ZS(Rb`O8`AWX8YVg5FkK+z%K>%UzjI;IsW>KXSPrKkHA1q@Zr;w za1ZXmJ-q)G)TI3`-4CDlDr(Yxm+t4=`F#C>{T|)zx4YY~Us?amZTIY+-NXBDK~384 z@}GY{{WJGxX?N4^rrk}un|3$tZra_nyJ>gR?xx*MyPI}5?QYuLw7Y3{)9$9-O}m?R zH|=iP-L$)Dchl~s-A%ijb~o*A+TFCfX?N4^rrnMHz5@N7wtx2%bbAuBboH(<6p4mP(II+QrGu!8x?Q??@8=N?^eV*AqH#o7ui8I^h zneB6f6C0d3vwfb~J~ueA!HF~5=b7zugA*H^IJ148**-TovB8Nm+vl0>bAuBboH(<6 zp4mP(II+QrGu!8x?Q??@8=N?^eV*AqH#o7ui8I^hneB6f6C0d3vwfb~J~ueA!HF~5 z=b7zugA*H^IJ148**-TovB8PY?EJ;A&+{MO=ezv;l?_hV^XW;r2lwC}-hT^f(telj zhtGQzHEF*~_rvGCikh_FrTgLYUPVpX@6!G7d9R`-?RV*Z_`Fw9llHrGKYZS+s7bra z-~IL9ZR{)a>6*0Pr9GebDr(Yxm+t2;^7$oo|5@!{j=%n5H*NcmKuy|w__&An--4R7 z-=+KE^IkK{Vv@PpZ6+i z(teljhtGQzHEF-gzv6!SPyF=X^Ur<%Des@epQlP+8vR^zb0<57uW%3U!9BeH7SyEu zF5M5G_bO`AewXft&wCX$X}?SN!{@z%WwBM!s;qzWa zP1^6${qT9Oq9*Nk>3;aUS5cGpyL3N%-m9od`(3&pKJQi3r2Q`451;ocYSMm}?uXBN z6*XzUOZUU)y^5N&-=+KE^IkK{Vv@PpZ6+i(teljhtGQzHEF*~_rvGCikh_FrTgLYUPVpX@6!G7d9R`-?RV*Z z_`Fw9llHrGKYZS+s7d=>x*tC8Rn(;YF5M5G_bO`AewXft&wCX$X}?SN!{@z%WwBM!s;qzWaP1^6${qT9Oq9*Nk>3;aUS5cGpyL3N%-m9od z`(3&pKJQi3r2Q`451;ocYSMm}?uXBN6*XzUOZUU)y^5N&-=+KE^IkK{Vv@PpZ6+i(teljhtGQzHEF*~_rvGC zikh_FrTgLYUPVpX@6!G7d9R`-?RV*Z_`Fw9llHrGKYZS+s7d=>x*tC8Rn(;YF5M5G z_bO`AewXft&wCX$X}?SN!{@z%WwBM!s;qzWaP1^6$ z{qT9Oq9*Nk>3;qqpI=hP5aFD*-d+c6Wz4WY@gkP5aFD*-d+c6Wz4WY@gk zP5aFD*-d+c6Wz4WY@gkP5aFD*-d+c6Wz4WY@gkP5aFD z*-d+c6Wz4WY@gkP5aFD*-d+c6Wz4WY@gkP5aFD*-d+c z6Wz4WY@gkr+aw+EvQNRUH+Z-^ZwKOXKTO9zw^)8 zKgp;4dHDA#+=F{?5AVMPHEF*~_rvGCikh_FrTgLYUPVpX@6!G7d9R`-?RV*Z_`Fw9 zllHrGKYZS+s7d=>x}SfgPk*Sd_Ir2dJqh>V9^AwGZ$VAk@6!GJ6+WEZ)n9Q}{d>0E zn|pH)@4p2#X}?SN!{@zaUX>V|%oA#OQvzzt? zC%S2$**?2zZ*ZcU_L=RooAw4Lx@n);KD%jeaH5;`neDTi_68@qX`k6XyJ>H5qMP=a z?X#Qq1}C~{pV>aUX>V|%oA#OQvzzt?C%S2$**?2zZ*ZcU_L=RooAw4Lx@n);KD%je zaH5;`neDTi_68@qX`k6XyJ>H5qMP=a?X#Qq1}C~{pV>aUX>V|%oA#OQvzzt?C%S2$ z**?2zZ*ZcU_L=RooAw4Lx@n);KD%jeaH5;`neDTi_68@qX`k6XyJ>H5qMP=a`6NK# z#|ren89x0xig#&WD?aX__wc)RC+?)T;U3(BdwBmXs7d=>x}QJMXMZcs?rO*I74E@3 zxQF-Of||77rTgLYUPVpX@ABV$KmFM?0RjXF5Fqfs6ZmQW_k*AJ-~E3d{-b`sUw@e2 z{ag8u`g4B$A^mwi0)Af~0r%h@`tO2!_&N9B_x@gcP1^7B=iEtu_L{W2X?N4^rrk}u zn|3$tZra_nyJ>gR?xx*MyPI}5?QYuLw7Y3{)9$9-O}m?RH|=iP-L$)Dchl~s-A%ij zb~l;;0RjXF5FkK+z;_Yo|4rNe-Jidkw*5z-CT%`H%4ffNzxN;Yd*82n2XJD86KA&1 zGu!6|CpI{7X8Sy|eQt1KgA-@A&okTS1}8Q+ac28Gvwd!GVuKTBw$C%$=LRP>IB{nC zJhOdnaAJcKXSUBX+vf%+HaKx+`#iIKZg66Q6KA&1Gu!6|CpI{7X8Sy|eQt1KgA-@A z&okTS1}8Q+ac28Gvwd!GVuKT(+4+lKpXWcm&v*IxD;u1!|9gCX=j^W&AV7cs0RjXF z5FkK+z`tAI{C~su`MaNbgA?{2fy1&-K0n82zx1#0d;dAV_x`SX1MP0w-L$)Dchl~s z-A%ijb~o*A+TFCfX?N4^rrk}un|3$tZra_nyJ>gR?xx*MyPI}5?QYuLw7Y3{)9$9- zO}m@+*QTGE009C72oNAZfB*pk1PBlyK;SPD=>JyJ{@p*Wo3{N&peAiTKf$NJ@lX4` z|6_mezx&nr>jVf8AV7cs0RjXF5FkK+009C72oU%d0`LDK?SGfRo`g@vnaQGibr zh%$TTxVmSyPZZ!21)_}nL>c#vY(xRshyqbYZ$ufrAsbOZHlje3(Hl`lZ^%Xzkc}u1 zW%Ndr(HpW61!N-%M44}3Z$ygh{0-OG@AUV>;ln2h$VL=MENO_Vd*-y12Kz*T#FB=% z$}q8{flm}jENO_V@{?H7z$XeMmNdjw`AIBk;1dNBOB&*;{3Mn%@QDJ6B@J;^eiBO> z_(Xxkl7_e{KZzv`e4;>NNkd$fpTv>|K2adCq#>@#Phv>}pD2)6(hyhWC$Xe~PZUTj zX^5-xlUUNgCkiB%G{jZ;Ni1pL69p1W8se(_B$hPri2{iw4RKX|5=$ERM1jPThPWy} zi6squqCjFvLtK@g#F7R+Q6RCTA+E|#Vo3v^D3Dmv5Le|Vv7~`d6i6&-h^z9GSkk~J z3M7^^#8vr8ENS2q1rkdd;;Q^4mNf8*0*NIJaaDd2OB(n@fy9!AxGF!1B@KL{Kw?Ql zT$P{1k_J9eAhDz&uF6khNduoKkXX_XSLG+Mq=8QqNGxfHtMZdr(!eJQB$hP9RryIQ zY2XtD5=$E5s{ACDG-OTEyk{U51@^8NDGJQ9w4LK$OuNQATgbMih{ZC=g}zMwHPTvJnMjBML+ry%A;f zhHOLu*@yyBMsGwJy&)S>KsKU4l+hbeMsLVQ6p)Q55M}g6l+hcq5d~x;3Pc&b5oPp- zY(xRshyqbYZ$ufrAsbOZHlje3(Hl`lZ^%Xzkc}u1W%Ndr(HpW61!N-%L>aviW%Pz@ zL;=}|0#Qb9L>avy8&N9jVKUh^hT7?8?q4vWFrbh8NCr@^oDFi z0ojNGQATe>8NDGJQ9w4LK$OuNQATgbMih{ZC=g}zMwHPTvJnMjBML+ry%A;fhHOLu z*@yyBMsGwJy&)S>KsKU4l+hbeMsLVQ6p)Q55M}g6l+hcq5d~x;3Pc&b5oPp-Y(xRs zhyqbYZ$ufrAsbOZHlje3(Hl`lZ^%Xzkc}u1W%Ndr(HpW61!N-%L>aviW%Pz@L;=}| z0#Qb9L>avy8&N9jVKUh^hT7?8?q4vWFrbh8NCr@^oDFi0ojNG zQATe>8NDGJQ9w4LK$OuNQATgbMih{ZC=g}zMwHPTvJnMjBML+ry%A;fhHOLu*@yyB zMsGwJy&)S>KsKU4l+hbeMsLVQ6p)Q55M}g6l+hcq5d~x;3Pc&b5oPp-Y(xRshyqbY zZ$ufrAsbOZHlje3(Hl`lZ^%Xzkc}u1W%Ndr(HpW61!N-%L>aviW%Pz@L;=}|0#Qb9 zL>avy8&N9jVKUh^hT7?8?q4vWFrbh8NCr@^oDFi0ojNGQATe> z8NDGJQ9w4LK$OuNQATgbMih{ZC=g}zMwHPTvJnMjBML+ry%A;fhHOLu*@yyBMsGwJ zy&)S>KsKU4l+hbeMsLVQ6p)Q55M}g6l+hcq5d~x;3Pc&b5oPp-Y(xRshyqbYZ$ufr zAsbOZHlje3(Hl`lZ^%Xzkc}u1W%Ndr(HpW61!N-%L>aviW%Pz@L;=}|0#Qb9L>avy z8&N9jVKUh^hT7?8?q4vWFrbh8NCr@^oDFi0ojNGQATe>8NDGJ zQ9w4LK$OuNQATgbMih{ZC=g}zMwHPTvJnOT%WUlT!??e@X+NCoCZ8z4CkjLv_fC}A zGxLc8e4;>8NDGJQ9w4LK$OuNQATgbMih{ZC=g}zMwHPT zvJnMjBML+ry%A;fhHOLu*@yyBMsGwJy&)S>KsKU4l+hbeMsLVQ6p)Q55M}g6l+hcq z5d~x;3Pc&b5oPp-Y(xRshyqbYZ$ufrAsbOZHlje3(Hl`lZ^%Xzkc}u1W%Ndr(HpW6 z1!N-%L>aviW%Pz@L;=}|0#Qb9L>avy8&NzD3dlwjh%$O3 z%IFQ*hyt<^1)_}Jh%$OZHll!RM1d&t<$L3c@A4A@1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72z&*B^V@am*z&xVojbQ|)wbkgjxN~qVO96NR{GlyZ(0BI z?e!o3ByChKoch3yXIp*$_U%vKe&i2&Hf`VG%-w69FB#mYTi>p|FH3uVy@ukI#p^Sb z+P7)fCX^LetF&Xqi-00^MqR_-g?{ZcieODefK}` z;FO1c_s40EKk?-Br=EW1#Xr6D^6WWt=gt4~vRD6O`HGdRR=@G)hK-vxZ+Yu4Z*MCs z`ryNlKK^9;rzPiAa-RDhdroY>^D4-mSE;!QU72Qo*nVo!#Lo`=_^`#z zH!MGE!-0<%XGYfCmAmk>SBf)d7iT6nule!hx0?)FGx@{f%v-xoFV58X`KE^(+>m)^ z%lmECHJSTrvt2WGKKJWhi{|`h+M)e6tm-$W*Jr2SJA1&4>z^%n=E`b6ZZ&`8g$-&g zDpRfP88?l(^Qx6A=Pqk|(AkfEXI|!(4RwC|4-aPYti!KbSF=OWNADGW)N*o@qlPT( z`og##&)1*)$*1EEoB!_akGLqW&t0edszXuNCi&wU%(~^q%C)Y2uzBOg^^R{)dD&%e z4ag~deY1iY^NT(_sOFF*4X--1aN5TA7GLm5lh=DSEzXSlQNuBf*Or`4apsM3#hDgG z%^z>RZ1|QXb9Zg|bnKcYy><=%wD~zN%vrnrk}q>8C*}3MtM#oN3hzFo!sv!AM~yG2 z)wNfR#*G^^`g88G%PJ0NQM>JzIh21OJNl}EC$Ct3LaRk1U$56}L76U1&l+Fm&Yde) zPI+|hIcHD1dv0E}FC*(RyUhQ~ck@@RwTdH}SN+MHBVQ~x^|5C4TdeBxM)xPiuk3!; zj9T4>ug;xb_0n^WxU}PYMeC>Cx^T_rzHJWwxZk=vPoDkWcQ-Vh-Rz^;jpt0e<(_N4 zyL{VckH1`gQqN0P-8bg8=Qm6n{p0JJO}xEh$BJz>W-=r1zIa&ESMOQ1v1PAAZk@hh z^_U@jR!uvs`){5<`}u}QhU1jIa z(xdJ=x=qCuwZ^Y+I{s^RAe$em+iGyXt^+qsYc%Y#V>h1o#+KRT=1v%K#1UivFn>np zo6Z_Hr~3GJijJN(>F}XfPFb|Md(W-wnwY1f_lxfio_f^xUvz&3Arq%KW7*j%@JBit};`-+JkHqmIAuww2%N z_}8M#7jEj=eQoD6->%oLS^tL`7iUf^-*n)NeiOz#|C6CxUn}ZAebCRZK7CQyokdkk z-v6^J-u__1?8*1nsqpfMnKRqo|EIiulWysJVE&CuPkpe}mjPJ%!mmE8dsJb&kqs6M zJ)-}DT9a;{)Z_7qJEqN;SogSt&)N3V57sXkx?yblzivJK;%eQO6bpuASnA3mt*1(d3xNma76Ia%nKCoZc+m4wy_{HWY)P8m7vCW&Dy=_X3cWz#H$M1V* zKVZc>YcH(UYi{9_@3z^pU`ONEcdq;3FGmfV`gZ-256Bx<@3|XK9a(Tew=23``^Tv- z&iPsGN9UY2r0q+^na9iat#|6UO54{C-Pyj;6+0IdX9m2My`1ZY-c^6gw!ds@y=(2| zZ~eC7*qxo8zNh)h^UD=(yz=DZKk2jV4>QhbHf7bL73VH|uBgKWpIkSuaQK_&-aBv8 zJ3BsFwPyOf9j_Nnt~vRl#uvTU|GL(hOCNabi5@jpt?pe^vu)u|Hw~G&wC@q`&ly(u z^JjiP<4S7-6Dt6Odv)o?`F`|6Jw`}D)ZZpdt{wQb1w z!V4aKf9ie-t1p* zu3CFraps!gFBI-r`uCTXpH{^?31vef3IEi4<*VM`U-kVt#jE!HR-JuK-=Cb(u4BoM zTjcIpykYOJ-sv~+JwN`&{rj8tebG1W0{@dwINe`+zn`4B_lA0E-|K#Ve#~2D?$6Td z#Yv~gL?Pt+p$OYegiJ<-M827{Z4xH>gfl~9MK}X%irCv zdUkDf<-V0zcShF4qXQi?2$PtJ6+!Mchy$Z-?NgR-EP;XPmciuvwzU;&mUR3 z7xe|?YMdtEE-d)>F=-q(12*U$U>`YwKbRI^TPPi%ef^7RFq z=Qdr^>io{_&hC`Sbne}!N2ftu`&>S7zYqGFY?gfeUP3W!=}8ytTv5FH+QviQ-*4vL z|Nh*&@3mCiJCliiM=$u?ul4oUJv(gvi|udQGK<=1u;azfJz_y;`#MpGPE=ZU6uP literal 0 HcmV?d00001 diff --git a/core/lls_core/sample/LLS7_t2_ch1.czi b/core/lls_core/sample/LLS7_t2_ch1.czi new file mode 100755 index 0000000000000000000000000000000000000000..1bb1fc686ef1d5f820b38bd31baa590a8eb000d7 GIT binary patch literal 6967744 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:k*dTs*a|a%T|!%% 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�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�wR5Uw`Xe>XYEPwP>xVP_{qn@8`@#CIE7f~^KQ+_4W0{YsM zN=tV)@2a+$)8C)#&FLH7&=Z?89oMC&q_RgjeXVlgGU=pB3up4_ghbAXC!bC#5UP2T zmylb}7t}5L)}Zhq;i>$)l)bZNUHQ}OUNoA?k2z9U`9S53_00TDYCqRh$37QDIwsms z*K>`|kc+Q*OCqJKO}r#Ju(dX8>au#({T-prl@rafF9{n+O|qKoN=;YzT0pWYURn=X zEh*hssdE`No2;hxjiDA!pf*1 z4Vq3?XO;xlhD6rnbYt?Uhqe~Y1n)m>HM4HfRtxZbt;R+~t9{efNiA>JFIN;0nWP}HzwBvPsAiF8MOB6rcg_kObl zXV#OKEyU^-xe3CKO1Eg5-kR*(r)-$)s@D$F^x3CU9s87Q)3PblsdYE`*~>Aoui2-Q z&3*P$gRdpAv`nn))w7wd4HWfB4RVtx;7+@=?H~uv zIOo;G+2J9!=_~RZqUGE$u~cO$eQm?09H*`jHNmrTn>bGlPpFOSsqbMGH=Q+8#437u z0=Rsp27`$2Tk%nodO5^?%WM9H>-UzFd)Y!U)+=VNex1R3KkJNZl4@bm-^>N=wZ!|q zpZW^Dm41>S^U3}|p?t6gtTAS|eIF!dRixFXm0^;}b(O*GRt zjVcjmNf2M%Rt!s#{2SD2#$!lqIhZJtA_pcZMQXBrcxp%*b=WEEH(iGi(hw@BLyo@_ zpsVbMa3ySC0G^uG7l7v{5+8&Jyi`{hX`J?BLqw(IR~CU6i?+{Q@X7k=eXJ{ys$c&s zrg*BH$AQyDy(Ha{K;2m%r1YLmR}-6Rv=_NfQ%xvBTm#~>VmS9T`Iy;uSeLP;*|WD( zoA}x5a1PyKUDI#6(cR8%AM;N%H2)AZ=nqQ#BZ01Uu8aMSe+*Sv5WXuZvTKfPhOJGa zK=T!W{V_014GH*zZfi?LTM=BvG`A+MsbzMvkHNgwAH~^<;0klQ$J8LM_1Fv#($L;I zEF(?b?9nt~hj?D?D$|aMhSlKY^v*814C{&MJy?Rbqz0Q=Pg*tj;B~~L(k(T(xCgMM z2IEJX8ruNTaVNGfC~Ms$RfG4>W`0yAKRh(d{!Rbk5T){Vx58^5FICTQt35m<1gm%^ zKb}!KqY%!AI7%lqxcSS9$;o0f($TK+++-%Jf;~3F6V&59NMr8MkUe8!R$pdBd6ZoL zLS?!4VO7?K`m9W{gfN~|mFHCci2MfcZk+})UKlRjpL;Y&k4f#&J+o%LH`JkglsS1$ z7wM}phFe=ULU9gmeQ^dAHMM%qAI%LZH&Jo%_Hfn%_fQ<6qD8HtW-AvBn3`a$$|ki2 zTae9X*JTDL7^|X1*-%HXJc&KpN-sN}EAViS7#=O&ROvZS+{`!TV&%skJu<75C%&O1 zKGdkGqc;TF`PQlrQcl6Uj~UDMm731#*PKeXrMXO4@0-y?Esb&1nbMU&!2{ z%z&1ir8^6=y~Fvw**c$RG~*JNBad8I?wxdxp_!m>s3fNPi9PP|KcxeEY>7*4_v5^t zxnsxX#&TMzbMv1$X0Che%27&SJT_;&&e+Lu-pj&*R-w(_Q9LWif6$MUBiczpe|T=J zkWm5;a|Y0|;o+fJwF)N@Hua{VGe+_A+_8m9bxNXl9S38U#k6+&g%NI|ELir#a&yE` zJ~O!daGM4p+c)F0hpTROESXFy2T1go`O`U1#WuHwK@Zar1G;CAj}?Z8W)%vfxs~GuPkMDF<6Nn;4i>6Ht!IBc z_6btQd5TFsFIP}Fac;1OQ@u(b+vkrClLt=&n9SsdPD@PVR4;nc(l829ZU+6U zY<5J~8t)~;O8X*x=~~f4BZp2?npS1;>X;sjE{dfOiFF(jkIm>xcPK}B>XWhPAtaHT zGng3>y`+=YHD!my5;MA@sdy@(kfynZbC{8PVSDML*JejY#|IUFJXpv9w0w4Oer8nE z2} z{6{haA}b4qbN+7SHKji8cT!wIfRxU*iTxIPquvr*Mpz7`;w|>ua|_&#wpsBzj+)yp ze)PAhISc12aq8_i;jE$wXFStCWjgl8CcKHb8GIi%VehngH{qMIxrz9MHxXN2*KOEF z&AScHr0e+#X(JGh7r57Dha5kDG}9+g96vjk@7r+vf}@X0ET27Z$%5grqxoYlXK=G#SvI)_Oi&-)##x6Olj2+v5dzSMY|43#1BcNXQV|f6RFq)#4;M4 zp`%8+qMZbmide>5#B#uV#UGEyqtg|h(ISoRO0?cMvmR@ayB29IV^SU}!{g)Ox#S!s zHT6j2(nF3P92{FNX|k}qA6en}UP%DU4?+T&JS3Kyk?c%I(_IrulOmAHp&hjujzu6# zVyyi->k|;Co-~P8U^rMyYP5vWE{B%XSWQ@;=@)caH@z*=c)VCZ$MQmadGD%3a1qgf zu%vjyt-EKIKD)~P%o^H$w=%#EkIFnF%S_QffDLlgY&4QsII~pjIp?=nD0dnxt!LSs zquM{dQpsf7v$6?jG#L#{MM z&wQbBJ1^;pDgA(b=XTb0C|KGPQ_!z{=czh+G$-q7W)QQOlG0*To0l!D8t&^^Hdg3U zkJ=5Yj4~(RJKQHj&0otol`AMuEO+mYP+sZjKdX1mxa^sH*F2+Hd1Hq0KH6x!_-NCK zuEa8ZTh;q`M&)K_o3j(lo3ff^J)O=2S?2w2>9nYa6AsK9Pp6{g@UUEAHs@XUu#758 zJzeJK)@KJXUG{utG_!%`r4moG!!g(L=>2<-yB!wbFJ=8%soB8Fd4GuD#Zo$S8q%QO z4Fy-R8S#7m>6ZC;cudwa>|pu=%#X5hcuPw&clyj792}Qx z$ROX`?VOREl0gq^LG_XmT27+!78PTd^O7=S1!wgg;p<*jS<(6M>_}nN(fvno$W+f! znWM00dP*wV&(Gyo4G(7difwamK}G91>wD!EVsVysiuWiT6py7!O=bUz#J%>S?nW!m zs1t&)9 zV6^FqHv5Zos%yI0k5}bvAtuK+Stw7ST)App7mH(;E=P9AZrj}!p~?(D zn;A0;Np?oZ#px}n(!ssB!K3@(5Q9Sx%d?NVbVtLZH7DC`Pd*zSAC=*|W-ATlX*WSL zHI!*StB3*Curha$3fx;#c~9!%OYNaxABE6)e>k_$*QDwoo~3O7vMVR6 z_T>PseLNQLN-)b%ezqeQ&T~9g`K%nICE=^|JQek$#P5ZLW~EKAg1o~|(5zyi1z2b5 z($KQf(IMU>veh5pKA&IZ(r7I!EfeqeUTs+^<~8`T@-4pFWUkvRVm#647gfGrb4vQ* zxlzj{bnpvpOP|ZJtjpr@tvO(~%`Bv4uKfJwri%rKC~t4&{dO;$KX;y2z?lFc=I}Bj zA}!42dlT)6=z7V>TEQGWWXj8@t1scGSr(-nm`UCxLBv;7rHgkurLYz2w5`S*b(LZ> z_qK}#hiHdy0|kGvDYuw*BDij zu@$dGrg)nJ&L(y8N0Q<=5=c zH9K_8&hl$^mS3~8{F+_uUAksh`8B)BuNl)<(ykXhP(Ao|*~j#}V>6PxbhO7*r)SDb znwPG2h25x3&yN?i*K^`U?US@uv7QGnYM-LL?0USYLigJq(>?N{_8qiWiSC6LweO_8 z79=lfuer9z<(J_l$xBCjTpwh?<0Z|DzTmjp>m|jD+RK2*i`pk>A07$tVqS`GHLFmG zO;=@KoHrznL@cWbfauYk$Rb!?>cw04JXu-Chk8t)sEz@%pL{KfBtYXBZd5G5?BwM~ zqK3s-+Dd6d$63pVH`Y5ml66Nam6S^v-3zTbvfcSh^x(?4GEpcU!t|A$ed+1R&fdiI zOuVCadS5h|h{iIRShn9pts6V0V-8z5uYJ0{Sy_G)@!0ZMD%p{eJmU=NT4oQOc1*rk zHp`WYcgyzqxwSiyc^O5qt?!AoN1cDuOK&~zMB5xYs%`IklAj$O)VX1^*Nw>_EduWS z=xgUr&zKq2(D%92vRL8a9oqCScAXhqkW~z3rA^Tql!irr2eL-d!e;O3Zm8^nVM0fb z7|e}GOdhoDi0GUWnA5(kY#SG_74DOc(hTB%=w%>UDKSyAmA<0z->E%La8P*T~(ucH4;_ zn`{db1(-c}oxS_as@#BEm0e~ao9~&P=n_eciOHps@pwWXmhh)K>^Ugy8-(gi)MLX@^h488O9s@&dk&A?-;u{Tm~ z6G&EkY4*MiVm=5DK!nf#4M@M4N=v}EG`#50CN#VR!=v6pEkC!|;KlvmnC46Fvi6S` zHx@YC4ch-K_KAvT`I*enP(~ymqi}C*4ZhE1&wZXY==DnwlP9elcbXO+ltJ+*`?sv? z%$j{&XGodfuJ&EZ>|4Ccc(uEXPnNqZ-MaT1E=j}QQa#|J6j~;(772c-fh18*=*;$6 z;$QKtA1T-DQL|)s!{gz6DEr2a8piuMI4?7h&uM?OwU$VCk>p*qs;v9s;nCG{Ou5@{ zdg2@rQeIfrXa4v|ZtyeNVQn=S-Ow{TH`=Q$H=`o-zVfn8#m!&k`he9EYF5`8#?={$ z9m}cD?4n5Re>UX8t5sKav6^5ojK~70=9d`&Va2h0_fm5~72WQ|e(gtVC(7trvj}P} zCJ~PYR#H+nfe-vrYHS^NzJmk52Fjdk9v#?Al}dgEZ>eZi(Y;`3Rq<1$(YV_U7_e>p z!x%XD8@M{QZCEVBDQX#k4B#)j1-g?x&?xG+t}V1dFGm{%KV>wW>cY zPpS7icOHzls5mXpcU@&Q8$EC+S1@}{P56gz$(Y49d%Q3=-(xZt*|&b~pew2P=iX z@6Y@y>DBu(ztTf3Jro&KDlI78&XyZOq)Ojdx1va-sPO6*O2{nkPAnDTgBz4_M40e9 zM7w=8jy^Is-#fn2h)4^xqkoi`(RS0*CaE@W%U57=pgDtCY(J&@;S1I}b}!`Ao8?l? z^IF?o7>^}7qMZsZHBHIwEyQ=!MbRYZZ7k8e9R@sJfn(p3K+0#fU z*t3DBLGxxmjd;MPk#L>{nIYvzf z%1>kp7ftYF>=zxEJ96TiEL+bNSFUTmpH-l>Hbrc3fbGmyrGGxb487kKwgvC!>e$or zx3|@y4HgsMmYSB390=KE_f*$&n0<8I{|Ho2?<2q<-oPWE`GEy<>1OJ>_wrk>|IXtw zJT!9Woj98g?7bInW`bMyFf@zDSeY8+q1vMT70u&~HBZ!NE(zXE9#f4GBUM#MH5GTI zO#}N94Z07L+qZA5$}CDqQ(CE6`8;k~QLeFKDlUh^meu+(t|j2eYL1{(Hpi-RRE4w+ zgVZo(dvxM<`%^p_O?Skno*Y%D@!_>*s);HS$3sg(h5Z*`d=&E@IvHpfqbgP^!ezv_IMM-7ghjwp$rBGTgaSJ$SnFaE8$pX**nm1RD^Caos6YB>Vab{ z$qw-Sv`e{Je5-&)VjnrF6yPTJMs9FWac1_TU&aKAgl#$LfU6ADLZ55+tavyLs0fpu zFP8C3mzAPRl_!&6pO-AmCF`^zmkXJ`Od;cu1C&RyFkIdcU4>2f^RgMXw)a)=j*@%7 z#~vm>qv+0Gu61tXLp*+6&sSFJYN7&*{o0HDY+Q+_w`o|yOSjE%YD(EOTrEzAyqTV6 z6`f5~Z!v&G!J>bmU$=|D((NncR(Vw2a|_ua_ggS1c1Jbl^d){PlVc=OEPqm|G-F-y z4#lo0@kM%$l5JMgDHN(?!l#s0wIC%`$wGP_zjsIktD@MQCUnG|g7>kzWoBAS56)>V zt2(X30e(2bj^{g6T&)i_^7f4>1Ly{4`#|xLy8F?5-@R*MBlg~TKT)CybE-DJk(iFM z>`KM7C^{2KWm%OuydpWZNgGY$bVpJrG$x~+T^&iXrpVq{ao%3Hn{&hZiB0|%CkscV z=eWDxqzT=JZ^#915^_fu)#rz{D^gIlsj*m6`og;xDKYZA%*cqB$m$W8d=Dffmexvk2+y!{@*1`` zJ6o~)M=Cpfp+~|;MOkJ01=#@%?n=KgKdT%Ec{U^T#6+;1zE$hpVMM*F%(MI0Tvnm3 zZiKzI=ZXH571_3{?2yly=9B4lsQFB`O`gb&Z1d|&ssFAM^43ou^tJH#SA}H%!Xu5(%!DlbV@n& zB<7a4W_Q9sc8R+$u;-I*CJH5IRyqsC;hVh;-X^xo(qfnCZkKddrxe^S{F`RfQj>0z z_M@^#G@0t05s$Is(%(WGUUIXwdb6W;59pH8#JHMh8xyN&RNifAPjPbUAZBU~87u3y zlr1ghQ7S`HW?}ED>=1GfnLRSStMG@H%4Qay{?f>*$WSC3$wXF0Rz!OBu3vw1`kagO z>EDTwMS9m88Bi-eAE$OFMUL0I-pCrY9MI>L>a|?It5lj*ixVP)s$HUy2K2W-a;!!< zK{XluX4QH~Wy5BSVeET8AJMy&`pkH=95i!Ytyv947OMB^$f&uq z+E56F?@xa{41E8lrsc)*Y-gI@vPp^W&S)|j??jDl;k$w?1jF|UW*r}A(3g*#thEhR zml+&_Ezk~9LCS#EJ=j@c(6UOn$O;()!U}lGs@HP;&8hVgA?PHv&WHw_q|%kD8&O-3 z1~pk3S*qWxuy~?Ibny)adsQ~5R?AenOyh$46IF7Os6@Z|K9A>v%e|zHe{Fj;^6kjiBfr)5TIBcIz8U$RN`4yox5(3x zdn4b7ychX_O5TqAP2{!6cOpL#>v$(}SLADvUq_ya?2SAT`Hi+GBkx4s)d>HhHjk;z zch&lV$TwC0NaUW#TPpi@WM|~z$di#@=_(IL-q1Cljoco&URQWA@_6Lu`umFdy{4A; zsQxmI`*oH4P@nJC6?R8h~1=o~3^mYt(a1>FJR#M=pw7 zqW71p?vBv$9eFhjeE;s$#J4t~q_l%R-s0aO%IEN6_#OQBMDfmL;;)0^wS6juYv)wA zSlkm{eUg4xtK?XftTJ4cRx8B=SBU2=S4qF*+?DFPLYxpD$*6Ed_$Hs%>J$7ONxxU+ ztIfL;)cORK!iiU?KQmab-xJismcUctyM5yOaB8lxMssD}@ZXI3z>%#+k=2Y&&`3l2 zi^j2Buf3{WsaNE4WQPI$Lq2DGr1vq6f_`#>c=}Sk8&|s(q2v3_*TcZ~^r?w&_;)g{ z>F8+FZhqjh>=9Srp{w1Y*FBLf zk?SI_OES1ae0_(meu3IvVfgpgRI(+q)%5s6J;_6UUzq%u6VcJU!vc$B3}^C-zaX*HMVG8wSR)?-@CpO2EIQzb@3gGr&|0wO#9kT zguj1`{JSvvp|;;?`+H&Z+mSbg+8+x;KNC`aEPOu``48dr`;wC15@P>dZGNuupBnW3 zX4{KG)nmfW9wFr|$>!e`s=lt0r-bo`L>nHEEd8CxOTzPa)dp#MkEG}qh2ndK*`31i z9lF*{D&H&AJ|oHaDPeTGD8k+Y z8M3T>N%Y_}wb)b}-U0RR&2NT*Z|rDZTiR@Ap5C%aX!_&0JZw(0>% zt{KUuC+J^BvMP3`6_QbrqZdnxMH0rsja8f1K1shPsxK1ofJ%_aSE(+qe^}M9%pv(B zANNVNK2e`W^d8$9Hg*1C^*TW{StAi+IqTKCyySFj+(`831Ly|G(OB`ak-swS`-MU* zdsy|e<`;`S7C+|8>`#y_oSCiCUv!KCb1k$A?CZ2%sdxAcPSO?oj9#)zy^!Xyxg*{8 zg^ury?}UNx|8HvITk?4HjKzerVg%;+QW>B6z^?v zzo9_I3Jnm;UY0nD=UwTh;O! z$>*1fH{Yn*o23ctR-e5p-zm<1tFCyHWa@J@$D73eZxH9cL;cT}o$LzryhMEe4DsXZ zB>`NjYuqa7{5<=r1y98mhG!dp72FT2n6-Gp-7~6(m*OKkPTnue=itNWfpo=XY*8EFLHK61!1l`{)0aP(z$$2(#L^~q z=5)1q_-*2}mx}*x)4RKjwDF(XE*96l-RRpFNb|m3<8IMau^4<=JopUp=gV|Y=c~_o zs(nbae^2FSM?SCe)5W9DkR9zh$q(O*{790;S+eV&r`M~*wJ#9&zClmq8DkR+8{h3e z4+Gybr!2lZQ!(WrZt36Q>EDkDSuaZ3-XqMuBc%Sjknkg|?U#k$9YWg8Lc!|>@y`l} ze=kh_KyB|84&N1e-cj98gp#id$B#<7e_pS5sr5F=;g5+@?9fO*mSyW(s(Dtg(2g5b z^RRS+XVvRvjrXX~iN$P}W^#+Jc86#M7P;LT=?0Zwr|aFR@$S*@Eutj1X_njc=^kPJ z0ip6#{caZ}*rb2I)bDv}b(Ii)jws4alE|+RzP}-aZ<55m)wDlPbGX{HyHj)dj?jLE z=))HypA+A|Q1s_Kl|G_bzfc?A0qtv%Uxb10k55^AYj2g}*<1Jy58rbt_^>q7j$McUdv+V(xwy&=T!)$0%B`}$7gkJ|o28ut^CKWzi+-;&Kd()Nsc zzp1`I6>fhdJUyd!Kal19KH=nPA@LSra*t5+wm9QM@@d_r7LTg#0sZ|aaq2hJ{t0pK zJwoISLhF@6@qNPi8=@XhXa@I-%iphe=-@j=4ZflG59{AU;`#T80$i@vXNt>Tq4u|n zQ{Se4S82S{Rezo^d$#ZkM}9$lAJF@)8tJRT`&s(EL8VWKj@_)1uNZt^ER3Hjday2Z ze82h2F!24`Qxo4LSxv^f(y0W?F`!n9MSCW0ol-R8)%uIo8vPqxcht09rt9?^eHV>-Ky}#IiA-HCU3pM( zCC6w4>V`Bf*FyIn(A7^=PvUQ^evQ>1U7J~BKUgOHnP?oM%8A86+df|Zh^V!;d}3QE z!J>|(fLZZN|C~zl<~qdP(~=p5Z$IDlUKsfPKc_0byL7~p4(4v*J3M?_TNqJeU~Rt8 zcD!(e-wM2fAnaMhjoAn>u(ZY?;dtSMKA@N#DOjg^^-6r2jSd6JgJvA)0NyM1q8z7w zM2h2&14YmlcB=FVVZD)~)gBtLRKIyM681Bw#4^pv&IOwsb}=k+*vRm>f&Im51(xxv z4Xd0ucH-Uf^|6nId4XMG%b^>1;uw3WN{B^YCYpftjWO9llM9`HZ~k={`0kpT_)g7; zrQ)4k@s|D_qJ8aVNw=p-zTGPRie>2w`Zq57^+uzUULy(iD(R(PP|OEbs8OL{L^AA` z^oxYMUe>L(s@Whc;3NOM#yMa5?3iTb(}k=rYNS!2cfH!5E{&MBd9_-nf2Rm*BkFgy zdY_>YH>mBIdY6%njPliLyIyS>_se?6P8H@gs`n!rZKK8?(kKPfig(bEF}}^~oAYT{|*t~@K|paA&)}b9LY8fp7cNIkEB10R+6rB-SJxrp1>Ve#F*vT> zAIiHW8hc2enHMNTKj0OKeOP^%Epj#77+IVscXp%^aYH=Jpo44T=RqugEvIMi8ytL@ zxflFpNEGNG$ST}7yWWrv26XjIIS@UcgZal+d^ikz|KC#;-(AV1vWK+xkA;YD_GX-< zHI5g1P!cYhGsuA-qm?dG9sXl>VuEkvZ7^UX+AP}9j8}*&A}Ql>W^XsUzE^0xarHs! zg(9rcwP*nXk%*DRp&Z9bD$j`qu(uOwyXD|= zmRIQ=9_$NLewwZd&KY~qjD@Uzsd)OSn&ld`AJxe0@CM%_vguuCq!{dQ$of!{FFP-O-_34K4jUJpG$pSwx(I>0zz;#RgsQ;356O`;J!)Q6EbT z^3jB`i`jUTCF;$t?-PWQB|=ia{+_I!?5JKNq!Cp@Hiew<(ko1XH+EWMlOyuv1l43! zf2tW9-an=m@aAFtJxN}y(}l`{Fb?jq@F7JEOWOakB=?ggbALhQ$P?)1+`~iavqM_L zMH+Lha6Vw@4)Vo!<&WDf|M$((1@h7ah-<}aw@Mzl-NH8!@>`^%oT-viHTJOn)&2>F z@Alsh1K)o>HStY+YG<-D*@|xok$=A=U(?^nJGD)IqwmYV^?mKoKS68@E_`X)wg9}u9p1kB&$%}rC@QdBxGHL&3t9D$}-~xHO zw#h5ETi0AGPLBWULQ#%WL?`f(pRCrGizeU$zgTo=M6E8;h_!!$;XCq&Vc`4!nVR@c z&ge=-J315T7XJ>9f1|r1FJnhuDUJnaWiR$}t#Q0b*sI{x=(n^vNwp`5k0QOED9IXa zmRdOXNwSXNdxA?NlcU?>`(nSx8N!d{r)bQpG#Y)da^Edfo?)zd)(^*?5$p3Je(h#a zf^#*ClU2r*PSn4gJlRi)c04FbvQcOqRrv`*CN}xqq7?YXZx`Bc6z@J)a>Qy$09g}7 zbH8f$m=+hR4td?iwA`*)K*gX_cMIXC7;D@C@PGebVc`3}O<8=WyV6}J&U&fp zuUFs8)stKeb_W>QTppIBKup?s!cU)m=`|)Keod0`*Tk)#Q^d=5m13{Y==TO;bgNMO zuq5m6>XjJMytL~PA@B;}@gc?3e6;P`s<~5~`irW|tNeO#_N_wdPa?nD*4D-jb8Po( zRsR)5y*#Mezmc5sj$WVBi05bqXN$kzCE5I9$p&vK-sd^d0Ct#etj)dy#;@)AlQ8go z=+wlw{9_%R9WDELsQDG~C1a}|5q}&Lda<@)M`KS1_HcHHt`@I@b7#cKv2YEl2fQAw zeN3-dzgDU*UM0KRbX={F3kqt1)eDUseHLk8i6{jlVFM>S8oq9FuAx2mi`uZi6Px-{ zLs^&&-YdpnUIQw{!;Wo$yliOk$ROl(nXg|vLu>J8-w5_{EQRboT_jEqy;-VJtZl*C z5%7*%djh*ou^1qwaPPH$g30Hb|1=DIC#EdEm7+A2Z27-K1yi>aKP2$lnOZMI^ef~A+<2RYzBaiCu7L|S7@Z?uC+FiQJ?V8yZjkH%B z{QHs&UR8^`_3tLtkU`=${obH4o|NU_eto)L|8CX4J7t0RdyVk8N;hjho24Dp{t1Tf zjnl%w_is&2d@DXx=#D2_@u?x=8{0GfB^v{l*I)7;tPuaQdnnP8$t6t0m(8Dmjhkpw z_Gj9-H?(D9+_0O&xAB2l|MXHx;be7y-=8G@4Nr&DBN-4Owp`qNNbm5lQRiy-_`LAU z=Ssm**A)X9=X6qwyjs+mAW6S0E~}qVNmnOGJqt3 z&4Jl+mvDbH5b`D?AJqN{hVK<03j^POG*$7f*tO2CWGg;3M0~H%`XTtNjxU-=F>;Vc@%CYT{e^x3-rgI$H91 zi1@xnQtIuJmv5Dve!t}LJLIL>EvwU)CGBpJRQsr`+nXfCvM1|4+1tJ-E7W6>=aH!& zkmP>9B=oPT{ByEweL-^kR{gt0rFW?BwHoUx*{(h(n;5eBcGZ!eS0KWKti_|oFZCty{m)B|{;HALx2c?biD%2=b)9T_Yh~MeK>s#NdjG1%*(Ce> zxMqBo@OqABeX&|ztu}d$vQFcircp=L>P$)QS8J^Ebd7Oo`d<`vStn|-M*qe&%e88Y zC;sz#cbfk8)`oXL`&#>qF!22cQxo5k&pW!J+F{c2kA>$Svr#KnqWx>bdy$gKxkmIT zQZC$=-5d4FFH^d%JTsGEa_Kyu2^(#0MS4eWC(;#Z9^5iES)RZd)Ziqc5zQZ&9XTFJpPim)_-oC0$EXx}9a^(k zb;60sB(eXF_Z_SmxgF%|jRL{xLKZyH^emA=&~o3Qs`opJ4cY zGZhBD|Lc^+cUM<57K^v|cX<38jhpyU);f|ZTCZzyv-#K9Mahm%BGvf^cf}V5hetQZ z5=O>0@M_O6!iT<0V}W({nGlPPcgyY~<%(F-z%AY_w0CfhZcIcC=tM)O#Kx3UP8sw7 zshPgm2#^Y(1n^-vJ^nJdJ~8reXXpji!4XFqA@Uus9B8*^5#`jLaiAjXK_Rl{I8g$$ zc}i;k1jF~Pt}yWZKc*(W6`z`lB@$h&{Usst@3)j2jVO?RQ%;tb6)*aXyiLS~kSq09 z@-Dq8ztInrlZA{;FUx24mhv|}qUex!)#GXT#NJSx$ZIMgTiVy=S+j0 z#>4ii${v;0jc4m6`Ovl~e&kV&M6~Ej@}QBw%FRZ*$CK&rCFb&+AZ>_-Kn~p_5NPz_}9rQut}e;Qa`f4 z?NKk{Wv-QezFEIlNyolQZMMk6w@teLjj|wYQVh&Ris@nB>w5L8{Syq|n-2{G-~WAT z;#={lU8z)OD}PIv=NsIm^}b!}{7$X^w}s?yYprjV?dfy+OAg~Fh1Bgr{mb%-?-epH z7XK#e;n$3f?GDvEBX2oboHh#)cM2O<3u9z%dqIePG4h9PkLmAe!p~OutRGPAGwS<> zUjIX*Js|7b9m3#c29d<1zbAkBYeM?lvf^RcyFq`q$zHcf?VixTe-(xJf$&UzV9>>RwENwMwYSGZGCmSyp z`ZxY?yy)oDHr{Pa*C0m}{&ggDtA7s}shhmYcApsf`*G^SS!wA0SOT%!A(bC%#v{I+ zGUDm*${nM&=mBUG_{zCkyPMT|`50q~erdzmztx{+)<* zrd#J5gvr10VUz8gjAX2BaDwIwEUocaZcv^t z@3qEh-Rlq5h zC~_@gvBE+{o)_>0(y*V|z2hfn6ze;)yT~{poZV<^)E}EQJJ7J)K~?OD2P?#XvC%-@O zFcPtHmhzC74Jt>p9QWN@8{Prs^Y(dR;QOzpCcY(~cf=FvR(^*t`8UWWqY2y+l)|;Z z2-%VFlVMF>Y&aYEK2BHzCG7ch_lNQq6yjMXTNAVae6xED4>z0{AJ(y|U9MO5l5iF9 z4L?RAM`FjT&J~xa)W)<~ueO!lX$fWF6->j|p(A+otp6KsPL4)ocKmus6P!srtWu7VgVs3%^3vvHN7RdrG@iekQxvnd)(+%CAzJ zn`94TPH$`X_NB6$ou^SwSDf2+y}wUAeyBOWrg1jQW`DYRd{tJ$O?r==?{$rNu0}gk zujeQ}Zky(`MdOeI{+7`B_nV8tz;}A8;#;R1bfpqqE&Ez{_BBrMW1raT1`TfuhmUE+ ze_3|x%|h)RTH|*MNp|DhE?^ny)b)8y*(1%11?iJqH8Meb<`U0WzN?n0-gtqFH zbBeYZ|-6LktXhG$$Dl zi0-=t`HuP`MK?Wu3&w@H&F~=sw z$tGa?Sk>86#6euIN{ABclXv+B2vFA+t;(wpx=?@YpbHwo>J`Dn~GTbTa#0Gm;Qc3?;ezO?R&DG zeYou{Y2Z6#e|tzeHv2?xllAQvveNxX+5E!cY9keO{<8agkTB+!+u9u0q`C%l#t#vD`$adbnH z3tSJn-HVDB+)D0P43Hks*^#}`>5UT#>e&vXPVBfsCd1$dmPy z&-vnys^|48Jy(CXYi5t@YJ2s2p|10kxcVi!GCsM}Lg(L+kudQ6hf@>ZicgIuyE>CC ze20i{I4jyL`Y84_drA=TBWuLZ;C$?d?iXLh?qxF}k!=Z!c%Qf*T+`-2hO=T1!v=U(85Kr*TQ z6HNbpb1V#eA2K!Zt^7>sbY~*oif;)K-(Qlp`DMw`8-%?tN*~Q?7uYFU+x_w5b^1(nsy!E!{py^uv_jMZU4n6t>#^o@ULiZqPu7T>fk3VoP6)R)_)Kkh z2h_iJtqTL+e>yesE&kn+NO#6s`ge%<-XY2LBBAOOVRo%Bj=zg&(iM`IiF`R%mZ!^w z;8RsMB1GXy+b)aSEs}@#NVYvs)PZwA$bHnSxRqU!jqi~ZjI_N|^x*>aC94v&gCY`6*5~p^v zr0-{x3He3U?NZCjHPTmg7gy^^Y*)W~G>`Ae()X%LYX1b|-#fpC6+lby!c|utsDweBPW_h(DZjjqs1*Kf|MkHy%468U)(I z;@a>I$iFv!Aq;%~r>Ti=#ka(wNhN4%#ixcBp9)^U2%c?n0)ubtMKNa6NF>uG}v(1%Gsaa}-H36SKyHN)9j(O%o$M=dag@Nzis1x69W$h-oozSbg=gnC(>+o5NX7zM0 z$_$M4bkFKtGoBmE6>`J*MY*ADPdpk=Ophj}$K#8l@k3&%Lt>pX67fV=Jl4v89wPt7 zKSrDi(VpzPBBm^>7&JH|In}UZ5l==$3idX5AkiU2g%Q(cPZ7l0#wld@zQ7)PM&aS0 z)S?hC6}!ptdEvQQsTx{iZ_!_Nu)M1o zez@%sjeM7UWINUVa*c4STHLBxysZ}hpzEF@U7Acx=jrt!dDm{!m~Uye-bb{j}e`Po;vh_NR0iP#|ozq8=pW)~1+D6JJYZBT6 zF{=Dx0jCw87~RIV*z7Fm7dAefcgzc20x{@zDV!5OCna$?m6iw{PT$- z7leWD`BN3&iD+j$+S$T)i1@}zMn(jidCBH^z`KMb%n3zU*zga7B=ChcjD}1E8wkW- zjy#M%8wnn{83`E-fnjS2V|?oqx7nSrtRXjJN3))8tN-G2N0VlJqQS|*Oxzmwe7k>a ztr@eFqoH5uQ3pn4=QT(Y5fxWxmg%UB#kuw%K;+)DKC=e^9S4=mrhNd@|*SUR$bv~MXEj| z9r+$zozuj&$rp~!{g_(pR*OCAN1kK++E?iJbiLl7aW2%%`SdyMIJrVv_1V(6FV!q= z)A-~*zry(B(XUUDMc@*(y-a%twdEfR&p!sIf+rs@ZUu+q9dfhFnFpnd(_&m)4t*Mr_X%oulHS>qIWa7Q=V9h|xJn%IYH?uBIM^aSeTih` zv&Fy86vxEIe7pD|_U4V^c;|{2o+k^~1LB*viqBps9(jSR(q{-|tQ~5`RBUpUzOfuV|z({rjA3-IuBly!#CCWcFr`n;4b#8lTe?&JxZp zP~1vh7I5OjPgOm8JTDiw9Wi6C)s?>_3=q^drBzzR6q%3mJ8awA@F6gDcF1tc*3z`p_{}1 zv2de{V?Sr#Cp;B>mvN8{(Ej1FjEJS4tHRCEx2-)6-VY|1s0`hi{Ug}oI7JM*JGKHM zXt2?d&4D`3AjK94U&kJRP2TeFg2u&jj*Wmjr!UbhgZhj7QTrztzCXP+41CvnKJX;O zw-PiZTjzg=sDC5jf+J#4mWhKQn=dt=@gNcP#d;@48*!yr%fTsDbT}Y+lGw>j)-`Nk zSkJI)AzL4B>aay0YbeK3!$&!<)Y{-dEHNiY#A}2&*EWw$4vRdtE}}}oH8wbGdZ3eQ za*bjA1>aB)Y~n=BU{8ZCApa8=L;g1ULWS@N*!WW{=GKmgKEm~EE@)_i_1M{%x@>KD z2aHc`zcmbee^~SK%H6Y_d3wu;Z<%!XPNWkZsg`^mCjTZb6+DpP0c3+<(29NyFGjMp zdMVy-{Mp2T69tAx4et|Zh8tVo5}eXTky+2%m_Z%-Jl-;D&~2e2AP|ike>P_ZQBQV5 z>mOrIoLWp7^2DHIXZS2-S;Vub)xrUfG@>mlgaj*|dMA__Fs@ za*u4;&nVydi>kXzIg-CEYxlc4!|!@&(8Py5p!Pdd|A?Z>INR^L8s}PR_&4iyoBF(< z*C%!5AIOGIhNVsFbBi?mn^pI$tnsf#ext3u?Q+fRO7$Sy@$DMnc8&F%qSSu3jq?RJ zOaHz|+B^9fZcsGb)A9nmt?PePt*_S%Z`Av(y8bs!)~5%hk)NZh62G=h_VnF~YkNZ1 zBGWVHhHllh$!PJgEb~9ryl&ALcTIqWVEBIX?lAEEpEWNpmTTdA67uhKN1~NKHBA1U zHJU6sHdzpQ7M*F z+np;`*GDoZCe7}&ULk&IYmuMP@sYrZg(D)4d9nA!?rkIH-CDuXPWZZ*Fx04$*9JjEj&gmBXfl%d_;@1+U-sg^zY+jcShRA zCPi!*vOKmhP9-D@8YM`}APhX>9|Q4d_TZE=j*gS8O&kfMp-UqbBT=JwBPD}H`qCa* znJ5^L2!_EbS~&7NQLb)I78`X+HZ}AFdX1>1Jpqy3GSCmMg7$zEk7huWE3-ddcY?-G z%nbPEZtSUL#LIF8r1siB!SKEL!7%VWSJRq;_}2Mj>3A~Q)v~XJh;O?q4E*%VPejfl ztApBe1L5CToxuvIE*LCflY_%j15!aJ91+gWSqN}?MzOXlG-zwNI>EFe(jERz1gt$D z81B1RrCbG`jc#rE@&=7&_nMXMmWJs2HwH;BAt-{ zCBTMmEqRP!F;3n$&ikewq*)uh)dTDvZ?FbFIh~Nqt624!ljYy^Le7U$EiqRks>WL7 zm?<7Q=8nxB*~IRUu{&7t)Y({Bz#gWoO5+Dg$pe4>FiTjz^k@dbrv-6#M_(9*31pMLoC=;_+cE#Jlik2HZ-8%l;9AB~&OT#G&K&fzx^p7Resd!gsYyWqM_(q>ai)O`J-!UF8EM*`7BoNVp zh099f@HVn8*b_=X0={LmWKax;$9HT`EW@e=`sl+xP>>G#;rEeyfY|?%@l*58$NG!cH(8phUn%`wf_5zs1IIl?gQN!-#U3*;Pl+fkov6A$jpx& zuGs9rI{G~KY~y7{O_|;2I#?UtLH*y)gn{pR<#VR*#ry=f6M9tu{!M=5R99!Tqvan9 z&p(EZ2`$+2Y-?{qwg$K0=NKUc2^)(O`Oo0hZUhSNkd4umi5o>`2kA)F=+Z=}gGwSz z*jEiNCN7N~<=DQK7!^B`J<{oCYawp0WO06q@=VJ1lZ ze1hjgLD05sjy3KL4?fyE@9a)c{P##2HoA`dvCM^0;peq~g7NRj*TcZ~QJR()x3isT zdduM7{CLZ3NpoHU;9K%}S3KU)+1ei(BL8NkqrKXlAjs2bwIBpkxce=^ApRowskNB1 zV|uY#!T-S?(!9;m1b)Z$iL=LOdy*t`bYSR%-CqL#20iHCENrp$H z18eib!cTM@yqR3*{NS?QCbBUMx>e`Mfg~kHuR2J4AfjxK6k@@+|f% z{Mhh1^A!Peq1w^{eCHI)vM2Iu#pgVwHg{Y zBy2b|@-k9CvNaxa>$`$m!*3ZAIh*rTxDu9edq&xS=E0fbaCoQ=(X#MkcsraP`hjhn zIUcKbNGV(qTOx5U_}#cJGel}Zqo)@coeKKpO0|E2@$b#Ahk@^U&nE)glL+6PGm?qU zWGvmncZm2tP6z-4HU^CIfytM~9&9XLXu_N$GA^v(Il|&iYY=H=UwC<5z2L{z8;<=N z>pNHnQAEIjrelRx=!LuM46Irm7>-RWIed1F{vzpP$wzw!d*F6hqhJ9;%4eS&R=dSU z2O!fjy1BK4!>y6vnG^513irT%awH4r6Y=aeer|z2kv|y<#V#7|7Yjdkc_8q;@jGGQ z`?F=Zo$ZhzFP0~#{+;SfiiNlAYvI|~EKjpGGdQX}MH_VSk3A+x>O`?Y0kDvf;la%* zj-AW$N;n|4DR@6f1EXLLE=r6EctQW>>;ZUe&g4x)W^gka!r!40*0zOB9Bq54O7NTG zwPRd7UF<1AcNo(z)P^`%G;IDcUmMYiUVyKD*vyc7v$41~Clj-Uek@m;ymCchg;=GC zcJfecP#M;_<+a&&K>PWMH^ad9f2$MUla7CSQS-b`rPr(OF9Du+F5dqu6mAYZ_pJVR@u|qBlD`-4Qm(6g(e@w)f(j$?R(j!9W&pFe8^>p zRd60mKL7MPVc`3Brz*a6x;GMwA*dG+r#^Q|k-2c4>IA^)!bv8qUjeo~>z@1rd zAC`7Lp?zbeCzb|D9?hfnPcVG9|1b=E|K^m%cQV$QZ0!#XGd>m6+PnU$D3#4cI;6oF9h7j+Bol+n!Fw)j1!D7*i+_S^<_jyj$qp*1l%5s9ImR zr6eoVgK_cJEiv~-?sWQ*UkcQtheJPB>n~KL_D?W;N8Sws->EwB&Fozf#Qj_Qj>4@8 zU_V#>wRkG345}^pJUsdQYw{-DBahLOl6#-f&g<_e7uzfP<(!bKlx6K(l9#_FY5Ph2 zzO7y1*Gn?rB`Nwo^?FcsJ5~CQdXN$ItIBnHt9DdBp%!;b`n^}Jc1Y^JM|-qil2puz zA$ObCC$%ekms;MevrcZ&zUyuBPJK_k-_upjQcl)ujq)@YV~_b`Y*kHZ9LCU3ug~X|Gh(~gqGZDazeZq z`O`KRZ|57#?GUASLV7VLCY>epk>&AxWoEp>;B~ulID8}WcPhQupqaCMx2qp#7;jO} z=T&-{u>2*V^BVQPOxiW~_khORB%EI^szFBObJgN1_1dgExm{N|N603-Bj=r;qS;@e zwzsRU_D?W;@A~I3@csX4T3#$q9ej6mq|?dP{*n;!eUDcE3&PN5t?Dbawm0Y>-lP%X z;!$DdP9c(Wye?B-(v7l}6-w|;+ZEM*r7+Z%=Li*-L((a7KO zEzzZ?^=_;BU9Y>i)YLw&`R$U-@Qh@NuL{>+QR!8h<5tlEtZNTx)W>VXJD~l1^Do1| z_kXVw-_QUr9hl&DLaz!SpLfns?zLzuzhW#*{dOFqp#p37Kr}1cGD`#IQQDgMMA5E+p(J$EI z84VkqcYi1zY3mQS(J4fd5d+KESh6{*n^CZIV_(Dm#^0Q*U|7uTo=^PV>@;)pyI@sg zp5%tWzK*>c3m(xgSO$qBxAAJk(b)ZM*wd~5+n#K0qgaSmCui({u4gUx_z|!L*8T~G z?-joe1K)pKC%z{g|4w$q;vKQBmVGTe`x^U3i0s1BO$;cR)|MHIx7~9={0EUDCkg}X z-+~HYtHy_gof)4Lc5^K2Sku9h9gPSSB1rH$VT~u&WU2gK#GG;s@}jZ#n+R2|!9S>i z-BXGW42pv1il{dHWO%utDg&Yjc7G~4l8LYF)i~5*Yafw5VDltfRAYH3x~`y>>rHRY zAS7moh#RcrXX@XWc>R!cjDoI!UyiGWzklr0ANoiYlJQ$ATjG>|)bn?#^Zs`{f0yZf zyMF88-;)mCsYEmyi?{Ue5cTh;Br)G5jdPoH&W9yU@6h|}jD_kE$>tYIUcOWME3quE z>#VTXRe!a9@09M1Wom~s@I8u5*(*ESZb{cj&O24MNj=DLO6ZJtD!ENM^Lx^M$)|dwey^4ue6^(F+oUDDpb=k_)O>}c@JpppZ`O>imlpA?dNSs< z(xq?IwH}t7eYg5B@`JkaZ?~Nx`TS1l=bI#h-y+GLGg+|Ny)2pg4E5l|@l*BgOpUuq z()?D<>m1qKE|4wYbkQ5mAG=Ie<$_tc4R=Ix~e)$Zd!l?Bkh zkEQNaPhA!^i^dAJ$x_xrAMmdiIDAF}(V%{k9!<39Vp-?iSg z*8ceZ)_?x5=6IcD*_jbJH=n9dW<;JAC+;7T?SM(IVpeKhMSOUfZv8 z8q2$2|5)aKuUWfhgYl`0tbY&VLK>079L!+_l+NYYoVaL&ykOaX$fGHZFfrS*-dp-_dAnE8wt(J+daaG+uGi9cb>2wljt?_RhPzHzeC}&)H)!3W zcb~4=*kg5sTxX&Tcb~50+I1VdnoX~<*T3_xrN#GSKd^}S{@)&FeD8AQ^EK;MulD@! zg|?r!HryyZFrW11k$wJ8+nI(H43Ejr&IoA)9zBb#VO`{|o!0vDp{Wv*yGtF?5bdbM;}iXOGtWyT@$(y;st89ovrB z_Z^$x`<{+@8?JTH@xAQ_7ZKk-w!6mn+I4F;tX=E*zzZy&-<=+@KTDnX!PITsUG@)CZ~o(qT>C^`za_n4_hp33 zSJR*L7kTd=q;CF&)QS7tkb6?Q{ot&x{14KH{r8rCcGkmoLwdHh=KH+@rd{Bs)WJ{6mX4))IK6rI=e3Ko?=5-# zof!l3_SC0;CBL;!|H15iYhLM7QO`_Uz}xe#i*pV4<~2bz)uYOu)-}zs&_$^DSf3I4(Zr#dN=7%mY zzS~AUtgm=TRy%CY)ts>{+O2&zSL;*x+5+92w{_~~k*za#J*46JV{L10?Yi~pmVjF? z@9fL2Pu~2p<@2s$d}vAv!;!x^zocd9gVS2pGj%R@>)q|=YLC~@j@|b1mYDa=@t&FV z(CXb1en-AHH}1@~=F~0S?--NboPK!5Xxsan*S8ek8Lx-ts#;nZj^CWV?e;CBbk1r^ z6kS(!cn;dmKle+E?-PG`5%K+h%*E|q+b?q(%e!FxJL6L`IyDan)<3qm{;{rK)b)+p zr}g4|sv9+pr_QcEI&IONZ}H5OVmk|@Jz}jBpOjHxoiW;#4iA_`e@o)+ySg3R*4QbmxA@6H{*mIMs z-k&4>QCh!$HTn99Y1=+N5!m@F?@z9OcgDMYG<$T^>#?&K?>*(5oawsc-dCkn|5IrN ze|wJh%EbRc$*V6+3E`TQHLh9qNHVrl(>i}@uJv`v^*g5i`n37qn{vTDIr4|n-qv-e zcK)@r_}>3V77^e7>~Y5Tu2=tFy>iW(_44lp=HG3tZkto{=jNLo0rIT;9G-^Vmh0A1 zJ9n~eaGh<|R<^c2b$({^z+ntYbHvU7Yi)OU=5YI?+uqi;w~l-1=#V06Ih0r#D&Npo-qGMoM2k-NXyDocc?_DFYPakP(T%U^4+`jGI!)&px z_R{rRnllfx&$>$LFqWqGnyv}gzptya_8R(M|C+YcwY{w^?=5lcxAX8`;`r1j|L7v( z`==gfeE0IFdHQ$wa~8g7Y31|P8`rM1KJp^l&)YlJ$bNpxzx_2G`tYqhjhY*@dg(cJQQTgci%-LWdom)kPd9<#=GS4Zu7rER_IimV;c(kJ}3EIo`z z>x|JJe;AYU%tUKP+;ok#9(P!8x#i}zcy+y(&L8i5@0Qft4t9LL+YdJ!bGRQ3b5?uL z=yDmt?l;xD|Y_1wETOIA6rCx|A$=GG;a6W zewowPT0Y<9^6zykR<7H)-u%!7=HGXx)#>-r7WKJhf0-E^oeB1vY0>(4O1HOWRJzDy?llwEUB4S^GjtxWAFst@~3l{=VfON*mnglm6qhy8TI7${x+D4`ttblKOaB z?H*1E`LUFR?@PJ&!x?pQNy^P%NsC)&bpAPpSKojDZOwa9gn2FV=A%?Mdo$h=xAojxuk5 zcl*aWs=T9L+GEz-?V#CSt$}wuc}vI5lUtH+zTNfFTRLvppnJD%ttJ1CF*$5j%5JM% zpAg!utq*jym*%y-mi9|`MfUc+byc@MS8T`nsU0!XG3y;MJ3KA8d41~{9q-aHI9;Eu zW$mN$@6c1;D{I;PkhBUkk8a-I@_YZvj#O`nrFDmA=9P|LZ@Hn@yYsK5#rI=Bv55HY z7}m!V-@W?jemnnb7QbZ)<@2nMyf&lH*LptD0`u?TS$ypoZkc%Be76SO@hy$fmc<); z{kLsYT^Y4?=Ar+)J=KRNDtcFF{e2i6-uON?5!n$Z!%B=t=V!~{Z66=zva}Vi_2I^N zd*6qVCmkL4oY^&W{92FMl6PzCt+jXL+HhTM$M0B{-ftSqElIa^ptbtmY1*#e5jkxa z@3pn{uXl=;>wC9o`$FsLT{*JPF>YDE|82@0q4$=q@X|9q_d9&-mlogKRxTpGe|#=( z_u77)(^%dG+t1fNVa4k8Ycc>`{=K;T`?D!s|4GW`UrtH*A1wb=%He;JHC+Db@*i6M z#guX%NNM*oDVKjfrQlDdY<+85t3Q+S`+fQT-ITxoIBm^$XUlJ-M1EUZoj;oWuFCJ% zrW}1w`oAwuDfxz!i0@7R^<6o`r}OS@vl8~*`Po$wmAj9tGW^@OXl|En^;^(}ezB}u;} zv3}9Q<9o~6Ma1|2{W#;h@ji|D@9^g=eA5#0?+s5_w_@$O^&72!zQFwZEs4EPBvNij zgnee&cQ60ivVXPw%ZZY^6IokQSH3(k^s&@|zm(|yKbL=Z>b!rM+Uxrg`HlHcC7Lf> z_FZ}PkMjOU60KLHPTO^v?n})7dH(%uYSn+9Eq5l~e=qO2C_Q11%+7X8wsuC#Yx7%I zRsCSL-IvIGBzt}+QF=?}v)q%b`*`-cF30&yBJ`a6dwH(xqq!^Go!76*dv8kW*AmMY zC*9R1F3jEKzT87@P0PT^+2@?>e|=v4!0h{moZ;fU@AAa>x!M06vwpMFbB3-4aY6RH zG?Cx+WKYX0JO5f*{(a)cMa1`a=dz}8yR-azmy7S!dBDNy)#isTFups|vvulW4bq|2 zykp8+er>I^qdhtrd?@p_wthffAI6P$9!A@=+vn81Tjp*JxTW~#raa$Kpj|<+E#EEu z@1OnKd)9PI*Ije9eQCoAj6>bIqu>w9wyw$8XB8cpbe}u0|MvR#@GOI&{@=d5mZn?F zZtcCjYb{?7vp(Bq&{x~T)-4_N-nNFe<+nE8miYD$bf4$W#-By0_&?FK#e&cBux z-~0dMBI5g}A7^~`>ZkXCo&Pn9-?D`KJM&96tXsWejpqw4Fuq$K?#zoJ0vj2v!M4Y` zEl|Ta?n7toTSGmyGcejh)+ZM934l9lvOU74)LPp=HjE%2o*>?s8pf=*e%$(S+s20V ziQ8w@mbB-m$9tHgeNgTSy`uJ=b!J;5tu1)1*>~JoS6^r=*#WcoY+u;$w4p|ETlo3} zwf3gBAFi|3hZ$!*UXOTiw)Xs;<39Agx23M*@cwJ|Zr!~ta;*rIi{aDsf`($4ERQjGDNGdW-#(D~ z?C+;9{Hv+e-kzT1FQw-EXzHq;O&$5;X)F7SW&biQa-T~r`cKp9c3W!hx1^rCeEFTJ znctswwQZ@H-O`uV8~w{QIJw99>A zcC`C)eP^d;d|PV87v<~^=2%^a@#0*=ucZC#ooO+Cbk-u)8#zLM{o6YmcuQofj1Z^#|t{zUgj)3S9{+Osam_lNS@NAmvr6K%KV zE^zuQ9<^FJAeqNd=zc&B9D>KX9Kl|;hy!R8?=j_DvdvfKk z%`q=XyW7>d%DdA3cTw&RZGXQkTi%}E-jjCpb8`H<79QV^{q!Q@`w6>me6LCqy7sjN z=HHhjRxV96b>*b%GbZh8iPpAPe<%@gNxsj|Zy!l6eRcBi-%H%xmKeG^TQ5%}ej*Y4 zKqBd*$)g`kEUsF9b|U7Q#P02hj&Ee!XOh!Dn)iP$N53ff>$y4F4f(BYndLul- z`sb3qCglBJer{fUQ}*aA)SZ7Vt$e=i=N1v)D|XxXUcGAVx|Q1378u{{ZR(n5mnA;0ON`x_ zf4g#VS690&QTmBQS65&ARHF96#McLt>Il(Gv+pf=z4NX$VORe9HC z$;Z2n@r|?cdwbyf?37b-1()Sq*C&cEOB`RC_kArn?&aC*itK%E&hxI6vLDR$i?i25 zvpHz*&Yo9g%RBSkx#qW|Omb}^``pC#Df#Y}(^5)*Uyk-b{;HjSEiJyc?7fKiUbWlC z_nNh9wVy9^{g#^(i8mx7`mCTk5^o)s@{L67ZHdp*6Bk>PPkt^je@9~H_N?o$ZP`Cx zeqEyIO(}Wblc?&tihWYRZzLE0p5+(jXe~qEnft=6iQ?N5SGOb&|C1cylElm1+4tJi zsP9O+t2%r>as2MY`KNOBt%>87p|8w){{HfNQlszP%Anu|7!B~vr|uhAirH5h3j&xx9mK;mstNkai2xR_s>4g`0nLT+tk0qpR@2yOX?q6 zy<+XERmQI^vi{w1rJccbY}$+4;@mNv9WmbCqK-UmU-65QYEM+hYPXlEYa(`p>hLV^ z&a~`mY#o8xKB~?|Yisn2vd6x8{m8s>*z5>hKcPL(orO8fQ9Cp(>mBLc`C&ckFw3iB z+uJkNzN}#mX#3FGb3OFVwRdY6@!n&#)qUtG@6~nxVIB5i-dlU}+Mm`}yGmO}uMSr` z^qaSTYe7?Z) z`AbqyeMwep=qQiQ_H3_q`-h*OobKiM@5EVrbd`t3NY^fXQF7Q*GVbf$X}3Nxd-ti} zeL_KJja;2qyCTvVdG{MKdVARJPhNRda{R5yL(j?k4$gOb#1BmQ{QT6xI~uK{ z%&yDRMP9J%HR-WBD5d#6LFHq4?a}1FZ8v*YO3yFNHJq1M&za@SZC!gb`T3Td@2z?7 zhc{I_h9n%8&ghyeM;VErgqSAuUBR3g*oTeS?T>1`CEQHx%oSibN5Lq zpUAQQB>SA1y86z)mKNVn-hUDC{dk`ry357)y7enFg-rk00^|FvM9szd%)c)&a!FdG z+p4xTad>WiUYw}uoXLw5HRq*d-T%(a@8=~B&P%*~B_;NcWWP&hHQ%ceB{!wq-e(PM z$q~*htrSM{>-M<@alI6*nf%dk1KgpO$l6 zk>B2vI{(cnL0^?J^sgqu-)-+$C2WmHD@;GMtdtUy^YvCuZwQ^84}G^YE;i)F*@VxkN9@_7i8Nfg_TqAD&|# zp4VQQ@h!*X=g~RR%k%FsDI2s7(D5ZLHNPfDdqv97C+6H8Q*v1LIXp){Fnhi-?|w=4 zJ}$3!W%Q0y9nRYEH7`oKV_-U>t!05e6TD+tUzOjE$(}8Dcl2vV*Y*iY&r8XqNA4^A zK6F-l=zP(GbC!O0j#SFMtNFEYLxPaW&s zv1t8lcfI3|`|Ps?I-B#L#Ma)4ovvl{l6>wPksU$W@#Vu@&S4~ZW9f)|KRbJLEPHFe z`=@r@m7H1!?#fDg=O`U_+VN+tV|N^EN0Pp9R^x4LzcuC7-@9({rrZw>o!v?L`cQi~ zAZP6ewAbYx&{1by53b{4yGC4Be%UX%^cl(D`z(?Zay=a{-`S;IIj+wceSgXeZ^&P9 zWWGCswSU(kX&JmEErA^o)7JAh=DaV?ov45P&cBux-;cd$5%K-h#~I(f`e~lN^S@^C zTb9(mwkqRW)~_-@bb;mbuBg=3tFD&N`4z)F?#`+n+Sxh=wC!z;>9&eB*KMpfYCEr^ zqqjR7rE4T~jCWhx8hOKf?~ViQ6T$k(X)I9V^>b zy#q7)y=`M%E3J2l=GMcy<-^)dy-y5ffnUmf;#v8vdkiDn56t;Hv!quy+;N(#56?zy z`&w6#=u-%XCm?h!iLScZ<2Uy{>^pqymlogKj#xx||G-?_?zR0or?I?Cme13=mPe48 zAG*N!ZalY)+qD~7((TNymQ7ol?Pt8N4{INE-d7{Jd3sCO!%Alz_uaADjnzg~Be{2r zeG`u_NO`^O(2e}A=hD(|^V_bP(s?XhQ@MLRJ2BOxHR4)|czS*to^8+-7&_ylan~5{ zb45D-z3a7fO_;X&^{FR4YDdSn6w%S}!#uH$g6~>yy=!!ZivFD~ne14x^GpWcj=+9$cF?=#CZbIM1_E zMtyzCx5uaL>!P$~-JX`O>(kEMm5AGZ{ffjyXJ@w+>*lmyeKqCUzf3FJ)hQLfEiF?w zH#i`}qR%@1cDD+|)QbSF7dou94K)TSKna z^+bO;d0%_SI)AJE(p@pNx%j5ksGB3Vblg01!=Y+Mk-X6Hl^lquUM`-!I>$W$aZLZ(- zoQ|Ab)8=JAmsi`SuutCE9KTmIlrft7w-2!EGd(M*&SveikXi?6*`&wp)gO^`=gu9t z^Xz+x<6HKB*&^b5ug4kRz5HpJ{X6_Q3*WS~`uEBW>sGAVV11^=ttkz&X&JhUVdJp>4>EJ&c4=iZA;OunYU%G?bG|_=deP0YtEek zI+VeWPvi~t_14JS{??jt$J6xbosHbKY!6QgJUIDjP zO}aJu*01-@&#s!-rvtTC-8ISkJfxnhy=5H>)3p|RPiem2(n0ItUEk%9?9sXBJyP2e z`t+ifCWdylVO-p>-g?K{_1YWJL#+4dOT%9?lr(xbY8j$;g`Iybt^U2oYZejTkN5dw zyIy>!b#3(u&nH@7d=KN&+CSEw;*LFSzT7;ut!nLMZi%%e_x7N*6yDyiuDa5^{W*C@ zN3{*5>&Eil+198WVrp2=seAWM&?q`6typcxYx%y<3TeB2BdR5U7v=Z1xHZljvCm)j z-*Y{U$M#tdYe%)UyVufK9(t|YE;zK>_37fr&bIaJ?Mpi%zqQ=myH3Zqwx_#KIUSyp zHmri&JI#KxJHo+vci-7Ozjv|4m(Q2IVG;5D|IX#hdu_iBP4ntqvi*F0Mz3vLX@2Me zt6gcOt;wx>b{&&Oc>BZmnZhp`* zs@)Y_+Q-#V<9jD+8e{FT7(B4zyc$q!FB+MM5p zmhqN2T1M}GJwhY0WreQN(pYEEe=jh;TOMvXySZ}X zbLg>dTUysjYkvBYeB#?u)w*j-@r~y8Vzoc2W%r>Mt)=g-jNUxCb@5-wwmw;Wcn)`S z{+^+=`{tU>PrHB132hA%Ek*PmvGcE`#rL+i zE+W34virvOx)rH`YhPPj`&!Gk%`v;OTGv5s%lA-6ZB4fMY0K>`Q4edQJ}cW>x^3>- z*;~!G`y|k=m^j34_wO;g>RH>>T9581v96rfI_&{@UuTxJMXhD#mb>@KJ*3AU+WNY8 zpP|q;fM;jlmX+J%-n#eC=Uu%Iw6xGWOh>5nPI6R^(ECE~8ja1??EAj`a_o*(?bWol zen8U0^96fV9eLBga{s))@9BKD-fdcn_}ged-}0-Ai0{32+xSiaef389_X6|p)}`Ab z-I1ex=1<$R`i!4plxkbSo0B$1`&6RNA8Wa`cY?;n3-fAUZ%(_2f3!D6Vart-asx4eM{)g4_l&b>sX%<-r3j9!5b@`XWd-)my!qew-@F}eTsKye>e6z*0d4Y z`6c^hYkS(-2i`XEjv{UQTkjezzqg;R$84W>+r)d;j)U(WZ82*Jpi$X-Q)}^k*DwRceDuXlOwc@&@DUvT3Y#h|934S zzMq!Mn#S#3+b?ta+OApuUcYW*R%jC6i;M5pU^|+lt!FJmx0E~7i~Da|)I0LLrRe6( z&4U|boekFB?dE-L5iW1afc^)H)@9#v97M%*0Sd0!}yd&X3HC`$@d-YTkErf zdnaqmHg|5lp=E-ezbhmUrH%gO9huucz~PRy==gr}8H(WC>yFJh?`?l?^&+u$nzpJ+N zXUhSt`L-p#ueHA1-tg9roAb7v`nk)Vk#`TH+>gjH8jGz549_=cS)%QE9sROjBEIE_ z)+idg!>pL0-rsvqBf0mf=ElRQ_s$9JNS2n!TZibKqgOrLO&a?>Tj!uO;*Xx)QCeyk zM$<1kzV~?FBI5hM?6&c}aebaoq`gdEK8)MDc4$KJb_F%P)-QSvjcU6{WCPLdz zK9tWpo3uG{TesS>*3xa`cE`-JBhupDxn&*kIrMJ#mFLb{#)kf}=E&_aZ+W1ld=7Z~#KmLl4d*Bbvf>)&lJ=yO$ejK*s%y!GuJ<5yd5Xs$lg z3R=JK`C6M8`uJM|XghoFA-xZ@2GDZN&cBvcK413!Ma1`i-hJbH)rPg654_0vB^?#o zy73YD{I@3{@w^|?%Wo* zma1D4?>mNeuGZ(9?j58t*!J_@AzF{$H~+Ooe|Sn*>jHiEFb2Ktay`<}Ki)gRP-|!; z_g>Kayra&0pJ^ZcFg9-Jjq91)E7vh~9WVEy?AH=R?^Esd>wT&4S8sJYx(`?#7ak#yfArpTi)8kHq>%|F?WfcvvJ$rvyN!{nH<0M^_G)| zytDQF-bZ?G8S2veB-N6B%hYYt?=c(MT{WVw^-6kY>Cs!d=t%O;{~lWHhE&@Dh7}n* zU#6q&S`O%6&@BgK{96C|9_@(PD~ry*w_UV|`2MfEYkY55xqAJ&Rod4U7~jpqTB;q! zbnlb67{+5Cp7>}!*Ux{;y&cWfey<^C@3^nlhFiLC>vePEzS3O3M{CaA5oN8{w{5!Z zVndtt0oki{+phD{{J8IFn^s4#H}~x7OU+4#zUq#ZX>Mq#w>P`z zZjRsa@4uL>9Rt@Ic%!$m+Lrd-ms;v?%fRNGrDgNJHqz-|UZjbQLC)`%B=4@?y zYL3>`mRcun%U9dV+DqKAAsxlGPhRZ^(nfZV);mIbz+1O(YxhBkm)76gKG*klwpVlU zpS|(*WX`| z-(Q@c2j{iE=ZL)LghX1$ueDcfpTt+!P;At9rNm)&cE_c3#7WBoFH5>(LObjFfE=T- zJ&Xu%pLN$@?AbdLtT}LJf&FrRYx`Q?|BKnbd40?8&G*|PaNz9RLqB=1qd9rs*`xN} z)O@=6cT4iUg8m%pA?>?sNn_|IJ1T!!OAyEA-3O-(F{~T2`1s!c+C{|oeBAD}Jz}S^ zyi4|fuU@%f-Qw(!PFdy|Ga z8;2*RhbSAyqBMerF{5o?YfD|*<%jttZE0_>baT$Ozc;$Ub{-}wp^37kh(7Um-C!}GxOS=NnMcj$@}!(yRuSK zpHud5-uFj&#?al_vE*`lf&Mf zbe{lvM%Hq=DqC;LG5h4wEm?W#?EH6SuKRcM`^|a%<2lwD+1i!P`()57vi8zfv+cI5 z0QJrs_x*V;QP1}L(6t!t1VpZ@ot-XYs$k2*3JxR>sHJ9 zUF~Vd`p5^R1U!sIdwy!}z0Y*aTwC(nQrEHKZJ!%vgEcC9?2dqMnS7WV)*8SNr+vM3 zjF$Tw{q5cE-Q4sK>(0NHmVZBX z=OW^JO)hI1w|i~B%;{^pWc_>Hy7g;TY*7DRV0@pMwL-V1ynACxr(HjFTh=H2Se`d@ z@vQuPOG@jf<(WR$rPO+1O39y3N&Cu_X8X+EK4bfWY(Fcd+MBY1>K!RF_gUgsX3I6% zZ(GXNpG+C~_N<`#k-X!QJg4a0DU08f{kmrBqdDFUIdY#^)D>LMO}Y8g+5by9OP?)t zdP>f%J8VsP{^pdhzm(GS9l5frXRltA`6d_Too6rmd&}?6GYPiM%H$X2NFPYq`r_0T zu1cBvo}Br@ocDs9`G#Eg+Xox37aOVaI|YA)Wf>Ma56lYFtQRLu*A8P#pe?iw#We*4Y(&gRK|&+x>9p^V=( zBl=25kabph_v|ZeuN(TgTH0=3+0ZMu|7;zmmd866t^HnI)v5h$eQ&Q|n3vMM+yCBU zx1`V>`Cd``=9}+#erWe^-&$Mno4>bZZ+NQda~2-oTRyUg_+GcW#`n7Qt5$DVXZ+d% zR#_V@h))c=o?|o;sU6rQQ`iguXzVP@y@negK?_;ZQ8FQxSQ`LrT`*Yf-GM1ij_`;D|`U!T;UFZ+XK z|6f|X|7zLqW##{`F8lpidH#=6#=d)2KEF5R?fdiF2T~%wBJp!>ox!L0rIm2lw@o!Cxzc;VEBdN1xw;-`_|t*dxn+ zV0oY9dtGwkK24-g75-$N0CH<$^v-ea`@uc=d3DbH{>18Q^CXp%@|SJRwYDby*s?#)6>QCSpA!7e z9QCTy*ssi0{#NcsFVD60StqBZgmOmG_vWwpSg!h^g~#`kKeLGV-mu%o_r^7=Hf&V? zUSRqB@_eS>k~n?atTg*8DHXpo_0L1op8e{4*3Zh8H|F0yyXQ^${kXjPlEl$#Q`&uB zV&ZMd4PTc3PD-A5Olr7qO_cn4BCjRs*Jtb7(rfkdyyK*#T7rK`qU*S2ugN+3>Om<5 zACgk_$tg3xK3iX(BmGLYym~e}<+!|hUXI$*{jcQT6H;D2JoWPzXX_hs1;^%<*XGJj z$~#}3I&?>*9h~cU`K)|>O18Z^IricCc~IWb=NO-uZHMPwugZQcv3D$8N6sFUWArb3 zRrcspLw`9(Yi)h;&kx<>u|>r9`rS6ZGq`1~@#l-ozpqX%dQNh-)~UCq%-ojblT&AX zbMnJ&voh}cl1H|FeR}e~H>8I9((L*1e7`TT`e5FDTHe$8`7w!)mX}XYF1jTV`Od`j zdvm0tQ#*b|a@g~;?|q5b*0tZBh-y5Yoc+$t>u*lo_?-NAQsVfvDOLBG-@S%k%ULeT z+253Mb@zE~{yjcXcuKD1>>Q;}FMm@a{p1|&HHpLlY4%PK0;F`pJp8SLF8>CQdfzUB8@G_an34Te3&<+~%=;f_K}@ zUzjr;msmP3?|gB#9iL;oBvJXYw0XZQuf8(d8|yF45%$aZn|HUz`&Bu6|Lgelmn9B6 z{^gjouD6H$h1u)4#Py4F4`?s`0okX0+ehc@?E~m@%n!}ht|8a;Qrr4=RQ`KuUhAlt zpdvY*d>=j3R84(S6qPus~Z&A;d7JfFzcds4T5G{0|6$$o3f{ypnm zsc%1&*07cTjF1+`l*PJ3W8Pg(-!!Y;kJp z6sPH-4@~cQ`?H%L zcU6aB{k0ymIcLX~9h$w4$sWyVJFC01TRID67_ZuKZo~R89i`fy?oD}ec%LvdtnS=d z<6Vbsn04B5uU)OFGf_I`wPzb!Iv1tA{q6Vf%(Xt-cI^F!O7 zK5bL~4u8(VH!Y!mEbAlZ>A)K{=pS2P`Mf3Ao}zDev|(-bn4!g<5KDbo99( zN6uoik@Ni2QCs>wHl^tU^4?=+@z5EcE$<$da_({2`}iF9=zJfW7;Tw&^RoYz$az8D z*Hv4(DpKRE^FX^6%WLzqW5ru)?l{;!m#m{?UXndKC$(cxdJpK63Hr0EEO#vYFnYb~ zymW4Q?Zsy6dBKo@i%a?~^DzD4+h$Ea|+B#%)(|>&UZV#COMZACi&g%~Lz~WLW)dSbwc` z;6`4Luva3s@p^o|JEp7=+UV_b#5%vD|4j;brgE9@X*{1I#;G=?s`qn&XHS2Xl~v!Hj?}NlK!pTzxRY*|1a%4yq8%2 z-v5sm5#RrEca870D^{*tv&#A{3ykl!Id!~fpA^(hdMYU;u#q&#qR&ij%a@51zgUz48q z%d_Q_T+izg!CmFyIjNy<&8v5$HQ*1Ez9f71>BL?A@v_w1FPpU|+@6~EY59Il+SyzB zI4^tNoa?wWBWE5?-}sHG`@b{CJZj@U+A){$+U*KzBt zZS5;*?fP)q#BNBG-k0NlE#q81lNh@$QTLv-eqEGUyCSb#le0gVn7lR7c3N7#&PbGB zoEEVUCxY+Exh~0j&rHwU8}puX^6xwH?=3mgwYke&mln3S=Y6lvtFK8*`enI?T#+N3 zk^X|$=V+(r-;P5)CtGjHG0vKOzawpjho}DW`t$%Cmv`;_YiZ^4J^pGD@%?XdS<|@P zYx`wRU)v?i=WExl*|=i0@#hPS@7of^4=wxNW$8yC?bE6=@-FyZDFF3jX2j`IWS*eBSLcP1v!P1JrW zM`+*K_btCZy=CvqYuDzT9nW%Uwp^Dx$3I{G!9;Awh__sQMUHcR&V6Cd@<++L&r3u$ zQeU5K=jUv9q^$8!&ij?wmJj4yrzWD$N*;b*`u0AZqkbV#`p*13dDipw-bC!Xa)jpN zossg69OLyl$1qywlx(>$J@{L){nYHy@i-^vx0cM`kmFvS|91YhwD^APdv?U?^vHkH z`2LaIHojM;_j{G~9Tu2>UzmLE^3;7h0{gXz&qLFa-jQ1eCZalf>%2tE*@?F|B~NQR zb6eJ1&OI})wj}(u#Mj%iwIfDekvKUg<$#YSZf;M+elgK_UUI|Nq|AF|a?mRio1H8E z*+l;DXTOINb0;RA-k&qwnq2pS+%JA7x&EKzoE>*_N}}z|d|#ON+>$ZrUrc2GW$r{R zi(i#!+?FVQLvsGR^ZjtP-JP?Yl+h~hPlRsG-*jHC^S)f|A7y6Db=m)|DUZJ?uV0w0 zS0)$#Vy^NBa))_qUOPT_i+81*-?6Z7&fVg}>CyklGzjO?ON;Mq-?xbP{=atH_+GzZ z{YK9RUS$4#Z$8PFrL6q}%fFbpVU4{<5)c1&`S&hw-hF#!oP6){<;#CB5&0L1yWdW^ z`zwj+oAcY%iN$5hevP^qlDa;3gO8+y|Al3LmhaE!_iJ($w`Q*o1EJb&eu z9Py0YnL6I?gSkgtpZ_}R?1sF0WXc+^&g(bN#q{*orN#G_A6P_u|KRQ#-)mN{$^Sh6 zdx80P`-r+WQ(K>pPCr=NyxU{le&+UowRfrg(L0`B(y<@ISeW)K9h|*}CyKORtZP8@ z`3P-|-;|?uY@T?P!>ewP_1| z{~exN&~YilicHVT6&;rE_Sd!bzhh$B8@GS{f_?MOj$Rp7f$H(P*2C~5g#K*LS+}>B zzbjAlZ|^&YtLydlyU#v3WE#+O>7~W@i9fiA`2P31YkaTWxN-f8b>e%0@%?MF+V%&N zcej1)kMr|O$%p?SIpo>N1;2Osm(t>Wcgp8q&Fc@R%(`s(V|mB6eWoVRW07p6pfXXcDQl)SMmXPv!ybMo*9^2*)G?e9y@`7;s8RYk-MDf+s(zj)=Pvm$X%&VVEiJ)UmKa>3W6Uo!>Ob*-} zxv#V>pjUBT>KwNv&uV z`MK=-NZxr#uJc{98Emge{B|XWM{<4d$@$Jn+5ZDM-Wh2{>uk9j^V=;c_n(&|UyvI8 zDJi$VHQ#U8d3Y~ze9M!6coFgaJ-cgsXLX158#frgw!r-Rz?4ioO04Unb|hEFvkarQ zTVC#1m(B%mxw-47^@(G{*zgynJlqkj&(7-y=4VUmEmc1!<>5XHp`*V$8no-34P)MV zrtUkexYl$_%Pkx4lOuKYw64kAQu{FC=J`4HFjB7LYPvFVudAb2Tdr>Tyz8`f>`bq} z^@lzI={ecb=L>d)xL$9^y7e3#(Y9l(O`lk}V~qQAQWEb-xR>Qvtzit$Xc|iKi(emk zj~`h?eE+-MHNMwwT)lqvM(d|8Fup&LvhPQer+sMI^0bwGI=S~A%bPbZTYi6D{d~%| zZ3Fv9%fGSgA1;3|uY5hZ`#H&z|2%o_?a7_*Ox}K1^5##b)#~At!Ea7}`q|{Twt~oR=r>y)s))ODp`1X#wwAEgw#<{^7Kt-;y(2m;Cvp z?DgyU{iYo0*8DY><=7v}zUL(W-jeO_$ZNNx{C{zd*L7hp&cE#$e@*_~`Pb6Q=gWR{ z5%K*KyK8)}Te)G~D$fsHWcj=+D0JrLR}xK+BvyVmvE90HpB8dy;^f-I*0pJ={_f?U zP9%4X%bz7WI{R{4YOG&RKi9c=$GzF>Q;F}biOTB|vt23a;Y8yn6Y1?WKR+?n(K226 zXj@);EdSn|bABRsiN~^UN6Orv|IVAWlz$}mgU<5&a$@qMdB^#Q+AH$!hjV>r=jXkN z|d0u;4_UV&W-j~1ef?U&8`8!*~J}vv* zkvr6fvbCdIuF8Mcr47F8Nw#17)V$;Lorm`l%jb{%+eO6pf7@N-d&A1L>({O^{(OP? zciWX)s&1*c?bMz3(YYmk5<$z?o!QyZ<()gyHs=G9>U^!1x;uNT&j9PQ3;O)gmhL+@ zq|Z6%jIlmBp|d)VN(s3o-%Tky_sE^G^{i~|nfrR**)w$wV4d^bJ^Ol}(cL(ay1fIDxb zb5#2DzCI77k=S+5UpT8PKRf&Ne2v~Ca+ZUZ?VUcc-gi2me8=;`UzBv8Ryv$vh~l1e z-<-L1?e?hk`M$%P(Z+07b?Gz0TYGPtL7#opmDM}XyYozk8hYDscq9M0 zv#9O7utsa=Q8o`9a`gV&8JN9GG~#+E=(EF`$9F}Cw$pXySfjFecISWh&eHs|_ms|6 zZ5!F}6yV-{y6!~hsqLLDJM#LLIND>~l0xr8-MddIZ;7JU)axFe=H2xuhN$nEhL*Zs z+rfEHXS22(v2T7Ko}AE`u6?#a?=d~v&cBwHf1kK=5%K-AyKQ{0S+#Dp{Cko0@1a$^ zpZ7-Ve)+67>bjy?+npQXeJ)u?dknGj-0bu0L{qo-d0u_qNn5r1+`pFI8>a{59Yc$H zTe_S3c5e4Rvmi|_r{E+W1k@ACya2W2`BW#@m*;Y*P3g$-rU-7Yx6BdbY-R&=4bCR9n&`C$z6G&`FnHtJ}YojQmp~B zB+&RCo;lPNXZqCAMr=z6&Ds0D-XFSe%M)GSp=;9g>UxhD>LgvSv3Yz;1Z^+q-EHSz zON;L(Z(Kxt|8y>E8n=6Gzs%`tyX5@P4J$K!Y`y1yFR=dIyzk(|)zB;5Qg|a{X!q(W zN?m!aG1waV@a*5_$Sq-aL~8E>UD>7YY47&|vuJ8tTg$i2*}M0CIYy)AknFo}VtIJB zS$oZgXA*anrbcA*%-%s7m&22V`~0!SYkREQO3*&-XU+b7UTVngGw(>%*63TqXgRyN zdhav)<=@u)yB=!qPaUP&yncB8SodzZo38t#8T_4J>9+V;QU?|tjf9rvOgf8jrS=kNU3cm3<9KI6t8 zeB=$+zyCjca{l-3x5xHl?EhUm-j&zB_gkI6xkc*lvcT~9cOXkAl3gP)e14wsw7sA5)TiwIl-vitXSnWvvE%1=?;3x{-_}*` z`Sim!yab169CF0r z+h59y-}06{W?y^$;?bLr`k76KZGO@Tn-4kq=slk}{@%m(YtJYCwYLoyFdjGi{K6q8 zY~JTdE3-oOo=+UoJKny1D{*=FdwmqLXvin=H@l8+ zbHrFr{?hzk_$|ym+mVNEI{p_nA9KvN;O}+tro#{4eCW3bZGUYBv_1VFSN!N6{b2v( zkM6Z{?o$8LA;;`>#Ic7T^Yfbyf5GM_ZP@dP-}=Hg8S?+%&mD5y=0j)yul=VN9JOlA zo=^Pd3*WNeQ;**~3#0s8nZI@WZ{M=ZUPl~y#8J;U;=s*M`k5mR-@NA&zxn$&cgw58 zpEtUXuYB_l-gED~{~4Q(JaX!iYn>c>mYf{+X@}>q2W;N!&_j;==|c`Xt9@5;}y{Vlhzzbn_|ItUOTK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U 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 z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ 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 z+o*tTRG>2d`*-90r+kF~0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk 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 f-P~a5+Z|rJqX^KT# literal 0 HcmV?d00001 diff --git a/core/lls_core/sample/LLS7_t2_ch3.czi b/core/lls_core/sample/LLS7_t2_ch3.czi new file mode 100755 index 0000000000000000000000000000000000000000..10655d2de5d8439990004d3314438453f2fdd02e GIT binary patch literal 7337184 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

      }q5$YES-A0~c%Uc0i;jWpO#CaP^~MN%QhdjoFxw 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 yei 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%0DtnmS@ca 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 Iterable[Workflow]: - """ - Yields copies of the input workflow, modified with the addition of deskewing and optionally, - cropping and deconvolution - """ - user_workflow = copy(workflow) - _, _, first_task_name, _ = get_first_last_image_and_task(workflow) +# def augment_workflow( +# workflow: Workflow, +# lattice: LatticeData, +# ) -> Iterable[Workflow]: +# """ +# Yields copies of the input workflow, modified with the addition of deskewing and optionally, +# cropping and deconvolution +# """ +# from copy import copy +# for lattice_slice in lattice.iter_sublattices(): +# user_workflow = copy(workflow) +# user_workflow.set( +# "deskew_image", +# LatticeData.process, +# lattice_slice +# ) +# yield user_workflow # for loop_time_idx, time_point in enumerate(times): # output_array = [] # data_table = [] # for loop_ch_idx, ch in enumerate(channels): - if crop: - yield from make_crop_workflows( - user_workflow=user_workflow, - roi_layer_list=roi_layer_list, - lattice=lattice, - deconvolution=deconvolution - ) - - # save_img_workflow(vol=vol, - # workflow=user_workflow, - # input_arg=volume, - # first_task="crop_deskew_image", - # last_task=task_name_last, - # time_start=time_start, - # time_end=time_end, - # channel_start=ch_start, - # channel_end=ch_end, - # save_file_type=save_as_type, - # save_path=save_path, - # #roi_layer = roi_layer, - # save_name_prefix="ROI_" + \ - # str(idx), - # save_name=self.llsz_parent.lattice.save_name, - # dx=dx, - # dy=dy, - # dz=dz, - # angle=angle, - # deconvolution=self.llsz_parent.deconvolution.value, - # decon_processing=self.llsz_parent.lattice.decon_processing, - # otf_path=otf_path, - # psf_arg=psf_arg, - # psf=psf) - else: - INPUT_ARG = "input" - - # IF just deskewing and its not in the tasks, add that as first task - if user_workflow.get_task(first_task_name)[0] not in (cle.deskew_y, cle.deskew_x): - # add task to the workflow - user_workflow.set( - "deskew_image", - lattice.deskew_func, - input_image=INPUT_ARG, - angle_in_degrees=lattice.angle, - voxel_size_x=lattice.dx, - voxel_size_y=lattice.dy, - voxel_size_z=lattice.dz, - linear_interpolation=True - ) - # Set input of the workflow to be from deskewing - # change workflow task starts from is "deskew_image" and - replace_first_arg(user_workflow, new_arg="deskew_image") - - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if deconvolution: - PSF_ARG = "psf" - - if lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set( - "deconvolution", - pycuda_decon, - image=INPUT_ARG, - psf=PSF_ARG, - dzdata=lattice.dz, - dxdata=lattice.dx, - dzpsf=lattice.dz, - dxpsf=lattice.dx, - num_iter=lattice.psf_num_iter - ) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set( - "deconvolution", - skimage_decon, - vol_zyx=INPUT_ARG, - psf=PSF_ARG, - num_iter=lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest' - ) - # modify the user workflow so "deconvolution" is accepted - replace_first_arg(user_workflow, new_arg="deconvolution") - - yield workflow - - # save_img_workflow(vol=vol, - # workflow=user_workflow, - # input_arg=INPUT_ARG, - # first_task=task_name_start, - # last_task=task_name_last, - # time_start=time_start, - # time_end=time_end, - # channel_start=ch_start, - # channel_end=ch_end, - # save_file_type=save_as_type, - # save_path=save_path, - # save_name=self.llsz_parent.lattice.save_name, - # dx=dx, - # dy=dy, - # dz=dz, - # angle=angle, - # deconvolution=self.llsz_parent.deconvolution, - # decon_processing=self.llsz_parent.lattice.decon_processing, - # otf_path=OTF_PATH_ARG, - # psf_arg=psf_arg, - # psf=PSF_ARG) + # if crop: + # yield from make_crop_workflows( + # user_workflow=user_workflow, + # roi_layer_list=roi_layer_list, + # lattice=lattice, + # deconvolution=deconvolution + # ) + + # # save_img_workflow(vol=vol, + # # workflow=user_workflow, + # # input_arg=volume, + # # first_task="crop_deskew_image", + # # last_task=task_name_last, + # # time_start=time_start, + # # time_end=time_end, + # # channel_start=ch_start, + # # channel_end=ch_end, + # # save_file_type=save_as_type, + # # save_path=save_path, + # # #roi_layer = roi_layer, + # # save_name_prefix="ROI_" + \ + # # str(idx), + # # save_name=self.llsz_parent.lattice.save_name, + # # dx=dx, + # # dy=dy, + # # dz=dz, + # # angle=angle, + # # deconvolution=self.llsz_parent.deconvolution.value, + # # decon_processing=self.llsz_parent.lattice.decon_processing, + # # otf_path=otf_path, + # # psf_arg=psf_arg, + # # psf=psf) + # else: + # INPUT_ARG = "input" + + # # IF just deskewing and its not in the tasks, add that as first task + # if user_workflow.get_task(first_task_name)[0] not in (cle.deskew_y, cle.deskew_x): + # # add task to the workflow + # user_workflow.set( + # "deskew_image", + # lattice.deskew_func, + # input_image=INPUT_ARG, + # angle_in_degrees=lattice.angle, + # voxel_size_x=lattice.dx, + # voxel_size_y=lattice.dy, + # voxel_size_z=lattice.dz, + # linear_interpolation=True + # ) + # # Set input of the workflow to be from deskewing + # # change workflow task starts from is "deskew_image" and + # replace_first_arg(user_workflow, new_arg="deskew_image") + + # # if deconvolution checked, add it to start of workflow (add upstream of deskewing) + # if deconvolution: + # PSF_ARG = "psf" + + # if lattice.decon_processing == DeconvolutionChoice.cuda_gpu: + # user_workflow.set( + # "deconvolution", + # pycuda_decon, + # image=INPUT_ARG, + # psf=PSF_ARG, + # dzdata=lattice.dz, + # dxdata=lattice.dx, + # dzpsf=lattice.dz, + # dxpsf=lattice.dx, + # num_iter=lattice.psf_num_iter + # ) + # # user_workflow.set(input_arg_first,"deconvolution") + # else: + # user_workflow.set( + # "deconvolution", + # skimage_decon, + # vol_zyx=INPUT_ARG, + # psf=PSF_ARG, + # num_iter=lattice.psf_num_iter, + # clip=False, + # filter_epsilon=0, + # boundary='nearest' + # ) + # # modify the user workflow so "deconvolution" is accepted + # replace_first_arg(user_workflow, new_arg="deconvolution") + + # yield workflow + + # # save_img_workflow(vol=vol, + # # workflow=user_workflow, + # # input_arg=INPUT_ARG, + # # first_task=task_name_start, + # # last_task=task_name_last, + # # time_start=time_start, + # # time_end=time_end, + # # channel_start=ch_start, + # # channel_end=ch_end, + # # save_file_type=save_as_type, + # # save_path=save_path, + # # save_name=self.llsz_parent.lattice.save_name, + # # dx=dx, + # # dy=dy, + # # dz=dz, + # # angle=angle, + # # deconvolution=self.llsz_parent.deconvolution, + # # decon_processing=self.llsz_parent.lattice.decon_processing, + # # otf_path=OTF_PATH_ARG, + # # psf_arg=psf_arg, + # # psf=PSF_ARG) def make_crop_workflows( diff --git a/core/tests/test_arg_parser.py b/core/tests/test_arg_parser.py index afcf0af..28a47dc 100644 --- a/core/tests/test_arg_parser.py +++ b/core/tests/test_arg_parser.py @@ -1,13 +1,18 @@ -from lls_core.cmds.__main__ import make_parser +from typer.testing import CliRunner +from typer.main import get_command +from lls_core.cmds.__main__ import app + +runner = CliRunner() def test_voxel_parsing(): # Tests that we can parse voxel lists correctly - parser = make_parser() - args = parser.parse_args([ + parser = get_command(app).make_parser() + parser.make_context() + args = parser.parse_args(args=[ "--input", "input", "--output", "output", "--processing", "deskew", "--output_file_type", "tiff", "--voxel_sizes", "1", "1", "1" ]) - assert args.voxel_sizes == [1.0, 1.0, 1.0] \ No newline at end of file + assert args.voxel_sizes == [1.0, 1.0, 1.0] From c48b074e496de4875d8529e79f65928ab9d8c03d Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 15:19:45 +1000 Subject: [PATCH 033/147] Add pkg_resources dependency --- core/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/pyproject.toml b/core/pyproject.toml index efc5f38..9340a68 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -55,6 +55,7 @@ dependencies = [ "npy2bdv", "numpy", "pandas", + "pkg_resources", "pyyaml", "read-roi", "resource-backed-dask-array>=0.1.0", From 61901ef665c66e61c4fea4679376fe0101013f76 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 15:49:12 +1000 Subject: [PATCH 034/147] GUI fixes --- core/lls_core/lattice_data.py | 207 --------------------------- core/lls_core/models/lattice_data.py | 34 ++++- core/lls_core/models/output.py | 28 ---- core/lls_core/models/utils.py | 1 - core/lls_core/sample/__init__.py | 10 +- core/pyproject.toml | 4 +- plugin/napari_lattice/dock_widget.py | 4 +- plugin/napari_lattice/fields.py | 15 +- plugin/napari_lattice/icons.py | 10 +- plugin/napari_lattice/reader.py | 4 +- plugin/pyproject.toml | 4 +- 11 files changed, 59 insertions(+), 262 deletions(-) delete mode 100644 core/lls_core/lattice_data.py diff --git a/core/lls_core/lattice_data.py b/core/lls_core/lattice_data.py deleted file mode 100644 index 650e7f4..0000000 --- a/core/lls_core/lattice_data.py +++ /dev/null @@ -1,207 +0,0 @@ -from __future__ import annotations -# class for initializing lattice data and setting metadata -# TODO: handle scenes -from dataclasses import dataclass, field -from aicsimageio.aics_image import AICSImage -from aicsimageio.dimensions import Dimensions -from numpy.typing import NDArray -from dataclasses import dataclass -import math -import numpy as np - -from typing import Any, List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar - -from aicsimageio.types import ArrayLike, PhysicalPixelSizes -import pyclesperanto_prototype as cle - -from lls_core import DeskewDirection, DeconvolutionChoice -from lls_core.utils import get_deskewed_shape - -if TYPE_CHECKING: - import pyclesperanto_prototype as cle - -T = TypeVar("T") -def raise_if_none(obj: Optional[T], message: str) -> T: - if obj is None: - raise TypeError(message) - return obj - -@dataclass -class DefinedPixelSizes: - """ - Like PhysicalPixelSizes, but it's a dataclass, and - none of its fields are None - """ - X: float = 0.14 - Y: float = 0.14 - Z: float = 0.3 - -@dataclass -class LatticeData: - """ - Holds data and metadata for a given image in a consistent format - """ - #: 3-5D array - data: ArrayLike - dims: Dimensions - - #: The filename of this data when it is saved - save_name: str - - #: Geometry of the light path - skew: DeskewDirection = DeskewDirection.Y - angle: float = 30.0 - - decon_processing: Optional[DeconvolutionChoice] = None - - #: Pixel size in microns - physical_pixel_sizes: DefinedPixelSizes = field(default_factory=DefinedPixelSizes) - - new_dz: Optional[float] = None - - # Dimensions of the deskewed output - deskew_vol_shape: Optional[Tuple[int, ...]] = None - deskew_affine_transform: Optional[cle.AffineTransform3D] = None - - # PSF data that should be refactored into another class eventually - psf: Optional[List[NDArray]] = None - psf_num_iter: Optional[int] = None - otf_path: Optional[List] = None - - #: Number of time points - time: int = 0 - #: Number of channels - channels: int = 0 - - # TODO: add defaults here, rather than in the CLI - # Hack to ensure that .skew_dir behaves identically to .skew - @property - def skew_dir(self) -> DeskewDirection: - return self.skew - - @skew_dir.setter - def skew_dir(self, value: DeskewDirection): - self.skew = value - - @property - def deskew_func(self): - # Chance deskew function absed on skew direction - if self.skew == DeskewDirection.Y: - return cle.deskew_y - elif self.skew == DeskewDirection.X: - return cle.deskew_x - else: - raise ValueError() - - @property - def dx(self) -> float: - return self.physical_pixel_sizes.X - - @dx.setter - def dx(self, value: float): - self.physical_pixel_sizes.X = value - - @property - def dy(self) -> float: - return self.physical_pixel_sizes.Y - - @dy.setter - def dy(self, value: float): - self.physical_pixel_sizes.Y = value - - @property - def dz(self) -> float: - return self.physical_pixel_sizes.Z - - @dz.setter - def dz(self, value: float): - self.physical_pixel_sizes.Z = value - - def get_angle(self) -> float: - return self.angle - - def set_angle(self, angle: float) -> None: - self.angle = angle - - def set_skew(self, skew: DeskewDirection) -> None: - self.skew = skew - - def __post_init__(self): - # set new z voxel size - if self.skew == DeskewDirection.Y or self.skew == DeskewDirection.X: - self.new_dz = math.sin(self.angle * math.pi / 180.0) * self.dz - - # process the file to get shape of final deskewed image - self.deskew_vol_shape, self.deskew_affine_transform = get_deskewed_shape(self.data, self.angle, self.dx, self.dy, self.dz) - print(f"Channels: {self.channels}, Time: {self.time}") - print("If channel and time need to be swapped, you can enforce this by choosing 'Last dimension is channel' when initialising the plugin") - -def lattice_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None), **kwargs: Any) -> LatticeData: - # Note: The reason we copy all of these fields rather than just storing the AICSImage is because that class is mostly immutable and so not suitable - - pixel_sizes = DefinedPixelSizes( - X = physical_pixel_sizes[0] or img.physical_pixel_sizes.X or LatticeData.physical_pixel_sizes.X, - Y = physical_pixel_sizes[1] or img.physical_pixel_sizes.Y or LatticeData.physical_pixel_sizes.Y, - Z = physical_pixel_sizes[2] or img.physical_pixel_sizes.Z or LatticeData.physical_pixel_sizes.Z - ) - - return LatticeData( - data = img.dask_data, - dims = img.dims, - time = img.dims.T, - channels = img.dims.C, - physical_pixel_sizes = pixel_sizes, - **kwargs - ) - -def img_from_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> AICSImage: - """ - Creates an AICSImage from an array without metadata - - Args: - arr (ArrayLike): An array - last_dimension: How to handle the dimension order - kwargs: Additional arguments to pass to the AICSImage constructor - """ - dim_order: str - - if len(arr.shape) < 3 or len(arr.shape) > 5: - raise ValueError("Array dimensions must be in the range [3, 5]") - - # if aicsimageio tiffreader assigns last dim as time when it should be channel, user can override this - if len(arr.shape) == 3: - dim_order="ZYX" - else: - if last_dimension not in ["channel", "time"]: - raise ValueError("last_dimension must be either channel or time") - if len(arr.shape) == 4: - if last_dimension == "channel": - dim_order = "CZYX" - elif last_dimension == "time": - dim_order = "TZYX" - elif len(arr.shape) == 5: - if last_dimension == "channel": - dim_order = "CTZYX" - elif last_dimension == "time": - dim_order = "TCZYX" - else: - raise ValueError() - - img = AICSImage(image=arr, dim_order=dim_order, **kwargs) - - # if last axes of "aicsimage data" shape is not equal to time, then swap channel and time - if img.data.shape[0] != img.dims.T or img.data.shape[1] != img.dims.C: - arr = np.swapaxes(arr, 0, 1) - return AICSImage(image=arr, dim_order=dim_order, **kwargs) - - -def lattice_fom_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> LatticeData: - """ - Creates a `LatticeData` from an array - - Args: - arr: Array to use as the data source - last_dimension: See img_from_array - """ - aics = img_from_array(arr, last_dimension) - return lattice_from_aics(aics, **kwargs) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 4a9ea64..48eea19 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -10,7 +10,7 @@ from itertools import groupby import tifffile -from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING, Tuple +from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING from typing_extensions import TypedDict, NotRequired, Generic, TypeVar from aicsimageio.types import PhysicalPixelSizes @@ -40,7 +40,7 @@ T = TypeVar("T") S = TypeVar("S") -class SlicedData(BaseModel, Generic[T]): +class SlicedData(BaseModel, Generic[T], arbitrary_types_allowed=True): data: T time_index: NonNegativeInt time: NonNegativeInt @@ -61,7 +61,7 @@ def copy_with_data(self, data: S) -> SlicedData[S]: ) ProcessedVolume = SlicedData[ArrayLike] -class ProcessedSlices(BaseModel): +class ProcessedSlices(BaseModel, arbitrary_types_allowed=True): #: Iterable of result slices. #: Note that this is a finite iterator that can only be iterated once slices: Iterable[ProcessedVolume] @@ -172,6 +172,34 @@ def default_save_name(cls, values: dict): values["save_name"] = Path(values["image"]).stem return values + @validator("time_range", pre=True) + def parse_time_range(cls, v: Any, values: dict) -> Any: + """ + Sets the default time range if undefined + """ + default_start = 0 + default_end = values["image"].sizes["T"] + if v is None: + return range(default_start, default_end) + elif isinstance(v, tuple) and len(v) == 2: + # Allow 2-tuples to be used as input for this field + return range(v[0] or default_start, v[1] or default_end) + return v + + @validator("channel_range", pre=True) + def parse_channel_range(cls, v: Any, values: dict) -> Any: + """ + Sets the default channel range if undefined + """ + default_start = 0 + default_end = values["image"].sizes["C"] + if v is None: + return range(default_start, default_end) + elif isinstance(v, tuple) and len(v) == 2: + # Allow 2-tuples to be used as input for this field + return range(v[0] or default_start, v[1] or default_end) + return v + @validator("time_range") def disjoint_time_range(cls, v: range, values: dict): """ diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 16b0aab..d17ad69 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -29,31 +29,3 @@ class OutputParams(FieldAccessMixin, arbitrary_types_allowed=True): channel_range: range = Field( description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." ) - - @validator("time_range", pre=True) - def parse_time_range(cls, v: Any, values: dict) -> Any: - """ - Sets the default time range if undefined - """ - default_start = 0 - default_end = values["image"].sizes["T"] - if v is None: - return range(default_start, default_end) - elif isinstance(v, tuple) and len(v) == 2: - # Allow 2-tuples to be used as input for this field - return range(v[0] or default_start, v[1] or default_end) - return v - - @validator("channel_range", pre=True) - def parse_channel_range(cls, v: Any, values: dict) -> Any: - """ - Sets the default channel range if undefined - """ - default_start = 0 - default_end = values["image"].sizes["C"] - if v is None: - return range(default_start, default_end) - elif isinstance(v, tuple) and len(v) == 2: - # Allow 2-tuples to be used as input for this field - return range(v[0] or default_start, v[1] or default_end) - return v diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py index 35f407d..949e795 100644 --- a/core/lls_core/models/utils.py +++ b/core/lls_core/models/utils.py @@ -3,7 +3,6 @@ from enum import Enum from pydantic import BaseModel from typer import Option -from typer.models import OptionInfo def enum_choices(enum: Type[Enum]) -> str: diff --git a/core/lls_core/sample/__init__.py b/core/lls_core/sample/__init__.py index cde99d7..f2e4d56 100644 --- a/core/lls_core/sample/__init__.py +++ b/core/lls_core/sample/__init__.py @@ -1,9 +1,3 @@ -from pkg_resources import resource_filename +import importlib_resources -LLS7_T1_CH1 = resource_filename(__name__, "LLS7_t1_ch1.czi") -LLS7_T1_CH3 = resource_filename(__name__, "LLS7_t1_ch3.czi") -LLS7_T2_CH1 = resource_filename(__name__, "LLS7_t2_ch1.czi") -LLS7_T2_CH3 = resource_filename(__name__, "LLS7_t2_ch4.czi") -MULTICH_MULTITIME = resource_filename(__name__, "multich_multi_time.tif") -RBC_LATTICE_TIF = resource_filename(__name__, "RBC_lattice.tif") -RBC_TINY_CZI = resource_filename(__name__, "RBC_tiny.czi") +resources = importlib_resources.files(__name__) diff --git a/core/pyproject.toml b/core/pyproject.toml index 9340a68..82a1c5d 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -55,7 +55,7 @@ dependencies = [ "npy2bdv", "numpy", "pandas", - "pkg_resources", + "importlib_resources", "pyyaml", "read-roi", "resource-backed-dask-array>=0.1.0", @@ -65,7 +65,7 @@ dependencies = [ "tifffile", "tqdm", "typer", - "typing_extensions", + "typing_extensions>=4.7.0", "xarray[parallel]", ] diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index c5a6560..05f00d1 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -84,8 +84,10 @@ class WidgetContainer(LlszTemplate): def __post_init__(self): tab_widget: QTabWidget= self._widget._tab_widget + from importlib_resources import as_file for i in range(5): - tab_widget.setTabIcon(i, QIcon(GREY)) + with as_file(GREY) as path: + tab_widget.setTabIcon(i, QIcon(str(path))) for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: field._validate() diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index e4ced2a..726d5d3 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -11,14 +11,15 @@ DeskewDirection, Log_Levels, ) -from lls_core.models.lattice_data import ( +from lls_core.models import ( CropParams, DeconvolutionParams, - DefinedPixelSizes, DeskewParams, LatticeData, OutputParams, ) +from lls_core.models.deskew import DefinedPixelSizes +from lls_core.models.output import SaveFileType from lls_core.workflow import workflow_from_path from magicclass import FieldGroup, MagicTemplate, field from magicclass.fields import MagicField @@ -173,16 +174,20 @@ def _get_tab_index(self: Any) -> int: def _set_valid(self: Any, valid: bool): from qtpy.QtGui import QIcon + from importlib_resources import as_file tab_parent = self._get_parent_tab_widget() index = self._get_tab_index() if hasattr(self, "fields_enabled") and not self.fields_enabled.value: # Special case for "diabled" sections - tab_parent.setTabIcon(index, QIcon(GREY)) + icon = GREY elif valid: - tab_parent.setTabIcon(index, QIcon(GREEN)) + icon = GREEN else: - tab_parent.setTabIcon(index, QIcon(RED)) + icon = RED + + with as_file(icon) as path: + tab_parent.setTabIcon(index, QIcon(str(path))) def _validate(self: Any): self.errors.value = get_friendly_validations(self) diff --git a/plugin/napari_lattice/icons.py b/plugin/napari_lattice/icons.py index 7c01abe..dff6eb2 100644 --- a/plugin/napari_lattice/icons.py +++ b/plugin/napari_lattice/icons.py @@ -1,5 +1,7 @@ -import pkg_resources +import importlib_resources -GREEN = pkg_resources.resource_filename(__name__, "valid.svg") -GREY = pkg_resources.resource_filename(__name__, "circle-regular.svg") -RED = pkg_resources.resource_filename(__name__, "invalid.svg") +resources = importlib_resources.files(__name__) + +GREEN = resources / "valid.svg" +GREY = resources / "circle-regular.svg" +RED = resources / "invalid.svg" diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index ae04e70..134e034 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -17,7 +17,9 @@ from typing import List, Optional, Tuple, Collection, TYPE_CHECKING -from lls_core.models.lattice_data import DefinedPixelSizes, AicsLatticeParams, PhysicalPixelSizes +from aicsimageio.types import PhysicalPixelSizes +from lls_core.models.lattice_data import AicsLatticeParams +from lls_core.models.deskew import DefinedPixelSizes if TYPE_CHECKING: from aicsimageio.types import ImageLike diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index a7e9a18..ef7a844 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -52,13 +52,13 @@ dependencies = [ "npy2bdv", "numpy", "pandas", - "pkg_resources", + "importlib_resources", "psutil", "pyclesperanto_prototype>=0.20.0", "pydantic", "qtpy", "tqdm", - "typing_extensions", + "typing_extensions>=4.7.0", "pyyaml", "StrEnum", "xarray" From 251240173cb6b7b0d32d1e3dfa44c14aeaa17fb5 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 15:54:38 +1000 Subject: [PATCH 035/147] Fix type errors --- core/lls_core/models/lattice_data.py | 1 + core/lls_core/workflow.py | 179 +-------------------------- 2 files changed, 2 insertions(+), 178 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 48eea19..f3e1e8c 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -580,6 +580,7 @@ def img_from_array(arr: ArrayLike, dimension_order: Optional[str] = None, **kwar # if last axes of "aicsimage data" shape is not equal to time, then swap channel and time if img.data.shape[0] != img.dims.T or img.data.shape[1] != img.dims.C: + import numpy as np arr = np.swapaxes(arr, 0, 1) return AICSImage(image=arr, dim_order=dimension_order, **kwargs) diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index 19ee222..25ceeff 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -5,13 +5,12 @@ from os import path from pathlib import Path -from typing import Optional, Tuple, Union, Iterable +from typing import Optional, Tuple, Union import dask.array as da import numpy as np import pandas as pd import pyclesperanto_prototype as cle -from lls_core.llsz_core import crop_volume_deskew from lls_core.types import ArrayLike from typing_extensions import TYPE_CHECKING @@ -205,179 +204,3 @@ def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike] workflow_output, name="Workflow_preview_" + suffix_name, scale=scale) else: return workflow_output - -# def augment_workflow( -# workflow: Workflow, -# lattice: LatticeData, -# ) -> Iterable[Workflow]: -# """ -# Yields copies of the input workflow, modified with the addition of deskewing and optionally, -# cropping and deconvolution -# """ -# from copy import copy -# for lattice_slice in lattice.iter_sublattices(): -# user_workflow = copy(workflow) -# user_workflow.set( -# "deskew_image", -# LatticeData.process, -# lattice_slice -# ) -# yield user_workflow - - # for loop_time_idx, time_point in enumerate(times): - # output_array = [] - # data_table = [] - # for loop_ch_idx, ch in enumerate(channels): - - # if crop: - # yield from make_crop_workflows( - # user_workflow=user_workflow, - # roi_layer_list=roi_layer_list, - # lattice=lattice, - # deconvolution=deconvolution - # ) - - # # save_img_workflow(vol=vol, - # # workflow=user_workflow, - # # input_arg=volume, - # # first_task="crop_deskew_image", - # # last_task=task_name_last, - # # time_start=time_start, - # # time_end=time_end, - # # channel_start=ch_start, - # # channel_end=ch_end, - # # save_file_type=save_as_type, - # # save_path=save_path, - # # #roi_layer = roi_layer, - # # save_name_prefix="ROI_" + \ - # # str(idx), - # # save_name=self.llsz_parent.lattice.save_name, - # # dx=dx, - # # dy=dy, - # # dz=dz, - # # angle=angle, - # # deconvolution=self.llsz_parent.deconvolution.value, - # # decon_processing=self.llsz_parent.lattice.decon_processing, - # # otf_path=otf_path, - # # psf_arg=psf_arg, - # # psf=psf) - # else: - # INPUT_ARG = "input" - - # # IF just deskewing and its not in the tasks, add that as first task - # if user_workflow.get_task(first_task_name)[0] not in (cle.deskew_y, cle.deskew_x): - # # add task to the workflow - # user_workflow.set( - # "deskew_image", - # lattice.deskew_func, - # input_image=INPUT_ARG, - # angle_in_degrees=lattice.angle, - # voxel_size_x=lattice.dx, - # voxel_size_y=lattice.dy, - # voxel_size_z=lattice.dz, - # linear_interpolation=True - # ) - # # Set input of the workflow to be from deskewing - # # change workflow task starts from is "deskew_image" and - # replace_first_arg(user_workflow, new_arg="deskew_image") - - # # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - # if deconvolution: - # PSF_ARG = "psf" - - # if lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - # user_workflow.set( - # "deconvolution", - # pycuda_decon, - # image=INPUT_ARG, - # psf=PSF_ARG, - # dzdata=lattice.dz, - # dxdata=lattice.dx, - # dzpsf=lattice.dz, - # dxpsf=lattice.dx, - # num_iter=lattice.psf_num_iter - # ) - # # user_workflow.set(input_arg_first,"deconvolution") - # else: - # user_workflow.set( - # "deconvolution", - # skimage_decon, - # vol_zyx=INPUT_ARG, - # psf=PSF_ARG, - # num_iter=lattice.psf_num_iter, - # clip=False, - # filter_epsilon=0, - # boundary='nearest' - # ) - # # modify the user workflow so "deconvolution" is accepted - # replace_first_arg(user_workflow, new_arg="deconvolution") - - # yield workflow - - # # save_img_workflow(vol=vol, - # # workflow=user_workflow, - # # input_arg=INPUT_ARG, - # # first_task=task_name_start, - # # last_task=task_name_last, - # # time_start=time_start, - # # time_end=time_end, - # # channel_start=ch_start, - # # channel_end=ch_end, - # # save_file_type=save_as_type, - # # save_path=save_path, - # # save_name=self.llsz_parent.lattice.save_name, - # # dx=dx, - # # dy=dy, - # # dz=dz, - # # angle=angle, - # # deconvolution=self.llsz_parent.deconvolution, - # # decon_processing=self.llsz_parent.lattice.decon_processing, - # # otf_path=OTF_PATH_ARG, - # # psf_arg=psf_arg, - # # psf=PSF_ARG) - - -def make_crop_workflows( - user_workflow: Workflow, - roi_layer_list: ShapesData, - lattice: LatticeData, - deconvolution: bool -) -> Iterable[Workflow]: - """ - Yields a copy of `user_workflow` for each region of interest, with deskewing, cropping and deconvolution steps added on to the start - """ - - # Convert Roi pixel coordinates to canvas coordinates - # necessary only when scale is used for napari.viewer.add_image operations - - # Here we generate a workflow for each ROI - for idx, roi_layer in enumerate(tqdm([x/lattice for x in roi_layer_list], desc="ROI:", position=0)): - # Check if decon ticked, if so set as first and crop as second? - - # Create workflow for cropping and deskewing - # volume and roi used will be set dynamically - current_workflow = copy(user_workflow) - current_workflow.set( - "crop_deskew_image", - crop_volume_deskew, - original_volume="volume", - roi_shape="roi", - angle_in_degrees=lattice.angle, - voxel_size_x=lattice.dx, - voxel_size_y=lattice.dy, - voxel_size_z=lattice.dy, - z_start=0, - z_end=lattice.deskew_vol_shape[0], - deconvolution=deconvolution, - decon_processing=lattice.decon_processing, - psf="psf", - skew_dir=lattice.skew_dir - ) - - # change the first task so it accepts "crop_deskew as input" - replace_first_arg(current_workflow, new_arg="crop_deskew_image") - - logging.info(f"Processing ROI {idx}") - current_workflow.set("roi", roi_layer) - - yield current_workflow From 57f9a2c40c08f877205ba9a5e60f274626be917b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 16:37:12 +1000 Subject: [PATCH 036/147] Fix dependency issues --- core/pyproject.toml | 17 ++++++----------- plugin/pyproject.toml | 4 ++-- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/core/pyproject.toml b/core/pyproject.toml index 82a1c5d..5b20df1 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -41,28 +41,23 @@ dependencies = [ "aicspylibczi>=3.1.1", "dask", "dask[distributed]", - "pyopencl", - "read-roi", - "pyclesperanto_prototype>=0.20.0", - "pydantic~=1.0", - "npy2bdv", - # Older tifffile are not compatible with aicsimageio, see: https://github.com/AllenCellModeling/aicsimageio/issues/518 - - "tifffile>=2023.3.15", #>=2022.8.12 "fsspec>=2022.8.2", - "pyclesperanto_prototype>=0.20.0", + "importlib_resources", + # Older tifffile are not compatible with aicsimageio, see: https://github.com/AllenCellModeling/aicsimageio/issues/518 "napari-workflows>=0.2.8", "npy2bdv", "numpy", "pandas", - "importlib_resources", + "pyclesperanto_prototype>=0.20.0", + "pyopencl", + "pydantic~=1.0", "pyyaml", "read-roi", "resource-backed-dask-array>=0.1.0", "scikit-image", "StrEnum", + "tifffile>=2023.3.15", #>=2022.8.12 "toolz", - "tifffile", "tqdm", "typer", "typing_extensions>=4.7.0", diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index ef7a844..34302d3 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -35,11 +35,12 @@ classifiers = [ ] requires-python = ">=3.8" dependencies = [ - "aicsimageio>=4.11.0", + "aicsimageio>=4.6.3", "dask", "dask[distributed]", # This isn't used directly, but we need to pin this version "fsspec>=2022.8.2", + "importlib_resources", # We need this Python 3.8 fix: https://github.com/hanjinliu/magic-class/pull/108 "lls_core", "magic-class>=0.7.5", @@ -52,7 +53,6 @@ dependencies = [ "npy2bdv", "numpy", "pandas", - "importlib_resources", "psutil", "pyclesperanto_prototype>=0.20.0", "pydantic", From 04ba1d28c9b15d3a0b6441528dba368c6e885de9 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 18:47:44 +1000 Subject: [PATCH 037/147] Cartesian product tests --- core/lls_core/models/deskew.py | 4 +-- core/lls_core/models/lattice_data.py | 41 ++++++++++++++-------------- core/lls_core/models/output.py | 20 ++++++++++++-- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 99af70b..38a4f33 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -84,9 +84,9 @@ def reshaping(cls, v: Any): return array.transpose("T", "C", "Z", "Y", "X") def get_3d_slice(self) -> DataArray: - return self.image.sel(C=0, T=0) + return self.image.isel(C=0, T=0) - @root_validator(pre=True) + @root_validator(pre=False) def set_deskew(cls, values: dict) -> dict: """ Sets the default deskew shape values if the user has not provided them diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index f3e1e8c..eea1d92 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -38,6 +38,21 @@ logger = logging.getLogger(__name__) +def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[str] = None, channel: Optional[str] = None, time: Optional[str] = None) -> str: + """ + Generates a filename for this result + """ + components: List[str] = [] + if prefix is not None: + components.append(prefix) + if roi_index is not None: + components.append(f"ROI_{roi_index}") + if channel is not None: + components.append(f"C{channel}") + if time is not None: + components.append(f"T{time}") + return "_".join(components) + T = TypeVar("T") S = TypeVar("S") class SlicedData(BaseModel, Generic[T], arbitrary_types_allowed=True): @@ -75,13 +90,12 @@ def save_image(self): """ # TODO: refactor this into a class system, one for each format import numpy as np - from pathlib import Path import npy2bdv for roi, roi_results in groupby(self.slices, key=lambda it: it.roi_index): if self.lattice_data.save_type == SaveFileType.h5: bdv_writer = npy2bdv.BdvWriter( - make_filename_prefix(prefix=self.lattice_data.save_name, roi_index=roi) + ".h5", + filename=str(self.lattice_data.make_filepath(make_filename_prefix(roi_index=str(roi)))), compression='gzip', nchannels=len(self.lattice_data.channel_range), subsamp=((1, 1, 1), (1, 2, 2), (2, 4, 4)), @@ -102,7 +116,7 @@ def save_image(self): first_result = result_list[0] images_array = np.swapaxes(np.expand_dims([result.data for result in result_list], axis=0), 1, 2) tifffile.imwrite( - file = Path(make_filename_prefix(channel=first_result.channel, time=time, roi_index=roi)).with_suffix("tiff"), + file = str(self.lattice_data.make_filepath(make_filename_prefix(channel=first_result.channel, time=time, roi_index=roi))), data = images_array, bigtiff=True, resolution=(1./self.lattice_data.dx, 1./self.lattice_data.dy, "MICROMETER"), @@ -110,21 +124,6 @@ def save_image(self): imagej=True ) -def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[int] = None, channel: Optional[int] = None, time: Optional[int] = None) -> str: - """ - Generates a filename for this result - """ - components: List[str] = [] - if prefix is not None: - components.append(prefix) - if roi_index is not None: - components.append(f"ROI_{roi_index}") - if channel is not None: - components.append(f"C{channel}") - if time is not None: - components.append(f"T{time}") - return "_".join(components) - workflow: Optional[Workflow] = Field( default=None, @@ -172,7 +171,7 @@ def default_save_name(cls, values: dict): values["save_name"] = Path(values["image"]).stem return values - @validator("time_range", pre=True) + @validator("time_range", pre=True, always=True) def parse_time_range(cls, v: Any, values: dict) -> Any: """ Sets the default time range if undefined @@ -186,7 +185,7 @@ def parse_time_range(cls, v: Any, values: dict) -> Any: return range(v[0] or default_start, v[1] or default_end) return v - @validator("channel_range", pre=True) + @validator("channel_range", pre=True, always=True) def parse_channel_range(cls, v: Any, values: dict) -> Any: """ Sets the default channel range if undefined @@ -322,7 +321,7 @@ def slice_data(self, time: int, channel: int) -> DataArray: if channel > self.channels: raise ValueError("channel is out of range") - return self.image.sel(T=time, C=channel) + return self.image.isel(T=time, C=channel) if len(self.image.shape) == 3: return self.image diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index d17ad69..07b1b60 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -1,7 +1,7 @@ -from typing import Any from pydantic import Field, validator, DirectoryPath from strenum import StrEnum from os import getcwd +from pathlib import Path from lls_core.models.utils import FieldAccessMixin, enum_choices @@ -27,5 +27,19 @@ class OutputParams(FieldAccessMixin, arbitrary_types_allowed=True): description="The range of times to process. This defaults to all time points in the image array." ) channel_range: range = Field( - description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." -) + description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc.", + default=None + ) + + @property + def file_extension(self): + if self.save_type == SaveFileType.h5: + return "h5" + else: + return "tif" + + def make_filepath(self, suffix: str) -> Path: + """ + Returns a filepath for the resulting data + """ + return self.save_dir / Path(self.save_name + suffix).with_suffix("." + self.file_extension) From 094651a9b77646414db89cf4f72d4465e07f4640 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 15 Sep 2023 20:19:29 +1000 Subject: [PATCH 038/147] Fix some tests, add comprehensive LatticeData tests --- core/lls_core/cmds/__main__.py | 8 +-- core/lls_core/models/deskew.py | 14 +++++ core/lls_core/models/lattice_data.py | 93 ++++------------------------ core/tests/test_arg_parser.py | 21 +++---- core/tests/test_batch_deskew_args.py | 28 ++++----- core/tests/test_batch_deskew_yaml.py | 13 ++-- core/tests/test_deskew.py | 7 ++- 7 files changed, 63 insertions(+), 121 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 22677a9..851f259 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -28,7 +28,7 @@ class CliDeskewDirection(StrEnum): @app.command() def main( - image: Path = Argument(help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi"), + image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi"), skew: CliDeskewDirection = Option( default=DeskewParams.get_default("skew").name, help=DeskewParams.get_description("skew") @@ -47,9 +47,9 @@ def main( z_start: Optional[int] = Option(None, help="The index of the first Z slice to use. All prior Z slices will be discarded."), z_end: Optional[int] = Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded."), - enable_deconvolution: Annotated[bool, Option("--deconvolution/--disable-deconvolution")] = False, + enable_deconvolution: bool = Option(False, "--deconvolution/--disable-deconvolution"), decon_processing: DeconvolutionChoice = DeconvolutionParams.make_typer_field("decon_processing"), - psf: Annotated[List[Path], Option(help="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.")] = [], + psf: List[Path] = Option([], help="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array."), psf_num_iter: int = DeconvolutionParams.make_typer_field("psf_num_iter"), background: str = DeconvolutionParams.make_typer_field("background"), @@ -107,7 +107,7 @@ def main( from yaml import safe_load yaml_args = safe_load(fp) - return LatticeData.parse_obj(merge(yaml_args, json_args, cli_args)) + LatticeData.parse_obj(merge(yaml_args, json_args, cli_args)).process().save_image() if __name__ == '__main__': app() diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 38a4f33..2018b44 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -68,6 +68,20 @@ class DeskewParams(FieldAccessMixin, arbitrary_types_allowed=True): def dims(self): return self.image.dims + @validator("skew", pre=True) + def convert_skew(cls, v: Any): + # Allow skew to be provided as a string + if isinstance(v, str): + return DeskewDirection[v] + return v + + @validator("physical_pixel_sizes", pre=True) + def convert_pixels(cls, v: Any): + # Allow the pixel sizes to be specified as a tuple + if isinstance(v, tuple) and len(v) == 3: + return DefinedPixelSizes(X=v[0], Y=v[1], Z=v[2]) + return v + # convert_image = validator("image", pre=True, allow_reuse=True)(image_like_to_image) @validator("image", pre=True) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index eea1d92..813997c 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -95,7 +95,7 @@ def save_image(self): for roi, roi_results in groupby(self.slices, key=lambda it: it.roi_index): if self.lattice_data.save_type == SaveFileType.h5: bdv_writer = npy2bdv.BdvWriter( - filename=str(self.lattice_data.make_filepath(make_filename_prefix(roi_index=str(roi)))), + filename=str(self.lattice_data.make_filepath(make_filename_prefix(roi_index=roi))), compression='gzip', nchannels=len(self.lattice_data.channel_range), subsamp=((1, 1, 1), (1, 2, 2), (2, 4, 4)), @@ -116,7 +116,7 @@ def save_image(self): first_result = result_list[0] images_array = np.swapaxes(np.expand_dims([result.data for result in result_list], axis=0), 1, 2) tifffile.imwrite( - file = str(self.lattice_data.make_filepath(make_filename_prefix(channel=first_result.channel, time=time, roi_index=roi))), + str(self.lattice_data.make_filepath(make_filename_prefix(channel=first_result.channel, time=time, roi_index=roi))), data = images_array, bigtiff=True, resolution=(1./self.lattice_data.dx, 1./self.lattice_data.dy, "MICROMETER"), @@ -460,7 +460,7 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: data = slice.data.compute() if self.deconvolution is not None: if self.deconvolution.decon_processing == DeconvolutionChoice.cuda_gpu: - data= pycuda_decon( + data = pycuda_decon( image=data, psf=self.deconvolution.psf[slice.channel], background=self.deconvolution.background, @@ -471,14 +471,14 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: num_iter=self.deconvolution.psf_num_iter ) else: - data= skimage_decon( - vol_zyx=data, - psf=self.deconvolution.psf[slice.channel], - num_iter=self.deconvolution.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest' - ) + data = skimage_decon( + vol_zyx=data, + psf=self.deconvolution.psf[slice.channel], + num_iter=self.deconvolution.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest' + ) yield slice.copy_with_data( cle.pull_zyx(self.deskew_func( @@ -522,74 +522,3 @@ def process(self) -> ProcessedSlices: lattice_data=self, slices=self._process_non_crop() ) - -class AicsLatticeParams(TypedDict): - data: DataArray - physical_pixel_sizes: DefinedPixelSizes - -def lattice_params_from_aics(img: AICSImage, physical_pixel_sizes: PhysicalPixelSizes = PhysicalPixelSizes(None, None, None)) -> AicsLatticeParams: - # Note: The reason we copy all of these fields rather than just storing the AICSImage is because that class is mostly immutable and so not suitable - - pixel_sizes = DefinedPixelSizes( - X = physical_pixel_sizes[0] or img.physical_pixel_sizes.X or LatticeData.physical_pixel_sizes.X, - Y = physical_pixel_sizes[1] or img.physical_pixel_sizes.Y or LatticeData.physical_pixel_sizes.Y, - Z = physical_pixel_sizes[2] or img.physical_pixel_sizes.Z or LatticeData.physical_pixel_sizes.Z - ) - - return AicsLatticeParams( - data = img.dask_data, - dims = img.dims, - physical_pixel_sizes = pixel_sizes, - ) - -def img_from_array(arr: ArrayLike, dimension_order: Optional[str] = None, **kwargs: Any) -> AICSImage: - """ - Creates an AICSImage from an array without metadata - - Args: - arr (ArrayLike): An array - last_dimension: How to handle the dimension order - kwargs: Additional arguments to pass to the AICSImage constructor - """ - # dim_order: str - - if len(arr.shape) < 3 or len(arr.shape) > 5: - raise ValueError("Array dimensions must be in the range [3, 5]") - - # if aicsimageio tiffreader assigns last dim as time when it should be channel, user can override this - # if len(arr.shape) == 3: - # dim_order="ZYX" - # else: - # if last_dimension not in ["channel", "time"]: - # raise ValueError("last_dimension must be either channel or time when convertin") - # if len(arr.shape) == 4: - # if last_dimension == "channel": - # dim_order = "CZYX" - # elif last_dimension == "time": - # dim_order = "TZYX" - # elif len(arr.shape) == 5: - # if last_dimension == "channel": - # dim_order = "CTZYX" - # elif last_dimension == "time": - # dim_order = "TCZYX" - # else: - # raise ValueError() - - img = AICSImage(image=arr, dim_order=dimension_order, **kwargs) - - # if last axes of "aicsimage data" shape is not equal to time, then swap channel and time - if img.data.shape[0] != img.dims.T or img.data.shape[1] != img.dims.C: - import numpy as np - arr = np.swapaxes(arr, 0, 1) - return AICSImage(image=arr, dim_order=dimension_order, **kwargs) - -def lattice_fom_array(arr: ArrayLike, last_dimension: Optional[Literal["channel", "time"]] = None, **kwargs: Any) -> AicsLatticeParams: - """ - Creates a `LatticeData` from an array - - Args: - arr: Array to use as the data source - last_dimension: See img_from_array - """ - aics = img_from_array(arr, last_dimension) - return lattice_params_from_aics(aics, **kwargs) diff --git a/core/tests/test_arg_parser.py b/core/tests/test_arg_parser.py index 28a47dc..c33d211 100644 --- a/core/tests/test_arg_parser.py +++ b/core/tests/test_arg_parser.py @@ -1,18 +1,17 @@ -from typer.testing import CliRunner from typer.main import get_command +from click import Context from lls_core.cmds.__main__ import app -runner = CliRunner() def test_voxel_parsing(): # Tests that we can parse voxel lists correctly - parser = get_command(app).make_parser() - parser.make_context() - args = parser.parse_args(args=[ - "--input", "input", - "--output", "output", - "--processing", "deskew", - "--output_file_type", "tiff", - "--voxel_sizes", "1", "1", "1" + command = get_command(app) + ctx = Context(command) + parser = command.make_parser(ctx) + args, _, _ = parser.parse_args(args=[ + "input", + "--save-name", "output", + "--save-type", "tiff", + "--pixel-sizes", "1", "1", "1" ]) - assert args.voxel_sizes == [1.0, 1.0, 1.0] + assert args["pixel_sizes"] == ("1", "1", "1") diff --git a/core/tests/test_batch_deskew_args.py b/core/tests/test_batch_deskew_args.py index eb09bc3..c3ced00 100644 --- a/core/tests/test_batch_deskew_args.py +++ b/core/tests/test_batch_deskew_args.py @@ -1,10 +1,11 @@ # Tests for napari_lattice using arguments and saving output files as h5, as well as tiff +from typer.testing import CliRunner from skimage.io import imsave import numpy as np from pathlib import Path import tempfile -from lls_core.cmds.__main__ import main as run_cli +from lls_core.cmds.__main__ import app def create_image(path: Path): # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) @@ -14,6 +15,7 @@ def create_image(path: Path): imsave(str(path), raw) assert path.exists() +runner = CliRunner() def test_batch_deskew_h5(): """Write image to disk and then execute napari_lattice from terminal @@ -24,16 +26,15 @@ def test_batch_deskew_h5(): input_file = out_dir / 'raw.tiff' create_image(input_file) # Batch deskew and save as h5 - run_cli([ - "--input", str(input_file), - "--output", str(out_dir), - "--processing", "deskew", - "--output_file_type", "h5" + runner.invoke(app, [ + str(input_file), + "--save-dir", str(out_dir), + "--save-type", "h5" ]) # checks if h5 files written - assert (out_dir / "raw" / "raw.h5").exists() - assert (out_dir / "raw" / "raw.xml").exists() + assert (out_dir / "raw.h5").exists() + # assert (out_dir / "raw.xml").exists() def test_batch_deskew_tiff(): @@ -42,12 +43,11 @@ def test_batch_deskew_tiff(): out_dir = Path(out_dir) input_file = out_dir / 'raw.tiff' create_image(input_file) - run_cli([ - "--input", str(input_file), - "--output", str(out_dir), - "--processing", "deskew", - "--output_file_type", "tiff" + runner.invoke(app, [ + str(input_file), + "--save-dir", str(out_dir), + "--save-type", "tiff" ]) # checks if tiff written - assert (out_dir / "raw" / "C0T0_raw.tif").exists() + assert len(list(out_dir.glob("*.tif"))) == 1 diff --git a/core/tests/test_batch_deskew_yaml.py b/core/tests/test_batch_deskew_yaml.py index b8c4f3e..e793443 100644 --- a/core/tests/test_batch_deskew_yaml.py +++ b/core/tests/test_batch_deskew_yaml.py @@ -5,7 +5,7 @@ import tempfile import numpy as np from pathlib import Path -from lls_core.cmds.__main__ import main as run_cli +from lls_core.cmds.__main__ import app as run_cli def write_config_file(config_settings: dict, output_file_location: Path): # Write config file for napari_lattice @@ -40,9 +40,8 @@ def create_data(dir: Path) -> Path: assert input_file.exists() config: dict[str, str] = { - "input": str(input_file), - "output": str(dir), - "processing": "deskew", + "image": str(input_file), + "save_dir": str(dir), "output_file_type": "h5" } @@ -60,8 +59,8 @@ def test_yaml_deskew(): test_dir = Path(test_dir) config_location = create_data(test_dir) # Batch deskew and save as h5 - run_cli(["--config", str(config_location)]) + run_cli(["--yaml-config", str(config_location)]) # checks if h5 files written - assert (test_dir / "raw" / "raw.h5").exists() - assert (test_dir / "raw" / "raw.xml").exists() + assert (test_dir / "raw.h5").exists() + # assert (test_dir / "raw" / "raw.xml").exists() diff --git a/core/tests/test_deskew.py b/core/tests/test_deskew.py index 1b30dc7..d08fc4b 100644 --- a/core/tests/test_deskew.py +++ b/core/tests/test_deskew.py @@ -1,7 +1,8 @@ #filename and function name should start with "test_" when using pytest import pyclesperanto_prototype as cle import numpy as np -from lls_core.models.lattice_data import lattice_fom_array +from lls_core.models.lattice_data import LatticeData +from xarray import DataArray def test_deskew(): @@ -15,6 +16,6 @@ def test_deskew(): assert deskewed[2,2,0] == 0.5662433505058289 def test_lattice_data_deskew(): - raw = np.zeros((5, 5, 5)) - lattice = lattice_fom_array(raw, physical_pixel_sizes = (1, 1, 1), save_name="test") + raw = DataArray(np.zeros((5, 5, 5)), dims=["X", "Y", "Z"]) + lattice = LatticeData(image=raw, physical_pixel_sizes = (1, 1, 1), save_name="test") assert lattice.deskew_vol_shape == [2, 9, 5] From bc006e38760f3caa0829c4a1fbc633c25ef2c368 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Sun, 17 Sep 2023 00:05:05 +1000 Subject: [PATCH 039/147] Skip validations when image is missing --- core/lls_core/models/deskew.py | 2 ++ core/lls_core/models/lattice_data.py | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 2018b44..196e505 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -106,6 +106,8 @@ def set_deskew(cls, values: dict) -> dict: Sets the default deskew shape values if the user has not provided them """ # process the file to get shape of final deskewed image + if "image" not in values: + return values data: DataArray = cls.reshaping(values["image"]) if values.get('deskew_vol_shape') is None: if values.get('deskew_affine_transform') is None: diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 813997c..f855c9a 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -167,7 +167,7 @@ def default_save_name(cls, values: dict): # This needs to be a root validator to ensure it runs before the # reshaping validator. We can't override that either since it's # a field validator and can't modify save_name - if values.get("save_name", None) is None and isinstance(values["image"], PathLike): + if values.get("save_name", None) is None and isinstance(values.get("image"), PathLike): values["save_name"] = Path(values["image"]).stem return values @@ -176,6 +176,11 @@ def parse_time_range(cls, v: Any, values: dict) -> Any: """ Sets the default time range if undefined """ + # This skips the conversion if no image was provided, to ensure a more + # user-friendly error is provided, namely "image was missing" + if "image" not in values: + return v + default_start = 0 default_end = values["image"].sizes["T"] if v is None: @@ -190,6 +195,9 @@ def parse_channel_range(cls, v: Any, values: dict) -> Any: """ Sets the default channel range if undefined """ + if "image" not in values: + return v + default_start = 0 default_end = values["image"].sizes["C"] if v is None: From 867ca38e7eb69e2227bff0c386a2bea5d673176c Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Sun, 17 Sep 2023 01:31:02 +1000 Subject: [PATCH 040/147] invoke() test utility, forbid extra fields, ignore_keyerror() utility --- core/lls_core/cmds/__main__.py | 11 ++++- core/lls_core/models/__init__.py | 5 ++ core/lls_core/models/crop.py | 2 +- core/lls_core/models/deconvolution.py | 2 +- core/lls_core/models/deskew.py | 2 +- core/lls_core/models/lattice_data.py | 70 ++++++++++++++------------- core/lls_core/models/output.py | 2 +- core/lls_core/models/utils.py | 21 +++++++- core/lls_core/types.py | 5 +- core/tests/test_batch_deskew_args.py | 7 ++- core/tests/test_batch_deskew_yaml.py | 7 +-- core/tests/test_lattice_data.py | 59 ++++++++++++++++++++++ core/tests/utils.py | 6 +++ 13 files changed, 150 insertions(+), 49 deletions(-) create mode 100644 core/lls_core/models/__init__.py create mode 100644 core/tests/test_lattice_data.py create mode 100644 core/tests/utils.py diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 851f259..a08f303 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -18,7 +18,7 @@ from typer import Typer, Argument, Option from lls_core.models.output import SaveFileType -from toolz.dicttoolz import merge +from toolz.dicttoolz import merge, valfilter class CliDeskewDirection(StrEnum): X = auto() @@ -107,7 +107,14 @@ def main( from yaml import safe_load yaml_args = safe_load(fp) - LatticeData.parse_obj(merge(yaml_args, json_args, cli_args)).process().save_image() + + LatticeData.parse_obj( + # Merge all three sources of config: YAML, JSON and CLI + merge( + # Remove None values from all dictonaries, so that they merge appropriately + valfilter(lambda x: x is not None, it) for it in [yaml_args, json_args, cli_args] + ) + ).process().save_image() if __name__ == '__main__': app() diff --git a/core/lls_core/models/__init__.py b/core/lls_core/models/__init__.py new file mode 100644 index 0000000..3a10295 --- /dev/null +++ b/core/lls_core/models/__init__.py @@ -0,0 +1,5 @@ +from lls_core.models.crop import CropParams +from lls_core.models.deconvolution import DeconvolutionParams +from lls_core.models.deskew import DeskewParams +from lls_core.models.output import OutputParams +from lls_core.models.lattice_data import LatticeData diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index 3000181..0363ac4 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -4,7 +4,7 @@ from lls_core.models.utils import FieldAccessMixin -class CropParams(FieldAccessMixin, arbitrary_types_allowed=True): +class CropParams(FieldAccessMixin): """ Parameters for the optional cropping step """ diff --git a/core/lls_core/models/deconvolution.py b/core/lls_core/models/deconvolution.py index c2b360c..11f95e4 100644 --- a/core/lls_core/models/deconvolution.py +++ b/core/lls_core/models/deconvolution.py @@ -18,7 +18,7 @@ class MakeKwargs(TypedDict, total=False): psf_num_iter: int background: str -class DeconvolutionParams(FieldAccessMixin, arbitrary_types_allowed=True): +class DeconvolutionParams(FieldAccessMixin): """ Parameters for the optional deconvolution step """ diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 196e505..a9a78fe 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -40,7 +40,7 @@ def from_physical(cls, pixels: PhysicalPixelSizes) -> Self: ) -class DeskewParams(FieldAccessMixin, arbitrary_types_allowed=True): +class DeskewParams(FieldAccessMixin): image: DataArray = Field( description="A 3-5D array containing the image data" ) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index f855c9a..accda41 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -23,6 +23,7 @@ from lls_core.models.crop import CropParams from lls_core.models.deconvolution import DeconvolutionParams from lls_core.models.output import OutputParams, SaveFileType +from lls_core.models.utils import ignore_keyerror from lls_core.types import ArrayLike from lls_core.models.deskew import DeskewParams from napari_workflows import Workflow @@ -146,7 +147,7 @@ class CommonLatticeArgs(CommonDeskewArgs, CommonOutputArgs): crop: Optional[CropParams] workflow: Optional[Workflow] -class LatticeData(OutputParams, DeskewParams, arbitrary_types_allowed=True): +class LatticeData(OutputParams, DeskewParams): """ Holds data and metadata for a given image in a consistent format """ @@ -167,7 +168,8 @@ def default_save_name(cls, values: dict): # This needs to be a root validator to ensure it runs before the # reshaping validator. We can't override that either since it's # a field validator and can't modify save_name - if values.get("save_name", None) is None and isinstance(values.get("image"), PathLike): + from lls_core.models.utils import is_pathlike + if values.get("save_name", None) is None and is_pathlike(values.get("image")): values["save_name"] = Path(values["image"]).stem return values @@ -178,16 +180,14 @@ def parse_time_range(cls, v: Any, values: dict) -> Any: """ # This skips the conversion if no image was provided, to ensure a more # user-friendly error is provided, namely "image was missing" - if "image" not in values: - return v - - default_start = 0 - default_end = values["image"].sizes["T"] - if v is None: - return range(default_start, default_end) - elif isinstance(v, tuple) and len(v) == 2: - # Allow 2-tuples to be used as input for this field - return range(v[0] or default_start, v[1] or default_end) + with ignore_keyerror(): + default_start = 0 + default_end = values["image"].sizes["T"] + if v is None: + return range(default_start, default_end) + elif isinstance(v, tuple) and len(v) == 2: + # Allow 2-tuples to be used as input for this field + return range(v[0] or default_start, v[1] or default_end) return v @validator("channel_range", pre=True, always=True) @@ -195,16 +195,14 @@ def parse_channel_range(cls, v: Any, values: dict) -> Any: """ Sets the default channel range if undefined """ - if "image" not in values: - return v - - default_start = 0 - default_end = values["image"].sizes["C"] - if v is None: - return range(default_start, default_end) - elif isinstance(v, tuple) and len(v) == 2: - # Allow 2-tuples to be used as input for this field - return range(v[0] or default_start, v[1] or default_end) + with ignore_keyerror(): + default_start = 0 + default_end = values["image"].sizes["C"] + if v is None: + return range(default_start, default_end) + elif isinstance(v, tuple) and len(v) == 2: + # Allow 2-tuples to be used as input for this field + return range(v[0] or default_start, v[1] or default_end) return v @validator("time_range") @@ -212,11 +210,13 @@ def disjoint_time_range(cls, v: range, values: dict): """ Validates that the time range is within the range of channels in our array """ - max_time = values["image"].sizes["T"] - if v.start < 0: - raise ValueError("The lowest valid start value is 0") - if v.stop > max_time: - raise ValueError(f"The highest valid time value is the length of the time axis, which is {max_time}") + with ignore_keyerror(): + max_time = values["image"].sizes["T"] + if v.start < 0: + raise ValueError("The lowest valid start value is 0") + if v.stop > max_time: + raise ValueError(f"The highest valid time value is the length of the time axis, which is {max_time}") + return v @validator("channel_range") @@ -224,17 +224,19 @@ def disjoint_channel_range(cls, v: range, values: dict): """ Validates that the channel range is within the range of channels in our array """ - max_channel = values["image"].sizes["C"] - if v.start < 0: - raise ValueError("The lowest valid start value is 0") - if v.stop > max_channel: - raise ValueError(f"The highest valid channel value is the length of the channel axis, which is {max_channel}") + with ignore_keyerror(): + max_channel = values["image"].sizes["C"] + if v.start < 0: + raise ValueError("The lowest valid start value is 0") + if v.stop > max_channel: + raise ValueError(f"The highest valid channel value is the length of the channel axis, which is {max_channel}") return v @validator("channel_range") def channel_range_subset(cls, v: range, values: dict): - if min(v) < 0 or max(v) > values["image"].sizes["C"]: - raise ValueError("The output channel range must be a subset of the total available channels") + with ignore_keyerror(): + if min(v) < 0 or max(v) > values["image"].sizes["C"]: + raise ValueError("The output channel range must be a subset of the total available channels") return v @validator("time_range") diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 07b1b60..7520c32 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -10,7 +10,7 @@ class SaveFileType(StrEnum): h5 = "h5" tiff = "tiff" -class OutputParams(FieldAccessMixin, arbitrary_types_allowed=True): +class OutputParams(FieldAccessMixin): save_dir: DirectoryPath = Field( default = getcwd(), description="The directory where the output data will be saved" diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py index 949e795..c0c42f6 100644 --- a/core/lls_core/models/utils.py +++ b/core/lls_core/models/utils.py @@ -1,9 +1,13 @@ from typing import Any, Type from enum import Enum -from pydantic import BaseModel +from pydantic import BaseModel, Extra from typer import Option +from contextlib import contextmanager +def is_pathlike(x: Any) -> bool: + from os import PathLike + return isinstance(x, (str, bytes, PathLike)) def enum_choices(enum: Type[Enum]) -> str: """ @@ -11,10 +15,25 @@ def enum_choices(enum: Type[Enum]) -> str: """ return "{" + ", ".join([it.name for it in enum]) + "}" +@contextmanager +def ignore_keyerror(): + """ + Context manager that ignores KeyErrors from missing fields. + This allows for the validation to continue even if a single field + is missing, eventually resulting in a more user-friendly error message + """ + try: + yield + except KeyError: + pass + class FieldAccessMixin(BaseModel): """ Adds methods to a BaseModel for accessing useful field information """ + class Config: + extra = Extra.forbid + arbitrary_types_allowed = True @classmethod def get_default(cls, field_name: str) -> Any: diff --git a/core/lls_core/types.py b/core/lls_core/types.py index 26691bb..3e5d53b 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -19,8 +19,11 @@ def image_like_to_image(img: ImageLike) -> DataArray: """ Converts an image in one of many formats to a DataArray """ - if isinstance(img, PathLike): + # First try treating it as a path + try: img = AICSImage(fspath(img)) + except TypeError: + pass if isinstance(img, AICSImage): return img.xarray_dask_data else: diff --git a/core/tests/test_batch_deskew_args.py b/core/tests/test_batch_deskew_args.py index c3ced00..f9b066e 100644 --- a/core/tests/test_batch_deskew_args.py +++ b/core/tests/test_batch_deskew_args.py @@ -1,11 +1,11 @@ # Tests for napari_lattice using arguments and saving output files as h5, as well as tiff -from typer.testing import CliRunner from skimage.io import imsave import numpy as np from pathlib import Path import tempfile from lls_core.cmds.__main__ import app +from tests.utils import invoke def create_image(path: Path): # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) @@ -15,7 +15,6 @@ def create_image(path: Path): imsave(str(path), raw) assert path.exists() -runner = CliRunner() def test_batch_deskew_h5(): """Write image to disk and then execute napari_lattice from terminal @@ -26,7 +25,7 @@ def test_batch_deskew_h5(): input_file = out_dir / 'raw.tiff' create_image(input_file) # Batch deskew and save as h5 - runner.invoke(app, [ + invoke([ str(input_file), "--save-dir", str(out_dir), "--save-type", "h5" @@ -43,7 +42,7 @@ def test_batch_deskew_tiff(): out_dir = Path(out_dir) input_file = out_dir / 'raw.tiff' create_image(input_file) - runner.invoke(app, [ + invoke([ str(input_file), "--save-dir", str(out_dir), "--save-type", "tiff" diff --git a/core/tests/test_batch_deskew_yaml.py b/core/tests/test_batch_deskew_yaml.py index e793443..b702f09 100644 --- a/core/tests/test_batch_deskew_yaml.py +++ b/core/tests/test_batch_deskew_yaml.py @@ -5,7 +5,8 @@ import tempfile import numpy as np from pathlib import Path -from lls_core.cmds.__main__ import app as run_cli +from lls_core.cmds.__main__ import app +from tests.utils import invoke def write_config_file(config_settings: dict, output_file_location: Path): # Write config file for napari_lattice @@ -42,7 +43,7 @@ def create_data(dir: Path) -> Path: config: dict[str, str] = { "image": str(input_file), "save_dir": str(dir), - "output_file_type": "h5" + "save_type": "h5" } write_config_file(config, config_location) @@ -59,7 +60,7 @@ def test_yaml_deskew(): test_dir = Path(test_dir) config_location = create_data(test_dir) # Batch deskew and save as h5 - run_cli(["--yaml-config", str(config_location)]) + invoke(["--yaml-config", str(config_location)], ) # checks if h5 files written assert (test_dir / "raw.h5").exists() diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py new file mode 100644 index 0000000..298a327 --- /dev/null +++ b/core/tests/test_lattice_data.py @@ -0,0 +1,59 @@ +import pytest +from lls_core.models import LatticeData +from lls_core.models.output import SaveFileType +from lls_core.sample import resources +from importlib_resources import as_file +from importlib_resources.abc import Traversable +import tempfile +from pathlib import Path +from lls_core import DeconvolutionChoice +from itertools import product + +inputs = pytest.mark.parametrize( + ["path"], [ + ("RBC_tiny.czi", ), + ("RBC_lattice.tif", ), + ("LLS7_t1_ch1.czi", ), + ("LLS7_t1_ch3.czi", ), + ("LLS7_t2_ch1.czi", ), + ("LLS7_t2_ch3.czi", ), + ("multich_multi_time.tif", ), +]) + +def open_psf(name: str): + with as_file(resources / "psfs" / "zeiss_simulated" / name) as path: + return path + +parameterized = pytest.mark.parametrize("args", [ + {"skew": "X"}, + {"skew": "Y"}, + {"angle": 30}, + {"angle": 90}, + {"physical_pixel_sizes": (1, 1, 1)}, + {"rois": []}, + {"psf": [open_psf("488.czi")]}, + {"psf": [open_psf("640.tif")]}, + {"background": 1}, + {"background": "auto"}, + {"background": "second_last"}, + {"save_type": SaveFileType.h5}, + {"save_type": SaveFileType.tiff}, +]) + +@inputs +@parameterized +def test_process(path: str, args: dict): + with as_file(resources / path) as lattice_path: + args["image"] = lattice_path + for slice in LatticeData.parse_obj(args).process().slices: + assert slice.data.ndim == 3 + +@inputs +@parameterized +def test_save(path: str, args: dict): + with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: + args["image"] = lattice_path + args["save_dir"] = tempdir + LatticeData.parse_obj(args).process().save_image() + results = list(Path(tempdir).iterdir()) + assert len(results) > 0 diff --git a/core/tests/utils.py b/core/tests/utils.py new file mode 100644 index 0000000..ad68041 --- /dev/null +++ b/core/tests/utils.py @@ -0,0 +1,6 @@ +from typing import Sequence +from typer.testing import CliRunner +from lls_core.cmds.__main__ import app + +def invoke(args: Sequence[str]): + CliRunner().invoke(app, args, catch_exceptions=False) From 0d20f0dea2be463f8723249bf49a7abf5bc7802b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Sun, 17 Sep 2023 15:58:19 +1000 Subject: [PATCH 041/147] Add formal click depenedency --- core/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/pyproject.toml b/core/pyproject.toml index 5b20df1..d3d5282 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ # Earlier versions don't have Python 3.11 binaries, and the sdist # is misconfigured: https://github.com/AllenCellModeling/aicspylibczi/issues/90 "aicspylibczi>=3.1.1", + "click", "dask", "dask[distributed]", "fsspec>=2022.8.2", From bb5d3217e34e913d98c0b6b4c250235aa95279b7 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Sep 2023 10:38:19 +1000 Subject: [PATCH 042/147] Refactor CLI to use None default values, add JSON config help, throw error on CLI argument conflict, and add dump-schema subcommand --- core/lls_core/cmds/__main__.py | 110 +++++++++++++++++--------- core/lls_core/deconvolution.py | 9 +-- core/lls_core/models/crop.py | 16 +++- core/lls_core/models/deconvolution.py | 18 +++-- core/lls_core/models/deskew.py | 8 +- core/lls_core/models/lattice_data.py | 10 ++- core/lls_core/models/utils.py | 28 ++++++- core/lls_core/types.py | 7 +- core/lls_core/utils.py | 14 +++- core/tests/test_lattice_data.py | 49 +++++++----- plugin/napari_lattice/fields.py | 2 +- 11 files changed, 186 insertions(+), 85 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index a08f303..9079cce 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -3,69 +3,104 @@ # Run processing on command line instead of napari. # Example for deskewing files in a folder # python lattice_processing.py --input /home/pradeep/to_deskew --output /home/pradeep/output_save/ --processing deskew +from __future__ import annotations from enum import auto from pathlib import Path -from typing import List, Optional, Tuple -from typing_extensions import Annotated +from typing import TYPE_CHECKING, List, Optional, Tuple from strenum import StrEnum from lls_core.models.lattice_data import LatticeData from lls_core.models.deskew import DefinedPixelSizes, DeskewParams from lls_core.models.deconvolution import DeconvolutionParams from lls_core.models.output import OutputParams +from lls_core.models.crop import CropParams from lls_core import DeconvolutionChoice, DeskewDirection from typer import Typer, Argument, Option from lls_core.models.output import SaveFileType -from toolz.dicttoolz import merge, valfilter +from toolz.dicttoolz import merge_with, valfilter + +if TYPE_CHECKING: + from lls_core.models.utils import FieldAccessMixin + from typing import Type, Any class CliDeskewDirection(StrEnum): X = auto() Y = auto() -app = Typer(add_completion=False) +app = Typer(add_completion=False, rich_markup_mode="rich") + +def format_default(default: str): + """ + Given a string, formats it like a default value in Typer + """ + from typer.rich_utils import STYLE_OPTION_DEFAULT + default_style = STYLE_OPTION_DEFAULT + return f"[{default_style}]\\[default: {default}][/{default_style}]" + + +def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_description: str = "", description: Optional[str] = None) -> Any: + """ + Generates a type Field from a Pydantic model field + """ + field = model.__fields__[field_name] + + from enum import Enum + default = field.get_default() + if isinstance(default, Enum): + default = default.name + + if description is None: + description = f"{field.field_info.description} {extra_description}" + + return Option( + # We make all defaults None so they can be removed + default = None, + show_default=False, + help=f"{description}\n{format_default(default)}" + ) + +def handle_merge(values: list): + if len(values) > 0: + raise ValueError(f"A parameter has been passed multiple times! Got: {', '.join(values)}") + +@app.command("dump-schema") +def dump_schema(): + import json + import sys + json.dump(LatticeData.to_definition_dict(), fp=sys.stdout, indent=4) -@app.command() +@app.command("process") def main( - image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi"), - skew: CliDeskewDirection = Option( - default=DeskewParams.get_default("skew").name, - help=DeskewParams.get_description("skew") - ),# DeskewParams.make_typer_field("skew"), - angle: float = DeskewParams.make_typer_field("angle") , - pixel_sizes: Tuple[float, float, float] = Option( - ( - LatticeData.get_default("physical_pixel_sizes").X, - LatticeData.get_default("physical_pixel_sizes").Y, - LatticeData.get_default("physical_pixel_sizes").Z, - ), help=DeskewParams.get_description("physical_pixel_sizes") + ". This takes three arguments, corresponding to the X Y and Z pixel dimensions respectively" - ), - - rois: List[Path] = Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), + image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi", show_default=False), + skew: CliDeskewDirection = field_from_model(DeskewParams, "skew"),# DeskewParams.make_typer_field("skew"), + angle: float = field_from_model(DeskewParams, "angle") , + pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the X Y and Z pixel dimensions respectively"), + rois: List[Path] = field_from_model(CropParams, "roi_list", description="A list of paths pointing to regions of interest to crop to, in ImageJ format."), #Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), # Ideally this and other range values would be defined as Tuples, but these seem to be broken: https://github.com/tiangolo/typer/discussions/667 - z_start: Optional[int] = Option(None, help="The index of the first Z slice to use. All prior Z slices will be discarded."), - z_end: Optional[int] = Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded."), + z_start: Optional[int] = Option(None, help="The index of the first Z slice to use. All prior Z slices will be discarded. " + format_default("0"), show_default=False), + z_end: Optional[int] = Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded. Defaults to the last z index of the image.", show_default=False), enable_deconvolution: bool = Option(False, "--deconvolution/--disable-deconvolution"), decon_processing: DeconvolutionChoice = DeconvolutionParams.make_typer_field("decon_processing"), - psf: List[Path] = Option([], help="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array."), - psf_num_iter: int = DeconvolutionParams.make_typer_field("psf_num_iter"), - background: str = DeconvolutionParams.make_typer_field("background"), + psf: List[Path] = field_from_model(DeconvolutionParams, "psf", description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array."), + psf_num_iter: int = field_from_model(DeconvolutionParams, "psf_num_iter"), + background: str = field_from_model(DeconvolutionParams, "background"), - time_start: Optional[int] = Option(None, help="Index of the first time slice to use (inclusive)"), - time_end: Optional[int] = Option(None, help="Index of the first time slice to use (exclusive)"), + time_start: Optional[int] = Option(None, help="Index of the first time slice to use (inclusive). Defaults to the first time index of the image.", show_default=False), + time_end: Optional[int] = Option(None, help="Index of the first time slice to use (exclusive). Defaults to the last time index of the image.", show_default=False), - channel_start: Optional[int] = Option(None, help="Index of the first channel slice to use (inclusive)"), - channel_end: Optional[int] = Option(None, help="Index of the first channel slice to use (exclusive)"), + channel_start: Optional[int] = Option(None, help="Index of the first channel slice to use (inclusive). Defaults to the first channel index of the image.", show_default=False), + channel_end: Optional[int] = Option(None, help="Index of the first channel slice to use (exclusive). Defaults to the last channel index of the image.", show_default=False), - save_dir: Path = OutputParams.make_typer_field("save_dir"), - save_name: Optional[str] = OutputParams.make_typer_field("save_name"), - save_type: SaveFileType = OutputParams.make_typer_field("save_type"), + save_dir: Path = field_from_model(OutputParams, "save_dir"), + save_name: Optional[str] = field_from_model(OutputParams, "save_name"), + save_type: SaveFileType = field_from_model(OutputParams, "save_type"), - workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow."), - json_config: Optional[Path] = Option(None), - yaml_config: Optional[Path] = Option(None) + workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow.", show_default=False), + json_config: Optional[Path] = Option(None, show_default=False, help="Path to a JSON file from which parameters will be read."), + yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read.") ): cli_args = dict( image=image, @@ -110,9 +145,10 @@ def main( LatticeData.parse_obj( # Merge all three sources of config: YAML, JSON and CLI - merge( + merge_with( + handle_merge, # Remove None values from all dictonaries, so that they merge appropriately - valfilter(lambda x: x is not None, it) for it in [yaml_args, json_args, cli_args] + *[valfilter(lambda x: x is not None, it) for it in [yaml_args, json_args, cli_args]] ) ).process().save_image() diff --git a/core/lls_core/deconvolution.py b/core/lls_core/deconvolution.py index 3d225af..c3d7c87 100644 --- a/core/lls_core/deconvolution.py +++ b/core/lls_core/deconvolution.py @@ -16,9 +16,8 @@ import numpy as np import dask.array as da from dask.array.core import Array as DaskArray -from resource_backed_dask_array import ResourceBackedDaskArray -from lls_core.utils import pad_image_nearest_multiple +from lls_core.utils import array_to_dask, pad_image_nearest_multiple from lls_core.types import ArrayLike, is_arraylike if TYPE_CHECKING: @@ -227,11 +226,7 @@ def skimage_decon( from skimage.restoration import richardson_lucy as rl_decon_skimage depth = tuple(np.array(psf.shape) // 2) - if not isinstance(vol_zyx, ( - DaskArray, - ResourceBackedDaskArray, - )): - vol_zyx = da.asarray(vol_zyx) + vol_zyx = array_to_dask(vol_zyx) decon_data = vol_zyx.map_overlap( rl_decon_skimage, psf=psf, diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index 0363ac4..f6db276 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -1,5 +1,5 @@ -from typing import List, Tuple -from pydantic import Field, NonNegativeInt +from typing import List, Tuple, Any +from pydantic import Field, NonNegativeInt, validator from xarray import DataArray from lls_core.models.utils import FieldAccessMixin @@ -9,9 +9,19 @@ class CropParams(FieldAccessMixin): Parameters for the optional cropping step """ roi_list: List[DataArray] = Field( - description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex." + description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex.", + default = [] ) z_range: Tuple[NonNegativeInt, NonNegativeInt] = Field( default=None, description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." ) + + @validator("roi_list", each_item=True) + def read_roi(cls, v: Any): + from lls_core.types import is_pathlike + from lls_core.cropping import read_roi_array + # Converts the ROI from a path to an array + if is_pathlike(v): + return read_roi_array(v) + return v diff --git a/core/lls_core/models/deconvolution.py b/core/lls_core/models/deconvolution.py index 11f95e4..bd50f2b 100644 --- a/core/lls_core/models/deconvolution.py +++ b/core/lls_core/models/deconvolution.py @@ -12,12 +12,6 @@ from lls_core.types import image_like_to_image, ImageLike Background = Union[float, Literal["auto", "second_last"]] -class MakeKwargs(TypedDict, total=False): - decon_processing: Union[DeconvolutionChoice, str] - psf: List[ImageLike] - psf_num_iter: int - background: str - class DeconvolutionParams(FieldAccessMixin): """ Parameters for the optional deconvolution step @@ -45,4 +39,14 @@ def convert_decon(cls, v: Any): return DeconvolutionChoice[v] return v - convert_image = validator("psf", pre=True, each_item=True, allow_reuse=True)(image_like_to_image) + @validator("psf", pre=True, each_item=True, allow_reuse=True) + def convert_image(cls, v): + img = image_like_to_image(v) + # Ensure the PSF is 3D + if "C" in img.dims: + img = img.isel(C=0) + if "T" in img.dims: + img = img.isel(T=0) + if len(img.dims) != 3: + raise ValueError("PSF is not a 3D array!") + return img diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index a9a78fe..3d6c652 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -42,19 +42,19 @@ def from_physical(cls, pixels: PhysicalPixelSizes) -> Self: class DeskewParams(FieldAccessMixin): image: DataArray = Field( - description="A 3-5D array containing the image data" + description="A 3-5D array containing the image data." ) skew: DeskewDirection = Field( default=DeskewDirection.Y, - description=f"Axis along which to deskew the image. Choices: {enum_choices(DeskewDirection)}" + description=f"Axis along which to deskew the image. Choices: {enum_choices(DeskewDirection)}." ) angle: float = Field( default=30.0, - description="Angle of deskewing, in degrees" + description="Angle of deskewing, in degrees." ) physical_pixel_sizes: DefinedPixelSizes = Field( default_factory=DefinedPixelSizes, - description="Pixel size of the microscope, in microns" + description="Pixel size of the microscope, in microns." ) deskew_vol_shape: Tuple[int, ...] = Field( init_var=False, diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index accda41..dbe8862 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -125,7 +125,6 @@ def save_image(self): imagej=True ) - workflow: Optional[Workflow] = Field( default=None, description="If defined, this is a workflow to add lightsheet processing onto" @@ -245,6 +244,13 @@ def time_range_subset(cls, v: range, values: dict): raise ValueError("The output time range must be a subset of the total available time points") return v + @validator("deconvolution") + def check_psfs(cls, v: DeconvolutionParams, values: dict) -> DeconvolutionParams: + with ignore_keyerror(): + if len(v.psf) != values["image"].sizes["C"]: + raise ValueError("There should be one PSF per channel") + return v + # Hack to ensure that .skew_dir behaves identically to .skew @property def skew_dir(self) -> DeskewDirection: @@ -483,7 +489,7 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: else: data = skimage_decon( vol_zyx=data, - psf=self.deconvolution.psf[slice.channel], + psf=self.deconvolution.psf[slice.channel].to_numpy(), num_iter=self.deconvolution.psf_num_iter, clip=False, filter_epsilon=0, diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py index c0c42f6..b929a93 100644 --- a/core/lls_core/models/utils.py +++ b/core/lls_core/models/utils.py @@ -5,10 +5,6 @@ from typer import Option from contextlib import contextmanager -def is_pathlike(x: Any) -> bool: - from os import PathLike - return isinstance(x, (str, bytes, PathLike)) - def enum_choices(enum: Type[Enum]) -> str: """ Returns a human readable list of enum choices @@ -37,14 +33,38 @@ class Config: @classmethod def get_default(cls, field_name: str) -> Any: + """ + Shortcut method for returning the default value of a given field + """ return cls.__fields__[field_name].get_default() @classmethod def get_description(cls, field_name: str) -> str: + """ + Shortcut method for returning the description of a given field + """ return cls.__fields__[field_name].field_info.description + @classmethod + def to_definition_dict(cls) -> dict: + """ + Recursively converts the model into a dictionary whose keys are field names and whose + values are field descriptions. This is used to document the model to users + """ + ret = {} + for key, value in cls.__fields__.items(): + if isinstance(value.outer_type_, type) and issubclass(value.outer_type_, FieldAccessMixin): + value = value.outer_type_.to_definition_dict() + else: + value = value.field_info.description + ret[key] = value + return ret + @classmethod def make_typer_field(cls, field_name: str, extra_description: str = "") -> Any: + """ + Convert this field into a Typer field + """ field = cls.__fields__[field_name] return Option( default = field.get_default(), diff --git a/core/lls_core/types.py b/core/lls_core/types.py index 3e5d53b..5152719 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -7,7 +7,12 @@ from numpy.typing import NDArray from xarray import DataArray from aicsimageio import AICSImage -from os import PathLike, fspath +from os import fspath, PathLike as OriginalPathLike + +# This is a superset of os.PathLike +PathLike: TypeAlias = Union[str, bytes, OriginalPathLike] +def is_pathlike(x: Any) -> TypeGuard[PathLike]: + return isinstance(x, (str, bytes, OriginalPathLike)) ArrayLike: TypeAlias = Union[DaskArray, NDArray, OCLArray, DataArray] diff --git a/core/lls_core/utils.py b/core/lls_core/utils.py index e0025c9..36671c5 100644 --- a/core/lls_core/utils.py +++ b/core/lls_core/utils.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: from xml.etree.ElementTree import Element - + from dask.array.core import Array as DaskArray from napari.layers import Shapes # Enable Logging @@ -350,3 +350,15 @@ def raise_if_none(obj: Optional[T], message: str) -> T: if obj is None: raise TypeError(message) return obj + +def array_to_dask(arr: ArrayLike) -> DaskArray: + from dask.array.core import Array as DaskArray, from_array + from xarray import DataArray + from resource_backed_dask_array import ResourceBackedDaskArray + + if isinstance(arr, DataArray): + arr = arr.data + if isinstance(arr, (DaskArray, ResourceBackedDaskArray)): + return arr + else: + return from_array(arr) diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index 298a327..854ed2a 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -1,3 +1,4 @@ +from typing import Any import pytest from lls_core.models import LatticeData from lls_core.models.output import SaveFileType @@ -6,18 +7,16 @@ from importlib_resources.abc import Traversable import tempfile from pathlib import Path -from lls_core import DeconvolutionChoice -from itertools import product inputs = pytest.mark.parametrize( - ["path"], [ - ("RBC_tiny.czi", ), - ("RBC_lattice.tif", ), - ("LLS7_t1_ch1.czi", ), - ("LLS7_t1_ch3.czi", ), - ("LLS7_t2_ch1.czi", ), - ("LLS7_t2_ch3.czi", ), - ("multich_multi_time.tif", ), + ["path", "channels"], [ + ("RBC_tiny.czi", 1), + ("RBC_lattice.tif", 1), + ("LLS7_t1_ch1.czi", 1), + ("LLS7_t1_ch3.czi", 1), + ("LLS7_t2_ch1.czi", 1), + ("LLS7_t2_ch3.czi", 1), + ("multich_multi_time.tif", 3), ]) def open_psf(name: str): @@ -30,29 +29,43 @@ def open_psf(name: str): {"angle": 30}, {"angle": 90}, {"physical_pixel_sizes": (1, 1, 1)}, - {"rois": []}, - {"psf": [open_psf("488.czi")]}, - {"psf": [open_psf("640.tif")]}, - {"background": 1}, - {"background": "auto"}, - {"background": "second_last"}, {"save_type": SaveFileType.h5}, {"save_type": SaveFileType.tiff}, + + # Cropping enabled + {"crop": {"roi_list": []}}, ]) @inputs @parameterized -def test_process(path: str, args: dict): +def test_process(path: str, channels:int, args: dict): with as_file(resources / path) as lattice_path: args["image"] = lattice_path + if "deconvolution" in args: + args["deconvolution"]["psf"] = args["deconvolution"]["psf"] * channels for slice in LatticeData.parse_obj(args).process().slices: assert slice.data.ndim == 3 +@pytest.mark.parametrize(["background"], [(1, ), ("auto",), ("second_last",)]) +@parameterized +def test_process_deconvolution(args: dict, background: Any): + root = Path(__file__).parent / "data" + args["image"] = root / "raw.tif" + args["deconvolution"] = { + "psf": [root / "psf.tif"], + "background": background + } + for slice in LatticeData.parse_obj(args).process().slices: + assert slice.data.ndim == 3 + + @inputs @parameterized -def test_save(path: str, args: dict): +def test_save(path: str, channels: int, args: dict): with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: args["image"] = lattice_path + if "deconvolution" in args: + args["deconvolution"]["psf"] = args["deconvolution"]["psf"] * channels args["save_dir"] = tempdir LatticeData.parse_obj(args).process().save_image() results = list(Path(tempdir).iterdir()) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 726d5d3..d067a39 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -331,7 +331,7 @@ class DeconvolutionFields(NapariFieldGroup, FieldGroup): """ fields_enabled = field(False, label="Enabled") decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") - psf = field(List[Path], label = "PSFs") + psf: MagicField[ValueWidget[List[Path]]] = field(List[Path], label = "PSFs") psf_num_iter = field(int, label = "Number of Iterations") background = field(ComboBox).with_choices( [it.value for it in BackgroundSource] From 8a4836eeec549611c472ef93f4d1c4ac918bdf16 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Sep 2023 12:50:27 +1000 Subject: [PATCH 043/147] Fix GUI bugs, fix some CLI tests to use subcommands --- core/lls_core/cmds/__main__.py | 6 +++--- core/lls_core/models/lattice_data.py | 8 +++++--- core/tests/test_batch_deskew_args.py | 3 ++- plugin/napari_lattice/fields.py | 3 ++- plugin/napari_lattice/reader.py | 26 ++++++++++++++++++-------- 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 9079cce..3c53a4a 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -40,7 +40,7 @@ def format_default(default: str): return f"[{default_style}]\\[default: {default}][/{default_style}]" -def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_description: str = "", description: Optional[str] = None) -> Any: +def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_description: str = "", description: Optional[str] = None, default: Optional[Any] = None) -> Any: """ Generates a type Field from a Pydantic model field """ @@ -66,7 +66,7 @@ def handle_merge(values: list): raise ValueError(f"A parameter has been passed multiple times! Got: {', '.join(values)}") @app.command("dump-schema") -def dump_schema(): +def dump_schema() -> None: import json import sys json.dump(LatticeData.to_definition_dict(), fp=sys.stdout, indent=4) @@ -101,7 +101,7 @@ def main( workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow.", show_default=False), json_config: Optional[Path] = Option(None, show_default=False, help="Path to a JSON file from which parameters will be read."), yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read.") -): +) -> None: cli_args = dict( image=image, angle=angle, diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index dbe8862..6176fb8 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -167,7 +167,7 @@ def default_save_name(cls, values: dict): # This needs to be a root validator to ensure it runs before the # reshaping validator. We can't override that either since it's # a field validator and can't modify save_name - from lls_core.models.utils import is_pathlike + from lls_core.types import is_pathlike if values.get("save_name", None) is None and is_pathlike(values.get("image")): values["save_name"] = Path(values["image"]).stem return values @@ -245,7 +245,9 @@ def time_range_subset(cls, v: range, values: dict): return v @validator("deconvolution") - def check_psfs(cls, v: DeconvolutionParams, values: dict) -> DeconvolutionParams: + def check_psfs(cls, v: Optional[DeconvolutionParams], values: dict): + if v is None: + return v with ignore_keyerror(): if len(v.psf) != values["image"].sizes["C"]: raise ValueError("There should be one PSF per channel") @@ -478,7 +480,7 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: if self.deconvolution.decon_processing == DeconvolutionChoice.cuda_gpu: data = pycuda_decon( image=data, - psf=self.deconvolution.psf[slice.channel], + psf=self.deconvolution.psf[slice.channel].to_numpy(), background=self.deconvolution.background, dzdata=self.dz, dxdata=self.dx, diff --git a/core/tests/test_batch_deskew_args.py b/core/tests/test_batch_deskew_args.py index f9b066e..34a53b4 100644 --- a/core/tests/test_batch_deskew_args.py +++ b/core/tests/test_batch_deskew_args.py @@ -4,7 +4,6 @@ import numpy as np from pathlib import Path import tempfile -from lls_core.cmds.__main__ import app from tests.utils import invoke def create_image(path: Path): @@ -26,6 +25,7 @@ def test_batch_deskew_h5(): create_image(input_file) # Batch deskew and save as h5 invoke([ + "process", str(input_file), "--save-dir", str(out_dir), "--save-type", "h5" @@ -43,6 +43,7 @@ def test_batch_deskew_tiff(): input_file = out_dir / 'raw.tiff' create_image(input_file) invoke([ + "process", str(input_file), "--save-dir", str(out_dir), "--save-type", "tiff" diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index d067a39..491970f 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -331,7 +331,7 @@ class DeconvolutionFields(NapariFieldGroup, FieldGroup): """ fields_enabled = field(False, label="Enabled") decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") - psf: MagicField[ValueWidget[List[Path]]] = field(List[Path], label = "PSFs") + psf = field(List[Path], label = "PSFs") psf_num_iter = field(int, label = "Number of Iterations") background = field(ComboBox).with_choices( [it.value for it in BackgroundSource] @@ -373,6 +373,7 @@ def _make_model(self) -> Optional[DeconvolutionParams]: return DeconvolutionParams( decon_processing=self.decon_processing.value, background=background, + psf=self.psf.value, psf_num_iter=self.psf_num_iter.value ) diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index 134e034..3ffd04f 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -15,16 +15,21 @@ from napari.layers import Image from aicsimageio.aics_image import AICSImage -from typing import List, Optional, Tuple, Collection, TYPE_CHECKING +from typing import List, Optional, Tuple, Collection, TYPE_CHECKING, TypedDict from aicsimageio.types import PhysicalPixelSizes -from lls_core.models.lattice_data import AicsLatticeParams from lls_core.models.deskew import DefinedPixelSizes +from logging import getLogger +logger = getLogger(__name__) + if TYPE_CHECKING: from aicsimageio.types import ImageLike + from xarray import DataArray -class NapariImageParams(AicsLatticeParams): +class NapariImageParams(TypedDict): + data: DataArray + physical_pixel_sizes: DefinedPixelSizes save_name: str def lattice_params_from_napari( @@ -73,12 +78,17 @@ def lattice_params_from_napari( # Only process pixel sizes that are not none if all(img_data_aics.physical_pixel_sizes): pixel_sizes.add(img_data_aics.physical_pixel_sizes) - # if pixel_size_metadata is not None and pixel_sizes != img_data_aics.physical_pixel_sizes: - # raise Exception(f"Two or more layers that you have tried to merge have different pixel sizes according to their metadata! A previous image has size {physical_pixel_sizes}, whereas {img.name} has size {img_data_aics.physical_pixel_sizes}.") - # else: - # pixel_size_metadata = img_data_aics.physical_pixel_sizes - calculated_order = tuple(img_data_aics.dims.order) + metadata_order = list(img_data_aics.dims.order) + metadata_shape = list(img_data_aics.dims.shape) + while len(metadata_order) > len(img.data.shape): + logger.info(f"Image metadata implies there are more dimensions ({len(metadata_order)}) than the image actually has ({len(img.data.shape)})") + for i, size in enumerate(metadata_shape): + if size not in img.data.shape: + logger.info(f"Excluding the {metadata_order[i]} dimension to reconcile dimension order") + del metadata_order[i] + del metadata_shape[i] + calculated_order = metadata_order elif dimension_order is None: raise ValueError("Either the Napari image must have dimensional metadata, or a dimension order must be provided") else: From af36f86e26daca090256fb813892ceb77c7f6ed1 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Sep 2023 16:51:36 +1000 Subject: [PATCH 044/147] Fix cli tests, use click argument panels, clean up CLI code --- core/lls_core/cmds/__main__.py | 132 +++++++++++++-------------- core/tests/test_arg_parser.py | 3 +- core/tests/test_batch_deskew_yaml.py | 2 +- 3 files changed, 68 insertions(+), 69 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 3c53a4a..b33232a 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -7,19 +7,18 @@ from enum import auto from pathlib import Path -from typing import TYPE_CHECKING, List, Optional, Tuple +from typing import TYPE_CHECKING, Annotated, List, Optional, Tuple from strenum import StrEnum from lls_core.models.lattice_data import LatticeData -from lls_core.models.deskew import DefinedPixelSizes, DeskewParams +from lls_core.models.deskew import DeskewParams from lls_core.models.deconvolution import DeconvolutionParams from lls_core.models.output import OutputParams from lls_core.models.crop import CropParams -from lls_core import DeconvolutionChoice, DeskewDirection -from typer import Typer, Argument, Option +from lls_core import DeconvolutionChoice +from typer import Typer, Argument, Option, Context from lls_core.models.output import SaveFileType -from toolz.dicttoolz import merge_with, valfilter if TYPE_CHECKING: from lls_core.models.utils import FieldAccessMixin @@ -31,23 +30,15 @@ class CliDeskewDirection(StrEnum): app = Typer(add_completion=False, rich_markup_mode="rich") -def format_default(default: str): - """ - Given a string, formats it like a default value in Typer - """ - from typer.rich_utils import STYLE_OPTION_DEFAULT - default_style = STYLE_OPTION_DEFAULT - return f"[{default_style}]\\[default: {default}][/{default_style}]" - - -def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_description: str = "", description: Optional[str] = None, default: Optional[Any] = None) -> Any: +def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_description: str = "", description: Optional[str] = None, default: Optional[Any] = None, **kwargs) -> Any: """ Generates a type Field from a Pydantic model field """ field = model.__fields__[field_name] from enum import Enum - default = field.get_default() + if default is None: + default = field.get_default() if isinstance(default, Enum): default = default.name @@ -55,15 +46,15 @@ def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_descr description = f"{field.field_info.description} {extra_description}" return Option( - # We make all defaults None so they can be removed - default = None, - show_default=False, - help=f"{description}\n{format_default(default)}" + default = default, + help=description, + **kwargs ) def handle_merge(values: list): - if len(values) > 0: + if len(values) > 1: raise ValueError(f"A parameter has been passed multiple times! Got: {', '.join(values)}") + return values[0] @app.command("dump-schema") def dump_schema() -> None: @@ -72,63 +63,68 @@ def dump_schema() -> None: json.dump(LatticeData.to_definition_dict(), fp=sys.stdout, indent=4) @app.command("process") -def main( +def process( + ctx: Context, image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi", show_default=False), skew: CliDeskewDirection = field_from_model(DeskewParams, "skew"),# DeskewParams.make_typer_field("skew"), angle: float = field_from_model(DeskewParams, "angle") , - pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the X Y and Z pixel dimensions respectively"), + pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the X Y and Z pixel dimensions respectively", default=( + DeskewParams.get_default("physical_pixel_sizes").X, + DeskewParams.get_default("physical_pixel_sizes").Y, + DeskewParams.get_default("physical_pixel_sizes").Z + )), rois: List[Path] = field_from_model(CropParams, "roi_list", description="A list of paths pointing to regions of interest to crop to, in ImageJ format."), #Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), # Ideally this and other range values would be defined as Tuples, but these seem to be broken: https://github.com/tiangolo/typer/discussions/667 - z_start: Optional[int] = Option(None, help="The index of the first Z slice to use. All prior Z slices will be discarded. " + format_default("0"), show_default=False), + z_start: Optional[int] = Option(0, help="The index of the first Z slice to use. All prior Z slices will be discarded.", show_default=False), z_end: Optional[int] = Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded. Defaults to the last z index of the image.", show_default=False), - enable_deconvolution: bool = Option(False, "--deconvolution/--disable-deconvolution"), - decon_processing: DeconvolutionChoice = DeconvolutionParams.make_typer_field("decon_processing"), - psf: List[Path] = field_from_model(DeconvolutionParams, "psf", description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array."), - psf_num_iter: int = field_from_model(DeconvolutionParams, "psf_num_iter"), - background: str = field_from_model(DeconvolutionParams, "background"), + enable_deconvolution: bool = Option(False, "--deconvolution/--disable-deconvolution", rich_help_panel="Deconvolution"), + decon_processing: DeconvolutionChoice = field_from_model(DeconvolutionParams, "decon_processing", rich_help_panel="Deconvolution"), + psf: List[Path] = field_from_model(DeconvolutionParams, "psf", description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.", rich_help_panel="Deconvolution"), + psf_num_iter: int = field_from_model(DeconvolutionParams, "psf_num_iter", rich_help_panel="Deconvolution"), + background: str = field_from_model(DeconvolutionParams, "background", rich_help_panel="Deconvolution"), - time_start: Optional[int] = Option(None, help="Index of the first time slice to use (inclusive). Defaults to the first time index of the image.", show_default=False), - time_end: Optional[int] = Option(None, help="Index of the first time slice to use (exclusive). Defaults to the last time index of the image.", show_default=False), + time_start: Optional[int] = Option(0, help="Index of the first time slice to use (inclusive). Defaults to the first time index of the image.", rich_help_panel="Output"), + time_end: Optional[int] = Option(None, help="Index of the first time slice to use (exclusive). Defaults to the last time index of the image.", show_default=False, rich_help_panel="Output"), - channel_start: Optional[int] = Option(None, help="Index of the first channel slice to use (inclusive). Defaults to the first channel index of the image.", show_default=False), - channel_end: Optional[int] = Option(None, help="Index of the first channel slice to use (exclusive). Defaults to the last channel index of the image.", show_default=False), + channel_start: Optional[int] = Option(0, help="Index of the first channel slice to use (inclusive). Defaults to the first channel index of the image.", rich_help_panel="Output"), + channel_end: Optional[int] = Option(None, help="Index of the first channel slice to use (exclusive). Defaults to the last channel index of the image.", show_default=False, rich_help_panel="Output"), - save_dir: Path = field_from_model(OutputParams, "save_dir"), - save_name: Optional[str] = field_from_model(OutputParams, "save_name"), - save_type: SaveFileType = field_from_model(OutputParams, "save_type"), + save_dir: Path = field_from_model(OutputParams, "save_dir", rich_help_panel="Output"), + save_name: Optional[str] = field_from_model(OutputParams, "save_name", rich_help_panel="Output"), + save_type: SaveFileType = field_from_model(OutputParams, "save_type", rich_help_panel="Output"), workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow.", show_default=False), json_config: Optional[Path] = Option(None, show_default=False, help="Path to a JSON file from which parameters will be read."), - yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read.") + yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read."), ) -> None: - cli_args = dict( - image=image, - angle=angle, - skew=DeskewDirection[skew.name], - physical_pixel_sizes=DefinedPixelSizes( - X=pixel_sizes[0], - Y=pixel_sizes[1], - Z=pixel_sizes[2], - ), - crop=None if len(rois) == 0 else dict( - roi_list=rois, - z_range=(z_start, z_end) - ), - deconvolution=None if not enable_deconvolution else dict( - decon_processing = decon_processing, - psf = psf, - psf_num_iter = psf_num_iter, - background = background - ), - workflow=workflow, - - time_range=(time_start, time_end), - channel_range=(channel_start, channel_end), - save_dir=save_dir, - save_name=save_name, - save_type=save_type - ) + from toolz.dicttoolz import merge_with, update_in + cli_param_map = { + "image": ["image"], + "angle": ["angle"], + "skew": ["skew"], + "pixel_sizes": ["physical_pixel_sizes"], + "rois": ["crop", "roi_list"], + "z_start": ["crop", "z_range", 0], + "z_end": ["crop", "z_range", 1], + "decon_processing": ["deconvolution", "decon_processing"], + "psf": ["deconvolution", "psf"], + "psf_num_iter": ["deconvolution", "psf_num_iter"], + "background": ["deconvolution", "background"], + "workflow": ["workflow"], + "time_start": ["time_range", 0], + "time_end": ["time_range", 1], + "channel_start": ["channel_range", 0], + "channel_end": ["channel_range", 1], + "save_dir": ["save_dir"], + "save_name": ["save_name"], + "save_type": ["save_type"], + } + cli_args = {} + for source, dest in cli_param_map.items(): + from click.core import ParameterSource + if ctx.get_parameter_source(source) != ParameterSource.DEFAULT: + cli_args = update_in(cli_args, dest, lambda x: ctx.params[source]) json_args = {} if json_config is not None: @@ -147,10 +143,12 @@ def main( # Merge all three sources of config: YAML, JSON and CLI merge_with( handle_merge, - # Remove None values from all dictonaries, so that they merge appropriately - *[valfilter(lambda x: x is not None, it) for it in [yaml_args, json_args, cli_args]] + [yaml_args, json_args, cli_args] ) ).process().save_image() -if __name__ == '__main__': +def main(): app() + +if __name__ == '__main__': + main() diff --git a/core/tests/test_arg_parser.py b/core/tests/test_arg_parser.py index c33d211..da66c6a 100644 --- a/core/tests/test_arg_parser.py +++ b/core/tests/test_arg_parser.py @@ -5,10 +5,11 @@ def test_voxel_parsing(): # Tests that we can parse voxel lists correctly - command = get_command(app) + command = get_command(app).commands["process"] ctx = Context(command) parser = command.make_parser(ctx) args, _, _ = parser.parse_args(args=[ + "process", "input", "--save-name", "output", "--save-type", "tiff", diff --git a/core/tests/test_batch_deskew_yaml.py b/core/tests/test_batch_deskew_yaml.py index b702f09..ce2f423 100644 --- a/core/tests/test_batch_deskew_yaml.py +++ b/core/tests/test_batch_deskew_yaml.py @@ -60,7 +60,7 @@ def test_yaml_deskew(): test_dir = Path(test_dir) config_location = create_data(test_dir) # Batch deskew and save as h5 - invoke(["--yaml-config", str(config_location)], ) + invoke(["process", "--yaml-config", str(config_location)], ) # checks if h5 files written assert (test_dir / "raw.h5").exists() From fe0d7f6fa7ec564156897fbd692f4c5fb33a9ff9 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Sep 2023 17:52:35 +1000 Subject: [PATCH 045/147] Fix CLI tests --- core/lls_core/cmds/__main__.py | 2 +- core/lls_core/models/lattice_data.py | 6 ++++-- core/tests/test_lattice_data.py | 2 -- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index b33232a..87f466b 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -7,7 +7,7 @@ from enum import auto from pathlib import Path -from typing import TYPE_CHECKING, Annotated, List, Optional, Tuple +from typing import TYPE_CHECKING, List, Optional, Tuple from strenum import StrEnum from lls_core.models.lattice_data import LatticeData diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 6176fb8..1179a62 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -249,8 +249,10 @@ def check_psfs(cls, v: Optional[DeconvolutionParams], values: dict): if v is None: return v with ignore_keyerror(): - if len(v.psf) != values["image"].sizes["C"]: - raise ValueError("There should be one PSF per channel") + channels = values["image"].sizes["C"] + psfs = len(v.psf) + if psfs != channels: + raise ValueError(f"There should be one PSF per channel, but there are {psfs} PSFs and {channels} channels.") return v # Hack to ensure that .skew_dir behaves identically to .skew diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index 854ed2a..2e4ae55 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -64,8 +64,6 @@ def test_process_deconvolution(args: dict, background: Any): def test_save(path: str, channels: int, args: dict): with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: args["image"] = lattice_path - if "deconvolution" in args: - args["deconvolution"]["psf"] = args["deconvolution"]["psf"] * channels args["save_dir"] = tempdir LatticeData.parse_obj(args).process().save_image() results = list(Path(tempdir).iterdir()) From 8b248c8f669fc7cd949c7f79bd4e661f286b9f2e Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Sep 2023 18:35:34 +1000 Subject: [PATCH 046/147] Clean up LatticeData tests --- core/tests/test_lattice_data.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index 2e4ae55..aff4521 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -9,14 +9,14 @@ from pathlib import Path inputs = pytest.mark.parametrize( - ["path", "channels"], [ - ("RBC_tiny.czi", 1), - ("RBC_lattice.tif", 1), - ("LLS7_t1_ch1.czi", 1), - ("LLS7_t1_ch3.czi", 1), - ("LLS7_t2_ch1.czi", 1), - ("LLS7_t2_ch3.czi", 1), - ("multich_multi_time.tif", 3), + ["path"], [ + ("RBC_tiny.czi", ), + ("RBC_lattice.tif", ), + ("LLS7_t1_ch1.czi", ), + ("LLS7_t1_ch3.czi", ), + ("LLS7_t2_ch1.czi", ), + ("LLS7_t2_ch3.czi", ), + ("multich_multi_time.tif", ), ]) def open_psf(name: str): @@ -38,11 +38,9 @@ def open_psf(name: str): @inputs @parameterized -def test_process(path: str, channels:int, args: dict): +def test_process(path: str, args: dict): with as_file(resources / path) as lattice_path: args["image"] = lattice_path - if "deconvolution" in args: - args["deconvolution"]["psf"] = args["deconvolution"]["psf"] * channels for slice in LatticeData.parse_obj(args).process().slices: assert slice.data.ndim == 3 @@ -61,7 +59,7 @@ def test_process_deconvolution(args: dict, background: Any): @inputs @parameterized -def test_save(path: str, channels: int, args: dict): +def test_save(path: str, args: dict): with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: args["image"] = lattice_path args["save_dir"] = tempdir From b93be4f7ff9b136b542e0ba37591175b39e433fc Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 21 Sep 2023 13:34:49 +1000 Subject: [PATCH 047/147] Implement pixel data source selection --- plugin/napari_lattice/_dock_widget.py | 1153 ------------------------- plugin/napari_lattice/_reader.py | 161 ---- plugin/napari_lattice/fields.py | 31 +- plugin/napari_lattice/reader.py | 24 +- plugin/pyproject.toml | 2 +- 5 files changed, 35 insertions(+), 1336 deletions(-) delete mode 100644 plugin/napari_lattice/_dock_widget.py delete mode 100644 plugin/napari_lattice/_reader.py diff --git a/plugin/napari_lattice/_dock_widget.py b/plugin/napari_lattice/_dock_widget.py deleted file mode 100644 index 4bb7811..0000000 --- a/plugin/napari_lattice/_dock_widget.py +++ /dev/null @@ -1,1153 +0,0 @@ -import os -import sys -import yaml -import numpy as np -from pathlib import Path -import dask.array as da -import pandas as pd -from typing import Union, Optional, Callable, Literal -from enum import Enum - -from magicclass.wrappers import set_design -from magicgui import magicgui -from magicclass import magicclass, field, vfield, set_options, MagicTemplate -from magicclass.utils import click -from qtpy.QtCore import Qt - -from napari.layers import Layer, Shapes -from napari.types import ImageData -from napari.utils import history - -import pyclesperanto_prototype as cle - -from napari.types import ImageData, ShapesData - -from tqdm import tqdm - -from napari_workflows import Workflow, WorkflowManager -from napari_workflows._io_yaml_v1 import load_workflow - -from lls_core import config, DeskewDirection, DeconvolutionChoice, SaveFileType, Log_Levels -from lls_core.io import LatticeData, save_img, save_img_workflow -from lls_core.utils import read_imagej_roi, get_first_last_image_and_task, modify_workflow_task, get_all_py_files, as_type, process_custom_workflow_output, check_dimensions, load_custom_py_modules -from lls_core.llsz_core import crop_volume_deskew -from lls_core.deconvolution import read_psf, pycuda_decon, skimage_decon - -from napari_lattice.ui_core import _Preview, _Deskew_Save -from napari_lattice._reader import lattice_from_napari - -# Enable Logging -import logging -logger = logging.getLogger(__name__) -logger.setLevel(logging.INFO) - -class LastDimensionOptions(Enum): - channel = "Channel" - time = "Time" - get_from_metadata = "Get from Metadata" - -@magicclass(widget_type="split") -class LLSZWidget(MagicTemplate): - - @magicclass(widget_type="split") - class LlszMenu(MagicTemplate): - open_file: bool = False - lattice: LatticeData = None - skew_dir: DeskewDirection - angle_value: float - deskew_func: Callable - - main_heading = field("

      Napari Lattice: Visualization & Analysis

      ", widget_type="Label") - heading1 = field("Drag and drop an image file onto napari.\nOnce image has opened, initialize the\nplugin by clicking the button below.\nEnsure the image layer and voxel sizes are accurate in the prompt.\n If everything initalises properly, the button turns green.", widget_type="Label") - - @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Initialize Plugin", max_height=75, font_size=13) - @set_options(pixel_size_dx={"widget_type": "FloatSpinBox", "value": 0.1449922, "step": 0.000000001}, - pixel_size_dy={"widget_type": "FloatSpinBox", - "value": 0.1449922, "step": 0.000000001}, - pixel_size_dz={"widget_type": "FloatSpinBox", - "value": 0.3, "step": 0.000000001}, - angle={"widget_type": "FloatSpinBox", - "value": 30, "step": 0.1}, - select_device={"widget_type": "ComboBox", "choices": cle.available_device_names( - ), "value": cle.available_device_names()[0]}, - last_dimension_channel={"widget_type": "ComboBox", - "label": "Set Last dimension (channel/time)", "tooltip": "If the last dimension is initialised incorrectly, you can assign it as either channel/time"}, - merge_all_channel_layers={"widget_type": "CheckBox", "value": True, "label": "Merge all napari layers as channels", - "tooltip": "Use this option if the channels are in separate layers. napari-lattice requires all channels to be in same layer"}, - skew_dir={"widget_type": "ComboBox", "choices": DeskewDirection, "value": DeskewDirection.Y, - "label": "Direction of skew (Y or X)", "tooltip": "Skew direction when image is acquired. Ask your microscopist for details"}, - set_logging={"widget_type": "ComboBox", "choices": Log_Levels, "value": Log_Levels.INFO, - "label": "Log Level", "tooltip": "Only use for debugging. Leave it as INFO for regular operation"} - ) - def Choose_Image_Layer(self, - img_layer: Layer, - pixel_size_dx: float = 0.1449922, - pixel_size_dy: float = 0.1449922, - pixel_size_dz: float = 0.3, - angle: float = 30, - select_device: str = cle.available_device_names()[ - 0], - last_dimension_channel: LastDimensionOptions = LastDimensionOptions.get_from_metadata, - merge_all_channel_layers: bool = False, - skew_dir: DeskewDirection=DeskewDirection.Y, - set_logging: Log_Levels=Log_Levels.INFO): - - logger.setLevel(set_logging.value) - config.log_level = set_logging.value - logger.info(f"Logging set to {set_logging}") - logger.info("Using existing image layer") - - if self.parent_viewer is None: - raise Exception("This function can only be used when inside of a Napari viewer") - - # Select device for processing - cle.select_device(select_device) - - #assert skew_dir in DeskewDirection, "Skew direction not recognised. Enter either Y or X" - LLSZWidget.LlszMenu.skew_dir = skew_dir - LLSZWidget.LlszMenu.angle_value = angle - - if LLSZWidget.LlszMenu.skew_dir == DeskewDirection.Y: - LLSZWidget.LlszMenu.deskew_func = cle.deskew_y - #LLSZWidget.LlszMenu.skew_dir = DeskewDirection.Y - elif LLSZWidget.LlszMenu.skew_dir == DeskewDirection.X: - LLSZWidget.LlszMenu.deskew_func = cle.deskew_x - #LLSZWidget.LlszMenu.skew_dir = DeskewDirection.X - - # merge all napari image layers as one multidimensional image - if merge_all_channel_layers: - from napari.layers.utils.stack_utils import images_to_stack - # get list of napari layers as a list - layer_list = list(self.parent_viewer.layers) - # if more than one layer - if len(layer_list) > 1: - # convert the list of images into a stack - new_layer = images_to_stack(layer_list) - # select all current layers - self.parent_viewer.layers.select_all() - # remove selected layers - self.parent_viewer.layers.remove_selected() - # add the new composite image layer - self.parent_viewer.add_layer(new_layer) - img_layer = new_layer - - LLSZWidget.LlszMenu.lattice = lattice_from_napari( - img=img_layer, - last_dimension=None if last_dimension_channel == LastDimensionOptions.get_from_metadata else last_dimension_channel, - angle=angle, - skew=LLSZWidget.LlszMenu.skew_dir, - physical_pixel_sizes=(pixel_size_dx, pixel_size_dy, pixel_size_dz) - ) - #LLSZWidget.LlszMenu.aics = LLSZWidget.LlszMenu.lattice.data - - # LLSZWidget.LlszMenu.dask = False # Use GPU by default - - # We initialise these variables here, but they can be changed in the deconvolution section - # list to store psf images for each channel - LLSZWidget.LlszMenu.lattice.psf = [] - LLSZWidget.LlszMenu.lattice.psf_num_iter = 10 - LLSZWidget.LlszMenu.lattice.decon_processing = DeconvolutionChoice.cpu - # list to store otf paths for each channel (Deprecated) - LLSZWidget.LlszMenu.lattice.otf_path = [] - # if not using GPU - #LLSZWidget.LlszMenu.dask = not use_GPU - - # flag for ensuring a file has been opened and plugin initialised - LLSZWidget.LlszMenu.open_file = True - - logger.info( - f"Pixel size (ZYX) in microns: {LLSZWidget.LlszMenu.lattice.dz,LLSZWidget.LlszMenu.lattice.dy,LLSZWidget.LlszMenu.lattice.dx}") - logger.info( - f"Dimensions of image layer (ZYX): {list(LLSZWidget.LlszMenu.lattice.data.shape[-3:])}") - logger.info( - f"Dimensions of deskewed image (ZYX): {LLSZWidget.LlszMenu.lattice.deskew_vol_shape}") - logger.info( - f"Deskewing angle is :{LLSZWidget.LlszMenu.lattice.angle}") - logger.info( - f"Deskew Direction :{LLSZWidget.LlszMenu.lattice.skew}") - # Add dimension labels correctly - # if channel, and not time - if LLSZWidget.LlszMenu.lattice.time == 0 and (last_dimension_channel or LLSZWidget.LlszMenu.lattice.channels > 0): - self.parent_viewer.dims.axis_labels = ('Channel', "Z", "Y", "X") - # if no channel, but has time - elif LLSZWidget.LlszMenu.lattice.channels == 0 and LLSZWidget.LlszMenu.lattice.time > 0: - self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") - # if it has channels - elif LLSZWidget.LlszMenu.lattice.channels > 1: - # If merge to stack is used, channel slider goes to the bottom - if int(self.parent_viewer.dims.dict()["range"][0][1]) == LLSZWidget.LlszMenu.lattice.channels: - self.parent_viewer.dims.axis_labels = ('Channel', "Time", "Z", "Y", "X") - else: - self.parent_viewer.dims.axis_labels = ('Time', "Channel", "Z", "Y", "X") - # if channels initialized by aicsimagio, then channels is 1 - elif LLSZWidget.LlszMenu.lattice.channels == 1 and LLSZWidget.LlszMenu.lattice.time > 1: - self.parent_viewer.dims.axis_labels = ('Time', "Z", "Y", "X") - - logger.info(f"Initialised") - self["Choose_Image_Layer"].background_color = "green" - self["Choose_Image_Layer"].text = "Plugin Initialised" - - return - - # Pycudadecon library for deconvolution - # options={"enabled": True}, - deconvolution = vfield(bool, name="Use Deconvolution") - deconvolution.value = False - - @deconvolution.connect - def _set_decon(self): - if self.deconvolution: - logger.info("Deconvolution Activated") - LLSZWidget.LlszMenu.deconvolution.value = True - else: - logger.info("Deconvolution Disabled") - LLSZWidget.LlszMenu.deconvolution.value = False - return - - @set_design(background_color="magenta", font_family="Consolas", visible=True, text="Click to select PSFs for deconvolution", max_height=75, font_size=11) - @set_options(header=dict(widget_type="Label", label="

      Enter path to the PSF images

      "), - psf_ch1_path={"widget_type": "FileEdit", - "label": "Channel 1:"}, - psf_ch2_path={"widget_type": "FileEdit", - "label": "Channel 2"}, - psf_ch3_path={"widget_type": "FileEdit", - "label": "Channel 3"}, - psf_ch4_path={"widget_type": "FileEdit", - "label": "Channel 4"}, - device_option={ - "widget_type": "ComboBox", "label": "Choose processing device", "choices": DeconvolutionChoice}, - no_iter={ - "widget_type": "SpinBox", "label": "No of iterations (Deconvolution)", "value": 10, "min": 1, "max": 50, "step": 1} - ) - def deconvolution_gui(self, - header, - psf_ch1_path: Path, - psf_ch2_path: Path, - psf_ch3_path: Path, - psf_ch4_path: Path, - device_option, - no_iter: int): - """GUI for Deconvolution button""" - LLSZWidget.LlszMenu.lattice.decon_processing = device_option - assert LLSZWidget.LlszMenu.deconvolution.value == True, "Deconvolution is set to False. Tick the box to activate deconvolution." - LLSZWidget.LlszMenu.lattice.psf = list(read_psf([ - psf_ch1_path, - psf_ch2_path, - psf_ch3_path, - psf_ch4_path, - ], - device_option, - lattice_class=LLSZWidget.LlszMenu.lattice - )) - LLSZWidget.LlszMenu.lattice.psf_num_iter = no_iter - self["deconvolution_gui"].background_color = "green" - self["deconvolution_gui"].text = "PSFs added" - - @magicclass(widget_type="collapsible") - class Preview: - @magicgui(header=dict(widget_type="Label", label="

      Preview Deskew

      "), - time=dict(label="Time:", max=2**15), - channel=dict(label="Channel:"), - call_button="Preview") - def Preview_Deskew(self, - header, - time: int, - channel: int, - img_data: ImageData): - """ - Preview deskewed data for a single timepoint and channel - - """ - _Preview(LLSZWidget, - self, - time, - channel, - img_data) - return - - # Tabbed Widget container to house all the widgets - @magicclass(widget_type="tabbed", name="Functions") - class WidgetContainer(MagicTemplate): - - @magicclass(name="Deskew", widget_type="scrollable", properties={"min_width": 100}) - class DeskewWidget(MagicTemplate): - - @magicgui(header=dict(widget_type="Label", label="

      Deskew and Save

      "), - time_start=dict(label="Time Start:", max=2**20), - time_end=dict(label="Time End:", value=1, max=2**20), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType, "value": SaveFileType.h5}, - save_path=dict(mode='d', label="Directory to save"), - call_button="Save") - def Deskew_Save(self, - header, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - save_as_type: str, - save_path: Path = Path(history.get_save_history()[0])): - """ Widget to Deskew and Save Data""" - _Deskew_Save(LLSZWidget, - time_start, - time_end, - ch_start, - ch_end, - save_as_type, - save_path) - return - - @magicclass(name="Crop and Deskew", widget_type="scrollable") - class CropWidget(MagicTemplate): - - # add function for previewing cropped image - @magicclass(name="Cropping Preview", widget_type="scrollable", properties={ - "min_width": 100, - "shapes_layer": Shapes - }) - class Preview_Crop_Menu(MagicTemplate): - shapes_layer: Shapes - - @set_design(font_size=10, text="Click to activate Cropping Layer", background_color="magenta") - @click(enables=["Import_ImageJ_ROI", "Crop_Preview"]) - def activate_cropping(self): - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer = self.parent_viewer.add_shapes(shape_type='polygon', edge_width=1, edge_color='white', - face_color=[1, 1, 1, 0], name="Cropping BBOX layer") - # TO select ROIs if needed - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.mode = "SELECT" - self["activate_cropping"].text = "Cropping layer active" - self["activate_cropping"].background_color = "green" - return - - heading2 = field("You can either import ImageJ ROI (.zip) files or manually define ROIs using the shape layer", widget_type="Label") - - @click(enabled=False) - def Import_ImageJ_ROI(self, path: Path = Path(history.get_open_history()[0])): - logger.info(f"Opening{path}") - roi_list = read_imagej_roi(path) - # convert to canvas coordinates - roi_list = (np.array(roi_list) * - LLSZWidget.LlszMenu.lattice.dy).tolist() - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.add(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', - face_color=[1, 1, 1, 0]) - return - - time_crop = field( - int, options={"min": 0, "step": 1, "max": 2**20}, name="Time") - chan_crop = field( - int, options={"min": 0, "step": 1}, name="Channels") - heading_roi = field("If there are multiple ROIs, select the ROI before clicking button below", widget_type="Label") - #roi_idx = field(int, options={"min": 0, "step": 1}, name="ROI number") - - @click(enabled=False) - # -> LayerDataTuple: - def Crop_Preview(self, roi_layer: ShapesData): - assert roi_layer, "No coordinates found for cropping. Check if right shapes layer or initialise shapes layer and draw ROIs." - # TODO: Add assertion to check if bbox layer or coordinates - time = self.time_crop.value - channel = self.chan_crop.value - - assert time < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" - assert channel < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" - - logger.info(f"Using channel {channel} and time {time}") - - vol = LLSZWidget.LlszMenu.lattice.data - vol_zyx = vol[time, channel, ...] - vol_zyx = np.array(vol_zyx) - - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape - # Create a dask array same shape as deskewed image - deskewed_volume = da.zeros(deskewed_shape) - - # Option for entering custom z start value? - z_start = 0 - z_end = deskewed_shape[0] - - # if only one roi drawn, use the first ROI for cropping - if len(roi_layer) == 1: - roi_idx = 0 - else: - assert len( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data) > 0, "Please select an ROI" - roi_idx = list( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data)[0] - - roi_choice = roi_layer[roi_idx] - # As the original image is scaled, the coordinates are in microns, so we need to convert - # roi from micron to canvas/world coordinates - roi_choice = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_choice] - logger.info(f"Previewing ROI {roi_idx}") - - # crop - if LLSZWidget.LlszMenu.deconvolution.value: - logger.info( - f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") - #psf = LLSZWidget.LlszMenu.lattice.psf[channel] - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter).astype(vol_zyx.dtype) - else: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter).astype(vol_zyx.dtype) - else: - crop_roi_vol_desk = crop_volume_deskew(original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - skew_dir=LLSZWidget.LlszMenu.skew_dir).astype(vol_zyx.dtype) - crop_roi_vol_desk = cle.pull(crop_roi_vol_desk) - - # get array back from gpu or addding cle array to napari can throw errors - - scale = (LLSZWidget.LlszMenu.lattice.new_dz, - LLSZWidget.LlszMenu.lattice.dy, - LLSZWidget.LlszMenu.lattice.dx) - self.parent_viewer.add_image( - crop_roi_vol_desk, scale=scale) - - @magicclass(name="Crop and Save Data") - class CropSaveData(MagicTemplate): - @magicgui(header=dict(widget_type="Label", label="

      Crop and Save Data

      "), - time_start=dict(label="Time Start:"), - time_end=dict(label="Time End:", value=1), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType}, - save_path=dict(mode='d', label="Directory to save ")) - def Crop_Save(self, - header, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - save_as_type: str, - roi_layer_list: ShapesData, - save_path: Path = Path(history.get_save_history()[0])): - - if not roi_layer_list: - logger.error( - "No coordinates found or cropping. Initialise shapes layer and draw ROIs.") - else: - assert LLSZWidget.LlszMenu.open_file, "Image not initialised" - - check_dimensions(time_start, time_end, ch_start, ch_end, - LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) - - angle = LLSZWidget.LlszMenu.lattice.angle - dx = LLSZWidget.LlszMenu.lattice.dx - dy = LLSZWidget.LlszMenu.lattice.dy - dz = LLSZWidget.LlszMenu.lattice.dz - - # get image data - img_data = LLSZWidget.LlszMenu.lattice.data - # Get shape of deskewed image - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - - logger.info("Cropping and saving files...") - - # necessary when scale is used for napari.viewer.add_image operations - roi_layer_list = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_layer_list] - - for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): - # pass arguments for save tiff, callable and function arguments - logger.info("Processing ROI ", idx) - # pass parameters for the crop_volume_deskew function - - save_img(vol=img_data, - func=crop_volume_deskew, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_name_prefix="ROI_" + - str(idx), - save_path=save_path, - save_file_type=save_as_type, - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deskewed_volume=deskewed_volume, - roi_shape=roi_layer, - angle_in_degrees=angle, - z_start=z_start, - z_end=z_end, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - LLSZWidget=LLSZWidget - ) - - logger.info( - f"Cropping and Saving Complete -> {save_path}") - return - - @magicclass(name="Workflow", widget_type="scrollable") - class WorkflowWidget: - @magicclass(name="Preview Workflow", widget_type="scrollable") - class PreviewWorkflow: - #time_preview= field(int, options={"min": 0, "step": 1}, name="Time") - #chan_preview = field(int, options={"min": 0, "step": 1}, name="Channels") - @magicgui(header=dict(widget_type="Label", label="

      Preview Workflow

      "), - time_preview=dict(label="Time:", max=2**20), - chan_preview=dict(label="Channel:"), - get_active_workflow=dict( - widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), - workflow_path=dict( - mode='r', label="Load custom workflow (.yaml/yml)"), - Use_Cropping=dict( - widget_type="Checkbox", label="Crop Data", value=False), - #custom_module=dict(widget_type="Checkbox",label="Load custom module (looks for *.py files in the workflow directory)",value = False), - call_button="Apply and Preview Workflow") - def Workflow_Preview(self, - header, - time_preview: int, - chan_preview: int, - get_active_workflow: bool, - Use_Cropping: bool, - roi_layer_list: ShapesData, - workflow_path: Path = Path.home()): - """ - Apply napari_workflows to the processing pipeline - User can define a pipeline which can be inspected in napari workflow inspector - and then execute it by ticking the get active workflow checkbox, - OR - Use a predefined workflow - - In both cases, if deskewing is not present as first step, it will be added on - and rest of the task will be made followers - Args: - - """ - print("Previewing deskewed channel and time with workflow") - if get_active_workflow: - # installs the workflow to napari - user_workflow = WorkflowManager.install( - self.parent_viewer).workflow - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - logger.info("Workflow loaded from napari") - else: - - try: - # Automatically scan workflow file directory for *.py files. - # If it findss one, load it as a module - - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - sys.path.append(parent_dir) - custom_py_files = get_all_py_files(parent_dir) - if len(custom_py_files) == 0: - logger.error( - f"No custom modules imported. If you'd like to use a cusotm module, place a *.py file in same folder as the workflow file {parent_dir}") - else: - modules = load_custom_py_modules( - custom_py_files) - - logger.info( - f"Custom modules imported {modules}") - user_workflow = load_workflow( - workflow_path.__str__()) - except yaml.loader.ConstructorError as e: - logger.error( - "\033[91m While loading workflow, got the following error which may mean you need to install the corresponding module in your Python environment: \033[0m") - logger.error(e) - - #user_workflow = load_workflow(workflow_path) - logger.info("Workflow loaded from file") - - assert type( - user_workflow) is Workflow, "Workflow loading error. Check if file is workflow or if required libraries are installed" - - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - #print(input_arg_first, input_arg_last, first_task_name,last_task_name ) - # get list of tasks - task_list = list(user_workflow._tasks.keys()) - logger.info(f"Workflow loaded:{user_workflow}") - # logger.info() - - # when using fields, self.time_preview.value - assert time_preview < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" - assert chan_preview < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" - - time = time_preview - channel = chan_preview - - # to access current time and channel and pass it to workflow file - config.channel = channel - config.time = time - - logger.info( - f"Processing for Time: {time} and Channel: {channel}") - - vol = LLSZWidget.LlszMenu.lattice.data - vol_zyx = vol[time, channel, ...] - vol_zyx = np.array(vol_zyx) - - task_name_start = first_task_name[0] - try: - task_name_last = last_task_name[0] - except IndexError: - task_name_last = task_name_start - - # variables to hold task name, initialize it as None - # if gpu, set otf_path, otherwise use psf - psf = None - otf_path = None - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - otf_path = "otf_path" - else: - psf = "psf" - - # if cropping, set that as first task - # get the function associated with the first task and check if its deskewing - if Use_Cropping: - # use deskewed volume for cropping function - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - if user_workflow.get_task(task_name_start)[0] not in [crop_volume_deskew]: - # if only one roi drawn, use the first ROI for cropping - if len(roi_layer_list) == 1: - roi_idx = 0 - else: # else get the user selection - assert len( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data) > 0, "Please select an ROI" - roi_idx = list( - LLSZWidget.WidgetContainer.CropWidget.Preview_Crop_Menu.shapes_layer.selected_data)[0] - - roi_choice = roi_layer_list[roi_idx] - # As the original image is scaled, the coordinates are in microns, so we need to convert - # roi to from micron to canvas/world coordinates - roi_choice = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_choice] - logger.info(f"Previewing ROI {roi_idx}") - if LLSZWidget.LlszMenu.deconvolution.value: - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - otf_path=otf_path, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - skew_dir=LLSZWidget.LlszMenu.skew_dir) - else: - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=vol_zyx, - deskewed_volume=deskewed_volume, - roi_shape=roi_choice, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - z_start=z_start, - z_end=z_end, - skew_dir=LLSZWidget.LlszMenu.skew_dir) - - # Set input of the workflow to be crop_deskewing, i.e., the original first operation will now have crop_deskew_image as an input (becoming second instead) - user_workflow.set( - input_arg_first, "crop_deskew_image") - else: - user_workflow.set(input_arg_first, vol_zyx) - # Not cropping; If deskew not in workflow, append to start - elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = LLSZWidget.LlszMenu.lattice.psf[channel] - input_arg_first_decon, input_arg_last_decon, first_task_name_decon, last_task_name_decon = get_first_last_image_and_task( - user_workflow) - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) - # user_workflow.set(input_arg_first_decon,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # user_workflow.set(input_arg_first_decon,"deconvolution") - - user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, - "deconvolution", - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - linear_interpolation=True) - - # user_workflow.set("change_bitdepth",as_type,"deskew_image",vol_zyx) - # Set input of the workflow to be from deskewing output with same bit depth as original volume - # user_workflow.set(input_arg_first,"change_bitdepth") - - else: - user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, - vol_zyx, - angle_in_degrees=LLSZWidget.LlszMenu.lattice.angle, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - linear_interpolation=True) - # Set input of the workflow to be from deskewing - # user_workflow.set(input_arg_first,"deskew_image") - - user_workflow.set( - "change_bitdepth", as_type, "deskew_image", vol_zyx) - # Set input of the workflow to be from deskewing with same bit depth as original volume - user_workflow.set( - input_arg_first, "change_bitdepth") - - else: - # if deskew already in workflow, just check if deconvolution needs to be added - # repitition of above (maybe create a function?) - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = LLSZWidget.LlszMenu.lattice.psf[channel] - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=vol_zyx, - psf=LLSZWidget.LlszMenu.lattice.psf[channel], - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - - # set input to subsequent task as deconvolution output - user_workflow.set( - input_arg_first, "deconvolution") - - logger.info("Workflow to be executed:") - logger.info(user_workflow) - # Execute workflow - processed_vol = user_workflow.get(task_name_last) - - # check if a measurement table (usually a dictionary or list) or a tuple with different data types - # The function below saves the tables and adds any images to napari window - if type(processed_vol) in [dict, list, tuple]: - if (len(processed_vol) > 1): - df = pd.DataFrame() - for idx, i in enumerate(processed_vol): - df_temp = process_custom_workflow_output( - i, parent_dir, idx, LLSZWidget, self, channel, time, preview=True) - final_df = pd.concat([df, df_temp]) - # append dataframes from every loop and have table command outside loop? - # TODO: Figure out why table is not displaying - from napari_spreadsheet import _widget - table_viewer = _widget.TableViewerWidget( - show=True) - table_viewer.add_spreadsheet(final_df) - # widgets.Table(value=final_df).show() - - else: - # add image to napari window - # TODO: check if its an image napari supports? - process_custom_workflow_output( - processed_vol, parent_dir, 0, LLSZWidget, self, channel, time) - - print("Workflow complete") - pass - - @magicgui(header=dict(widget_type="Label", label="

      Apply Workflow and Save Output

      "), - time_start=dict(label="Time Start:", max=2**20), - time_end=dict(label="Time End:", - value=1, max=2**20), - ch_start=dict(label="Channel Start:"), - ch_end=dict(label="Channel End:", value=1), - Use_Cropping=dict( - widget_type="Checkbox", label="Crop Data", value=False), - get_active_workflow=dict( - widget_type="Checkbox", label="Get active workflow in napari-workflow", value=False), - workflow_path=dict( - mode='r', label="Load custom workflow (.yaml/yml)"), - save_as_type={ - "label": "Save as filetype:", "choices": SaveFileType}, - save_path=dict( - mode='d', label="Directory to save "), - #custom_module=dict(widget_type="Checkbox",label="Load custom module (same dir as workflow)",value = False), - call_button="Apply Workflow and Save Result") - def Apply_Workflow_and_Save(self, - header, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - Use_Cropping, - roi_layer_list: ShapesData, - get_active_workflow: bool = False, - workflow_path: Path = Path.home(), - save_as_type: str = SaveFileType.tiff, - save_path: Path = Path(history.get_save_history()[0])): - """ - Apply a user-defined analysis workflow using napari-workflows - - Args: - time_start (int): Start Time - time_end (int): End Time - ch_start (int): Start Channel - ch_end (int): End Channel - Use_Cropping (_type_): Use cropping based on ROIs in the shapes layer - roi_layer_list (ShapesData): Shapes layer to use for cropping; can be a list of shapes - get_active_workflow (bool, optional): Gets active workflow in napari. Defaults to False. - workflow_path (Path, optional): User can also choose a custom workflow defined in a yaml file. - save_path (Path, optional): Path to save resulting data - """ - assert LLSZWidget.LlszMenu.open_file, "Image not initialised" - - check_dimensions(time_start, time_end, ch_start, ch_end, - LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) - - # Get parameters - angle = LLSZWidget.LlszMenu.lattice.angle - dx = LLSZWidget.LlszMenu.lattice.dx - dy = LLSZWidget.LlszMenu.lattice.dy - dz = LLSZWidget.LlszMenu.lattice.dz - - if get_active_workflow: - # installs the workflow to napari - user_workflow = WorkflowManager.install( - self.parent_viewer).workflow - print("Workflow installed") - else: - # Automatically scan workflow file directory for *.py files. - # If it findss one, load it as a module - import importlib - parent_dir = workflow_path.resolve( - ).parents[0].__str__()+os.sep - sys.path.append(parent_dir) - custom_py_files = get_all_py_files(parent_dir) - if len(custom_py_files) == 0: - print( - f"No custom modules imported. If you'd like to use a cusotm module, place a *.py file in same folder as the workflow file {parent_dir}") - else: - modules = map( - importlib.import_module, custom_py_files) - print(f"Custom modules imported {modules}") - user_workflow = load_workflow(workflow_path) - - assert type( - user_workflow) is Workflow, "Workflow file is not a napari workflow object. Check file! You can use workflow inspector if needed" - - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - print(input_arg_first, input_arg_last, - first_task_name, last_task_name) - # get list of tasks - task_list = list(user_workflow._tasks.keys()) - print("Workflow loaded:") - print(user_workflow) - - vol = LLSZWidget.LlszMenu.lattice.data - - #vol_zyx= vol[time,channel,...] - - task_name_start = first_task_name[0] - - try: - task_name_last = last_task_name[0] - except IndexError: - task_name_last = task_name_start - - # variables to hold task name, initialize it as None - # if gpu, set otf_path, otherwise use psf - psf = None - otf_path = None - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - #otf_path = "otf_path" - psf_arg = "psf" - psf = LLSZWidget.LlszMenu.lattice.psf - else: - psf_arg = "psf" - psf = LLSZWidget.LlszMenu.lattice.psf - # if cropping, set that as first task - - if Use_Cropping: - # convert Roi pixel coordinates to canvas coordinates - # necessary only when scale is used for napari.viewer.add_image operations - roi_layer_list = [ - x/LLSZWidget.LlszMenu.lattice.dy for x in roi_layer_list] - - deskewed_shape = LLSZWidget.LlszMenu.lattice.deskew_vol_shape - deskewed_volume = da.zeros(deskewed_shape) - z_start = 0 - z_end = deskewed_shape[0] - roi = "roi" - volume = "volume" - # Check if decon ticked, if so set as first and crop as second? - - # Create workflow for cropping and deskewing - # volume and roi used will be set dynamically - user_workflow.set("crop_deskew_image", crop_volume_deskew, - original_volume=volume, - deskewed_volume=deskewed_volume, - roi_shape=roi, - angle_in_degrees=angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - z_start=z_start, - z_end=z_end, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - psf=psf_arg, - skew_dir=LLSZWidget.LlszMenu.skew_dir) - - # change the first task so it accepts "crop_deskew as input" - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="crop_deskew_image", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - - for idx, roi_layer in enumerate(tqdm(roi_layer_list, desc="ROI:", position=0)): - print("Processing ROI ", idx) - user_workflow.set(roi, roi_layer) - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=volume, - first_task="crop_deskew_image", - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - #roi_layer = roi_layer, - save_name_prefix="ROI_" + \ - str(idx), - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - # IF just deskewing and its not in the tasks, add that as first task - elif user_workflow.get_task(task_name_start)[0] not in (cle.deskew_y, cle.deskew_x): - input = "input" - # add task to the workflow - user_workflow.set("deskew_image", - LLSZWidget.LlszMenu.deskew_func, - input_image=input, - angle_in_degrees=angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - linear_interpolation=True) - # Set input of the workflow to be from deskewing - # change workflow task starts from is "deskew_image" and - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deskew_image", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = "psf" - otf_path = "otf_path" - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=input, - psf=psf_arg, - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=input, - psf=psf_arg, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # modify the user workflow so "deconvolution" is accepted - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - task_name_start = first_task_name[0] - - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=input, - first_task=task_name_start, - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - # If deskewing is already as a task, then set the first argument to input so we can modify that later - else: - # if deskewing is already first task, then check if deconvolution needed - # if deconvolution checked, add it to start of workflow (add upstream of deskewing) - if LLSZWidget.LlszMenu.deconvolution.value: - psf = "psf" - otf_path = "otf_path" - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - user_workflow.set("deconvolution", - pycuda_decon, - image=input, - psf=psf_arg, - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter) - # user_workflow.set(input_arg_first,"deconvolution") - else: - user_workflow.set("deconvolution", - skimage_decon, - vol_zyx=input, - psf=psf_arg, - num_iter=LLSZWidget.LlszMenu.lattice.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest') - # modify the user workflow so "deconvolution" is accepted - new_task = modify_workflow_task( - old_arg=input_arg_first, task_key=task_name_start, new_arg="deconvolution", workflow=user_workflow) - user_workflow.set(task_name_start, new_task) - input_arg_first, input_arg_last, first_task_name, last_task_name = get_first_last_image_and_task( - user_workflow) - task_name_start = first_task_name[0] - - # we pass first argument as input - save_img_workflow(vol=vol, - workflow=user_workflow, - input_arg=input_arg_first, - first_task=task_name_start, - last_task=task_name_last, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - deconvolution=LLSZWidget.LlszMenu.deconvolution.value, - decon_processing=LLSZWidget.LlszMenu.lattice.decon_processing, - otf_path=otf_path, - psf_arg=psf_arg, - psf=psf) - - print("Workflow complete") - return - - pass - -def _napari_lattice_widget_wrapper() -> LLSZWidget: - # split widget type enables a resizable widget - #max_height = 50 - # Important to have this or napari won't recognize the classes and magicclass qidgets - widget = LLSZWidget() - # aligning collapsible widgets at the top instead of having them centered vertically - widget._widget._layout.setAlignment(Qt.AlignTop) - - # widget._widget._layout.setWidgetResizable(True) - return widget diff --git a/plugin/napari_lattice/_reader.py b/plugin/napari_lattice/_reader.py deleted file mode 100644 index 79da871..0000000 --- a/plugin/napari_lattice/_reader.py +++ /dev/null @@ -1,161 +0,0 @@ -""" -reader plugin for h5 saved using np2bdv -https://github.com/nvladimus/npy2bdv -#TODO: pass pyramidal layer to napari -##use ilevel parameter in read_view to access different subsamples/pyramids -#pass a list of images with different resolution for pyramid; use is_pyramid=True flag in napari.add_image -, however pyramidal support for 3D not available yet -""" -from __future__ import annotations - -import dask.array as da -import dask.delayed as delayed -import os -import numpy as np -from napari.layers import image, Layer -from napari.layers._data_protocols import LayerDataProtocol - -from typing_extensions import Literal -from typing import Any, Optional, cast, TYPE_CHECKING, Tuple, List - -from lls_core.lattice_data import lattice_from_aics, LatticeData, img_from_array -from aicsimageio.types import ArrayLike, ImageLike - -if TYPE_CHECKING: - from aicsimageio.aics_image import AICSImage - -def lattice_from_napari( - img: Layer, - last_dimension: Optional[Literal["channel", "time"]], - **kwargs: Any -) -> LatticeData: - """ - Factory function for generating a LatticeData from a Napari Image - - Arguments: - kwargs: Extra arguments to pass to the LatticeData constructor - """ - - img_data_aics: AICSImage - - if 'aicsimage' in img.metadata.keys(): - img_data_aics = img.metadata['aicsimage'] - else: - if not last_dimension: - raise ValueError("Either the Napari image must have dimensional metadata, or last_dimension must be provided") - img_data_aics = img_from_array(cast(ArrayLike, img.data), last_dimension=last_dimension, physical_pixel_sizes=kwargs.get("physical_pixel_sizes")) - - save_name: str - if img.source.path is None: - # remove colon (:) and any leading spaces - save_name = img.name.replace(":", "").strip() - # replace any group of spaces with "_" - save_name = '_'.join(save_name.split()) - else: - file_name_noext = os.path.basename(img.source.path) - file_name = os.path.splitext(file_name_noext)[0] - # remove colon (:) and any leading spaces - save_name = file_name.replace(":", "").strip() - # replace any group of spaces with "_" - save_name = '_'.join(save_name.split()) - - return lattice_from_aics(img_data_aics, save_name=save_name, **kwargs) - -def napari_get_reader(path: list[str] | str): - """Check if file ends with h5 and returns reader function if true - Parameters - ---------- - path : str or list of str - Path to file, or list of paths. - Returns - ------- - function - """ - if isinstance(path, list): - # reader plugins may be handed single path, or a list of paths. - # if it is a list, we are only going to open first file - path = path[0] - - tiff_formats = (".tif",".tiff") - - if path.endswith(".h5"): - return bdv_h5_reader - elif path.endswith(tiff_formats): - return tiff_reader - # if we know we cannot read the file, we immediately return None. - else: - return None - - -def bdv_h5_reader(path): - """Take a path and returns a list of LayerData tuples.""" - - os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" - - #print(path) - import npy2bdv - h5_file = npy2bdv.npy2bdv.BdvEditor(path) - - img = [] - - #get dimensions of first image - first_timepoint = h5_file.read_view(time=0,channel=0) - - #Threshold to figure out when to use out-of-memory loading/dask - #Got the idea from napari-aicsimageio - #https://github.com/AllenCellModeling/napari-aicsimageio/blob/22934757c2deda30c13f39ec425343182fa91a89/napari_aicsimageio/core.py#L222 - mem_threshold_bytes = 4e9 - mem_per_threshold = 0.3 - - from psutil import virtual_memory - - file_size = os.path.getsize(path) - avail_mem = virtual_memory().available - - #if file size <30% of available memory and <4GB, open - if file_size<=mem_per_threshold*avail_mem and file_size List[Tuple[AICSImage, dict, str]]: - """Take path to tiff image and returns a list of LayerData tuples. - Specifying tiff_reader to have better control over tifffile related errors when using AICSImage - """ - - try: - image = AICSImage(path) - except Exception as e: - raise Exception("Error reading TIFF. Try upgrading tifffile library: pip install tifffile --upgrade.") from e - - # optional kwargs for the corresponding viewer.add_* method - add_kwargs = {} - - layer_type = "image" # optional, default is "image" - return [(image, add_kwargs, layer_type)] - diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 491970f..80d8a55 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -1,7 +1,6 @@ # FieldGroups that the users interact with to input data import logging -from enum import Enum from pathlib import Path from typing import Any, Callable, List, Optional, Tuple, TypeVar, cast @@ -23,7 +22,7 @@ from lls_core.workflow import workflow_from_path from magicclass import FieldGroup, MagicTemplate, field from magicclass.fields import MagicField -from magicclass.widgets import ComboBox, Label, Widget +from magicclass.widgets import ComboBox, Label, Widget, RadioButtons from napari.layers import Image, Shapes from napari.types import ShapesData from napari.utils import history @@ -62,6 +61,10 @@ def get_friendly_validations(model: FieldGroup) -> str: except BaseException as e: return exception_to_html(e) +class PixelSizeSource(StrEnum): + Metadata = "Image Metadata" + Manual = "Manual" + class WorkflowSource(StrEnum): ActiveWorkflow = "Active Workflow" CustomPath = "Custom Path" @@ -133,10 +136,8 @@ class StackAlong(StrEnum): CHANNEL = "Channel" TIME = "Time" -class LastDimensionOptions(Enum): - XYZTC = "XYZTC" - XYZCT = "XYZCT" - Metadata = "Get from Metadata" + + class NapariFieldGroup: # This implementation is a bit ugly. This is a mixin that can only be used on a `FieldGroup`. @@ -226,7 +227,7 @@ def _get_dimension_options(self, _) -> List[str]: tooltip="All the image layers you select will be stacked into one image and then deskewed. To select multiple layers, hold Command (MacOS) or Control (Windows, Linux)." ).with_choices(lambda _x, _y: get_layers(Image)) stack_along = field( - str + str, ).with_choices( [it.value for it in StackAlong] ).with_options( @@ -234,6 +235,7 @@ def _get_dimension_options(self, _) -> List[str]: tooltip="The direction along which to stack multiple selected layers.", value=StackAlong.CHANNEL ) + pixel_sizes_source = field(PixelSizeSource.Metadata, widget_type="RadioButtons").with_options(label="Pixel Size Source", orientation="horizontal").with_choices([it.value for it in PixelSizeSource]) pixel_sizes = field(Tuple[float, float, float]).with_options( label="Pixel Sizes: XYZ (µm)", tooltip="The size of each pixel in microns. The first field selects the X pixel size, then Y, then Z." @@ -255,11 +257,12 @@ def _get_dimension_options(self, _) -> List[str]: ).with_options( label="Dimension Order", tooltip="Specifies the order of dimensions in the input images. For example, if your image is a 4D array with multiple channels along the first axis, you will specify CZYX.", - value=LastDimensionOptions.Metadata.value + value="Get from Metadata" ) - skew_dir = field(DeskewDirection.Y).with_options( + skew_dir = field(DeskewDirection.Y, widget_type="RadioButtons").with_options( label="Skew Direction", - tooltip="The axis along which to deskew" + tooltip="The axis along which to deskew", + orientation="horizontal" ) errors = field(Label).with_options(label="Errors") @@ -288,6 +291,12 @@ def _img_changed(self) -> None: # Recalculate the dimension options whenever the image changes self.dimension_order.reset_choices() + @pixel_sizes_source.connect + @enable_if([pixel_sizes]) + def _hide_pixel_sizes(self): + # Hide the "Pixel Sizes" option unless the user specifies manual pixel size source + return self.pixel_sizes_source.value == PixelSizeSource.Manual + @img_layer.connect @enable_if([stack_along]) def _hide_stack_along(self): @@ -303,7 +312,7 @@ def _get_kwargs(self) -> DeskewKwargs: params = lattice_params_from_napari( imgs=self.img_layer.value, dimension_order=None if self.dimension_order.value == "Get from Metadata" else self.dimension_order.value, - physical_pixel_sizes=PhysicalPixelSizes( + physical_pixel_sizes= None if self.pixel_sizes_source.value == PixelSizeSource.Metadata else PhysicalPixelSizes( X = self.pixel_sizes.value[0], Y = self.pixel_sizes.value[1], Z = self.pixel_sizes.value[2] diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index 3ffd04f..eb596c4 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -35,7 +35,7 @@ class NapariImageParams(TypedDict): def lattice_params_from_napari( imgs: Collection[Image], dimension_order: Optional[str], - physical_pixel_sizes: PhysicalPixelSizes, + physical_pixel_sizes: Optional[PhysicalPixelSizes], stack_along: str ) -> NapariImageParams: """ @@ -51,9 +51,9 @@ def lattice_params_from_napari( raise ValueError(f"The input images have multiple different dimensions, which napari lattice doesn't support: {size_message}") save_name: str - pixel_sizes: set[PhysicalPixelSizes] = {physical_pixel_sizes} + # This is a set of all pixel sizes that we have seen so far + metadata_pixel_sizes: set[PhysicalPixelSizes] = set() save_names = [] - # The pixel sizes according to the AICS metadata, if any final_imgs: list[DataArray] = [] @@ -75,9 +75,10 @@ def lattice_params_from_napari( if 'aicsimage' in img.metadata.keys(): img_data_aics: AICSImage = img.metadata['aicsimage'] + # If the user has not provided pixel sizes, we extract them fro the metadata # Only process pixel sizes that are not none - if all(img_data_aics.physical_pixel_sizes): - pixel_sizes.add(img_data_aics.physical_pixel_sizes) + if physical_pixel_sizes is None and all(img_data_aics.physical_pixel_sizes): + metadata_pixel_sizes.add(img_data_aics.physical_pixel_sizes) metadata_order = list(img_data_aics.dims.order) metadata_shape = list(img_data_aics.dims.shape) @@ -96,12 +97,15 @@ def lattice_params_from_napari( final_imgs.append(DataArray(img.data, dims=calculated_order)) - if len(pixel_sizes) > 1: - raise Exception(f"Two or more layers that you have tried to merge have different pixel sizes according to their metadata! {pixel_sizes}") - elif len(pixel_sizes) == 1: - final_pixel_size = DefinedPixelSizes.from_physical(pixel_sizes.pop()) - else: + if physical_pixel_sizes: final_pixel_size = DefinedPixelSizes.from_physical(physical_pixel_sizes) + else: + if len(metadata_pixel_sizes) > 1: + raise Exception(f"Two or more layers that you have tried to merge have different pixel sizes according to their metadata! {metadata_pixel_sizes}") + elif len(metadata_pixel_sizes) < 1: + raise Exception("No pixel sizes could be determined from the image metadata. Consider manually specifying the pixel sizes.") + else: + final_pixel_size = DefinedPixelSizes.from_physical(metadata_pixel_sizes.pop()) final_img = concat(final_imgs, dim=stack_along) return NapariImageParams(save_name=save_names[0], physical_pixel_sizes=final_pixel_size, data=final_img, dims=final_img.shape) diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index 34302d3..ddaf21ab 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -72,7 +72,7 @@ SourceCode = "https://github.com/BioimageAnalysisCoreWEHI/napari_lattice" UserSupport = "https://github.com/BioimageAnalysisCoreWEHI/napari_lattice/issues" [tool.setuptools.package-data] -napari_lattice = ["*.yaml"] +napari_lattice = ["*.yaml", "*.svg", "*.png"] [project.optional-dependencies] testing = [ From 6113c373b590ace6f41639bb93ee1029405d9216 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 21 Sep 2023 14:25:11 +1000 Subject: [PATCH 048/147] Copy the args before modifying them --- core/tests/test_lattice_data.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index aff4521..762f652 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -1,3 +1,4 @@ +from copy import copy from typing import Any import pytest from lls_core.models import LatticeData @@ -39,6 +40,7 @@ def open_psf(name: str): @inputs @parameterized def test_process(path: str, args: dict): + args = copy(args) with as_file(resources / path) as lattice_path: args["image"] = lattice_path for slice in LatticeData.parse_obj(args).process().slices: @@ -48,6 +50,7 @@ def test_process(path: str, args: dict): @parameterized def test_process_deconvolution(args: dict, background: Any): root = Path(__file__).parent / "data" + args = copy(args) args["image"] = root / "raw.tif" args["deconvolution"] = { "psf": [root / "psf.tif"], @@ -60,6 +63,7 @@ def test_process_deconvolution(args: dict, background: Any): @inputs @parameterized def test_save(path: str, args: dict): + args = copy(args) with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: args["image"] = lattice_path args["save_dir"] = tempdir From ba2f5cc0365027f062c28eb7f6631bfe9afe5d58 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 22 Sep 2023 18:19:05 +1000 Subject: [PATCH 049/147] Improvements to workflow handling --- core/lls_core/models/deskew.py | 69 +++++++++++- core/lls_core/models/lattice_data.py | 151 ++++++++++----------------- core/lls_core/models/utils.py | 16 ++- core/lls_core/types.py | 3 + core/lls_core/utils.py | 43 -------- core/lls_core/workflow.py | 21 +++- core/tests/test_batch_deskew_args.py | 1 - core/tests/test_lattice_data.py | 80 ++++++-------- core/tests/test_workflows.py | 151 +++++++-------------------- plugin/napari_lattice/dock_widget.py | 12 +-- plugin/napari_lattice/fields.py | 98 ++++++++++------- plugin/napari_lattice/utils.py | 4 +- pyproject.toml | 10 -- 13 files changed, 292 insertions(+), 367 deletions(-) delete mode 100644 pyproject.toml diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 3d6c652..ab0d83b 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -64,10 +64,77 @@ class DeskewParams(FieldAccessMixin): deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False, default=None, description="Deskewing transformation function. This is set automatically based on other input parameters, and doesn't need to be provided by the user.") + # Hack to ensure that .skew_dir behaves identically to .skew + @property + def skew_dir(self) -> DeskewDirection: + return self.skew + + @skew_dir.setter + def skew_dir(self, value: DeskewDirection): + self.skew = value + + @property + def deskew_func(self): + # Chance deskew function absed on skew direction + if self.skew == DeskewDirection.Y: + return cle.deskew_y + elif self.skew == DeskewDirection.X: + return cle.deskew_x + else: + raise ValueError() + + @property + def dx(self) -> float: + return self.physical_pixel_sizes.X + + @dx.setter + def dx(self, value: float): + self.physical_pixel_sizes.X = value + + @property + def dy(self) -> float: + return self.physical_pixel_sizes.Y + + @dy.setter + def dy(self, value: float) -> None: + self.physical_pixel_sizes.Y = value + + @property + def dz(self) -> float: + return self.physical_pixel_sizes.Z + + @dz.setter + def dz(self, value: float): + self.physical_pixel_sizes.Z = value + + def get_angle(self) -> float: + return self.angle + + def set_angle(self, angle: float) -> None: + self.angle = angle + + def set_skew(self, skew: DeskewDirection) -> None: + self.skew = skew + @property def dims(self): return self.image.dims + @property + def time(self) -> int: + """Number of time points""" + return self.image.sizes["T"] + + @property + def channels(self) -> int: + """Number of channels""" + return self.image.sizes["C"] + + @property + def new_dz(self): + import math + return math.sin(self.angle * math.pi / 180.0) * self.dz + @validator("skew", pre=True) def convert_skew(cls, v: Any): # Allow skew to be provided as a string @@ -82,8 +149,6 @@ def convert_pixels(cls, v: Any): return DefinedPixelSizes(X=v[0], Y=v[1], Z=v[2]) return v - # convert_image = validator("image", pre=True, allow_reuse=True)(image_like_to_image) - @validator("image", pre=True) def reshaping(cls, v: Any): # This allows a user to pass in any array-like object and have it diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 1179a62..962d59d 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -104,12 +104,14 @@ def save_image(self): ) for result in roi_results: bdv_writer.append_view( - result.data, + np.array(result.data), time=result.time, channel=result.channel, voxel_size_xyz=(self.lattice_data.dx, self.lattice_data.dy, self.lattice_data.new_dz), voxel_units='um' ) + bdv_writer.write_xml() + bdv_writer.close() elif self.lattice_data.save_type == SaveFileType.tiff: # For each time point, we write a separate TIFF for time, results in groupby(roi_results, key=lambda it: it.time): @@ -172,6 +174,17 @@ def default_save_name(cls, values: dict): values["save_name"] = Path(values["image"]).stem return values + @validator("workflow", pre=True) + def parse_workflow(cls, v: Any): + # Load the workflow from disk if it was provided as a path + from lls_core.types import is_pathlike + from napari_workflows._io_yaml_v1 import load_workflow + from os import fspath + + if is_pathlike(v): + return load_workflow(fspath(v)) + return v + @validator("time_range", pre=True, always=True) def parse_time_range(cls, v: Any, values: dict) -> Any: """ @@ -255,58 +268,6 @@ def check_psfs(cls, v: Optional[DeconvolutionParams], values: dict): raise ValueError(f"There should be one PSF per channel, but there are {psfs} PSFs and {channels} channels.") return v - # Hack to ensure that .skew_dir behaves identically to .skew - @property - def skew_dir(self) -> DeskewDirection: - return self.skew - - @skew_dir.setter - def skew_dir(self, value: DeskewDirection): - self.skew = value - - @property - def deskew_func(self): - # Chance deskew function absed on skew direction - if self.skew == DeskewDirection.Y: - return cle.deskew_y - elif self.skew == DeskewDirection.X: - return cle.deskew_x - else: - raise ValueError() - - @property - def dx(self) -> float: - return self.physical_pixel_sizes.X - - @dx.setter - def dx(self, value: float): - self.physical_pixel_sizes.X = value - - @property - def dy(self) -> float: - return self.physical_pixel_sizes.Y - - @dy.setter - def dy(self, value: float): - self.physical_pixel_sizes.Y = value - - @property - def dz(self) -> float: - return self.physical_pixel_sizes.Z - - @dz.setter - def dz(self, value: float): - self.physical_pixel_sizes.Z = value - - def get_angle(self) -> float: - return self.angle - - def set_angle(self, angle: float) -> None: - self.angle = angle - - def set_skew(self, skew: DeskewDirection) -> None: - self.skew = skew - @property def cropping_enabled(self) -> bool: "True if cropping should be performed" @@ -317,20 +278,6 @@ def deconv_enabled(self) -> bool: "True if deconvolution should be performed" return self.deconvolution is not None - @property - def time(self) -> int: - """Number of time points""" - return self.image.sizes["T"] - - @property - def channels(self) -> int: - """Number of channels""" - return self.image.sizes["C"] - - @property - def new_dz(self): - return math.sin(self.angle * math.pi / 180.0) * self.dz - def __post_init__(self): logger.info(f"Channels: {self.channels}, Time: {self.time}") logger.info("If channel and time need to be swapped, you can enforce this by choosing 'Last dimension is channel' when initialising the plugin") @@ -343,15 +290,6 @@ def slice_data(self, time: int, channel: int) -> DataArray: return self.image.isel(T=time, C=channel) - if len(self.image.shape) == 3: - return self.image - elif len(self.image.shape) == 4: - return self.image[time, :, :, :] - elif len(self.image.shape) == 5: - return self.image[time, channel, :, :, :] - - raise Exception("Lattice data must be 3-5 dimensions") - def iter_slices(self) -> Iterable[SlicedData[ArrayLike]]: """ Yields array slices for each time and channel of interest. @@ -377,11 +315,12 @@ def iter_sublattices(self, update_with: dict = {}) -> Iterable[SlicedData[Lattic update_with: dictionary of arguments to update the generated lattices with """ for subarray in self.iter_slices(): - yield subarray.copy_with_data( - self.copy(update={ "image": subarray, - **update_with - }) - ) + # Copy doesn't manu + new_lattice = self.copy_validate(update={ + "image": subarray.data, + **update_with + }) + yield subarray.copy_with_data( new_lattice) def generate_workflows( self, @@ -394,14 +333,22 @@ def generate_workflows( return from copy import copy + from lls_core.workflow import get_workflow_inputs, update_workflow # We make a copy of the lattice for each slice, each of which has no associated workflow for lattice_slice in self.iter_sublattices(update_with={"workflow": None}): user_workflow = copy(self.workflow) user_workflow.set( "deskew_image", - LatticeData.process, + LatticeData.process_into_image, lattice_slice.data ) + for task_name, arg_index, arg_name in get_workflow_inputs(user_workflow): + update_workflow( + user_workflow, + task_name, + arg_index, + "deskew_image" + ) yield lattice_slice.copy_with_data(user_workflow) def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int): @@ -510,6 +457,22 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: voxel_size_z=self.dz )) ) + + def _process_workflow(self) -> ProcessedSlices: + outputs = [] + for workflow in self.generate_workflows(): + for leaf in workflow.data.leafs(): + outputs.append( + workflow.copy_with_data( + workflow.data.get(leaf) + ) + ) + + return ProcessedSlices( + slices = outputs, + lattice_data=self + ) + def process(self) -> ProcessedSlices: """ Execute the processing and return the result. @@ -518,20 +481,7 @@ def process(self) -> ProcessedSlices: ProcessedSlices.update_forward_refs() if self.workflow is not None: - outputs = [] - for workflow in self.generate_workflows(): - for leaf in workflow.data.leafs(): - outputs.append( - workflow.copy_with_data( - workflow.data.get(leaf) - ) - ) - - return ProcessedSlices( - slices = outputs, - lattice_data=self - ) - + return self._process_workflow() elif self.cropping_enabled: return ProcessedSlices( lattice_data=self, @@ -542,3 +492,12 @@ def process(self) -> ProcessedSlices: lattice_data=self, slices=self._process_non_crop() ) + + def process_into_image(self) -> ArrayLike: + """ + Shortcut method for calling process, then extracting one image layer. + This is mostly here to simplify the Workflow integration + """ + for slice in self.process().slices: + return slice.data + raise Exception("No slices produced!") diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py index b929a93..6f56a77 100644 --- a/core/lls_core/models/utils.py +++ b/core/lls_core/models/utils.py @@ -1,8 +1,8 @@ from typing import Any, Type +from typing_extensions import Self from enum import Enum from pydantic import BaseModel, Extra -from typer import Option from contextlib import contextmanager def enum_choices(enum: Type[Enum]) -> str: @@ -30,6 +30,7 @@ class FieldAccessMixin(BaseModel): class Config: extra = Extra.forbid arbitrary_types_allowed = True + validate_assignment = True @classmethod def get_default(cls, field_name: str) -> Any: @@ -60,13 +61,10 @@ def to_definition_dict(cls) -> dict: ret[key] = value return ret - @classmethod - def make_typer_field(cls, field_name: str, extra_description: str = "") -> Any: + def copy_validate(self, **kwargs) -> Self: """ - Convert this field into a Typer field + Like `.copy()`, but validates the results. + See https://github.com/pydantic/pydantic/issues/418 for more information """ - field = cls.__fields__[field_name] - return Option( - default = field.get_default(), - help=field.field_info.description + extra_description - ) + updated = self.copy(**kwargs) + return updated.validate(updated.dict()) diff --git a/core/lls_core/types.py b/core/lls_core/types.py index 5152719..3b14ea0 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -32,4 +32,7 @@ def image_like_to_image(img: ImageLike) -> DataArray: if isinstance(img, AICSImage): return img.xarray_dask_data else: + for required_key in ("shape", "dtype", "ndim", "__array__", "__array_ufunc__"): + if not hasattr(img, required_key): + raise ValueError(f"The provided object {img} is not array like!") return DataArray(img) diff --git a/core/lls_core/utils.py b/core/lls_core/utils.py index 36671c5..d27c0aa 100644 --- a/core/lls_core/utils.py +++ b/core/lls_core/utils.py @@ -190,49 +190,6 @@ def dask_expand_dims(a: ArrayLike, axis: Union[Collection[int], int]): return a.reshape(shape) -def read_imagej_roi(roi_zip_path: str): - """Read an ImageJ ROI zip file so it loaded into napari shapes layer - If non rectangular ROI, will convert into a rectangle based on extreme points - Args: - roi_zip_path (zip file): ImageJ ROI zip file - - Returns: - list: List of ROIs - """ - roi_extension = path.splitext(roi_zip_path)[1] - - # handle reading single roi or collection of rois in zip file - if roi_extension == ".zip": - ij_roi = read_roi_zip(roi_zip_path) - elif roi_extension == ".roi": - ij_roi = read_roi_file(roi_zip_path) - else: - raise Exception("ImageJ ROI file needs to be a zip/roi file") - - # initialise list of rois - roi_list = [] - - # Read through each roi and create a list so that it matches the organisation of the shapes from napari shapes layer - for k in ij_roi.keys(): - if ij_roi[k]['type'] in ('oval', 'rectangle'): - width = ij_roi[k]['width'] - height = ij_roi[k]['height'] - left = ij_roi[k]['left'] - top = ij_roi[k]['top'] - roi = [[top, left], [top, left+width], - [top+height, left+width], [top+height, left]] - roi_list.append(roi) - elif ij_roi[k]['type'] in ('polygon', 'freehand'): - left = min(ij_roi[k]['x']) - top = min(ij_roi[k]['y']) - right = max(ij_roi[k]['x']) - bottom = max(ij_roi[k]['y']) - roi = [[top, left], [top, right], [bottom, right], [bottom, left]] - roi_list.append(roi) - else: - print("Cannot read ROI ", - ij_roi[k], ".Recognised as type ", ij_roi[k]['type']) - return roi_list def pad_image_nearest_multiple(img: NDArray, nearest_multiple: int) -> NDArray: """pad an Image to the nearest multiple of provided number diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index 25ceeff..0577721 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -5,7 +5,7 @@ from os import path from pathlib import Path -from typing import Optional, Tuple, Union +from typing import Any, Optional, Tuple, Union import dask.array as da import numpy as np @@ -16,12 +16,27 @@ if TYPE_CHECKING: from napari_workflows import Workflow - from lls_core.models import LatticeData import logging logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +def get_workflow_inputs(workflow: Workflow): + """ + Yields tuples of (task_name, argument_index, input_argument) corresponding to the workflow's inputs, + namely the arguments that are unfilled. + Note that the index returned is the index in the overall task tuple, which includes the task name + """ + for root_arg in workflow.roots(): + for taskname, (task_func, *args) in workflow._tasks.items(): + if root_arg in args: + yield taskname, args.index(root_arg) + 1, root_arg + +def update_workflow(workflow: Workflow, task_name: str, task_index: int, new_value: Any): + task = list(workflow.get_task(task_name)) + task[task_index] = new_value + workflow.set_task(task_name, tuple(task)) + def get_first_last_image_and_task(user_workflow: Workflow) -> Tuple[str, str, str, str]: """ Get images and tasks for first and last entry @@ -51,7 +66,7 @@ def get_first_last_image_and_task(user_workflow: Workflow) -> Tuple[str, str, st def modify_workflow_task(old_arg: str, task_key: str, new_arg: str, workflow: Workflow) -> tuple: """ - Modify items in a workflow task + Replies one argument in a workflow with another Workflow is not modified, only a new task with updated arg is returned Args: old_arg: The argument in the workflow that needs to be modified diff --git a/core/tests/test_batch_deskew_args.py b/core/tests/test_batch_deskew_args.py index 34a53b4..df52978 100644 --- a/core/tests/test_batch_deskew_args.py +++ b/core/tests/test_batch_deskew_args.py @@ -14,7 +14,6 @@ def create_image(path: Path): imsave(str(path), raw) assert path.exists() - def test_batch_deskew_h5(): """Write image to disk and then execute napari_lattice from terminal Checks if an deskewed output file is created for both tif and h5 diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index 762f652..e05cf6d 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -1,72 +1,58 @@ -from copy import copy from typing import Any import pytest from lls_core.models import LatticeData -from lls_core.models.output import SaveFileType from lls_core.sample import resources from importlib_resources import as_file -from importlib_resources.abc import Traversable import tempfile from pathlib import Path -inputs = pytest.mark.parametrize( - ["path"], [ - ("RBC_tiny.czi", ), - ("RBC_lattice.tif", ), - ("LLS7_t1_ch1.czi", ), - ("LLS7_t1_ch3.czi", ), - ("LLS7_t2_ch1.czi", ), - ("LLS7_t2_ch3.czi", ), - ("multich_multi_time.tif", ), -]) +from .params import inputs, parameterized def open_psf(name: str): with as_file(resources / "psfs" / "zeiss_simulated" / name) as path: return path - -parameterized = pytest.mark.parametrize("args", [ - {"skew": "X"}, - {"skew": "Y"}, - {"angle": 30}, - {"angle": 90}, - {"physical_pixel_sizes": (1, 1, 1)}, - {"save_type": SaveFileType.h5}, - {"save_type": SaveFileType.tiff}, - - # Cropping enabled - {"crop": {"roi_list": []}}, -]) - @inputs @parameterized def test_process(path: str, args: dict): - args = copy(args) with as_file(resources / path) as lattice_path: - args["image"] = lattice_path - for slice in LatticeData.parse_obj(args).process().slices: + for slice in LatticeData.parse_obj({ + "image": lattice_path, + **args + }).process().slices: assert slice.data.ndim == 3 +@inputs +@parameterized +def test_save(path: str, args: dict): + with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: + LatticeData.parse_obj({ + "image": lattice_path, + "save_dir": tempdir, + **args + }).process().save_image() + results = list(Path(tempdir).iterdir()) + assert len(results) > 0 + @pytest.mark.parametrize(["background"], [(1, ), ("auto",), ("second_last",)]) @parameterized def test_process_deconvolution(args: dict, background: Any): root = Path(__file__).parent / "data" - args = copy(args) - args["image"] = root / "raw.tif" - args["deconvolution"] = { - "psf": [root / "psf.tif"], - "background": background - } - for slice in LatticeData.parse_obj(args).process().slices: + for slice in LatticeData.parse_obj({ + "image": root / "raw.tif", + "deconvolution": { + "psf": [root / "psf.tif"], + "background": background + }, + **args + }).process().slices: assert slice.data.ndim == 3 - -@inputs @parameterized -def test_save(path: str, args: dict): - args = copy(args) - with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: - args["image"] = lattice_path - args["save_dir"] = tempdir - LatticeData.parse_obj(args).process().save_image() - results = list(Path(tempdir).iterdir()) - assert len(results) > 0 +def test_process_workflow(args: dict): + root = Path(__file__).parent / "data" + for slice in LatticeData.parse_obj({ + "image": root / "raw.tif", + "workflow": root / "raw.tif", + **args + }).process().slices: + assert slice.data.ndim == 3 diff --git a/core/tests/test_workflows.py b/core/tests/test_workflows.py index bd285f2..4d060f6 100644 --- a/core/tests/test_workflows.py +++ b/core/tests/test_workflows.py @@ -1,126 +1,53 @@ -from skimage.io import imread, imsave +from typing import Callable import os -import numpy as np -from pathlib import Path -import platform -import pyclesperanto_prototype as cle +from copy import copy +from numpy.typing import NDArray from napari_workflows import Workflow -from napari_workflows._io_yaml_v1 import load_workflow, save_workflow - -from lls_core.cmds.__main__ import main as run_cli - -# For testing in Windows -if platform.system() == "Windows": - home_dir = str(Path.home()) - home_dir = home_dir.replace("\\", "\\\\") - img_dir = home_dir + "\\\\raw.tiff" - workflow_location = home_dir + "\\\\deskew_segment.yaml" - config_location = home_dir + "\\\\config_deskew.yaml" -else: - home_dir = str(Path.home()) - img_dir = os.path.join(home_dir, "raw.tiff") - workflow_location = os.path.join(home_dir, "deskew_segment.yaml") - config_location = os.path.join(home_dir, "config_deskew.yaml") - - -def write_config_file(config_settings, output_file_location): - with open(output_file_location, 'w') as f: - for key, val in config_settings.items(): - if val is not None: - if type(val) is str: - print('%s: "%s"' % (key, val), file=f) - - if type(val) is int: - print('%s: %i' % (key, val), file=f) - - if type(val) is list: - print("%s:" % key, file=f) - for x in val: - if type(x) is int: - print(" - %i" % x, file=f) - else: - print(' - "%s"' % x, file=f) - - print("Config found written to %s" % output_file_location) - - -def create_data(): - # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) - raw = np.zeros((5, 5, 5)) - raw[2, 2, 2] = 10 - # Save image as a tif filw in home directory - imsave(img_dir, raw) - assert os.path.exists(img_dir) - - # Create a config file - config = { - "input": img_dir, - "output": home_dir, - "processing": "workflow", - "workflow_path": workflow_location, - "output_file_type": "h5"} - - write_config_file(config, config_location) - assert os.path.exists(config_location) - - -def create_workflow(): - # Zeiss lattice - voxel_size_x_in_microns = 0.14499219272808386 - voxel_size_y_in_microns = 0.14499219272808386 - voxel_size_z_in_microns = 0.3 - deskewing_angle_in_degrees = 30.0 +import tempfile - # Instantiate segmentation workflow - image_seg_workflow = Workflow() - - image_seg_workflow.set("gaussian", cle.gaussian_blur, - "input", sigma_x=1, sigma_y=1, sigma_z=1) - - image_seg_workflow.set("binarisation", cle.threshold, - "gaussian", constant=0.5) - - image_seg_workflow.set( - "labeling", cle.connected_components_labeling_box, "binarisation") - - save_workflow(workflow_location, image_seg_workflow) - - assert os.path.exists(workflow_location) +from tests.utils import invoke +from pathlib import Path +from .params import config_types +from .utils import invoke -def test_napari_workflow(): - """Test napari workflow to see if it works before we run it using napari_lattice - This is without deskewing +def test_napari_workflow(workflow: Workflow, test_image: NDArray): """ - create_data() - create_workflow() - - image_seg_workflow = load_workflow(workflow_location) - - # Open the saved image from above - raw = imread(img_dir) + Test napari workflow to see if it works before we run it using napari_lattice + This is without deskewing + """ + workflow = copy(workflow) # Set input image to be the "raw" image - image_seg_workflow.set("input", raw) - labeling = image_seg_workflow.get("labeling") - assert (labeling[2, 2, 2] == 1) + workflow.set("input", test_image) + labeling = workflow.get("labeling") + assert labeling[2, 2, 2] == 1 - -def test_workflow_lattice(): - """Test workflow by loading into napari lattice - This will apply deskewing before processing the workflow +@config_types +def test_workflow_cli(workflow_config_cli: dict, save_func: Callable, cli_param: str): + """ + Test workflow processing via CLI + This will apply deskewing before processing the workflow """ - # Deskew, apply workflow and save as h5 - run_cli(["--config", config_location]) + with tempfile.NamedTemporaryFile(mode="w") as fp: + save_func(workflow_config_cli, fp) + fp.flush() + + # Deskew, apply workflow and save as h5 + invoke([ + "process", + cli_param, fp.name + ]) # checks if h5 file written - h5_img = os.path.join(home_dir, "raw", "_0_raw.h5") - assert os.path.exists(h5_img) + save_dir = Path(workflow_config_cli["save_dir"]) + saved_files = list(save_dir.glob("*.h5")) + assert len(saved_files) > 0 + assert len(list(save_dir.glob("*.xml"))) > 0 import npy2bdv - h5_file = npy2bdv.npy2bdv.BdvEditor(h5_img) - - label_img = h5_file.read_view(time=0, channel=0) - - assert (label_img.shape == (3, 14, 5)) - assert (label_img[1, 6, 2] == 1) + for h5_img in saved_files: + h5_file = npy2bdv.npy2bdv.BdvEditor(str(h5_img)) + label_img = h5_file.read_view(time=0, channel=0) + assert label_img.shape == (3, 14, 5) + assert label_img[1, 6, 2] == 1 diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 05f00d1..4debcc0 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -91,11 +91,11 @@ def __post_init__(self): for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: field._validate() - deskew_fields = DeskewFields(name = "1. Deskew") - deconv_fields = DeconvolutionFields(name = "2. Deconvolution") - cropping_fields = CroppingFields(name = "3. Crop") - workflow_fields = WorkflowFields(name = "4. Workflow") - output_fields = OutputFields(name = "5. Output") + deskew_fields = DeskewFields() + deconv_fields = DeconvolutionFields() + cropping_fields = CroppingFields() + workflow_fields = WorkflowFields() + output_fields = OutputFields() @set_options(header=dict(widget_type="Label", label="

      Preview Deskew

      "), time=dict(label="Time:", max=2**15), @@ -106,7 +106,7 @@ def __post_init__(self): def preview(self, header:str, time: int, channel: int): # We only need to process one time point for the preview, # so we made a copy using a subset of the times - lattice = self._make_model().copy(update=dict( + lattice = self._make_model().copy_validate(update=dict( time_range = range(time, time+1), channel_range = range(time, time+1), )) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 80d8a55..1185468 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -20,9 +20,9 @@ from lls_core.models.deskew import DefinedPixelSizes from lls_core.models.output import SaveFileType from lls_core.workflow import workflow_from_path -from magicclass import FieldGroup, MagicTemplate, field +from magicclass import FieldGroup, MagicTemplate, field, magicclass, set_design from magicclass.fields import MagicField -from magicclass.widgets import ComboBox, Label, Widget, RadioButtons +from magicclass.widgets import ComboBox, Label, Widget from napari.layers import Image, Shapes from napari.types import ShapesData from napari.utils import history @@ -75,20 +75,19 @@ class BackgroundSource(StrEnum): Custom = "Custom" -def enable_field(parent: MagicTemplate, field: MagicField, enabled: bool = True) -> None: +def enable_field(field: MagicField, enabled: bool = True) -> None: """ Enable the widget associated with a field Args: - parent: The parent magicclass that contains the field field: The field to enable/disable enabled: If False, disable the field instead of enabling it """ - real_field = getattr(parent, field.name) - if not isinstance(real_field, Widget): - raise Exception("Define your fields with field() not vfield()!") - real_field.visible = enabled - real_field.enabled = enabled + for real_field in field._guis.values(): + if not isinstance(real_field, Widget): + raise Exception("Define your fields with field() not vfield()!") + real_field.visible = enabled + real_field.enabled = enabled EnabledHandlerType = TypeVar("EnabledHandlerType") @@ -114,18 +113,18 @@ def _enable_fields(self) -> bool: def _decorator(fn: Callable[[EnabledHandlerType], bool])-> Callable[[EnabledHandlerType], None]: for field in fields: - field.visible = False field.enabled = False + field.visible = False - def make_handler(fn: Callable) -> Callable[[EnabledHandlerType], None]: - def handler(parent: EnabledHandlerType): - enable = fn(parent) + def make_handler(fn: Callable[[EnabledHandlerType], bool]) -> Callable[[EnabledHandlerType], None]: + def handler(value: Any): + enable = fn(value) for field in fields: if enable: logger.info(f"{field.name} Activated") else: logger.info(f"{field.name} Deactivated") - enable_field(parent, field, enabled=enable) + enable_field(field, enabled=enable) return handler return make_handler(fn) @@ -136,9 +135,6 @@ class StackAlong(StrEnum): CHANNEL = "Channel" TIME = "Time" - - - class NapariFieldGroup: # This implementation is a bit ugly. This is a mixin that can only be used on a `FieldGroup`. # However, it can't inherit from FieldGroup because then the metaclass would look for fields in this @@ -203,7 +199,8 @@ class DeskewKwargs(NapariImageParams): angle: float skew: DeskewDirection -class DeskewFields(NapariFieldGroup, FieldGroup): +@magicclass(name="1. Deskew") +class DeskewFields(NapariFieldGroup): def _get_dimension_options(self, _) -> List[str]: """ @@ -293,15 +290,15 @@ def _img_changed(self) -> None: @pixel_sizes_source.connect @enable_if([pixel_sizes]) - def _hide_pixel_sizes(self): + def _hide_pixel_sizes(pixel_sizes_source: str): # Hide the "Pixel Sizes" option unless the user specifies manual pixel size source - return self.pixel_sizes_source.value == PixelSizeSource.Manual + return pixel_sizes_source == PixelSizeSource.Manual @img_layer.connect @enable_if([stack_along]) - def _hide_stack_along(self): + def _hide_stack_along(img_layer): # Hide the "Stack Along" option if we only have one image - return len(self.img_layer.value) > 1 + return len(img_layer.value) > 1 def _get_kwargs(self) -> DeskewKwargs: """ @@ -334,7 +331,8 @@ def _make_model(self) -> DeskewParams: skew = kwargs["skew"] ) -class DeconvolutionFields(NapariFieldGroup, FieldGroup): +@magicclass(name="2. Deconvolution") +class DeconvolutionFields(NapariFieldGroup): """ A counterpart to the DeconvolutionParams Pydantic class """ @@ -355,8 +353,8 @@ class DeconvolutionFields(NapariFieldGroup, FieldGroup): @enable_if( [background_custom] ) - def _enable_custom_background(self) -> bool: - return self.background.value == BackgroundSource.Custom + def _enable_custom_background(background: str) -> bool: + return background == BackgroundSource.Custom @fields_enabled.connect @enable_if( @@ -367,8 +365,8 @@ def _enable_custom_background(self) -> bool: background ] ) - def _enable_fields(self) -> bool: - return self.fields_enabled.value + def _enable_fields(enabled) -> bool: + return enabled def _make_model(self) -> Optional[DeconvolutionParams]: if not self.fields_enabled.value: @@ -386,12 +384,13 @@ def _make_model(self) -> Optional[DeconvolutionParams]: psf_num_iter=self.psf_num_iter.value ) -class CroppingFields(NapariFieldGroup, FieldGroup): +@magicclass(name="3. Crop") +class CroppingFields(MagicTemplate, NapariFieldGroup): """ A counterpart to the CropParams Pydantic class """ fields_enabled = field(False, label="Enabled") - shapes= field(List[Shapes], widget_type="Select", label = "ROIs").with_options(choices=lambda _x, _y: get_layers(Shapes)) + shapes= field(List[Shapes], widget_type="Select", label = "ROI Shape Layers").with_options(choices=lambda _x, _y: get_layers(Shapes)) z_range = field(Tuple[int, int]).with_options( label = "Z Range", value = (0, 1), @@ -402,21 +401,46 @@ class CroppingFields(NapariFieldGroup, FieldGroup): ) errors = field(Label).with_options(label="Errors") + def _get_deskew(self) -> DeskewParams: + "Returns the DeskewParams from the other tab" + from napari_lattice.dock_widget import LLSZWidget + parent = self.find_ancestor(LLSZWidget) + return parent.LlszMenu.WidgetContainer.deskew_fields._make_model() + + @set_design(text="Import ROI") + def import_roi(self, path: Path): + from lls_core.cropping import read_imagej_roi + from napari_lattice.utils import get_viewer + import numpy as np + roi_list = read_imagej_roi(path) + # convert to canvas coordinates + roi_list = (np.array(roi_list) * self._get_deskew().dy).tolist() + viewer = get_viewer() + viewer.add_shapes(roi_list, shape_type='polygon', edge_width=1, edge_color='yellow', face_color=[1, 1, 1, 0]) + + @set_design(text="New Crop") + def new_crop_layer(self): + from napari_lattice.utils import get_viewer + shapes = get_viewer().add_shapes() + shapes.mode = "SELECT" + shapes.name = "Napari Lattice Crop" + @fields_enabled.connect @enable_if([shapes, z_range]) - def _enable_workflow(self) -> bool: - return self.fields_enabled.value + def _enable_crop(enabled: bool) -> bool: + return enabled def _make_model(self) -> Optional[CropParams]: import numpy as np if self.fields_enabled.value: return CropParams( z_range=self.z_range.value, - roi_list=ShapesData([np.array(shape.data) for shape in self.shapes.value]) + roi_list=ShapesData([np.array(shape.data) / self._get_deskew().dy for shape in self.shapes.value]) ) return None -class WorkflowFields(NapariFieldGroup, FieldGroup): +@magicclass(name="4. Workflow") +class WorkflowFields(NapariFieldGroup): """ Handles the workflow related parameters """ @@ -427,8 +451,8 @@ class WorkflowFields(NapariFieldGroup, FieldGroup): @fields_enabled.connect @enable_if([workflow_source]) - def _enable_workflow(self) -> bool: - return self.fields_enabled.value + def _enable_workflow(enabled: bool) -> bool: + return enabled @fields_enabled.connect @enable_if([workflow_path]) @@ -445,8 +469,8 @@ def _make_model(self) -> Optional[Workflow]: else: return workflow_from_path(self.workflow_path.value) -# @magicclass(name="5. Output") -class OutputFields(NapariFieldGroup, FieldGroup): +@magicclass(name="5. Output") +class OutputFields(NapariFieldGroup): set_logging = field(Log_Levels.INFO).with_options(label="Logging Level") time_range = field(Tuple[int, int]).with_options( label="Time Export Range", diff --git a/plugin/napari_lattice/utils.py b/plugin/napari_lattice/utils.py index 25778c7..fd7594a 100644 --- a/plugin/napari_lattice/utils.py +++ b/plugin/napari_lattice/utils.py @@ -19,5 +19,7 @@ def get_layers(type: Type[LayerType]) -> Sequence[LayerType]: For example, if you pass `napari.layers.Image`, it will return a list of Image layers """ - viewer = get_viewer() + viewer = current_viewer() + if viewer is None: + return [] return [layer for layer in viewer.layers if isinstance(layer, type)] diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 3779839..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,10 +0,0 @@ -[build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta" - -[tool.black] -line-length = 79 - -[tool.isort] -profile = "black" -line_length = 79 From a6a2f0157ce43b2729e3fcdecbc131611164b683 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 22 Sep 2023 18:25:06 +1000 Subject: [PATCH 050/147] Missing test files --- core/lls_core/models/lattice_data.py | 4 +- core/tests/conftest.py | 63 ++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 core/tests/conftest.py diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 962d59d..bf3095a 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -178,11 +178,11 @@ def default_save_name(cls, values: dict): def parse_workflow(cls, v: Any): # Load the workflow from disk if it was provided as a path from lls_core.types import is_pathlike - from napari_workflows._io_yaml_v1 import load_workflow + from lls_core.workflow import workflow_from_path from os import fspath if is_pathlike(v): - return load_workflow(fspath(v)) + return workflow_from_path(fspath(v)) return v @validator("time_range", pre=True, always=True) diff --git a/core/tests/conftest.py b/core/tests/conftest.py new file mode 100644 index 0000000..b69022f --- /dev/null +++ b/core/tests/conftest.py @@ -0,0 +1,63 @@ +from typer.testing import CliRunner +import pytest +from skimage.io import imsave +import numpy as np +from pathlib import Path +import pyclesperanto_prototype as cle +import tempfile +from numpy.typing import NDArray + +from napari_workflows import Workflow +from napari_workflows._io_yaml_v1 import save_workflow + +@pytest.fixture +def runner() -> CliRunner: + return CliRunner() + +@pytest.fixture +def workflow() -> Workflow: + # Instantiate segmentation workflow + image_seg_workflow = Workflow() + image_seg_workflow.set("gaussian", cle.gaussian_blur, "input", sigma_x=1, sigma_y=1, sigma_z=1) + image_seg_workflow.set("binarisation", cle.threshold, "gaussian", constant=0.5) + image_seg_workflow.set("labeling", cle.connected_components_labeling_box, "binarisation") + return image_seg_workflow + +@pytest.fixture +def test_image() -> NDArray[np.float64]: + raw = np.zeros((5, 5, 5)) + raw[2, 2, 2] = 10 + return raw + +@pytest.fixture +def workflow_config(workflow: Workflow, test_image: NDArray): + # Create a config file + yield { + "image": test_image, + "workflow": workflow, + } + +@pytest.fixture +def workflow_config_cli(workflow: Workflow, test_image: NDArray): + with tempfile.TemporaryDirectory() as tempdir_str: + tempdir = Path(tempdir_str) + input = tempdir / "raw.tiff" + output = tempdir / "output" + output.mkdir(parents=True) + workflow_path = tempdir / "workflow.json" + save_workflow(str(workflow_path), workflow) + + # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) + imsave(input, test_image) + assert input.exists() + + # Create a config file + yield { + key: str(val) + for key, val in + { + "image": input, + "save_dir": output, + "workflow": workflow_path, + }.items() + } From e27b19ef2faf8e4920011945eba9e7bb5141c8c8 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 22 Sep 2023 18:27:48 +1000 Subject: [PATCH 051/147] More missing files, disable cropping test until we have ROIs --- core/lls_core/cropping.py | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 core/lls_core/cropping.py diff --git a/core/lls_core/cropping.py b/core/lls_core/cropping.py new file mode 100644 index 0000000..e4b973a --- /dev/null +++ b/core/lls_core/cropping.py @@ -0,0 +1,61 @@ +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from lls_core.types import PathLike + from numpy.typing import NDArray + +def read_roi_array(roi: PathLike) -> NDArray: + from read_roi import read_roi_file + from numpy import array + return array(read_roi_file(roi)) + +def read_imagej_roi(roi_path: PathLike): + """Read an ImageJ ROI zip file so it loaded into napari shapes layer + If non rectangular ROI, will convert into a rectangle based on extreme points + Args: + roi_zip_path (zip file): ImageJ ROI zip file + + Returns: + list: List of ROIs + """ + from pathlib import Path + from os import fspath + from read_roi import read_roi_file, read_roi_zip + + roi_path = Path(fspath(roi_path)) + + # handle reading single roi or collection of rois in zip file + if roi_path.suffix == ".zip": + ij_roi = read_roi_zip(roi_path) + elif roi_path.suffix == ".roi": + ij_roi = read_roi_file(roi_path) + else: + raise Exception("ImageJ ROI file needs to be a zip/roi file") + + if ij_roi is None: + raise Exception("Failed reading ROI file") + + # initialise list of rois + roi_list = [] + + # Read through each roi and create a list so that it matches the organisation of the shapes from napari shapes layer + for value in ij_roi.values(): + if value['type'] in ('oval', 'rectangle'): + width = value['width'] + height = value['height'] + left = value['left'] + top = value['top'] + roi = [[top, left], [top, left+width], [top+height, left+width], [top+height, left]] + roi_list.append(roi) + elif value['type'] in ('polygon', 'freehand'): + left = min(value['x']) + top = min(value['y']) + right = max(value['x']) + bottom = max(value['y']) + roi = [[top, left], [top, right], [bottom, right], [bottom, left]] + roi_list.append(roi) + else: + print(f"Cannot read ROI {value}. Recognised as type {value['type']}") + + return roi_list From 02baf7b26061b04a72c02a1a053fd8edd1f85a78 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 22 Sep 2023 19:21:06 +1000 Subject: [PATCH 052/147] Add missing params.py --- core/tests/params.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 core/tests/params.py diff --git a/core/tests/params.py b/core/tests/params.py new file mode 100644 index 0000000..e49711f --- /dev/null +++ b/core/tests/params.py @@ -0,0 +1,34 @@ +import pytest +import json +import yaml + +from lls_core.models.output import SaveFileType + +inputs = pytest.mark.parametrize( + ["path"], [ + ("RBC_tiny.czi", ), + ("RBC_lattice.tif", ), + ("LLS7_t1_ch1.czi", ), + ("LLS7_t1_ch3.czi", ), + ("LLS7_t2_ch1.czi", ), + ("LLS7_t2_ch3.czi", ), + ("multich_multi_time.tif", ), +]) +parameterized = pytest.mark.parametrize("args", [ + {"skew": "X"}, + {"skew": "Y"}, + {"angle": 30}, + {"angle": 90}, + {"physical_pixel_sizes": (1, 1, 1)}, + {"save_type": SaveFileType.h5}, + {"save_type": SaveFileType.tiff}, + + # # Cropping enabled + # {"crop": {"roi_list": []}}core/lls_core/cropping.py, +]) + +# Allows parameterisation over two serialization formats +config_types = pytest.mark.parametrize(["save_func", "cli_param"], [ + (json.dump, "--json-config"), + (yaml.safe_dump, "--yaml-config") +]) From b593ecdd9d3caaf5324d93cf29bc851048d52aba Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 22 Sep 2023 19:53:05 +1000 Subject: [PATCH 053/147] Fix test_process_workflow test suite --- core/lls_core/models/lattice_data.py | 4 ++-- core/tests/test_lattice_data.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index bf3095a..ad1bd45 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -179,10 +179,10 @@ def parse_workflow(cls, v: Any): # Load the workflow from disk if it was provided as a path from lls_core.types import is_pathlike from lls_core.workflow import workflow_from_path - from os import fspath + from pathlib import Path if is_pathlike(v): - return workflow_from_path(fspath(v)) + return workflow_from_path(Path(v)) return v @validator("time_range", pre=True, always=True) diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index e05cf6d..104bef2 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -5,6 +5,7 @@ from importlib_resources import as_file import tempfile from pathlib import Path +from napari_workflows import Workflow from .params import inputs, parameterized @@ -48,11 +49,11 @@ def test_process_deconvolution(args: dict, background: Any): assert slice.data.ndim == 3 @parameterized -def test_process_workflow(args: dict): +def test_process_workflow(args: dict, workflow: Workflow): root = Path(__file__).parent / "data" for slice in LatticeData.parse_obj({ "image": root / "raw.tif", - "workflow": root / "raw.tif", + "workflow": workflow, **args }).process().slices: assert slice.data.ndim == 3 From 6b856c45262ebd132a70737442fcb92acee96238 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 25 Sep 2023 12:36:59 +1000 Subject: [PATCH 054/147] Removed plugin preview --- .github/workflows/plugin_preview.yml | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 .github/workflows/plugin_preview.yml diff --git a/.github/workflows/plugin_preview.yml b/.github/workflows/plugin_preview.yml deleted file mode 100644 index bfd5c9e..0000000 --- a/.github/workflows/plugin_preview.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: napari hub Preview Page # we use this name to find your preview page artifact, so don't change it! -# For more info on this action, see https://github.com/chanzuckerberg/napari-hub-preview-action/blob/main/action.yml - -on: - pull_request: - branches: - - "**" - paths: - - "**/README.md" - -jobs: - preview-page: - name: Preview Page Deploy - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v2 - - - name: napari hub Preview Page Builder - uses: chanzuckerberg/napari-hub-preview-action@v0.1.5 - with: - hub-ref: main From 4202b580850e45dff85b8cd21df02d07649f4c27 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 25 Sep 2023 17:15:26 +1000 Subject: [PATCH 055/147] Clean up GUI code, and fix tests --- plugin/napari_lattice/dock_widget.py | 73 +++----------- plugin/napari_lattice/fields.py | 43 ++++---- plugin/napari_lattice/napari.yaml | 2 +- plugin/napari_lattice/ui_core.py | 143 --------------------------- plugin/pyproject.toml | 7 +- plugin/tests/test_dock_widget.py | 17 +--- 6 files changed, 40 insertions(+), 245 deletions(-) delete mode 100644 plugin/napari_lattice/ui_core.py diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 4debcc0..3d69774 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -1,15 +1,10 @@ # Enable Logging import logging -from pathlib import Path -from typing import Union import numpy as np from lls_core.models.lattice_data import LatticeData -from lls_core.workflow import _import_workflow_modules -from magicclass import MagicTemplate, field, magicclass, set_options +from magicclass import MagicTemplate, field, magicclass, set_options, vfield from magicclass.wrappers import set_design -from napari import Viewer -from napari.layers import Shapes from napari_lattice.fields import ( CroppingFields, DeconvolutionFields, @@ -18,8 +13,6 @@ WorkflowFields, ) from napari_lattice.icons import GREY -from napari_workflows import Workflow, WorkflowManager -from napari_workflows._io_yaml_v1 import load_workflow from qtpy.QtCore import Qt from qtpy.QtGui import QIcon from qtpy.QtWidgets import QTabWidget @@ -27,21 +20,11 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -class LlszTemplate(MagicTemplate): - @property - def llsz_parent(self) -> "LLSZWidget": - return self.find_ancestor(LLSZWidget) - -def parent_viewer(mc: MagicTemplate) -> Viewer: - viewer = mc.parent_viewer - if viewer is None: - raise Exception("This function can only be used when inside of a Napari viewer") - return mc.parent_viewer - @magicclass(widget_type="split") -class LLSZWidget(LlszTemplate): - open_file: bool = False - shapes_layer: Shapes +class LLSZWidget(MagicTemplate): + def __post_init__(self): + # aligning collapsible widgets at the top instead of having them centered vertically + self._widget._layout.setAlignment(Qt.AlignTop) def _check_validity(self) -> bool: """ @@ -73,14 +56,14 @@ def _make_model(self) -> LatticeData: ) @magicclass(widget_type="split") - class LlszMenu(LlszTemplate): + class LlszMenu(MagicTemplate): main_heading = field("

      Napari Lattice: Visualization & Analysis

      ", widget_type="Label") heading1 = field("Drag and drop an image file onto napari.", widget_type="Label") # Tabbed Widget container to house all the widgets @magicclass(widget_type="tabbed", name="Functions", labels=False) - class WidgetContainer(LlszTemplate): + class WidgetContainer(MagicTemplate): def __post_init__(self): tab_widget: QTabWidget= self._widget._tab_widget @@ -91,11 +74,11 @@ def __post_init__(self): for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: field._validate() - deskew_fields = DeskewFields() - deconv_fields = DeconvolutionFields() - cropping_fields = CroppingFields() - workflow_fields = WorkflowFields() - output_fields = OutputFields() + deskew_fields = vfield(DeskewFields) + deconv_fields = vfield(DeconvolutionFields) + cropping_fields = vfield(CroppingFields) + workflow_fields = vfield(WorkflowFields) + output_fields = vfield(OutputFields) @set_options(header=dict(widget_type="Label", label="

      Preview Deskew

      "), time=dict(label="Time:", max=2**15), @@ -125,35 +108,3 @@ def preview(self, header:str, time: int, channel: int): def save(self): lattice = self._make_model() lattice.process().save_image() - -def _napari_lattice_widget_wrapper() -> LLSZWidget: - # split widget type enables a resizable widget - #max_height = 50 - # Important to have this or napari won't recognize the classes and magicclass qidgets - widget = LLSZWidget() - # aligning collapsible widgets at the top instead of having them centered vertically - widget._widget._layout.setAlignment(Qt.AlignTop) - - # widget._widget._layout.setWidgetResizable(True) - return widget - -def get_workflow(source: Union[Path, Viewer]) -> Workflow: - """ - Gets a user defined workflow object, either from a viewer or from a file - - Args: - source: Either the path to a workflow file, or a Napari viewer from which to extract the workflow - """ - if isinstance(source, Viewer): - # installs the workflow to napari - user_workflow = WorkflowManager.install(source).workflow - logger.info("Workflow installed") - else: - _import_workflow_modules(source) - user_workflow = load_workflow(str(source)) - - if not isinstance(user_workflow, Workflow): - raise Exception("Workflow file is not a napari workflow object. Check file! You can use workflow inspector if needed") - - logger.info(f"Workflow loaded: {user_workflow}") - return user_workflow diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 1185468..363fc33 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -86,8 +86,11 @@ def enable_field(field: MagicField, enabled: bool = True) -> None: for real_field in field._guis.values(): if not isinstance(real_field, Widget): raise Exception("Define your fields with field() not vfield()!") - real_field.visible = enabled - real_field.enabled = enabled + try: + real_field.visible = enabled + real_field.enabled = enabled + except RuntimeError: + pass EnabledHandlerType = TypeVar("EnabledHandlerType") @@ -136,17 +139,8 @@ class StackAlong(StrEnum): TIME = "Time" class NapariFieldGroup: - # This implementation is a bit ugly. This is a mixin that can only be used on a `FieldGroup`. - # However, it can't inherit from FieldGroup because then the metaclass would look for fields in this - # class definition, find none, and then make an empty GUI page when this is rendered. - # It also can't inherit from a FieldGroup-like Protocol as mypy suggests for mixin classes - # (https://mypy.readthedocs.io/en/latest/more_types.html#mixin-classes) because it doesn't - # implement the attributes of a FieldGroup. Ideally this could be a Protocol subclass as well - # to make it remain abstract, but the Protocol metaclass interferes with the FieldGroup metaclass - def __init__(self, *args: Any, **kwargs: Any): - super().__init__(*args, **kwargs) + def __post_init__(self): self = cast(FieldGroup, self) - # self._widget._mgui_bind_parent_change_callback(self._validate) self.changed.connect(self._validate, unique=False) # Style the error label. @@ -156,12 +150,9 @@ def __init__(self, *args: Any, **kwargs: Any): if isinstance(errors, QLabel): errors.setStyleSheet("color: red;") errors.setWordWrap(True) - # super(Container, self).connect(self._validate) - # self.connect(self._validate) - # def _on_value_change(self, *args, **kwargs) -> None: - # super()._on_value_change(*args, **kwargs) - # self._validate() + from qtpy.QtCore import Qt + self._widget._layout.setAlignment(Qt.AlignTop) def _get_parent_tab_widget(self: Any) -> QTabWidget: return self.parent.parentWidget() @@ -290,15 +281,17 @@ def _img_changed(self) -> None: @pixel_sizes_source.connect @enable_if([pixel_sizes]) + @staticmethod def _hide_pixel_sizes(pixel_sizes_source: str): # Hide the "Pixel Sizes" option unless the user specifies manual pixel size source return pixel_sizes_source == PixelSizeSource.Manual @img_layer.connect @enable_if([stack_along]) - def _hide_stack_along(img_layer): + @staticmethod + def _hide_stack_along(img_layer: List[Image]): # Hide the "Stack Along" option if we only have one image - return len(img_layer.value) > 1 + return len(img_layer) > 1 def _get_kwargs(self) -> DeskewKwargs: """ @@ -353,6 +346,7 @@ class DeconvolutionFields(NapariFieldGroup): @enable_if( [background_custom] ) + @staticmethod def _enable_custom_background(background: str) -> bool: return background == BackgroundSource.Custom @@ -365,7 +359,8 @@ def _enable_custom_background(background: str) -> bool: background ] ) - def _enable_fields(enabled) -> bool: + @staticmethod + def _enable_fields(enabled: bool) -> bool: return enabled def _make_model(self) -> Optional[DeconvolutionParams]: @@ -451,17 +446,17 @@ class WorkflowFields(NapariFieldGroup): @fields_enabled.connect @enable_if([workflow_source]) + @staticmethod def _enable_workflow(enabled: bool) -> bool: return enabled @fields_enabled.connect @enable_if([workflow_path]) - def _workflow_path(self) -> bool: - return self.workflow_source.value == WorkflowSource.CustomPath + @staticmethod + def _workflow_path(workflow_source: WorkflowSource) -> bool: + return workflow_source == WorkflowSource.CustomPath def _make_model(self) -> Optional[Workflow]: - from lls_core.workflow import _import_workflow_modules - from napari_workflows._io_yaml_v1 import load_workflow if not self.fields_enabled.value: return None if self.workflow_source.value == WorkflowSource.ActiveWorkflow: diff --git a/plugin/napari_lattice/napari.yaml b/plugin/napari_lattice/napari.yaml index b83b0f0..2ec0f9a 100644 --- a/plugin/napari_lattice/napari.yaml +++ b/plugin/napari_lattice/napari.yaml @@ -4,7 +4,7 @@ contributions: commands: - id: napari-lattice.dock_widget title: Create napari_lattice widget - python_name: napari_lattice.dock_widget:_napari_lattice_widget_wrapper + python_name: napari_lattice.dock_widget:LLSZWidget # ~~ Reader ~~ - id: napari-lattice.get_reader diff --git a/plugin/napari_lattice/ui_core.py b/plugin/napari_lattice/ui_core.py deleted file mode 100644 index 1e9af07..0000000 --- a/plugin/napari_lattice/ui_core.py +++ /dev/null @@ -1,143 +0,0 @@ -from __future__ import annotations - -import numpy as np -from pathlib import Path -from typing import TYPE_CHECKING - -import pyclesperanto_prototype as cle -from lls_core.utils import check_dimensions -from lls_core.deconvolution import pycuda_decon, skimage_decon -from lls_core import config, DeskewDirection, DeconvolutionChoice -from lls_core .io import save_img - -if TYPE_CHECKING: - from napari.types import ImageData - -# Enable Logging -import logging -logger = logging.getLogger(__name__) -# inherit log level from config -logger.setLevel(config.log_level) - -def _Preview(LLSZWidget, - self_class, - time: int, - channel: int, - img_data: ImageData): - - logger.info("Previewing deskewed channel and time") - assert img_data.size, "No image open or selected" - assert time < LLSZWidget.LlszMenu.lattice.time, "Time is out of range" - assert channel < LLSZWidget.LlszMenu.lattice.channels, "Channel is out of range" - assert LLSZWidget.LlszMenu.lattice.skew in DeskewDirection, f"Skew direction not recognised. Got {LLSZWidget.LlszMenu.lattice.skew}" - - vol = LLSZWidget.LlszMenu.lattice.data - vol_zyx = np.array(vol[time, channel, :, :, :]) - - # apply deconvolution if checked - if LLSZWidget.LlszMenu.deconvolution.value: - print( - f"Deskewing for Time:{time} and Channel: {channel} with deconvolution") - psf = LLSZWidget.LlszMenu.lattice.psf[channel] - if LLSZWidget.LlszMenu.lattice.decon_processing == DeconvolutionChoice.cuda_gpu: - decon_data = pycuda_decon(image=vol_zyx, - psf=psf, - dzdata=LLSZWidget.LlszMenu.lattice.dz, - dxdata=LLSZWidget.LlszMenu.lattice.dx, - dzpsf=LLSZWidget.LlszMenu.lattice.dz, - dxpsf=LLSZWidget.LlszMenu.lattice.dx) - # pycuda_decon(image,otf_path,dzdata,dxdata,dzpsf,dxpsf) - else: - decon_data = skimage_decon( - vol_zyx=vol_zyx, psf=psf, num_iter=10, clip=False, filter_epsilon=0, boundary='nearest') - - deskew_final = LLSZWidget.LlszMenu.deskew_func(decon_data, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - linear_interpolation=True).astype(vol.dtype) - else: - logger.info(f"Deskewing for Time:{time} and Channel: {channel}") - deskew_final = LLSZWidget.LlszMenu.deskew_func(vol_zyx, - angle_in_degrees=LLSZWidget.LlszMenu.angle_value, - voxel_size_x=LLSZWidget.LlszMenu.lattice.dx, - voxel_size_y=LLSZWidget.LlszMenu.lattice.dy, - voxel_size_z=LLSZWidget.LlszMenu.lattice.dz, - linear_interpolation=True).astype(vol.dtype) - - # if getting an error LogicError: clSetKernelArg failed: #INVALID_ARG_SIZE - when processing arg#13 (1-based) - # make sure array is pulled from GPU - - deskew_final = cle.pull(deskew_final) - # TODO: Use dask - # if LLSZWidget.LlszMenu.dask: - #logger.info(f"Using CPU for deskewing") - # use cle library for affine transforms, but use dask and scipy - # deskew_final = deskew_final.compute() - - max_proj_deskew = cle.maximum_z_projection(deskew_final) - - # add channel and time information to the name - suffix_name = "_c" + str(channel) + "_t" + str(time) - scale = (LLSZWidget.LlszMenu.lattice.new_dz, - LLSZWidget.LlszMenu.lattice.dy, LLSZWidget.LlszMenu.lattice.dx) - # TODO:adding img of difff scales change dim slider - self_class.parent_viewer.add_image( - deskew_final, name="Deskewed image" + suffix_name, scale=scale) - self_class.parent_viewer.add_image( - max_proj_deskew, name="Deskew_MIP", scale=scale[1:3]) - self_class.parent_viewer.layers[0].visible = False - - logger.info(f"Preview: Deskewing complete") - return - - -def _Deskew_Save(LLSZWidget, - time_start: int, - time_end: int, - ch_start: int, - ch_end: int, - save_as_type, - save_path: Path): - - assert LLSZWidget.LlszMenu.open_file, "Image not initialised" - check_dimensions(time_start, time_end, ch_start, ch_end, - LLSZWidget.LlszMenu.lattice.channels, LLSZWidget.LlszMenu.lattice.time) - #time_range = range(time_start, time_end) - #channel_range = range(ch_start, ch_end) - angle = LLSZWidget.LlszMenu.lattice.angle - dx = LLSZWidget.LlszMenu.lattice.dx - dy = LLSZWidget.LlszMenu.lattice.dy - dz = LLSZWidget.LlszMenu.lattice.dz - - # Convert path to string - #save_path = save_path.__str__() - - # get the image data as dask array - img_data = LLSZWidget.LlszMenu.lattice.data - - # pass arguments for save tiff, callable and function arguments - save_img(vol=img_data, - func=LLSZWidget.LlszMenu.deskew_func, - time_start=time_start, - time_end=time_end, - channel_start=ch_start, - channel_end=ch_end, - save_file_type=save_as_type, - save_path=save_path, - save_name=LLSZWidget.LlszMenu.lattice.save_name, - dx=dx, - dy=dy, - dz=dz, - angle=angle, - angle_in_degrees=angle, - voxel_size_x=dx, - voxel_size_y=dy, - voxel_size_z=dz, - linear_interpolation=True, - LLSZWidget=LLSZWidget) - - print("Deskewing and Saving Complete -> ", save_path) - return - diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index ddaf21ab..fa6d28c 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -36,16 +36,17 @@ classifiers = [ requires-python = ">=3.8" dependencies = [ "aicsimageio>=4.6.3", - "dask", "dask[distributed]", # This isn't used directly, but we need to pin this version "fsspec>=2022.8.2", "importlib_resources", - # We need this Python 3.8 fix: https://github.com/hanjinliu/magic-class/pull/108 "lls_core", + # We need this Python 3.8 fix: https://github.com/hanjinliu/magic-class/pull/108 "magic-class>=0.7.5", "magicgui", - "napari-aicsimageio>=0.7.2", + # Currently commented out to avoid installation issues, although + # This can be reinstated once https://github.com/pypa/pip/pull/12095 is merged + # "napari-aicsimageio>=0.7.2", "napari-spreadsheet", "napari-workflow-inspector", "napari-workflows>=0.2.8", diff --git a/plugin/tests/test_dock_widget.py b/plugin/tests/test_dock_widget.py index 67d4ccf..20d6947 100644 --- a/plugin/tests/test_dock_widget.py +++ b/plugin/tests/test_dock_widget.py @@ -1,5 +1,5 @@ from __future__ import annotations -from napari_lattice.dock_widget import _napari_lattice_widget_wrapper +from napari_lattice.dock_widget import LLSZWidget import numpy as np from typing import Callable, TYPE_CHECKING from magicclass.testing import check_function_gui_buildable, FunctionGuiTester @@ -37,18 +37,9 @@ def test_dock_widget(make_napari_viewer: Callable[[], Viewer]): viewer.add_image(np.random.random((100, 100))) # Test if napari-lattice widget can be created in napari - gui = _napari_lattice_widget_wrapper() - viewer.window.add_dock_widget(gui) + viewer.window.add_dock_widget(LLSZWidget()) def test_check_buildable(): - widget = _napari_lattice_widget_wrapper() - check_function_gui_buildable(widget) - -def test_plugin_initialize(make_napari_viewer: Callable[[], Viewer]): - ui = _napari_lattice_widget_wrapper() - viewer = make_napari_viewer() - viewer.window.add_dock_widget(ui) - image = Image(np.random.random((100, 100, 100, 100))) + ui = LLSZWidget() set_debug(ui) - tester = FunctionGuiTester(ui.LlszMenu.Choose_Image_Layer) - tester.call(img_layer=image, last_dimension_channel="time") + check_function_gui_buildable(ui) From 2e750d19648c90a446b701f47a708e1944de74bf Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 25 Sep 2023 18:46:33 +1000 Subject: [PATCH 056/147] Fix ROI handling, add ROI class, fix plugin linting --- core/lls_core/cropping.py | 32 +++++++++++++---------- core/lls_core/models/crop.py | 26 +++++++++++++------ core/lls_core/models/lattice_data.py | 39 ++++++++++++++++++---------- core/pyproject.toml | 1 + core/tests/conftest.py | 10 +++---- core/tests/test_lattice_data.py | 17 ++++++++++-- plugin/pyproject.toml | 5 ++-- 7 files changed, 85 insertions(+), 45 deletions(-) diff --git a/core/lls_core/cropping.py b/core/lls_core/cropping.py index e4b973a..f2b30d8 100644 --- a/core/lls_core/cropping.py +++ b/core/lls_core/cropping.py @@ -1,16 +1,22 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, NamedTuple, Tuple, List if TYPE_CHECKING: from lls_core.types import PathLike from numpy.typing import NDArray +class Roi(NamedTuple): + top_left: Tuple[int, int] + top_right: Tuple[int, int] + bottom_left: Tuple[int, int] + bottom_right: Tuple[int, int] + def read_roi_array(roi: PathLike) -> NDArray: from read_roi import read_roi_file from numpy import array - return array(read_roi_file(roi)) + return array(read_roi_file(str(roi))) -def read_imagej_roi(roi_path: PathLike): +def read_imagej_roi(roi_path: PathLike) -> List[Roi]: """Read an ImageJ ROI zip file so it loaded into napari shapes layer If non rectangular ROI, will convert into a rectangle based on extreme points Args: @@ -42,18 +48,18 @@ def read_imagej_roi(roi_path: PathLike): # Read through each roi and create a list so that it matches the organisation of the shapes from napari shapes layer for value in ij_roi.values(): if value['type'] in ('oval', 'rectangle'): - width = value['width'] - height = value['height'] - left = value['left'] - top = value['top'] - roi = [[top, left], [top, left+width], [top+height, left+width], [top+height, left]] + width = int(value['width']) + height = int(value['height']) + left = int(value['left']) + top = int(value['top']) + roi = Roi((top, left), (top, left+width), (top+height, left+width), (top+height, left)) roi_list.append(roi) elif value['type'] in ('polygon', 'freehand'): - left = min(value['x']) - top = min(value['y']) - right = max(value['x']) - bottom = max(value['y']) - roi = [[top, left], [top, right], [bottom, right], [bottom, left]] + left = min(int(it) for it in value['x']) + top = min(int(it) for it in value['y']) + right = max(int(it) for it in value['x']) + bottom = max(int(it) for it in value['y']) + roi = Roi((top, left), (top, right), (bottom, right), (bottom, left)) roi_list.append(roi) else: print(f"Cannot read ROI {value}. Recognised as type {value['type']}") diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index f6db276..b2741f5 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -1,14 +1,14 @@ from typing import List, Tuple, Any from pydantic import Field, NonNegativeInt, validator -from xarray import DataArray from lls_core.models.utils import FieldAccessMixin +from lls_core.cropping import Roi class CropParams(FieldAccessMixin): """ Parameters for the optional cropping step """ - roi_list: List[DataArray] = Field( + roi_list: List[Roi] = Field( description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex.", default = [] ) @@ -17,11 +17,21 @@ class CropParams(FieldAccessMixin): description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." ) - @validator("roi_list", each_item=True) - def read_roi(cls, v: Any): + @validator("roi_list", pre=True) + def read_roi(cls, v: Any) -> List[Roi]: from lls_core.types import is_pathlike - from lls_core.cropping import read_roi_array - # Converts the ROI from a path to an array + from lls_core.cropping import read_imagej_roi + # Allow a single path if is_pathlike(v): - return read_roi_array(v) - return v + v = [v] + + rois: List[Roi] = [] + for item in v: + if is_pathlike(item): + rois += read_imagej_roi(item) + elif isinstance(item, Roi): + rois.append(item) + else: + raise ValueError(f"{item} cannot be intepreted as an ROI") + + return rois diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index ad1bd45..b34a214 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -185,6 +185,20 @@ def parse_workflow(cls, v: Any): return workflow_from_path(Path(v)) return v + @validator("crop") + def default_z_range(cls, v: CropParams, values: dict): + # If any part of the z range is missing, assume the user wants all Z indices + with ignore_keyerror(): + default_start = 0 + default_end = values["image"].sizes["Z"] + if v.z_range is None: + v.z_range = (default_start, default_end) + if v.z_range[0] is None: + v.z_range[0] = default_start + if v.z_range[1] is None: + v.z_range[1] = default_end + return v + @validator("time_range", pre=True, always=True) def parse_time_range(cls, v: Any, values: dict) -> Any: """ @@ -371,7 +385,8 @@ def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, chann @property def deskewed_volume(self) -> DaskArray: - return da.zeros(self.deskew_vol_shape) + from dask.array import zeros + return zeros(self.deskew_vol_shape) def _process_crop(self) -> Iterable[ProcessedVolume]: """ @@ -383,7 +398,7 @@ def _process_crop(self) -> Iterable[ProcessedVolume]: # We have an extra level of iteration for the crop path: iterating over each ROI for roi_index, roi in enumerate(tqdm(self.crop.roi_list, desc="ROI:", position=0)): # pass arguments for save tiff, callable and function arguments - logger.info("Processing ROI ", roi_index) + logger.info(f"Processing ROI {roi_index}") deconv_args: dict[Any, Any] = {} if self.deconvolution is not None: @@ -393,14 +408,14 @@ def _process_crop(self) -> Iterable[ProcessedVolume]: decon_processing=self.deconvolution.decon_processing ) - for time_idx, time, ch_idx, ch, data in self.iter_slices(): - yield ProcessedVolume( - data = crop_volume_deskew( - original_volume=data, + for slice in self.iter_slices(): + yield slice.copy_with_data( + crop_volume_deskew( + original_volume=slice.data, deconvolution=self.deconv_enabled, get_deskew_and_decon=False, debug=False, - roi_shape=roi, + roi_shape=list(roi), linear_interpolation=True, voxel_size_x=self.dx, voxel_size_y=self.dy, @@ -410,13 +425,9 @@ def _process_crop(self) -> Iterable[ProcessedVolume]: z_start=self.crop.z_range[0], z_end=self.crop.z_range[1], **deconv_args - ), - channel=ch, - channel_index=ch_idx, - time=time, - time_index=time_idx, - roi_index=roi_index - ) + ) + ) + def _process_non_crop(self) -> Iterable[ProcessedVolume]: """ Yields processed image slices without cropping diff --git a/core/pyproject.toml b/core/pyproject.toml index d3d5282..5e98645 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -111,6 +111,7 @@ reportUntypedFunctionDecorator = false reportMissingTypeArgument = false reportPrivateUsage = false reportPrivateImportUsage = false +reportUnnecessaryComparison = false [tool.fawltydeps] ignore_unused = [ diff --git a/core/tests/conftest.py b/core/tests/conftest.py index b69022f..9bc3a8a 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -31,11 +31,11 @@ def test_image() -> NDArray[np.float64]: @pytest.fixture def workflow_config(workflow: Workflow, test_image: NDArray): - # Create a config file - yield { - "image": test_image, - "workflow": workflow, - } + # Create a config file + yield { + "image": test_image, + "workflow": workflow, + } @pytest.fixture def workflow_config_cli(workflow: Workflow, test_image: NDArray): diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index 104bef2..8af6891 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -9,6 +9,8 @@ from .params import inputs, parameterized +root = Path(__file__).parent / "data" + def open_psf(name: str): with as_file(resources / "psfs" / "zeiss_simulated" / name) as path: return path @@ -37,7 +39,6 @@ def test_save(path: str, args: dict): @pytest.mark.parametrize(["background"], [(1, ), ("auto",), ("second_last",)]) @parameterized def test_process_deconvolution(args: dict, background: Any): - root = Path(__file__).parent / "data" for slice in LatticeData.parse_obj({ "image": root / "raw.tif", "deconvolution": { @@ -50,10 +51,22 @@ def test_process_deconvolution(args: dict, background: Any): @parameterized def test_process_workflow(args: dict, workflow: Workflow): - root = Path(__file__).parent / "data" for slice in LatticeData.parse_obj({ "image": root / "raw.tif", "workflow": workflow, **args }).process().slices: assert slice.data.ndim == 3 + +@parameterized +def test_process_crop(args: dict, workflow: Workflow): + with as_file(resources / "RBC_tiny.czi") as lattice_path: + rois = root / "crop" / "two_rois.zip" + for slice in LatticeData.parse_obj({ + "image": lattice_path, + "crop": { + "roi_list": [rois] + }, + **args + }).process().slices: + assert slice.data.ndim == 3 diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index fa6d28c..d91ad33 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -53,14 +53,11 @@ dependencies = [ "napari>=0.4.11", "npy2bdv", "numpy", - "pandas", "psutil", "pyclesperanto_prototype>=0.20.0", "pydantic", "qtpy", - "tqdm", "typing_extensions>=4.7.0", - "pyyaml", "StrEnum", "xarray" ] @@ -97,6 +94,7 @@ ignore_unused = [ # These napari plugins are needed to use the plugin, but aren't imported directly "napari-aicsimageio", "napari-workflow-inspector", + "napari-spreadsheet", # This is pinned but unused "fsspec", @@ -118,3 +116,4 @@ reportUntypedFunctionDecorator = false reportMissingTypeArgument = false reportPrivateUsage = false reportPrivateImportUsage = false +reportUnnecessaryComparison = false From cba0eb372fe49adf0284515ae518be7de70f660b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 25 Sep 2023 19:21:36 +1000 Subject: [PATCH 057/147] Fix for when crop is None --- core/lls_core/models/lattice_data.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index b34a214..20a9f4e 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -188,6 +188,8 @@ def parse_workflow(cls, v: Any): @validator("crop") def default_z_range(cls, v: CropParams, values: dict): # If any part of the z range is missing, assume the user wants all Z indices + if v is None: + return v with ignore_keyerror(): default_start = 0 default_end = values["image"].sizes["Z"] From f61f887b9547bd9c23b6bdea31bb0aef18c510de Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 25 Sep 2023 19:45:51 +1000 Subject: [PATCH 058/147] Add missing ROI test data --- core/tests/data/crop/roi_1.tif | Bin 0 -> 429522 bytes core/tests/data/crop/roi_2.tif | Bin 0 -> 355919 bytes core/tests/data/crop/two_rois.zip | Bin 0 -> 320 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/tests/data/crop/roi_1.tif create mode 100644 core/tests/data/crop/roi_2.tif create mode 100644 core/tests/data/crop/two_rois.zip diff --git a/core/tests/data/crop/roi_1.tif b/core/tests/data/crop/roi_1.tif new file mode 100644 index 0000000000000000000000000000000000000000..b702ebe03d7817cccfd1a5562808324d185da4e6 GIT binary patch literal 429522 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%0A06J$f=oPQd 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
      Va2PX*=pC3iw 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�ywAQFV%D*5!s{LeA37cy+O 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
      jGw@ZVq^uP`H~hD=nr}}9h+Tr^V&JmN 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 literal 0 HcmV?d00001 diff --git a/core/tests/data/crop/roi_2.tif b/core/tests/data/crop/roi_2.tif new file mode 100644 index 0000000000000000000000000000000000000000..d31ee2ca1ea339404832b5515ed840d576d4a09f GIT binary patch literal 355919 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

      (#%#3t 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 T#|@;u~WbuiKU{Q8 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 z z(8K4Vj?GXS2F9*`A9?; zxP7A?{riv%r{X-wLb}W)F~dFFdziruLW*`h4dsZ)Y@s^ez8ZXSn*ULq8DBA`z0F<< zIG)f^v|#3`q!dPKOMD=MroR3{?23@NtW_-ZhmnAs#Yjq`l8!=s7nGWPRt+WeKvWZx zH;N3;wi-FZuhtz`8bVpNAE1h$X{~>?PXT1gRBssNj<`>ilfp?ztY^xY=RbpDa>xJ{}Gi?f{g+n zT_9tkYr?D{NzVNe-`x|fxHUn^N|yYcwQsd2mVa zHQVukTW5Bpy^vZdU{|Stbh~3tXF{fMWN`7C%#vB-MV1>a%n!Pq;K-#Vb!A^I@Z@0| z8UNp6m5U>|P_0krt`%Uurz~Z&5m$e+UE{ME5JXFFkvmN-{N#8eW;S zJfAKa4AZb9Z}X-?!-6uLYO+yCqne%bS0~Db!u2xRO<>=Q1ks4K>EGO!7HU?<77DIQ zW!SdlAO7dF#;7GGntAI{B6FcS{^-}_i&G&7Oj4>s^}*Fe{4f@-;3qSAu2eXdNh;GT zWsEUaF_sMg$1S0(xXlj-g@C1AnnLqdHA+QH<3&>g|3!MF>5Z9hGl1^A~~DY|yt?@r%W4aCk%wvsR1_-5^!tC~E%TPd_ueu>gq@f+JS7~*Lju22y_ z^22-ktQr@Y6{ltlXMiTp8cqMv3A5yVq4K^pYvE4N`kQ}*TJV-^F{7jAT5pmTX7zh* z%Mmv1&kA{%R2m@`j0mxD_F@~!9&oLKmg$jIts#DDAUV=vR=vD>{4J;T!SQo_#SJ zrO0unlMbUo%OIQXVHK`@J3o9iX4vhI;L!AS6NMKY1Yq^AI zFCwg2rWL-@sz(?S+r<513!2IIz{7Fb z9a$-pBpAygTYC?bYHfHi2eUg|`JWI^aGDZYyd(jf;cXoNG)LAP$PpbEsfah-M!uMq zXer}gS4Lmn%n<_)iDkmFUy44e$B~86lk6^bY?-;RfxpM;q(2ps64abc&hl-fWnfOX z=@=yP2$!3U=bm~!V`xjiA*o%cjp7*nMuRt$?(!OGV-x(;VWWSDJJ>2B#~cv47@R)b zU|fwEt8NTBWi403s+BIxJpPabx)azvw+p!7@=p(?dyGJ9Z!`|No zO{N5Wpk;Uol0oP=7nh6APAAAV6WZ*>jr_)bN;Zyp_3L7W)yBo_%GcHuKTai!$`tRK zOx_lwwcX`UjZu20Jzorm>lP_|*>Pp}Hq!Ad>Pym0e8Xfs2l!gZ0+PTc*lFV=-?Ifa z1?s+eZ*V@53EzESNO;;daMi|4Iko*+myqq(bJA|yJf+8;FM^(u=a9o#|-*%$8jfIj$mlbZ5h*z_){v)R!_e)YoWa@&T< z&+h!CV;#&V1iw_AKdccnH}H2HH#pVnB{L0(*hpMlL7v-N<_e79D)NyAo=`dV?T≠@KC)ZDnClv;wXrOXe-io3jndWi!ZdVz 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

      F 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 literal 0 HcmV?d00001 diff --git a/core/tests/data/crop/two_rois.zip b/core/tests/data/crop/two_rois.zip new file mode 100644 index 0000000000000000000000000000000000000000..2a60e8e12b25e50fddf84b17c75daf924f7e8703 GIT binary patch literal 320 zcmWIWW@Zs#;Nak3P+4Xc&VU3sf$XCE%y>gRAo=->zHVa0p%Vu-^f@+7>{V=PGF! zM=%R)7Rq%JbhdL=bGB&~1e^ZjT=q1C>5NPw3<%dF+YE9&DuDYXz#CN?vXQ9T;(#WA Xw1FKN;LXYgGLZ=g^MLdo5QhN(mX1bw literal 0 HcmV?d00001 From ac06150c937b92140c11ae975571a6d276ae5b25 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 25 Sep 2023 20:43:39 +1000 Subject: [PATCH 059/147] Stop using static methods --- plugin/napari_lattice/fields.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 363fc33..482afd7 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -93,7 +93,8 @@ def enable_field(field: MagicField, enabled: bool = True) -> None: pass -EnabledHandlerType = TypeVar("EnabledHandlerType") +FieldValueType = TypeVar("FieldValueType") +SelfType = TypeVar("SelfType") def enable_if(fields: List[MagicField]): """ Makes an event handler that should be used via `fields_enabled.connect(make_enabled_handler())`. @@ -114,14 +115,14 @@ def _enable_fields(self) -> bool: # Start by disabling all the fields - def _decorator(fn: Callable[[EnabledHandlerType], bool])-> Callable[[EnabledHandlerType], None]: + def _decorator(fn: Callable[[SelfType, FieldValueType], bool])-> Callable[[SelfType, FieldValueType], None]: for field in fields: field.enabled = False field.visible = False - def make_handler(fn: Callable[[EnabledHandlerType], bool]) -> Callable[[EnabledHandlerType], None]: - def handler(value: Any): - enable = fn(value) + def make_handler(fn: Callable[[SelfType, FieldValueType], bool]) -> Callable[[SelfType, FieldValueType], None]: + def handler(self: Any, value: Any): + enable = fn(self, value) for field in fields: if enable: logger.info(f"{field.name} Activated") @@ -281,15 +282,13 @@ def _img_changed(self) -> None: @pixel_sizes_source.connect @enable_if([pixel_sizes]) - @staticmethod - def _hide_pixel_sizes(pixel_sizes_source: str): + def _hide_pixel_sizes(self, pixel_sizes_source: str): # Hide the "Pixel Sizes" option unless the user specifies manual pixel size source return pixel_sizes_source == PixelSizeSource.Manual @img_layer.connect @enable_if([stack_along]) - @staticmethod - def _hide_stack_along(img_layer: List[Image]): + def _hide_stack_along(self, img_layer: List[Image]): # Hide the "Stack Along" option if we only have one image return len(img_layer) > 1 @@ -346,8 +345,7 @@ class DeconvolutionFields(NapariFieldGroup): @enable_if( [background_custom] ) - @staticmethod - def _enable_custom_background(background: str) -> bool: + def _enable_custom_background(self, background: str) -> bool: return background == BackgroundSource.Custom @fields_enabled.connect @@ -359,8 +357,7 @@ def _enable_custom_background(background: str) -> bool: background ] ) - @staticmethod - def _enable_fields(enabled: bool) -> bool: + def _enable_fields(self, enabled: bool) -> bool: return enabled def _make_model(self) -> Optional[DeconvolutionParams]: @@ -446,14 +443,12 @@ class WorkflowFields(NapariFieldGroup): @fields_enabled.connect @enable_if([workflow_source]) - @staticmethod - def _enable_workflow(enabled: bool) -> bool: + def _enable_workflow(self, enabled: bool) -> bool: return enabled @fields_enabled.connect @enable_if([workflow_path]) - @staticmethod - def _workflow_path(workflow_source: WorkflowSource) -> bool: + def _workflow_path(self, workflow_source: WorkflowSource) -> bool: return workflow_source == WorkflowSource.CustomPath def _make_model(self) -> Optional[Workflow]: From fa251056c697f506af9e2d64bbcda9dfda80bb47 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 14:13:33 +1100 Subject: [PATCH 060/147] Remove commented crop test --- core/tests/params.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/tests/params.py b/core/tests/params.py index e49711f..afc4c6c 100644 --- a/core/tests/params.py +++ b/core/tests/params.py @@ -22,9 +22,6 @@ {"physical_pixel_sizes": (1, 1, 1)}, {"save_type": SaveFileType.h5}, {"save_type": SaveFileType.tiff}, - - # # Cropping enabled - # {"crop": {"roi_list": []}}core/lls_core/cropping.py, ]) # Allows parameterisation over two serialization formats From 872da0e979176444c217d9c9e615f1865eb4c94b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 14:14:03 +1100 Subject: [PATCH 061/147] Fix enable_if documentation --- plugin/napari_lattice/fields.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 482afd7..08ba152 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -97,7 +97,7 @@ def enable_field(field: MagicField, enabled: bool = True) -> None: SelfType = TypeVar("SelfType") def enable_if(fields: List[MagicField]): """ - Makes an event handler that should be used via `fields_enabled.connect(make_enabled_handler())`. + Makes an event handler that dynamically disables and enables a set of fields based on a criteria Args: condition: A function that takes an instance of the class and returns True if the fields should be enabled fields: A list of fields to be dynamically enabled or disabled @@ -107,8 +107,8 @@ def enable_if(fields: List[MagicField]): @enable_if( [some_field] ) - def _enable_fields(self) -> bool: - return some_field.value + def _enable_fields(self, value) -> bool: + return value """ # Ideally we could use subclassing to add both the vfield and this event handler, but there # seems to be a bug preventing this: https://github.com/hanjinliu/magic-class/issues/113. From 2d31b9ba1859bc5b874cb202c6a1fa28af0a9912 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 15:10:46 +1100 Subject: [PATCH 062/147] Fix tab naming --- plugin/napari_lattice/dock_widget.py | 8 ++++---- plugin/napari_lattice/fields.py | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 3d69774..7f54a36 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -67,10 +67,10 @@ class WidgetContainer(MagicTemplate): def __post_init__(self): tab_widget: QTabWidget= self._widget._tab_widget - from importlib_resources import as_file - for i in range(5): - with as_file(GREY) as path: - tab_widget.setTabIcon(i, QIcon(str(path))) + # Manually set the tab labels, because by default magicgui uses the widget names, but setting + # the names to human readable text makes them difficult to access via self + for i, label in enumerate(["1. Deskew", "2. Deconvolution", "3. Crop", "4. Workflow", "5. Output"]): + tab_widget.setTabText(i, label) for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: field._validate() diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 08ba152..966b40d 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -191,7 +191,7 @@ class DeskewKwargs(NapariImageParams): angle: float skew: DeskewDirection -@magicclass(name="1. Deskew") +@magicclass class DeskewFields(NapariFieldGroup): def _get_dimension_options(self, _) -> List[str]: @@ -323,7 +323,7 @@ def _make_model(self) -> DeskewParams: skew = kwargs["skew"] ) -@magicclass(name="2. Deconvolution") +@magicclass class DeconvolutionFields(NapariFieldGroup): """ A counterpart to the DeconvolutionParams Pydantic class @@ -376,7 +376,7 @@ def _make_model(self) -> Optional[DeconvolutionParams]: psf_num_iter=self.psf_num_iter.value ) -@magicclass(name="3. Crop") +@magicclass class CroppingFields(MagicTemplate, NapariFieldGroup): """ A counterpart to the CropParams Pydantic class @@ -431,7 +431,7 @@ def _make_model(self) -> Optional[CropParams]: ) return None -@magicclass(name="4. Workflow") +@magicclass class WorkflowFields(NapariFieldGroup): """ Handles the workflow related parameters @@ -459,7 +459,7 @@ def _make_model(self) -> Optional[Workflow]: else: return workflow_from_path(self.workflow_path.value) -@magicclass(name="5. Output") +@magicclass class OutputFields(NapariFieldGroup): set_logging = field(Log_Levels.INFO).with_options(label="Logging Level") time_range = field(Tuple[int, int]).with_options( From 98c65d1d3c6d22ef5112d8ef1717f35358332654 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 15:26:52 +1100 Subject: [PATCH 063/147] Pretty print LatticeData in GUI --- plugin/napari_lattice/dock_widget.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 7f54a36..d569a8f 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -37,9 +37,12 @@ def _check_validity(self) -> bool: return False def _make_model(self) -> LatticeData: + from rich import print + from sys import stdout + deskew_args = self.LlszMenu.WidgetContainer.deskew_fields._get_kwargs() output_args = self.LlszMenu.WidgetContainer.output_fields._make_model() - return LatticeData( + params = LatticeData( image=deskew_args["data"], angle=deskew_args["angle"], channel_range=output_args.channel_range, @@ -54,6 +57,9 @@ def _make_model(self) -> LatticeData: deconvolution=self.LlszMenu.WidgetContainer.deconv_fields._make_model(), crop=self.LlszMenu.WidgetContainer.cropping_fields._make_model() ) + # Log the lattice + print(params, file=stdout) + return params @magicclass(widget_type="split") class LlszMenu(MagicTemplate): From 7048b6efb966274ad7f4027836bb9a989883613a Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 17:13:03 +1100 Subject: [PATCH 064/147] Stop re-running validations when napari layers change --- plugin/napari_lattice/fields.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 966b40d..4bcaab2 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -178,8 +178,14 @@ def _set_valid(self: Any, valid: bool): with as_file(icon) as path: tab_parent.setTabIcon(index, QIcon(str(path))) + def reset_choices(self: Any): + # This is used to prevent validation from re-running when a napari layer is added or removed + from magicgui.widgets import Container + with self.changed.blocked(): + super(Container, self).reset_choices() + def _validate(self: Any): - self.errors.value = get_friendly_validations(self) + self.errors.value = get_friendly_validations(self) valid = not bool(self.errors.value) self.errors.visible = not valid self._set_valid(valid) From 49fb8b7e96d91a3edd83a00f1b886a7a562962b8 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 17:56:07 +1100 Subject: [PATCH 065/147] Fix for crop enabling, ADD_RECTANGLE for cropping, fix for ROI selection --- core/lls_core/cropping.py | 6 ++++++ core/lls_core/models/crop.py | 3 +++ plugin/napari_lattice/fields.py | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/lls_core/cropping.py b/core/lls_core/cropping.py index f2b30d8..1eb61d7 100644 --- a/core/lls_core/cropping.py +++ b/core/lls_core/cropping.py @@ -3,6 +3,7 @@ if TYPE_CHECKING: from lls_core.types import PathLike + from typing_extensions import Self from numpy.typing import NDArray class Roi(NamedTuple): @@ -11,6 +12,11 @@ class Roi(NamedTuple): bottom_left: Tuple[int, int] bottom_right: Tuple[int, int] + @classmethod + def from_array(cls, array: NDArray) -> Self: + import numpy as np + return Roi(*np.reshape(array, (-1, 2)).tolist()) + def read_roi_array(roi: PathLike) -> NDArray: from read_roi import read_roi_file from numpy import array diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index b2741f5..a2c0f3b 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -21,6 +21,7 @@ class CropParams(FieldAccessMixin): def read_roi(cls, v: Any) -> List[Roi]: from lls_core.types import is_pathlike from lls_core.cropping import read_imagej_roi + from numpy import ndarray # Allow a single path if is_pathlike(v): v = [v] @@ -29,6 +30,8 @@ def read_roi(cls, v: Any) -> List[Roi]: for item in v: if is_pathlike(item): rois += read_imagej_roi(item) + elif isinstance(item, ndarray): + rois.append(Roi.from_array(item)) elif isinstance(item, Roi): rois.append(item) else: diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 4bcaab2..aa5ef88 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -420,12 +420,12 @@ def import_roi(self, path: Path): def new_crop_layer(self): from napari_lattice.utils import get_viewer shapes = get_viewer().add_shapes() - shapes.mode = "SELECT" + shapes.mode = "ADD_RECTANGLE" shapes.name = "Napari Lattice Crop" @fields_enabled.connect @enable_if([shapes, z_range]) - def _enable_crop(enabled: bool) -> bool: + def _enable_crop(self, enabled: bool) -> bool: return enabled def _make_model(self) -> Optional[CropParams]: From b8da3446e6924ce0e9443befb2fb99b3ed0537cb Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 18:01:47 +1100 Subject: [PATCH 066/147] Add more checks before we load the workflow modules --- core/lls_core/workflow.py | 5 +++++ plugin/napari_lattice/fields.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index 0577721..682f45f 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -157,6 +157,11 @@ def _import_workflow_modules(workflow: Path) -> None: Args: workflow: Path to the workflow YAML file """ + if not workflow.exists(): + raise Exception("Workflow doesn't exist!") + if not workflow.is_file(): + raise Exception("Workflow must be a file!") + counter = 0 for script in workflow.parent.glob("*.py"): if script.stem == "__init__": diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index aa5ef88..b335216 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -452,7 +452,7 @@ class WorkflowFields(NapariFieldGroup): def _enable_workflow(self, enabled: bool) -> bool: return enabled - @fields_enabled.connect + @workflow_source.connect @enable_if([workflow_path]) def _workflow_path(self, workflow_source: WorkflowSource) -> bool: return workflow_source == WorkflowSource.CustomPath From f459cc998e0fe44f1ad29e0c71c521160f07bcee Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 19:27:44 +1100 Subject: [PATCH 067/147] Add save completed notification --- plugin/napari_lattice/dock_widget.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index d569a8f..34b167e 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -112,5 +112,7 @@ def preview(self, header:str, time: int, channel: int): @set_design(text="Save") def save(self): + from napari.utils.notifications import show_info lattice = self._make_model() lattice.process().save_image() + show_info(f"Deskewing successfuly completed. Results are located in {lattice.save_dir}") From 4dc8bc1b5ba0519ebeb1d2a031cab93939b86b6d Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 2 Oct 2023 19:27:53 +1100 Subject: [PATCH 068/147] Add rich as formal plugin dependency --- plugin/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index d91ad33..42b172e 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -58,6 +58,7 @@ dependencies = [ "pydantic", "qtpy", "typing_extensions>=4.7.0", + "rich", "StrEnum", "xarray" ] From baf0c24a2d73e89749a6ffddc67b138ecf8948f0 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 3 Oct 2023 15:44:57 +1100 Subject: [PATCH 069/147] Show CLI help by default --- core/lls_core/cmds/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 87f466b..2a38f0a 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -28,7 +28,7 @@ class CliDeskewDirection(StrEnum): X = auto() Y = auto() -app = Typer(add_completion=False, rich_markup_mode="rich") +app = Typer(add_completion=False, rich_markup_mode="rich", no_args_is_help=True) def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_description: str = "", description: Optional[str] = None, default: Optional[Any] = None, **kwargs) -> Any: """ From cef8dce248ce99d81bd9a6d8c9ae5440914a9481 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 3 Oct 2023 16:21:58 +1100 Subject: [PATCH 070/147] Pretty print table of validation errors in CLI --- core/lls_core/cmds/__main__.py | 89 ++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 2a38f0a..75b3f30 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -16,18 +16,42 @@ from lls_core.models.output import OutputParams from lls_core.models.crop import CropParams from lls_core import DeconvolutionChoice -from typer import Typer, Argument, Option, Context +from typer import Typer, Argument, Option, Context, Exit from lls_core.models.output import SaveFileType +from pydantic import ValidationError if TYPE_CHECKING: from lls_core.models.utils import FieldAccessMixin from typing import Type, Any + from rich.table import Table class CliDeskewDirection(StrEnum): X = auto() Y = auto() +CLI_PARAM_MAP = { + "image": ["image"], + "angle": ["angle"], + "skew": ["skew"], + "pixel_sizes": ["physical_pixel_sizes"], + "rois": ["crop", "roi_list"], + "z_start": ["crop", "z_range", 0], + "z_end": ["crop", "z_range", 1], + "decon_processing": ["deconvolution", "decon_processing"], + "psf": ["deconvolution", "psf"], + "psf_num_iter": ["deconvolution", "psf_num_iter"], + "background": ["deconvolution", "background"], + "workflow": ["workflow"], + "time_start": ["time_range", 0], + "time_end": ["time_range", 1], + "channel_start": ["channel_range", 0], + "channel_end": ["channel_range", 1], + "save_dir": ["save_dir"], + "save_name": ["save_name"], + "save_type": ["save_type"], +} + app = Typer(add_completion=False, rich_markup_mode="rich", no_args_is_help=True) def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_description: str = "", description: Optional[str] = None, default: Optional[Any] = None, **kwargs) -> Any: @@ -56,6 +80,25 @@ def handle_merge(values: list): raise ValueError(f"A parameter has been passed multiple times! Got: {', '.join(values)}") return values[0] +def rich_validation(e: ValidationError) -> Table: + """ + Converts + """ + from rich.table import Table + + table = Table(title="Validation Errors") + table.add_column("Model Field") + # table.add_column("Command Line Argument") + table.add_column("Error") + + for error in e.errors(): + table.add_row( + str(error["loc"][0]), + str(error["msg"]), + ) + + return table + @app.command("dump-schema") def dump_schema() -> None: import json @@ -99,29 +142,8 @@ def process( yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read."), ) -> None: from toolz.dicttoolz import merge_with, update_in - cli_param_map = { - "image": ["image"], - "angle": ["angle"], - "skew": ["skew"], - "pixel_sizes": ["physical_pixel_sizes"], - "rois": ["crop", "roi_list"], - "z_start": ["crop", "z_range", 0], - "z_end": ["crop", "z_range", 1], - "decon_processing": ["deconvolution", "decon_processing"], - "psf": ["deconvolution", "psf"], - "psf_num_iter": ["deconvolution", "psf_num_iter"], - "background": ["deconvolution", "background"], - "workflow": ["workflow"], - "time_start": ["time_range", 0], - "time_end": ["time_range", 1], - "channel_start": ["channel_range", 0], - "channel_end": ["channel_range", 1], - "save_dir": ["save_dir"], - "save_name": ["save_name"], - "save_type": ["save_type"], - } cli_args = {} - for source, dest in cli_param_map.items(): + for source, dest in CLI_PARAM_MAP.items(): from click.core import ParameterSource if ctx.get_parameter_source(source) != ParameterSource.DEFAULT: cli_args = update_in(cli_args, dest, lambda x: ctx.params[source]) @@ -138,14 +160,21 @@ def process( from yaml import safe_load yaml_args = safe_load(fp) - - LatticeData.parse_obj( - # Merge all three sources of config: YAML, JSON and CLI - merge_with( - handle_merge, - [yaml_args, json_args, cli_args] + try: + lattice = LatticeData.parse_obj( + # Merge all three sources of config: YAML, JSON and CLI + merge_with( + handle_merge, + [yaml_args, json_args, cli_args] + ) ) - ).process().save_image() + except ValidationError as e: + from rich.console import Console + Console().print(rich_validation(e)) + # Console().print(ctx.get_help()) + raise Exit(code=1) + + lattice.process().save_image() def main(): app() From cdf12541c4246b3688bb1f695246ddd98702144b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 3 Oct 2023 16:35:27 +1100 Subject: [PATCH 071/147] Add rich as formal CLI dependency --- core/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/pyproject.toml b/core/pyproject.toml index 5e98645..e04bddd 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -54,6 +54,7 @@ dependencies = [ "pydantic~=1.0", "pyyaml", "read-roi", + "rich", "resource-backed-dask-array>=0.1.0", "scikit-image", "StrEnum", From 3eed0f3a5b938865661b60e25bc1118bb726ff5f Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 4 Oct 2023 13:02:38 +1100 Subject: [PATCH 072/147] Always print help when no argumeunts provided --- core/lls_core/cmds/__main__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 75b3f30..d20f968 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -99,13 +99,7 @@ def rich_validation(e: ValidationError) -> Table: return table -@app.command("dump-schema") -def dump_schema() -> None: - import json - import sys - json.dump(LatticeData.to_definition_dict(), fp=sys.stdout, indent=4) - -@app.command("process") +@app.command() def process( ctx: Context, image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi", show_default=False), @@ -141,6 +135,11 @@ def process( json_config: Optional[Path] = Option(None, show_default=False, help="Path to a JSON file from which parameters will be read."), yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read."), ) -> None: + # Just print help if the user didn't provide any arguments + if len(ctx.args) == 0: + print(ctx.get_help()) + raise Exit() + from toolz.dicttoolz import merge_with, update_in cli_args = {} for source, dest in CLI_PARAM_MAP.items(): From cb65609d862f9239eafdc9b0c0bc46b557b1e02f Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 4 Oct 2023 14:22:43 +1100 Subject: [PATCH 073/147] Fix tests after subcommands were removed --- core/tests/conftest.py | 4 ++-- core/tests/test_arg_parser.py | 2 +- core/tests/test_batch_deskew_args.py | 2 -- core/tests/test_batch_deskew_yaml.py | 4 ++-- core/tests/test_deskew.py | 2 +- core/tests/test_lattice_data.py | 10 +++++----- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/core/tests/conftest.py b/core/tests/conftest.py index 9bc3a8a..01d3b8f 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -33,7 +33,7 @@ def test_image() -> NDArray[np.float64]: def workflow_config(workflow: Workflow, test_image: NDArray): # Create a config file yield { - "image": test_image, + "input_image": test_image, "workflow": workflow, } @@ -56,7 +56,7 @@ def workflow_config_cli(workflow: Workflow, test_image: NDArray): key: str(val) for key, val in { - "image": input, + "input_image": input, "save_dir": output, "workflow": workflow_path, }.items() diff --git a/core/tests/test_arg_parser.py b/core/tests/test_arg_parser.py index da66c6a..26bb1e2 100644 --- a/core/tests/test_arg_parser.py +++ b/core/tests/test_arg_parser.py @@ -5,7 +5,7 @@ def test_voxel_parsing(): # Tests that we can parse voxel lists correctly - command = get_command(app).commands["process"] + command = get_command(app) ctx = Context(command) parser = command.make_parser(ctx) args, _, _ = parser.parse_args(args=[ diff --git a/core/tests/test_batch_deskew_args.py b/core/tests/test_batch_deskew_args.py index df52978..be56cce 100644 --- a/core/tests/test_batch_deskew_args.py +++ b/core/tests/test_batch_deskew_args.py @@ -24,7 +24,6 @@ def test_batch_deskew_h5(): create_image(input_file) # Batch deskew and save as h5 invoke([ - "process", str(input_file), "--save-dir", str(out_dir), "--save-type", "h5" @@ -42,7 +41,6 @@ def test_batch_deskew_tiff(): input_file = out_dir / 'raw.tiff' create_image(input_file) invoke([ - "process", str(input_file), "--save-dir", str(out_dir), "--save-type", "tiff" diff --git a/core/tests/test_batch_deskew_yaml.py b/core/tests/test_batch_deskew_yaml.py index ce2f423..c67c4e3 100644 --- a/core/tests/test_batch_deskew_yaml.py +++ b/core/tests/test_batch_deskew_yaml.py @@ -41,7 +41,7 @@ def create_data(dir: Path) -> Path: assert input_file.exists() config: dict[str, str] = { - "image": str(input_file), + "input_image": str(input_file), "save_dir": str(dir), "save_type": "h5" } @@ -60,7 +60,7 @@ def test_yaml_deskew(): test_dir = Path(test_dir) config_location = create_data(test_dir) # Batch deskew and save as h5 - invoke(["process", "--yaml-config", str(config_location)], ) + invoke(["--yaml-config", str(config_location)], ) # checks if h5 files written assert (test_dir / "raw.h5").exists() diff --git a/core/tests/test_deskew.py b/core/tests/test_deskew.py index d08fc4b..e46e3fc 100644 --- a/core/tests/test_deskew.py +++ b/core/tests/test_deskew.py @@ -17,5 +17,5 @@ def test_deskew(): def test_lattice_data_deskew(): raw = DataArray(np.zeros((5, 5, 5)), dims=["X", "Y", "Z"]) - lattice = LatticeData(image=raw, physical_pixel_sizes = (1, 1, 1), save_name="test") + lattice = LatticeData(input_image=raw, physical_pixel_sizes = (1, 1, 1), save_name="test") assert lattice.deskew_vol_shape == [2, 9, 5] diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index 8af6891..ebc829f 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -19,7 +19,7 @@ def open_psf(name: str): def test_process(path: str, args: dict): with as_file(resources / path) as lattice_path: for slice in LatticeData.parse_obj({ - "image": lattice_path, + "input_image": lattice_path, **args }).process().slices: assert slice.data.ndim == 3 @@ -29,7 +29,7 @@ def test_process(path: str, args: dict): def test_save(path: str, args: dict): with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: LatticeData.parse_obj({ - "image": lattice_path, + "input_image": lattice_path, "save_dir": tempdir, **args }).process().save_image() @@ -40,7 +40,7 @@ def test_save(path: str, args: dict): @parameterized def test_process_deconvolution(args: dict, background: Any): for slice in LatticeData.parse_obj({ - "image": root / "raw.tif", + "input_image": root / "raw.tif", "deconvolution": { "psf": [root / "psf.tif"], "background": background @@ -52,7 +52,7 @@ def test_process_deconvolution(args: dict, background: Any): @parameterized def test_process_workflow(args: dict, workflow: Workflow): for slice in LatticeData.parse_obj({ - "image": root / "raw.tif", + "input_image": root / "raw.tif", "workflow": workflow, **args }).process().slices: @@ -63,7 +63,7 @@ def test_process_crop(args: dict, workflow: Workflow): with as_file(resources / "RBC_tiny.czi") as lattice_path: rois = root / "crop" / "two_rois.zip" for slice in LatticeData.parse_obj({ - "image": lattice_path, + "input_image": lattice_path, "crop": { "roi_list": [rois] }, From 79091060ed5b49e8ec7d6cc37fa7d307b7a180dc Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 4 Oct 2023 14:24:38 +1100 Subject: [PATCH 074/147] Rename image to input_image, and fix the CLI validation error display --- core/lls_core/cmds/__main__.py | 10 ++++++---- core/lls_core/models/deskew.py | 16 ++++++++-------- core/lls_core/models/lattice_data.py | 24 ++++++++++++------------ core/lls_core/models/output.py | 2 +- plugin/napari_lattice/dock_widget.py | 2 +- plugin/napari_lattice/fields.py | 2 +- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index d20f968..f1f4bd1 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -31,7 +31,7 @@ class CliDeskewDirection(StrEnum): Y = auto() CLI_PARAM_MAP = { - "image": ["image"], + "input_image": ["input_image"], "angle": ["angle"], "skew": ["skew"], "pixel_sizes": ["physical_pixel_sizes"], @@ -87,7 +87,7 @@ def rich_validation(e: ValidationError) -> Table: from rich.table import Table table = Table(title="Validation Errors") - table.add_column("Model Field") + table.add_column("Parameter") # table.add_column("Command Line Argument") table.add_column("Error") @@ -102,7 +102,7 @@ def rich_validation(e: ValidationError) -> Table: @app.command() def process( ctx: Context, - image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi", show_default=False), + input_image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi", show_default=False), skew: CliDeskewDirection = field_from_model(DeskewParams, "skew"),# DeskewParams.make_typer_field("skew"), angle: float = field_from_model(DeskewParams, "angle") , pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the X Y and Z pixel dimensions respectively", default=( @@ -135,8 +135,10 @@ def process( json_config: Optional[Path] = Option(None, show_default=False, help="Path to a JSON file from which parameters will be read."), yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read."), ) -> None: + from click.core import ParameterSource + # Just print help if the user didn't provide any arguments - if len(ctx.args) == 0: + if all(src != ParameterSource.COMMANDLINE for src in ctx._parameter_source.values()): print(ctx.get_help()) raise Exit() diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index ab0d83b..76888e6 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -41,7 +41,7 @@ def from_physical(cls, pixels: PhysicalPixelSizes) -> Self: class DeskewParams(FieldAccessMixin): - image: DataArray = Field( + input_image: DataArray = Field( description="A 3-5D array containing the image data." ) skew: DeskewDirection = Field( @@ -118,17 +118,17 @@ def set_skew(self, skew: DeskewDirection) -> None: @property def dims(self): - return self.image.dims + return self.input_image.dims @property def time(self) -> int: """Number of time points""" - return self.image.sizes["T"] + return self.input_image.sizes["T"] @property def channels(self) -> int: """Number of channels""" - return self.image.sizes["C"] + return self.input_image.sizes["C"] @property def new_dz(self): @@ -149,7 +149,7 @@ def convert_pixels(cls, v: Any): return DefinedPixelSizes(X=v[0], Y=v[1], Z=v[2]) return v - @validator("image", pre=True) + @validator("input_image", pre=True) def reshaping(cls, v: Any): # This allows a user to pass in any array-like object and have it # converted and reshaped appropriately @@ -163,7 +163,7 @@ def reshaping(cls, v: Any): return array.transpose("T", "C", "Z", "Y", "X") def get_3d_slice(self) -> DataArray: - return self.image.isel(C=0, T=0) + return self.input_image.isel(C=0, T=0) @root_validator(pre=False) def set_deskew(cls, values: dict) -> dict: @@ -171,9 +171,9 @@ def set_deskew(cls, values: dict) -> dict: Sets the default deskew shape values if the user has not provided them """ # process the file to get shape of final deskewed image - if "image" not in values: + if "input_image" not in values: return values - data: DataArray = cls.reshaping(values["image"]) + data: DataArray = cls.reshaping(values["input_image"]) if values.get('deskew_vol_shape') is None: if values.get('deskew_affine_transform') is None: # If neither has been set, calculate them ourselves diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 20a9f4e..767f08a 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -170,8 +170,8 @@ def default_save_name(cls, values: dict): # reshaping validator. We can't override that either since it's # a field validator and can't modify save_name from lls_core.types import is_pathlike - if values.get("save_name", None) is None and is_pathlike(values.get("image")): - values["save_name"] = Path(values["image"]).stem + if values.get("save_name", None) is None and is_pathlike(values.get("input_image")): + values["save_name"] = Path(values["input_image"]).stem return values @validator("workflow", pre=True) @@ -192,7 +192,7 @@ def default_z_range(cls, v: CropParams, values: dict): return v with ignore_keyerror(): default_start = 0 - default_end = values["image"].sizes["Z"] + default_end = values["input_image"].sizes["Z"] if v.z_range is None: v.z_range = (default_start, default_end) if v.z_range[0] is None: @@ -210,7 +210,7 @@ def parse_time_range(cls, v: Any, values: dict) -> Any: # user-friendly error is provided, namely "image was missing" with ignore_keyerror(): default_start = 0 - default_end = values["image"].sizes["T"] + default_end = values["input_image"].sizes["T"] if v is None: return range(default_start, default_end) elif isinstance(v, tuple) and len(v) == 2: @@ -225,7 +225,7 @@ def parse_channel_range(cls, v: Any, values: dict) -> Any: """ with ignore_keyerror(): default_start = 0 - default_end = values["image"].sizes["C"] + default_end = values["input_image"].sizes["C"] if v is None: return range(default_start, default_end) elif isinstance(v, tuple) and len(v) == 2: @@ -239,7 +239,7 @@ def disjoint_time_range(cls, v: range, values: dict): Validates that the time range is within the range of channels in our array """ with ignore_keyerror(): - max_time = values["image"].sizes["T"] + max_time = values["input_image"].sizes["T"] if v.start < 0: raise ValueError("The lowest valid start value is 0") if v.stop > max_time: @@ -253,7 +253,7 @@ def disjoint_channel_range(cls, v: range, values: dict): Validates that the channel range is within the range of channels in our array """ with ignore_keyerror(): - max_channel = values["image"].sizes["C"] + max_channel = values["input_image"].sizes["C"] if v.start < 0: raise ValueError("The lowest valid start value is 0") if v.stop > max_channel: @@ -263,13 +263,13 @@ def disjoint_channel_range(cls, v: range, values: dict): @validator("channel_range") def channel_range_subset(cls, v: range, values: dict): with ignore_keyerror(): - if min(v) < 0 or max(v) > values["image"].sizes["C"]: + if min(v) < 0 or max(v) > values["input_image"].sizes["C"]: raise ValueError("The output channel range must be a subset of the total available channels") return v @validator("time_range") def time_range_subset(cls, v: range, values: dict): - if min(v) < 0 or max(v) > values["image"].sizes["T"]: + if min(v) < 0 or max(v) > values["input_image"].sizes["T"]: raise ValueError("The output time range must be a subset of the total available time points") return v @@ -278,7 +278,7 @@ def check_psfs(cls, v: Optional[DeconvolutionParams], values: dict): if v is None: return v with ignore_keyerror(): - channels = values["image"].sizes["C"] + channels = values["input_image"].sizes["C"] psfs = len(v.psf) if psfs != channels: raise ValueError(f"There should be one PSF per channel, but there are {psfs} PSFs and {channels} channels.") @@ -304,7 +304,7 @@ def slice_data(self, time: int, channel: int) -> DataArray: if channel > self.channels: raise ValueError("channel is out of range") - return self.image.isel(T=time, C=channel) + return self.input_image.isel(T=time, C=channel) def iter_slices(self) -> Iterable[SlicedData[ArrayLike]]: """ @@ -333,7 +333,7 @@ def iter_sublattices(self, update_with: dict = {}) -> Iterable[SlicedData[Lattic for subarray in self.iter_slices(): # Copy doesn't manu new_lattice = self.copy_validate(update={ - "image": subarray.data, + "input_image": subarray.data, **update_with }) yield subarray.copy_with_data( new_lattice) diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 7520c32..ba4b73e 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -1,4 +1,4 @@ -from pydantic import Field, validator, DirectoryPath +from pydantic import Field, DirectoryPath from strenum import StrEnum from os import getcwd from pathlib import Path diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 34b167e..8a75cdf 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -43,7 +43,7 @@ def _make_model(self) -> LatticeData: deskew_args = self.LlszMenu.WidgetContainer.deskew_fields._get_kwargs() output_args = self.LlszMenu.WidgetContainer.output_fields._make_model() params = LatticeData( - image=deskew_args["data"], + input_image=deskew_args["data"], angle=deskew_args["angle"], channel_range=output_args.channel_range, time_range=output_args.time_range, diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index b335216..f8f5494 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -323,7 +323,7 @@ def _get_kwargs(self) -> DeskewKwargs: def _make_model(self) -> DeskewParams: kwargs = self._get_kwargs() return DeskewParams( - image=kwargs["data"], + input_image=kwargs["data"], physical_pixel_sizes=kwargs["physical_pixel_sizes"], angle=kwargs["angle"], skew = kwargs["skew"] From 98c435db5b779d77162e7d033d2d4d1450af5c40 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 4 Oct 2023 14:54:22 +1100 Subject: [PATCH 075/147] =?UTF-8?q?Fix=20for=20the=20confusing=20"time=5Fr?= =?UTF-8?q?ange=20=20=20=20=E2=94=82=20'NoneType'=20object=20is=20not=20it?= =?UTF-8?q?erable"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/lls_core/models/lattice_data.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 767f08a..b3f74a4 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -1,19 +1,14 @@ from __future__ import annotations -from os import PathLike # class for initializing lattice data and setting metadata # TODO: handle scenes from pydantic import BaseModel, DirectoryPath, Field, NonNegativeInt, root_validator, validator -from aicsimageio.aics_image import AICSImage -import math from dask.array.core import Array as DaskArray -import dask as da from itertools import groupby import tifffile -from typing import Any, Iterable, List, Literal, Optional, TYPE_CHECKING +from typing import Any, Iterable, List, Optional, TYPE_CHECKING from typing_extensions import TypedDict, NotRequired, Generic, TypeVar -from aicsimageio.types import PhysicalPixelSizes import pyclesperanto_prototype as cle from tqdm import tqdm @@ -261,15 +256,15 @@ def disjoint_channel_range(cls, v: range, values: dict): return v @validator("channel_range") - def channel_range_subset(cls, v: range, values: dict): + def channel_range_subset(cls, v: Optional[range], values: dict): with ignore_keyerror(): - if min(v) < 0 or max(v) > values["input_image"].sizes["C"]: + if v is not None and (min(v) < 0 or max(v) > values["input_image"].sizes["C"]): raise ValueError("The output channel range must be a subset of the total available channels") return v @validator("time_range") - def time_range_subset(cls, v: range, values: dict): - if min(v) < 0 or max(v) > values["input_image"].sizes["T"]: + def time_range_subset(cls, v: Optional[range], values: dict): + if v is not None and (min(v) < 0 or max(v) > values["input_image"].sizes["T"]): raise ValueError("The output time range must be a subset of the total available time points") return v From 16b28fec307f19e73b8d55e677621082037b2710 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 4 Oct 2023 15:43:27 +1100 Subject: [PATCH 076/147] Add roi_subset parameter --- core/lls_core/cmds/__main__.py | 3 +++ core/lls_core/models/crop.py | 20 ++++++++++++++++++-- core/lls_core/models/lattice_data.py | 2 +- core/tests/test_lattice_data.py | 12 +++++++++--- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index f1f4bd1..d45593c 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -36,6 +36,7 @@ class CliDeskewDirection(StrEnum): "skew": ["skew"], "pixel_sizes": ["physical_pixel_sizes"], "rois": ["crop", "roi_list"], + "roi_indices": ["crop", "roi_subset"], "z_start": ["crop", "z_range", 0], "z_end": ["crop", "z_range", 1], "decon_processing": ["deconvolution", "decon_processing"], @@ -110,7 +111,9 @@ def process( DeskewParams.get_default("physical_pixel_sizes").Y, DeskewParams.get_default("physical_pixel_sizes").Z )), + rois: List[Path] = field_from_model(CropParams, "roi_list", description="A list of paths pointing to regions of interest to crop to, in ImageJ format."), #Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), + roi_indices: List[int] = field_from_model(CropParams, "roi_subset"), # Ideally this and other range values would be defined as Tuples, but these seem to be broken: https://github.com/tiangolo/typer/discussions/667 z_start: Optional[int] = Option(0, help="The index of the first Z slice to use. All prior Z slices will be discarded.", show_default=False), z_end: Optional[int] = Option(None, help="The index of the last Z slice to use. The selected index and all subsequent Z slices will be discarded. Defaults to the last z index of the image.", show_default=False), diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index a2c0f3b..65e6160 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -1,9 +1,8 @@ -from typing import List, Tuple, Any +from typing import Iterable, List, Tuple, Any from pydantic import Field, NonNegativeInt, validator from lls_core.models.utils import FieldAccessMixin from lls_core.cropping import Roi - class CropParams(FieldAccessMixin): """ Parameters for the optional cropping step @@ -12,11 +11,21 @@ class CropParams(FieldAccessMixin): description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex.", default = [] ) + roi_subset: List[int] = Field( + description="A subset of all the ROIs to process", + default=None + ) z_range: Tuple[NonNegativeInt, NonNegativeInt] = Field( default=None, description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." ) + @property + def selected_rois(self) -> Iterable[Roi]: + "Returns the relevant ROIs that should be processed" + for i in self.roi_subset: + yield self.roi_list[i] + @validator("roi_list", pre=True) def read_roi(cls, v: Any) -> List[Roi]: from lls_core.types import is_pathlike @@ -38,3 +47,10 @@ def read_roi(cls, v: Any) -> List[Roi]: raise ValueError(f"{item} cannot be intepreted as an ROI") return rois + + @validator("roi_subset", pre=True) + def default_roi_range(cls, v: Any, values: dict): + # If the roi range isn't provided, assume all rois should be processed + if v is None: + return list(range(len(values["roi_list"]))) + return v diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index b3f74a4..2ca93bf 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -393,7 +393,7 @@ def _process_crop(self) -> Iterable[ProcessedVolume]: raise Exception("This function can only be called when crop is set") # We have an extra level of iteration for the crop path: iterating over each ROI - for roi_index, roi in enumerate(tqdm(self.crop.roi_list, desc="ROI:", position=0)): + for roi_index, roi in enumerate(tqdm(self.crop.selected_rois, desc="ROI:", position=0)): # pass arguments for save tiff, callable and function arguments logger.info(f"Processing ROI {roi_index}") diff --git a/core/tests/test_lattice_data.py b/core/tests/test_lattice_data.py index ebc829f..c81a945 100644 --- a/core/tests/test_lattice_data.py +++ b/core/tests/test_lattice_data.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, List, Optional import pytest from lls_core.models import LatticeData from lls_core.sample import resources @@ -58,14 +58,20 @@ def test_process_workflow(args: dict, workflow: Workflow): }).process().slices: assert slice.data.ndim == 3 +@pytest.mark.parametrize(["roi_subset"], [ + [None], + [[0]], + [[0, 1]], +]) @parameterized -def test_process_crop(args: dict, workflow: Workflow): +def test_process_crop(args: dict, roi_subset: Optional[List[int]], workflow: Workflow): with as_file(resources / "RBC_tiny.czi") as lattice_path: rois = root / "crop" / "two_rois.zip" for slice in LatticeData.parse_obj({ "input_image": lattice_path, "crop": { - "roi_list": [rois] + "roi_list": [rois], + "roi_subset": roi_subset }, **args }).process().slices: From 17900d8b73df820207199c6c408c93b701403699 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 4 Oct 2023 16:37:13 +1100 Subject: [PATCH 077/147] Fix workflow test --- core/tests/test_workflows.py | 1 - 1 file changed, 1 deletion(-) diff --git a/core/tests/test_workflows.py b/core/tests/test_workflows.py index 4d060f6..c149e51 100644 --- a/core/tests/test_workflows.py +++ b/core/tests/test_workflows.py @@ -35,7 +35,6 @@ def test_workflow_cli(workflow_config_cli: dict, save_func: Callable, cli_param: # Deskew, apply workflow and save as h5 invoke([ - "process", cli_param, fp.name ]) From 79f13659d27e6a54f4d187e984258de3b27a4eb2 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 10 Oct 2023 17:53:37 +1100 Subject: [PATCH 078/147] Infer the output dir correctly, separate validation and process tests --- core/lls_core/models/lattice_data.py | 17 ++++++++++++++--- core/lls_core/models/output.py | 4 ++-- .../{test_lattice_data.py => test_process.py} | 0 core/tests/test_validation.py | 10 ++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) rename core/tests/{test_lattice_data.py => test_process.py} (100%) create mode 100644 core/tests/test_validation.py diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 2ca93bf..6938f29 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -160,13 +160,24 @@ class LatticeData(OutputParams, DeskewParams): workflow: Optional[Workflow] = workflow @root_validator(pre=True) - def default_save_name(cls, values: dict): + def use_image_path(cls, values: dict): # This needs to be a root validator to ensure it runs before the # reshaping validator. We can't override that either since it's # a field validator and can't modify save_name from lls_core.types import is_pathlike - if values.get("save_name", None) is None and is_pathlike(values.get("input_image")): - values["save_name"] = Path(values["input_image"]).stem + input_image = values.get("input_image") + if is_pathlike(input_image): + if values.get("save_name") is None: + values["save_name"] = Path(values["input_image"]).stem + + save_dir = values.get("save_dir") + if save_dir is None: + # By default, make the save dir be the same dir as the input + values["save_dir"] = Path(input_image).parent + elif is_pathlike(save_dir): + # Convert a string path to a Path object + values["save_name"] = Path(save_dir) + return values @validator("workflow", pre=True) diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index ba4b73e..50c69b3 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -12,7 +12,7 @@ class SaveFileType(StrEnum): class OutputParams(FieldAccessMixin): save_dir: DirectoryPath = Field( - default = getcwd(), + default=None, description="The directory where the output data will be saved" ) save_name: str = Field( @@ -23,7 +23,7 @@ class OutputParams(FieldAccessMixin): description=f"The data type to save the result as. This will also be used to determine the file extension of the output files. Choices: {enum_choices(SaveFileType)}" ) time_range: range = Field( - default = None, + default=None, description="The range of times to process. This defaults to all time points in the image array." ) channel_range: range = Field( diff --git a/core/tests/test_lattice_data.py b/core/tests/test_process.py similarity index 100% rename from core/tests/test_lattice_data.py rename to core/tests/test_process.py diff --git a/core/tests/test_validation.py b/core/tests/test_validation.py new file mode 100644 index 0000000..9084ff9 --- /dev/null +++ b/core/tests/test_validation.py @@ -0,0 +1,10 @@ +from lls_core.models.lattice_data import LatticeData +from lls_core.sample import resources +from importlib_resources import as_file + + +def test_default_save_dir(): + # Test that the save dir is inferred to be the input dir + with as_file(resources / "RBC_tiny.czi") as path: + params = LatticeData(input_image=path) + assert params.save_dir == path.parent From 44836939ba88ca11604e7c0163467ef060f16290 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 10 Oct 2023 17:58:09 +1100 Subject: [PATCH 079/147] Workflow loads from YAML in CLI --- core/lls_core/cmds/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index d45593c..2062767 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -134,7 +134,7 @@ def process( save_name: Optional[str] = field_from_model(OutputParams, "save_name", rich_help_panel="Output"), save_type: SaveFileType = field_from_model(OutputParams, "save_type", rich_help_panel="Output"), - workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in JSON format. If provided, the configured desekewing processing will be added to the chosen workflow.", show_default=False), + workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in YAML format. If provided, the configured desekewing processing will be added to the chosen workflow.", show_default=False), json_config: Optional[Path] = Option(None, show_default=False, help="Path to a JSON file from which parameters will be read."), yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read."), ) -> None: From 82a73b229a5fc81d533c179116efe71962d788d6 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 10 Oct 2023 18:08:30 +1100 Subject: [PATCH 080/147] Fix broken CLI tests --- core/lls_core/cmds/__main__.py | 2 +- core/lls_core/models/lattice_data.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 2062767..65c4fac 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -120,7 +120,7 @@ def process( enable_deconvolution: bool = Option(False, "--deconvolution/--disable-deconvolution", rich_help_panel="Deconvolution"), decon_processing: DeconvolutionChoice = field_from_model(DeconvolutionParams, "decon_processing", rich_help_panel="Deconvolution"), - psf: List[Path] = field_from_model(DeconvolutionParams, "psf", description="A list of paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array.", rich_help_panel="Deconvolution"), + psf: List[Path] = field_from_model(DeconvolutionParams, "psf", description="One or more paths pointing to point spread functions to use for deconvolution. Each file should in a standard image format (.czi, .tiff etc), containing a 3D image array. This option can be used multiple times to provide multiple PSF files.", rich_help_panel="Deconvolution"), psf_num_iter: int = field_from_model(DeconvolutionParams, "psf_num_iter", rich_help_panel="Deconvolution"), background: str = field_from_model(DeconvolutionParams, "background", rich_help_panel="Deconvolution"), diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 6938f29..5e4990a 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -176,7 +176,7 @@ def use_image_path(cls, values: dict): values["save_dir"] = Path(input_image).parent elif is_pathlike(save_dir): # Convert a string path to a Path object - values["save_name"] = Path(save_dir) + values["save_dir"] = Path(save_dir) return values From 404f80baae2d8e84035e35dd8e7bc68e45bbe326 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 13 Oct 2023 14:01:15 +1100 Subject: [PATCH 081/147] Create test_cli test suite --- core/tests/test_batch_deskew_args.py | 50 --------------- core/tests/test_batch_deskew_yaml.py | 67 ------------------- core/tests/test_cli.py | 96 ++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 117 deletions(-) delete mode 100644 core/tests/test_batch_deskew_args.py delete mode 100644 core/tests/test_batch_deskew_yaml.py create mode 100644 core/tests/test_cli.py diff --git a/core/tests/test_batch_deskew_args.py b/core/tests/test_batch_deskew_args.py deleted file mode 100644 index be56cce..0000000 --- a/core/tests/test_batch_deskew_args.py +++ /dev/null @@ -1,50 +0,0 @@ -# Tests for napari_lattice using arguments and saving output files as h5, as well as tiff - -from skimage.io import imsave -import numpy as np -from pathlib import Path -import tempfile -from tests.utils import invoke - -def create_image(path: Path): - # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) - raw = np.zeros((5, 5, 5)) - raw[2, 4, 2] = 10 - # Save image as a tif filw in home directory - imsave(str(path), raw) - assert path.exists() - -def test_batch_deskew_h5(): - """Write image to disk and then execute napari_lattice from terminal - Checks if an deskewed output file is created for both tif and h5 - """ - with tempfile.TemporaryDirectory() as out_dir: - out_dir = Path(out_dir) - input_file = out_dir / 'raw.tiff' - create_image(input_file) - # Batch deskew and save as h5 - invoke([ - str(input_file), - "--save-dir", str(out_dir), - "--save-type", "h5" - ]) - - # checks if h5 files written - assert (out_dir / "raw.h5").exists() - # assert (out_dir / "raw.xml").exists() - - -def test_batch_deskew_tiff(): - # tiff file deskew - with tempfile.TemporaryDirectory() as out_dir: - out_dir = Path(out_dir) - input_file = out_dir / 'raw.tiff' - create_image(input_file) - invoke([ - str(input_file), - "--save-dir", str(out_dir), - "--save-type", "tiff" - ]) - - # checks if tiff written - assert len(list(out_dir.glob("*.tif"))) == 1 diff --git a/core/tests/test_batch_deskew_yaml.py b/core/tests/test_batch_deskew_yaml.py deleted file mode 100644 index c67c4e3..0000000 --- a/core/tests/test_batch_deskew_yaml.py +++ /dev/null @@ -1,67 +0,0 @@ -# Tests for napari_lattice using the config file and saving ouput as h5 -# Thanks to DrLachie for cool function to write the config file - -from skimage.io import imread, imsave -import tempfile -import numpy as np -from pathlib import Path -from lls_core.cmds.__main__ import app -from tests.utils import invoke - -def write_config_file(config_settings: dict, output_file_location: Path): - # Write config file for napari_lattice - with output_file_location.open('w') as f: - for key, val in config_settings.items(): - if val is not None: - if type(val) is str: - print('%s: "%s"' % (key, val), file=f) - - if type(val) is int: - print('%s: %i' % (key, val), file=f) - - if type(val) is list: - print("%s:" % key, file=f) - for x in val: - if type(x) is int: - print(" - %i" % x, file=f) - else: - print(' - "%s"' % x, file=f) - - print("Config found written to %s" % output_file_location) - -def create_data(dir: Path) -> Path: - input_file = dir / 'raw.tiff' - config_location = dir / "config_deskew.yaml" - - # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) - raw = np.zeros((5, 5, 5)) - raw[2, 4, 2] = 10 - # Save image as a tif filw in home directory - imsave(input_file, raw) - assert input_file.exists() - - config: dict[str, str] = { - "input_image": str(input_file), - "save_dir": str(dir), - "save_type": "h5" - } - - write_config_file(config, config_location) - assert config_location.exists() - - return config_location - - -def test_yaml_deskew(): - """Write image to disk and then execute napari_lattice from terminal - Checks if an deskewed output file is created for both tif and h5 - """ - with tempfile.TemporaryDirectory() as test_dir: - test_dir = Path(test_dir) - config_location = create_data(test_dir) - # Batch deskew and save as h5 - invoke(["--yaml-config", str(config_location)], ) - - # checks if h5 files written - assert (test_dir / "raw.h5").exists() - # assert (test_dir / "raw" / "raw.xml").exists() diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py new file mode 100644 index 0000000..5724d0a --- /dev/null +++ b/core/tests/test_cli.py @@ -0,0 +1,96 @@ +# Tests for napari_lattice using arguments and saving output files as h5, as well as tiff + +from aicsimageio.aics_image import AICSImage +from npy2bdv import BdvEditor +import numpy as np +from pathlib import Path +import tempfile +from tests.utils import invoke +import yaml + +def create_image(path: Path): + # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) + raw = np.zeros((5, 5, 5)) + raw[2, 4, 2] = 10 + # Save image as a tif filw in home directory + AICSImage(raw).save(path) + assert path.exists() + + +def create_data(dir: Path) -> Path: + # Creates and returns a YAML config file + input_file = dir / 'raw.tiff' + config_location = dir / "config_deskew.yaml" + + # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) + raw = np.zeros((5, 5, 5)) + raw[2, 4, 2] = 10 + # Save image as a tif filw in home directory + AICSImage(raw).save(input_file) + assert input_file.exists() + + config: dict[str, str] = { + "input_image": str(input_file), + "save_dir": str(dir), + "save_type": "h5" + } + + with config_location.open("w") as fp: + yaml.safe_dump(config, fp) + + return config_location + + +def test_batch_deskew_h5(): + """Write image to disk and then execute napari_lattice from terminal + Checks if an deskewed output file is created for both tif and h5 + """ + with tempfile.TemporaryDirectory() as out_dir: + out_dir = Path(out_dir) + input_file = out_dir / 'raw.tiff' + create_image(input_file) + # Batch deskew and save as h5 + invoke([ + str(input_file), + "--save-dir", str(out_dir), + "--save-type", "h5" + ]) + + # checks if h5 files written + assert (out_dir / "raw.h5").exists() + assert (out_dir / "raw.xml").exists() + BdvEditor(str(out_dir / "raw.h5")).read_view() + +def test_batch_deskew_tiff(): + # tiff file deskew + with tempfile.TemporaryDirectory() as out_dir: + out_dir = Path(out_dir) + input_file = out_dir / 'raw.tiff' + create_image(input_file) + invoke([ + str(input_file), + "--save-dir", str(out_dir), + "--save-type", "tiff" + ]) + + # checks if tiff written + results = list(out_dir.glob("*.tif")) + assert len(results) == 1 + for result in results: + AICSImage(result).get_image_data() + + +def test_yaml_deskew(): + """Write image to disk and then execute napari_lattice from terminal + Checks if an deskewed output file is created for both tif and h5 + """ + with tempfile.TemporaryDirectory() as test_dir: + test_dir = Path(test_dir) + config_location = create_data(test_dir) + # Batch deskew and save as h5 + invoke(["--yaml-config", str(config_location)], ) + + # checks if h5 files written + assert (test_dir / "raw.h5").exists() + assert (test_dir / "raw.xml").exists() + BdvEditor(str(test_dir / "raw.h5")).read_view() From 561f1253fe393d172fca4d5c0629345593106d6a Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 13 Oct 2023 15:20:38 +1100 Subject: [PATCH 082/147] Fix save directory selection --- plugin/napari_lattice/fields.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index f8f5494..bdb7866 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -489,7 +489,9 @@ class OutputFields(NapariFieldGroup): ) save_path = field(Path).with_options( label = "Save Directory", - value = Path(history.get_save_history()[0]) + value = Path(history.get_save_history()[0]), + # Directory select + mode="d" ) save_name = field(str).with_options( label = "Save Prefix" From 8e5508e0131cf051a5a5add4824dcd641755a3e2 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 27 Oct 2023 21:39:03 +1100 Subject: [PATCH 083/147] Workflow processing into data frames --- core/lls_core/models/lattice_data.py | 133 ++++++--------------------- core/lls_core/models/output.py | 4 + core/lls_core/models/results.py | 128 ++++++++++++++++++++++++++ core/lls_core/utils.py | 15 +++ core/lls_core/writers.py | 99 ++++++++++++++++++++ core/tests/conftest.py | 38 +++++++- core/tests/test_process.py | 22 +++-- core/tests/test_workflows.py | 38 +++++++- core/tests/utils.py | 11 +++ plugin/napari_lattice/dock_widget.py | 2 +- 10 files changed, 374 insertions(+), 116 deletions(-) create mode 100644 core/lls_core/models/results.py create mode 100644 core/lls_core/writers.py diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 5e4990a..56f56a0 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -4,9 +4,8 @@ from pydantic import BaseModel, DirectoryPath, Field, NonNegativeInt, root_validator, validator from dask.array.core import Array as DaskArray from itertools import groupby -import tifffile -from typing import Any, Iterable, List, Optional, TYPE_CHECKING +from typing import Any, Iterable, List, Optional, TYPE_CHECKING, Type from typing_extensions import TypedDict, NotRequired, Generic, TypeVar import pyclesperanto_prototype as cle @@ -18,6 +17,7 @@ from lls_core.models.crop import CropParams from lls_core.models.deconvolution import DeconvolutionParams from lls_core.models.output import OutputParams, SaveFileType +from lls_core.models.results import WorkflowSlices from lls_core.models.utils import ignore_keyerror from lls_core.types import ArrayLike from lls_core.models.deskew import DeskewParams @@ -29,99 +29,13 @@ import pyclesperanto_prototype as cle from lls_core.models.deskew import DefinedPixelSizes from numpy.typing import NDArray + from lls_core.models.results import ImageSlice, ImageSlices, ProcessedSlice, ProcessedSlices + from lls_core.writers import Writer, BdvWriter, TiffWriter import logging logger = logging.getLogger(__name__) -def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[str] = None, channel: Optional[str] = None, time: Optional[str] = None) -> str: - """ - Generates a filename for this result - """ - components: List[str] = [] - if prefix is not None: - components.append(prefix) - if roi_index is not None: - components.append(f"ROI_{roi_index}") - if channel is not None: - components.append(f"C{channel}") - if time is not None: - components.append(f"T{time}") - return "_".join(components) - -T = TypeVar("T") -S = TypeVar("S") -class SlicedData(BaseModel, Generic[T], arbitrary_types_allowed=True): - data: T - time_index: NonNegativeInt - time: NonNegativeInt - channel_index: NonNegativeInt - channel: NonNegativeInt - roi_index: Optional[NonNegativeInt] = None - - def copy_with_data(self, data: S) -> SlicedData[S]: - """ - Return a modified version of this with new inner data - """ - from typing_extensions import cast - return cast( - SlicedData[S], - self.copy(update={ - "data": data - }) - ) -ProcessedVolume = SlicedData[ArrayLike] - -class ProcessedSlices(BaseModel, arbitrary_types_allowed=True): - #: Iterable of result slices. - #: Note that this is a finite iterator that can only be iterated once - slices: Iterable[ProcessedVolume] - - #: The "parent" LatticeData that was used to create this result - lattice_data: LatticeData - - def save_image(self): - """ - Saves result slices to disk - """ - # TODO: refactor this into a class system, one for each format - import numpy as np - import npy2bdv - - for roi, roi_results in groupby(self.slices, key=lambda it: it.roi_index): - if self.lattice_data.save_type == SaveFileType.h5: - bdv_writer = npy2bdv.BdvWriter( - filename=str(self.lattice_data.make_filepath(make_filename_prefix(roi_index=roi))), - compression='gzip', - nchannels=len(self.lattice_data.channel_range), - subsamp=((1, 1, 1), (1, 2, 2), (2, 4, 4)), - overwrite=False - ) - for result in roi_results: - bdv_writer.append_view( - np.array(result.data), - time=result.time, - channel=result.channel, - voxel_size_xyz=(self.lattice_data.dx, self.lattice_data.dy, self.lattice_data.new_dz), - voxel_units='um' - ) - bdv_writer.write_xml() - bdv_writer.close() - elif self.lattice_data.save_type == SaveFileType.tiff: - # For each time point, we write a separate TIFF - for time, results in groupby(roi_results, key=lambda it: it.time): - result_list = list(results) - first_result = result_list[0] - images_array = np.swapaxes(np.expand_dims([result.data for result in result_list], axis=0), 1, 2) - tifffile.imwrite( - str(self.lattice_data.make_filepath(make_filename_prefix(channel=first_result.channel, time=time, roi_index=roi))), - data = images_array, - bigtiff=True, - resolution=(1./self.lattice_data.dx, 1./self.lattice_data.dy, "MICROMETER"), - metadata={'spacing': self.lattice_data.new_dz, 'unit': 'um', 'axes': 'TZCYX'}, - imagej=True - ) - workflow: Optional[Workflow] = Field( default=None, description="If defined, this is a workflow to add lightsheet processing onto" @@ -312,16 +226,17 @@ def slice_data(self, time: int, channel: int) -> DataArray: return self.input_image.isel(T=time, C=channel) - def iter_slices(self) -> Iterable[SlicedData[ArrayLike]]: + def iter_slices(self) -> Iterable[ProcessedSlice[ArrayLike]]: """ Yields array slices for each time and channel of interest. Returns: An iterable of tuples. Each tuple contains (time_index, time, channel_index, channel, slice) """ + from lls_core.models.results import ProcessedSlice for time_idx, time in enumerate(self.time_range): for ch_idx, ch in enumerate(self.channel_range): - yield SlicedData( + yield ProcessedSlice( data=self.slice_data(time=time, channel=ch), time_index=time_idx, time= time, @@ -329,7 +244,7 @@ def iter_slices(self) -> Iterable[SlicedData[ArrayLike]]: channel=ch, ) - def iter_sublattices(self, update_with: dict = {}) -> Iterable[SlicedData[LatticeData]]: + def iter_sublattices(self, update_with: dict = {}) -> Iterable[ProcessedSlice[LatticeData]]: """ Yields copies of the current LatticeData, one for each slice. These copies can then be processed separately. @@ -337,16 +252,17 @@ def iter_sublattices(self, update_with: dict = {}) -> Iterable[SlicedData[Lattic update_with: dictionary of arguments to update the generated lattices with """ for subarray in self.iter_slices(): - # Copy doesn't manu new_lattice = self.copy_validate(update={ "input_image": subarray.data, + "time_range": range(1), + "channel_range": range(1), **update_with }) yield subarray.copy_with_data( new_lattice) def generate_workflows( self, - ) -> Iterable[SlicedData[Workflow]]: + ) -> Iterable[ProcessedSlice[Workflow]]: """ Yields copies of the input workflow, modified with the addition of deskewing and optionally, cropping and deconvolution @@ -396,7 +312,7 @@ def deskewed_volume(self) -> DaskArray: from dask.array import zeros return zeros(self.deskew_vol_shape) - def _process_crop(self) -> Iterable[ProcessedVolume]: + def _process_crop(self) -> Iterable[ImageSlice]: """ Yields processed image slices with cropping enabled """ @@ -436,7 +352,7 @@ def _process_crop(self) -> Iterable[ProcessedVolume]: ) ) - def _process_non_crop(self) -> Iterable[ProcessedVolume]: + def _process_non_crop(self) -> Iterable[ImageSlice]: """ Yields processed image slices without cropping """ @@ -477,7 +393,9 @@ def _process_non_crop(self) -> Iterable[ProcessedVolume]: )) ) - def _process_workflow(self) -> ProcessedSlices: + def process_workflow(self) -> WorkflowSlices: + from lls_core.models.results import WorkflowSlices + WorkflowSlices.update_forward_refs(LatticeData=LatticeData) outputs = [] for workflow in self.generate_workflows(): for leaf in workflow.data.leafs(): @@ -487,7 +405,7 @@ def _process_workflow(self) -> ProcessedSlices: ) ) - return ProcessedSlices( + return WorkflowSlices( slices = outputs, lattice_data=self ) @@ -497,11 +415,12 @@ def process(self) -> ProcessedSlices: Execute the processing and return the result. This is the main public API for processing """ - ProcessedSlices.update_forward_refs() + from lls_core.models.results import ProcessedSlices + ProcessedSlices.update_forward_refs(LatticeData=LatticeData) - if self.workflow is not None: - return self._process_workflow() - elif self.cropping_enabled: + # if self.workflow is not None: + # return self._process_workflow() + if self.cropping_enabled: return ProcessedSlices( lattice_data=self, slices=self._process_crop() @@ -520,3 +439,11 @@ def process_into_image(self) -> ArrayLike: for slice in self.process().slices: return slice.data raise Exception("No slices produced!") + + def get_writer(self) -> Type[Writer]: + from lls_core.writers import BdvWriter, TiffWriter + if self.save_type == SaveFileType.h5: + return BdvWriter + elif self.save_type == SaveFileType.tiff: + return TiffWriter + raise Exception("Unknown output type") diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 50c69b3..e80fa87 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -2,9 +2,13 @@ from strenum import StrEnum from os import getcwd from pathlib import Path +from typing import TYPE_CHECKING from lls_core.models.utils import FieldAccessMixin, enum_choices +if TYPE_CHECKING: + from lls_core.writers import Writer, BdvWriter, TiffWriter + #Choice of File extension to save class SaveFileType(StrEnum): h5 = "h5" diff --git a/core/lls_core/models/results.py b/core/lls_core/models/results.py new file mode 100644 index 0000000..c8498a5 --- /dev/null +++ b/core/lls_core/models/results.py @@ -0,0 +1,128 @@ +from __future__ import annotations +from itertools import groupby +from pathlib import Path + +from typing import Iterable, Optional, Tuple, Union, cast, TYPE_CHECKING +from typing_extensions import Generic, TypeVar +from pydantic import BaseModel, NonNegativeInt +from lls_core.types import ArrayLike, is_arraylike +from lls_core.utils import make_filename_prefix +from lls_core.writers import RoiIndex, Writer +from pandas import DataFrame + +if TYPE_CHECKING: + from lls_core.models.lattice_data import LatticeData + +T = TypeVar("T") +S = TypeVar("S") +class ProcessedSlice(BaseModel, Generic[T], arbitrary_types_allowed=True): + """ + A single slice of some data that is split across multiple slices along time or channel axes + This class is generic over T, the type of data that is sliced. + """ + data: T + time_index: NonNegativeInt + time: NonNegativeInt + channel_index: NonNegativeInt + channel: NonNegativeInt + roi_index: Optional[NonNegativeInt] = None + + def copy_with_data(self, data: S) -> ProcessedSlice[S]: + """ + Return a modified version of this with new inner data + """ + from typing_extensions import cast + return cast( + ProcessedSlice[S], + self.copy(update={ + "data": data + }) + ) + +class ProcessedSlices(BaseModel, Generic[T], arbitrary_types_allowed=True): + """ + The main output from deskewing. A class that holds an iterable of + output image slices. + """ + #: Iterable of result slices. + #: Note that this is a finite iterator that can only be iterated once + slices: Iterable[ProcessedSlice[T]] + + #: The "parent" LatticeData that was used to create this result + lattice_data: LatticeData + +ImageSlice = ProcessedSlice[ArrayLike] +class ImageSlices(ProcessedSlices[ArrayLike]): + """ + A collection of image slices + """ + def save_image(self): + """ + Saves result slices to disk + """ + Writer = self.lattice_data.get_writer() + for roi, roi_results in groupby(self.slices, key=lambda it: it.roi_index): + writer = Writer(self.lattice_data, roi_index=roi) + for slice in roi_results: + writer.write_slice(slice) + + +RawWorkflowOutput = Union[ + ArrayLike, + dict, + list +] +ProcessedWorkflowOutput = Union[ + # A path indicates a saved file + Path, + DataFrame +] + +class WorkflowSlices(ProcessedSlices[Tuple[RawWorkflowOutput]]): + def process(self) -> Iterable[Tuple[RoiIndex, ProcessedWorkflowOutput]]: + """ + Incrementally processes the workflow outputs, and returns both image paths and data frames of the outputs, + for image slices and dict/list outputs respectively + """ + from pathlib import Path + import pandas as pd + + for roi, roi_results in groupby(self.slices, key=lambda it: it.roi_index): + values = [] + for result in roi_results: + # Ensure the data is in a tuple + data = (result.data,) if is_arraylike(result.data) else result.data + for i, element in enumerate(data): + if is_arraylike(element): + # Make the writer the first time only + if len(values) <= i: + values.append(self.lattice_data.get_writer()(self.lattice_data, roi_index=roi)) + + writer = cast(Writer, values[i]) + writer.write_slice( + result.copy_with_data( + element + ) + ) + else: + if len(values) <= i: + values.append([]) + + rows = cast(list, values[i]) + rows.append(element) + + for element in values: + if isinstance(element, Writer): + element.close() + yield roi, Path(element.get_filepath()) + else: + yield roi, pd.DataFrame(element) + + def save(self) -> Iterable[Path]: + for roi, result in self.process(): + if isinstance(result, DataFrame): + path = self.lattice_data.make_filepath(make_filename_prefix(roi_index=roi)) + result.to_csv(str(path)) + yield path + else: + yield result diff --git a/core/lls_core/utils.py b/core/lls_core/utils.py index d27c0aa..0def3c6 100644 --- a/core/lls_core/utils.py +++ b/core/lls_core/utils.py @@ -319,3 +319,18 @@ def array_to_dask(arr: ArrayLike) -> DaskArray: return arr else: return from_array(arr) + +def make_filename_prefix(prefix: Optional[str] = None, roi_index: Optional[str] = None, channel: Optional[str] = None, time: Optional[str] = None) -> str: + """ + Generates a filename for this result + """ + components: List[str] = [] + if prefix is not None: + components.append(prefix) + if roi_index is not None: + components.append(f"ROI_{roi_index}") + if channel is not None: + components.append(f"C{channel}") + if time is not None: + components.append(f"T{time}") + return "_".join(components) diff --git a/core/lls_core/writers.py b/core/lls_core/writers.py new file mode 100644 index 0000000..e8cf5ab --- /dev/null +++ b/core/lls_core/writers.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from itertools import groupby +from typing import TYPE_CHECKING, Iterator, List, Optional + +from lls_core.types import ArrayLike + +from pydantic import NonNegativeInt + +from lls_core.utils import make_filename_prefix +RoiIndex = Optional[NonNegativeInt] + +if TYPE_CHECKING: + from lls_core.models.lattice_data import LatticeData + import npy2bdv + from lls_core.models.results import ProcessedSlice + + +@dataclass +class Writer(ABC): + lattice: LatticeData + roi_index: RoiIndex + + @abstractmethod + def get_filepath(self) -> str: + pass + + @abstractmethod + def write_slice(self, slice: ProcessedSlice[ArrayLike]): + pass + + def close(self): + pass + +@dataclass +class BdvWriter(Writer): + bdv_writer: npy2bdv.BdvWriter = field(init=False) + + def get_filepath(self) -> str: + return str(self.lattice.make_filepath(make_filename_prefix(roi_index=self.roi_index))) + + def __post_init__(self): + import npy2bdv + self.bdv_writer = npy2bdv.BdvWriter( + filename=self.get_filepath(), + compression='gzip', + nchannels=len(self.lattice.channel_range), + subsamp=((1, 1, 1), (1, 2, 2), (2, 4, 4)), + overwrite=False + ) + + def write_slice(self, slice: ProcessedSlice[ArrayLike]): + import numpy as np + self.bdv_writer.append_view( + np.array(slice.data), + time=slice.time, + channel=slice.channel, + voxel_size_xyz=(self.lattice.dx, self.lattice.dy, self.lattice.new_dz), + voxel_units='um' + ) + + def close(self): + self.bdv_writer.write_xml() + self.bdv_writer.close() + +@dataclass +class TiffWriter(Writer): + pending_slices: list = field(default_factory=list, init=False) + time: Optional[NonNegativeInt] = None + + def get_filepath(self) -> str: + return str(self.lattice.make_filepath(make_filename_prefix(roi_index=self.roi_index))) + + def __post_init__(self): + self.pending_slices = [] + + def write_slice(self, slice: ProcessedSlice[ArrayLike]): + import numpy as np + import tifffile + if slice.time != self.time and len(self.pending_slices) > 0: + # Write the old timepoint once we + first_result = self.pending_slices[0] + images_array = np.swapaxes(np.expand_dims([result.data for result in self.pending_slices], axis=0), 1, 2) + tifffile.imwrite( + str(self.lattice.make_filepath(make_filename_prefix(channel=first_result.channel, time=slice.time, roi_index=slice.roi))), + data = images_array, + bigtiff=True, + resolution=(1./self.lattice.dx, 1./self.lattice.dy, "MICROMETER"), + metadata={'spacing': self.lattice.new_dz, 'unit': 'um', 'axes': 'TZCYX'}, + imagej=True + ) + + # Reinitialise + self.pending_slices = [] + + self.time = slice.time + self.pending_slices.append(slice) diff --git a/core/tests/conftest.py b/core/tests/conftest.py index 01d3b8f..e9fbcfb 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -1,3 +1,4 @@ +from importlib_resources import as_file from typer.testing import CliRunner import pytest from skimage.io import imsave @@ -6,6 +7,8 @@ import pyclesperanto_prototype as cle import tempfile from numpy.typing import NDArray +from copy import copy +from lls_core.sample import resources from napari_workflows import Workflow from napari_workflows._io_yaml_v1 import save_workflow @@ -14,15 +17,46 @@ def runner() -> CliRunner: return CliRunner() +@pytest.fixture(params=[ + "RBC_tiny.czi", + "RBC_lattice.tif", + "LLS7_t1_ch1.czi", + "LLS7_t1_ch3.czi", + "LLS7_t2_ch1.czi", + "LLS7_t2_ch3.czi", + "multich_multi_time.tif" +]) +def image_path(request: pytest.FixtureRequest): + """ + Fixture function that yields test images as file paths + """ + with as_file(resources / request.param) as image_path: + yield image_path + @pytest.fixture -def workflow() -> Workflow: - # Instantiate segmentation workflow +def image_workflow() -> Workflow: + # Simple segmentation workflow that returns an image image_seg_workflow = Workflow() image_seg_workflow.set("gaussian", cle.gaussian_blur, "input", sigma_x=1, sigma_y=1, sigma_z=1) image_seg_workflow.set("binarisation", cle.threshold, "gaussian", constant=0.5) image_seg_workflow.set("labeling", cle.connected_components_labeling_box, "binarisation") return image_seg_workflow +@pytest.fixture +def table_workflow(image_workflow: Workflow) -> Workflow: + # Complex workflow that returns a tuple of an image and a value + ret = copy(image_workflow) + # Returns a tuple of: (image, dict, list) + ret.set("result", lambda x: ( + x, + { + "foo": 1, + "bar": 2 + }, + ["foo", "bar"] + ), "labeling") + return ret + @pytest.fixture def test_image() -> NDArray[np.float64]: raw = np.zeros((5, 5, 5)) diff --git a/core/tests/test_process.py b/core/tests/test_process.py index c81a945..7de0440 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -6,6 +6,7 @@ import tempfile from pathlib import Path from napari_workflows import Workflow +from pytest import FixtureRequest from .params import inputs, parameterized @@ -50,13 +51,20 @@ def test_process_deconvolution(args: dict, background: Any): assert slice.data.ndim == 3 @parameterized -def test_process_workflow(args: dict, workflow: Workflow): - for slice in LatticeData.parse_obj({ - "input_image": root / "raw.tif", - "workflow": workflow, - **args - }).process().slices: - assert slice.data.ndim == 3 +@pytest.mark.parametrize(["workflow_name"], [("image_workflow", ), ("table_workflow", )]) +def test_process_workflow(args: dict, request: FixtureRequest, workflow_name: str): + from pandas import DataFrame + + workflow: Workflow = request.getfixturevalue(workflow_name) + with tempfile.TemporaryDirectory() as tmpdir: + for roi, output in LatticeData.parse_obj({ + "input_image": root / "raw.tif", + "workflow": workflow, + "save_dir": tmpdir, + **args + }).process_workflow().process(): + assert roi is None or isinstance(roi, int) + assert isinstance(output, (Path, DataFrame)) @pytest.mark.parametrize(["roi_subset"], [ [None], diff --git a/core/tests/test_workflows.py b/core/tests/test_workflows.py index c149e51..d38d5ae 100644 --- a/core/tests/test_workflows.py +++ b/core/tests/test_workflows.py @@ -1,15 +1,20 @@ from typing import Callable -import os from copy import copy +from importlib_resources import as_file from numpy.typing import NDArray from napari_workflows import Workflow import tempfile +from pandas import DataFrame +from lls_core.models.lattice_data import LatticeData +from lls_core.sample import resources +from aicsimageio.aics_image import AICSImage + from tests.utils import invoke from pathlib import Path -from .params import config_types -from .utils import invoke +from .params import config_types, inputs +from .utils import invoke, valid_image_path def test_napari_workflow(workflow: Workflow, test_image: NDArray): @@ -50,3 +55,30 @@ def test_workflow_cli(workflow_config_cli: dict, save_func: Callable, cli_param: label_img = h5_file.read_view(time=0, channel=0) assert label_img.shape == (3, 14, 5) assert label_img[1, 6, 2] == 1 + +def test_image_workflow(image_path: Path, image_workflow: Workflow): + # Test that a regular workflow that returns an image directly works + with tempfile.TemporaryDirectory() as tmpdir: + for roi, output in LatticeData( + input_image = image_path, + workflow = image_workflow, + save_dir = tmpdir + ).process_workflow().process(): + assert isinstance(output, Path) + assert valid_image_path(output) + +def test_table_workflow(image_path: Path, table_workflow: Workflow): + # Test a complex workflow that returns a tuple of images and data + with tempfile.TemporaryDirectory() as tmpdir: + for roi, output in LatticeData( + input_image = image_path, + workflow = table_workflow, + save_dir = tmpdir + ).process_workflow().process(): + assert isinstance(output, (DataFrame, Path)) + if isinstance(output, DataFrame): + nrow, ncol = output.shape + assert nrow > 0 + assert ncol > 0 + else: + assert valid_image_path(output) diff --git a/core/tests/utils.py b/core/tests/utils.py index ad68041..c9744a0 100644 --- a/core/tests/utils.py +++ b/core/tests/utils.py @@ -1,6 +1,17 @@ +from pathlib import Path from typing import Sequence from typer.testing import CliRunner from lls_core.cmds.__main__ import app +import npy2bdv +from aicsimageio import AICSImage def invoke(args: Sequence[str]): CliRunner().invoke(app, args, catch_exceptions=False) + +def valid_image_path(path: Path) -> bool: + if path.suffix in {".hdf5", ".h5"}: + npy2bdv.npy2bdv.BdvEditor(str(path)).read_view() + return True + else: + AICSImage(path).get_image_data() + return True diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 8a75cdf..a277a64 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -65,7 +65,7 @@ def _make_model(self) -> LatticeData: class LlszMenu(MagicTemplate): main_heading = field("

      Napari Lattice: Visualization & Analysis

      ", widget_type="Label") - heading1 = field("Drag and drop an image file onto napari.", widget_type="Label") + heading1 = field("Select the tabs one at a time to specify analysis parameters. Tabs 1 and 5 are mandatory.", widget_type="Label") # Tabbed Widget container to house all the widgets @magicclass(widget_type="tabbed", name="Functions", labels=False) From 72be81e0e74ebf99d8feb556427483bdcb611fbd Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 30 Oct 2023 12:33:53 +1100 Subject: [PATCH 084/147] Document the new IO modules, remove the old io.py --- core/lls_core/cmds/__main__.py | 2 +- core/lls_core/io.py | 618 --------------------------- core/lls_core/models/lattice_data.py | 22 +- core/lls_core/models/results.py | 24 +- core/lls_core/writers.py | 71 ++- core/tests/conftest.py | 8 +- core/tests/params.py | 10 - core/tests/test_process.py | 25 +- core/tests/test_workflows.py | 6 +- 9 files changed, 104 insertions(+), 682 deletions(-) delete mode 100644 core/lls_core/io.py diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 65c4fac..1fd8919 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -178,7 +178,7 @@ def process( # Console().print(ctx.get_help()) raise Exit(code=1) - lattice.process().save_image() + lattice.save() def main(): app() diff --git a/core/lls_core/io.py b/core/lls_core/io.py deleted file mode 100644 index 4862035..0000000 --- a/core/lls_core/io.py +++ /dev/null @@ -1,618 +0,0 @@ -from __future__ import annotations -# Opening and saving files -import aicsimageio -from aicsimageio.types import ImageLike, ArrayLike -from aicsimageio import AICSImage - -from pathlib import Path - -import pyclesperanto_prototype as cle -import sys -import dask -from resource_backed_dask_array import ResourceBackedDaskArray -import dask.array as da -from dask.array.core import Array as DaskArray -import pandas as pd -from typing import TYPE_CHECKING, Callable, Optional, Literal, Any -from os import PathLike - -from dask.distributed import Client -from dask.cache import Cache - -from lls_core.utils import etree_to_dict -from lls_core.llsz_core import crop_volume_deskew -from lls_core.deconvolution import skimage_decon, pycuda_decon -from lls_core import config, DeconvolutionChoice, SaveFileType -from lls_core.models.lattice_data import LatticeData - -import os -import numpy as np -from tqdm import tqdm -from tifffile import imwrite, TiffWriter - -import npy2bdv - -# Enable Logging -import logging -logger = logging.getLogger(__name__) - -if TYPE_CHECKING: - from napari.types import ImageData - from napari_workflows import Workflow - -def convert_imgdata_aics(img_data: ImageData): - """Return AICSimage object from napari ImageData type - - Args: - img_data ([type]): [description] - - Returns: - AICImage: [description] - """ - # Error handling for czi file - try: - # stack=aicsimageio.imread_dask(img_location) - # using AICSImage will read data as STCZYX - stack = aicsimageio.AICSImage(img_data) - # stack_meta=aicsimageio.imread_dask(img_location) - except Exception: - print("Error: A ", sys.exc_info()[ - 0], "has occurred. See below for details.") - raise - - # Dask setup - # Setting up dask scheduler - # start distributed scheduler locally. Launch dashboard - client = Client(processes=False) - # memory_limit='30GB', - dask.config.set({"temporary_directory": "C:\\Dask_temp\\", "optimization.fuse.active": False, - 'array.slicing.split_large_chunks': False}) - cache = Cache(2e9) - cache.register() - print("If using dask, dask Client can be viewed at:", client.dashboard_link) - - # Check metadata to verify postprocessing or any modifications by Zen - return stack - - -# will flesh this out once Zeiss lattice has more relevant metadata in the czi file -def check_metadata(img_path: ImageLike): - print("Checking CZI metadata") - metadatadict_czi = etree_to_dict(aicsimageio.AICSImage(img_path).metadata) - metadatadict_czi = metadatadict_czi["ImageDocument"]["Metadata"] - acquisition_mode_setup = metadatadict_czi["Experiment"]["ExperimentBlocks"][ - "AcquisitionBlock"]["HelperSetups"]["AcquisitionModeSetup"]["Detectors"] - print(acquisition_mode_setup["Detector"]["ImageOrientation"]) - print("Image Orientation: If any post processing has been applied, it will appear here.\n \ - For example, Zen 3.2 flipped the image so the coverslip was towards the bottom of Z stack. So, value will be 'Flip'") - -# TODO: write save function for deskew and for crop - - -def save_img(vol: ArrayLike, - func: Callable, - time_start: int, - time_end: int, - channel_start: int, - channel_end: int, - save_file_type: SaveFileType, - save_path: PathLike, - save_name_prefix: str = "", - save_name: str = "img", - dx: float = 1, - dy: float = 1, - dz: float = 1, - angle: Optional[float] = None, - # This is a MagicTemplate object but we want to avoid depending on magicclass - # TODO: refactor this out - LLSZWidget: Optional[Any]=None, - terminal: bool = False, - lattice: Optional[LatticeData]=None, - *args, **kwargs): - """ - Applies a function as described in callable - Args: - vol (_type_): Volume to process - func (callable): _description_ - time_start (int): _description_ - time_end (int): _description_ - channel_start (int): _description_ - channel_end (int): _description_ - save_file_type: either 'tiff' or SaveFileType.h5 - save_path (Path): _description_ - save_name_prefix (str, optional): Add a prefix to name. For example, if processng ROIs, add ROI_1_. Defaults to "". - save_name (str, optional): name of file being saved. Defaults to "img". - dx (float, optional): _description_. Defaults to 1. - dy (float, optional): _description_. Defaults to 1. - dz (float, optional): _description_. Defaults to 1. - angle(float, optional) = Deskewing angle in degrees, used to calculate new z - LLSZWidget(class,optional) = LLSZWidget class - """ - - # save_path = save_path.__str__() - _save_path: Path = Path(save_path) - - # replace any : with _ and remove spaces in case it hasn't been processed/skipped - save_name = str(save_name).replace(":", "_").replace(" ", "") - - time_range = range(time_start, time_end+1) - - channel_range = range(channel_start, channel_end+1) - - # Calculate new_pixel size in z after deskewing - if angle > 0: - import math - new_dz = math.sin(angle * math.pi / 180.0) * dz - else: - new_dz = dz - - if func is crop_volume_deskew: - # create folder for each ROI; disabled as each roi is saved as hyperstack - save_name_prefix = save_name_prefix + "_" - #save_path = save_path+os.sep+save_name_prefix+os.sep - # if not os.path.exists(save_path): - # os.makedirs(save_path) - im_final = [] - - # setup bdvwriter - if save_file_type == SaveFileType.h5: - if func is crop_volume_deskew: - save_path_h5 = _save_path / (save_name_prefix + "_" + save_name + ".h5") - else: - save_path_h5 = _save_path / (save_name + ".h5") - - bdv_writer = npy2bdv.BdvWriter(str(save_path_h5), - compression='gzip', - nchannels=len(channel_range), - subsamp=( - (1, 1, 1), (1, 2, 2), (2, 4, 4)), - overwrite=False) - - # bdv_writer = npy2bdv.BdvWriter(save_path_h5, compression=None, nchannels=len(channel_range)) #~30% faster, but up to 10x bigger filesize - else: - pass - - if terminal: - if lattice.decon_processing: - decon_value = True - decon_option = lattice.decon_processing # decon_processing holds the choice - lattice_class = lattice - logging.debug(f"Decon option {decon_option}") - - else: - try: - decon_value = LLSZWidget.LlszMenu.deconvolution.value - lattice_class = LLSZWidget.LlszMenu.lattice - decon_option = LLSZWidget.LlszMenu.lattice.decon_processing - except: - decon_value = 0 - lattice_class = 0 - decon_option = 0 - - # loop is ordered so image is saved in order TCZYX for ometiffwriter - for loop_time_idx, time_point in enumerate(tqdm(time_range, desc="Time", position=0)): - images_array = [] - for loop_ch_idx, ch in enumerate(tqdm(channel_range, desc="Channels", position=1, leave=False)): - try: - if len(vol.shape) == 3: - raw_vol = vol - elif len(vol.shape) == 4: - raw_vol = vol[time_point, :, :, :] - elif len(vol.shape) == 5: - raw_vol = vol[time_point, ch, :, :, :] - except IndexError: - assert ch <= channel_end, f"Channel out of range. Got {ch}, but image has channels {channel_end+1}" - assert time_point <= channel_end, f"Channel out of range. Got {ch}, but image has channels {channel_end+1}" - assert len(vol.shape) in [ - 3, 4, 5], f"Check shape of volume. Expected volume with shape 3,4 or 5. Got {vol.shape} with shape {len(vol.shape)}" - print(f"Using time points {time_point} and channel {ch}") - exit() - - #raw_vol = np.array(raw_vol) - image_type = raw_vol.dtype - #print(decon_value) - #print(decon_option) - #print(func) - # Add a check for last timepoint, in case acquisition incomplete - if time_point == time_end: - orig_shape = raw_vol.shape - raw_vol = raw_vol.compute() - if raw_vol.shape != orig_shape: - print( - f"Time {time_point}, channel {ch} is incomplete. Actual shape {orig_shape}, got {raw_vol.shape}") - z_diff, y_diff, x_diff = np.subtract( - orig_shape, raw_vol.shape) - print(f"Padding with{z_diff,y_diff,x_diff}") - raw_vol = np.pad( - raw_vol, ((0, z_diff), (0, y_diff), (0, x_diff))) - assert raw_vol.shape == orig_shape, f"Shape of last timepoint still doesn't match. Got {raw_vol.shape}" - - # If deconvolution is checked - if decon_value and func != crop_volume_deskew: - # Use CUDA or skimage for deconvolution based on user choice - if decon_option == DeconvolutionChoice.cuda_gpu: - raw_vol = pycuda_decon(image=raw_vol, - #otf_path = LLSZWidget.LlszMenu.lattice.otf_path[ch], - psf=lattice_class.psf[ch], - dzdata=lattice_class.dz, - dxdata=lattice_class.dx, - dzpsf=lattice_class.dz, - dxpsf=lattice_class.dx, - num_iter=lattice_class.psf_num_iter) - else: - raw_vol = skimage_decon(vol_zyx=raw_vol, - psf=lattice_class.psf[ch], - num_iter=lattice_class.psf_num_iter, - clip=False, filter_epsilon=0, boundary='nearest') - - # The following will apply the user-passed function to the input image - if func is crop_volume_deskew and decon_value == True: - processed_vol = func(original_volume=raw_vol, - deconvolution=decon_value, - decon_processing=decon_option, - psf=lattice_class.psf[ch], - num_iter=lattice_class.psf_num_iter, - *args, **kwargs).astype(image_type) - - elif func is cle.deskew_y or func is cle.deskew_x: - processed_vol = func(input_image=raw_vol, - *args, **kwargs).astype(image_type) - - elif func is crop_volume_deskew: - processed_vol = func( - original_volume=raw_vol, *args, **kwargs).astype(image_type) - else: - # if its not deskew or crop/deskew, apply the user-passed function and any specific parameters - processed_vol = func(*args, **kwargs).astype(image_type) - - processed_vol = cle.pull_zyx(processed_vol) - - if save_file_type == SaveFileType.h5: - # convert opencl array to dask array - #pvol = da.asarray(processed_vol) - # channel and time index are based on loop iteration - bdv_writer.append_view(processed_vol, - time=loop_time_idx, - channel=loop_ch_idx, - voxel_size_xyz=(dx, dy, new_dz), - voxel_units='um') - - #print("\nAppending volume to h5\n") - else: - images_array.append(processed_vol) - - # if function is not for cropping, then dataset can be quite large, so save each channel and timepoint separately - # otherwise, append it into im_final - - if func != crop_volume_deskew and save_file_type == SaveFileType.tiff: - final_name = _save_path / (save_name_prefix + "C" + str(ch) + "T" + str( time_point) + "_" + save_name+".tif") - images_array = np.array(images_array) - images_array = np.expand_dims(images_array, axis=0) - images_array = np.swapaxes(images_array, 1, 2) - imwrite(final_name, - images_array, - bigtiff=True, - resolution=(1./dx, 1./dy, "MICROMETER"), - metadata={'spacing': new_dz, 'unit': 'um', 'axes': 'TZCYX'}, imagej=True) - elif save_file_type == SaveFileType.tiff: - # convert list of arrays into a numpy array and then append to im_final - im_final.append(np.array(images_array)) - - # close the h5 writer or if its tiff, save images - if save_file_type == SaveFileType.h5: - bdv_writer.write_xml() - bdv_writer.close() - - elif func is crop_volume_deskew and save_file_type == SaveFileType.tiff: - - im_final = np.array(im_final) - final_name = _save_path / (save_name_prefix + "_" + save_name + ".tif") - - im_final = np.swapaxes(im_final, 1, 2) - # imagej=True; ImageJ hyperstack axes must be in TZCYXS order - - imwrite(final_name, - im_final, - # specify resolution unit for consistent metadata) - resolution=(1./dx, 1./dy, "MICROMETER"), - metadata={'spacing': new_dz, 'unit': 'um', 'axes': 'TZCYX'}, - imagej=True) - im_final = None - - -def save_img_workflow(vol, - workflow: Workflow, - input_arg: str, - first_task: str, - last_task: str, - time_start: int, - time_end: int, - channel_start: int, - channel_end: int, - save_file_type, - save_path: Path, - save_name_prefix: str = "", - save_name: str = "img", - dx: float = 1, - dy: float = 1, - dz: float = 1, - angle: float = None, - deconvolution: bool = False, - decon_processing: str = None, - psf_arg=None, - psf=None, - otf_path=None): - """ - Applies a workflow to the image and saves the output - Use of workflows ensures its agnostic to the processing operation - Args: - vol (_type_): Volume to process - workflow (Workflow): napari workflow - input_arg (str): name for input image - task_name (str): name of the task that should be executed in the workflow - time_start (int): _description_ - time_start (int): _description_ - time_end (int): _description_ - channel_start (int): _description_ - channel_end (int): _description_ - save_path (Path): _description_ - save_name_prefix (str, optional): Add a prefix to name. For example, if processng ROIs, add ROI_1_. Defaults to "". - save_name (str, optional): name of file being saved. Defaults to "img". - dx (float, optional): _description_. Defaults to 1. - dy (float, optional): _description_. Defaults to 1. - dz (float, optional): _description_. Defaults to 1. - angle(float, optional) = Deskewing angle in degrees, used to calculate new z - """ - - # TODO: Implement h5 saving - - save_path = save_path.__str__() - - # replace any : with _ and remove spaces in case it hasn't been processed/skipped - save_name = save_name.replace(":", "_").replace(" ", "") - - # adding +1 at the end so the last channel and time is included - - time_range = range(time_start, time_end + 1) - channel_range = range(channel_start, channel_end + 1) - - # Calculate new_pixel size in z - # convert voxel sixes to an aicsimage physicalpixelsizes object for metadata - if angle: - import math - new_dz = math.sin(angle * math.pi / 180.0) * dz - #aics_image_pixel_sizes = PhysicalPixelSizes(new_dz,dy,dx) - else: - #aics_image_pixel_sizes = PhysicalPixelSizes(dz,dy,dx) - new_dz = dz - - # get list of all functions in the workflow - workflow_functions = [i[0] for i in workflow._tasks.values()] - - # iterate through time and channels and apply workflow - # TODO: add error handling so the image writers will "close",if an error causes the program to exit - # try except? - for loop_time_idx, time_point in enumerate(tqdm(time_range, desc="Time", position=0)): - output_array = [] - data_table = [] - for loop_ch_idx, ch in enumerate(tqdm(channel_range, desc="Channels", position=1, leave=False)): - - if len(vol.shape) == 3: - raw_vol = vol - else: - raw_vol = vol[time_point, ch, :, :, :] - - # TODO: disable if support for resourc backed dask array is added - # if type(raw_vol) in [resource_backed_dask_array]: - # raw_vol = raw_vol.compute() #convert to numpy array as resource backed dask array not su - - # to access current time and channel, create a file config.py in same dir as workflow or in home directory - # add "channel = 0" and "time=0" in the file and save - # https://docs.python.org/3/faq/programming.html?highlight=global#how-do-i-share-global-variables-across-modules - - config.channel = ch - config.time = time_point - - # if deconvolution, need to define psf and choose the channel appropriate one - if deconvolution: - workflow.set(psf_arg, psf[ch]) - # if decon_processing == "cuda_gpu": - # workflow.set("psf",psf[ch]) - # else: - # workflow.set("psf",psf[ch]) - - # Add a check for last timepoint, in case acquisition incomplete or an incomplete frame - if time_point == time_end: - orig_shape = raw_vol.shape - raw_vol = raw_vol.compute() - if raw_vol.shape != orig_shape: - print( - f"Time {time_point}, channel {ch} is incomplete. Actual shape {orig_shape}, got {raw_vol.shape}") - z_diff, y_diff, x_diff = np.subtract( - orig_shape, raw_vol.shape) - print(f"Padding with{z_diff,y_diff,x_diff}") - raw_vol = np.pad( - raw_vol, ((0, z_diff), (0, y_diff), (0, x_diff))) - assert raw_vol.shape == orig_shape, f"Shape of last timepoint still doesn't match. Got {raw_vol.shape}" - - # Set input to the workflow to be volume from each time point and channel - workflow.set(input_arg, raw_vol) - # execute workflow - processed_vol = workflow.get(last_task) - - output_array.append(processed_vol) - - output_array = np.array(output_array) - - # if workflow returns multiple objects (images, dictionaries, lsits etc..), each object can be accessed by - # output_array[:,index_of_object] - - # use data from first timepoint to get the output type from workflow - # check if multiple objects in the workflow output, if so, get the index for each item - # currently, images, lists and dictionaries are supported - if loop_time_idx == 0: - - # get no of elements - - no_elements = len(processed_vol) - # initialize lsits to hold indexes for each datatype - list_element_index = [] # store indices of lists - dict_element_index = [] # store indices of dicts - # store indices of images (numpy array, dask array and pyclesperanto array) - image_element_index = [] - - # single output and is just dictionary - if isinstance(processed_vol, dict): - dict_element_index = [0] - # if image - elif isinstance(processed_vol, (np.ndarray, cle._tier0._pycl.OCLArray, DaskArray, ResourceBackedDaskArray)): - image_element_index = [0] - no_elements = 1 - # multiple elements - # list with values returns no_elements>1 so make sure its actually a list with different objects - # test this with different workflows - elif no_elements > 1 and type(processed_vol[0]) not in [np.int16, np.int32, np.float16, np.float32, np.float64, int, float]: - array_element_type = [type(output_array[0, i]) - for i in range(no_elements)] - image_element_index = [idx for idx, data_type in enumerate( - array_element_type) if data_type in [np.ndarray, cle._tier0._pycl.OCLArray, da.core.Array]] - dict_element_index = [idx for idx, data_type in enumerate( - array_element_type) if data_type in [dict]] - list_element_index = [idx for idx, data_type in enumerate( - array_element_type) if data_type in [list]] - elif type(processed_vol) is list: - list_element_index = [0] - - # setup required image writers - if len(image_element_index) > 0: - # pass list of images and index to function - writer_list = [] - # create an image writer for each image - for element in range(len(image_element_index)): - final_save_path = save_path + os.sep + save_name_prefix + "_" + \ - str(element)+"_" + save_name + \ - "." + save_file_type.value - # setup writer based on user choice of filetype - if save_file_type == SaveFileType.h5: - bdv_writer = npy2bdv.BdvWriter(final_save_path, - compression='gzip', - nchannels=len( - channel_range), - subsamp=( - (1, 1, 1), (1, 2, 2), (2, 4, 4)), - overwrite=True) # overwrite set to True; is this good practice? - writer_list.append(bdv_writer) - else: - # imagej =true throws an error - writer_list.append(TiffWriter( - final_save_path, bigtiff=True)) - - # handle image saving: either h5 or tiff saving - if len(image_element_index) > 0: - # writer_idx is for each writers, image_idx will be the index of images - for writer_idx, image_idx in enumerate(image_element_index): - # access the image - # print(len(time_range)) - if len(channel_range) == 1: - if (len(time_range)) == 1: # if only one timepoint - im_final = np.stack( - output_array[image_idx, ...]).astype(raw_vol.dtype) - else: - im_final = np.stack( - output_array[0, image_idx]).astype(raw_vol.dtype) - else: - im_final = np.stack( - output_array[:, image_idx]).astype(raw_vol.dtype) - if save_file_type == SaveFileType.h5: - for ch_idx in channel_range: - # write h5 images as 3D stacks - assert len( - im_final.shape) >= 3, f"Image shape should be >=3, got {im_final.shape}" - # print(im_final.shape) - if len(im_final.shape) == 3: - im_channel = im_final - elif len(im_final.shape) > 3: - im_channel = im_final[ch_idx, ...] - - writer_list[writer_idx].append_view(im_channel, - time=loop_time_idx, - channel=loop_ch_idx, - voxel_size_xyz=( - dx, dy, new_dz), - voxel_units='um') - else: # default to tif - # Use below with imagej=True - # if len(im_final.shape) ==4: #if only one image with no channel, then dimension will 1,z,y,x, so swap 0 and 1 - # im_final = np.swapaxes(im_final,0,1).astype(raw_vol.dtype) #was 1,2,but when stacking images, dimension is CZYX - # im_final = im_final[np.newaxis,...] #adding extra dimension for T - # elif len(im_final.shape)>4: - # im_final = np.swapaxes(im_final,1,2).astype(raw_vol.dtype) #if image with multiple channels, , it will be 1,c,z,y,x - # imagej=True; ImageJ hyperstack axes must be in TZCYXS order - #images_array = np.swapaxes(images_array,0,1).astype(raw_vol.dtype) - writer_list[writer_idx].write(im_final, - resolution=( - 1./dx, 1./dy, "MICROMETER"), - metadata={'spacing': new_dz, 'unit': 'um', 'axes': 'TZCYX', 'PhysicalSizeX': dx, - 'PhysicalSizeXUnit': 'µm', 'PhysicalSizeY': dy, 'PhysicalSizeYUnit': 'µm'}) - im_final = None - - # handle dict saving - # convert to pandas dataframe; update columns with channel and time - if len(dict_element_index) > 0: - # Iterate through the dict output from workflow and add columns for Channel and timepoint - for element in dict_element_index: - for j in channel_range: - output_array[j, element].update({"Channel": "C"+str(j)}) - output_array[j, element].update( - {"Time": "T"+str(time_point)}) - - # convert to pandas dataframe - output_dict_pd = [pd.DataFrame(i) - for i in output_array[:, element]] - - output_dict_pd = pd.concat(output_dict_pd) - # set index to the channel/time - output_dict_pd = output_dict_pd.set_index(["Time", "Channel"]) - - # Save path - dict_save_path = os.path.join( - save_path, "Measurement_"+save_name_prefix) - if not (os.path.exists(dict_save_path)): - os.mkdir(dict_save_path) - - #dict_save_path = os.path.join(dict_save_path,"C" + str(ch) + "T" + str(time_point)+"_"+str(element) + "_measurement.csv") - dict_save_path = os.path.join( - dict_save_path, "Summary_measurement_"+save_name_prefix+"_"+str(element)+"_.csv") - # Opens csv and appends it if file already exists; not efficient. - if os.path.exists(dict_save_path): - output_dict_pd_existing = pd.read_csv( - dict_save_path, index_col=["Time", "Channel"]) - output_dict_summary = pd.concat( - (output_dict_pd_existing, output_dict_pd)) - output_dict_summary.to_csv(dict_save_path) - else: - output_dict_pd.to_csv(dict_save_path) - - if len(list_element_index) > 0: - row_idx = [] - for element in dict_element_index: - for j in channel_range: - row_idx.append("C"+str(j)+"T"+str(time_point)) - output_list_pd = pd.DataFrame( - np.vstack(output_array[:, element]), index=row_idx) - # Save path - list_save_path = os.path.join( - save_path, "Measurement_"+save_name_prefix) - if not (os.path.exists(list_save_path)): - os.mkdir(list_save_path) - list_save_path = os.path.join(list_save_path, "C" + str(ch) + "T" + str( - time_point)+"_"+save_name_prefix+"_"+str(element) + "_measurement.csv") - output_list_pd.to_csv(list_save_path) - - if len(image_element_index) > 0: - for writer_idx in range(len(image_element_index)): - if save_file_type == SaveFileType.h5: - # write h5 metadata - writer_list[writer_idx].write_xml() - # close the writers (applies for both tiff and h5) - writer_list[writer_idx].close() diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 56f56a0..3653d81 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -410,27 +410,37 @@ def process_workflow(self) -> WorkflowSlices: lattice_data=self ) - def process(self) -> ProcessedSlices: + def process(self) -> ImageSlices: """ Execute the processing and return the result. - This is the main public API for processing + This will not execute the attached workflow. """ - from lls_core.models.results import ProcessedSlices - ProcessedSlices.update_forward_refs(LatticeData=LatticeData) + from lls_core.models.results import ImageSlices + ImageSlices.update_forward_refs(LatticeData=LatticeData) # if self.workflow is not None: # return self._process_workflow() if self.cropping_enabled: - return ProcessedSlices( + return ImageSlices( lattice_data=self, slices=self._process_crop() ) else: - return ProcessedSlices( + return ImageSlices( lattice_data=self, slices=self._process_non_crop() ) + def save(self): + """ + + This is the main public API for processing + """ + if self.workflow: + list(self.process_workflow().save()) + else: + self.process().save_image() + def process_into_image(self) -> ArrayLike: """ Shortcut method for calling process, then extracting one image layer. diff --git a/core/lls_core/models/results.py b/core/lls_core/models/results.py index c8498a5..213c655 100644 --- a/core/lls_core/models/results.py +++ b/core/lls_core/models/results.py @@ -41,8 +41,9 @@ def copy_with_data(self, data: S) -> ProcessedSlice[S]: class ProcessedSlices(BaseModel, Generic[T], arbitrary_types_allowed=True): """ - The main output from deskewing. A class that holds an iterable of - output image slices. + A generic parent class for holding deskewing outputs. + This will never be instantiated directly. + Refer to the concrete child classes for more detail. """ #: Iterable of result slices. #: Note that this is a finite iterator that can only be iterated once @@ -54,7 +55,9 @@ class ProcessedSlices(BaseModel, Generic[T], arbitrary_types_allowed=True): ImageSlice = ProcessedSlice[ArrayLike] class ImageSlices(ProcessedSlices[ArrayLike]): """ - A collection of image slices + A collection of image slices, which is the main output from deskewing. + This holds an iterable of output image slices before they are saved to disk, + and provides a `save_image()` method for this purpose. """ def save_image(self): """ @@ -65,6 +68,7 @@ def save_image(self): writer = Writer(self.lattice_data, roi_index=roi) for slice in roi_results: writer.write_slice(slice) + writer.close() RawWorkflowOutput = Union[ @@ -79,12 +83,16 @@ def save_image(self): ] class WorkflowSlices(ProcessedSlices[Tuple[RawWorkflowOutput]]): + """ + The counterpart of `ImageSlices`, but for workflow outputs. + This is needed because workflows have vastly different outputs that may include regular + Python types rather than only image slices. + """ def process(self) -> Iterable[Tuple[RoiIndex, ProcessedWorkflowOutput]]: """ Incrementally processes the workflow outputs, and returns both image paths and data frames of the outputs, for image slices and dict/list outputs respectively """ - from pathlib import Path import pandas as pd for roi, roi_results in groupby(self.slices, key=lambda it: it.roi_index): @@ -114,11 +122,17 @@ def process(self) -> Iterable[Tuple[RoiIndex, ProcessedWorkflowOutput]]: for element in values: if isinstance(element, Writer): element.close() - yield roi, Path(element.get_filepath()) + for file in element.written_files: + yield roi, file else: yield roi, pd.DataFrame(element) def save(self) -> Iterable[Path]: + """ + Processes all workflow outputs and saves them to disk. + Images are saved in the format specified in the `LatticeData`, while + other data types are saved as a data frame. + """ for roi, result in self.process(): if isinstance(result, DataFrame): path = self.lattice_data.make_filepath(make_filename_prefix(roi_index=roi)) diff --git a/core/lls_core/writers.py b/core/lls_core/writers.py index e8cf5ab..410eeb4 100644 --- a/core/lls_core/writers.py +++ b/core/lls_core/writers.py @@ -2,8 +2,7 @@ from abc import ABC, abstractmethod from dataclasses import dataclass, field -from itertools import groupby -from typing import TYPE_CHECKING, Iterator, List, Optional +from typing import TYPE_CHECKING, Iterable, Iterator, List, Optional from lls_core.types import ArrayLike @@ -15,41 +14,52 @@ if TYPE_CHECKING: from lls_core.models.lattice_data import LatticeData import npy2bdv - from lls_core.models.results import ProcessedSlice + from lls_core.models.results import ProcessedSlice, ImageSlice + from pathlib import Path @dataclass class Writer(ABC): + """ + A writer is an abstraction over the logic used to write image slices to disk + Writers need to work incrementally, in order that we don't need the entire multidimensional + image in memory at the same time + """ lattice: LatticeData roi_index: RoiIndex + written_files: List[Path] = field(default_factory=list, init=False) - @abstractmethod - def get_filepath(self) -> str: - pass - @abstractmethod def write_slice(self, slice: ProcessedSlice[ArrayLike]): + """ + Writes a 3D image slice + """ pass def close(self): + """ + Called when no more image slices are available, and the writer should finalise its output files + """ pass @dataclass class BdvWriter(Writer): + """ + A writer for for Fiji BigDataViewer output format + """ bdv_writer: npy2bdv.BdvWriter = field(init=False) - def get_filepath(self) -> str: - return str(self.lattice.make_filepath(make_filename_prefix(roi_index=self.roi_index))) - def __post_init__(self): import npy2bdv + path = self.lattice.make_filepath(make_filename_prefix(roi_index=self.roi_index)) self.bdv_writer = npy2bdv.BdvWriter( - filename=self.get_filepath(), + filename=str(path), compression='gzip', nchannels=len(self.lattice.channel_range), subsamp=((1, 1, 1), (1, 2, 2), (2, 4, 4)), overwrite=False ) + self.written_files.append(path) def write_slice(self, slice: ProcessedSlice[ArrayLike]): import numpy as np @@ -67,33 +77,52 @@ def close(self): @dataclass class TiffWriter(Writer): - pending_slices: list = field(default_factory=list, init=False) + """ + A writer for for TIFF output format + """ + pending_slices: List[ImageSlice] = field(default_factory=list, init=False) time: Optional[NonNegativeInt] = None - def get_filepath(self) -> str: - return str(self.lattice.make_filepath(make_filename_prefix(roi_index=self.roi_index))) - def __post_init__(self): self.pending_slices = [] - - def write_slice(self, slice: ProcessedSlice[ArrayLike]): + + def flush(self): + "Write out all pending slices" import numpy as np import tifffile - if slice.time != self.time and len(self.pending_slices) > 0: - # Write the old timepoint once we + if len(self.pending_slices) > 0: first_result = self.pending_slices[0] - images_array = np.swapaxes(np.expand_dims([result.data for result in self.pending_slices], axis=0), 1, 2) + images_array = np.swapaxes( + np.expand_dims([result.data for result in self.pending_slices], axis=0), + 1, 2 + ).astype("uint16") + # ImageJ TIFF can only handle 16-bit uints, not 32 + path = self.lattice.make_filepath( + make_filename_prefix( + channel=first_result.channel, + time=first_result.time, + roi_index=first_result.roi_index + ) + ) tifffile.imwrite( - str(self.lattice.make_filepath(make_filename_prefix(channel=first_result.channel, time=slice.time, roi_index=slice.roi))), + str(path), data = images_array, bigtiff=True, resolution=(1./self.lattice.dx, 1./self.lattice.dy, "MICROMETER"), metadata={'spacing': self.lattice.new_dz, 'unit': 'um', 'axes': 'TZCYX'}, imagej=True ) + self.written_files.append(path) # Reinitialise self.pending_slices = [] + def write_slice(self, slice: ProcessedSlice[ArrayLike]): + if slice.time != self.time: + self.flush() + self.time = slice.time self.pending_slices.append(slice) + + def close(self): + self.flush() diff --git a/core/tests/conftest.py b/core/tests/conftest.py index e9fbcfb..22e05ed 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -64,22 +64,22 @@ def test_image() -> NDArray[np.float64]: return raw @pytest.fixture -def workflow_config(workflow: Workflow, test_image: NDArray): +def workflow_config(image_workflow: Workflow, test_image: NDArray): # Create a config file yield { "input_image": test_image, - "workflow": workflow, + "workflow": image_workflow, } @pytest.fixture -def workflow_config_cli(workflow: Workflow, test_image: NDArray): +def workflow_config_cli(image_workflow: Workflow, test_image: NDArray): with tempfile.TemporaryDirectory() as tempdir_str: tempdir = Path(tempdir_str) input = tempdir / "raw.tiff" output = tempdir / "output" output.mkdir(parents=True) workflow_path = tempdir / "workflow.json" - save_workflow(str(workflow_path), workflow) + save_workflow(str(workflow_path), image_workflow) # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) imsave(input, test_image) diff --git a/core/tests/params.py b/core/tests/params.py index afc4c6c..4e927bf 100644 --- a/core/tests/params.py +++ b/core/tests/params.py @@ -4,16 +4,6 @@ from lls_core.models.output import SaveFileType -inputs = pytest.mark.parametrize( - ["path"], [ - ("RBC_tiny.czi", ), - ("RBC_lattice.tif", ), - ("LLS7_t1_ch1.czi", ), - ("LLS7_t1_ch3.czi", ), - ("LLS7_t2_ch1.czi", ), - ("LLS7_t2_ch3.czi", ), - ("multich_multi_time.tif", ), -]) parameterized = pytest.mark.parametrize("args", [ {"skew": "X"}, {"skew": "Y"}, diff --git a/core/tests/test_process.py b/core/tests/test_process.py index 7de0440..7df0dd0 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -8,29 +8,26 @@ from napari_workflows import Workflow from pytest import FixtureRequest -from .params import inputs, parameterized +from .params import parameterized root = Path(__file__).parent / "data" def open_psf(name: str): with as_file(resources / "psfs" / "zeiss_simulated" / name) as path: return path -@inputs @parameterized -def test_process(path: str, args: dict): - with as_file(resources / path) as lattice_path: - for slice in LatticeData.parse_obj({ - "input_image": lattice_path, - **args - }).process().slices: - assert slice.data.ndim == 3 +def test_process(image_path: str, args: dict): + for slice in LatticeData.parse_obj({ + "input_image": image_path, + **args + }).process().slices: + assert slice.data.ndim == 3 -@inputs @parameterized -def test_save(path: str, args: dict): - with as_file(resources / path) as lattice_path, tempfile.TemporaryDirectory() as tempdir: +def test_save(image_path: str, args: dict): + with tempfile.TemporaryDirectory() as tempdir: LatticeData.parse_obj({ - "input_image": lattice_path, + "input_image": image_path, "save_dir": tempdir, **args }).process().save_image() @@ -72,7 +69,7 @@ def test_process_workflow(args: dict, request: FixtureRequest, workflow_name: st [[0, 1]], ]) @parameterized -def test_process_crop(args: dict, roi_subset: Optional[List[int]], workflow: Workflow): +def test_process_crop(args: dict, roi_subset: Optional[List[int]]): with as_file(resources / "RBC_tiny.czi") as lattice_path: rois = root / "crop" / "two_rois.zip" for slice in LatticeData.parse_obj({ diff --git a/core/tests/test_workflows.py b/core/tests/test_workflows.py index d38d5ae..89bfa5b 100644 --- a/core/tests/test_workflows.py +++ b/core/tests/test_workflows.py @@ -13,16 +13,16 @@ from tests.utils import invoke from pathlib import Path -from .params import config_types, inputs +from .params import config_types from .utils import invoke, valid_image_path -def test_napari_workflow(workflow: Workflow, test_image: NDArray): +def test_napari_workflow(image_workflow: Workflow, test_image: NDArray): """ Test napari workflow to see if it works before we run it using napari_lattice This is without deskewing """ - workflow = copy(workflow) + workflow = copy(image_workflow) # Set input image to be the "raw" image workflow.set("input", test_image) labeling = workflow.get("labeling") From d037d9dbfab16d1275e90f0373d6369f7e73a382 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 30 Oct 2023 17:12:25 +1100 Subject: [PATCH 085/147] Apply feedback to plugin, change save suffix behaviour --- core/lls_core/models/deskew.py | 5 +++++ core/lls_core/models/lattice_data.py | 30 ++++++---------------------- core/lls_core/models/output.py | 27 +++++++++++++++++++------ core/tests/test_workflows.py | 7 ++++--- plugin/napari_lattice/dock_widget.py | 23 +++++++++++++++++++-- plugin/napari_lattice/fields.py | 16 ++++++++------- 6 files changed, 66 insertions(+), 42 deletions(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 76888e6..96d5c55 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -130,6 +130,11 @@ def channels(self) -> int: """Number of channels""" return self.input_image.sizes["C"] + @property + def nslices(self) -> int: + """The number of 3D slices within the image""" + return self.time * self.channels + @property def new_dz(self): import math diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 3653d81..96eb9de 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -1,12 +1,11 @@ from __future__ import annotations # class for initializing lattice data and setting metadata # TODO: handle scenes -from pydantic import BaseModel, DirectoryPath, Field, NonNegativeInt, root_validator, validator +from pydantic import DirectoryPath, Field, root_validator, validator from dask.array.core import Array as DaskArray -from itertools import groupby -from typing import Any, Iterable, List, Optional, TYPE_CHECKING, Type -from typing_extensions import TypedDict, NotRequired, Generic, TypeVar +from typing import Any, Iterable, Optional, TYPE_CHECKING +from typing_extensions import TypedDict, NotRequired import pyclesperanto_prototype as cle from tqdm import tqdm @@ -27,10 +26,7 @@ if TYPE_CHECKING: import pyclesperanto_prototype as cle - from lls_core.models.deskew import DefinedPixelSizes - from numpy.typing import NDArray - from lls_core.models.results import ImageSlice, ImageSlices, ProcessedSlice, ProcessedSlices - from lls_core.writers import Writer, BdvWriter, TiffWriter + from lls_core.models.results import ImageSlice, ImageSlices, ProcessedSlice import logging @@ -41,22 +37,6 @@ description="If defined, this is a workflow to add lightsheet processing onto" ) -class CommonOutputArgs(TypedDict): - # Arguments - save_dir: NotRequired[DirectoryPath] - save_type: SaveFileType - time_range: range - channel_range: range - -class CommonDeskewArgs(TypedDict): - skew: DeskewDirection - angle: float - -class CommonLatticeArgs(CommonDeskewArgs, CommonOutputArgs): - deconvolution: Optional[DeconvolutionParams] - crop: Optional[CropParams] - workflow: Optional[Workflow] - class LatticeData(OutputParams, DeskewParams): """ Holds data and metadata for a given image in a consistent format @@ -78,6 +58,8 @@ def use_image_path(cls, values: dict): # This needs to be a root validator to ensure it runs before the # reshaping validator. We can't override that either since it's # a field validator and can't modify save_name + # TODO: separate the image file from the image file path as two separate fields, + # so we don't have to put so much logic here from lls_core.types import is_pathlike input_image = values.get("input_image") if is_pathlike(input_image): diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index e80fa87..b1374b2 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -1,13 +1,13 @@ -from pydantic import Field, DirectoryPath +from pydantic import Field, DirectoryPath, validator from strenum import StrEnum from os import getcwd from pathlib import Path -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from lls_core.models.utils import FieldAccessMixin, enum_choices if TYPE_CHECKING: - from lls_core.writers import Writer, BdvWriter, TiffWriter + pass #Choice of File extension to save class SaveFileType(StrEnum): @@ -16,16 +16,19 @@ class SaveFileType(StrEnum): class OutputParams(FieldAccessMixin): save_dir: DirectoryPath = Field( - default=None, description="The directory where the output data will be saved" ) + save_suffix: str = Field( + default="_deskewed", + description="The filename suffix that will be used for output files. This will be added as a suffix to the input file name if the input image was specified using a file name. If the input image was provided as an in-memory object, the `save_name` field should instead be specified." + ) save_name: str = Field( - description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." + description="The filename that will be used for output files. This should not contain a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." ) save_type: SaveFileType = Field( default=SaveFileType.h5, description=f"The data type to save the result as. This will also be used to determine the file extension of the output files. Choices: {enum_choices(SaveFileType)}" -) + ) time_range: range = Field( default=None, description="The range of times to process. This defaults to all time points in the image array." @@ -35,6 +38,18 @@ class OutputParams(FieldAccessMixin): default=None ) + @validator("save_dir", pre=True) + def validate_save_dir(cls, v: Path): + if isinstance(v, Path) and not v.is_absolute(): + # This stops the empty path being considered a valid directory + raise ValueError("The save directory must be an absolute path that exists") + return v + + @validator("save_name") + def add_save_suffix(cls, v: str, values: dict): + # This is the only place that the save suffix is used. + return v + values["save_suffix"] + @property def file_extension(self): if self.save_type == SaveFileType.h5: diff --git a/core/tests/test_workflows.py b/core/tests/test_workflows.py index 89bfa5b..df74fe8 100644 --- a/core/tests/test_workflows.py +++ b/core/tests/test_workflows.py @@ -70,15 +70,16 @@ def test_image_workflow(image_path: Path, image_workflow: Workflow): def test_table_workflow(image_path: Path, table_workflow: Workflow): # Test a complex workflow that returns a tuple of images and data with tempfile.TemporaryDirectory() as tmpdir: - for roi, output in LatticeData( + params = LatticeData( input_image = image_path, workflow = table_workflow, save_dir = tmpdir - ).process_workflow().process(): + ) + for roi, output in params.process_workflow().process(): assert isinstance(output, (DataFrame, Path)) if isinstance(output, DataFrame): nrow, ncol = output.shape - assert nrow > 0 + assert nrow == params.nslices assert ncol > 0 else: assert valid_image_path(output) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index a277a64..1c9f7b9 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -1,5 +1,7 @@ # Enable Logging import logging +from textwrap import dedent +from typing import cast import numpy as np from lls_core.models.lattice_data import LatticeData @@ -63,9 +65,26 @@ def _make_model(self) -> LatticeData: @magicclass(widget_type="split") class LlszMenu(MagicTemplate): - main_heading = field("

      Napari Lattice: Visualization & Analysis

      ", widget_type="Label") - heading1 = field("Select the tabs one at a time to specify analysis parameters. Tabs 1 and 5 are mandatory.", widget_type="Label") + heading1 = field(dedent(""" +
      + """.strip()), widget_type="Label") + + def __post_init__(self): + from qtpy.QtCore import Qt + from qtpy.QtWidgets import QLabel, QLayout + + if isinstance(self._widget._layout, QLayout): + self._widget._layout.setAlignment(Qt.AlignmentFlag.AlignTop) + + if isinstance(self.heading1.native, QLabel): + self.heading1.native.setWordWrap(True) # Tabbed Widget container to house all the widgets @magicclass(widget_type="tabbed", name="Functions", labels=False) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index bdb7866..a9ca0af 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -150,10 +150,10 @@ def __post_init__(self): from qtpy.QtWidgets import QLabel if isinstance(errors, QLabel): errors.setStyleSheet("color: red;") - errors.setWordWrap(True) + # errors.setWordWrap(True) from qtpy.QtCore import Qt - self._widget._layout.setAlignment(Qt.AlignTop) + self._widget._layout.setAlignment(Qt.AlignmentFlag.AlignTop) def _get_parent_tab_widget(self: Any) -> QTabWidget: return self.parent.parentWidget() @@ -489,12 +489,12 @@ class OutputFields(NapariFieldGroup): ) save_path = field(Path).with_options( label = "Save Directory", - value = Path(history.get_save_history()[0]), # Directory select mode="d" ) - save_name = field(str).with_options( - label = "Save Prefix" + save_suffix = field(str).with_options( + value=OutputParams.get_default("save_suffix"), + label = "Save Suffix", ) errors = field(Label).with_options(label="Errors") @@ -503,6 +503,8 @@ def _make_model(self) -> OutputParams: channel_range=range(self.channel_range.value[0], self.channel_range.value[1]), time_range=range(self.time_range.value[0], self.time_range.value[1]), save_dir=self.save_path.value, - save_name=self.save_name.value, - save_type=self.save_type.value + save_suffix=self.save_suffix.value, + save_type=self.save_type.value, + # This is just to avoid the validation error caused by the missing field + save_name="PLACEHOLDER" ) From e7577796123942d04d20ee2b39acfc5669de117d Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 30 Oct 2023 18:42:01 +1100 Subject: [PATCH 086/147] Add test suite for GUI saving --- plugin/napari_lattice/dock_widget.py | 2 +- plugin/napari_lattice/fields.py | 2 +- plugin/napari_lattice/reader.py | 6 ++-- plugin/tests/test_dock_widget.py | 47 +++++++++++++++++++++++++--- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 1c9f7b9..5131bcf 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -133,5 +133,5 @@ def preview(self, header:str, time: int, channel: int): def save(self): from napari.utils.notifications import show_info lattice = self._make_model() - lattice.process().save_image() + lattice.save() show_info(f"Deskewing successfuly completed. Results are located in {lattice.save_dir}") diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index a9ca0af..9503515 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -205,7 +205,7 @@ def _get_dimension_options(self, _) -> List[str]: Returns the list of dimension order options that might be possible for the current image stack """ default = ["Get from Metadata"] - ndims = max([len(layer.data.shape) for layer in self.img_layer.value], default=None) + ndims = max([len(layer.data.shape) for layer in self.img_layer.value], default=None) if ndims is None: return default elif ndims == 3: diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index eb596c4..cbe9061 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -34,9 +34,9 @@ class NapariImageParams(TypedDict): def lattice_params_from_napari( imgs: Collection[Image], - dimension_order: Optional[str], - physical_pixel_sizes: Optional[PhysicalPixelSizes], - stack_along: str + stack_along: str, + dimension_order: Optional[str] = None, + physical_pixel_sizes: Optional[PhysicalPixelSizes] = None, ) -> NapariImageParams: """ Factory function for generating a LatticeData from a Napari Image diff --git a/plugin/tests/test_dock_widget.py b/plugin/tests/test_dock_widget.py index 20d6947..b654d86 100644 --- a/plugin/tests/test_dock_widget.py +++ b/plugin/tests/test_dock_widget.py @@ -1,12 +1,20 @@ from __future__ import annotations +from pathlib import Path + +from importlib_resources import as_file +from xarray import DataArray from napari_lattice.dock_widget import LLSZWidget import numpy as np -from typing import Callable, TYPE_CHECKING +from typing import Callable, TYPE_CHECKING, Tuple from magicclass.testing import check_function_gui_buildable, FunctionGuiTester -from napari.layers import Image from magicclass import MagicTemplate from magicclass.widgets import Widget from magicclass._gui._gui_modes import ErrorMode +import pytest +from lls_core.sample import resources +from aicsimageio.aics_image import AICSImage +from plugin.napari_lattice.fields import PixelSizeSource +from tempfile import TemporaryDirectory if TYPE_CHECKING: from napari import Viewer @@ -18,6 +26,20 @@ # @pytest.mark.skip(reason="GUI tests currently fail in github CI, unclear why") # When testing locally, need pytest-qt +@pytest.fixture(params=[ + "RBC_tiny.czi", + "LLS7_t1_ch1.czi", + "LLS7_t1_ch3.czi", + "LLS7_t2_ch1.czi", + "LLS7_t2_ch3.czi", +]) +def image_data(request: pytest.FixtureRequest): + """ + Fixture function that yields test images as file paths + """ + with as_file(resources / request.param) as image_path: + yield AICSImage(image_path) + def set_debug(cls: MagicTemplate): """ Recursively disables GUI error handling, so that this works with pytest @@ -29,15 +51,30 @@ def _handler(e: Exception, parent: Widget): for child in cls.__magicclass_children__: set_debug(child) -def test_dock_widget(make_napari_viewer: Callable[[], Viewer]): +def test_dock_widget(make_napari_viewer: Callable[[], Viewer], image_data: AICSImage): # make viewer and add an image layer using our fixture viewer = make_napari_viewer() # Check if an image can be added as a layer - viewer.add_image(np.random.random((100, 100))) + viewer.add_image(image_data.xarray_dask_data) # Test if napari-lattice widget can be created in napari - viewer.window.add_dock_widget(LLSZWidget()) + ui = LLSZWidget() + set_debug(ui) + viewer.window.add_dock_widget(ui) + + # Set the input parameters and execute the processing + with TemporaryDirectory() as tmpdir: + # Specify values for all the required GUI fields + fields = ui.LlszMenu.WidgetContainer.deskew_fields + # TODO: refactor this logic into a `lattice_params_from_aics` method + fields.img_layer.value = list(viewer.layers) + fields.dimension_order.value = image_data.dims.order + fields.pixel_sizes_source.value = PixelSizeSource.Manual + ui.LlszMenu.WidgetContainer.output_fields.save_path.value = tmpdir + # fields.pixel_sizes.value = image_data.physical_pixel_sizes + tester = FunctionGuiTester(ui.save) + tester.call() def test_check_buildable(): ui = LLSZWidget() From 8da66321876fc23525c67b6ba5b426213868d829 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 31 Oct 2023 09:46:59 +1100 Subject: [PATCH 087/147] Fix type import errors --- core/lls_core/models/lattice_data.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 96eb9de..d001a22 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -1,16 +1,15 @@ from __future__ import annotations # class for initializing lattice data and setting metadata # TODO: handle scenes -from pydantic import DirectoryPath, Field, root_validator, validator +from pydantic import Field, root_validator, validator from dask.array.core import Array as DaskArray -from typing import Any, Iterable, Optional, TYPE_CHECKING -from typing_extensions import TypedDict, NotRequired +from typing import Any, Iterable, Optional, TYPE_CHECKING, Type import pyclesperanto_prototype as cle from tqdm import tqdm -from lls_core import DeskewDirection, DeconvolutionChoice +from lls_core import DeconvolutionChoice from lls_core.deconvolution import pycuda_decon, skimage_decon from lls_core.llsz_core import crop_volume_deskew from lls_core.models.crop import CropParams @@ -27,6 +26,7 @@ if TYPE_CHECKING: import pyclesperanto_prototype as cle from lls_core.models.results import ImageSlice, ImageSlices, ProcessedSlice + from lls_core.writers import Writer import logging From 66462b3af1c09f7e3bfd7757a0b80f6a89ce078d Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 31 Oct 2023 12:17:28 +1100 Subject: [PATCH 088/147] Fix CLI tests that now need an output directory --- core/tests/test_cli.py | 32 +++++++++++++++++--------------- core/tests/test_deskew.py | 11 +++++++++-- plugin/tests/test_dock_widget.py | 7 ++----- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py index 5724d0a..5f3f144 100644 --- a/core/tests/test_cli.py +++ b/core/tests/test_cli.py @@ -40,6 +40,20 @@ def create_data(dir: Path) -> Path: return config_location +def assert_tiff(output_dir: Path): + """Checks that a valid TIFF was generated in the directory""" + results = list(output_dir.glob("*.tif")) + assert len(results) > 0 + for result in results: + AICSImage(result).get_image_data() + +def assert_h5(output_dir: Path): + """Checks that a valid H5 was generated""" + h5s = list(output_dir.glob("*.h5")) + assert len(h5s) > 0 + assert len(list(output_dir.glob("*.xml"))) == len(h5s) + for h5 in h5s: + BdvEditor(str(h5)).read_view() def test_batch_deskew_h5(): """Write image to disk and then execute napari_lattice from terminal @@ -56,10 +70,7 @@ def test_batch_deskew_h5(): "--save-type", "h5" ]) - # checks if h5 files written - assert (out_dir / "raw.h5").exists() - assert (out_dir / "raw.xml").exists() - BdvEditor(str(out_dir / "raw.h5")).read_view() + assert_h5(out_dir) def test_batch_deskew_tiff(): # tiff file deskew @@ -73,12 +84,7 @@ def test_batch_deskew_tiff(): "--save-type", "tiff" ]) - # checks if tiff written - results = list(out_dir.glob("*.tif")) - assert len(results) == 1 - for result in results: - AICSImage(result).get_image_data() - + assert_tiff(out_dir) def test_yaml_deskew(): """Write image to disk and then execute napari_lattice from terminal @@ -89,8 +95,4 @@ def test_yaml_deskew(): config_location = create_data(test_dir) # Batch deskew and save as h5 invoke(["--yaml-config", str(config_location)], ) - - # checks if h5 files written - assert (test_dir / "raw.h5").exists() - assert (test_dir / "raw.xml").exists() - BdvEditor(str(test_dir / "raw.h5")).read_view() + assert_h5(test_dir) diff --git a/core/tests/test_deskew.py b/core/tests/test_deskew.py index e46e3fc..aaa22a2 100644 --- a/core/tests/test_deskew.py +++ b/core/tests/test_deskew.py @@ -3,6 +3,7 @@ import numpy as np from lls_core.models.lattice_data import LatticeData from xarray import DataArray +import tempfile def test_deskew(): @@ -17,5 +18,11 @@ def test_deskew(): def test_lattice_data_deskew(): raw = DataArray(np.zeros((5, 5, 5)), dims=["X", "Y", "Z"]) - lattice = LatticeData(input_image=raw, physical_pixel_sizes = (1, 1, 1), save_name="test") - assert lattice.deskew_vol_shape == [2, 9, 5] + with tempfile.TemporaryDirectory() as tmpdir: + lattice = LatticeData( + input_image=raw, + physical_pixel_sizes = (1, 1, 1), + save_name="test", + save_dir=tmpdir + ) + assert lattice.deskew_vol_shape == [2, 9, 5] diff --git a/plugin/tests/test_dock_widget.py b/plugin/tests/test_dock_widget.py index b654d86..ca78020 100644 --- a/plugin/tests/test_dock_widget.py +++ b/plugin/tests/test_dock_widget.py @@ -1,11 +1,8 @@ from __future__ import annotations -from pathlib import Path from importlib_resources import as_file -from xarray import DataArray from napari_lattice.dock_widget import LLSZWidget -import numpy as np -from typing import Callable, TYPE_CHECKING, Tuple +from typing import Callable, TYPE_CHECKING from magicclass.testing import check_function_gui_buildable, FunctionGuiTester from magicclass import MagicTemplate from magicclass.widgets import Widget @@ -13,7 +10,7 @@ import pytest from lls_core.sample import resources from aicsimageio.aics_image import AICSImage -from plugin.napari_lattice.fields import PixelSizeSource +from napari_lattice.fields import PixelSizeSource from tempfile import TemporaryDirectory if TYPE_CHECKING: From 8b8c4e52a0f2e6fff4096c9703164192cf2e1c07 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 31 Oct 2023 12:19:00 +1100 Subject: [PATCH 089/147] Only run all the tests on one version, to avoid using excessive CPU hours --- .github/workflows/test_and_deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index ac3906f..7fc86b6 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -55,6 +55,8 @@ jobs: python: ${{ env.python }} - name: Test core + # Only run all the tests on one version, to avoid using excessive CPU hours + if: matrix.python-version == "3.10" run: pytest -v --cov=core --cov-report=xml core/ - uses: tlambert03/setup-qt-libs@v1 From eba792ae925343415069ff1070a384e5e43d8739 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 31 Oct 2023 12:22:08 +1100 Subject: [PATCH 090/147] ${{ }} notation --- .github/workflows/test_and_deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 7fc86b6..7548425 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -56,7 +56,7 @@ jobs: - name: Test core # Only run all the tests on one version, to avoid using excessive CPU hours - if: matrix.python-version == "3.10" + if: ${{ matrix.python-version == "3.10" }} run: pytest -v --cov=core --cov-report=xml core/ - uses: tlambert03/setup-qt-libs@v1 From 891549c477647959825cb1b81c39369f78cb5880 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 31 Oct 2023 12:28:25 +1100 Subject: [PATCH 091/147] Single quotes? --- .github/workflows/test_and_deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 7548425..2c5bacd 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -56,7 +56,7 @@ jobs: - name: Test core # Only run all the tests on one version, to avoid using excessive CPU hours - if: ${{ matrix.python-version == "3.10" }} + if: ${{ matrix.python-version == '3.10' }} run: pytest -v --cov=core --cov-report=xml core/ - uses: tlambert03/setup-qt-libs@v1 From db52dda922fc96bc14a68e32bf8600611ac3c31c Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 31 Oct 2023 15:59:43 +1100 Subject: [PATCH 092/147] Use public API for finding the tab widget --- plugin/napari_lattice/dock_widget.py | 3 --- plugin/napari_lattice/fields.py | 21 ++++++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 5131bcf..e754466 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -1,7 +1,6 @@ # Enable Logging import logging from textwrap import dedent -from typing import cast import numpy as np from lls_core.models.lattice_data import LatticeData @@ -14,9 +13,7 @@ OutputFields, WorkflowFields, ) -from napari_lattice.icons import GREY from qtpy.QtCore import Qt -from qtpy.QtGui import QIcon from qtpy.QtWidgets import QTabWidget logger = logging.getLogger(__name__) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 9503515..e405299 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -25,7 +25,6 @@ from magicclass.widgets import ComboBox, Label, Widget from napari.layers import Image, Shapes from napari.types import ShapesData -from napari.utils import history from napari_lattice.icons import GREEN, GREY, RED from napari_lattice.reader import NapariImageParams, lattice_params_from_napari from napari_lattice.utils import get_layers @@ -139,7 +138,7 @@ class StackAlong(StrEnum): CHANNEL = "Channel" TIME = "Time" -class NapariFieldGroup: +class NapariFieldGroup(MagicTemplate): def __post_init__(self): self = cast(FieldGroup, self) self.changed.connect(self._validate, unique=False) @@ -155,13 +154,17 @@ def __post_init__(self): from qtpy.QtCore import Qt self._widget._layout.setAlignment(Qt.AlignmentFlag.AlignTop) - def _get_parent_tab_widget(self: Any) -> QTabWidget: - return self.parent.parentWidget() + def _get_parent_tab_widget(self) -> QTabWidget: + qwidget = self.native + # Walk up the widget tree until we find the tab widget + while not isinstance(qwidget, QTabWidget): + qwidget = qwidget.parent() + return qwidget - def _get_tab_index(self: Any) -> int: + def _get_tab_index(self) -> int: return self._get_parent_tab_widget().indexOf(self._widget._qwidget) - def _set_valid(self: Any, valid: bool): + def _set_valid(self, valid: bool): from qtpy.QtGui import QIcon from importlib_resources import as_file tab_parent = self._get_parent_tab_widget() @@ -178,13 +181,13 @@ def _set_valid(self: Any, valid: bool): with as_file(icon) as path: tab_parent.setTabIcon(index, QIcon(str(path))) - def reset_choices(self: Any): + def reset_choices(self): # This is used to prevent validation from re-running when a napari layer is added or removed from magicgui.widgets import Container with self.changed.blocked(): super(Container, self).reset_choices() - def _validate(self: Any): + def _validate(self): self.errors.value = get_friendly_validations(self) valid = not bool(self.errors.value) self.errors.visible = not valid @@ -383,7 +386,7 @@ def _make_model(self) -> Optional[DeconvolutionParams]: ) @magicclass -class CroppingFields(MagicTemplate, NapariFieldGroup): +class CroppingFields(NapariFieldGroup): """ A counterpart to the CropParams Pydantic class """ From 6cf492f6a445228e6f78d822cb55394dbe4e43b4 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 31 Oct 2023 16:48:25 +1100 Subject: [PATCH 093/147] Magicgui dependency bound --- plugin/pyproject.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index 42b172e..9b92123 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -41,9 +41,10 @@ dependencies = [ "fsspec>=2022.8.2", "importlib_resources", "lls_core", - # We need this Python 3.8 fix: https://github.com/hanjinliu/magic-class/pull/108 - "magic-class>=0.7.5", - "magicgui", + # The lower bound is because we need this Python 3.8 fix: https://github.com/hanjinliu/magic-class/pull/108 + # The upper bound is because we are waiting on https://github.com/hanjinliu/magic-class/issues/128 + "magic-class>=0.7.5,<0.8.0", + "magicgui<0.8.0", # Currently commented out to avoid installation issues, although # This can be reinstated once https://github.com/pypa/pip/pull/12095 is merged # "napari-aicsimageio>=0.7.2", From 69500a783ed743878ca28537021a9506518d4824 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 3 Nov 2023 12:40:05 +1100 Subject: [PATCH 094/147] Fix for missing output directory for previewing; add make() method for models --- core/lls_core/models/utils.py | 10 ++++++++++ plugin/napari_lattice/dock_widget.py | 17 ++++++++++++----- plugin/napari_lattice/fields.py | 6 ++++-- plugin/tests/test_dock_widget.py | 10 +++++++++- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py index 6f56a77..3e49bcd 100644 --- a/core/lls_core/models/utils.py +++ b/core/lls_core/models/utils.py @@ -68,3 +68,13 @@ def copy_validate(self, **kwargs) -> Self: """ updated = self.copy(**kwargs) return updated.validate(updated.dict()) + + @classmethod + def make(cls, validate: bool = True, **kwargs: Any): + """ + Creates an instance of this class, with validation either enabled or disabled + """ + if validate: + return cls(**kwargs) + else: + return cls.construct(**kwargs) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index e754466..2dfddd4 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -35,13 +35,13 @@ def _check_validity(self) -> bool: except: return False - def _make_model(self) -> LatticeData: + def _make_model(self, validate: bool = True) -> LatticeData: from rich import print from sys import stdout deskew_args = self.LlszMenu.WidgetContainer.deskew_fields._get_kwargs() - output_args = self.LlszMenu.WidgetContainer.output_fields._make_model() - params = LatticeData( + output_args = self.LlszMenu.WidgetContainer.output_fields._make_model(validate=False) + args = dict( input_image=deskew_args["data"], angle=deskew_args["angle"], channel_range=output_args.channel_range, @@ -56,6 +56,7 @@ def _make_model(self) -> LatticeData: deconvolution=self.LlszMenu.WidgetContainer.deconv_fields._make_model(), crop=self.LlszMenu.WidgetContainer.cropping_fields._make_model() ) + params = LatticeData.make(validate=validate, **args) # Log the lattice print(params, file=stdout) return params @@ -108,12 +109,18 @@ def __post_init__(self): call_button="Preview" ) @set_design(text="Preview") - def preview(self, header:str, time: int, channel: int): + def preview(self, header: str, time: int, channel: int): + from pathlib import Path + # We only need to process one time point for the preview, # so we made a copy using a subset of the times - lattice = self._make_model().copy_validate(update=dict( + lattice = self._make_model(validate=False).copy_validate(update=dict( time_range = range(time, time+1), channel_range = range(time, time+1), + # Patch in a placeholder for the save dir because previewing doesn't use it + # TODO: use a more elegant solution such as making the "saveable" lattice + # a child class which more validations + save_dir = Path.home() )) for slice in lattice.process().slices: diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index e405299..3016437 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -501,8 +501,10 @@ class OutputFields(NapariFieldGroup): ) errors = field(Label).with_options(label="Errors") - def _make_model(self) -> OutputParams: - return OutputParams( + def _make_model(self, validate: bool = True) -> OutputParams: + return OutputParams.make( + validate=validate, + channel_range=range(self.channel_range.value[0], self.channel_range.value[1]), time_range=range(self.time_range.value[0], self.time_range.value[1]), save_dir=self.save_path.value, diff --git a/plugin/tests/test_dock_widget.py b/plugin/tests/test_dock_widget.py index ca78020..4a6cfd5 100644 --- a/plugin/tests/test_dock_widget.py +++ b/plugin/tests/test_dock_widget.py @@ -68,11 +68,19 @@ def test_dock_widget(make_napari_viewer: Callable[[], Viewer], image_data: AICSI fields.img_layer.value = list(viewer.layers) fields.dimension_order.value = image_data.dims.order fields.pixel_sizes_source.value = PixelSizeSource.Manual + + # Test previewing + tester = FunctionGuiTester(ui.preview) + tester.call("", 0, 0) + + # Add the save path which shouldn't be needed for previewing ui.LlszMenu.WidgetContainer.output_fields.save_path.value = tmpdir - # fields.pixel_sizes.value = image_data.physical_pixel_sizes + + # Test saving tester = FunctionGuiTester(ui.save) tester.call() + def test_check_buildable(): ui = LLSZWidget() set_debug(ui) From c09983f1550b7d3bf85ade80a68d8d814f84c1c0 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 3 Nov 2023 15:01:23 +1100 Subject: [PATCH 095/147] Add validation for z range, and loosen the GUI restrictions --- core/lls_core/models/lattice_data.py | 11 ++++++++++- plugin/napari_lattice/fields.py | 1 - 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index d001a22..699ebb5 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -89,18 +89,27 @@ def parse_workflow(cls, v: Any): @validator("crop") def default_z_range(cls, v: CropParams, values: dict): - # If any part of the z range is missing, assume the user wants all Z indices if v is None: return v with ignore_keyerror(): + # Fill in missing parts of the z range with the min/max z values default_start = 0 default_end = values["input_image"].sizes["Z"] + + # Set defaults if v.z_range is None: v.z_range = (default_start, default_end) if v.z_range[0] is None: v.z_range[0] = default_start if v.z_range[1] is None: v.z_range[1] = default_end + + # Validate + if v.z_range[1] > default_end: + raise ValueError(f"The z-index endpoint of {v.z_range[1]} is outside the size of the z-axis ({default_end})") + if v.z_range[0] < default_start: + raise ValueError(f"The z-index start of {v.z_range[0]} is outside the size of the z-axis") + return v @validator("time_range", pre=True, always=True) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 3016437..12707de 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -397,7 +397,6 @@ class CroppingFields(NapariFieldGroup): value = (0, 1), options = dict( min = 0, - max = 1 ), ) errors = field(Label).with_options(label="Errors") From f757e532e142308e1b60de56bf304380ab6ab2cf Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 10 Nov 2023 13:21:48 +1100 Subject: [PATCH 096/147] Dynamically update the maximum range values --- plugin/napari_lattice/dock_widget.py | 25 +++++++++++++-- plugin/napari_lattice/fields.py | 47 ++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 2dfddd4..7c90d74 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -1,7 +1,8 @@ -# Enable Logging +from __future__ import annotations + import logging from textwrap import dedent - +from typing import TYPE_CHECKING import numpy as np from lls_core.models.lattice_data import LatticeData from magicclass import MagicTemplate, field, magicclass, set_options, vfield @@ -16,6 +17,11 @@ from qtpy.QtCore import Qt from qtpy.QtWidgets import QTabWidget +if TYPE_CHECKING: + from typing import Iterable + from napari_lattice.fields import NapariFieldGroup + +# Enable Logging logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -25,6 +31,7 @@ def __post_init__(self): # aligning collapsible widgets at the top instead of having them centered vertically self._widget._layout.setAlignment(Qt.AlignTop) + def _check_validity(self) -> bool: """ Returns True if the model is valid @@ -97,6 +104,7 @@ def __post_init__(self): for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: field._validate() + # Using vfields here seems to prevent https://github.com/hanjinliu/magic-class/issues/110 deskew_fields = vfield(DeskewFields) deconv_fields = vfield(DeconvolutionFields) cropping_fields = vfield(CroppingFields) @@ -139,3 +147,16 @@ def save(self): lattice = self._make_model() lattice.save() show_info(f"Deskewing successfuly completed. Results are located in {lattice.save_dir}") + + @LlszMenu.WidgetContainer.deskew_fields.connect + def _on_image_changed(self, deskew: DeskewFields): + img = deskew._get_kwargs()["data"] + # We have to manually trigger _on_image_changed because siblings + # classes can't listen to each other's events: https://github.com/hanjinliu/magic-class/issues/129 + for field in self._get_fields(): + field._on_image_changed(img) + + def _get_fields(self) -> Iterable[NapariFieldGroup]: + """Yields all the child Field classes which inherit from NapariFieldGroup""" + container = self.LlszMenu.WidgetContainer + yield from set(container.__magicclass_children__) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 12707de..a21aef1 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -1,10 +1,10 @@ # FieldGroups that the users interact with to input data - import logging from pathlib import Path -from typing import Any, Callable, List, Optional, Tuple, TypeVar, cast - +from typing import Any, Callable, List, Optional, Tuple, TYPE_CHECKING +from typing_extensions import TypeVar import pyclesperanto_prototype as cle +from xarray import DataArray from lls_core import ( DeconvolutionChoice, DeskewDirection, @@ -32,9 +32,20 @@ from qtpy.QtWidgets import QTabWidget from strenum import StrEnum +if TYPE_CHECKING: + from magicgui.widgets.bases import RangedWidget + logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +def adjust_maximum(widget: "RangedWidget", max: int): + """ + Updates the maximum value + """ + widget.max = max + if widget.value > max: + widget.value = max + def exception_to_html(e: BaseException) -> str: """ Converts an exception to HTML for reporting back to the user @@ -73,7 +84,6 @@ class BackgroundSource(StrEnum): SecondLast = "Second Last" Custom = "Custom" - def enable_field(field: MagicField, enabled: bool = True) -> None: """ Enable the widget associated with a field @@ -91,7 +101,6 @@ def enable_field(field: MagicField, enabled: bool = True) -> None: except RuntimeError: pass - FieldValueType = TypeVar("FieldValueType") SelfType = TypeVar("SelfType") def enable_if(fields: List[MagicField]): @@ -140,7 +149,6 @@ class StackAlong(StrEnum): class NapariFieldGroup(MagicTemplate): def __post_init__(self): - self = cast(FieldGroup, self) self.changed.connect(self._validate, unique=False) # Style the error label. @@ -196,13 +204,15 @@ def _validate(self): def _make_model(self): raise NotImplementedError() + def _on_image_changed(self, img: DataArray): + pass + class DeskewKwargs(NapariImageParams): angle: float skew: DeskewDirection @magicclass class DeskewFields(NapariFieldGroup): - def _get_dimension_options(self, _) -> List[str]: """ Returns the list of dimension order options that might be possible for the current image stack @@ -334,9 +344,7 @@ def _make_model(self) -> DeskewParams: @magicclass class DeconvolutionFields(NapariFieldGroup): - """ - A counterpart to the DeconvolutionParams Pydantic class - """ + # A counterpart to the DeconvolutionParams Pydantic class fields_enabled = field(False, label="Enabled") decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") psf = field(List[Path], label = "PSFs") @@ -387,9 +395,7 @@ def _make_model(self) -> Optional[DeconvolutionParams]: @magicclass class CroppingFields(NapariFieldGroup): - """ - A counterpart to the CropParams Pydantic class - """ + # A counterpart to the CropParams Pydantic class fields_enabled = field(False, label="Enabled") shapes= field(List[Shapes], widget_type="Select", label = "ROI Shape Layers").with_options(choices=lambda _x, _y: get_layers(Shapes)) z_range = field(Tuple[int, int]).with_options( @@ -425,6 +431,11 @@ def new_crop_layer(self): shapes.mode = "ADD_RECTANGLE" shapes.name = "Napari Lattice Crop" + def _on_image_changed(self, img: DataArray): + # Update the maximum Z + for widget in self.z_range: + adjust_maximum(widget, img.sizes["Z"]) + @fields_enabled.connect @enable_if([shapes, z_range]) def _enable_crop(self, enabled: bool) -> bool: @@ -500,6 +511,9 @@ class OutputFields(NapariFieldGroup): ) errors = field(Label).with_options(label="Errors") + def __post_init__(self): + pass + def _make_model(self, validate: bool = True) -> OutputParams: return OutputParams.make( validate=validate, @@ -512,3 +526,10 @@ def _make_model(self, validate: bool = True) -> OutputParams: # This is just to avoid the validation error caused by the missing field save_name="PLACEHOLDER" ) + + def _on_image_changed(self, img: DataArray): + # Update the maximum T and C + for widget in self.time_range: + adjust_maximum(widget, img.sizes["T"]) + for widget in self.channel_range: + adjust_maximum(widget, img.sizes["C"]) From ae721cc5ca8e8634c0686d67e9f4278da451738d Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 10 Nov 2023 13:56:05 +1100 Subject: [PATCH 097/147] Clean up code --- core/lls_core/models/lattice_data.py | 2 -- plugin/napari_lattice/fields.py | 3 --- 2 files changed, 5 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 699ebb5..6ac98d6 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -409,8 +409,6 @@ def process(self) -> ImageSlices: from lls_core.models.results import ImageSlices ImageSlices.update_forward_refs(LatticeData=LatticeData) - # if self.workflow is not None: - # return self._process_workflow() if self.cropping_enabled: return ImageSlices( lattice_data=self, diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index a21aef1..126acb4 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -511,9 +511,6 @@ class OutputFields(NapariFieldGroup): ) errors = field(Label).with_options(label="Errors") - def __post_init__(self): - pass - def _make_model(self, validate: bool = True) -> OutputParams: return OutputParams.make( validate=validate, From 2000786354f11f2e0f25887cf9e6d0ac75887119 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 15 Nov 2023 17:08:19 +1100 Subject: [PATCH 098/147] Remove the z cropping fields from the GUI --- plugin/napari_lattice/fields.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 126acb4..c9aa883 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -398,13 +398,6 @@ class CroppingFields(NapariFieldGroup): # A counterpart to the CropParams Pydantic class fields_enabled = field(False, label="Enabled") shapes= field(List[Shapes], widget_type="Select", label = "ROI Shape Layers").with_options(choices=lambda _x, _y: get_layers(Shapes)) - z_range = field(Tuple[int, int]).with_options( - label = "Z Range", - value = (0, 1), - options = dict( - min = 0, - ), - ) errors = field(Label).with_options(label="Errors") def _get_deskew(self) -> DeskewParams: @@ -431,13 +424,8 @@ def new_crop_layer(self): shapes.mode = "ADD_RECTANGLE" shapes.name = "Napari Lattice Crop" - def _on_image_changed(self, img: DataArray): - # Update the maximum Z - for widget in self.z_range: - adjust_maximum(widget, img.sizes["Z"]) - @fields_enabled.connect - @enable_if([shapes, z_range]) + @enable_if([shapes]) def _enable_crop(self, enabled: bool) -> bool: return enabled @@ -445,7 +433,6 @@ def _make_model(self) -> Optional[CropParams]: import numpy as np if self.fields_enabled.value: return CropParams( - z_range=self.z_range.value, roi_list=ShapesData([np.array(shape.data) / self._get_deskew().dy for shape in self.shapes.value]) ) return None From 8251aca091b1c930f4bf5062bfa44618b403ac0a Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 17 Nov 2023 16:15:09 +1100 Subject: [PATCH 099/147] Fix for cropping by treating roi as floats --- core/lls_core/cropping.py | 10 +- core/lls_core/models/lattice_data.py | 896 +++++++++++++-------------- plugin/napari_lattice/fields.py | 2 +- 3 files changed, 455 insertions(+), 453 deletions(-) diff --git a/core/lls_core/cropping.py b/core/lls_core/cropping.py index 1eb61d7..2f0c35a 100644 --- a/core/lls_core/cropping.py +++ b/core/lls_core/cropping.py @@ -6,11 +6,13 @@ from typing_extensions import Self from numpy.typing import NDArray +RoiCoord = Tuple[float, float] + class Roi(NamedTuple): - top_left: Tuple[int, int] - top_right: Tuple[int, int] - bottom_left: Tuple[int, int] - bottom_right: Tuple[int, int] + top_left: RoiCoord + top_right: RoiCoord + bottom_left: RoiCoord + bottom_right: RoiCoord @classmethod def from_array(cls, array: NDArray) -> Self: diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 6ac98d6..857c497 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -1,448 +1,448 @@ -from __future__ import annotations -# class for initializing lattice data and setting metadata -# TODO: handle scenes -from pydantic import Field, root_validator, validator -from dask.array.core import Array as DaskArray - -from typing import Any, Iterable, Optional, TYPE_CHECKING, Type - -import pyclesperanto_prototype as cle -from tqdm import tqdm - -from lls_core import DeconvolutionChoice -from lls_core.deconvolution import pycuda_decon, skimage_decon -from lls_core.llsz_core import crop_volume_deskew -from lls_core.models.crop import CropParams -from lls_core.models.deconvolution import DeconvolutionParams -from lls_core.models.output import OutputParams, SaveFileType -from lls_core.models.results import WorkflowSlices -from lls_core.models.utils import ignore_keyerror -from lls_core.types import ArrayLike -from lls_core.models.deskew import DeskewParams -from napari_workflows import Workflow -from xarray import DataArray -from pathlib import Path - -if TYPE_CHECKING: - import pyclesperanto_prototype as cle - from lls_core.models.results import ImageSlice, ImageSlices, ProcessedSlice - from lls_core.writers import Writer - -import logging - -logger = logging.getLogger(__name__) - -workflow: Optional[Workflow] = Field( - default=None, - description="If defined, this is a workflow to add lightsheet processing onto" -) - -class LatticeData(OutputParams, DeskewParams): - """ - Holds data and metadata for a given image in a consistent format - """ - - # Note: originally the save-related fields were included via composition and not inheritance - # (similar to how `crop` and `workflow` are handled), but this was impractical for implementing validations - - #: If this is None, then deconvolution is disabled - deconvolution: Optional[DeconvolutionParams] = None - - #: If this is None, then cropping is disabled - crop: Optional[CropParams] = None - - workflow: Optional[Workflow] = workflow - - @root_validator(pre=True) - def use_image_path(cls, values: dict): - # This needs to be a root validator to ensure it runs before the - # reshaping validator. We can't override that either since it's - # a field validator and can't modify save_name - # TODO: separate the image file from the image file path as two separate fields, - # so we don't have to put so much logic here - from lls_core.types import is_pathlike - input_image = values.get("input_image") - if is_pathlike(input_image): - if values.get("save_name") is None: - values["save_name"] = Path(values["input_image"]).stem - - save_dir = values.get("save_dir") - if save_dir is None: - # By default, make the save dir be the same dir as the input - values["save_dir"] = Path(input_image).parent - elif is_pathlike(save_dir): - # Convert a string path to a Path object - values["save_dir"] = Path(save_dir) - - return values - - @validator("workflow", pre=True) - def parse_workflow(cls, v: Any): - # Load the workflow from disk if it was provided as a path - from lls_core.types import is_pathlike - from lls_core.workflow import workflow_from_path - from pathlib import Path - - if is_pathlike(v): - return workflow_from_path(Path(v)) - return v - - @validator("crop") - def default_z_range(cls, v: CropParams, values: dict): - if v is None: - return v - with ignore_keyerror(): - # Fill in missing parts of the z range with the min/max z values - default_start = 0 - default_end = values["input_image"].sizes["Z"] - - # Set defaults - if v.z_range is None: - v.z_range = (default_start, default_end) - if v.z_range[0] is None: - v.z_range[0] = default_start - if v.z_range[1] is None: - v.z_range[1] = default_end - - # Validate - if v.z_range[1] > default_end: - raise ValueError(f"The z-index endpoint of {v.z_range[1]} is outside the size of the z-axis ({default_end})") - if v.z_range[0] < default_start: - raise ValueError(f"The z-index start of {v.z_range[0]} is outside the size of the z-axis") - - return v - - @validator("time_range", pre=True, always=True) - def parse_time_range(cls, v: Any, values: dict) -> Any: - """ - Sets the default time range if undefined - """ - # This skips the conversion if no image was provided, to ensure a more - # user-friendly error is provided, namely "image was missing" - with ignore_keyerror(): - default_start = 0 - default_end = values["input_image"].sizes["T"] - if v is None: - return range(default_start, default_end) - elif isinstance(v, tuple) and len(v) == 2: - # Allow 2-tuples to be used as input for this field - return range(v[0] or default_start, v[1] or default_end) - return v - - @validator("channel_range", pre=True, always=True) - def parse_channel_range(cls, v: Any, values: dict) -> Any: - """ - Sets the default channel range if undefined - """ - with ignore_keyerror(): - default_start = 0 - default_end = values["input_image"].sizes["C"] - if v is None: - return range(default_start, default_end) - elif isinstance(v, tuple) and len(v) == 2: - # Allow 2-tuples to be used as input for this field - return range(v[0] or default_start, v[1] or default_end) - return v - - @validator("time_range") - def disjoint_time_range(cls, v: range, values: dict): - """ - Validates that the time range is within the range of channels in our array - """ - with ignore_keyerror(): - max_time = values["input_image"].sizes["T"] - if v.start < 0: - raise ValueError("The lowest valid start value is 0") - if v.stop > max_time: - raise ValueError(f"The highest valid time value is the length of the time axis, which is {max_time}") - - return v - - @validator("channel_range") - def disjoint_channel_range(cls, v: range, values: dict): - """ - Validates that the channel range is within the range of channels in our array - """ - with ignore_keyerror(): - max_channel = values["input_image"].sizes["C"] - if v.start < 0: - raise ValueError("The lowest valid start value is 0") - if v.stop > max_channel: - raise ValueError(f"The highest valid channel value is the length of the channel axis, which is {max_channel}") - return v - - @validator("channel_range") - def channel_range_subset(cls, v: Optional[range], values: dict): - with ignore_keyerror(): - if v is not None and (min(v) < 0 or max(v) > values["input_image"].sizes["C"]): - raise ValueError("The output channel range must be a subset of the total available channels") - return v - - @validator("time_range") - def time_range_subset(cls, v: Optional[range], values: dict): - if v is not None and (min(v) < 0 or max(v) > values["input_image"].sizes["T"]): - raise ValueError("The output time range must be a subset of the total available time points") - return v - - @validator("deconvolution") - def check_psfs(cls, v: Optional[DeconvolutionParams], values: dict): - if v is None: - return v - with ignore_keyerror(): - channels = values["input_image"].sizes["C"] - psfs = len(v.psf) - if psfs != channels: - raise ValueError(f"There should be one PSF per channel, but there are {psfs} PSFs and {channels} channels.") - return v - - @property - def cropping_enabled(self) -> bool: - "True if cropping should be performed" - return self.crop is not None - - @property - def deconv_enabled(self) -> bool: - "True if deconvolution should be performed" - return self.deconvolution is not None - - def __post_init__(self): - logger.info(f"Channels: {self.channels}, Time: {self.time}") - logger.info("If channel and time need to be swapped, you can enforce this by choosing 'Last dimension is channel' when initialising the plugin") - - def slice_data(self, time: int, channel: int) -> DataArray: - if time > self.time: - raise ValueError("time is out of range") - if channel > self.channels: - raise ValueError("channel is out of range") - - return self.input_image.isel(T=time, C=channel) - - def iter_slices(self) -> Iterable[ProcessedSlice[ArrayLike]]: - """ - Yields array slices for each time and channel of interest. - - Returns: - An iterable of tuples. Each tuple contains (time_index, time, channel_index, channel, slice) - """ - from lls_core.models.results import ProcessedSlice - for time_idx, time in enumerate(self.time_range): - for ch_idx, ch in enumerate(self.channel_range): - yield ProcessedSlice( - data=self.slice_data(time=time, channel=ch), - time_index=time_idx, - time= time, - channel_index=ch_idx, - channel=ch, - ) - - def iter_sublattices(self, update_with: dict = {}) -> Iterable[ProcessedSlice[LatticeData]]: - """ - Yields copies of the current LatticeData, one for each slice. - These copies can then be processed separately. - Args: - update_with: dictionary of arguments to update the generated lattices with - """ - for subarray in self.iter_slices(): - new_lattice = self.copy_validate(update={ - "input_image": subarray.data, - "time_range": range(1), - "channel_range": range(1), - **update_with - }) - yield subarray.copy_with_data( new_lattice) - - def generate_workflows( - self, - ) -> Iterable[ProcessedSlice[Workflow]]: - """ - Yields copies of the input workflow, modified with the addition of deskewing and optionally, - cropping and deconvolution - """ - if self.workflow is None: - return - - from copy import copy - from lls_core.workflow import get_workflow_inputs, update_workflow - # We make a copy of the lattice for each slice, each of which has no associated workflow - for lattice_slice in self.iter_sublattices(update_with={"workflow": None}): - user_workflow = copy(self.workflow) - user_workflow.set( - "deskew_image", - LatticeData.process_into_image, - lattice_slice.data - ) - for task_name, arg_index, arg_name in get_workflow_inputs(user_workflow): - update_workflow( - user_workflow, - task_name, - arg_index, - "deskew_image" - ) - yield lattice_slice.copy_with_data(user_workflow) - - def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int): - """ - Checks for a slice with incomplete data, caused by incomplete acquisition - """ - import numpy as np - if not isinstance(volume, DaskArray): - return volume - orig_shape = volume.shape - raw_vol = volume.compute() - if raw_vol.shape != orig_shape: - logger.warn(f"Time {time_point}, channel {channel} is incomplete. Actual shape {orig_shape}, got {raw_vol.shape}") - z_diff, y_diff, x_diff = np.subtract(orig_shape, raw_vol.shape) - logger.info(f"Padding with{z_diff,y_diff,x_diff}") - raw_vol = np.pad(raw_vol, ((0, z_diff), (0, y_diff), (0, x_diff))) - if raw_vol.shape != orig_shape: - raise Exception(f"Shape of last timepoint still doesn't match. Got {raw_vol.shape}") - return raw_vol - - @property - def deskewed_volume(self) -> DaskArray: - from dask.array import zeros - return zeros(self.deskew_vol_shape) - - def _process_crop(self) -> Iterable[ImageSlice]: - """ - Yields processed image slices with cropping enabled - """ - if self.crop is None: - raise Exception("This function can only be called when crop is set") - - # We have an extra level of iteration for the crop path: iterating over each ROI - for roi_index, roi in enumerate(tqdm(self.crop.selected_rois, desc="ROI:", position=0)): - # pass arguments for save tiff, callable and function arguments - logger.info(f"Processing ROI {roi_index}") - - deconv_args: dict[Any, Any] = {} - if self.deconvolution is not None: - deconv_args = dict( - num_iter = self.deconvolution.psf_num_iter, - psf = self.deconvolution.psf, - decon_processing=self.deconvolution.decon_processing - ) - - for slice in self.iter_slices(): - yield slice.copy_with_data( - crop_volume_deskew( - original_volume=slice.data, - deconvolution=self.deconv_enabled, - get_deskew_and_decon=False, - debug=False, - roi_shape=list(roi), - linear_interpolation=True, - voxel_size_x=self.dx, - voxel_size_y=self.dy, - voxel_size_z=self.dy, - angle_in_degrees=self.angle, - deskewed_volume=self.deskewed_volume, - z_start=self.crop.z_range[0], - z_end=self.crop.z_range[1], - **deconv_args - ) - ) - - def _process_non_crop(self) -> Iterable[ImageSlice]: - """ - Yields processed image slices without cropping - """ - for slice in self.iter_slices(): - data: ArrayLike = slice.data - if isinstance(slice.data, DaskArray): - data = slice.data.compute() - if self.deconvolution is not None: - if self.deconvolution.decon_processing == DeconvolutionChoice.cuda_gpu: - data = pycuda_decon( - image=data, - psf=self.deconvolution.psf[slice.channel].to_numpy(), - background=self.deconvolution.background, - dzdata=self.dz, - dxdata=self.dx, - dzpsf=self.dz, - dxpsf=self.dx, - num_iter=self.deconvolution.psf_num_iter - ) - else: - data = skimage_decon( - vol_zyx=data, - psf=self.deconvolution.psf[slice.channel].to_numpy(), - num_iter=self.deconvolution.psf_num_iter, - clip=False, - filter_epsilon=0, - boundary='nearest' - ) - - yield slice.copy_with_data( - cle.pull_zyx(self.deskew_func( - input_image=data, - angle_in_degrees=self.angle, - linear_interpolation=True, - voxel_size_x=self.dx, - voxel_size_y=self.dy, - voxel_size_z=self.dz - )) - ) - - def process_workflow(self) -> WorkflowSlices: - from lls_core.models.results import WorkflowSlices - WorkflowSlices.update_forward_refs(LatticeData=LatticeData) - outputs = [] - for workflow in self.generate_workflows(): - for leaf in workflow.data.leafs(): - outputs.append( - workflow.copy_with_data( - workflow.data.get(leaf) - ) - ) - - return WorkflowSlices( - slices = outputs, - lattice_data=self - ) - - def process(self) -> ImageSlices: - """ - Execute the processing and return the result. - This will not execute the attached workflow. - """ - from lls_core.models.results import ImageSlices - ImageSlices.update_forward_refs(LatticeData=LatticeData) - - if self.cropping_enabled: - return ImageSlices( - lattice_data=self, - slices=self._process_crop() - ) - else: - return ImageSlices( - lattice_data=self, - slices=self._process_non_crop() - ) - - def save(self): - """ - - This is the main public API for processing - """ - if self.workflow: - list(self.process_workflow().save()) - else: - self.process().save_image() - - def process_into_image(self) -> ArrayLike: - """ - Shortcut method for calling process, then extracting one image layer. - This is mostly here to simplify the Workflow integration - """ - for slice in self.process().slices: - return slice.data - raise Exception("No slices produced!") - - def get_writer(self) -> Type[Writer]: - from lls_core.writers import BdvWriter, TiffWriter - if self.save_type == SaveFileType.h5: - return BdvWriter - elif self.save_type == SaveFileType.tiff: - return TiffWriter - raise Exception("Unknown output type") +from __future__ import annotations +# class for initializing lattice data and setting metadata +# TODO: handle scenes +from pydantic import Field, root_validator, validator +from dask.array.core import Array as DaskArray + +from typing import Any, Iterable, Optional, TYPE_CHECKING, Type + +import pyclesperanto_prototype as cle +from tqdm import tqdm + +from lls_core import DeconvolutionChoice +from lls_core.deconvolution import pycuda_decon, skimage_decon +from lls_core.llsz_core import crop_volume_deskew +from lls_core.models.crop import CropParams +from lls_core.models.deconvolution import DeconvolutionParams +from lls_core.models.output import OutputParams, SaveFileType +from lls_core.models.results import WorkflowSlices +from lls_core.models.utils import ignore_keyerror +from lls_core.types import ArrayLike +from lls_core.models.deskew import DeskewParams +from napari_workflows import Workflow +from xarray import DataArray +from pathlib import Path + +if TYPE_CHECKING: + import pyclesperanto_prototype as cle + from lls_core.models.results import ImageSlice, ImageSlices, ProcessedSlice + from lls_core.writers import Writer + +import logging + +logger = logging.getLogger(__name__) + +workflow: Optional[Workflow] = Field( + default=None, + description="If defined, this is a workflow to add lightsheet processing onto" +) + +class LatticeData(OutputParams, DeskewParams): + """ + Holds data and metadata for a given image in a consistent format + """ + + # Note: originally the save-related fields were included via composition and not inheritance + # (similar to how `crop` and `workflow` are handled), but this was impractical for implementing validations + + #: If this is None, then deconvolution is disabled + deconvolution: Optional[DeconvolutionParams] = None + + #: If this is None, then cropping is disabled + crop: Optional[CropParams] = None + + workflow: Optional[Workflow] = workflow + + @root_validator(pre=True) + def use_image_path(cls, values: dict): + # This needs to be a root validator to ensure it runs before the + # reshaping validator. We can't override that either since it's + # a field validator and can't modify save_name + # TODO: separate the image file from the image file path as two separate fields, + # so we don't have to put so much logic here + from lls_core.types import is_pathlike + input_image = values.get("input_image") + if is_pathlike(input_image): + if values.get("save_name") is None: + values["save_name"] = Path(values["input_image"]).stem + + save_dir = values.get("save_dir") + if save_dir is None: + # By default, make the save dir be the same dir as the input + values["save_dir"] = Path(input_image).parent + elif is_pathlike(save_dir): + # Convert a string path to a Path object + values["save_dir"] = Path(save_dir) + + return values + + @validator("workflow", pre=True) + def parse_workflow(cls, v: Any): + # Load the workflow from disk if it was provided as a path + from lls_core.types import is_pathlike + from lls_core.workflow import workflow_from_path + from pathlib import Path + + if is_pathlike(v): + return workflow_from_path(Path(v)) + return v + + @validator("crop") + def default_z_range(cls, v: CropParams, values: dict): + if v is None: + return v + with ignore_keyerror(): + # Fill in missing parts of the z range with the min/max z values + default_start = 0 + default_end = values["input_image"].sizes["Z"] + + # Set defaults + if v.z_range is None: + v.z_range = (default_start, default_end) + if v.z_range[0] is None: + v.z_range[0] = default_start + if v.z_range[1] is None: + v.z_range[1] = default_end + + # Validate + if v.z_range[1] > default_end: + raise ValueError(f"The z-index endpoint of {v.z_range[1]} is outside the size of the z-axis ({default_end})") + if v.z_range[0] < default_start: + raise ValueError(f"The z-index start of {v.z_range[0]} is outside the size of the z-axis") + + return v + + @validator("time_range", pre=True, always=True) + def parse_time_range(cls, v: Any, values: dict) -> Any: + """ + Sets the default time range if undefined + """ + # This skips the conversion if no image was provided, to ensure a more + # user-friendly error is provided, namely "image was missing" + with ignore_keyerror(): + default_start = 0 + default_end = values["input_image"].sizes["T"] + if v is None: + return range(default_start, default_end) + elif isinstance(v, tuple) and len(v) == 2: + # Allow 2-tuples to be used as input for this field + return range(v[0] or default_start, v[1] or default_end) + return v + + @validator("channel_range", pre=True, always=True) + def parse_channel_range(cls, v: Any, values: dict) -> Any: + """ + Sets the default channel range if undefined + """ + with ignore_keyerror(): + default_start = 0 + default_end = values["input_image"].sizes["C"] + if v is None: + return range(default_start, default_end) + elif isinstance(v, tuple) and len(v) == 2: + # Allow 2-tuples to be used as input for this field + return range(v[0] or default_start, v[1] or default_end) + return v + + @validator("time_range") + def disjoint_time_range(cls, v: range, values: dict): + """ + Validates that the time range is within the range of channels in our array + """ + with ignore_keyerror(): + max_time = values["input_image"].sizes["T"] + if v.start < 0: + raise ValueError("The lowest valid start value is 0") + if v.stop > max_time: + raise ValueError(f"The highest valid time value is the length of the time axis, which is {max_time}") + + return v + + @validator("channel_range") + def disjoint_channel_range(cls, v: range, values: dict): + """ + Validates that the channel range is within the range of channels in our array + """ + with ignore_keyerror(): + max_channel = values["input_image"].sizes["C"] + if v.start < 0: + raise ValueError("The lowest valid start value is 0") + if v.stop > max_channel: + raise ValueError(f"The highest valid channel value is the length of the channel axis, which is {max_channel}") + return v + + @validator("channel_range") + def channel_range_subset(cls, v: Optional[range], values: dict): + with ignore_keyerror(): + if v is not None and (min(v) < 0 or max(v) > values["input_image"].sizes["C"]): + raise ValueError("The output channel range must be a subset of the total available channels") + return v + + @validator("time_range") + def time_range_subset(cls, v: Optional[range], values: dict): + if v is not None and (min(v) < 0 or max(v) > values["input_image"].sizes["T"]): + raise ValueError("The output time range must be a subset of the total available time points") + return v + + @validator("deconvolution") + def check_psfs(cls, v: Optional[DeconvolutionParams], values: dict): + if v is None: + return v + with ignore_keyerror(): + channels = values["input_image"].sizes["C"] + psfs = len(v.psf) + if psfs != channels: + raise ValueError(f"There should be one PSF per channel, but there are {psfs} PSFs and {channels} channels.") + return v + + @property + def cropping_enabled(self) -> bool: + "True if cropping should be performed" + return self.crop is not None + + @property + def deconv_enabled(self) -> bool: + "True if deconvolution should be performed" + return self.deconvolution is not None + + def __post_init__(self): + logger.info(f"Channels: {self.channels}, Time: {self.time}") + logger.info("If channel and time need to be swapped, you can enforce this by choosing 'Last dimension is channel' when initialising the plugin") + + def slice_data(self, time: int, channel: int) -> DataArray: + if time > self.time: + raise ValueError("time is out of range") + if channel > self.channels: + raise ValueError("channel is out of range") + + return self.input_image.isel(T=time, C=channel) + + def iter_slices(self) -> Iterable[ProcessedSlice[ArrayLike]]: + """ + Yields array slices for each time and channel of interest. + + Returns: + An iterable of tuples. Each tuple contains (time_index, time, channel_index, channel, slice) + """ + from lls_core.models.results import ProcessedSlice + for time_idx, time in enumerate(self.time_range): + for ch_idx, ch in enumerate(self.channel_range): + yield ProcessedSlice( + data=self.slice_data(time=time, channel=ch), + time_index=time_idx, + time= time, + channel_index=ch_idx, + channel=ch, + ) + + def iter_sublattices(self, update_with: dict = {}) -> Iterable[ProcessedSlice[LatticeData]]: + """ + Yields copies of the current LatticeData, one for each slice. + These copies can then be processed separately. + Args: + update_with: dictionary of arguments to update the generated lattices with + """ + for subarray in self.iter_slices(): + new_lattice = self.copy_validate(update={ + "input_image": subarray.data, + "time_range": range(1), + "channel_range": range(1), + **update_with + }) + yield subarray.copy_with_data( new_lattice) + + def generate_workflows( + self, + ) -> Iterable[ProcessedSlice[Workflow]]: + """ + Yields copies of the input workflow, modified with the addition of deskewing and optionally, + cropping and deconvolution + """ + if self.workflow is None: + return + + from copy import copy + from lls_core.workflow import get_workflow_inputs, update_workflow + # We make a copy of the lattice for each slice, each of which has no associated workflow + for lattice_slice in self.iter_sublattices(update_with={"workflow": None}): + user_workflow = copy(self.workflow) + user_workflow.set( + "deskew_image", + LatticeData.process_into_image, + lattice_slice.data + ) + for task_name, arg_index, arg_name in get_workflow_inputs(user_workflow): + update_workflow( + user_workflow, + task_name, + arg_index, + "deskew_image" + ) + yield lattice_slice.copy_with_data(user_workflow) + + def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int): + """ + Checks for a slice with incomplete data, caused by incomplete acquisition + """ + import numpy as np + if not isinstance(volume, DaskArray): + return volume + orig_shape = volume.shape + raw_vol = volume.compute() + if raw_vol.shape != orig_shape: + logger.warn(f"Time {time_point}, channel {channel} is incomplete. Actual shape {orig_shape}, got {raw_vol.shape}") + z_diff, y_diff, x_diff = np.subtract(orig_shape, raw_vol.shape) + logger.info(f"Padding with{z_diff,y_diff,x_diff}") + raw_vol = np.pad(raw_vol, ((0, z_diff), (0, y_diff), (0, x_diff))) + if raw_vol.shape != orig_shape: + raise Exception(f"Shape of last timepoint still doesn't match. Got {raw_vol.shape}") + return raw_vol + + @property + def deskewed_volume(self) -> DaskArray: + from dask.array import zeros + return zeros(self.deskew_vol_shape) + + def _process_crop(self) -> Iterable[ImageSlice]: + """ + Yields processed image slices with cropping enabled + """ + if self.crop is None: + raise Exception("This function can only be called when crop is set") + + # We have an extra level of iteration for the crop path: iterating over each ROI + for roi_index, roi in enumerate(tqdm(self.crop.selected_rois, desc="ROI:", position=0)): + # pass arguments for save tiff, callable and function arguments + logger.info(f"Processing ROI {roi_index}") + + deconv_args: dict[Any, Any] = {} + if self.deconvolution is not None: + deconv_args = dict( + num_iter = self.deconvolution.psf_num_iter, + psf = self.deconvolution.psf, + decon_processing=self.deconvolution.decon_processing + ) + + for slice in self.iter_slices(): + yield slice.copy_with_data( + crop_volume_deskew( + original_volume=slice.data, + deconvolution=self.deconv_enabled, + get_deskew_and_decon=False, + debug=False, + roi_shape=list(roi), + linear_interpolation=True, + voxel_size_x=self.dx, + voxel_size_y=self.dy, + voxel_size_z=self.dy, + angle_in_degrees=self.angle, + deskewed_volume=self.deskewed_volume, + # z_start=self.crop.z_range[0], + # z_end=self.crop.z_range[1], + **deconv_args + ) + ) + + def _process_non_crop(self) -> Iterable[ImageSlice]: + """ + Yields processed image slices without cropping + """ + for slice in self.iter_slices(): + data: ArrayLike = slice.data + if isinstance(slice.data, DaskArray): + data = slice.data.compute() + if self.deconvolution is not None: + if self.deconvolution.decon_processing == DeconvolutionChoice.cuda_gpu: + data = pycuda_decon( + image=data, + psf=self.deconvolution.psf[slice.channel].to_numpy(), + background=self.deconvolution.background, + dzdata=self.dz, + dxdata=self.dx, + dzpsf=self.dz, + dxpsf=self.dx, + num_iter=self.deconvolution.psf_num_iter + ) + else: + data = skimage_decon( + vol_zyx=data, + psf=self.deconvolution.psf[slice.channel].to_numpy(), + num_iter=self.deconvolution.psf_num_iter, + clip=False, + filter_epsilon=0, + boundary='nearest' + ) + + yield slice.copy_with_data( + cle.pull_zyx(self.deskew_func( + input_image=data, + angle_in_degrees=self.angle, + linear_interpolation=True, + voxel_size_x=self.dx, + voxel_size_y=self.dy, + voxel_size_z=self.dz + )) + ) + + def process_workflow(self) -> WorkflowSlices: + from lls_core.models.results import WorkflowSlices + WorkflowSlices.update_forward_refs(LatticeData=LatticeData) + outputs = [] + for workflow in self.generate_workflows(): + for leaf in workflow.data.leafs(): + outputs.append( + workflow.copy_with_data( + workflow.data.get(leaf) + ) + ) + + return WorkflowSlices( + slices = outputs, + lattice_data=self + ) + + def process(self) -> ImageSlices: + """ + Execute the processing and return the result. + This will not execute the attached workflow. + """ + from lls_core.models.results import ImageSlices + ImageSlices.update_forward_refs(LatticeData=LatticeData) + + if self.cropping_enabled: + return ImageSlices( + lattice_data=self, + slices=self._process_crop() + ) + else: + return ImageSlices( + lattice_data=self, + slices=self._process_non_crop() + ) + + def save(self): + """ + + This is the main public API for processing + """ + if self.workflow: + list(self.process_workflow().save()) + else: + self.process().save_image() + + def process_into_image(self) -> ArrayLike: + """ + Shortcut method for calling process, then extracting one image layer. + This is mostly here to simplify the Workflow integration + """ + for slice in self.process().slices: + return slice.data + raise Exception("No slices produced!") + + def get_writer(self) -> Type[Writer]: + from lls_core.writers import BdvWriter, TiffWriter + if self.save_type == SaveFileType.h5: + return BdvWriter + elif self.save_type == SaveFileType.tiff: + return TiffWriter + raise Exception("Unknown output type") diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index c9aa883..62861a6 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -433,7 +433,7 @@ def _make_model(self) -> Optional[CropParams]: import numpy as np if self.fields_enabled.value: return CropParams( - roi_list=ShapesData([np.array(shape.data) / self._get_deskew().dy for shape in self.shapes.value]) + roi_list=ShapesData([np.array(shape.data) / self._get_deskew().dy for shape in self.shapes.value]) ) return None From 60a1c152c910161b00e341660c66120279e80769 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 17 Nov 2023 16:23:39 +1100 Subject: [PATCH 100/147] Revert "Remove the z cropping fields from the GUI" This reverts commit 2000786354f11f2e0f25887cf9e6d0ac75887119. --- core/lls_core/models/lattice_data.py | 4 ++-- plugin/napari_lattice/fields.py | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 857c497..d3cd27c 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -337,8 +337,8 @@ def _process_crop(self) -> Iterable[ImageSlice]: voxel_size_z=self.dy, angle_in_degrees=self.angle, deskewed_volume=self.deskewed_volume, - # z_start=self.crop.z_range[0], - # z_end=self.crop.z_range[1], + z_start=self.crop.z_range[0], + z_end=self.crop.z_range[1], **deconv_args ) ) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 62861a6..e170bd7 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -398,6 +398,13 @@ class CroppingFields(NapariFieldGroup): # A counterpart to the CropParams Pydantic class fields_enabled = field(False, label="Enabled") shapes= field(List[Shapes], widget_type="Select", label = "ROI Shape Layers").with_options(choices=lambda _x, _y: get_layers(Shapes)) + z_range = field(Tuple[int, int]).with_options( + label = "Z Range", + value = (0, 1), + options = dict( + min = 0, + ), + ) errors = field(Label).with_options(label="Errors") def _get_deskew(self) -> DeskewParams: @@ -424,8 +431,13 @@ def new_crop_layer(self): shapes.mode = "ADD_RECTANGLE" shapes.name = "Napari Lattice Crop" + def _on_image_changed(self, img: DataArray): + # Update the maximum Z + for widget in self.z_range: + adjust_maximum(widget, img.sizes["Z"]) + @fields_enabled.connect - @enable_if([shapes]) + @enable_if([shapes, z_range]) def _enable_crop(self, enabled: bool) -> bool: return enabled @@ -433,7 +445,8 @@ def _make_model(self) -> Optional[CropParams]: import numpy as np if self.fields_enabled.value: return CropParams( - roi_list=ShapesData([np.array(shape.data) / self._get_deskew().dy for shape in self.shapes.value]) + roi_list=ShapesData([np.array(shape.data) / self._get_deskew().dy for shape in self.shapes.value]), + z_range=self.z_range.value, ) return None From 5addbccce1d2250e326647b27add0b50fc87e2bb Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 17 Nov 2023 17:40:56 +1100 Subject: [PATCH 101/147] Tests for float ROIs --- core/lls_core/models/crop.py | 8 ++++++-- core/tests/test_process.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index 65e6160..3e881f9 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -44,11 +44,15 @@ def read_roi(cls, v: Any) -> List[Roi]: elif isinstance(item, Roi): rois.append(item) else: - raise ValueError(f"{item} cannot be intepreted as an ROI") + # Try converting an iterable to ROI + try: + rois.append(Roi(*item)) + except: + raise ValueError(f"{item} cannot be intepreted as an ROI") return rois - @validator("roi_subset", pre=True) + @validator("roi_subset", pre=True, always=True) def default_roi_range(cls, v: Any, values: dict): # If the roi range isn't provided, assume all rois should be processed if v is None: diff --git a/core/tests/test_process.py b/core/tests/test_process.py index 7df0dd0..86c802c 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -69,7 +69,8 @@ def test_process_workflow(args: dict, request: FixtureRequest, workflow_name: st [[0, 1]], ]) @parameterized -def test_process_crop(args: dict, roi_subset: Optional[List[int]]): +def test_process_crop_roi_file(args: dict, roi_subset: Optional[List[int]]): + # Test cropping with a roi zip file, selecting different subsets from that file with as_file(resources / "RBC_tiny.czi") as lattice_path: rois = root / "crop" / "two_rois.zip" for slice in LatticeData.parse_obj({ @@ -81,3 +82,30 @@ def test_process_crop(args: dict, roi_subset: Optional[List[int]]): **args }).process().slices: assert slice.data.ndim == 3 + +@pytest.mark.parametrize(["roi"], [ + [[[ + (174.0, 24.0), + (174.0, 88.0), + (262.0, 88.0), + (262.0, 24.0) + ]]], + [[[ + (174.13, 24.2), + (173.98, 87.87), + (262.21, 88.3), + (261.99, 23.79) + ]]], +]) +@parameterized +def test_process_crop_roi_manual(args: dict, roi: List): + # Test manually provided ROIs, both with integer and float values + with as_file(resources / "RBC_tiny.czi") as lattice_path: + for slice in LatticeData.parse_obj({ + "input_image": lattice_path, + "crop": { + "roi_list": roi + }, + **args + }).process().slices: + assert slice.data.ndim == 3 From e36bcfde31f544fb4997d05e8957701c4447eaf1 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 20 Nov 2023 13:43:56 +1100 Subject: [PATCH 102/147] Rework Z-range handling to use deskewed max, and to update automatically --- core/lls_core/cmds/__main__.py | 4 +- core/lls_core/models/crop.py | 9 ++-- core/lls_core/models/deconvolution.py | 4 +- core/lls_core/models/deskew.py | 69 ++++++++++++++++----------- core/lls_core/models/lattice_data.py | 9 ++-- core/lls_core/models/output.py | 4 +- core/lls_core/models/utils.py | 4 +- core/tests/conftest.py | 5 ++ core/tests/test_validation.py | 20 +++++--- plugin/napari_lattice/fields.py | 19 ++++++-- 10 files changed, 95 insertions(+), 52 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 1fd8919..2eaabf7 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -22,7 +22,7 @@ from pydantic import ValidationError if TYPE_CHECKING: - from lls_core.models.utils import FieldAccessMixin + from lls_core.models.utils import FieldAccessModel from typing import Type, Any from rich.table import Table @@ -55,7 +55,7 @@ class CliDeskewDirection(StrEnum): app = Typer(add_completion=False, rich_markup_mode="rich", no_args_is_help=True) -def field_from_model(model: Type[FieldAccessMixin], field_name: str, extra_description: str = "", description: Optional[str] = None, default: Optional[Any] = None, **kwargs) -> Any: +def field_from_model(model: Type[FieldAccessModel], field_name: str, extra_description: str = "", description: Optional[str] = None, default: Optional[Any] = None, **kwargs) -> Any: """ Generates a type Field from a Pydantic model field """ diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index 3e881f9..7871452 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -1,11 +1,14 @@ from typing import Iterable, List, Tuple, Any from pydantic import Field, NonNegativeInt, validator -from lls_core.models.utils import FieldAccessMixin +from lls_core.models.utils import FieldAccessModel from lls_core.cropping import Roi -class CropParams(FieldAccessMixin): +class CropParams(FieldAccessModel): """ - Parameters for the optional cropping step + Parameters for the optional cropping step. + Note that cropping is performed in the space of the deskewed shape. + This is to support the workflow of performing a preview deskew and using that + to calculate the cropping coordinates. """ roi_list: List[Roi] = Field( description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex.", diff --git a/core/lls_core/models/deconvolution.py b/core/lls_core/models/deconvolution.py index bd50f2b..373734e 100644 --- a/core/lls_core/models/deconvolution.py +++ b/core/lls_core/models/deconvolution.py @@ -7,12 +7,12 @@ from xarray import DataArray from lls_core import DeconvolutionChoice -from lls_core.models.utils import enum_choices, FieldAccessMixin +from lls_core.models.utils import enum_choices, FieldAccessModel from lls_core.types import image_like_to_image, ImageLike Background = Union[float, Literal["auto", "second_last"]] -class DeconvolutionParams(FieldAccessMixin): +class DeconvolutionParams(FieldAccessModel): """ Parameters for the optional deconvolution step """ diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 96d5c55..bac6e2d 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -1,7 +1,7 @@ from __future__ import annotations # class for initializing lattice data and setting metadata # TODO: handle scenes -from pydantic import Field, NonNegativeFloat, validator, root_validator +from pydantic import Field, NonNegativeFloat, validator from typing import Any, Tuple from typing_extensions import Self, TYPE_CHECKING @@ -11,16 +11,14 @@ from lls_core import DeskewDirection from xarray import DataArray -from lls_core.models.utils import FieldAccessMixin, enum_choices +from lls_core.models.utils import FieldAccessModel, enum_choices from lls_core.types import image_like_to_image from lls_core.utils import get_deskewed_shape if TYPE_CHECKING: from aicsimageio.types import PhysicalPixelSizes -# DeskewDirection = Literal["X", "Y"] - -class DefinedPixelSizes(FieldAccessMixin): +class DefinedPixelSizes(FieldAccessModel): """ Like PhysicalPixelSizes, but it's a dataclass, and none of its fields are None @@ -39,8 +37,21 @@ def from_physical(cls, pixels: PhysicalPixelSizes) -> Self: Z=raise_if_none(pixels.Z, "All pixels must be defined"), ) +class DerivedDeskewFields(FieldAccessModel): + """ + Fields that are automatically calculated based on other fields in DeskewParams. + Grouping these together into one model makes validation simpler. + """ + deskew_vol_shape: Tuple[int, ...] = Field( + init_var=False, + default=None, + description="Dimensions of the deskewed output. This is set automatically based on other input parameters, and doesn't need to be provided by the user." + ) + + deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False, default=None, description="Deskewing transformation function. This is set automatically based on other input parameters, and doesn't need to be provided by the user.") + -class DeskewParams(FieldAccessMixin): +class DeskewParams(FieldAccessModel): input_image: DataArray = Field( description="A 3-5D array containing the image data." ) @@ -53,17 +64,14 @@ class DeskewParams(FieldAccessMixin): description="Angle of deskewing, in degrees." ) physical_pixel_sizes: DefinedPixelSizes = Field( - default_factory=DefinedPixelSizes, - description="Pixel size of the microscope, in microns." -) - deskew_vol_shape: Tuple[int, ...] = Field( + default_factory=DefinedPixelSizes, + description="Pixel size of the microscope, in microns." + ) + derived: DerivedDeskewFields = Field( init_var=False, default=None, - description="Dimensions of the deskewed output. This is set automatically based on other input parameters, and doesn't need to be provided by the user." + description="Refer to the DerivedDeskewFields docstring" ) - - deskew_affine_transform: cle.AffineTransform3D = Field(init_var=False, default=None, description="Deskewing transformation function. This is set automatically based on other input parameters, and doesn't need to be provided by the user.") - # Hack to ensure that .skew_dir behaves identically to .skew @property def skew_dir(self) -> DeskewDirection: @@ -170,19 +178,26 @@ def reshaping(cls, v: Any): def get_3d_slice(self) -> DataArray: return self.input_image.isel(C=0, T=0) - @root_validator(pre=False) - def set_deskew(cls, values: dict) -> dict: + @validator("derived", always=True) + def calculate_derived(cls, v: Any, values: dict) -> DerivedDeskewFields: """ Sets the default deskew shape values if the user has not provided them """ - # process the file to get shape of final deskewed image - if "input_image" not in values: - return values - data: DataArray = cls.reshaping(values["input_image"]) - if values.get('deskew_vol_shape') is None: - if values.get('deskew_affine_transform') is None: - # If neither has been set, calculate them ourselves - values["deskew_vol_shape"], values["deskew_affine_transform"] = get_deskewed_shape(data.isel(C=0, T=0).to_numpy(), values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, values["physical_pixel_sizes"].Z, values["skew"]) - else: - raise ValueError("deskew_vol_shape and deskew_affine_transform must be either both specified or neither specified") - return values + data: DataArray = values["input_image"] + if isinstance(v, DerivedDeskewFields): + return v + elif v is None: + deskew_vol_shape, deskew_affine_transform = get_deskewed_shape( + data.isel(C=0, T=0).to_numpy(), + values["angle"], + values["physical_pixel_sizes"].X, + values["physical_pixel_sizes"].Y, + values["physical_pixel_sizes"].Z, + values["skew"] + ) + return DerivedDeskewFields( + deskew_affine_transform=deskew_affine_transform, + deskew_vol_shape=deskew_vol_shape + ) + else: + raise ValueError("Invalid derived fields") diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index d3cd27c..8456250 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -88,13 +88,14 @@ def parse_workflow(cls, v: Any): return v @validator("crop") - def default_z_range(cls, v: CropParams, values: dict): + def default_z_range(cls, v: Optional[CropParams], values: dict) -> Optional[CropParams]: if v is None: return v with ignore_keyerror(): - # Fill in missing parts of the z range with the min/max z values + # Fill in missing parts of the z range + # The max allowed value is the length of the deskew Z axis default_start = 0 - default_end = values["input_image"].sizes["Z"] + default_end = values["derived"].deskew_vol_shape[0] # Set defaults if v.z_range is None: @@ -110,7 +111,7 @@ def default_z_range(cls, v: CropParams, values: dict): if v.z_range[0] < default_start: raise ValueError(f"The z-index start of {v.z_range[0]} is outside the size of the z-axis") - return v + return v @validator("time_range", pre=True, always=True) def parse_time_range(cls, v: Any, values: dict) -> Any: diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index b1374b2..b6dbc02 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any -from lls_core.models.utils import FieldAccessMixin, enum_choices +from lls_core.models.utils import FieldAccessModel, enum_choices if TYPE_CHECKING: pass @@ -14,7 +14,7 @@ class SaveFileType(StrEnum): h5 = "h5" tiff = "tiff" -class OutputParams(FieldAccessMixin): +class OutputParams(FieldAccessModel): save_dir: DirectoryPath = Field( description="The directory where the output data will be saved" ) diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py index 3e49bcd..768595c 100644 --- a/core/lls_core/models/utils.py +++ b/core/lls_core/models/utils.py @@ -23,7 +23,7 @@ def ignore_keyerror(): except KeyError: pass -class FieldAccessMixin(BaseModel): +class FieldAccessModel(BaseModel): """ Adds methods to a BaseModel for accessing useful field information """ @@ -54,7 +54,7 @@ def to_definition_dict(cls) -> dict: """ ret = {} for key, value in cls.__fields__.items(): - if isinstance(value.outer_type_, type) and issubclass(value.outer_type_, FieldAccessMixin): + if isinstance(value.outer_type_, type) and issubclass(value.outer_type_, FieldAccessModel): value = value.outer_type_.to_definition_dict() else: value = value.field_info.description diff --git a/core/tests/conftest.py b/core/tests/conftest.py index 22e05ed..457aa9e 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -17,6 +17,11 @@ def runner() -> CliRunner: return CliRunner() +@pytest.fixture +def rbc_tiny(): + with as_file(resources / "RBC_tiny.czi") as image_path: + yield image_path + @pytest.fixture(params=[ "RBC_tiny.czi", "RBC_lattice.tif", diff --git a/core/tests/test_validation.py b/core/tests/test_validation.py index 9084ff9..76d1ad6 100644 --- a/core/tests/test_validation.py +++ b/core/tests/test_validation.py @@ -1,10 +1,16 @@ +from pathlib import Path +from lls_core.models.crop import CropParams from lls_core.models.lattice_data import LatticeData -from lls_core.sample import resources -from importlib_resources import as_file - -def test_default_save_dir(): +def test_default_save_dir(rbc_tiny: Path): # Test that the save dir is inferred to be the input dir - with as_file(resources / "RBC_tiny.czi") as path: - params = LatticeData(input_image=path) - assert params.save_dir == path.parent + params = LatticeData(input_image=rbc_tiny) + assert params.save_dir == rbc_tiny.parent + +def test_auto_z_range(rbc_tiny: Path): + # Tests that the Z range is automatically set, and it is set + # based on the size of the deskewed volume + params = LatticeData(input_image=rbc_tiny, crop=CropParams( + roi_list=[] + )) + assert params.crop.z_range == (0, 59) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index e170bd7..09283db 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -1,6 +1,7 @@ # FieldGroups that the users interact with to input data import logging from pathlib import Path +from textwrap import dedent from typing import Any, Callable, List, Optional, Tuple, TYPE_CHECKING from typing_extensions import TypeVar import pyclesperanto_prototype as cle @@ -396,6 +397,10 @@ def _make_model(self) -> Optional[DeconvolutionParams]: @magicclass class CroppingFields(NapariFieldGroup): # A counterpart to the CropParams Pydantic class + header = field(dedent(""" + Note that all cropping, including the regions of interest and Z range, is performed in the space of the deskewed shape. + This is to support the workflow of performing a preview deskew and using that to calculate the cropping coordinates. + """), widget_type="Label") fields_enabled = field(False, label="Enabled") shapes= field(List[Shapes], widget_type="Select", label = "ROI Shape Layers").with_options(choices=lambda _x, _y: get_layers(Shapes)) z_range = field(Tuple[int, int]).with_options( @@ -432,9 +437,15 @@ def new_crop_layer(self): shapes.name = "Napari Lattice Crop" def _on_image_changed(self, img: DataArray): - # Update the maximum Z + deskew = self._get_deskew() + deskewed_zmax = deskew.derived.deskew_vol_shape[0] + + # Update the allowed Z based the deskewed shape for widget in self.z_range: - adjust_maximum(widget, img.sizes["Z"]) + adjust_maximum(widget, deskewed_zmax) + + # Update the current max value to be the max of the shape + self.z_range[1].value = deskewed_zmax @fields_enabled.connect @enable_if([shapes, z_range]) @@ -445,8 +456,10 @@ def _make_model(self) -> Optional[CropParams]: import numpy as np if self.fields_enabled.value: return CropParams( + # Convert from the input image space to the deskewed image space + # We assume here that dx == dy which isn't ideal roi_list=ShapesData([np.array(shape.data) / self._get_deskew().dy for shape in self.shapes.value]), - z_range=self.z_range.value, + z_range=tuple(np.array(self.z_range.value) / self._get_deskew().new_dz), ) return None From 9990f78947f6fb4ed9898e5af5730f63636cc713 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 20 Nov 2023 14:16:43 +1100 Subject: [PATCH 103/147] Fix broken GUI tests --- plugin/napari_lattice/dock_widget.py | 328 ++++++++++++++------------- plugin/tests/test_dock_widget.py | 174 +++++++------- 2 files changed, 253 insertions(+), 249 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 7c90d74..d712b56 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -1,162 +1,166 @@ -from __future__ import annotations - -import logging -from textwrap import dedent -from typing import TYPE_CHECKING -import numpy as np -from lls_core.models.lattice_data import LatticeData -from magicclass import MagicTemplate, field, magicclass, set_options, vfield -from magicclass.wrappers import set_design -from napari_lattice.fields import ( - CroppingFields, - DeconvolutionFields, - DeskewFields, - OutputFields, - WorkflowFields, -) -from qtpy.QtCore import Qt -from qtpy.QtWidgets import QTabWidget - -if TYPE_CHECKING: - from typing import Iterable - from napari_lattice.fields import NapariFieldGroup - -# Enable Logging -logger = logging.getLogger(__name__) -logger.setLevel(logging.INFO) - -@magicclass(widget_type="split") -class LLSZWidget(MagicTemplate): - def __post_init__(self): - # aligning collapsible widgets at the top instead of having them centered vertically - self._widget._layout.setAlignment(Qt.AlignTop) - - - def _check_validity(self) -> bool: - """ - Returns True if the model is valid - """ - try: - self._make_model() - return True - except: - return False - - def _make_model(self, validate: bool = True) -> LatticeData: - from rich import print - from sys import stdout - - deskew_args = self.LlszMenu.WidgetContainer.deskew_fields._get_kwargs() - output_args = self.LlszMenu.WidgetContainer.output_fields._make_model(validate=False) - args = dict( - input_image=deskew_args["data"], - angle=deskew_args["angle"], - channel_range=output_args.channel_range, - time_range=output_args.time_range, - save_dir=output_args.save_dir, - # We let the user specify a prefix, but if they don't, we can use the default - save_name=output_args.save_name or deskew_args["save_name"] , - save_type=output_args.save_type, - physical_pixel_sizes=deskew_args["physical_pixel_sizes"], - skew=deskew_args["skew"], - workflow=self.LlszMenu.WidgetContainer.workflow_fields._make_model(), - deconvolution=self.LlszMenu.WidgetContainer.deconv_fields._make_model(), - crop=self.LlszMenu.WidgetContainer.cropping_fields._make_model() - ) - params = LatticeData.make(validate=validate, **args) - # Log the lattice - print(params, file=stdout) - return params - - @magicclass(widget_type="split") - class LlszMenu(MagicTemplate): - main_heading = field("

      Napari Lattice: Visualization & Analysis

      ", widget_type="Label") - heading1 = field(dedent(""" -
      - Specify deskewing parameters and image layers in Tab 1. - Additional analysis parameters can be configured in the other tabs. - When you are ready to save, go to Tab 5. - Output to specify the output directory. - For more information, please refer to the documentation here. -
      - """.strip()), widget_type="Label") - - def __post_init__(self): - from qtpy.QtCore import Qt - from qtpy.QtWidgets import QLabel, QLayout - - if isinstance(self._widget._layout, QLayout): - self._widget._layout.setAlignment(Qt.AlignmentFlag.AlignTop) - - if isinstance(self.heading1.native, QLabel): - self.heading1.native.setWordWrap(True) - - # Tabbed Widget container to house all the widgets - @magicclass(widget_type="tabbed", name="Functions", labels=False) - class WidgetContainer(MagicTemplate): - - def __post_init__(self): - tab_widget: QTabWidget= self._widget._tab_widget - # Manually set the tab labels, because by default magicgui uses the widget names, but setting - # the names to human readable text makes them difficult to access via self - for i, label in enumerate(["1. Deskew", "2. Deconvolution", "3. Crop", "4. Workflow", "5. Output"]): - tab_widget.setTabText(i, label) - for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: - field._validate() - - # Using vfields here seems to prevent https://github.com/hanjinliu/magic-class/issues/110 - deskew_fields = vfield(DeskewFields) - deconv_fields = vfield(DeconvolutionFields) - cropping_fields = vfield(CroppingFields) - workflow_fields = vfield(WorkflowFields) - output_fields = vfield(OutputFields) - - @set_options(header=dict(widget_type="Label", label="

      Preview Deskew

      "), - time=dict(label="Time:", max=2**15), - channel=dict(label="Channel:"), - call_button="Preview" - ) - @set_design(text="Preview") - def preview(self, header: str, time: int, channel: int): - from pathlib import Path - - # We only need to process one time point for the preview, - # so we made a copy using a subset of the times - lattice = self._make_model(validate=False).copy_validate(update=dict( - time_range = range(time, time+1), - channel_range = range(time, time+1), - # Patch in a placeholder for the save dir because previewing doesn't use it - # TODO: use a more elegant solution such as making the "saveable" lattice - # a child class which more validations - save_dir = Path.home() - )) - - for slice in lattice.process().slices: - scale = ( - lattice.new_dz, - lattice.dy, - lattice.dx - ) - self.parent_viewer.add_image(slice.data, scale=scale) - max_z = np.argmax(np.sum(slice.data, axis=(1, 2))) - self.parent_viewer.dims.set_current_step(0, max_z) - - @set_design(text="Save") - def save(self): - from napari.utils.notifications import show_info - lattice = self._make_model() - lattice.save() - show_info(f"Deskewing successfuly completed. Results are located in {lattice.save_dir}") - - @LlszMenu.WidgetContainer.deskew_fields.connect - def _on_image_changed(self, deskew: DeskewFields): - img = deskew._get_kwargs()["data"] - # We have to manually trigger _on_image_changed because siblings - # classes can't listen to each other's events: https://github.com/hanjinliu/magic-class/issues/129 - for field in self._get_fields(): - field._on_image_changed(img) - - def _get_fields(self) -> Iterable[NapariFieldGroup]: - """Yields all the child Field classes which inherit from NapariFieldGroup""" - container = self.LlszMenu.WidgetContainer - yield from set(container.__magicclass_children__) +from __future__ import annotations + +import logging +from textwrap import dedent +from typing import TYPE_CHECKING +import numpy as np +from lls_core.models.lattice_data import LatticeData +from magicclass import MagicTemplate, field, magicclass, set_options, vfield +from magicclass.wrappers import set_design +from napari_lattice.fields import ( + CroppingFields, + DeconvolutionFields, + DeskewFields, + OutputFields, + WorkflowFields, +) +from qtpy.QtCore import Qt +from qtpy.QtWidgets import QTabWidget + +if TYPE_CHECKING: + from typing import Iterable + from napari_lattice.fields import NapariFieldGroup + +# Enable Logging +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +@magicclass(widget_type="split") +class LLSZWidget(MagicTemplate): + def __post_init__(self): + # aligning collapsible widgets at the top instead of having them centered vertically + self._widget._layout.setAlignment(Qt.AlignTop) + + + def _check_validity(self) -> bool: + """ + Returns True if the model is valid + """ + try: + self._make_model() + return True + except: + return False + + def _make_model(self, validate: bool = True) -> LatticeData: + from rich import print + from sys import stdout + + deskew_args = self.LlszMenu.WidgetContainer.deskew_fields._get_kwargs() + output_args = self.LlszMenu.WidgetContainer.output_fields._make_model(validate=False) + args = dict( + input_image=deskew_args["data"], + angle=deskew_args["angle"], + channel_range=output_args.channel_range, + time_range=output_args.time_range, + save_dir=output_args.save_dir, + # We let the user specify a prefix, but if they don't, we can use the default + save_name=output_args.save_name or deskew_args["save_name"] , + save_type=output_args.save_type, + physical_pixel_sizes=deskew_args["physical_pixel_sizes"], + skew=deskew_args["skew"], + workflow=self.LlszMenu.WidgetContainer.workflow_fields._make_model(), + deconvolution=self.LlszMenu.WidgetContainer.deconv_fields._make_model(), + crop=self.LlszMenu.WidgetContainer.cropping_fields._make_model() + ) + params = LatticeData.make(validate=validate, **args) + # Log the lattice + print(params, file=stdout) + return params + + @magicclass(widget_type="split") + class LlszMenu(MagicTemplate): + main_heading = field("

      Napari Lattice: Visualization & Analysis

      ", widget_type="Label") + heading1 = field(dedent(""" +
      + Specify deskewing parameters and image layers in Tab 1. + Additional analysis parameters can be configured in the other tabs. + When you are ready to save, go to Tab 5. + Output to specify the output directory. + For more information, please refer to the documentation here. +
      + """.strip()), widget_type="Label") + + def __post_init__(self): + from qtpy.QtCore import Qt + from qtpy.QtWidgets import QLabel, QLayout + + if isinstance(self._widget._layout, QLayout): + self._widget._layout.setAlignment(Qt.AlignmentFlag.AlignTop) + + if isinstance(self.heading1.native, QLabel): + self.heading1.native.setWordWrap(True) + + # Tabbed Widget container to house all the widgets + @magicclass(widget_type="tabbed", name="Functions", labels=False) + class WidgetContainer(MagicTemplate): + + def __post_init__(self): + tab_widget: QTabWidget= self._widget._tab_widget + # Manually set the tab labels, because by default magicgui uses the widget names, but setting + # the names to human readable text makes them difficult to access via self + for i, label in enumerate(["1. Deskew", "2. Deconvolution", "3. Crop", "4. Workflow", "5. Output"]): + tab_widget.setTabText(i, label) + for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: + field._validate() + + # Using vfields here seems to prevent https://github.com/hanjinliu/magic-class/issues/110 + deskew_fields = vfield(DeskewFields) + deconv_fields = vfield(DeconvolutionFields) + cropping_fields = vfield(CroppingFields) + workflow_fields = vfield(WorkflowFields) + output_fields = vfield(OutputFields) + + @set_options(header=dict(widget_type="Label", label="

      Preview Deskew

      "), + time=dict(label="Time:", max=2**15), + channel=dict(label="Channel:"), + call_button="Preview" + ) + @set_design(text="Preview") + def preview(self, header: str, time: int, channel: int): + from pathlib import Path + + # We only need to process one time point for the preview, + # so we made a copy using a subset of the times + lattice = self._make_model(validate=False).copy_validate(update=dict( + time_range = range(time, time+1), + channel_range = range(time, time+1), + # Patch in a placeholder for the save dir because previewing doesn't use it + # TODO: use a more elegant solution such as making the "saveable" lattice + # a child class which more validations + save_dir = Path.home() + )) + + for slice in lattice.process().slices: + scale = ( + lattice.new_dz, + lattice.dy, + lattice.dx + ) + self.parent_viewer.add_image(slice.data, scale=scale) + max_z = np.argmax(np.sum(slice.data, axis=(1, 2))) + self.parent_viewer.dims.set_current_step(0, max_z) + + @set_design(text="Save") + def save(self): + from napari.utils.notifications import show_info + lattice = self._make_model() + lattice.save() + show_info(f"Deskewing successfuly completed. Results are located in {lattice.save_dir}") + + @LlszMenu.WidgetContainer.deskew_fields.connect + def _on_image_changed(self, deskew: DeskewFields): + # An error at this point doesn't need to be communicated to the user + try: + img = deskew._get_kwargs()["data"] + # We have to manually trigger _on_image_changed because siblings + # classes can't listen to each other's events: https://github.com/hanjinliu/magic-class/issues/129 + for field in self._get_fields(): + field._on_image_changed(img) + except: + pass + + def _get_fields(self) -> Iterable[NapariFieldGroup]: + """Yields all the child Field classes which inherit from NapariFieldGroup""" + container = self.LlszMenu.WidgetContainer + yield from set(container.__magicclass_children__) diff --git a/plugin/tests/test_dock_widget.py b/plugin/tests/test_dock_widget.py index 4a6cfd5..ef846cf 100644 --- a/plugin/tests/test_dock_widget.py +++ b/plugin/tests/test_dock_widget.py @@ -1,87 +1,87 @@ -from __future__ import annotations - -from importlib_resources import as_file -from napari_lattice.dock_widget import LLSZWidget -from typing import Callable, TYPE_CHECKING -from magicclass.testing import check_function_gui_buildable, FunctionGuiTester -from magicclass import MagicTemplate -from magicclass.widgets import Widget -from magicclass._gui._gui_modes import ErrorMode -import pytest -from lls_core.sample import resources -from aicsimageio.aics_image import AICSImage -from napari_lattice.fields import PixelSizeSource -from tempfile import TemporaryDirectory - -if TYPE_CHECKING: - from napari import Viewer - -# Test if the widget can be created - -# make_napari_viewer is a pytest fixture that returns a napari viewer object -# Commenting this out as github CI is fixed -# @pytest.mark.skip(reason="GUI tests currently fail in github CI, unclear why") -# When testing locally, need pytest-qt - -@pytest.fixture(params=[ - "RBC_tiny.czi", - "LLS7_t1_ch1.czi", - "LLS7_t1_ch3.czi", - "LLS7_t2_ch1.czi", - "LLS7_t2_ch3.czi", -]) -def image_data(request: pytest.FixtureRequest): - """ - Fixture function that yields test images as file paths - """ - with as_file(resources / request.param) as image_path: - yield AICSImage(image_path) - -def set_debug(cls: MagicTemplate): - """ - Recursively disables GUI error handling, so that this works with pytest - """ - def _handler(e: Exception, parent: Widget): - raise e - ErrorMode.get_handler = lambda self: _handler - cls._error_mode = ErrorMode.stderr - for child in cls.__magicclass_children__: - set_debug(child) - -def test_dock_widget(make_napari_viewer: Callable[[], Viewer], image_data: AICSImage): - # make viewer and add an image layer using our fixture - viewer = make_napari_viewer() - - # Check if an image can be added as a layer - viewer.add_image(image_data.xarray_dask_data) - - # Test if napari-lattice widget can be created in napari - ui = LLSZWidget() - set_debug(ui) - viewer.window.add_dock_widget(ui) - - # Set the input parameters and execute the processing - with TemporaryDirectory() as tmpdir: - # Specify values for all the required GUI fields - fields = ui.LlszMenu.WidgetContainer.deskew_fields - # TODO: refactor this logic into a `lattice_params_from_aics` method - fields.img_layer.value = list(viewer.layers) - fields.dimension_order.value = image_data.dims.order - fields.pixel_sizes_source.value = PixelSizeSource.Manual - - # Test previewing - tester = FunctionGuiTester(ui.preview) - tester.call("", 0, 0) - - # Add the save path which shouldn't be needed for previewing - ui.LlszMenu.WidgetContainer.output_fields.save_path.value = tmpdir - - # Test saving - tester = FunctionGuiTester(ui.save) - tester.call() - - -def test_check_buildable(): - ui = LLSZWidget() - set_debug(ui) - check_function_gui_buildable(ui) +from __future__ import annotations + +from importlib_resources import as_file +from napari_lattice.dock_widget import LLSZWidget +from typing import Callable, TYPE_CHECKING +from magicclass.testing import check_function_gui_buildable, FunctionGuiTester +from magicclass import MagicTemplate +from magicclass.widgets import Widget +from magicclass._gui._gui_modes import ErrorMode +import pytest +from lls_core.sample import resources +from aicsimageio.aics_image import AICSImage +from napari_lattice.fields import PixelSizeSource +from tempfile import TemporaryDirectory + +if TYPE_CHECKING: + from napari import Viewer + +# Test if the widget can be created + +# make_napari_viewer is a pytest fixture that returns a napari viewer object +# Commenting this out as github CI is fixed +# @pytest.mark.skip(reason="GUI tests currently fail in github CI, unclear why") +# When testing locally, need pytest-qt + +@pytest.fixture(params=[ + "RBC_tiny.czi", + "LLS7_t1_ch1.czi", + "LLS7_t1_ch3.czi", + "LLS7_t2_ch1.czi", + "LLS7_t2_ch3.czi", +]) +def image_data(request: pytest.FixtureRequest): + """ + Fixture function that yields test images as file paths + """ + with as_file(resources / request.param) as image_path: + yield AICSImage(image_path) + +def set_debug(cls: MagicTemplate): + """ + Recursively disables GUI error handling, so that this works with pytest + """ + def _handler(e: Exception, parent: Widget): + raise e + ErrorMode.get_handler = lambda self: _handler + cls._error_mode = ErrorMode.stderr + for child in cls.__magicclass_children__: + set_debug(child) + +def test_dock_widget(make_napari_viewer: Callable[[], Viewer], image_data: AICSImage): + # make viewer and add an image layer using our fixture + viewer = make_napari_viewer() + + # Check if an image can be added as a layer + viewer.add_image(image_data.xarray_dask_data) + + # Test if napari-lattice widget can be created in napari + ui = LLSZWidget() + set_debug(ui) + viewer.window.add_dock_widget(ui) + + # Set the input parameters and execute the processing + with TemporaryDirectory() as tmpdir: + # Specify values for all the required GUI fields + fields = ui.LlszMenu.WidgetContainer.deskew_fields + # TODO: refactor this logic into a `lattice_params_from_aics` method + fields.img_layer.value = list(viewer.layers) + fields.dimension_order.value = image_data.dims.order + fields.pixel_sizes_source.value = PixelSizeSource.Manual + + # Test previewing + tester = FunctionGuiTester(ui.preview) + tester.call("", 0, 0) + + # Add the save path which shouldn't be needed for previewing + ui.LlszMenu.WidgetContainer.output_fields.save_path.value = tmpdir + + # Test saving + tester = FunctionGuiTester(ui.save) + tester.call() + + +def test_check_buildable(): + ui = LLSZWidget() + set_debug(ui) + check_function_gui_buildable(ui) From b770b32e0426ab4f3436f537c5ec158be4cda1cb Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 23 Nov 2023 11:43:10 +1100 Subject: [PATCH 104/147] Fix deskew vol usage --- core/lls_core/models/lattice_data.py | 2 +- core/tests/test_deskew.py | 56 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 8456250..42130fe 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -302,7 +302,7 @@ def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, chann @property def deskewed_volume(self) -> DaskArray: from dask.array import zeros - return zeros(self.deskew_vol_shape) + return zeros(self.derived.deskew_vol_shape) def _process_crop(self) -> Iterable[ImageSlice]: """ diff --git a/core/tests/test_deskew.py b/core/tests/test_deskew.py index aaa22a2..adbcd6c 100644 --- a/core/tests/test_deskew.py +++ b/core/tests/test_deskew.py @@ -1,28 +1,28 @@ -#filename and function name should start with "test_" when using pytest -import pyclesperanto_prototype as cle -import numpy as np -from lls_core.models.lattice_data import LatticeData -from xarray import DataArray -import tempfile - -def test_deskew(): - - raw = np.zeros((5,5,5)) - raw[2,0,0] = 10 - - deskewed = cle.deskew_y(raw,angle_in_degrees=60) - - #np.argwhere(deskewed>0) - assert deskewed.shape == (4,8,5) - assert deskewed[2,2,0] == 0.5662433505058289 - -def test_lattice_data_deskew(): - raw = DataArray(np.zeros((5, 5, 5)), dims=["X", "Y", "Z"]) - with tempfile.TemporaryDirectory() as tmpdir: - lattice = LatticeData( - input_image=raw, - physical_pixel_sizes = (1, 1, 1), - save_name="test", - save_dir=tmpdir - ) - assert lattice.deskew_vol_shape == [2, 9, 5] +#filename and function name should start with "test_" when using pytest +import pyclesperanto_prototype as cle +import numpy as np +from lls_core.models.lattice_data import LatticeData +from xarray import DataArray +import tempfile + +def test_deskew(): + + raw = np.zeros((5,5,5)) + raw[2,0,0] = 10 + + deskewed = cle.deskew_y(raw,angle_in_degrees=60) + + #np.argwhere(deskewed>0) + assert deskewed.shape == (4,8,5) + assert deskewed[2,2,0] == 0.5662433505058289 + +def test_lattice_data_deskew(): + raw = DataArray(np.zeros((5, 5, 5)), dims=["X", "Y", "Z"]) + with tempfile.TemporaryDirectory() as tmpdir: + lattice = LatticeData( + input_image=raw, + physical_pixel_sizes = (1, 1, 1), + save_name="test", + save_dir=tmpdir + ) + assert lattice.derived.deskew_vol_shape == (2, 9, 5) From ad575f5c420665b2c85406b3ad0a2ce192d93a50 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 6 Dec 2023 13:27:36 +1100 Subject: [PATCH 105/147] Minimal test images --- core/tests/conftest.py | 15 ++- core/tests/params.py | 1 - core/tests/test_crop_deskew.py | 70 +++++++------- core/tests/test_process.py | 16 +++- core/tests/test_workflows.py | 170 ++++++++++++++++----------------- 5 files changed, 147 insertions(+), 125 deletions(-) diff --git a/core/tests/conftest.py b/core/tests/conftest.py index 457aa9e..1d6814f 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -22,6 +22,19 @@ def rbc_tiny(): with as_file(resources / "RBC_tiny.czi") as image_path: yield image_path +@pytest.fixture(params=[ + "LLS7_t1_ch1.czi", + "LLS7_t1_ch3.czi", + "LLS7_t2_ch1.czi", + "LLS7_t2_ch3.czi", +]) +def minimal_image_path(request: pytest.FixtureRequest): + """ + Fixture function that yields a minimal set of test images as file paths + """ + with as_file(resources / request.param) as image_path: + yield image_path + @pytest.fixture(params=[ "RBC_tiny.czi", "RBC_lattice.tif", @@ -31,7 +44,7 @@ def rbc_tiny(): "LLS7_t2_ch3.czi", "multich_multi_time.tif" ]) -def image_path(request: pytest.FixtureRequest): +def minimal_image_path(request: pytest.FixtureRequest): """ Fixture function that yields test images as file paths """ diff --git a/core/tests/params.py b/core/tests/params.py index 4e927bf..2bb1f20 100644 --- a/core/tests/params.py +++ b/core/tests/params.py @@ -8,7 +8,6 @@ {"skew": "X"}, {"skew": "Y"}, {"angle": 30}, - {"angle": 90}, {"physical_pixel_sizes": (1, 1, 1)}, {"save_type": SaveFileType.h5}, {"save_type": SaveFileType.tiff}, diff --git a/core/tests/test_crop_deskew.py b/core/tests/test_crop_deskew.py index e873c3f..9b3a4b2 100644 --- a/core/tests/test_crop_deskew.py +++ b/core/tests/test_crop_deskew.py @@ -1,35 +1,35 @@ -import pyclesperanto_prototype as cle -import numpy as np - -from lls_core.llsz_core import crop_volume_deskew - - -def test_crop_deskew(): - raw = np.zeros((5,5,5)) - raw[2,4,2] = 10 - deskew_angle = 60 - - deskewed = cle.deskew_y(raw,angle_in_degrees=deskew_angle).astype(raw.dtype) - - #print(np.argwhere(deskewed>0)) - - #Crop deskewed volume - ref_crop_deskew_img = deskewed[0:4,3:5,0:5] - - #Similarly, generate an roi with coordinates (x1=0,y1=3,z1=0) to (x2=5,y2=5,z2=5) - #Use this for cropping deskewed volume to get matching area - roi = np.array(((3,0),(3,5),(5,5),(5,0))) - z1 = 0 - z2 = 5 - - cropped_deskew_img = crop_volume_deskew(original_volume = raw, - deskewed_volume = deskewed, - roi_shape = roi, - angle_in_degrees = deskew_angle, - z_start = z1, - z_end = z2, - linear_interpolation=True).astype(raw.dtype) - - assert cropped_deskew_img[0, 1, 2] == ref_crop_deskew_img[0,1,2] - assert cropped_deskew_img[0, 0, 2] == ref_crop_deskew_img[0,0,2] - assert ref_crop_deskew_img.shape == cropped_deskew_img.shape \ No newline at end of file +import pyclesperanto_prototype as cle +import numpy as np + +from lls_core.llsz_core import crop_volume_deskew + + +def test_crop_deskew(): + raw = np.zeros((5,5,5)) + raw[2,4,2] = 10 + deskew_angle = 60 + + deskewed = cle.deskew_y(raw,angle_in_degrees=deskew_angle).astype(raw.dtype) + + #print(np.argwhere(deskewed>0)) + + #Crop deskewed volume + ref_crop_deskew_img = deskewed[0:4,3:5,0:5] + + #Similarly, generate an roi with coordinates (x1=0,y1=3,z1=0) to (x2=5,y2=5,z2=5) + #Use this for cropping deskewed volume to get matching area + roi = np.array(((3,0),(3,5),(5,5),(5,0))) + z1 = 0 + z2 = 5 + + cropped_deskew_img = crop_volume_deskew(original_volume = raw, + deskewed_volume = deskewed, + roi_shape = roi, + angle_in_degrees = deskew_angle, + z_start = z1, + z_end = z2, + linear_interpolation=True).astype(raw.dtype) + + assert cropped_deskew_img[0, 1, 2] == ref_crop_deskew_img[0,1,2] + assert cropped_deskew_img[0, 0, 2] == ref_crop_deskew_img[0,0,2] + assert ref_crop_deskew_img.shape == cropped_deskew_img.shape diff --git a/core/tests/test_process.py b/core/tests/test_process.py index 86c802c..1a3fa99 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -15,8 +15,18 @@ def open_psf(name: str): with as_file(resources / "psfs" / "zeiss_simulated" / name) as path: return path + @parameterized -def test_process(image_path: str, args: dict): +def test_process(minimal_image_path: str, args: dict): + # Processes a minimal set of images, with multiple parameter combinations + for slice in LatticeData.parse_obj({ + "input_image": minimal_image_path, + **args + }).process().slices: + assert slice.data.ndim == 3 + +def test_process_all(image_path: str, args: dict): + # Processes all input images, but without parameter combinations for slice in LatticeData.parse_obj({ "input_image": image_path, **args @@ -24,10 +34,10 @@ def test_process(image_path: str, args: dict): assert slice.data.ndim == 3 @parameterized -def test_save(image_path: str, args: dict): +def test_save(minimal_image_path: str, args: dict): with tempfile.TemporaryDirectory() as tempdir: LatticeData.parse_obj({ - "input_image": image_path, + "input_image": minimal_image_path, "save_dir": tempdir, **args }).process().save_image() diff --git a/core/tests/test_workflows.py b/core/tests/test_workflows.py index df74fe8..e86ec28 100644 --- a/core/tests/test_workflows.py +++ b/core/tests/test_workflows.py @@ -1,85 +1,85 @@ -from typing import Callable -from copy import copy -from importlib_resources import as_file -from numpy.typing import NDArray - -from napari_workflows import Workflow -import tempfile - -from pandas import DataFrame -from lls_core.models.lattice_data import LatticeData -from lls_core.sample import resources -from aicsimageio.aics_image import AICSImage - -from tests.utils import invoke -from pathlib import Path -from .params import config_types -from .utils import invoke, valid_image_path - - -def test_napari_workflow(image_workflow: Workflow, test_image: NDArray): - """ - Test napari workflow to see if it works before we run it using napari_lattice - This is without deskewing - """ - workflow = copy(image_workflow) - # Set input image to be the "raw" image - workflow.set("input", test_image) - labeling = workflow.get("labeling") - assert labeling[2, 2, 2] == 1 - -@config_types -def test_workflow_cli(workflow_config_cli: dict, save_func: Callable, cli_param: str): - """ - Test workflow processing via CLI - This will apply deskewing before processing the workflow - """ - with tempfile.NamedTemporaryFile(mode="w") as fp: - save_func(workflow_config_cli, fp) - fp.flush() - - # Deskew, apply workflow and save as h5 - invoke([ - cli_param, fp.name - ]) - - # checks if h5 file written - save_dir = Path(workflow_config_cli["save_dir"]) - saved_files = list(save_dir.glob("*.h5")) - assert len(saved_files) > 0 - assert len(list(save_dir.glob("*.xml"))) > 0 - - import npy2bdv - for h5_img in saved_files: - h5_file = npy2bdv.npy2bdv.BdvEditor(str(h5_img)) - label_img = h5_file.read_view(time=0, channel=0) - assert label_img.shape == (3, 14, 5) - assert label_img[1, 6, 2] == 1 - -def test_image_workflow(image_path: Path, image_workflow: Workflow): - # Test that a regular workflow that returns an image directly works - with tempfile.TemporaryDirectory() as tmpdir: - for roi, output in LatticeData( - input_image = image_path, - workflow = image_workflow, - save_dir = tmpdir - ).process_workflow().process(): - assert isinstance(output, Path) - assert valid_image_path(output) - -def test_table_workflow(image_path: Path, table_workflow: Workflow): - # Test a complex workflow that returns a tuple of images and data - with tempfile.TemporaryDirectory() as tmpdir: - params = LatticeData( - input_image = image_path, - workflow = table_workflow, - save_dir = tmpdir - ) - for roi, output in params.process_workflow().process(): - assert isinstance(output, (DataFrame, Path)) - if isinstance(output, DataFrame): - nrow, ncol = output.shape - assert nrow == params.nslices - assert ncol > 0 - else: - assert valid_image_path(output) +from typing import Callable +from copy import copy +from importlib_resources import as_file +from numpy.typing import NDArray + +from napari_workflows import Workflow +import tempfile + +from pandas import DataFrame +from lls_core.models.lattice_data import LatticeData +from lls_core.sample import resources +from aicsimageio.aics_image import AICSImage + +from tests.utils import invoke +from pathlib import Path +from .params import config_types +from .utils import invoke, valid_image_path + + +def test_napari_workflow(image_workflow: Workflow, test_image: NDArray): + """ + Test napari workflow to see if it works before we run it using napari_lattice + This is without deskewing + """ + workflow = copy(image_workflow) + # Set input image to be the "raw" image + workflow.set("input", test_image) + labeling = workflow.get("labeling") + assert labeling[2, 2, 2] == 1 + +@config_types +def test_workflow_cli(workflow_config_cli: dict, save_func: Callable, cli_param: str): + """ + Test workflow processing via CLI + This will apply deskewing before processing the workflow + """ + with tempfile.NamedTemporaryFile(mode="w") as fp: + save_func(workflow_config_cli, fp) + fp.flush() + + # Deskew, apply workflow and save as h5 + invoke([ + cli_param, fp.name + ]) + + # checks if h5 file written + save_dir = Path(workflow_config_cli["save_dir"]) + saved_files = list(save_dir.glob("*.h5")) + assert len(saved_files) > 0 + assert len(list(save_dir.glob("*.xml"))) > 0 + + import npy2bdv + for h5_img in saved_files: + h5_file = npy2bdv.npy2bdv.BdvEditor(str(h5_img)) + label_img = h5_file.read_view(time=0, channel=0) + assert label_img.shape == (3, 14, 5) + assert label_img[1, 6, 2] == 1 + +def test_image_workflow(minimal_image_path: Path, image_workflow: Workflow): + # Test that a regular workflow that returns an image directly works + with tempfile.TemporaryDirectory() as tmpdir: + for roi, output in LatticeData( + input_image = minimal_image_path, + workflow = image_workflow, + save_dir = tmpdir + ).process_workflow().process(): + assert isinstance(output, Path) + assert valid_image_path(output) + +def test_table_workflow(minimal_image_path: Path, table_workflow: Workflow): + # Test a complex workflow that returns a tuple of images and data + with tempfile.TemporaryDirectory() as tmpdir: + params = LatticeData( + input_image = minimal_image_path, + workflow = table_workflow, + save_dir = tmpdir + ) + for roi, output in params.process_workflow().process(): + assert isinstance(output, (DataFrame, Path)) + if isinstance(output, DataFrame): + nrow, ncol = output.shape + assert nrow == params.nslices + assert ncol > 0 + else: + assert valid_image_path(output) From b2842f532d9e1552876df6dd3b9baa42a903b703 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 6 Dec 2023 13:41:29 +1100 Subject: [PATCH 106/147] Add progress bars more consistently --- core/lls_core/models/lattice_data.py | 8 ++++---- core/tests/conftest.py | 2 +- core/tests/test_process.py | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 42130fe..769ce90 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -312,7 +312,7 @@ def _process_crop(self) -> Iterable[ImageSlice]: raise Exception("This function can only be called when crop is set") # We have an extra level of iteration for the crop path: iterating over each ROI - for roi_index, roi in enumerate(tqdm(self.crop.selected_rois, desc="ROI:", position=0)): + for roi_index, roi in enumerate(tqdm(self.crop.selected_rois, desc="ROI", position=0)): # pass arguments for save tiff, callable and function arguments logger.info(f"Processing ROI {roi_index}") @@ -324,7 +324,7 @@ def _process_crop(self) -> Iterable[ImageSlice]: decon_processing=self.deconvolution.decon_processing ) - for slice in self.iter_slices(): + for slice in tqdm(self.iter_slices(), desc="2D Slice Number", position=1): yield slice.copy_with_data( crop_volume_deskew( original_volume=slice.data, @@ -348,7 +348,7 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: """ Yields processed image slices without cropping """ - for slice in self.iter_slices(): + for slice in tqdm(self.iter_slices(), desc="2d Slice Number"): data: ArrayLike = slice.data if isinstance(slice.data, DaskArray): data = slice.data.compute() @@ -389,7 +389,7 @@ def process_workflow(self) -> WorkflowSlices: from lls_core.models.results import WorkflowSlices WorkflowSlices.update_forward_refs(LatticeData=LatticeData) outputs = [] - for workflow in self.generate_workflows(): + for workflow in tqdm(self.generate_workflows(), desc="2D Slice Number"): for leaf in workflow.data.leafs(): outputs.append( workflow.copy_with_data( diff --git a/core/tests/conftest.py b/core/tests/conftest.py index 1d6814f..e3fcfa0 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -44,7 +44,7 @@ def minimal_image_path(request: pytest.FixtureRequest): "LLS7_t2_ch3.czi", "multich_multi_time.tif" ]) -def minimal_image_path(request: pytest.FixtureRequest): +def image_path(request: pytest.FixtureRequest): """ Fixture function that yields test images as file paths """ diff --git a/core/tests/test_process.py b/core/tests/test_process.py index 1a3fa99..6fe2417 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -25,11 +25,10 @@ def test_process(minimal_image_path: str, args: dict): }).process().slices: assert slice.data.ndim == 3 -def test_process_all(image_path: str, args: dict): +def test_process_all(image_path: str): # Processes all input images, but without parameter combinations for slice in LatticeData.parse_obj({ - "input_image": image_path, - **args + "input_image": image_path }).process().slices: assert slice.data.ndim == 3 From eaa6a7be8b36626c701607839d545e62acf8fa78 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 7 Dec 2023 20:41:59 +1100 Subject: [PATCH 107/147] Add tool tip for PSFs --- plugin/napari_lattice/fields.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 09283db..a84d515 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -348,7 +348,9 @@ class DeconvolutionFields(NapariFieldGroup): # A counterpart to the DeconvolutionParams Pydantic class fields_enabled = field(False, label="Enabled") decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") - psf = field(List[Path], label = "PSFs") + psf = field(List[Path], label = "PSFs").with_options( + tooltip="PSFs must be in the same order as the image channels" + ) psf_num_iter = field(int, label = "Number of Iterations") background = field(ComboBox).with_choices( [it.value for it in BackgroundSource] From 42a5537c96f62d9583c58fab928b71783eb393a4 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 8 Dec 2023 18:48:49 +1100 Subject: [PATCH 108/147] Add working parent_connect implementation --- plugin/napari_lattice/dock_widget.py | 7 +++++ plugin/napari_lattice/fields.py | 9 ++++-- plugin/napari_lattice/parent_connect.py | 39 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 plugin/napari_lattice/parent_connect.py diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index d712b56..da1111d 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -16,6 +16,7 @@ ) from qtpy.QtCore import Qt from qtpy.QtWidgets import QTabWidget +from napari_lattice.parent_connect import ParentConnect if TYPE_CHECKING: from typing import Iterable @@ -102,6 +103,12 @@ def __post_init__(self): for i, label in enumerate(["1. Deskew", "2. Deconvolution", "3. Crop", "4. Workflow", "5. Output"]): tab_widget.setTabText(i, label) for field in [self.deskew_fields, self.deconv_fields, self.cropping_fields, self.workflow_fields, self.output_fields]: + # Connect event handlers + for subfield_name in dir(field): + subfield = getattr(field, subfield_name) + if isinstance(subfield, ParentConnect): + subfield.resolve(self, field, subfield_name) + # Trigger validation of default data field._validate() # Using vfields here seems to prevent https://github.com/hanjinliu/magic-class/issues/110 diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index a84d515..bd04225 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -32,6 +32,7 @@ from napari_workflows import Workflow, WorkflowManager from qtpy.QtWidgets import QTabWidget from strenum import StrEnum +from napari_lattice.parent_connect import connect_parent if TYPE_CHECKING: from magicgui.widgets.bases import RangedWidget @@ -348,8 +349,9 @@ class DeconvolutionFields(NapariFieldGroup): # A counterpart to the DeconvolutionParams Pydantic class fields_enabled = field(False, label="Enabled") decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") - psf = field(List[Path], label = "PSFs").with_options( - tooltip="PSFs must be in the same order as the image channels" + psf = field(List[Path], label = "PSFs", widget_type="ListEdit").with_options( + tooltip="PSFs must be in the same order as the image channels", + layout="vertical" ) psf_num_iter = field(int, label = "Number of Iterations") background = field(ComboBox).with_choices( @@ -539,7 +541,8 @@ def _make_model(self, validate: bool = True) -> OutputParams: save_name="PLACEHOLDER" ) - def _on_image_changed(self, img: DataArray): + @connect_parent("deskew_fields.img_layer") + def on_image_changed(self, img: DataArray): # Update the maximum T and C for widget in self.time_range: adjust_maximum(widget, img.sizes["T"]) diff --git a/plugin/napari_lattice/parent_connect.py b/plugin/napari_lattice/parent_connect.py new file mode 100644 index 0000000..ad55a36 --- /dev/null +++ b/plugin/napari_lattice/parent_connect.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Callable, TYPE_CHECKING +from operator import attrgetter + +if TYPE_CHECKING: + from magicclass import MagicTemplate + from magicgui.widgets.bases import ValueWidget + + +@dataclass +class ParentConnect: + """ + A function that wants to be connected to a parent or sibling's field. + This will be resolved after the GUI is instantiated. + """ + path: str + func: Callable + + def resolve(self, root: MagicTemplate, event_owner: MagicTemplate, field_name: str) -> None:# -> Callable[..., Any]: + """ + Converts this object into a true function that is connected to the appropriate change event + """ + field: ValueWidget = attrgetter(self.path)(root) + # field_owner = field.parent + bound_func = field.changed.connect(lambda: self.func(event_owner, field.value)) + setattr(event_owner, field_name, bound_func) + +def connect_parent(path: str) -> Callable[..., ParentConnect]: + """ + Mark this function as wanting to connect to a parent or sibling event + """ + def decorator(fn: Callable) -> ParentConnect: + return ParentConnect( + path=path, + func=fn + ) + return decorator From 9d4debb866a85f4542d8897c3a8cfa6faa307561 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 11 Dec 2023 15:48:26 +1100 Subject: [PATCH 109/147] Use TupleEdit for PSFs --- plugin/napari_lattice/dock_widget.py | 20 +++------------ plugin/napari_lattice/fields.py | 37 +++++++++++++++++----------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index da1111d..c434cd7 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -74,10 +74,10 @@ class LlszMenu(MagicTemplate): main_heading = field("

      Napari Lattice: Visualization & Analysis

      ", widget_type="Label") heading1 = field(dedent("""
      - Specify deskewing parameters and image layers in Tab 1. - Additional analysis parameters can be configured in the other tabs. - When you are ready to save, go to Tab 5. - Output to specify the output directory. + Specify deskewing parameters and image layers in Tab 1. + Additional analysis parameters can be configured in the other tabs. + When you are ready to save, go to Tab 5. + Output to specify the output directory. For more information, please refer to the documentation here.
      """.strip()), widget_type="Label") @@ -155,18 +155,6 @@ def save(self): lattice.save() show_info(f"Deskewing successfuly completed. Results are located in {lattice.save_dir}") - @LlszMenu.WidgetContainer.deskew_fields.connect - def _on_image_changed(self, deskew: DeskewFields): - # An error at this point doesn't need to be communicated to the user - try: - img = deskew._get_kwargs()["data"] - # We have to manually trigger _on_image_changed because siblings - # classes can't listen to each other's events: https://github.com/hanjinliu/magic-class/issues/129 - for field in self._get_fields(): - field._on_image_changed(img) - except: - pass - def _get_fields(self) -> Iterable[NapariFieldGroup]: """Yields all the child Field classes which inherit from NapariFieldGroup""" container = self.LlszMenu.WidgetContainer diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index bd04225..171dc09 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -164,6 +164,12 @@ def __post_init__(self): from qtpy.QtCore import Qt self._widget._layout.setAlignment(Qt.AlignmentFlag.AlignTop) + def _get_deskew(self) -> DeskewParams: + "Returns the DeskewParams from the other tab" + from napari_lattice.dock_widget import LLSZWidget + parent = self.find_ancestor(LLSZWidget) + return parent.LlszMenu.WidgetContainer.deskew_fields._make_model() + def _get_parent_tab_widget(self) -> QTabWidget: qwidget = self.native # Walk up the widget tree until we find the tab widget @@ -206,9 +212,6 @@ def _validate(self): def _make_model(self): raise NotImplementedError() - def _on_image_changed(self, img: DataArray): - pass - class DeskewKwargs(NapariImageParams): angle: float skew: DeskewDirection @@ -349,7 +352,7 @@ class DeconvolutionFields(NapariFieldGroup): # A counterpart to the DeconvolutionParams Pydantic class fields_enabled = field(False, label="Enabled") decon_processing = field(DeconvolutionChoice, label="Processing Algorithm") - psf = field(List[Path], label = "PSFs", widget_type="ListEdit").with_options( + psf = field(Tuple[Path, Path, Path, Path], label = "PSFs").with_options( tooltip="PSFs must be in the same order as the image channels", layout="vertical" ) @@ -394,7 +397,8 @@ def _make_model(self) -> Optional[DeconvolutionParams]: return DeconvolutionParams( decon_processing=self.decon_processing.value, background=background, - psf=self.psf.value, + # Filter out unset PSFs + psf=[psf for psf in self.psf.value if psf.is_file()], psf_num_iter=self.psf_num_iter.value ) @@ -416,12 +420,6 @@ class CroppingFields(NapariFieldGroup): ) errors = field(Label).with_options(label="Errors") - def _get_deskew(self) -> DeskewParams: - "Returns the DeskewParams from the other tab" - from napari_lattice.dock_widget import LLSZWidget - parent = self.find_ancestor(LLSZWidget) - return parent.LlszMenu.WidgetContainer.deskew_fields._make_model() - @set_design(text="Import ROI") def import_roi(self, path: Path): from lls_core.cropping import read_imagej_roi @@ -440,8 +438,14 @@ def new_crop_layer(self): shapes.mode = "ADD_RECTANGLE" shapes.name = "Napari Lattice Crop" - def _on_image_changed(self, img: DataArray): - deskew = self._get_deskew() + @connect_parent("deskew_fields.img_layer") + def _on_image_changed(self, field: MagicField): + try: + deskew = self._get_deskew() + except: + # Ignore if the deskew parameters are invalid + return + deskewed_zmax = deskew.derived.deskew_vol_shape[0] # Update the allowed Z based the deskewed shape @@ -542,7 +546,12 @@ def _make_model(self, validate: bool = True) -> OutputParams: ) @connect_parent("deskew_fields.img_layer") - def on_image_changed(self, img: DataArray): + def _on_image_changed(self, field: MagicField): + try: + img = self._get_deskew().input_image + except: + return + # Update the maximum T and C for widget in self.time_range: adjust_maximum(widget, img.sizes["T"]) From ecf62d553d6dcf5473b85df5202186a23f7d264d Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 13 Dec 2023 15:05:38 +1100 Subject: [PATCH 110/147] Fix negative crop excess --- core/lls_core/llsz_core.py | 13 +++++++------ core/lls_core/utils.py | 2 +- plugin/napari_lattice/fields.py | 5 +++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/core/lls_core/llsz_core.py b/core/lls_core/llsz_core.py index d1be298..cb41fab 100644 --- a/core/lls_core/llsz_core.py +++ b/core/lls_core/llsz_core.py @@ -272,9 +272,9 @@ def crop_volume_deskew( crop_height = crop_vol_shape[1] # Find "excess" volume on both sides due to deskewing - crop_excess = ( - int(round((deskewed_height - crop_height) / 2)) - + out_bounds_correction + crop_excess: int = max( + int(round((deskewed_height - crop_height) / 2)) + out_bounds_correction, + 0 ) # Crop in Y deskewed_prelim = np.asarray(deskewed_prelim) @@ -285,10 +285,11 @@ def crop_volume_deskew( elif skew_dir == DeskewDirection.X: deskewed_width = deskewed_prelim.shape[2] crop_width = crop_vol_shape[2] + # Find "excess" volume on both sides due to deskewing - crop_excess = ( - int(round((deskewed_width - crop_width) / 2)) - + out_bounds_correction + crop_excess = max( + int(round((deskewed_width - crop_width) / 2)) + out_bounds_correction, + 0 ) # Crop in X deskewed_prelim = np.asarray(deskewed_prelim) diff --git a/core/lls_core/utils.py b/core/lls_core/utils.py index 0def3c6..2ccbf50 100644 --- a/core/lls_core/utils.py +++ b/core/lls_core/utils.py @@ -39,7 +39,7 @@ def check_subclass(obj: Any, pkg_name: str, cls_name: str) -> bool: def is_napari_shape(obj: Any) -> TypeGuard[Shapes]: return check_subclass(obj, "napari.shapes", "Shapes") -def calculate_crop_bbox(shape: int, z_start: int, z_end: int) -> tuple[List[List[Any]], List[int]]: +def calculate_crop_bbox(shape: list, z_start: int, z_end: int) -> tuple[List[List[Any]], List[int]]: """Get bounding box as vertices in 3D in the form xyz Args: diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 171dc09..8fd7c3f 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -463,11 +463,12 @@ def _enable_crop(self, enabled: bool) -> bool: def _make_model(self) -> Optional[CropParams]: import numpy as np if self.fields_enabled.value: + deskew = self._get_deskew() return CropParams( # Convert from the input image space to the deskewed image space # We assume here that dx == dy which isn't ideal - roi_list=ShapesData([np.array(shape.data) / self._get_deskew().dy for shape in self.shapes.value]), - z_range=tuple(np.array(self.z_range.value) / self._get_deskew().new_dz), + roi_list=ShapesData([np.array(shape.data) / deskew.dy for shape in self.shapes.value]), + z_range=tuple(self.z_range.value), ) return None From 611949ec9f140e7f9473f021a8091fa214d3cc6b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 13 Dec 2023 17:03:03 +1100 Subject: [PATCH 111/147] GUI cropping improvements; remove PLACEHOLDER --- core/lls_core/models/crop.py | 2 +- core/lls_core/models/output.py | 3 ++- plugin/napari_lattice/dock_widget.py | 2 +- plugin/napari_lattice/fields.py | 8 +++----- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index 7871452..48863a7 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -58,6 +58,6 @@ def read_roi(cls, v: Any) -> List[Roi]: @validator("roi_subset", pre=True, always=True) def default_roi_range(cls, v: Any, values: dict): # If the roi range isn't provided, assume all rois should be processed - if v is None: + if v is None and "roi_list" in values: return list(range(len(values["roi_list"]))) return v diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index b6dbc02..15f332c 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -23,7 +23,8 @@ class OutputParams(FieldAccessModel): description="The filename suffix that will be used for output files. This will be added as a suffix to the input file name if the input image was specified using a file name. If the input image was provided as an in-memory object, the `save_name` field should instead be specified." ) save_name: str = Field( - description="The filename that will be used for output files. This should not contain a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc." + description="The filename that will be used for output files. This should not contain a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc.", + default=None ) save_type: SaveFileType = Field( default=SaveFileType.h5, diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index c434cd7..73ca7d5 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -144,7 +144,7 @@ def preview(self, header: str, time: int, channel: int): lattice.dy, lattice.dx ) - self.parent_viewer.add_image(slice.data, scale=scale) + self.parent_viewer.add_image(slice.data, scale=scale, name="Napari Lattice Preview") max_z = np.argmax(np.sum(slice.data, axis=(1, 2))) self.parent_viewer.dims.set_current_step(0, max_z) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 8fd7c3f..e57c63a 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -434,9 +434,9 @@ def import_roi(self, path: Path): @set_design(text="New Crop") def new_crop_layer(self): from napari_lattice.utils import get_viewer - shapes = get_viewer().add_shapes() + shapes = get_viewer().add_shapes(name="Napari Lattice Crop") shapes.mode = "ADD_RECTANGLE" - shapes.name = "Napari Lattice Crop" + self.shapes.value += [shapes] @connect_parent("deskew_fields.img_layer") def _on_image_changed(self, field: MagicField): @@ -467,7 +467,7 @@ def _make_model(self) -> Optional[CropParams]: return CropParams( # Convert from the input image space to the deskewed image space # We assume here that dx == dy which isn't ideal - roi_list=ShapesData([np.array(shape.data) / deskew.dy for shape in self.shapes.value]), + roi_list=ShapesData([np.array(shape.data) / deskew.dy for shape in self.shapes.value if len(shape.data) > 0]), z_range=tuple(self.z_range.value), ) return None @@ -542,8 +542,6 @@ def _make_model(self, validate: bool = True) -> OutputParams: save_dir=self.save_path.value, save_suffix=self.save_suffix.value, save_type=self.save_type.value, - # This is just to avoid the validation error caused by the missing field - save_name="PLACEHOLDER" ) @connect_parent("deskew_fields.img_layer") From 979b92b6e534bbc9dd382de822b8a6af0be49b69 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 14 Dec 2023 11:14:40 +1100 Subject: [PATCH 112/147] Fix for array parameters such as time_range in the CLI --- core/lls_core/cmds/__main__.py | 30 ++++- core/lls_core/models/lattice_data.py | 20 +-- core/tests/test_cli.py | 194 +++++++++++++-------------- 3 files changed, 134 insertions(+), 110 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 2eaabf7..5bf896d 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -7,7 +7,7 @@ from enum import auto from pathlib import Path -from typing import TYPE_CHECKING, List, Optional, Tuple +from typing import TYPE_CHECKING, List, Optional, Tuple, Union from strenum import StrEnum from lls_core.models.lattice_data import LatticeData @@ -100,6 +100,30 @@ def rich_validation(e: ValidationError) -> Table: return table +def update_nested_data(data: Union[dict, list], keys: list, new_value: Any): + from itertools import pairwise + current = data + + for key, next_key in pairwise(keys): + next = {} if isinstance(next_key, str) else [] + if isinstance(current, dict): + current = current.setdefault(key, next) + elif isinstance(current, list): + if key >= len(current): + current.insert(key, next) + current = current[key] + else: + raise ValueError(f"Unknown data type {type(current)}. Cannot traverse.") + + last_key = keys[-1] + if isinstance(current, dict): + current[last_key] = new_value + elif isinstance(current, list): + current.insert(last_key, new_value) + else: + raise ValueError(f"Unknown data type {type(current)}. Cannot traverse.") + +# Example usage: @app.command() def process( ctx: Context, @@ -145,12 +169,12 @@ def process( print(ctx.get_help()) raise Exit() - from toolz.dicttoolz import merge_with, update_in + from toolz.dicttoolz import merge_with cli_args = {} for source, dest in CLI_PARAM_MAP.items(): from click.core import ParameterSource if ctx.get_parameter_source(source) != ParameterSource.DEFAULT: - cli_args = update_in(cli_args, dest, lambda x: ctx.params[source]) + update_nested_data(cli_args, dest, ctx.params[source]) json_args = {} if json_config is not None: diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 769ce90..374ae66 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -5,10 +5,6 @@ from dask.array.core import Array as DaskArray from typing import Any, Iterable, Optional, TYPE_CHECKING, Type - -import pyclesperanto_prototype as cle -from tqdm import tqdm - from lls_core import DeconvolutionChoice from lls_core.deconvolution import pycuda_decon, skimage_decon from lls_core.llsz_core import crop_volume_deskew @@ -20,13 +16,11 @@ from lls_core.types import ArrayLike from lls_core.models.deskew import DeskewParams from napari_workflows import Workflow -from xarray import DataArray -from pathlib import Path if TYPE_CHECKING: - import pyclesperanto_prototype as cle from lls_core.models.results import ImageSlice, ImageSlices, ProcessedSlice from lls_core.writers import Writer + from xarray import DataArray import logging @@ -61,6 +55,7 @@ def use_image_path(cls, values: dict): # TODO: separate the image file from the image file path as two separate fields, # so we don't have to put so much logic here from lls_core.types import is_pathlike + from pathlib import Path input_image = values.get("input_image") if is_pathlike(input_image): if values.get("save_name") is None: @@ -120,12 +115,13 @@ def parse_time_range(cls, v: Any, values: dict) -> Any: """ # This skips the conversion if no image was provided, to ensure a more # user-friendly error is provided, namely "image was missing" + from collections.abc import Sequence with ignore_keyerror(): default_start = 0 default_end = values["input_image"].sizes["T"] if v is None: return range(default_start, default_end) - elif isinstance(v, tuple) and len(v) == 2: + elif isinstance(v, Sequence) and len(v) == 2: # Allow 2-tuples to be used as input for this field return range(v[0] or default_start, v[1] or default_end) return v @@ -135,12 +131,13 @@ def parse_channel_range(cls, v: Any, values: dict) -> Any: """ Sets the default channel range if undefined """ + from collections.abc import Sequence with ignore_keyerror(): default_start = 0 default_end = values["input_image"].sizes["C"] if v is None: return range(default_start, default_end) - elif isinstance(v, tuple) and len(v) == 2: + elif isinstance(v, Sequence) and len(v) == 2: # Allow 2-tuples to be used as input for this field return range(v[0] or default_start, v[1] or default_end) return v @@ -308,6 +305,7 @@ def _process_crop(self) -> Iterable[ImageSlice]: """ Yields processed image slices with cropping enabled """ + from tqdm import tqdm if self.crop is None: raise Exception("This function can only be called when crop is set") @@ -348,6 +346,9 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: """ Yields processed image slices without cropping """ + from tqdm import tqdm + import pyclesperanto_prototype as cle + for slice in tqdm(self.iter_slices(), desc="2d Slice Number"): data: ArrayLike = slice.data if isinstance(slice.data, DaskArray): @@ -387,6 +388,7 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: def process_workflow(self) -> WorkflowSlices: from lls_core.models.results import WorkflowSlices + from tqdm import tqdm WorkflowSlices.update_forward_refs(LatticeData=LatticeData) outputs = [] for workflow in tqdm(self.generate_workflows(), desc="2D Slice Number"): diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py index 5f3f144..7f08a4c 100644 --- a/core/tests/test_cli.py +++ b/core/tests/test_cli.py @@ -1,98 +1,96 @@ -# Tests for napari_lattice using arguments and saving output files as h5, as well as tiff - -from aicsimageio.aics_image import AICSImage -from npy2bdv import BdvEditor -import numpy as np -from pathlib import Path -import tempfile -from tests.utils import invoke -import yaml - -def create_image(path: Path): - # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) - raw = np.zeros((5, 5, 5)) - raw[2, 4, 2] = 10 - # Save image as a tif filw in home directory - AICSImage(raw).save(path) - assert path.exists() - - -def create_data(dir: Path) -> Path: - # Creates and returns a YAML config file - input_file = dir / 'raw.tiff' - config_location = dir / "config_deskew.yaml" - - # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) - raw = np.zeros((5, 5, 5)) - raw[2, 4, 2] = 10 - # Save image as a tif filw in home directory - AICSImage(raw).save(input_file) - assert input_file.exists() - - config: dict[str, str] = { - "input_image": str(input_file), - "save_dir": str(dir), - "save_type": "h5" - } - - with config_location.open("w") as fp: - yaml.safe_dump(config, fp) - - return config_location - -def assert_tiff(output_dir: Path): - """Checks that a valid TIFF was generated in the directory""" - results = list(output_dir.glob("*.tif")) - assert len(results) > 0 - for result in results: - AICSImage(result).get_image_data() - -def assert_h5(output_dir: Path): - """Checks that a valid H5 was generated""" - h5s = list(output_dir.glob("*.h5")) - assert len(h5s) > 0 - assert len(list(output_dir.glob("*.xml"))) == len(h5s) - for h5 in h5s: - BdvEditor(str(h5)).read_view() - -def test_batch_deskew_h5(): - """Write image to disk and then execute napari_lattice from terminal - Checks if an deskewed output file is created for both tif and h5 - """ - with tempfile.TemporaryDirectory() as out_dir: - out_dir = Path(out_dir) - input_file = out_dir / 'raw.tiff' - create_image(input_file) - # Batch deskew and save as h5 - invoke([ - str(input_file), - "--save-dir", str(out_dir), - "--save-type", "h5" - ]) - - assert_h5(out_dir) - -def test_batch_deskew_tiff(): - # tiff file deskew - with tempfile.TemporaryDirectory() as out_dir: - out_dir = Path(out_dir) - input_file = out_dir / 'raw.tiff' - create_image(input_file) - invoke([ - str(input_file), - "--save-dir", str(out_dir), - "--save-type", "tiff" - ]) - - assert_tiff(out_dir) - -def test_yaml_deskew(): - """Write image to disk and then execute napari_lattice from terminal - Checks if an deskewed output file is created for both tif and h5 - """ - with tempfile.TemporaryDirectory() as test_dir: - test_dir = Path(test_dir) - config_location = create_data(test_dir) - # Batch deskew and save as h5 - invoke(["--yaml-config", str(config_location)], ) - assert_h5(test_dir) +# Tests for napari_lattice using arguments and saving output files as h5, as well as tiff + +from typing import Callable, List +import pytest +from aicsimageio.aics_image import AICSImage +from npy2bdv import BdvEditor +import numpy as np +from pathlib import Path +import tempfile +from tests.utils import invoke +import yaml + +def create_image(path: Path): + # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) + raw = np.zeros((5, 5, 5)) + raw[2, 4, 2] = 10 + # Save image as a tif filw in home directory + AICSImage(raw).save(path) + assert path.exists() + + +def create_data(dir: Path) -> Path: + # Creates and returns a YAML config file + input_file = dir / 'raw.tiff' + config_location = dir / "config_deskew.yaml" + + # Create a zero array of shape 5x5x5 with a value of 10 at (2,4,2) + raw = np.zeros((5, 5, 5)) + raw[2, 4, 2] = 10 + # Save image as a tif filw in home directory + AICSImage(raw).save(input_file) + assert input_file.exists() + + config: dict[str, str] = { + "input_image": str(input_file), + "save_dir": str(dir), + "save_type": "h5" + } + + with config_location.open("w") as fp: + yaml.safe_dump(config, fp) + + return config_location + +def assert_tiff(output_dir: Path): + """Checks that a valid TIFF was generated in the directory""" + results = list(output_dir.glob("*.tif")) + assert len(results) > 0 + for result in results: + AICSImage(result).get_image_data() + +def assert_h5(output_dir: Path): + """Checks that a valid H5 was generated""" + h5s = list(output_dir.glob("*.h5")) + assert len(h5s) > 0 + assert len(list(output_dir.glob("*.xml"))) == len(h5s) + for h5 in h5s: + BdvEditor(str(h5)).read_view() + +@pytest.mark.parametrize( + ["flags", "check_fn"], + [ + [["--save-type", "h5"], assert_h5], + [["--save-type", "tiff"], assert_tiff], + [["--save-type", "tiff", "--time-start", "0", "--time-end", "1"], assert_tiff], + ] +) +def test_batch_deskew(flags: List[str], check_fn: Callable[[Path], None]): + """ + Write image to disk and then execute napari_lattice from terminal + Checks if an deskewed output file is created for both tif and h5 + """ + with tempfile.TemporaryDirectory() as out_dir: + out_dir = Path(out_dir) + input_file = out_dir / 'raw.tiff' + create_image(input_file) + # Batch deskew and save as h5 + invoke([ + str(input_file), + "--save-dir", str(out_dir), + *flags + ]) + + check_fn(out_dir) + +def test_yaml_deskew(): + """ + Write image to disk and then execute napari_lattice from terminal + Checks if an deskewed output file is created for both tif and h5 + """ + with tempfile.TemporaryDirectory() as test_dir: + test_dir = Path(test_dir) + config_location = create_data(test_dir) + # Batch deskew and save as h5 + invoke(["--yaml-config", str(config_location)], ) + assert_h5(test_dir) From 6b0e4771200e4b2b1b0b65368b614f859cd305f5 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Mon, 18 Dec 2023 15:33:43 +1100 Subject: [PATCH 113/147] Don't compute the dask array when calculating deskewed shape --- core/lls_core/models/deskew.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index bac6e2d..9a0b86c 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -188,7 +188,7 @@ def calculate_derived(cls, v: Any, values: dict) -> DerivedDeskewFields: return v elif v is None: deskew_vol_shape, deskew_affine_transform = get_deskewed_shape( - data.isel(C=0, T=0).to_numpy(), + data.isel(C=0, T=0), values["angle"], values["physical_pixel_sizes"].X, values["physical_pixel_sizes"].Y, From b6455973b36cc0a3a6657736a317d92b9875a661 Mon Sep 17 00:00:00 2001 From: Pradeep R Date: Tue, 19 Dec 2023 10:42:41 +1100 Subject: [PATCH 114/147] fix voxel_size_z for cropping --- core/lls_core/models/lattice_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 374ae66..fd4491c 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -333,7 +333,7 @@ def _process_crop(self) -> Iterable[ImageSlice]: linear_interpolation=True, voxel_size_x=self.dx, voxel_size_y=self.dy, - voxel_size_z=self.dy, + voxel_size_z=self.dz, angle_in_degrees=self.angle, deskewed_volume=self.deskewed_volume, z_start=self.crop.z_range[0], From 0d7c7d3aa1714afd7d3de88ca9d0b76a0353db36 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 19 Dec 2023 11:35:00 +1100 Subject: [PATCH 115/147] Add the missing suffix parameter into the GUI save function --- plugin/napari_lattice/dock_widget.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 73ca7d5..0b7f8b4 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -49,22 +49,27 @@ def _make_model(self, validate: bool = True) -> LatticeData: deskew_args = self.LlszMenu.WidgetContainer.deskew_fields._get_kwargs() output_args = self.LlszMenu.WidgetContainer.output_fields._make_model(validate=False) - args = dict( + params = LatticeData.make( + validate=validate, + + # Deskew input_image=deskew_args["data"], angle=deskew_args["angle"], + physical_pixel_sizes=deskew_args["physical_pixel_sizes"], + skew=deskew_args["skew"], + + # Output channel_range=output_args.channel_range, time_range=output_args.time_range, save_dir=output_args.save_dir, - # We let the user specify a prefix, but if they don't, we can use the default - save_name=output_args.save_name or deskew_args["save_name"] , + save_name=output_args.save_name or deskew_args["save_name"], save_type=output_args.save_type, - physical_pixel_sizes=deskew_args["physical_pixel_sizes"], - skew=deskew_args["skew"], + save_suffix=output_args.save_suffix, + workflow=self.LlszMenu.WidgetContainer.workflow_fields._make_model(), deconvolution=self.LlszMenu.WidgetContainer.deconv_fields._make_model(), crop=self.LlszMenu.WidgetContainer.cropping_fields._make_model() ) - params = LatticeData.make(validate=validate, **args) # Log the lattice print(params, file=stdout) return params From ef448fcca17b62c66f4b41d1e34e99c7dc33e6c8 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 19 Dec 2023 15:52:29 +1100 Subject: [PATCH 116/147] Dynamically rescale raw input image according to pixel sizes --- plugin/napari_lattice/fields.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index e57c63a..f85babd 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -304,6 +304,23 @@ def _img_changed(self) -> None: # Recalculate the dimension options whenever the image changes self.dimension_order.reset_choices() + @pixel_sizes_source.connect + @pixel_sizes.connect + def _rescale_image(self): + # Whenever the pixel sizes are changed, this should be reflected in the viewer + image: Image + try: + pixels = self._get_kwargs()["physical_pixel_sizes"] + for image in self.img_layer.value: + image.scale = ( + *image.scale[0:-3], + pixels.Z, + pixels.Y, + pixels.X, + ) + except: + pass + @pixel_sizes_source.connect @enable_if([pixel_sizes]) def _hide_pixel_sizes(self, pixel_sizes_source: str): From b33772ddbde026501d031b71d97c88e69942a05e Mon Sep 17 00:00:00 2001 From: Pradeep R Date: Tue, 19 Dec 2023 16:10:21 +1100 Subject: [PATCH 117/147] Reset view to centre the image --- plugin/napari_lattice/fields.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index f85babd..0c0dfbc 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -309,6 +309,7 @@ def _img_changed(self) -> None: def _rescale_image(self): # Whenever the pixel sizes are changed, this should be reflected in the viewer image: Image + from napari_lattice.utils import get_viewer try: pixels = self._get_kwargs()["physical_pixel_sizes"] for image in self.img_layer.value: @@ -318,6 +319,8 @@ def _rescale_image(self): pixels.Y, pixels.X, ) + viewer = get_viewer() + viewer.reset_view() except: pass From 9adf3643eadd54e062e791f69275d7758acc57c7 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Dec 2023 10:20:18 +1100 Subject: [PATCH 118/147] Fix for crop + deconvolution, with a test --- core/lls_core/models/lattice_data.py | 16 ++++++++-------- core/tests/test_process.py | 13 ++++++++++++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index fd4491c..631f731 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -314,15 +314,15 @@ def _process_crop(self) -> Iterable[ImageSlice]: # pass arguments for save tiff, callable and function arguments logger.info(f"Processing ROI {roi_index}") - deconv_args: dict[Any, Any] = {} - if self.deconvolution is not None: - deconv_args = dict( - num_iter = self.deconvolution.psf_num_iter, - psf = self.deconvolution.psf, - decon_processing=self.deconvolution.decon_processing - ) - for slice in tqdm(self.iter_slices(), desc="2D Slice Number", position=1): + deconv_args: dict[Any, Any] = {} + if self.deconvolution is not None: + deconv_args = dict( + num_iter = self.deconvolution.psf_num_iter, + psf = self.deconvolution.psf[slice.channel], + decon_processing=self.deconvolution.decon_processing + ) + yield slice.copy_with_data( crop_volume_deskew( original_volume=slice.data, diff --git a/core/tests/test_process.py b/core/tests/test_process.py index 6fe2417..d49c326 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -1,6 +1,7 @@ from typing import Any, List, Optional import pytest from lls_core.models import LatticeData +from lls_core.models.crop import CropParams from lls_core.sample import resources from importlib_resources import as_file import tempfile @@ -43,9 +44,19 @@ def test_save(minimal_image_path: str, args: dict): results = list(Path(tempdir).iterdir()) assert len(results) > 0 +def test_process_deconv_crop(): + for slice in LatticeData.parse_obj({ + "input_image": root / "raw.tif", + "deconvolution": { + "psf": [root / "psf.tif"], + }, + "crop": CropParams(roi_list = [[[0, 0], [0, 110], [95, 0], [95, 110]]]) + }).process().slices: + assert slice.data.ndim == 3 + @pytest.mark.parametrize(["background"], [(1, ), ("auto",), ("second_last",)]) @parameterized -def test_process_deconvolution(args: dict, background: Any): +def test_process_deconvolution(args: dict, background: Any, crop: Any): for slice in LatticeData.parse_obj({ "input_image": root / "raw.tif", "deconvolution": { From 18686663e8970170de5502012b63f00a55d0c793 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Dec 2023 10:57:58 +1100 Subject: [PATCH 119/147] Fix "test_process_deconvolution" --- core/tests/test_cli.py | 1 + core/tests/test_process.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py index 7f08a4c..8fc3b28 100644 --- a/core/tests/test_cli.py +++ b/core/tests/test_cli.py @@ -63,6 +63,7 @@ def assert_h5(output_dir: Path): [["--save-type", "h5"], assert_h5], [["--save-type", "tiff"], assert_tiff], [["--save-type", "tiff", "--time-start", "0", "--time-end", "1"], assert_tiff], + [["--save-type", "tiff", "--z-start", "0", "--z-end", "1"], assert_tiff], ] ) def test_batch_deskew(flags: List[str], check_fn: Callable[[Path], None]): diff --git a/core/tests/test_process.py b/core/tests/test_process.py index d49c326..4765e15 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -56,7 +56,7 @@ def test_process_deconv_crop(): @pytest.mark.parametrize(["background"], [(1, ), ("auto",), ("second_last",)]) @parameterized -def test_process_deconvolution(args: dict, background: Any, crop: Any): +def test_process_deconvolution(args: dict, background: Any): for slice in LatticeData.parse_obj({ "input_image": root / "raw.tif", "deconvolution": { From 6a988a676381abeefe4dacd66549f4a2fe2d0acd Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Dec 2023 11:56:27 +1100 Subject: [PATCH 120/147] Reject CropParams with an empty ROI list --- core/lls_core/models/crop.py | 3 +++ core/tests/test_cli.py | 1 - core/tests/test_validation.py | 11 ++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index 48863a7..eaee299 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -53,6 +53,9 @@ def read_roi(cls, v: Any) -> List[Roi]: except: raise ValueError(f"{item} cannot be intepreted as an ROI") + if len(rois) == 0: + raise ValueError("At least one region of interest must be specified if cropping is enabled") + return rois @validator("roi_subset", pre=True, always=True) diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py index 8fc3b28..7f08a4c 100644 --- a/core/tests/test_cli.py +++ b/core/tests/test_cli.py @@ -63,7 +63,6 @@ def assert_h5(output_dir: Path): [["--save-type", "h5"], assert_h5], [["--save-type", "tiff"], assert_tiff], [["--save-type", "tiff", "--time-start", "0", "--time-end", "1"], assert_tiff], - [["--save-type", "tiff", "--z-start", "0", "--z-end", "1"], assert_tiff], ] ) def test_batch_deskew(flags: List[str], check_fn: Callable[[Path], None]): diff --git a/core/tests/test_validation.py b/core/tests/test_validation.py index 76d1ad6..dc1bf49 100644 --- a/core/tests/test_validation.py +++ b/core/tests/test_validation.py @@ -1,6 +1,8 @@ from pathlib import Path from lls_core.models.crop import CropParams from lls_core.models.lattice_data import LatticeData +import pytest +from pydantic import ValidationError def test_default_save_dir(rbc_tiny: Path): # Test that the save dir is inferred to be the input dir @@ -11,6 +13,13 @@ def test_auto_z_range(rbc_tiny: Path): # Tests that the Z range is automatically set, and it is set # based on the size of the deskewed volume params = LatticeData(input_image=rbc_tiny, crop=CropParams( - roi_list=[] + roi_list=[[[0, 0], [0, 1], [1, 0], [1, 1]]] )) assert params.crop.z_range == (0, 59) + +def test_reject_crop(): + # Tests that the parameters fail validation if cropping is specified without an ROI + with pytest.raises(ValidationError): + CropParams( + roi_list=[] + ) From 15e5d8edda496932b26d67ed53fbeba5cab0b033 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Dec 2023 15:46:45 +1100 Subject: [PATCH 121/147] Add --show-schema CLI flag, and rework the way we document CLI config flags --- core/lls_core/cmds/__main__.py | 11 +++++++++++ core/lls_core/models/crop.py | 6 ++++-- core/lls_core/models/deskew.py | 14 ++++++++------ core/lls_core/models/lattice_data.py | 11 +++++------ core/lls_core/models/output.py | 18 ++++++++++-------- core/lls_core/models/utils.py | 16 +++++++++++++--- 6 files changed, 51 insertions(+), 25 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 5bf896d..ef1f588 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -161,8 +161,19 @@ def process( workflow: Optional[Path] = Option(None, help="Path to a Napari Workflow file, in YAML format. If provided, the configured desekewing processing will be added to the chosen workflow.", show_default=False), json_config: Optional[Path] = Option(None, show_default=False, help="Path to a JSON file from which parameters will be read."), yaml_config: Optional[Path] = Option(None, show_default=False, help="Path to a YAML file from which parameters will be read."), + + show_schema: bool = Option(default=False, help="If provided, image processing will not be performed, and instead a JSON document outlining the JSON/YAML options will be printed to stdout. This can be used to assist with writing a config file for use with the --json-config and --yaml-config options.") ) -> None: from click.core import ParameterSource + if show_schema: + import json + import sys + json.dump( + LatticeData.to_definition_dict(), + sys.stdout, + indent=4 + ) + return # Just print help if the user didn't provide any arguments if all(src != ParameterSource.COMMANDLINE for src in ctx._parameter_source.values()): diff --git a/core/lls_core/models/crop.py b/core/lls_core/models/crop.py index eaee299..db7adea 100644 --- a/core/lls_core/models/crop.py +++ b/core/lls_core/models/crop.py @@ -12,15 +12,17 @@ class CropParams(FieldAccessModel): """ roi_list: List[Roi] = Field( description="List of regions of interest, each of which must be an NxD array, where N is the number of vertices and D the coordinates of each vertex.", + cli_description="List of regions of interest, each of which must be the file path to ImageJ ROI file.", default = [] ) roi_subset: List[int] = Field( - description="A subset of all the ROIs to process", + description="A subset of all the ROIs to process. Each array item should be an index into the ROI list indicating an ROI to include.", default=None ) z_range: Tuple[NonNegativeInt, NonNegativeInt] = Field( default=None, - description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out." + description="The range of Z slices to take. All Z slices before the first index or after the last index will be cropped out.", + cli_description="An array with two items, indicating the index of the first and last Z slice to include." ) @property diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 9a0b86c..4548f06 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -23,9 +23,9 @@ class DefinedPixelSizes(FieldAccessModel): Like PhysicalPixelSizes, but it's a dataclass, and none of its fields are None """ - X: NonNegativeFloat = 0.1499219272808386 - Y: NonNegativeFloat = 0.1499219272808386 - Z: NonNegativeFloat = 0.3 + X: NonNegativeFloat = Field(default=0.1499219272808386, description="Size of the X dimension of the microscope pixels, in microns.") + Y: NonNegativeFloat = Field(default=0.1499219272808386, description="Size of the Y dimension of the microscope pixels, in microns.") + Z: NonNegativeFloat = Field(default=0.3, description="Size of the Z dimension of the microscope pixels, in microns.") @classmethod def from_physical(cls, pixels: PhysicalPixelSizes) -> Self: @@ -53,7 +53,8 @@ class DerivedDeskewFields(FieldAccessModel): class DeskewParams(FieldAccessModel): input_image: DataArray = Field( - description="A 3-5D array containing the image data." + description="A 3-5D array containing the image data.", + cli_description="A path to any standard image file (TIFF, H5 etc) containing a 3-5D array to process." ) skew: DeskewDirection = Field( default=DeskewDirection.Y, @@ -61,7 +62,7 @@ class DeskewParams(FieldAccessModel): ) angle: float = Field( default=30.0, - description="Angle of deskewing, in degrees." + description="Angle of deskewing, in degrees, as a float." ) physical_pixel_sizes: DefinedPixelSizes = Field( default_factory=DefinedPixelSizes, @@ -70,7 +71,8 @@ class DeskewParams(FieldAccessModel): derived: DerivedDeskewFields = Field( init_var=False, default=None, - description="Refer to the DerivedDeskewFields docstring" + description="Refer to the DerivedDeskewFields docstring", + cli_hide=True ) # Hack to ensure that .skew_dir behaves identically to .skew @property diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 631f731..14655f4 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -26,11 +26,6 @@ logger = logging.getLogger(__name__) -workflow: Optional[Workflow] = Field( - default=None, - description="If defined, this is a workflow to add lightsheet processing onto" -) - class LatticeData(OutputParams, DeskewParams): """ Holds data and metadata for a given image in a consistent format @@ -45,7 +40,11 @@ class LatticeData(OutputParams, DeskewParams): #: If this is None, then cropping is disabled crop: Optional[CropParams] = None - workflow: Optional[Workflow] = workflow + workflow: Optional[Workflow] = Field( + default=None, + description="If defined, this is a workflow to add lightsheet processing onto", + cli_description="Path to a JSON file specifying a napari_workflow-compatible workflow to add lightsheet processing onto" + ) @root_validator(pre=True) def use_image_path(cls, values: dict): diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 15f332c..2f7b127 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -1,8 +1,7 @@ from pydantic import Field, DirectoryPath, validator from strenum import StrEnum -from os import getcwd from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from lls_core.models.utils import FieldAccessModel, enum_choices @@ -16,11 +15,12 @@ class SaveFileType(StrEnum): class OutputParams(FieldAccessModel): save_dir: DirectoryPath = Field( - description="The directory where the output data will be saved" + description="The directory where the output data will be saved." ) save_suffix: str = Field( default="_deskewed", - description="The filename suffix that will be used for output files. This will be added as a suffix to the input file name if the input image was specified using a file name. If the input image was provided as an in-memory object, the `save_name` field should instead be specified." + description="The filename suffix that will be used for output files. This will be added as a suffix to the input file name if the input image was specified using a file name. If the input image was provided as an in-memory object, the `save_name` field should instead be specified.", + cli_description="The filename suffix that will be used for output files. This will be added as a suffix to the input file name if the --save-name flag was not specified." ) save_name: str = Field( description="The filename that will be used for output files. This should not contain a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc.", @@ -28,15 +28,17 @@ class OutputParams(FieldAccessModel): ) save_type: SaveFileType = Field( default=SaveFileType.h5, - description=f"The data type to save the result as. This will also be used to determine the file extension of the output files. Choices: {enum_choices(SaveFileType)}" + description=f"The data type to save the result as. This will also be used to determine the file extension of the output files. Choices: {enum_choices(SaveFileType)}." ) time_range: range = Field( default=None, - description="The range of times to process. This defaults to all time points in the image array." + description="The range of times to process. This defaults to all time points in the image array.", + cli_description="The range of times to process, as an array with two items: the first and last time index. This defaults to all time points in the image array." ) channel_range: range = Field( - description="The filename prefix that will be used for output files, without a leading directory or file extension. The final output files will have additional elements added to the end of this prefix to indicate the region of interest, channel, timepoint, file extension etc.", - default=None + default=None, + description="The range of channels to process. This defaults to all time points in the image array.", + cli_description="The range of channels to process, as an array with two items: the first and last channel index. This defaults to all channels in the image array." ) @validator("save_dir", pre=True) diff --git a/core/lls_core/models/utils.py b/core/lls_core/models/utils.py index 768595c..2ec98a0 100644 --- a/core/lls_core/models/utils.py +++ b/core/lls_core/models/utils.py @@ -54,11 +54,21 @@ def to_definition_dict(cls) -> dict: """ ret = {} for key, value in cls.__fields__.items(): + if value.field_info.extra.get("cli_hide"): + # Hide any fields that have cli_hide = True + continue + if isinstance(value.outer_type_, type) and issubclass(value.outer_type_, FieldAccessModel): - value = value.outer_type_.to_definition_dict() + rhs = value.outer_type_.to_definition_dict() else: - value = value.field_info.description - ret[key] = value + rhs: str + if "cli_description" in value.field_info.extra: + # cli_description can be used to configure the help text that appears for fields for the CLI only + rhs = value.field_info.extra["cli_description"] + else: + rhs = value.field_info.description + rhs += f" Default: {value.get_default()}." + ret[key] = rhs return ret def copy_validate(self, **kwargs) -> Self: From 2a2024a95e9592c22ea7203766b24055e729fb0a Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Wed, 20 Dec 2023 16:24:27 +1100 Subject: [PATCH 122/147] Re-enable testing on all python versions; copy a pairwise implementation for older pythons --- .github/workflows/test_and_deploy.yml | 212 +++++++++++++------------- core/lls_core/cmds/__main__.py | 13 +- 2 files changed, 116 insertions(+), 109 deletions(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 2c5bacd..71ae1b9 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -1,107 +1,105 @@ -# This workflows will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries -# For pytest-qt related fixes: https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html#github-actions - -name: tests - -on: - pull_request: - paths-ignore: - - "**/README.md" - push: - paths-ignore: - - "**/README.md" - -jobs: - test: - runs-on: ubuntu-latest - defaults: - run: - shell: bash -l {0} - strategy: - fail-fast: false - matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] - env: - DISPLAY: ":99.0" - steps: - - uses: actions/checkout@v3 - - - uses: conda-incubator/setup-miniconda@v2 - with: - auto-update-conda: true - activate-environment: test - python-version: ${{ matrix.python-version }} - channels: conda-forge - - - name: Conda info - run: conda info - - - name: Save conda location - run: echo "python=$(which python)" >> "$GITHUB_ENV" - - - name: Install core - timeout-minutes: 10 - run: | - conda install -y pyopencl pocl - python --version - pip install --upgrade pip setuptools wheel - pip install --use-pep517 -e './core[testing]' - - - name: Lint core - uses: ./.github/lint - with: - directory: core - python: ${{ env.python }} - - - name: Test core - # Only run all the tests on one version, to avoid using excessive CPU hours - if: ${{ matrix.python-version == '3.10' }} - run: pytest -v --cov=core --cov-report=xml core/ - - - uses: tlambert03/setup-qt-libs@v1 - - - name: Install plugin - timeout-minutes: 10 - run: | - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX - pip install pytest-qt PyQt5 - pip install -e './plugin[testing]' - - - name: Lint plugin - uses: ./.github/lint - with: - directory: plugin - python: ${{ env.python }} - - - name: Test plugin - run: pytest -v --cov=plugin --cov-report=xml plugin - - - name: Coverage - uses: codecov/codecov-action@v3 - - deploy: - # this will run when you have tagged a commit, starting with "v*" - # and requires that you have put your twine API key in your - # github secrets (see readme for details) - needs: [test] - runs-on: ubuntu-latest - if: contains(github.ref, 'tags') - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -U setuptools setuptools_scm wheel twine - - name: Build and publish - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.TWINE_TOKEN }} - run: | - git tag - python setup.py sdist bdist_wheel - twine upload dist/* +# This workflows will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries +# For pytest-qt related fixes: https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html#github-actions + +name: tests + +on: + pull_request: + paths-ignore: + - "**/README.md" + push: + paths-ignore: + - "**/README.md" + +jobs: + test: + runs-on: ubuntu-latest + defaults: + run: + shell: bash -l {0} + strategy: + fail-fast: false + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11"] + env: + DISPLAY: ":99.0" + steps: + - uses: actions/checkout@v3 + + - uses: conda-incubator/setup-miniconda@v2 + with: + auto-update-conda: true + activate-environment: test + python-version: ${{ matrix.python-version }} + channels: conda-forge + + - name: Conda info + run: conda info + + - name: Save conda location + run: echo "python=$(which python)" >> "$GITHUB_ENV" + + - name: Install core + timeout-minutes: 10 + run: | + conda install -y pyopencl pocl + python --version + pip install --upgrade pip setuptools wheel + pip install --use-pep517 -e './core[testing]' + + - name: Lint core + uses: ./.github/lint + with: + directory: core + python: ${{ env.python }} + + - name: Test core + run: pytest -v --cov=core --cov-report=xml core/ + + - uses: tlambert03/setup-qt-libs@v1 + + - name: Install plugin + timeout-minutes: 10 + run: | + /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX + pip install pytest-qt PyQt5 + pip install -e './plugin[testing]' + + - name: Lint plugin + uses: ./.github/lint + with: + directory: plugin + python: ${{ env.python }} + + - name: Test plugin + run: pytest -v --cov=plugin --cov-report=xml plugin + + - name: Coverage + uses: codecov/codecov-action@v3 + + deploy: + # this will run when you have tagged a commit, starting with "v*" + # and requires that you have put your twine API key in your + # github secrets (see readme for details) + needs: [test] + runs-on: ubuntu-latest + if: contains(github.ref, 'tags') + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U setuptools setuptools_scm wheel twine + - name: Build and publish + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.TWINE_TOKEN }} + run: | + git tag + python setup.py sdist bdist_wheel + twine upload dist/* diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index ef1f588..33392c7 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -23,7 +23,7 @@ if TYPE_CHECKING: from lls_core.models.utils import FieldAccessModel - from typing import Type, Any + from typing import Type, Any, Iterable from rich.table import Table class CliDeskewDirection(StrEnum): @@ -100,8 +100,17 @@ def rich_validation(e: ValidationError) -> Table: return table +def pairwise(iterable: Iterable) -> Iterable: + """ + An implementation of the pairwise() function in Python 3.10+ + See: https://docs.python.org/3.12/library/itertools.html#itertools.pairwise + """ + from itertools import tee + a, b = tee(iterable) + next(b, None) + return zip(a, b) + def update_nested_data(data: Union[dict, list], keys: list, new_value: Any): - from itertools import pairwise current = data for key, next_key in pairwise(keys): From f26266fb0d7d56d8a774e40a220675d230a40f8c Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 21 Dec 2023 17:34:19 +1100 Subject: [PATCH 123/147] Revert workflow argument handling to the old method: only passing the image as the first unfilled argument --- core/lls_core/models/lattice_data.py | 14 +- core/lls_core/workflow.py | 453 +++++++++--------- core/tests/test_workflows.py | 11 + .../argument_order/custom_function.py | 7 + .../argument_order/test_workflow.yml | 7 + 5 files changed, 259 insertions(+), 233 deletions(-) create mode 100644 core/tests/workflows/argument_order/custom_function.py create mode 100644 core/tests/workflows/argument_order/test_workflow.yml diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 14655f4..f3ba60a 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -268,13 +268,13 @@ def generate_workflows( LatticeData.process_into_image, lattice_slice.data ) - for task_name, arg_index, arg_name in get_workflow_inputs(user_workflow): - update_workflow( - user_workflow, - task_name, - arg_index, - "deskew_image" - ) + task_name, arg_index, _arg_name = get_workflow_inputs(user_workflow) + update_workflow( + user_workflow, + task_name, + arg_index, + "deskew_image" + ) yield lattice_slice.copy_with_data(user_workflow) def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int): diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index 682f45f..ae13861 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -1,226 +1,227 @@ -""" -Functions related to manipulating Napari Workflows -""" -from __future__ import annotations - -from os import path -from pathlib import Path -from typing import Any, Optional, Tuple, Union - -import dask.array as da -import numpy as np -import pandas as pd -import pyclesperanto_prototype as cle -from lls_core.types import ArrayLike -from typing_extensions import TYPE_CHECKING - -if TYPE_CHECKING: - from napari_workflows import Workflow - -import logging -logger = logging.getLogger(__name__) -logger.setLevel(logging.INFO) - -def get_workflow_inputs(workflow: Workflow): - """ - Yields tuples of (task_name, argument_index, input_argument) corresponding to the workflow's inputs, - namely the arguments that are unfilled. - Note that the index returned is the index in the overall task tuple, which includes the task name - """ - for root_arg in workflow.roots(): - for taskname, (task_func, *args) in workflow._tasks.items(): - if root_arg in args: - yield taskname, args.index(root_arg) + 1, root_arg - -def update_workflow(workflow: Workflow, task_name: str, task_index: int, new_value: Any): - task = list(workflow.get_task(task_name)) - task[task_index] = new_value - workflow.set_task(task_name, tuple(task)) - -def get_first_last_image_and_task(user_workflow: Workflow) -> Tuple[str, str, str, str]: - """ - Get images and tasks for first and last entry - Returns: - Tuple of (name of first input image, name of last input image, name of first task, name of last task) - """ - - # get image with no preprocessing step (first image) - input_arg_first = user_workflow.roots()[0] - # get last image - input_arg_last = user_workflow.leafs()[0] - # get name of preceding image as that is the input to last task - img_source = user_workflow.sources_of(input_arg_last)[0] - first_task_name = [] - last_task_name = [] - - # loop through workflow keys and get key that has - for key in user_workflow._tasks.keys(): - for task in user_workflow._tasks[key]: - if task == input_arg_first: - first_task_name.append(key) - elif task == img_source: - last_task_name.append(key) - - return input_arg_first, img_source, first_task_name[0], last_task_name[0] if len(last_task_name) > 0 else first_task_name[0] - - -def modify_workflow_task(old_arg: str, task_key: str, new_arg: str, workflow: Workflow) -> tuple: - """ - Replies one argument in a workflow with another - Workflow is not modified, only a new task with updated arg is returned - Args: - old_arg: The argument in the workflow that needs to be modified - new_arg: New argument - task_key: Name of the task within the workflow - workflow: Workflow - - Returns: - tuple: Modified task with name task_key - """ - task = workflow._tasks[task_key] - # convert tuple to list for modification - task_list = list(task) - try: - item_index = task_list.index(old_arg) - except ValueError: - raise Exception(f"{old_arg} not found in workflow file") - - task_list[item_index] = new_arg - modified_task = tuple(task_list) - return modified_task - -def replace_first_arg(workflow: Workflow, new_arg: str, old_arg: Optional[str] = None): - """ - Replaces an argument in the first task of a workflow with some other value - Args: - old_arg: If provided, the name of an argument to replace, otherwise it defaults to the - first workflow arg - """ - img_arg_first, _, first_task_name, _ = get_first_last_image_and_task(workflow) - if old_arg is None: - old_arg = img_arg_first - workflow.set( - first_task_name, - modify_workflow_task( - old_arg=old_arg, - task_key=first_task_name, - new_arg=new_arg, - workflow=workflow - ) - ) - -def load_custom_py_modules(custom_py_files): - import sys - from importlib import import_module, reload - test_first_module_import = import_module(custom_py_files[0]) - if test_first_module_import not in sys.modules: - modules = map(import_module, custom_py_files) - else: - modules = map(reload, custom_py_files) - return modules - - -# TODO: CHANGE so user can select modules? Safer -def get_all_py_files(directory: str) -> list[str]: - """get all py files within directory and return as a list of filenames - Args: - directory: Directory with .py files - """ - import glob - from os.path import basename, dirname, isfile, join - - modules = glob.glob(join(dirname(directory), "*.py")) - all = [basename(f)[:-3] for f in modules if isfile(f) - and not f.endswith('__init__.py')] - print(f"Files found are: {all}") - - return all - -def import_script(script: Path): - """ - Imports a Python script given its path - """ - import importlib.util - import sys - module_name = script.stem - spec = importlib.util.spec_from_file_location(module_name, script) - if spec is None or spec.loader is None: - raise Exception(f"Failed to import {script}!") - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) - -def _import_workflow_modules(workflow: Path) -> None: - """ - Imports all the Python files that might be used in a given custom workflow - - Args: - workflow: Path to the workflow YAML file - """ - if not workflow.exists(): - raise Exception("Workflow doesn't exist!") - if not workflow.is_file(): - raise Exception("Workflow must be a file!") - - counter = 0 - for script in workflow.parent.glob("*.py"): - if script.stem == "__init__": - # Skip __init__.py - continue - import_script(script) - counter += 1 - - if counter == 0: - logger.warn(f"No custom modules imported. If you'd like to use a custom module, place a *.py file in same folder as the workflow file {workflow.parent}") - else: - logger.info(f"{counter} custom modules imported") - -def workflow_from_path(workflow: Path) -> Workflow: - """ - Imports the dependency modules for a workflow, and loads it from disk - """ - from napari_workflows._io_yaml_v1 import load_workflow - _import_workflow_modules(workflow) - return load_workflow(str(workflow)) - -def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike], - save_dir: Optional[str]=None, - idx: Optional[str]=None, - LLSZWidget=None, - widget_class=None, - channel=0, - time=0, - preview: bool = True): - """Check the output from a custom workflow; - saves tables and images separately - - Args: - workflow_output (_type_): _description_ - save_dir (_type_): _description_ - idx (_type_): _description_ - LLSZWidget (_type_): _description_ - widget_class (_type_): _description_ - channel (_type_): _description_ - time (_type_): _description_ - """ - if isinstance(workflow_output, (dict, list)): - # create function for tthis dataframe bit - df = pd.DataFrame(workflow_output) - if preview: - save_path = path.join( - save_dir, "lattice_measurement_"+str(idx)+".csv") - print(f"Detected a dictionary as output, saving preview at", save_path) - df.to_csv(save_path, index=False) - return df - - else: - return df - elif isinstance(workflow_output, (np.ndarray, cle._tier0._pycl.OCLArray, da.core.Array)): - if preview: - suffix_name = str(idx)+"_c" + str(channel) + "_t" + str(time) - scale = (LLSZWidget.LlszMenu.lattice.new_dz, - LLSZWidget.LlszMenu.lattice.dy, LLSZWidget.LlszMenu.lattice.dx) - widget_class.parent_viewer.add_image( - workflow_output, name="Workflow_preview_" + suffix_name, scale=scale) - else: - return workflow_output +""" +Functions related to manipulating Napari Workflows +""" +from __future__ import annotations + +from os import path +from pathlib import Path +from typing import Any, Optional, Tuple, Union + +import dask.array as da +import numpy as np +import pandas as pd +import pyclesperanto_prototype as cle +from lls_core.types import ArrayLike +from typing_extensions import TYPE_CHECKING + +if TYPE_CHECKING: + from napari_workflows import Workflow + +import logging +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +def get_workflow_inputs(workflow: Workflow) -> Tuple[str, int, str]: + """ + Yields tuples of (task_name, argument_index, input_argument) corresponding to the workflow's inputs, + namely the arguments that are unfilled. + Note that the index returned is the index in the overall task tuple, which includes the task name + """ + for root_arg in workflow.roots(): + for taskname, (task_func, *args) in workflow._tasks.items(): + if root_arg in args: + return taskname, args.index(root_arg) + 1, root_arg + raise Exception("No inputs could be calculated") + +def update_workflow(workflow: Workflow, task_name: str, task_index: int, new_value: Any): + task = list(workflow.get_task(task_name)) + task[task_index] = new_value + workflow.set_task(task_name, tuple(task)) + +def get_first_last_image_and_task(user_workflow: Workflow) -> Tuple[str, str, str, str]: + """ + Get images and tasks for first and last entry + Returns: + Tuple of (name of first input image, name of last input image, name of first task, name of last task) + """ + + # get image with no preprocessing step (first image) + input_arg_first = user_workflow.roots()[0] + # get last image + input_arg_last = user_workflow.leafs()[0] + # get name of preceding image as that is the input to last task + img_source = user_workflow.sources_of(input_arg_last)[0] + first_task_name = [] + last_task_name = [] + + # loop through workflow keys and get key that has + for key in user_workflow._tasks.keys(): + for task in user_workflow._tasks[key]: + if task == input_arg_first: + first_task_name.append(key) + elif task == img_source: + last_task_name.append(key) + + return input_arg_first, img_source, first_task_name[0], last_task_name[0] if len(last_task_name) > 0 else first_task_name[0] + + +def modify_workflow_task(old_arg: str, task_key: str, new_arg: str, workflow: Workflow) -> tuple: + """ + Replies one argument in a workflow with another + Workflow is not modified, only a new task with updated arg is returned + Args: + old_arg: The argument in the workflow that needs to be modified + new_arg: New argument + task_key: Name of the task within the workflow + workflow: Workflow + + Returns: + tuple: Modified task with name task_key + """ + task = workflow._tasks[task_key] + # convert tuple to list for modification + task_list = list(task) + try: + item_index = task_list.index(old_arg) + except ValueError: + raise Exception(f"{old_arg} not found in workflow file") + + task_list[item_index] = new_arg + modified_task = tuple(task_list) + return modified_task + +def replace_first_arg(workflow: Workflow, new_arg: str, old_arg: Optional[str] = None): + """ + Replaces an argument in the first task of a workflow with some other value + Args: + old_arg: If provided, the name of an argument to replace, otherwise it defaults to the + first workflow arg + """ + img_arg_first, _, first_task_name, _ = get_first_last_image_and_task(workflow) + if old_arg is None: + old_arg = img_arg_first + workflow.set( + first_task_name, + modify_workflow_task( + old_arg=old_arg, + task_key=first_task_name, + new_arg=new_arg, + workflow=workflow + ) + ) + +def load_custom_py_modules(custom_py_files): + import sys + from importlib import import_module, reload + test_first_module_import = import_module(custom_py_files[0]) + if test_first_module_import not in sys.modules: + modules = map(import_module, custom_py_files) + else: + modules = map(reload, custom_py_files) + return modules + + +# TODO: CHANGE so user can select modules? Safer +def get_all_py_files(directory: str) -> list[str]: + """get all py files within directory and return as a list of filenames + Args: + directory: Directory with .py files + """ + import glob + from os.path import basename, dirname, isfile, join + + modules = glob.glob(join(dirname(directory), "*.py")) + all = [basename(f)[:-3] for f in modules if isfile(f) + and not f.endswith('__init__.py')] + print(f"Files found are: {all}") + + return all + +def import_script(script: Path): + """ + Imports a Python script given its path + """ + import importlib.util + import sys + module_name = script.stem + spec = importlib.util.spec_from_file_location(module_name, script) + if spec is None or spec.loader is None: + raise Exception(f"Failed to import {script}!") + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + +def _import_workflow_modules(workflow: Path) -> None: + """ + Imports all the Python files that might be used in a given custom workflow + + Args: + workflow: Path to the workflow YAML file + """ + if not workflow.exists(): + raise Exception("Workflow doesn't exist!") + if not workflow.is_file(): + raise Exception("Workflow must be a file!") + + counter = 0 + for script in workflow.parent.glob("*.py"): + if script.stem == "__init__": + # Skip __init__.py + continue + import_script(script) + counter += 1 + + if counter == 0: + logger.warn(f"No custom modules imported. If you'd like to use a custom module, place a *.py file in same folder as the workflow file {workflow.parent}") + else: + logger.info(f"{counter} custom modules imported") + +def workflow_from_path(workflow: Path) -> Workflow: + """ + Imports the dependency modules for a workflow, and loads it from disk + """ + from napari_workflows._io_yaml_v1 import load_workflow + _import_workflow_modules(workflow) + return load_workflow(str(workflow)) + +def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike], + save_dir: Optional[str]=None, + idx: Optional[str]=None, + LLSZWidget=None, + widget_class=None, + channel=0, + time=0, + preview: bool = True): + """Check the output from a custom workflow; + saves tables and images separately + + Args: + workflow_output (_type_): _description_ + save_dir (_type_): _description_ + idx (_type_): _description_ + LLSZWidget (_type_): _description_ + widget_class (_type_): _description_ + channel (_type_): _description_ + time (_type_): _description_ + """ + if isinstance(workflow_output, (dict, list)): + # create function for tthis dataframe bit + df = pd.DataFrame(workflow_output) + if preview: + save_path = path.join( + save_dir, "lattice_measurement_"+str(idx)+".csv") + print(f"Detected a dictionary as output, saving preview at", save_path) + df.to_csv(save_path, index=False) + return df + + else: + return df + elif isinstance(workflow_output, (np.ndarray, cle._tier0._pycl.OCLArray, da.core.Array)): + if preview: + suffix_name = str(idx)+"_c" + str(channel) + "_t" + str(time) + scale = (LLSZWidget.LlszMenu.lattice.new_dz, + LLSZWidget.LlszMenu.lattice.dy, LLSZWidget.LlszMenu.lattice.dx) + widget_class.parent_viewer.add_image( + workflow_output, name="Workflow_preview_" + suffix_name, scale=scale) + else: + return workflow_output diff --git a/core/tests/test_workflows.py b/core/tests/test_workflows.py index e86ec28..113c5ca 100644 --- a/core/tests/test_workflows.py +++ b/core/tests/test_workflows.py @@ -83,3 +83,14 @@ def test_table_workflow(minimal_image_path: Path, table_workflow: Workflow): assert ncol > 0 else: assert valid_image_path(output) + +def test_argument_order(rbc_tiny: Path): + # Tests that only the first unfilled argument is passed an array + with tempfile.TemporaryDirectory() as tmpdir: + params = LatticeData( + input_image = rbc_tiny, + workflow = "core/tests/workflows/argument_order/test_workflow.yml", + save_dir = tmpdir + ) + for roi, output in params.process_workflow().process(): + print(output) diff --git a/core/tests/workflows/argument_order/custom_function.py b/core/tests/workflows/argument_order/custom_function.py new file mode 100644 index 0000000..07ac327 --- /dev/null +++ b/core/tests/workflows/argument_order/custom_function.py @@ -0,0 +1,7 @@ +from numpy import ndarray + +def test(a, b, c): + assert isinstance(a, ndarray) + assert isinstance(b, str) + assert isinstance(c, int) + return a diff --git a/core/tests/workflows/argument_order/test_workflow.yml b/core/tests/workflows/argument_order/test_workflow.yml new file mode 100644 index 0000000..b74fb9f --- /dev/null +++ b/core/tests/workflows/argument_order/test_workflow.yml @@ -0,0 +1,7 @@ +!!python/object:napari_workflows._workflow.Workflow +_tasks: + test: !!python/tuple + - !!python/name:custom_function.test '' + - input_img + - "abc" + - 3 \ No newline at end of file From 302315437a22e1a6b8ed3f188505bd6be10eaa08 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 21 Dec 2023 17:58:32 +1100 Subject: [PATCH 124/147] Fix tests broken by previous commit --- core/lls_core/models/lattice_data.py | 16 +++++++++------- core/lls_core/workflow.py | 4 ++-- .../measure_regionprops_multich.py | 3 +-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index f3ba60a..5bebbd9 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -268,13 +268,15 @@ def generate_workflows( LatticeData.process_into_image, lattice_slice.data ) - task_name, arg_index, _arg_name = get_workflow_inputs(user_workflow) - update_workflow( - user_workflow, - task_name, - arg_index, - "deskew_image" - ) + inputs = get_workflow_inputs(user_workflow) + if inputs is not None: + task_name, arg_index, _arg_name = inputs + update_workflow( + user_workflow, + task_name, + arg_index, + "deskew_image" + ) yield lattice_slice.copy_with_data(user_workflow) def check_incomplete_acquisition(self, volume: ArrayLike, time_point: int, channel: int): diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index ae13861..31c8bd5 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -def get_workflow_inputs(workflow: Workflow) -> Tuple[str, int, str]: +def get_workflow_inputs(workflow: Workflow) -> Optional[Tuple[str, int, str]]: """ Yields tuples of (task_name, argument_index, input_argument) corresponding to the workflow's inputs, namely the arguments that are unfilled. @@ -31,7 +31,7 @@ def get_workflow_inputs(workflow: Workflow) -> Tuple[str, int, str]: for taskname, (task_func, *args) in workflow._tasks.items(): if root_arg in args: return taskname, args.index(root_arg) + 1, root_arg - raise Exception("No inputs could be calculated") + return None def update_workflow(workflow: Workflow, task_name: str, task_index: int, new_value: Any): task = list(workflow.get_task(task_name)) diff --git a/workflow_examples/regionprops_workflow/measure_regionprops_multich.py b/workflow_examples/regionprops_workflow/measure_regionprops_multich.py index 803a30f..07a5ba9 100644 --- a/workflow_examples/regionprops_workflow/measure_regionprops_multich.py +++ b/workflow_examples/regionprops_workflow/measure_regionprops_multich.py @@ -1,7 +1,6 @@ #Channel specific thresholding -from napari_lattice import config from skimage.filters import threshold_triangle, threshold_otsu def segment_multich(img): @@ -11,4 +10,4 @@ def segment_multich(img): #if second channel, use Otsu threshold elif config.channel == 1: binary_img = threshold_otsu(img) - return binary_img \ No newline at end of file + return binary_img From 90f35234f9aa08454468da78d2e5be24146674a2 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 21 Dec 2023 17:59:10 +1100 Subject: [PATCH 125/147] Undo changes to workflow example --- .../regionprops_workflow/measure_regionprops_multich.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workflow_examples/regionprops_workflow/measure_regionprops_multich.py b/workflow_examples/regionprops_workflow/measure_regionprops_multich.py index 07a5ba9..803a30f 100644 --- a/workflow_examples/regionprops_workflow/measure_regionprops_multich.py +++ b/workflow_examples/regionprops_workflow/measure_regionprops_multich.py @@ -1,6 +1,7 @@ #Channel specific thresholding +from napari_lattice import config from skimage.filters import threshold_triangle, threshold_otsu def segment_multich(img): @@ -10,4 +11,4 @@ def segment_multich(img): #if second channel, use Otsu threshold elif config.channel == 1: binary_img = threshold_otsu(img) - return binary_img + return binary_img \ No newline at end of file From 9e19163b52e46d0a6ceef024c82bc5ef698dc073 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 11 Jan 2024 17:41:09 +1100 Subject: [PATCH 126/147] Remove some dead workflow code --- core/lls_core/workflow.py | 81 +-------------------------------------- 1 file changed, 1 insertion(+), 80 deletions(-) diff --git a/core/lls_core/workflow.py b/core/lls_core/workflow.py index 31c8bd5..e597758 100644 --- a/core/lls_core/workflow.py +++ b/core/lls_core/workflow.py @@ -3,15 +3,9 @@ """ from __future__ import annotations -from os import path from pathlib import Path -from typing import Any, Optional, Tuple, Union +from typing import Any, Optional, Tuple -import dask.array as da -import numpy as np -import pandas as pd -import pyclesperanto_prototype as cle -from lls_core.types import ArrayLike from typing_extensions import TYPE_CHECKING if TYPE_CHECKING: @@ -90,37 +84,6 @@ def modify_workflow_task(old_arg: str, task_key: str, new_arg: str, workflow: Wo modified_task = tuple(task_list) return modified_task -def replace_first_arg(workflow: Workflow, new_arg: str, old_arg: Optional[str] = None): - """ - Replaces an argument in the first task of a workflow with some other value - Args: - old_arg: If provided, the name of an argument to replace, otherwise it defaults to the - first workflow arg - """ - img_arg_first, _, first_task_name, _ = get_first_last_image_and_task(workflow) - if old_arg is None: - old_arg = img_arg_first - workflow.set( - first_task_name, - modify_workflow_task( - old_arg=old_arg, - task_key=first_task_name, - new_arg=new_arg, - workflow=workflow - ) - ) - -def load_custom_py_modules(custom_py_files): - import sys - from importlib import import_module, reload - test_first_module_import = import_module(custom_py_files[0]) - if test_first_module_import not in sys.modules: - modules = map(import_module, custom_py_files) - else: - modules = map(reload, custom_py_files) - return modules - - # TODO: CHANGE so user can select modules? Safer def get_all_py_files(directory: str) -> list[str]: """get all py files within directory and return as a list of filenames @@ -183,45 +146,3 @@ def workflow_from_path(workflow: Path) -> Workflow: from napari_workflows._io_yaml_v1 import load_workflow _import_workflow_modules(workflow) return load_workflow(str(workflow)) - -def process_custom_workflow_output(workflow_output: Union[dict, list, ArrayLike], - save_dir: Optional[str]=None, - idx: Optional[str]=None, - LLSZWidget=None, - widget_class=None, - channel=0, - time=0, - preview: bool = True): - """Check the output from a custom workflow; - saves tables and images separately - - Args: - workflow_output (_type_): _description_ - save_dir (_type_): _description_ - idx (_type_): _description_ - LLSZWidget (_type_): _description_ - widget_class (_type_): _description_ - channel (_type_): _description_ - time (_type_): _description_ - """ - if isinstance(workflow_output, (dict, list)): - # create function for tthis dataframe bit - df = pd.DataFrame(workflow_output) - if preview: - save_path = path.join( - save_dir, "lattice_measurement_"+str(idx)+".csv") - print(f"Detected a dictionary as output, saving preview at", save_path) - df.to_csv(save_path, index=False) - return df - - else: - return df - elif isinstance(workflow_output, (np.ndarray, cle._tier0._pycl.OCLArray, da.core.Array)): - if preview: - suffix_name = str(idx)+"_c" + str(channel) + "_t" + str(time) - scale = (LLSZWidget.LlszMenu.lattice.new_dz, - LLSZWidget.LlszMenu.lattice.dy, LLSZWidget.LlszMenu.lattice.dx) - widget_class.parent_viewer.add_image( - workflow_output, name="Workflow_preview_" + suffix_name, scale=scale) - else: - return workflow_output From c6fb7821a0153258381243ab7c9399da7f19bca1 Mon Sep 17 00:00:00 2001 From: "PRADEEP\\Pradeep" Date: Mon, 15 Jan 2024 23:07:37 +1100 Subject: [PATCH 127/147] added function to return csv filepath --- core/lls_core/models/output.py | 13 +++++++++++-- core/lls_core/models/results.py | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 2f7b127..94d8a79 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -1,8 +1,8 @@ from pydantic import Field, DirectoryPath, validator from strenum import StrEnum from pathlib import Path -from typing import TYPE_CHECKING - +from typing import TYPE_CHECKING, Union +from pandas import DataFrame from lls_core.models.utils import FieldAccessModel, enum_choices if TYPE_CHECKING: @@ -65,3 +65,12 @@ def make_filepath(self, suffix: str) -> Path: Returns a filepath for the resulting data """ return self.save_dir / Path(self.save_name + suffix).with_suffix("." + self.file_extension) + + def make_filepath_df(self, suffix: str,result:Union[DataFrame,list]) -> Path: + """ + Returns a filepath for the non-image data + """ + if isinstance(result, DataFrame): + return self.save_dir / Path(self.save_name + suffix).with_suffix(".csv") + + return diff --git a/core/lls_core/models/results.py b/core/lls_core/models/results.py index 213c655..68dcc62 100644 --- a/core/lls_core/models/results.py +++ b/core/lls_core/models/results.py @@ -135,7 +135,7 @@ def save(self) -> Iterable[Path]: """ for roi, result in self.process(): if isinstance(result, DataFrame): - path = self.lattice_data.make_filepath(make_filename_prefix(roi_index=roi)) + path = self.lattice_data.make_filepath_df(make_filename_prefix(roi_index=roi),result) result.to_csv(str(path)) yield path else: From 6f15bb4aaaa666f4251c74be36bf688216e8f060 Mon Sep 17 00:00:00 2001 From: Pradeep R Date: Tue, 16 Jan 2024 17:40:42 +1100 Subject: [PATCH 128/147] avoid overwriting of output file from workflow; fix how dataframes created --- core/lls_core/models/output.py | 14 ++++++++++++-- core/lls_core/models/results.py | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 94d8a79..c372f27 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -64,13 +64,23 @@ def make_filepath(self, suffix: str) -> Path: """ Returns a filepath for the resulting data """ - return self.save_dir / Path(self.save_name + suffix).with_suffix("." + self.file_extension) + return self.get_unique_filepath(self.save_dir / Path(self.save_name + suffix).with_suffix("." + self.file_extension)) def make_filepath_df(self, suffix: str,result:Union[DataFrame,list]) -> Path: """ Returns a filepath for the non-image data """ if isinstance(result, DataFrame): - return self.save_dir / Path(self.save_name + suffix).with_suffix(".csv") + return self.get_unique_filepath(self.save_dir / Path(self.save_name + suffix).with_suffix(".csv")) return + + def get_unique_filepath(self, path: Path) -> Path: + """ + Returns a unique filepath by appending a number to the filename if the file already exists. + """ + counter = 1 + while path.exists(): + path = path.with_name(f"{path.stem}_{counter}{path.suffix}") + counter += 1 + return path \ No newline at end of file diff --git a/core/lls_core/models/results.py b/core/lls_core/models/results.py index 68dcc62..7c44a6f 100644 --- a/core/lls_core/models/results.py +++ b/core/lls_core/models/results.py @@ -125,7 +125,7 @@ def process(self) -> Iterable[Tuple[RoiIndex, ProcessedWorkflowOutput]]: for file in element.written_files: yield roi, file else: - yield roi, pd.DataFrame(element) + yield roi, pd.DataFrame(element[0]) def save(self) -> Iterable[Path]: """ From ba51e685cdf1db393c9f3da08b54539b99a30f00 Mon Sep 17 00:00:00 2001 From: "PRADEEP\\Pradeep" Date: Wed, 17 Jan 2024 13:05:54 +1100 Subject: [PATCH 129/147] revert change, and use pandas series explode instead --- core/lls_core/models/results.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/lls_core/models/results.py b/core/lls_core/models/results.py index 7c44a6f..2cc3feb 100644 --- a/core/lls_core/models/results.py +++ b/core/lls_core/models/results.py @@ -8,7 +8,7 @@ from lls_core.types import ArrayLike, is_arraylike from lls_core.utils import make_filename_prefix from lls_core.writers import RoiIndex, Writer -from pandas import DataFrame +from pandas import DataFrame, Series if TYPE_CHECKING: from lls_core.models.lattice_data import LatticeData @@ -125,7 +125,7 @@ def process(self) -> Iterable[Tuple[RoiIndex, ProcessedWorkflowOutput]]: for file in element.written_files: yield roi, file else: - yield roi, pd.DataFrame(element[0]) + yield roi, pd.DataFrame(element) def save(self) -> Iterable[Path]: """ @@ -136,6 +136,7 @@ def save(self) -> Iterable[Path]: for roi, result in self.process(): if isinstance(result, DataFrame): path = self.lattice_data.make_filepath_df(make_filename_prefix(roi_index=roi),result) + result = result.apply(Series.explode) result.to_csv(str(path)) yield path else: From b294172dea21b62c2e531536b44c5e61b8d63e04 Mon Sep 17 00:00:00 2001 From: Pradeep Rajasekhar Date: Thu, 18 Jan 2024 12:43:07 +1100 Subject: [PATCH 130/147] Update core/lls_core/models/output.py Co-authored-by: Michael Milton --- core/lls_core/models/output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index c372f27..4a706be 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -83,4 +83,4 @@ def get_unique_filepath(self, path: Path) -> Path: while path.exists(): path = path.with_name(f"{path.stem}_{counter}{path.suffix}") counter += 1 - return path \ No newline at end of file + return path From 12429275ec19f4858d1fc4e7e086eb1b82121036 Mon Sep 17 00:00:00 2001 From: Pradeep Rajasekhar Date: Thu, 18 Jan 2024 12:43:42 +1100 Subject: [PATCH 131/147] take only dataframe for now can modify this in the future Co-authored-by: Michael Milton --- core/lls_core/models/output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lls_core/models/output.py b/core/lls_core/models/output.py index 4a706be..91c45e9 100644 --- a/core/lls_core/models/output.py +++ b/core/lls_core/models/output.py @@ -66,7 +66,7 @@ def make_filepath(self, suffix: str) -> Path: """ return self.get_unique_filepath(self.save_dir / Path(self.save_name + suffix).with_suffix("." + self.file_extension)) - def make_filepath_df(self, suffix: str,result:Union[DataFrame,list]) -> Path: + def make_filepath_df(self, suffix: str, result: DataFrame) -> Path: """ Returns a filepath for the non-image data """ From 2139687b4cbc042876afdcbb535bfe96abe07d2b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 1 Aug 2024 13:46:16 +1000 Subject: [PATCH 132/147] A pixel tuple should be in the order of Z, Y, then X --- core/lls_core/cmds/__main__.py | 6 +++--- core/lls_core/models/deskew.py | 2 +- core/tests/test_validation.py | 12 ++++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 33392c7..ad866ae 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -139,10 +139,10 @@ def process( input_image: Path = Argument(None, help="Path to the image file to read, in a format readable by AICSImageIO, for example .tiff or .czi", show_default=False), skew: CliDeskewDirection = field_from_model(DeskewParams, "skew"),# DeskewParams.make_typer_field("skew"), angle: float = field_from_model(DeskewParams, "angle") , - pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the X Y and Z pixel dimensions respectively", default=( - DeskewParams.get_default("physical_pixel_sizes").X, + pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the Z, Y and X pixel dimensions respectively", default=( + DeskewParams.get_default("physical_pixel_sizes").Z, DeskewParams.get_default("physical_pixel_sizes").Y, - DeskewParams.get_default("physical_pixel_sizes").Z + DeskewParams.get_default("physical_pixel_sizes").X )), rois: List[Path] = field_from_model(CropParams, "roi_list", description="A list of paths pointing to regions of interest to crop to, in ImageJ format."), #Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 4548f06..a2c1ba4 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -161,7 +161,7 @@ def convert_skew(cls, v: Any): def convert_pixels(cls, v: Any): # Allow the pixel sizes to be specified as a tuple if isinstance(v, tuple) and len(v) == 3: - return DefinedPixelSizes(X=v[0], Y=v[1], Z=v[2]) + return DefinedPixelSizes(Z=v[0], Y=v[1], X=v[2]) return v @validator("input_image", pre=True) diff --git a/core/tests/test_validation.py b/core/tests/test_validation.py index dc1bf49..80101ee 100644 --- a/core/tests/test_validation.py +++ b/core/tests/test_validation.py @@ -1,6 +1,7 @@ from pathlib import Path from lls_core.models.crop import CropParams from lls_core.models.lattice_data import LatticeData +from lls_core.models.deskew import DeskewParams import pytest from pydantic import ValidationError @@ -23,3 +24,14 @@ def test_reject_crop(): CropParams( roi_list=[] ) + +def test_pixel_tuple_order(rbc_tiny: Path): + # Tests that a tuple of Z, Y, X is appropriately assigned in the right order + deskew = DeskewParams( + input_image=rbc_tiny, + physical_pixel_sizes=(1., 2., 3.) + ) + + assert deskew.physical_pixel_sizes.X == 3. + assert deskew.physical_pixel_sizes.Y == 2. + assert deskew.physical_pixel_sizes.Z == 1. From 420898b4a59ee33c3b408f865132c35718c11fb5 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 1 Aug 2024 17:50:30 +1000 Subject: [PATCH 133/147] Use AICS metadata even when GUI isn't involved --- core/lls_core/models/deskew.py | 34 +++++++++++++-- core/lls_core/types.py | 76 +++++++++++++++++----------------- core/tests/test_validation.py | 17 ++++++++ 3 files changed, 85 insertions(+), 42 deletions(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index a2c1ba4..132c43b 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -1,7 +1,7 @@ from __future__ import annotations # class for initializing lattice data and setting metadata # TODO: handle scenes -from pydantic import Field, NonNegativeFloat, validator +from pydantic import Field, NonNegativeFloat, validator, root_validator from typing import Any, Tuple from typing_extensions import Self, TYPE_CHECKING @@ -12,7 +12,7 @@ from xarray import DataArray from lls_core.models.utils import FieldAccessModel, enum_choices -from lls_core.types import image_like_to_image +from lls_core.types import image_like_to_image, is_arraylike, is_pathlike from lls_core.utils import get_deskewed_shape if TYPE_CHECKING: @@ -164,11 +164,37 @@ def convert_pixels(cls, v: Any): return DefinedPixelSizes(Z=v[0], Y=v[1], X=v[2]) return v + @root_validator(pre=True) + def read_image(cls, values: dict): + from aicsimageio import AICSImage + from os import fspath + + img = values["input_image"] + + aics: AICSImage | None = None + if is_pathlike(img): + aics = AICSImage(fspath(img)) + elif isinstance(img, AICSImage): + aics = img + elif is_arraylike(img): + values["input_image"] = DataArray(img) + else: + raise ValueError("Value of input_image was neither a path, an AICSImage, or array-like.") + + # If the image was convertible to AICSImage, we should use the metadata from there + if aics: + values["input_image"] = aics.xarray_dask_data + values["physical_pixel_sizes"] = aics.physical_pixel_sizes + + # In all cases, input_image will be a DataArray (XArray) at this point + + return values + @validator("input_image", pre=True) - def reshaping(cls, v: Any): + def reshaping(cls, v: DataArray): # This allows a user to pass in any array-like object and have it # converted and reshaped appropriately - array = image_like_to_image(v) + array = v if not set(array.dims).issuperset({"X", "Y", "Z"}): raise ValueError("The input array must at least have XYZ coordinates") if "T" not in array.dims: diff --git a/core/lls_core/types.py b/core/lls_core/types.py index 3b14ea0..7cfe3a2 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -1,38 +1,38 @@ -from typing import Union -from typing_extensions import TypeGuard, Any, TypeAlias -from dask.array.core import Array as DaskArray -# from numpy.typing import NDArray -from pyopencl.array import Array as OCLArray -import numpy as np -from numpy.typing import NDArray -from xarray import DataArray -from aicsimageio import AICSImage -from os import fspath, PathLike as OriginalPathLike - -# This is a superset of os.PathLike -PathLike: TypeAlias = Union[str, bytes, OriginalPathLike] -def is_pathlike(x: Any) -> TypeGuard[PathLike]: - return isinstance(x, (str, bytes, OriginalPathLike)) - -ArrayLike: TypeAlias = Union[DaskArray, NDArray, OCLArray, DataArray] - -def is_arraylike(arr: Any) -> TypeGuard[ArrayLike]: - return isinstance(arr, (DaskArray, np.ndarray, OCLArray, DataArray)) - -ImageLike: TypeAlias = Union[PathLike, AICSImage, ArrayLike] -def image_like_to_image(img: ImageLike) -> DataArray: - """ - Converts an image in one of many formats to a DataArray - """ - # First try treating it as a path - try: - img = AICSImage(fspath(img)) - except TypeError: - pass - if isinstance(img, AICSImage): - return img.xarray_dask_data - else: - for required_key in ("shape", "dtype", "ndim", "__array__", "__array_ufunc__"): - if not hasattr(img, required_key): - raise ValueError(f"The provided object {img} is not array like!") - return DataArray(img) +from typing import Union +from typing_extensions import TypeGuard, Any, TypeAlias +from dask.array.core import Array as DaskArray +# from numpy.typing import NDArray +from pyopencl.array import Array as OCLArray +import numpy as np +from numpy.typing import NDArray +from xarray import DataArray +from aicsimageio import AICSImage +from os import fspath, PathLike as OriginalPathLike + +# This is a superset of os.PathLike +PathLike: TypeAlias = Union[str, bytes, OriginalPathLike] +def is_pathlike(x: Any) -> TypeGuard[PathLike]: + return isinstance(x, (str, bytes, OriginalPathLike)) + +ArrayLike: TypeAlias = Union[DaskArray, NDArray, OCLArray, DataArray] + +def is_arraylike(arr: Any) -> TypeGuard[ArrayLike]: + return isinstance(arr, (DaskArray, np.ndarray, OCLArray, DataArray)) + +ImageLike: TypeAlias = Union[PathLike, AICSImage, ArrayLike] +def image_like_to_image(img: ImageLike) -> DataArray: + """ + Converts an image in one of many formats to a DataArray + """ + # First try treating it as a path + try: + img = AICSImage(fspath(img)) + except TypeError: + pass + if isinstance(img, AICSImage): + return img.xarray_dask_data + else: + for required_key in ("shape", "dtype", "ndim", "__array__", "__array_ufunc__"): + if not hasattr(img, required_key): + raise ValueError(f"The provided object {img} is not array like!") + return DataArray(img) diff --git a/core/tests/test_validation.py b/core/tests/test_validation.py index 80101ee..bcf1210 100644 --- a/core/tests/test_validation.py +++ b/core/tests/test_validation.py @@ -2,8 +2,11 @@ from lls_core.models.crop import CropParams from lls_core.models.lattice_data import LatticeData from lls_core.models.deskew import DeskewParams +from lls_core.models.output import OutputParams import pytest from pydantic import ValidationError +import tempfile +from unittest.mock import patch, PropertyMock def test_default_save_dir(rbc_tiny: Path): # Test that the save dir is inferred to be the input dir @@ -35,3 +38,17 @@ def test_pixel_tuple_order(rbc_tiny: Path): assert deskew.physical_pixel_sizes.X == 3. assert deskew.physical_pixel_sizes.Y == 2. assert deskew.physical_pixel_sizes.Z == 1. + +def test_allow_trailing_slash(): + with tempfile.TemporaryDirectory() as tmpdir: + output = OutputParams( + save_dir=f"{tmpdir}/" + ) + assert str(output.save_dir) == tmpdir + +def test_infer_czi_pixel_sizes(rbc_tiny: Path): + mock = PropertyMock() + with patch("aicsimageio.AICSImage.physical_pixel_sizes", new=mock): + DeskewParams(input_image=rbc_tiny) + # The AICSImage should be queried for the pixel sizes + assert mock.called From b28e5efeb643a3f941542f4a11db914abbf340d2 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 2 Aug 2024 16:09:14 +1000 Subject: [PATCH 134/147] Override read_image validator in LatticeData to get the filename --- core/lls_core/models/deskew.py | 9 ++++++--- core/lls_core/models/lattice_data.py | 10 +++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 132c43b..754d46f 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -158,9 +158,12 @@ def convert_skew(cls, v: Any): return v @validator("physical_pixel_sizes", pre=True) - def convert_pixels(cls, v: Any): - # Allow the pixel sizes to be specified as a tuple - if isinstance(v, tuple) and len(v) == 3: + def convert_pixels(cls, v: Any, values: dict[Any, Any]): + from aicsimageio.types import PhysicalPixelSizes + if isinstance(v, PhysicalPixelSizes): + return DefinedPixelSizes.from_physical(v) + elif isinstance(v, tuple) and len(v) == 3: + # Allow the pixel sizes to be specified as a tuple return DefinedPixelSizes(Z=v[0], Y=v[1], X=v[2]) return v diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 5bebbd9..dbde3b8 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -47,12 +47,7 @@ class LatticeData(OutputParams, DeskewParams): ) @root_validator(pre=True) - def use_image_path(cls, values: dict): - # This needs to be a root validator to ensure it runs before the - # reshaping validator. We can't override that either since it's - # a field validator and can't modify save_name - # TODO: separate the image file from the image file path as two separate fields, - # so we don't have to put so much logic here + def read_image(cls, values: dict): from lls_core.types import is_pathlike from pathlib import Path input_image = values.get("input_image") @@ -68,7 +63,8 @@ def use_image_path(cls, values: dict): # Convert a string path to a Path object values["save_dir"] = Path(save_dir) - return values + # Use the Deskew version of this validator, to do the actual image loading + return super().read_image(values) @validator("workflow", pre=True) def parse_workflow(cls, v: Any): From f064ea089c4a7bc04d470c13397a520964f5d8b5 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 2 Aug 2024 17:17:10 +1000 Subject: [PATCH 135/147] Only use AICS pixels if they are all defined --- core/lls_core/models/deskew.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 754d46f..831414e 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -161,10 +161,11 @@ def convert_skew(cls, v: Any): def convert_pixels(cls, v: Any, values: dict[Any, Any]): from aicsimageio.types import PhysicalPixelSizes if isinstance(v, PhysicalPixelSizes): - return DefinedPixelSizes.from_physical(v) + v = DefinedPixelSizes.from_physical(v) elif isinstance(v, tuple) and len(v) == 3: # Allow the pixel sizes to be specified as a tuple - return DefinedPixelSizes(Z=v[0], Y=v[1], X=v[2]) + v = DefinedPixelSizes(Z=v[0], Y=v[1], X=v[2]) + return v @root_validator(pre=True) @@ -187,7 +188,8 @@ def read_image(cls, values: dict): # If the image was convertible to AICSImage, we should use the metadata from there if aics: values["input_image"] = aics.xarray_dask_data - values["physical_pixel_sizes"] = aics.physical_pixel_sizes + if all(size is not None for size in aics.physical_pixel_sizes): + values["physical_pixel_sizes"] = aics.physical_pixel_sizes # In all cases, input_image will be a DataArray (XArray) at this point From 3731a2106b246e12c5dd38e8b1a4abcc402e752e Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 2 Aug 2024 17:41:56 +1000 Subject: [PATCH 136/147] Remove the standard default for pixel sizes --- core/lls_core/models/deskew.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 831414e..2915078 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -65,7 +65,7 @@ class DeskewParams(FieldAccessModel): description="Angle of deskewing, in degrees, as a float." ) physical_pixel_sizes: DefinedPixelSizes = Field( - default_factory=DefinedPixelSizes, + # No default, because we need to distinguish between user provided arguments and defaults description="Pixel size of the microscope, in microns." ) derived: DerivedDeskewFields = Field( @@ -165,6 +165,10 @@ def convert_pixels(cls, v: Any, values: dict[Any, Any]): elif isinstance(v, tuple) and len(v) == 3: # Allow the pixel sizes to be specified as a tuple v = DefinedPixelSizes(Z=v[0], Y=v[1], X=v[2]) + elif v is None: + # At this point, we have exhausted all other methods of obtaining pixel sizes: + # User defined and image metadata. So we just use the defaults + return DefinedPixelSizes() return v @@ -188,7 +192,9 @@ def read_image(cls, values: dict): # If the image was convertible to AICSImage, we should use the metadata from there if aics: values["input_image"] = aics.xarray_dask_data - if all(size is not None for size in aics.physical_pixel_sizes): + # Take pixel sizes from the image metadata, but only if they're defined + # and only if we don't already have them + if all(size is not None for size in aics.physical_pixel_sizes) and values.get("physical_pixel_sizes") is None: values["physical_pixel_sizes"] = aics.physical_pixel_sizes # In all cases, input_image will be a DataArray (XArray) at this point From 2fb523a7e8b919e13416654d391f8b0429e1ac7b Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 2 Aug 2024 17:55:29 +1000 Subject: [PATCH 137/147] Get default pixels from another model --- core/lls_core/cmds/__main__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index ad866ae..84217f5 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -11,7 +11,7 @@ from strenum import StrEnum from lls_core.models.lattice_data import LatticeData -from lls_core.models.deskew import DeskewParams +from lls_core.models.deskew import DeskewParams, DefinedPixelSizes from lls_core.models.deconvolution import DeconvolutionParams from lls_core.models.output import OutputParams from lls_core.models.crop import CropParams @@ -140,9 +140,9 @@ def process( skew: CliDeskewDirection = field_from_model(DeskewParams, "skew"),# DeskewParams.make_typer_field("skew"), angle: float = field_from_model(DeskewParams, "angle") , pixel_sizes: Tuple[float, float, float] = field_from_model(DeskewParams, "physical_pixel_sizes", extra_description="This takes three arguments, corresponding to the Z, Y and X pixel dimensions respectively", default=( - DeskewParams.get_default("physical_pixel_sizes").Z, - DeskewParams.get_default("physical_pixel_sizes").Y, - DeskewParams.get_default("physical_pixel_sizes").X + DefinedPixelSizes.get_default("Z"), + DefinedPixelSizes.get_default("Y"), + DefinedPixelSizes.get_default("X") )), rois: List[Path] = field_from_model(CropParams, "roi_list", description="A list of paths pointing to regions of interest to crop to, in ImageJ format."), #Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), From d7c497e5c8c20d7d93ee11febf642d28c2fc592e Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 2 Aug 2024 18:18:42 +1000 Subject: [PATCH 138/147] Always run pixel validator --- core/lls_core/models/deskew.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index 2915078..4e4b3d8 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -66,7 +66,8 @@ class DeskewParams(FieldAccessModel): ) physical_pixel_sizes: DefinedPixelSizes = Field( # No default, because we need to distinguish between user provided arguments and defaults - description="Pixel size of the microscope, in microns." + description="Pixel size of the microscope, in microns.", + default=None ) derived: DerivedDeskewFields = Field( init_var=False, @@ -157,7 +158,7 @@ def convert_skew(cls, v: Any): return DeskewDirection[v] return v - @validator("physical_pixel_sizes", pre=True) + @validator("physical_pixel_sizes", pre=True, always=True) def convert_pixels(cls, v: Any, values: dict[Any, Any]): from aicsimageio.types import PhysicalPixelSizes if isinstance(v, PhysicalPixelSizes): From 0958c8a6602a574364e1e643e9212c2682c40f00 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 6 Aug 2024 13:02:48 +1000 Subject: [PATCH 139/147] Separate input and output dirs for CLI tests --- core/tests/test_cli.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py index 7f08a4c..7d96b33 100644 --- a/core/tests/test_cli.py +++ b/core/tests/test_cli.py @@ -70,10 +70,17 @@ def test_batch_deskew(flags: List[str], check_fn: Callable[[Path], None]): Write image to disk and then execute napari_lattice from terminal Checks if an deskewed output file is created for both tif and h5 """ - with tempfile.TemporaryDirectory() as out_dir: - out_dir = Path(out_dir) - input_file = out_dir / 'raw.tiff' + with tempfile.TemporaryDirectory() as _test_dir: + test_dir = Path(_test_dir) + + # Inputs + input_file = test_dir / "raw.tiff" create_image(input_file) + + # Outputs + out_dir = test_dir / "output" + out_dir.mkdir() + # Batch deskew and save as h5 invoke([ str(input_file), From 2f6ddf987c312abf6999c179b0404015dbbd812d Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 6 Aug 2024 17:22:43 +1000 Subject: [PATCH 140/147] User feedback: progress total and success message --- core/lls_core/cmds/__main__.py | 10 +++++++--- core/lls_core/models/lattice_data.py | 13 ++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 84217f5..04f1698 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -174,6 +174,10 @@ def process( show_schema: bool = Option(default=False, help="If provided, image processing will not be performed, and instead a JSON document outlining the JSON/YAML options will be printed to stdout. This can be used to assist with writing a config file for use with the --json-config and --yaml-config options.") ) -> None: from click.core import ParameterSource + from rich.console import Console + + console = Console(stderr=True) + if show_schema: import json import sys @@ -217,12 +221,12 @@ def process( ) ) except ValidationError as e: - from rich.console import Console - Console().print(rich_validation(e)) - # Console().print(ctx.get_help()) + console.print(rich_validation(e)) raise Exit(code=1) lattice.save() + console.print(f"Processing successful. Results can be found in {save_dir.resolve()}") + def main(): app() diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index dbde3b8..4ca77d4 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -228,6 +228,13 @@ def iter_slices(self) -> Iterable[ProcessedSlice[ArrayLike]]: channel=ch, ) + @property + def n_slices(self) -> int: + """ + Returns the number of slices that will be returned by the `iter_*` methods. + """ + return len(self.time_range) * len(self.channel_range) + def iter_sublattices(self, update_with: dict = {}) -> Iterable[ProcessedSlice[LatticeData]]: """ Yields copies of the current LatticeData, one for each slice. @@ -311,7 +318,7 @@ def _process_crop(self) -> Iterable[ImageSlice]: # pass arguments for save tiff, callable and function arguments logger.info(f"Processing ROI {roi_index}") - for slice in tqdm(self.iter_slices(), desc="2D Slice Number", position=1): + for slice in tqdm(self.iter_slices(), desc="2D Slice Number", position=1, total=self.n_slices): deconv_args: dict[Any, Any] = {} if self.deconvolution is not None: deconv_args = dict( @@ -346,7 +353,7 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: from tqdm import tqdm import pyclesperanto_prototype as cle - for slice in tqdm(self.iter_slices(), desc="2d Slice Number"): + for slice in tqdm(self.iter_slices(), desc="2d Slice Number", total=self.n_slices): data: ArrayLike = slice.data if isinstance(slice.data, DaskArray): data = slice.data.compute() @@ -388,7 +395,7 @@ def process_workflow(self) -> WorkflowSlices: from tqdm import tqdm WorkflowSlices.update_forward_refs(LatticeData=LatticeData) outputs = [] - for workflow in tqdm(self.generate_workflows(), desc="2D Slice Number"): + for workflow in tqdm(self.generate_workflows(), desc="2D Slice Number", total=self.n_slices): for leaf in workflow.data.leafs(): outputs.append( workflow.copy_with_data( From 371287ec89fa9839532a00c52dcc3dca3b01a951 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Tue, 6 Aug 2024 17:37:41 +1000 Subject: [PATCH 141/147] Get output dir from lattice --- core/lls_core/cmds/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index 04f1698..a2fabcc 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -225,7 +225,7 @@ def process( raise Exit(code=1) lattice.save() - console.print(f"Processing successful. Results can be found in {save_dir.resolve()}") + console.print(f"Processing successful. Results can be found in {lattice.save_dir.resolve()}") def main(): From 5d1118a64d02f6fb67a0fb25dc9a68f4f0de0c37 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 8 Aug 2024 12:12:50 +1000 Subject: [PATCH 142/147] Improve message spacing, fix incorrect channel and time in preview dialogue --- plugin/napari_lattice/dock_widget.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugin/napari_lattice/dock_widget.py b/plugin/napari_lattice/dock_widget.py index 0b7f8b4..4788cb7 100644 --- a/plugin/napari_lattice/dock_widget.py +++ b/plugin/napari_lattice/dock_widget.py @@ -79,11 +79,11 @@ class LlszMenu(MagicTemplate): main_heading = field("

      Napari Lattice: Visualization & Analysis

      ", widget_type="Label") heading1 = field(dedent("""
      - Specify deskewing parameters and image layers in Tab 1. - Additional analysis parameters can be configured in the other tabs. - When you are ready to save, go to Tab 5. - Output to specify the output directory. - For more information, please refer to the documentation here. + Specify deskewing parameters and image layers in Tab 1.  + Additional analysis parameters can be configured in the other tabs.  + When you are ready to save, go to Tab 5.  + Output to specify the output directory.  + For more information, please refer to the documentation here.
      """.strip()), widget_type="Label") @@ -136,7 +136,7 @@ def preview(self, header: str, time: int, channel: int): # so we made a copy using a subset of the times lattice = self._make_model(validate=False).copy_validate(update=dict( time_range = range(time, time+1), - channel_range = range(time, time+1), + channel_range = range(channel, channel+1), # Patch in a placeholder for the save dir because previewing doesn't use it # TODO: use a more elegant solution such as making the "saveable" lattice # a child class which more validations From ca6e1d7936ddcc43b81a90372b3e884708ad253d Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 8 Aug 2024 15:30:42 +1000 Subject: [PATCH 143/147] Rework progress bars to be in one place, and have separate ones for channels and time --- core/lls_core/models/lattice_data.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 4ca77d4..29cddd3 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -214,12 +214,17 @@ def iter_slices(self) -> Iterable[ProcessedSlice[ArrayLike]]: """ Yields array slices for each time and channel of interest. + Params: + progress: If the progress bar is enabled + Returns: An iterable of tuples. Each tuple contains (time_index, time, channel_index, channel, slice) """ from lls_core.models.results import ProcessedSlice - for time_idx, time in enumerate(self.time_range): - for ch_idx, ch in enumerate(self.channel_range): + from tqdm import tqdm + + for time_idx, time in tqdm(enumerate(self.time_range), desc="Timepoints", total=len(self.time_range)): + for ch_idx, ch in tqdm(enumerate(self.channel_range), desc="Channels", total=len(self.channel_range)): yield ProcessedSlice( data=self.slice_data(time=time, channel=ch), time_index=time_idx, @@ -318,7 +323,7 @@ def _process_crop(self) -> Iterable[ImageSlice]: # pass arguments for save tiff, callable and function arguments logger.info(f"Processing ROI {roi_index}") - for slice in tqdm(self.iter_slices(), desc="2D Slice Number", position=1, total=self.n_slices): + for slice in self.iter_slices(): deconv_args: dict[Any, Any] = {} if self.deconvolution is not None: deconv_args = dict( @@ -350,10 +355,9 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: """ Yields processed image slices without cropping """ - from tqdm import tqdm import pyclesperanto_prototype as cle - for slice in tqdm(self.iter_slices(), desc="2d Slice Number", total=self.n_slices): + for slice in self.iter_slices(): data: ArrayLike = slice.data if isinstance(slice.data, DaskArray): data = slice.data.compute() @@ -392,10 +396,9 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: def process_workflow(self) -> WorkflowSlices: from lls_core.models.results import WorkflowSlices - from tqdm import tqdm WorkflowSlices.update_forward_refs(LatticeData=LatticeData) outputs = [] - for workflow in tqdm(self.generate_workflows(), desc="2D Slice Number", total=self.n_slices): + for workflow in self.generate_workflows(): for leaf in workflow.data.leafs(): outputs.append( workflow.copy_with_data( From f04f30e75a3aa94c02b1267fc5ee5bc3e156aaab Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Thu, 8 Aug 2024 16:08:08 +1000 Subject: [PATCH 144/147] Clean up channel progress bar --- core/lls_core/models/lattice_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 29cddd3..cf0535e 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -224,7 +224,7 @@ def iter_slices(self) -> Iterable[ProcessedSlice[ArrayLike]]: from tqdm import tqdm for time_idx, time in tqdm(enumerate(self.time_range), desc="Timepoints", total=len(self.time_range)): - for ch_idx, ch in tqdm(enumerate(self.channel_range), desc="Channels", total=len(self.channel_range)): + for ch_idx, ch in tqdm(enumerate(self.channel_range), desc="Channels", total=len(self.channel_range), leave=False): yield ProcessedSlice( data=self.slice_data(time=time, channel=ch), time_index=time_idx, From 87a0d599a1b5071dc585304af4dfd047164b2680 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 9 Aug 2024 15:22:19 +1000 Subject: [PATCH 145/147] Fix bug when saving non-contiguous ranges --- core/lls_core/models/lattice_data.py | 9 +++++++-- core/lls_core/writers.py | 6 ++++-- core/tests/conftest.py | 5 +++++ core/tests/test_process.py | 13 +++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index cf0535e..d68b918 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -271,6 +271,7 @@ def generate_workflows( # We make a copy of the lattice for each slice, each of which has no associated workflow for lattice_slice in self.iter_sublattices(update_with={"workflow": None}): user_workflow = copy(self.workflow) + # We add a step whose result is called "deskew_image" that outputs a 2D image slice user_workflow.set( "deskew_image", LatticeData.process_into_image, @@ -395,19 +396,23 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: ) def process_workflow(self) -> WorkflowSlices: + """ + Runs the workflow on each slice and returns the workflow results + """ from lls_core.models.results import WorkflowSlices WorkflowSlices.update_forward_refs(LatticeData=LatticeData) - outputs = [] + outputs: list[ProcessedSlice[Any]] = [] for workflow in self.generate_workflows(): for leaf in workflow.data.leafs(): outputs.append( workflow.copy_with_data( + # Evaluates the workflow here workflow.data.get(leaf) ) ) return WorkflowSlices( - slices = outputs, + slices=outputs, lattice_data=self ) diff --git a/core/lls_core/writers.py b/core/lls_core/writers.py index 410eeb4..45513a9 100644 --- a/core/lls_core/writers.py +++ b/core/lls_core/writers.py @@ -65,8 +65,10 @@ def write_slice(self, slice: ProcessedSlice[ArrayLike]): import numpy as np self.bdv_writer.append_view( np.array(slice.data), - time=slice.time, - channel=slice.channel, + # We need to use the indices here to ensure they start from 0 and + # are contiguous + time=slice.time_index, + channel=slice.channel_index, voxel_size_xyz=(self.lattice.dx, self.lattice.dy, self.lattice.new_dz), voxel_units='um' ) diff --git a/core/tests/conftest.py b/core/tests/conftest.py index e3fcfa0..4a4e253 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -22,6 +22,11 @@ def rbc_tiny(): with as_file(resources / "RBC_tiny.czi") as image_path: yield image_path +@pytest.fixture +def multi_channel_time(): + with as_file(resources / "multich_multi_time.tif") as image_path: + yield image_path + @pytest.fixture(params=[ "LLS7_t1_ch1.czi", "LLS7_t1_ch3.czi", diff --git a/core/tests/test_process.py b/core/tests/test_process.py index 4765e15..99926e7 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -54,6 +54,19 @@ def test_process_deconv_crop(): }).process().slices: assert slice.data.ndim == 3 +def test_process_time_range(multi_channel_time: Path): + from lls_core.models.output import SaveFileType + with tempfile.TemporaryDirectory() as outdir: + LatticeData.parse_obj({ + "input_image": multi_channel_time, + # Channels 2 & 3 + "channel_range": range(1, 3), + # Time point 2 + "time_range": range(1, 2), + "save_dir": outdir, + "save_type": SaveFileType.h5 + }).save() + @pytest.mark.parametrize(["background"], [(1, ), ("auto",), ("second_last",)]) @parameterized def test_process_deconvolution(args: dict, background: Any): From 4dfeb7c1a3a566e341f41a2c0f73273b3d9049c6 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 9 Aug 2024 15:22:19 +1000 Subject: [PATCH 146/147] Fix bug when saving non-contiguous ranges --- core/lls_core/models/lattice_data.py | 9 +++++++-- core/lls_core/writers.py | 6 ++++-- core/tests/conftest.py | 5 +++++ core/tests/test_process.py | 13 +++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index cf0535e..d68b918 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -271,6 +271,7 @@ def generate_workflows( # We make a copy of the lattice for each slice, each of which has no associated workflow for lattice_slice in self.iter_sublattices(update_with={"workflow": None}): user_workflow = copy(self.workflow) + # We add a step whose result is called "deskew_image" that outputs a 2D image slice user_workflow.set( "deskew_image", LatticeData.process_into_image, @@ -395,19 +396,23 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: ) def process_workflow(self) -> WorkflowSlices: + """ + Runs the workflow on each slice and returns the workflow results + """ from lls_core.models.results import WorkflowSlices WorkflowSlices.update_forward_refs(LatticeData=LatticeData) - outputs = [] + outputs: list[ProcessedSlice[Any]] = [] for workflow in self.generate_workflows(): for leaf in workflow.data.leafs(): outputs.append( workflow.copy_with_data( + # Evaluates the workflow here workflow.data.get(leaf) ) ) return WorkflowSlices( - slices = outputs, + slices=outputs, lattice_data=self ) diff --git a/core/lls_core/writers.py b/core/lls_core/writers.py index 410eeb4..45513a9 100644 --- a/core/lls_core/writers.py +++ b/core/lls_core/writers.py @@ -65,8 +65,10 @@ def write_slice(self, slice: ProcessedSlice[ArrayLike]): import numpy as np self.bdv_writer.append_view( np.array(slice.data), - time=slice.time, - channel=slice.channel, + # We need to use the indices here to ensure they start from 0 and + # are contiguous + time=slice.time_index, + channel=slice.channel_index, voxel_size_xyz=(self.lattice.dx, self.lattice.dy, self.lattice.new_dz), voxel_units='um' ) diff --git a/core/tests/conftest.py b/core/tests/conftest.py index e3fcfa0..4a4e253 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -22,6 +22,11 @@ def rbc_tiny(): with as_file(resources / "RBC_tiny.czi") as image_path: yield image_path +@pytest.fixture +def multi_channel_time(): + with as_file(resources / "multich_multi_time.tif") as image_path: + yield image_path + @pytest.fixture(params=[ "LLS7_t1_ch1.czi", "LLS7_t1_ch3.czi", diff --git a/core/tests/test_process.py b/core/tests/test_process.py index 4765e15..99926e7 100644 --- a/core/tests/test_process.py +++ b/core/tests/test_process.py @@ -54,6 +54,19 @@ def test_process_deconv_crop(): }).process().slices: assert slice.data.ndim == 3 +def test_process_time_range(multi_channel_time: Path): + from lls_core.models.output import SaveFileType + with tempfile.TemporaryDirectory() as outdir: + LatticeData.parse_obj({ + "input_image": multi_channel_time, + # Channels 2 & 3 + "channel_range": range(1, 3), + # Time point 2 + "time_range": range(1, 2), + "save_dir": outdir, + "save_type": SaveFileType.h5 + }).save() + @pytest.mark.parametrize(["background"], [(1, ), ("auto",), ("second_last",)]) @parameterized def test_process_deconvolution(args: dict, background: Any): From 312f335a6e6ad3b80e4d89be334fcc65584528e0 Mon Sep 17 00:00:00 2001 From: Michael Milton Date: Fri, 9 Aug 2024 16:07:26 +1000 Subject: [PATCH 147/147] Re-enable napari-aicsimageio --- plugin/pyproject.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index 9b92123..2c07ce6 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -45,9 +45,7 @@ dependencies = [ # The upper bound is because we are waiting on https://github.com/hanjinliu/magic-class/issues/128 "magic-class>=0.7.5,<0.8.0", "magicgui<0.8.0", - # Currently commented out to avoid installation issues, although - # This can be reinstated once https://github.com/pypa/pip/pull/12095 is merged - # "napari-aicsimageio>=0.7.2", + "napari-aicsimageio>=0.7.2", "napari-spreadsheet", "napari-workflow-inspector", "napari-workflows>=0.2.8",

      y(l z=3LF%8(+4q`=d90ZN)3E7QXw#KcASG92}XCzZ;Cd3pq!}r-kXtQ^{o0aTiA9uucvN zR8aDNRhJx{-&?W*(5k5W6tS-i->lw4wMumyhmke=kORpoMP$Q{Nd=nzEAV+(j=-(X zs&Z(hI+ryw1{Hd#RAukvzMRFsR~O6uO0I3Ch*oJxT{65*96s zuvucS=-O0bimqXUUhSx$x%4e8=9)I{#H&!xB3A6K&l2R+ZX)u~EclZA$T5pNB0#u+d4%NQ+yx)f)*B{RipUq>^@sx^)uZ8-kX2k`hFAm?qt9F1K*QF6O*I! z`x7ff-9p1}xhB8>h*i){qjYZv>V>ET*0X>&b${qRaCkEk5 z@=EuK_VaLkI9N9|o)R%t}5B9xV8{aIg~ zU5vwGSC5p^p7L|~&OCH}f9+PVGCi3JM-;Nw0y$?IqlT{Xk45AL4Wgl5?=|rUj62s_*Zt9(f4==S zuj?@6YjbQ`>;8}Sz3=aI{Qpz?j`_PRKfjWn{o`+^%se(H^WWoxL*ol4=KHra=$}6t zedwPgWBkQrtbc6a)5&rFQ9GOc)#RYRl=r^W`iBF5nH=}e4tzFQ<1Z&y{nfS(?U~k3 z4m_9~_Lq`_{L>t~YlzBt+J`*X(qdG~=-fqyzVh$+dSSkA83Na$%01o}Zn$ z_UWAYhZ*ILjC^e};~$I;Z(Fo{*8okIM{oGv_Tx?9yOaGk%{hWqa`cbnyM@ z{Cz&?cs>|`w}*nL9Z`fn-1_qzdmxy4C?h=tS zN^~b*%XRk!(YNO3`HZFWgUXf{bN6d<&xdp61NnDvR->CU!W9|g${gF3cP`F+Zfzf- zg*S^UqPf)xLU)L(^ZKP+^LT#WA9OzuJq$(rJELOxSkAbjy{7DrhW3)o;lij`&yAM# z<6W@raBuit@l&rBzWah-%eGq+gY)}`9?&4ZdE+`Q$Xt3H&6D%RE^fgY6FbebKP z&Emyks5ZwdR_STy&0`X^>#D%(_D>8k?z zsNZ;IxH=rSx=_42{tmB)E345|h#wnXjV~|Lt%IUGo6k~lyk~g3;sEq&t_~LNK|xa; zDERQ3@@bqaa#-Csz7Kg_6Zr1rpZ5vA=l%1B_b;J3gJ3<$YWF={8JyI29>;|@*PoP% zrPwn#qff_bmm>V=U{>DQo`aY+eav7VrbW5fYF&@T(pCl8C=oREiCAB*%b04fttay4 z)$;)-Aifv7W8$q3#ly*u+Z$9CP0VhkvYz1TFyr_%**#IZN_9IQ`DEr~^|5k5{<<8o zR+y=3a-U#Xt~j=Fd@uNCuNJ=f=6&FwPfiRC_K|*V_Vq35NJ^RNH(m+a^%i9t?M|jD zgmtA5fn^w!;g%hjVWu?0PT{fP9;0TB;1S@{G1E|2xiJ_n#mZrAtmCv~on^v`!!GvR z{qb(4Xu-V|>xD1VJ+(SBSv9C=W#k~0rvSScaU5Rtm@*G6I_|&vx6tiaMdSzE7uyE+ z*h5%n^&RQ{=*>TG{ah3H{x|z!c~M^Xh5b%%#rggY&F(+8E(oOWk{^aol~>a}SjsPF zMWF`9?D2<5m&vv+K-QKVlg)-)^?dk@$V5VGi8S#K(JIct7_968LfzVxfL zm+A3sjXU%~YIzuVc$sZ8)vKL-#n8j0b9fy(s~eBa78g+6LOUkjW8(@%J~vRsChS@UMgy ze<}>uOQ|0F^Bj3R{MECqe=+cH2mUBr*;m8C{nLT(g>QQ${Q4bv_p>?rN8!mH3TO5g zVaR@V04sNIyL#+@h70>ruKuGia$gQB{&e`PUHSdRF!gG}pUPRg!+qTuM(k@j_wii+ zV*@{Ey>{T%@NGASpSwC-+=Y4niM)Ow_joc4+V=2Z*Qdhk##DXl+4@rWyvH)q13B}Y z9RF}Cx~~d9rW-2G{@Tp*oUq{U3X66{uEo>c8wPDp__Z7I&z^=KO{Lgr8S93O_+ZXD zJ0rX=yxQp*?eskB{^-qq@A-u$@cn=8hvh~4ra9;FJw7-#KCy6q|B?pr{h@sCKNEiZ zj%1!bAB26g^@{^v4`2Ogu(v(P+7;gW)4`Cs(0?`X+JTpnN4qqrz??r4gxwsz`S0iG zpXT}(!*c7va#M~zku~AZ^ZN_I_n+sThl9mG$rU?;pcmVByC?77o_qgZUOk`tJdpRE z2)19$nRY0>Csn48Wp>Zytlb&s^WoAj&F@{A*)!qqAImUAx6`M2lj4ecNM9A1;_b_MyD=lqXlb=Z-scjxzIncMAo|Mc{@_+VCx z9o^yGqy66gORpBb`y&4CJABW6{7plmZj(2vYD^qktQc>$Dn`Y{VrIM*)eBW9HtLA> zad9oJ7>29*Ba3^n)qH1c7dDMD1v7?6tIACNaP?=-CwNj1=7 zx6x^KtGcDb7);8#K|ih>KVP-tkS5}$FB7?A(P4QiN2V2~I4;q7sm(;xj?ki8Git9u zbHWEMYtKZ-b#sSJM_qAbxOK|6Qq>oe?_R|v_}o+Nc~Q@r5iYM-_tM}u&!4aO_f6ot zlm5Iv__c+7#-GoA{g_;_8VuPi711!m5^l{83s-#)9N!u?iI5mM${{caQ&88WV2hF-xxD2 z94`V_4y9HOWAY$;A|sa-r*W_Rw!88}WCY{}Xw%tuOd9^)4^9pbw={RKZ({dHZ}Imb z2Q-22PVskN;Cp;A`OlRhV^MW_Qu`?z&(r7O^978~ zN-v92&F%(P0CgjFLSnVw(H^6$zKRyJSD99tnu^-hn=NUN=2OUusa|9EYu7^2%Cf8% z<@YzQUu%swf$v|vd*MB!?zdsQ!{JOBlFNbA*G0gXO zlJ}wwexeD{s{{i)3FlKg!<8Lg)?hnvFHUlca{z8t$TxwGAw@y^U=d!C(@ zW4m(fBRS`s%;BPZ#;Y=?n=?Lz`B^#dp{ULu%)53sxHpvQbL@$%8`otmcrhqesq)%^*Co&PVxHX{d_5S{%7eX_yZ>QUkKIB2 z<++pEuieRUznBp}8wB5(89o)Xo{?uC%DvQ$J(i!#^8OP!^P+T5-IaYwF3Vlitv#N5 zKbU_X$~fm|CAcUVvd44oHTn11ymw*#UY`uv2U_pW@9vM@{PR8kR}=W|#Gm&EzWa?o zpM88k9K?MyXn84U`%>`q>C|z3Hb?$2=zThvd?^;)P9!%38~-h}qTh&>y+0`bT5Ru2 z!RHr(rssm$zi9o-fv*I24`h$wKMOV=YGY<#;Nh$oPiEb))6v5@_HusT5ft5&(eKEe z&T6j;8E@9qe1PL+oM?xE(zl93-)*B?$2f{`x#u5IlMnzj?ak2 zy(VYek$3EB^r=|ub28I&a<4OE#m~=?dvfmXe8LZ8F29}MXU1NCFgu!Dp5yn$_qaAk z&d=X-qhot-6fT!_hj)+md;9B~z;`GAyf5PKY%kbf{`u_3-#Dq0lF3#9LdEU1sFVWz z@Vpk~ijwOe9!-`RuPu9prN++bn4+2lAMRZ}A-u-JW4iEPvSN5L9VP4=tXqWpiGAgq ziz~-^>DhvxJ0@pM*OFD=7uk2S#mPC}oONhf)jBkz$?l1{so`~UEw#CffO!|kK#^M9 zof*lJ** zfmII9Oz7Y@?2DITb^ys`Ep9j+PSjod&W9a z$(+>gu2!9gWu_`0ZHEEp!95#Zxh~e9EtTogUELWnT6k`Eu34!J@k;%earhMcgf+Rs z@56Jw%$U87u&?H=C3p{6mUEhh&C(QI;(+-G4`7ZPrhw+e&o)&KYFv@hrFQ) zd>@!;?Qg%$bmp-^cBK9W}eLs^Z8bf_mejd!Rr*i$1iH7e@ zlz&xX?ytA`uqP9He=a|_BzC_)EcKm83U!1dK11`x~x8*ff&8v%Z?PoI6OA>RRmzn=w z#=I*0+AW#oBjE~nCVD?VKiB7-)AQ=QRG3_v@gM09?;hjt1#f<}@ZBf=`Q+HdJ(;(POs9TB_oiPy)PP1i{u7aPdN(Z#4KmG_o4W;N|cq(g&jI^_t} z$;KR^ko(Vhepud7H_94|;7@K}<&N&e`c7r6)v?K&^Oxp^A3ojTwY>dOtIAaKIm&Z4 zzP(Xaq%ZnWnJuf#%YJ)iuq9*g59rrK{WS2#W#e^AuGyHY-i-3^uCLG69pJs$@7Cfb z@IAbrtzMMZ{ekb%$6wR*J`ce7c5Xy)a>7*G>7(!^zuo1{5!DqqtP4 zrFnr-k#y}CK%G`=gV>uwY-`>#Qmr1)i=C@}5fwAjnAm^JUZUb|E6rhfwY+`jjk!Wk za-Ei)uhTImZgqQZ>c8!&q_0EucU_xxfmTkZ8Q#33_E+*u->72ERIi({*_x?%z-mSF zKHa~?IcvMayGQ%IcS#fY9_)Z`Q=R+%`N;6t_)s79N1pxsm}<*w;_qG`UwC&sy zrVjg$9wr+s(4-|g|1ugs{Q&EGF3k9Jx7=BsnX6Y=&h&Na8htG_j)eLbVy zoU7!dbv?W@@5+BWdSg83>+;Thx%;*Gdv0Fcl6c^ec<>kJ-5tq!ZI6fk-i&!|KHu$$ z4?dcA&dtA@^UfLhxgoD*&PtZ-qKx)*K2`TeZ}xl7kxk&c6MxOpOaBSiH{=m)c4}5K~MZb1skaK(bJbW|Jw0i3YgY0Ybqs!=1$#UNkT>nYp;`@Tb zTk`I%VEr>e)YHMoGs$s38{`iUToPo$=KWb2zLodr;9ko0U(Dz`gR{#s+8ueN#`})o zXLqjreD3pPF!@*zEUy1bM!YMz_&ammO<5;y&fTuenRF-jXPirO-IKY?y}9nnocn0j zh>P3zxGhJY4o1&TZTFt!#IFsSZ^%^-=l89-!>;7J&&%&i^ZpGP|C}6Ezy9&ei(2ih zcq%vN`K{@ndO<$vP2J($qy65#q6vI=%76EVe?FYufd}X9cZ2>pznpiAQI=W8SJQlp zkaZ-b2A2!Nrpt%%g+<7`ZhH+Z&2>C_e!Bb?&A7OhXRjhmwxTyM`l#0wL-z} zmNq`uXC;xtca=^QYRalI=7iR7=9$l8t*Vuv_D^!8x;e|LxnuW7Z}?tuLKFBN>RWt| z#&S)L&EvZ{e8Vz?Z3;FqRqSAOFe!emdMrSxT5EQ`P+nXaJz5=uW;^k+;*h5v3%j)@ z>j#YC$Y4@GM=VzDEd)h4Z25PYbvbn^I%|R>^kq67v+&hhk>&=vtq?NUP^zJWWP;y zrcIxl`R}pe(TRog_Pas<{LVzAHziuVBk}EviDqS>UrB^~OXBi}^3JV^SMN=%`t@Y8 zFU+xr^X#fb!_?if)_AxVa^0UMK7TZ4d_0l&&Ky58k@h1Q^TzzWJFjs*(e|V(<4uX(Z^^4O!@WHccHq{`>gvpDM&gO_}$1^7`%^f3B@8zcu%|xt-a*F){o%^4@)^ zJ~<=fUY5AseLfpD;I5o|b!L54_9*K9=*@mF*w_TVJLSjBbnfGObZl&}pY|7=eg6FJ zFj#j4g?9xN4+YoH1vTGld~BgTkFdi?NfRG(XeQr3kP;n)`rV- zrwcO=xO~3#nt{hsv4WjfJNkImhez`2#r&py`EKxiWv)9j>jG8~gRZCRSAyM3a_quj z{)zm)CC62x+S_PP?r>FpP7D68&NG#-H{|!HGv90T`lI3AE=e8A(|L7E{x9s#SRd*R z?;h=UYikqu?qt7Vd+y`A-|YA7>r?rpJlWED@O$Y;Xi7!eYf`sCtw}@bJU+1)9m6Jb zO`&Rk0asN&2Jec;r*dRPJl)N0evbYG`$oS?_gV3D^>(|v(wOwmF$YFrUE zb&a^*DA=(Ra@P|Q4T$?i1Jo&0F}hA*1YCtPSd!US?+A0^!QT}@ZHHj?+^RkZ~po0>6&BTq# zG~ur>S?WECnZjCChRX4&U;{d!zpTIRhn1spl|F{f!$4v=HBWAv1aH>yxzvlkZV_OO zH>k(fJIh+t{n4BK-u|v8@ZCwj)*txpH~re|q4s* z%W6>hBbZUgWp6<|dm)XswYnKXi&reBZluP8E}a>@b|<=ZJ>b-jRqkA79X@5=mj&(S zA(LLc7^|A>#Jr>4v=DE8M%LxcJopdg1IXQ5A>{Ci`8O6NtmB)ra@8DtVxQdq1>U;* zJG1+vH+*k<_p62P{?NCK4vr0v_tE}>v(K-w)oLco-W?f?_?cX;>cpRagd6Zr0w|Lza_-Ea7wef!O`R=rUj_f^LF7+{|7#sxXUd35Nt9+(q^MJBx*B#IS4-JKdj&^*$o+mu?j<^&W>LnwHYS8OsP% zFR%x5`YaSf24nnlYlo=0W@QEy#hd*Rxsp%L;zGNaA3Ayc))$^S)Ux2_$9JfRzG}s) zg9|HebUofxy;d9mL7gAH`R5BxYXaY6nbJ%wFWUEd=CL`7@A1*0e(H}r`}k%Pedm|% zLlGNF%?*p#WmvClR`KCDW{fv(%DJUL!=IIsoi70MY^ZK4M`dJJ+W!D@tq@{fd-+-k z)RMm=$h9uu#Wn_8?C0_yj_zK#@n!A5URjSY{QP#7ms*xaeo5{q*01>%$Ib&eG50F| zP|mCL^FD>O1aDV1zx;~I$*DPiHTd(^$C|)*C;M#`>H~aFjLz#*hnno)a$&lhT-~}j zYO!0R0ih^)HkyxbMiWAxc26`dXQjvJ?&x23L`C+cC{1pP+Dpx*8m+7H{>4#!-4)%6 zj;U(VZpu5lx9rLFpN;ge6>j^gBNEnQi^8TE;yJLB)~%$1k4{@%c&>C3Vw&o0Rks+HR_ zd!3S>jsoYYoO4m;d2a5dC+itGN~uG4=X2Z`m5kmn7iA_-=h}-ikL^+6U6TJV9|xK) zkKXL}-ZNhxHa?HE1q7Q@oT>me7U@yO!t=dQL#eRwb94Z(zDSOfAbF1uj%k0 zC-47+ocyvb*mk%#eD67@34C|r*UV||+waklv7zDl{!9((&##VszCTv&vDnw|Mf?3f zq5{1m7S0~rkLKs**iZXxe>E!e7h69)@ab6S-BF=FA6t5Ftlno+8-8z8+YjgG@!0KK zqfFfqedq%@&z>TWrc2wmVj-`LT|7VcxGU%BobX8gUKL&H_u?m97mwk(9MvQ3mb`a& zx-R@_uDc{>-yf@ePVRSa?*7S~@nq(-H)mfFuVPo`wKLb6q9?Q2}-;?dZ`t|v{ zD|mXU{qMrOz9KleA{cu)IC(1PT^q0Yks#)i!P)NzVHf1O$KvVU9K7kaa8Z0<{`rpV zM8F%@mEpRe^z=OYWN>?78{fBO7Iy}XSLfclTkj0ot_a?*jyL{55Pwxh`)Ioo<&2}+spMt)n4Z_8)cmS^1`z2SS?MNQy)Jky$Ks~6>UfB5GU<0BIj^Yx((;u~{L z-}Tn`qpRb~>S?AToc}C?gsbMc%9hDa;mvr)IBWe6v25x-^&;0Nf)|dF$Ck^O^ON;R z!{Mz?%#3N{0rQ|`&}6tVSv8)qDq`Tp%f@5Dux-Vy%f<7>56xYSQ&}*4pZS_G{!SFZ zzcwT4ayl4)G4%Eseq&xAm5jG{-BB%i#Ut3ksy`JmG~eH0#bvJ+zO6(vF+Vr?^NHb+ z$?^>=@{HIwHB;B;`DyW7&xv2GX48IqXU3YH6I*M~u8+k6s@A$aHu2I} z+FN5e&x`FlE0+7hSiy7hk5#=aR{!+;-4;vy-tb>KAzhJiug{$>%k%f8R_kN&qqk<< zn=;0Z-1n22%enb|POR(68Slo}+k0~VyJEM`j{UzNSA94mzN38~m15QN>CE(E`h(o( z{pk;RPMb|TJEOckcRDTSzALNC@5C0Lo_Vt0AI+=l;*svF* z`pov!{5|zm!1p0ny;}H&?Rk73ziP>wHf&k5bxZf!U{l8%~HlWqq+^MHs?I~YjzkTC)34hvb(iE5zAefaaz_=ubZ2? zG4IHa@hMcZn2BD=#s0C%SUFW@KA+FyUgm%{k8FuU$i^)~G~{LJ?Yhm|&CYo94x+>c{Lw4#%PZD~(b4+jMoROQE{_ zX*Dl2>AI@H@Wwowiq&775nvgAUn>RQU5~eM4Zo5zJuJ&;b z_Kd@v$q)i$Kp+!H6h(>ftC2x+u-x{yZ_wnM~s(7$zK@1{l!uG%j5Z*XYcXit#|$W$oCv_WO^I z{}p$ZXl>WmNA^~?HO|%w@XwEDW}CNmd9`QfqLnX=irrq}TFK55>-VZL%inqSSnc1= z6KO23Iq=%pjpJ44SF*9a?jP2yci_=`7I-m(P!kESp;)+8HC=g`^Cwh2I!;D^$zZ{(k@1_KA^Awdu>= z)~EIvI+MG*iO$}4x!EgpwCNszttW5AYsZAz@vX;c6@2S$dezpv%=@L(mak{uQDL4v zQvP@TOW*HvXL()ke;#)#kwLHc=J>N3{`B_;{?^|B=!(|D{>GdfM|F&EwAeTywqLoa>v{XSy-k{_|+mw(fvjrgwFyYX7_c+*|$q z^}ln9{C?_CS6{vL_`$Eg$Hh~}6&BA=`~3drcNtoq^=EZmebuha)F{?m{n_5%Ih5sK zb#QI=YV{XKZ@QjqZR>efMy>Zs_g^!~9WOp~b~abPU*1&LSMSbw^B#2!xOSGQT~DoJ zOZ9u>;InTq&lu}>s%LxDIV+8bca}t*r_BmW!4@BW6bjcpxYx;9&F?XJY!8RgaO z9cLPCe|c=F-_UB?`OW*xjqx>p-+T0VKXv!?zQ^^cFaEt#F%TZm&WY!Z=H>beQnG>|JcIjrZu;PoIse{mOWqKR@Qke{KBtwK0eNv*SJ98ujOw$FqNRylS)OzcT*(>d3zC zTJVLj^_RydczZna&GE^8WBleXj-0Gj@Y%C5yyma}!r1nUXCr*y9PiVJ-7k*M+MM=R z$FrXu|9xqE%dYvyF*E%MvfRH`nohOI_z6slT(UM*qHNjJjw!pNNFxvP;o&-(eZ zD?EK@#EpbCD^-j7PmfYvd7*2xm1(oxP-~!ajkWboT~Vpm>GRYRpR-u?7g~YV9Y(wM z!>7l4be?Nxf6sZnzFA{^T@z}~(KqK+@4RzMdzIP$?!F9j^;@HA)$olB^y%;Zb8qeQ zhyRCDL{`1j>Xm;<{ zMos?b#;g6($jaBpGv64m`R|T8|BX=(*G_oj?75fES^&Q`KJ71!-~ZL|TYq+x`{Kyw z*T*}3bv*kE3`q< z{O*1tzj>A~zcDiNn*q#&_{PYt-y9`sQ`d(6>?qgu9lC1D zmq#vrdHm)V#~5+UAYPePg_P|NFV|eD^(T zM(gINrR(!Id;R5+*IyXNfVqP1jZv~s^Y*B_tDC<#KJORCf3-6{Jhpvfyvv*8J-P}+ zErmuFTTgTS?&*Dx{qOJmgHz=9o$Bv%(mp?D^JDivzZ>7G$SR)N}1R76z4Ln)ChV zOnTe(|c7 z{Mb2Lzjpt}&$~+590Tv1uyUY`Y(}~~?%p8HQI+wtzul3bUUBP3=D2y;HP@BTdkM~K ztaYR)`&y&&{CK_k&Si3=(v4+zmxR{6mvi-a=UUV`uU#LwG5g9^BY$S}-8+8F*|28p z=bG^whH#p%Ghes51eH~Ye;&oy17xZ z`l4O&yqWUWu~lzBH%iZIqLiQId~0G_;a#2FYgV5&k2Xic%G|lWY@Ww5=fFQUa;?=X zt%_;vtFu(fwa<^t?U>M9cC~xC`NF7SZ;bbC6;8R{{CWS?I_USG8zs65V>8{g4(3dH zuhN|vdeu2**Z5jjee1YY%cW7cR=aj&Xl}0hyVgtBRKNSrz2*1UzjKQGzA`@6J{NZN z*5m!T2Xg)SQx_h;_~?ambpH4KufLlOZk=amZg=KK?cnOS>fZXbjZ2p+)pws5&&~V$ z&b6Sk1>ZRu9~rsT8CTV-y+S!zJz6x|0?j*h-=Id~t5Lg#YI)c?u`+Gm?V#GcS8IJr zC8|kVJ=@tJ^UROiXO7i#Zd`q?f2kebO0DOVI~vVZu)X(OnOEziBTsqV z_vsvqhx27RSR}5NBLYHmf2-iE5xdKtAD%d zcxR7wzrlLMt@ZAhP!GR5m2{NpIMZv^255|{UiQsVqLHxHug@`pX1uC-TW!)=Uqi^5Ysl95mVsnPB*DYHc4``mPmPhY4d-%Q1T#Hv9qN7pseO+7P^y^dK^?#fq zzklM8%U4V8@pBIH`@)6MMwj2G%kS#3TDzSIUAwg!>|^6`cNJ)_Zxz_jjlWwv()d+*3CA;FpnqA?)RE~`p$ouqYN`|?)2xqwa*{^$EV2ef9;UVS8qLj@bmk^ z#fujnJxAAXx&Q6+GNKIm(AcwQHqI3&^=!-5vZ_{gW6aGbeQIPxXM*3ZgKwYdb$eWE z_FX$3w92*fP0Hbp9z9okyn4PYt6e^`^Al&U*EnBwb0zAXHxl;R*%6`f`94qC-T7yY zy4CjX%&*?1I=3D{^>&}Dqf@Pj@_ml4)syI$@ch}I^9;E9-18fj)wLvQdt`C@onE|C zV&8i1-tzmQe{zcazEl7Ee2lMMRDYj%eM^1j>hSjY+PlplH78ab+x}iI{pqpym+4*K zxcacWg4AnmrT1L_UF}dW4( zt3x!xU76lz&WV+KjpofW!>Z%Ee#CsQc|U|cZLiViZJeMZNuRoV9`syCgW4XQnbN!U ziLQ@1@wT=?{j1}*x_im&!FSepz45#M+*^Ks=l?oIem^}v)?Qa%z4ds1?!g>iyZGdV z$1gcQ^n~Y!wm)~4NA1)(FV?kGs@WPZucoesE?0kM~7$-gj>Bj z&s1r(XLWG-R6Sl_w~_Hivnp3_xSn{u{f-fHoUS8C^?kK&|J84ppRV)3`d{arb{uMi ztopl_K;vNbD?0CMu3>#+yhFcR7B~M^&%C~Pweq~PN+SckPVI$81u8v9^5+=;JP)q# za`&Hm%kOXh->1m$JGIaIqz`_6kKXsC^K^db{huGJ|J)gpUps3H|J+&rl{cRpbz7^t znj>x{OKZeimtJOco2Y-Gx)!ZQuZ2rwRC>rvEp z=)PuioldQS`T?E0TKk~$UGDyKZ~6W8|MwL6eW&{SeDuFBT)J@S@pE+k_x;cBMx7fI ztL^=#&N6E5@gF^FvA54R)6^Q!+Rx=sZSwl)wTC}6e!G$IuC>_tU|n&sHhDFE=U28; zru=F~`}q;gJA#+7A0=Af^3m~``UGWjzt<6`zgwp_`xa$yuhg01jnH>A>(kEBfMy12qtwGVegFH# z-}TSm;gIor#&gf#dj1b?f4^`m{{7qE-81P<_4oP6?~9i&Ja(S$4}Aagd)9i*QlRy|kmyqu~;bz@mkty=51_HC;~8iViL%8!gky>hMWYUS={_U2iM zTKJ7(e{!_rt4(V~HwV}FcG>>+DAf_9yltiHoNp^CKWCj^{#BwQ(Y)qUE%e6F8>4Uh zpzQt7`2D^`ztg8Is^0F(FGXd2Q?1N)#Yvy~m#tfSyZ{^F~f9|dRe%J3kMSkCD z{`;Kd_hT2Hy!e>;@6*kHS4Y;`ZNB?cqZd0zm_BfpIn7{Ib2h5{!r3$PuDPv&DLg!eWusPbG5Xa_ny~bEW11QbU(pa zvRtk$(KqU7)GK!7$X3VQ{pa5D`{Cbziu`_Ne5}2%eRVuOANl?G*#rTFk94Yc=@CM+TN{_2J8pjtI@Q)gSNb;*~5< zUO3C)=9Xvfbe31STAf@iS}k5a-xy5?vpeo@YeY6c2B(a{M_0Vp33b$W9xKhyZzMp{m?&miv0f9 z?~vc+`=0X;=KR>WyWr&u=ji>B4c3C)At(47We8+%(zZ|W#{fUv^wZ*G}TPs>_S7*L@)?VnkiapaYrk-;n zZv9tVyLD`}(HkkN-~6HRxZ_Cm`D_E!qNuIV*j?{gJwNBux<=%jFYk!ab7gt&-nW_= z-go`tc%^3QS_9jWqocyh;}iEAcmKJ!`ujV7=oI<=Cl9%N_14FH@bmkL#~yv`QR~l7 zc>T4qp%JS_y_+BEdM4#c*;X66YZ<*V{w&MN(fZg$m8oSt`>%6#X=CNjjcw)09J8+1 z-bh+Uh|cqD2J3?(yBd3I#0}gb{wde z&|L}2*ttHZGhn(ddj03>;pWgfy3{u47|?rF{)1<~Tf4pduNU#rk^5zTtJgas)xxh` zbN8Qn%kOXhi>Ju%JJsKP(g#1k&s+U{|Hs#=#~MGcR_;m)twgPctLz|q$K&eZ z+6VQA>m$#0{XDz09KSK@)K;$C81LA)cdOT0Q&WC-bgF&Pqvo$0A*?mfC+XG7;Q9ld zr}pXbI*sgi9(hNuzG=srM)Thsr5h8N_fR^0|NC2i_!Rklr}4FN`@zre3zsikdh{I4 zkKO@6Z)LIi)`+kmSHOEKcd){fN zEw$Ze8>4*hxb&rw)4fY$YQ6t#pSSAe_S{&#^D?|6RU?GmZ}AAf8a{215 z#}9sfKmO#SV}8#aSA)U$6 zHC(Fo=eh2ElJ1gn_n&)fpTGEHr^xSj-OA@aT{(S@@_U>;_L%kOCtQF2=Ge2V3qNp{ z4b3Js?p2LgAGB5E^-XI{mkHH?ohMpWRfE?epJUneSF8Ox7i{*et3ey1YE-;7Z+X>u zW3%R+bK}+YwF}Dl>gPt!tEC&0ueV>eR^qE8+iLfB3@Ll(^;P=Ka<#s8BX{%F`Wzh} z8mX%#P}KF+diToL2C3ejJ&IPv*0Si?&OqxMeSY*88VR^w0^Q~3-tzlhf8-STeW&rY z^8CTCzt7wF+WoJ;=T%FZ@2&2f@9&;rtzoIgEB~s6KRC9{ z8LHagvksmaTmF^r)s2mS&;E3+Whk3u3n2=PLJkC5YLZ==;tw#d;g{!rzJOw> zo+7{Bb;#wbw~j#%ettjx_@hrf>i(f896zsxI_H#YJ=db`OswkY&Y-Lo`{H<}zVN(5 zch^R()jns9%Y^#Qb9MP#!PctokB|01IoFjUYVlVWS4UREH)=h5<8z%#E6V05+?-{r zS5UjU_w1cI#xy=K$Ka}|o6DQ4TKeR*t2_U#yN-9X>a5b*^Oa~EufOMdo9?dG82_yA zYlC;yh_-dbhgu5N?z6YjCp!K0Qy=>AcPhWn$@-Qj9)0qObJRY+|KsN`jqEL(%8sto z)Tnv;d*^DEm2*U`k*{ja?$*_vz`K%DvtZ?BdEe@m#-OWLuaCc*mzrx`sxhnaYXdZ5 zT^-q|+Vf|<=B~$9Yx=|E?|SpS#uvu+d3~miJ+dH5xRv)8we;HfrzHP0do%4WYb3KvT2VE_y73ytkbiLL|J&I4hlm#oOoW`_B31TBx;RtJx~iELk=2Tz_8nG{aT>-HMbla9$O$^EYcXx8ALm zbmQJHj~VbruI90%+PbW&|6N8sKi;v|@2Jsl*XOT&&!_CBFOK?d-ov|`dTHcDqg{=QHzw6p8>-nmT6|&r*SgcTHm_XXbWV76Y$I1)&E?IJ zUFBG-$2+prLY|}Rtx4_uYbDQXJJdR_-CckD`iMIYHM-X8SFgWz_IyW&uJJP4|BbVM z{Oq?ngSFm9ZInJ`U%Y#I-{bv>zWx)Z$nUG;bM1BY z?Z^9b4`%)S)T5W5aQ*WiKjHjX^;CIK{Z(7GY^lXub~hqE$Gp3HN#kf`U{_RW4yw$k zf8IKfc}3LQcLSgGZ9UvM)>ST6OZE=6o4f95{r+aJ=KAsZ?z4|w8@;->^T}(8*JH2O z-Ere%V@s|0+198{J!k7`kGICXw)L!g`<&JMb6&3FPe+>BuAg)G^XOFze%}3{5AhWA;0^i=ODkIxI9*%pP%(D_rHB!=9SguO>OFGxN5F) zt@d~$($&k=o3+;Kf445JKKEPWw`zlzZRO!?Rey5S)jeu1tG;+wI-GYts*Y}DOzr2^m#U1 z$C&yMT@|7u*&Iv!()cd*1)4)^T(H&fed4)d_WAJ&^#R&)_n&*q?{~fXoyzZW`yAx= zh0BjW<^ClntiS66mrI@NF;|9HV^>pFvo(j^_;c4q>&`&sOWD#`TeDHE2djK7?(5?> z%8+@NlCre^w9bHz%t@!uUsudh5; zpS8ZVmcs0F_x`o%8w2lX)OYB3(s8SC!0Y3v(*}F7a&=|p7 zHFxU#e)v77$nT##U|nwJLYMc$N#bm{G5Pw*Kc@ zx7ybAk{cy2+sgg^Zd|PK@N%`xZM3|ef8+X%19YtFNHN#1^)8L0)jsJx=BRpQXFtAN zerb&2_fCDwzT+HaYt3FQm%IPmTmAjedry(y@4iERpO5jir=GezE`hFne!AdfieXNgo-&h;(5 zQ*q_Xx_Ja?_Pfk4^QuX^7EIT7YvoLH=dGQo-fjeZp2s?S^p)>(m9wpVtN+~iLBH3r zq@3?))9366(W>$n#>n36Uzgdn;5!et*2C3ov2Sf{x!*WKYxL^L_YE3x zm~-*H7GM$JI_~j%uzq z{or_=xn{Ky@M`j|?{IA#UFui2`edH@)fFYm=<3{Zw;b=C`UHK0o^51o_M#il@7hrv zuX_K!Wp#L;wo$}Zv6Sb{t9LZ&n|H;B`U7*s@YMPJt>;gX-**~6KPT;T_G*owzkOD|94s5lzgE(9#_GJ5OF7#r*jB(b+SWBvd-Yc3RMXZ5 znB#1n>rxwIo@v@=oO5#Z$UAn-Il#v3`cCEdY_rTcfH^x}+!5kacTewoo&WyWDf0VH z<7?+5zb`#@>GBij==u)#e}1ewxK*8<7gl~ZPc+w&)Pk-T-K=ug53Xdb>ss7h;dqXr zRd<%DtvP9wdbZlDjmx0w;;wVp-M^}D%lC4&_37o;ygu4o8{WN7IyzK4mzCAC<=L!n z%h$5?_WW2a>i$>O&dlvNGRrqk-zS>=@8-pt8}E}h($|q=MzviU8@zqxnCs&>GYxv5 z+8lTPxwrQDi!YuczyFQ#vG%_9)mxAE=N`=UEtj9T^!Q`vXngI5;r!5A%3Xu0c6asZ zyvjrM;vBc0*F1S^;y8 z>iLd5joj7$uKsUbe6{@?_3yKET`X?52!O!of zE?vIxiw_3%hb-m?3#ri8MSQJC7S)_)|poiR`1oXEsv`4x^qw?+|6>$k+S-{ zja{|Mb=L9K*Uh9=qFJum2d|$!SN+|YYRz4j@nzM=AA0Zj?b`0wMyXcEb&Tj0%h)o% z`Ma{b>ozxjUj19&x;eSp8_l*iHc(HyafGtA{O&axN1yBO8UviGS38pR%Jl|%w~vn_ zPd)Ot&U*YEWt#Cj{rdBVKYNP&zIw>z9-oi)`BRTye(Z7Qhn{f!yt=9yuXc6W+6wW` z-mHaO4t#X9%^TmU9;{Ve&Dl)Vyd!5d^z219N>^XJzdHkb)_|>enXT=v%2G?Ye4Iyx z*+Xwkt8KNln;q-(bPd-2{@lp6*$e0z5wku2{_%V*gVw3cmGjN(RbzKn>$Q=gU9Dx_ zGpXZ8$C8dYU6H*}@p|KPO-(KE+WhtH%k_Ci%p5OFYI?3Q?IXH+hF#ME7{p;b53hMU#ohq zR;leUYvGPWjeA$`e`UPmM@E_1=PqOG19ZN4$BnttZl0gkck7!pvNl)Q%{D?upW5bg zB(9qN#k=HoPv2X9f9Lg6=Krqu}8i(`e!9BZn_+BqL{_P9QIGilY%FP^p7yXNrc&+@BQbz@ewZ)+)+h2>6J z+X#I9`{HW&`oy&es-5f2zi?J#m+g&e*RJTdTemWg8@*Efh0a@PM6f=1M}c{C=p2*o zgxHyGjjnafDTC)b_xBuE?~^`1{;R#-ajWA`*QxmI=o_@!u20qx>h$gNZ@+bl{Jzup z+WBaoU%qtdqV+AOod2%Qu0LCyT~D^vF7y1*#-CIp2t{QuNJnD>%+b#0i)}5hEuT=@j{Wr~3PRw9hX;cIk;r=cxX^|Lyb6m#&7aW~*i_*UPXnqde*U zgRLVA?sSIU2nYlv9)H^)wP+cg{x1?<~b@h+vi=st-7l9s=Zz7xf=Sj z<2~vX)U&VlovrLz_noiOS!%T^>Nn3%)ty1-TDWTW>hUtQ)=u zH*tK-=f% ztp2|L`90UB-FWC1MrPD=p84I}RC7{|RhJPR1F9`MC%bF7)H|JPw_67?XUWRoa#_t^<=fuE8iG+EsA>4Jt`Z^`_{*G z%xGrsV-LM&977rv>-XpUT>0B4?^x8?Gqv%{@n^D)VYh{XNUhtE|?lE|cmh&$;XB(ekEo?Azt)}*UR(@A{=v9NP;_X+cRj#Skauh#wM z*wf4CImR@{+TIvrY_;O6k-NS_>sV_sx30A{sT~<=8`rX~?f&{WZ*<-#pnAJjbcvI_AjgGn8lLYxz9a&@@gk=hHfR)Sj;9t>4`l zXR{?mF=CA(u&+(r#bf7!{}&m`s=4Y{L81v?>n{6 z&q;n?zI5^NOU@5H-T9%ls%tklem=*s>U(z=lCJnr%X_wNYp<5Ym8}2T8q<%R<$CSt zTG{o2=apTWXL@VYy3bnVZ=NmaM*Ji7E zw$b}+&yW9FDbt#ixk9ZY!i{kx=sJl-<#cyCZA89S`0L~G?JGsjsP;qGoat!QyFE8P zaqW!WrIteLQ@bN_^9en3_n&*KzaRSbQ{?x*d8n(e-ujpieEogt!edW8dC~oWPgs9f z<5uf627P_x(5q(|UH&xU*0y@(wb{FaNwZD0(>p)A@#Sj&&JV5j@0r<#ua2#MTn=?! zNoz{xnc|IR)vupdG;D6H-)R+kBks*kePxuN?c~O~8<%UYtTR-~%+|O5%y_+7r&pr) zZuY*l>7938PoiFY**ixGXX~O?L4AW(%vN`IteWkQYWCixS+d@v8oai{hsK|G|GBsP z{?5OBiu}G){e3R_-ebe|Zru6xv%0XEs@AQ} zwV(BJYirN>sxqpc_MC|>lRz{r)xvb*zN$Af05%EFEljd#^r=voXfjiXAn>>Fnp z*(g}KIj^GL2wk7*3!_~9f{sD8<@>Ms^76EnL^XCZT^%<%KGgoN72X(q`8sFWd(`=B zW%O)6^r)G?dh;C<8h!7~)Xp4jjqTlk?k&H+{pU}S-*>9N&qsb=7*}7re2&g9`Qcc9 zUVT@t&iSM=@A}x|%Yas@zBpR3^Bl~cDSI2qzMbFI)N@q4nypb+=&W-}Lwad|7zm`T@ z>Sy%7uB%wDpf*C;I%fnr{&cKqOuw9NwN9^Fdw-4vbhIkhd%gPXvrlpA{QlOze~SEm z@Az1ITm0&+$NO^+))}Ql?vBtE@>Uz=TXRAER=Elk9 znw2>VePd*DHD1?3Yg}s1IL(o{@~IZUoI@`UyIOi9WOFroL8Rq!d z?Q1P`mT7(L+V*|E>iXWhR`k3YOTXV)EORYub8*GHvFo*ar;c8?j|bPr zv8yZ4%$d867=4rK|K{DV-#xwWaeeC7|HD({_nqqRa{Iy0?+X{78X10m#@FtDez(WZ zbtmP_TxnK4H&Pr*VantOtZ$|wD zYB!2rnU9as)!*~F$YpW4Q+8ET&Q^Y-YdzcAlv)I>32WPIUC%SZI#1>LXeW0JnAzG| zm)5MkZ@lJQ(_Vjhj)317d0Ra{&-SW+@U^qgGOrjj&ncf-9^o|$9owY_UGS0`5=R|A)^eX5>m zF0HY#+5hg@*4s2nQ115WI|B5*T3^?AUp<9p{i?BRFU)a=o}1sfW5NsLlgwU0?fTQN zUwi1Uog%;QG``lM=)uqL^ESSA|HseUyBjyF{o36*Z?{vsK1w~;a;$Y-;^rm7(>+=k~_fs)K7!*CKAjyjkbj!)|5CoDr{wP@UO52z$SJ!PV1U z>!B;bG-_9F&iV4@zMdZ~iQc)^dRgClc2--xirKsGo`#jFcAl$PYL9dsm|ElWN^D&* zs&zWEFH-$pE#7PPuBXrM@BH;s=8H0T|c!9s#I<5TJg1I=N-MPe>?Ykj>a_tRx7>{xsDu-oYzaQ-`&xqQTo?M z?#+>`p6@qjpSYHMM~eE!y=ygcb66cgI(jr$-W`wTSo-HiE;im*md_c!u9n`rb<}u% z#IsdWf1uT9eePGsJGWk@?=olanlr1uZ^o=+LC3B+LVx=8Yv2AaPm$kuYM-Bz{61&x z^ZTFQ?aT8z$IVn%|ISu%c~{$Yt`}(xygIK|?_5_}3%oXUz4V#wopEygEK|z$&eSR| zE8QBlR-!g4|H2r9n`7Pe`FqdXqv2&~Bl5MWtHJxtdFQZRxw^j=Kx5qf`H4}(b`Dzo zfLa92vUM+pMhMF4MgVI$bgib=mw)&yZ=0QKth~~V5_J6NsMMW-tI21E*P7_4@%fVG zE*gFGcvNZBF$6v zzeePmX>MkzXUoZI!|L4X=K9IC0BQ?2vR%Kny0`VNZ;ZzsO=@X3)7-k!R-AX`rs~!= zMxJ$7qWT8&m{KdbUVdW)_3yj0P`&MPvL1c8*lM#{7abSo+SW$yd(Xa2Es3Jm=k?pI zPpf==_r}~R(|BITtGCBfExRoSyyA=FQ5O&yShl#?a>&*PJ77mbsd;)n4M3{E z(Q^34*)>|4lj@jICRLx7V{_KMF}98;ty%4uGS{k=m7g4$-#C1|@jmNE#;Y|pK3CCn zJeg;SRQGpBvaSGoeQd348 z)8nwSkELC`^>}~o!5lw-?9!7@JbsSOFS-BY=VfJeY-3O#8GCgr(C0OhtJk_i(CzW_ zdcM`{<@%iUp2vb#oX$RSwP{(@t9)WSSKc*V)=c(XkviMLy<>giYVUfx^`^VBV@H(g z+Bw$VoOUho*45NEE*I-7&owe-Wn*$3F`D1*Ebp?uRzxlPdKz69rha*S{y8sSn_!Nu zcPwhuprgfHzgC~%!{ap?7x?rzlGFpa`_H}A-|zZ6?^J&GN#*xvZan|K*WY^i=F6|Y z_Vkqx+`RUg8!xQKf4t6cKTlr1^!SfIdHL~2uRL-na^)vqzy9`H-~StrKK{g|AHO*6 z+I!`Z?=5lV{ja}#^~P&AZ@l?uKK<#pZrptS=|48#WB&Q${dMJ$?<;itwHKaVA8!1@ zg;TcAt0Cta(bU!0!Ew6?yllrrH?v4bF zXqUg;!K5sIX*{maKCfLk+XmIt^}y#k_S*ifM}2LS?o%}$*O_Q#cr)a;$Ko0hEWaDc zt1VyYc?I)6b#3-q*^S#Z);34gXCGs(f$c~%v%7J=jw6+=Pchfq*7L8|e)pey%kPK( z-YN3?&m403>aEA;Aipm^`S|5?bbo{UUw?PLbouqAvp=hU%kw#_RqflzRekR3XYK1+ zvU7dNtbxnoa-r;~rtEiT>$$VN+S0nu+3qhRE8SI6>m4`#Hm}FlO7B_=txK8rJ(y>Z zc2ud=Fwbf$lN+zAm;A}mPw(vXKKZVu!(k6(Q9vGa6($^Eat%gTDX z)x+J*pgVQdC#_bj9bPtfwq`3@ss}$l%GC01d+S`f7F)SqZq!4s9&Rm3t>ykV+sgHW zpFexW#@ld?t;h+F*ZHzc$;{ zxKr0%s72e_)#~$kwS{WSdFSos*cxANTlw96kLG&P&I+xK-B?y@$=lkoq`aKhbgGuF zz0k8|?YyICE$&(ZjS*DO*Q2h^?Hykl$An5YHeRp19zk_?uU&oI`Qb&ae*eg5h14pU z>(qMnX3l!2js$(@dIKGG>V2QS|NZU%@tw-=^D%z@_~j=bb^p*)_P?9guCA*td~IY( zwOH-xGP9bkK5q4SHD7;UACDVnyWQI^Q)|OFepTChu4$>wJFjdyXQ9jLu29~rR%2wH zomEyhCf;%3`a{1ovapfx#+-chH|)~Mc_<8kBS zjn;Kk=v|s8>r)l=KCNq+zw!L|osMSZeY1AmKXD#cTA%mUc)#vMeD|MwtG~bX4^ENa zcj|will;~`Kk@ilBVcpY%Js2_&-2DUJfi1EKFqQ5vZcF%RNIx=v+p`HquRE%dFO+6 zwn_P2Pq#ekx(Od2Sv^OsYS%X--EYq;wRIdR`x`5({;k%ZW82lEKQn&2c6*~_t#7F{ zUqAn&P45c^NvU5ck@}F9aoL%x8}K@wL|KG&vC+P<&FsT5IU-L1ZvG*<7>T3 zk6Yp1v-Qq9-rW7?-tznF|L_#~eW&($ZS!-`K7ZoLi;q3-{O>34Fzo)q(^npO?%HSG zdiu(<*KS_>!1d4Gc=g)Dk34V-)|p&pqTn*YN z^=#R8rbjLE?iySMloMt5r^dNjuZ-HY5wCjNbBwvVzS_CB)YT4Qey-(xj^^$w^!g#e0kCL4e(($5P)o0M>$`+eU8EPOusv?=`znd zo!_q!w~i@&g4VeG|Id#-^W1aKTs#R(a?$2!B{N#Ins+*nm(*dZT zTYukfYs@Dfdgx#H_x{Nr`Q87@v+w=ef9QYv<^Sfd{IUP___%Tp-}>DbfA{U*8qfW+ z-|PI{EutT0fw_M?`=0l|`|5Ll=KU``^qpHD_P@LJ_i_LDr~O@?{m}0kZ~3j?cc=Gl zH+=rqhOhtL8T9uHx1PInYyZ6c_pPVD=dWkq{ZsFL?%mI>=N`V_UwD0z2P2sK&+TUF zI1c8D#q^vBQs|Jl8tvp&(%9(}Ca?{?*xw{O0FEZFz>${RPHe(#NIZ@vBIjhDv9 z>EZgFNAC1XSKf2;#;d!=m1kdm>*lrBuHSh0Jum&lk3Iim>-W#-KXmPtw{JXs;m4nR z_aA@ssVk4nhqvG1ti-c7K7H-&S8jgrwU=-H{@RTzk9_y> zcZ-c@=hs{A>zVI<;VaLL-+%A5H{RG?#%JAL`z+jE_T8_Im%V=D>MJk5@l!9q`tr@E zAG`9%_dWOh<==nfGb1%i;P>5l^ZGd2j04Zp7oJ$Zd&lj!UuNv>clexFKJ@a9FWq?a z{jc9VJ35Uqi${*n%ojM@V~%^kl}ENC-L2GJ$ERBd+~fG$!~f@;ZTkba|Gs@-{>StA zce*`r`|W;M{=Ra%Y8{8KAC}MLa}XdvfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly 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 z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF 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 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 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 z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfWWU1xU*T?lt#BTYS*q& za?|{7%Ex`zS&4Ib(I0o-sK)o#+mGIT{-Rj+td~x{m;8rG$LCKfdu!La&0F7mrmWrl z{d#sv?b!W+^EH24CwUw5wg*LiFyaK)9Ct5m+Y+%?s&NvK{a zp>jfgCt=ai(XlbH#bRTNC6taYo$%j2@)iYgF~JjIL3r5ZL4mli@VKzNkAiYR5EdC4 zZT^7I9$^K-L*tE(xgfS+sKQ%Cg92gU;RPbXBO@asLbV5l%7ciw$crwk(I~1|n~u?! z_by)Zkte6cRJd`@;u0xq4_2(zsn5_0V&hAey0~N#e*M!2JoF!b88-YsA007r)VT2zo|^ddGn1a3 z{=$qGUwV1wE3f`FbM9Mj&wFS7yC44TqmP#?&04nn@9RGOuk{-?Zrc3CmpgXu+P!D* zS6?5>&N=+;k)y|spU96ZKhATEP{=}>-XXCnJ$ferm1MRq>0@8jAW z6bcUuO_8C1-8PT)8c({_Dr<L{YhTXGB3zTs&F*p>pFe6WPF^;>WFDUotU2oJJ73|w{u*tSM zS)bNFwIKKXC%Ua#`dntIUOP7R8r|)5gBO%fVzUzGUFG3axXF9LPS>D5K7${a1EeIJV0NH8PH! z7<=*ZZ_adaPejkD*FM%dr(>Piu{9Sz^?0$0kG!0em{{fNn#I;Vu(wZG3J*;xare@)AI8j@Q@=*TP3^zv{MNXQou@9S*lEb-@OdSAv?|jh6&Z*bjDr*GQXs9VXW=dIX0x_{42nHP6{?t_*e)S7a<;FNF1 z54oyw(09d-f-e@n=)=YB8{fV&XqGd5+Q{O$xsk)CmTyvcL&b61>yG=44P^H#m6QAR z>X^1Gv-aQzuH0Gmi#;7$Z z>b<>e!G`^{@`5$Vcg-puwyxTkeh-$bI=cDHl#iE<8Cs(1!a9Xkx9@txuA2r=pD})E z>G8d?w`V?D@9pDFSM1s|c+0{%m!#i3Z{5C6hODWP-+cYN$KPz$qWg)IhgaTyb)kq+ zcU5n4eEzKsKbg6|*t>meXXW;OrRtiR1+SYkFa6okTe~KvZpleo@WABv2d_)YJ$&u* zwGGSGJhtJsu z4epEYye22TXOkT(Pu{X`;I_j@uW0bt-n9Hd%*cp)>!FJC(t34#=8B2^K1{l%(x>TH zCe>+qXlA*6B4u5s&;8|bS$nPLxaFzET zuQx2NL#F|q9{KC6510O_((I)-^l$o6UNAR0wMxCQMUQVy&uw0NK<=u%pwDNa>D-n+ zwZ@)9U+rpqYU_i0pD#Qnx9$AtNgMBs$=>-;^{bEdT=)8dR`q9Ynq4?+<@-6UI~;p- zS@w`G+q|%B*S?cSHf@==?BwS;85J__NxbJk??)R4JzkpgR+n;{Hh0gd&@}trUHuoW zO)Yb9>EP@?zx!JMk2~~=yZNd$d+vX?^&Ro8BdSLvtiEz{qdmiG4UL{rWAvE$Zwww4 z?5}vJ|G4Z9vk%T1I%4<4_QA`mXZ0(yxX3prs#LfsX;a!K17Di;^2oj$8?4$rp#SQ7 zu3En3oi`684w~G5aH}s*)}NFYys>}7bDz&D9<*uvt5VpUQzK9R*e2Kj?+f z*PDS`pUDdz9m${LAD!8@F!}Ds9}qOKRU1-P8J>uld_Lc_%d7$WTfMrO$T$ zA^h3SeUtJdC>~19QaktS)Uj{(Ua2WvI``^xU-#5*XX;g(J!oE$MME2gs{Cxd(xI}= z#Zr^ImL7JdUR3D1b;o{bU4lzPXAgFLHGV^l?@D4r*UftN?9wMK^g(C(9~Rl@$(Tjs z&b0fvafBM3Iet37<62--|*bYpG$@ji zc$%cNNvgZGqq{snE&?P^;CtWrHdlc!^B%d$BLo?vCI{>LT|eYkv+9$U2vUn{Ev`9g z)~qpS&6-v9=+WeVoJ=PFc=Ast|KH?4!=vBfn?LmUzxdtn&L01h-~EHhKMXJbQ@rON zo<7E#fA^0nkN?ZbKfrtbJ-+|%-~HF8fBP^0<-h;ke^YtSe_Q#?|4xs8_b-3<-%tK? z6aTy8-~IEm$N%&1{yP5cfBN6~{(tg+LF)hVpZ*U({TKi5KbuTe{e~+#V33G<@k%Wm6eT8wqI=>9`5erw@;4u_qOr$wUyORUL0*5;NjIzUcGy> zdvtuVwf*xKCr590KRI~4v-`#N>;2b9pS(TXJNe?x-fz46pS;*RJlQ?^Wo!S7)lZJ! zY;EryzWie4%GI0oe`}xYzkdDZix*q_$M~SV!!K@Jzq0bl!PakItZr=J(PXms#Xq0? zSGWJCv;QZP|6%ek{?`*UoQj+E&5l`qr`N$hf}WS(OC2n2U}*zO8(7-F(gv0`u(W}t z4J>V7X#-0e`1ow#A7eha{9fu{X#-0eSlYnS29`Fkw1K4!ENx(E14|oN+Q8BVmNu}o zfu#*BZD45wOB-0)z|sbmHn6mTr41}?U}*zO8(7-F(gv0`&~F1PlThE!y#@bp^5AbC z{LS9si`V$&cl%q<_fhQse;#cez6`&=Jvk0<+ML{(Jeb_a_v6W>$uE=L$ zIh_0v&#X+YOx7pY@N;GI3EsCi*~fb>OmbzqC)>e22Y{pSKo1X$7CaPg$GX2!H(m{oQP% zwe#`m$Y)#FYmdVJw6%J#KY?^=npRDn9ECAOYxxaYW2-)&dvJ!F$Np+ES{pTI1%SN05bFwb99B~-1;tjetOPw3^phF&kDC;!#t zJx1eSqc?AcXKtd;*TSuL6qxPp!(9H9rb$!*R47Y>Sp~+-ljZcUM2z zG)DG+iEmmA8Evoinf>ppep=HOtG!{`Vhd=E8o{(4v!HG0r1`A9@->jrp8qs?h_B64 z8;;LL1o6|@_p` zF-{nhe%hL#ey+}`pH+D0MRjEFACr#i+Bm;eejYr?8~B?O==VqXu)hmDYtbHk?B9V) zY1KQ3xBhOR2#I*9ZOOF}Ypqz+yKCh4N_op!^ zqvp53cY^lP^H?MoqtL}Zlb^IhuO&-fIP_x<1{_&>iEMR^x}!6;!1e*YfW(;{$1 zkd$#PL^4W4uK%KY-<%jJmBbVqE;Q_F)NcLysL+bXuRw+z;=ez0m(N zx;YF#7nSx9Huq~oKa6-8``$q0!YFqQt*|-3O=#)rIoee3P5E$g@S=Dhqs8n~vJGRo z;m1wi?D0!T-olu&Ip*}y{R~>=9E_vy4Zet$OmYwZTU@{%y94}tc=aKEa(r_B%W*l| z&gc95r;tHS5esLhf5GoIZ#H}3h^?~LY#!*7tC-I4e|_5eS$+F0Fzv$nI7ea3S!EE- zMVZmK2L|ITo;}YfkevHI%(VZEaY8vSdHc1;*;Nl|81_sIU>gt zKb>>gH~8J8bB=Gb$J?+0=0G^hwtm%^H(h=T3o$*!*~a+VaCPKucpv8d^hnG^(VwbU zzD2)s-0h6avf3?DAC=IezYlsQgGEi#kLDV?RXoyD33%=YFG3b$)X3R(Rb!T^+ATnD z-8iMml5w`jNl7DLcG=c%8@IGw()ezpEK$2{l;!A>@!&pU2xcG21B@yyKlB1#$yRP6 zhvttV=EE!fjsb7mI?A!(+1hTSm+tfPgwQ{LW!v1s=4%$SSMvkW!gKqQMKDX#W5L|E zmGUfomaVkgpk64)-rH+#$00Lu5XJ%XqU~0c*AwSyMLB)J?DXr_Y`q#?=l4zh7^TNj zF~)ecs!`^ZnIn0ZZN}2A(K=e`wtTcDv&1Y-x3Np_`FXkuCFRG>8&he?eS2`xbD_u8?W?Q*7$AXEK{#-oaN|~Ro|>TVAaKo5Cf+f?y^jrh+k#55`$c=9-4gXl31dO4vK$4R^=vg}bGBcO5j->ds%>nysifW+ zs!iqeldUOouFZW`KcGUIJ;ipG9%a7ZJQ{?zUR|)8^82khE4B+$xl$xr;`cDn@1r^v6BHf zhO2g+H?M9wpBJ+MuTPe%d=h1t+4eEvb_Wl+e%{^GLlGSE{JVk`jxOU}K?LBv- zdlx=ue$>+Scu;dMV%uiQvHN+o-`x)N$9Qc2L+Fck8%pViW~P$GeDnPXk)Te&`!vFZzwJ4}A^0uCHSM^?Ifn zv}w4d+cDv8TS%!^EemNQmfmyQba%GZEcg%M)PSqu#IO}u?+rX!3#&wV)`;p@rNPp% z-bS3t^@Zk+FrJxUBa=C0za8;Wi%n$GXj#bGW2cnS-mS?tUg@=*vHmp7>2^`;$TQ|x z=~%C0;waB;oqZUk%~CP8`@cMU?b2-@9)lzP00OPVbSS9@fY!V6tmY@?M<^Yegg(X!Ngr)O~UJh`{1 zhOnyfZadAD1B@D;AG7p57R+snDbK#=(|)@p>Wgx0fA)zmxniVf>-`v|PkTo^hcver z=|=n=q6psX@BnefliG?{-U^Ocnr`Ek-p7QyJsQj7m3i!@-469e>8#R5K9(M6BQHni zs%QTNk+p7f$yM|CAk-vtCB)D)^~Up*^IIuTgW@_4cZ}E^&+ZR-js1c=zx!#Jndr6Z z{3LhX(F34c6uT^SY}lK&6KCvWv!HfM)F&mhglDHSDzWuTo{WAE<6;XngB&9~Nk3}7 z1RI(MukMok-^^p~4kyYRJpyBzCm_jVhtbX$gPK0%J2@$pHx ztszDMxiUi5<4si3SvjcM{aTh4RyEkhDt(q_gTxhWa0Au2yr(nQiI7cOG<4EkdFa~x zT&hi8?bt6Pz2>&bM}ZIfDzPKF?!8vIvNGpGEuD`Cb-6aacn`GOqW*|hmS>fNcLQ(Y zoasHB?!lOwH~O7~Sr%`2K0*D2-6&gS>v#uQj@A7|5KGIvp^c{yuc2kE2`O9GZEGoo z-RISPy9Mfr66~F)BJ80~=oK>C%p327%vkp+AyvE2JAxYc*|vtc{Sc{ zf%IP{Pqb^S#M5Pt$u?f;cMN#j)*)?=!8+RLrTa3`xAB&(?Ka+0^c&AHRczOQ`&cNS|S5!aMAbC#<;SY=trB7Jh$)Sjh3+MRrV#tS?VK2QF#il(Wv5*Q)ki!54#wti@>ft97L zA5U%TS^<{zW1+gTJTZ4Ua&?5>rddsyi>rAj3Uk<9*u-O{VQaESIbNern{C9>ZGIn7 z?SGwaRn62gN3lM+j+3LVPd37%735g#9JE~f2MimCj4B<@)_jaW`A{-rZb#xvAOtkV>~@BQ4v8XKT8RUwY4N)0<&NdWbll z>uI-8XYnSiu$>^5;L7v&s7K%pP<6)66X7p{Rr<}v#q(lqTSh7DeEt?l_hYbaR^C)? zJJ;-W9&HP&^jkXCaoaYv-EM(&U%G8G#}MZ&rCN%ilupY9=&OZOf3BOCOQacDn`A{TN!Hd6=|bst=;Jg;n}39c$Y%q~)<#M!N;leOWDF#=3bs zA=mLsn>F_1lwQlQ>292DYbev6=S44_mv74@W*Ep6ww-(BR&k3&2`=HR{= z#+@$_f428$=d;~;P)eueqHNniIT6Jy4Y#pN-zC`apRwl67V!23+Hw2NfP9hW?xY`` zbXXp`c7K;-tKAxHW0rnPv(YD*zr6;_9>HFZ@l~C><=sEbyGyFt?LsQ8mW{M+4W(Le zQ^#%O(s+J5eidqhEk|Pvah>`VGYS^4-&Q+r%q9 zj{)zuu$W_fKgTzt;gh1yr9S0O?Y=M5YMYvE zXSHg*`DbwRpp|aRN87fGa;$llrrX%1_xv`^86{`&o$66z8t&$8g_kv96aOwswL;jT134zkx6%DA#3+>DO}sF$u?f;wFH~w{S`bd zt!|4a_?>kp`B=4%OByQ)S4q)3EiY=UicFfz6~lKUYmbSN#&BDcZM@QJS!1}3vrN6V zah9UbZxBr$hx&@6Fhgf9`RAIS&Yj`vM<^YZiID3GmhWu$ekoSkt=nnHrSsf8FdKRI zJKp1bCFTKMhw4(^b;qqXZPWPL^CU3Bi(qjFVMXoqwW2Kr#!P-^z9id zz#XFq~jUL)^U&-ncT?4QSi zt9YcN)k%Il?H(*;ZXk`d5lVkC2J`6C2VisFz{#6Ae*wd@l7u&dkrzFqKAz|CTaYa# zdq260=Z-Od-U4S^o%wg+@f-X`2Jge|>tnl-zdoB0+4+~OYar%LRLgU|z&lW1%@9??Zw+pRRZ0$^VE3(}e$koby z#9s&apLywcCyChX(^m%nFT*WOn*D9y>3GH zy!lyuVWWV5^$TpDaq&f1?hRye-gGdOCVVMHH=v3}CHv*)7o;`DfVP73wPS~vT;$e5T1Z(IAi z2_kY3qk;WXWa}5a8}z)Gm|bHp@1Mp3P2T3sIq9u2J#WmXpJQ<9qT}KoM#t9aNK#xk z1lQe0uUMXAck)#jdppSFa18FAaxO<|WUy{_zKxzG9`)3z8_@CMftj81ye7eUjE(}vMo8&~QP5bdCA z&osGl1NRBuz&v+7+@`aF$Jg=O27crHG<^0VbKrO?lvUGnE+jj)*x@Van|CJDW8Dol z%laGhJ@4TmEpPrjSe$$mXE5Y0ves&dQ?4Qk(T#HK&#y1gW1g7hp~}a6HRyS5RC zOV=}Jen((yj)K(@=(ve@Z2Hv=fu)YSs(#17ZB4vmvIpvSO!hDa4zAh*@s7zJh_`fm zur)bm;GTwfHO}7^#paHIbW(6L?&GObmT7o!mJff98Tv+;rLTscmocxpfmb&sml1*9 zz?@?ZkGAmIbv(X|S9qf38h)E}qw184%%P`AjU1YG9zPvJ|4Qc&{m#`V-cQZ7N@WIYEpsnqX!y!g%_ps{xB%_ViS92L>)caz4z?kErmRZNjF&;UVt2>8yMine( z2eOmL!RH-LzC%PoF6P){RSWBrFSa5vY@4*E!CD2Y1<-2P*OPB15Al_c&14r`bBOC? zM?rtFAIcejo6sPw;UuieTCAhlvZ3Va)W!DGU$pnN;wL-ZgcVc2RlZ>5$aF5bxX+-x zZ7@|m#uBO*XRkiOqqoqR*86VZ@k?k~k@-btoAY9@vCK77tzZ!q*SxG(*(Y3CJ-NVG znHS3wNTy8*N%h7EL*tVGlsPY_TPaEY(<%;E4QotZ3M9T<@{c2UOU{xD4Z8pv#OaZ_e0b(&ySTe z@NFPAuN2REArg+L7lBh%?|hN!!}&4Mi`ZQBC3>^YsXP(*1z1kCGamifjVE{Cu|88V zS=&Djt(V*A5!M|s@6TA3D;10k>2sLvun~5V9Zuc$!A$oX$a;my<3)%@EIKfGtKk-` zwZTU0xw?KV}`Q}@E?ZJBgh_#VrA8} zP-$hz`S@yR%?>d0(G8$p!@yImu|lkC$m! zZv&5wk6ReIW^a91k3&VHy{B1`Z`g;(s#np*-5b?soBF{{o_UOYol#WOET?r`JS7pe!q^bE+zQq}oh-Lk8z9E=*O@AR~4jxgJ!_hYc% z>ij&1<;EWPF~n~c(baWV#q6o8X=^>HB9^ERMzhs9hwXi;k7{OTZ(}~voPmfhUtlCD zHjTb4&-kV`FM?j)EFb!B!2FC0$(_yI7JH&PrjNr~tDVbY>zj;`o4Yu2SkY|c)ppiL z;vMi_^}PC>mL7(Wa;A+4YWxMhkMM1KLGNHz#=H+R*W5j-*=W6H;ab>jz?!Z#M8ek* z-!jhiFX2o{$-AWZr$fV-1N$Ie#{Y()_rQ6lN>j)l+`Gz=>t5OPqFfGYb=$_dh??08(pO^ z-n{MT9&uf*$isH?w4<*ta`47Hc)N9;3oWy^%+#kE$Zu^@<&H^Kjgq|XY91|KHOu^^H(BAaopR9D~2JXHETQb zNbiX)8QOm6J8flws|q~N>IfsQxq>^qHgaS%O{P^OA7D(Mptai=o%QORR18Du4 z7_Q6no+4snRZ;cCLheuJ%CWV8Hpa6A**br{32m$;VO0Rn$6{aR(%ojTr~cly(~eks zsoJjZfS9$TuYkH*H8p&mcosHkbk_T3w?Qk3!!zA1Y{Ru-?bL%0UuQU68`||nW3ixa z&cq1%Vke1q&zdZ27Wed3E2f^J^6$YZ6Q`QNntYy4$ZRPqg4$zT6>;h2szSkN zF;-Wo>Ra>rA*;`6QTv#~JwXh|)8biusjOuBx`^4*S@}x_vI+08k z^8B-@c`z_{WqnKkaxqz)!B=f4b&t|h9EPZh_PgaOoBd}DQEl<9kj0=EJn|wZHzg0>QtgRjhQxNG?}VKctgWxEFx2ZLb+gVz?7LeN z=fk5YIUk<;u*jcbznqasJNbI6iblE_9t|TJyJ^w*?$Z!Y-^cjW*ZJA6+d5M7U%ynX zJ}`!3oY}WOdkDYUk0QQer;oE%UpZ$YOA|pxsa0lXU&L2*qp0p_GK;u)R23!k%YG1i z75&4P_-0=E2z^99tmw|GSDr#E{=d7E&7h{I{>Uw>wq(;%j)5KQIRw;@bzHgd3nFmcemTE^b zbv-bl%p7sdC}Tw3a~*phU-T9n zM|O(uA)e(tZ40kgD;tf~E_%Zu?NTRU)%9}`l$HhMT=S^1a>HB=<&h_tgRo5GMR;I% z>|?ZwO?aAkwoM+3poOxkx$7i^c>1W;1Q@H*(%*$TN6RAbAx>gNAT6J(F5L6Ki;=+W z9!E;uQqB5@iTM||BF6a^X;jZvpjC5*La#@ks}bsJ;nfu@jz8*%C-B&bi2KkcxrTK_ zZ^9^4RR`N-H8etgv24QPTAdGh+lSEpUxxXiY5XcG)~;XZX2ZIzRU^)RR{Rnc@ftQv z%jVh$Etyp{%%3Rh$=UmCyE zT2;NUdBFDB1Jr5L?&w?CGFnMqKhoWXXTg$fE=v}%>MK?_FhVN_J!{p?lRtni$HRB> z&paoXZQy+&%^gLo%H-*dyZEvbzGIY}*Jc(+U$vjS*vW#b_5L}GK+Btumlgln>x@S1 zG{JZx;7T|}@nS1;i?ibVm>qp_pvb#QdYsnol?wy zUvw38@t9;DoOLE|>)Dsn=Ry2k@HJe`w9!+RUQ6=wO>J_OiRU76wfLwq128>a#z?vu zYP`6P!2J`v=k{WID9*#2U8s*Rnr?-4eXb~Q#{qYC#@s5iBfr8fEMpZtQ?3kEUmL2N zR$b8}Vx0(*CptL~Vx1aN3gX+rDp;j?P^m;P>;48d^K-D9U-2_9tG&n0U)rxBWxnGC z-|@VUX8f{-=*mXoA22F;E!L;;d+J4)Z)w-iZdkwL?EJU0=z?=E z?+AUm?Q$KIr{<8)IIGn>Vijwj-g&QXq`dcT2e|6>3D+t-b`>Wrm%(FqV3%Zqm*C_h ztj`$}#2Q=1OI)#gj`tj8w_on}v$Kj`VtkRUj-X{Cv{fQQUy4U&eKdFS@C-J~Xf%CW zHRdcQ;^eBaP2?mEY7mhcE?BcUAZF)t>p6tW#$&TqGV?C7)i%SgDqga3Rgu- zjGEah%SwD0?WtFZ+uDVgtW5g6$M_J-6 zyuvJ|W-_aY`M4`IzfYPUyXG`wz~0U=Y5rcWtu(VYr69IgkW9#x1X}SpJk>PZs?y*} z!)vsvp6jdTA9C9ZIlh|Du_z`#Z$ysk%G-Odm$zf>h_8GO?#Egl=SHmMa%{v{*18RE zoz;A#9y^n(a_#__HhxO=OHr4+sc#SQFnQ8E6sy>nC(;fc#a$)w=4{j&{rOR|BF9$f zn|BM^D^{sXOX2EMb?ujNJ@2rMXHa^3RI6~Hg>b%Y^>g(3^_@f2EL}cpSsZIV8|lZ| z_6*ErY+yzDCMxOHG3((Rq8vMDGMvrL^5CN06i)bNn+SyMvsO)fZch^fSED+{fgrcdIqzN)uN^nKxwBRCSLKSN?V0c%JNGvmR1= z+igYWRant$o?DYM+i7@Xoz&RyE?R|_(<-8t+o+q>Xv5%S_V?uMZe&KSjI&r-Xg8sx zKXZ`WGaE(!yX=l8anJGT{xfDc6eDQ74^t(Jbb0z)_G)Gost$Fn>(R+9%?tc0M zbZZ%a-6`)nKWi%XXAe>X)U9W4&XTX!iH_@0wsuOLu$J}W`H*M_zO6P}>(e^Vs;Jhx zz7M+(8NqT?bDm@8cSob`=D%=TvW z)*{I60w*~^pZPwVHA1FUrAW$AbupxmV1Lvx@t2R5er-EgE~{OJ@zT>6Ycbv0YAvHG zJD0%lUHlX<*;SucKR-%4siH)jP2W{kUpLVyN-~rOu+4N4$RfQRE$2<((nX@Xa@)|3 zS+0bt2OodHdDPBX{7{OQnvSXz549-DeBNnMr1?D4ETs9p(jo|Rd8FwWn|$x-jTS?e z#}iG*SiTop6n_p6M24|kC{MOxyk1>(Wc@UAIGlk;eaCrtv|!#MQoSXm{aLI6XSHl%-t-9`jSnOtMC{jJe=!JbC@=dGL9A*7IQW_Nnt=%;ilN#pdfrXQPY!Lq^g5 zuatR_#W8vM&e`Z9i5&@jOEy&*zhd zK%dWBodKXy^nxjfwBxO4ixi=)ls6&J@H*}YVMIYgUsdD7RIGnV5mr=iL35f?+3 z!|N@EFo$QGjxeY9nje|Jhnj{hj~7}5k+-LrhOK08vKa0h9^=Q;>O!8G5%ZR|`pEj? zcgQck2zkY$P+7*C_l|>q8Rj;J*n2SpTE)?ak9pu1q0&vWL*lU=9<18oev`eBcRe{h z8;`qC;2$&am_OtyaEk<0DF%ldcL$DD+U!}>m}`B$lV_CmUq z_VZ!y8*kUJJB4>;+zj70@qhj6BD-79Ywt~;K+TV_Y^TEcRkuIc$`tky(Z;(3HIJman7LJTGe=qKloMna4`2tl}3|A#;RKfc_&&JXOK@64kQvBHOuW4^dP#1cz=@XV}Fkzuse%@0E# zR=vJ;T3z`3^cJj`XJ^E}6u)lJDt)3heGT57H7>!xW< z9JbWjB3cu(eY{$0YD>A+YFst8`m!OIYnoofP)ohU^I>T(;-IBo>Ut5&EcFugGR>E% zws*r)f>BwA9)nS`%k2wYG@X#AqL{)|$1Uk8l#xZB)MS#KI@2SAT@>=8hQd ztK}Ztk8d6*ZHc26(H8TBIv>57C;WJ|D9&0$iyyDH#8r!EYiV=JyW8EN+^xgCX1pot z1-`i_%XZaN=ZPP4esCT=iT##(nr=_xx22w@>q*SE)KkBnHnCR7TWc71Rk>q@+})YWudP0RKyb7YIK*)&axb&dW?mWGj9PzKEHdy>N%g_qDlJiKllzLzcx(A?NPlS9l!H7w&J7M$zTb zo!Z#nCXJ^Lr)s5AKiuj;Vg)AAv@V! zx1uoeRxRp}c)W+1)A8gAa)Yd;=WPdk9INiH;B@)z$)E8I-x<%CufHpLkABIvM)nmx z<0bsd1NavrJUso>caX~SEq8Gu#UZ@U^Dvj&sg3oq%nbTl_ z92VOB(djv^%2>pk8R|A~RrZ{1J;I!e=fU!CA7T{o_J<`8dGv)w!1TUYD}2^fCMzvE zE)-Kt?~5Gw@^Qvwv3)DDGM2Pkr*IU45YVXE_Qi&+N}bih-7+Am1oZ9JCw-xkiDa zq4A7@J*+m_Ix>B;PF$Xzp{Q@d#C%`p`I9r?_fLbhKiL~NkS_-LNyC-J9&n0-iT7= zv9WOSB5hNzjj3%sDcmZ9cF;pRVPA(;ELnvRD{5J*#667W$G;1n=P=Ynyu|x?Q|_G2lUo);W2q%8`KkHYr`gLSM6kKR=LuP{Z<<(rSF@n_D5e^)HgZWQlICr z>Lthgz|NYpT{DbCenCIS{^lKyM`v?qi&LyZCx^z2_2NA1_9>U|~1ta|S-9q(}J4S6FNN5_0YH&x4-&J-{b1m%@5`vj@`-qgB@YQZrxUG3$E! z*XWyi;U^yk%^yM2oY&mIx%sTqKR;S(fwLp4&FAMI;}%KUGb5Te&pH>k>?hyvIQO^H-2Re{c7FF6V^twI`?jv4>~bOR}!TM@Ib2dOM^u(~NFr zSj2AoKlv?e?|Ened7ifTGT5{1Yg*l0S3lUEl`Yt5lxeu9F>(8o$UC;5=9g3JzldJ> zX_E04?X8X^LwBCun)a!hRUW~*sf~+Bxr4vwow2lv$M`()8^@_;<@WjKVavL8J%xm? zaTnH4kn#wAu6wLOmy+Cr)|{ z*=Db%<9N~%*?sXI?czUg?Q~&Kd$}e+j@b(~rJirw^0UbwCx3??`wso}1dr~7zu1Ac z{|*&VEBIc+&o%tN2+K}G)@Itjf#fe2unNw2>GQV0KXp%MmYt?e7OB;DV5`T)fV0Qc z?Jzz$Tj!|f`VB4Cq7K@qd2DM(KMV42-{7xl(-se$AhzJh=O;JWv_A&#`kedtT(ZY= z=$D_yxN$#x_OGW=P}g5Q#{0=SM~HZo**y+X&$EE?W#}u$De=_6d3mTyUKdKn2ekX= z(CNz%;WDB#D{0pr%op#@C!10kOREQ-o%Rb);CxU(EZg=XRd^G3pxV#zQX@J)2_rZp-QH9d(69yp$*r2?WnsAWsZg$y{&kN z=>KjwHGsKCp76oUEa&J?@tB^xM9aE4XL}xpsFx$9+}1D}@Ark(-r;PDncuQo)9=~G z$YQI=HuUvQHv^P6URKX;iJzB=OJuRtNxx4YBa5x#JiI>3s<&qxEY|Ps>#Zr9#kzJS zi_!YCS2?1_E~MhB%kU{zLbj9{)2{E5SgNZfnumH%5t* zW@SN3V5Vv95MzzK9pbC8w?k|-_I8NN#@-Gw**Mx^6~L^-KSpdD?^gI0*(>I)`geYn z!Bb8AVyCg<7b}ewzu0K3_{BnF#V__L5kJ@NxGJlf1@cW?SK26QeWe?#*kyHc9mva> zk#(aLmy83gn4`ZE(BI>9>s1_4QqHX#so0>TmCJ6VTD2*^_q8%pe(!6|rS#s9>nSDo zzUuX=QL@!4Zij3=vv$A1hpmLKa(zR!9r9;=>pWMG!DoBesx{R8TidRlJsL+V^!ZJ2l+5s zBQNn`v__ub!)T2d|HEjFSba>bX=aqg%VWr>MP_ccLVP^#R)~kk-3syVxLYCK9d|3l zx8rVwcyG0uPBZpe71DbKCoiFpU%T!?r&A5W3t zSspu;6XNI#ZC7>Qx-(D9;Hefb&rY`C=h&XD*{7V?U)S$Fs3V=_jh~{9_OQ{q$JnLI ziZ`x3{{`5%VyROw>`XWN?Vxeb(;p_$8tnW5(+AH<(wQ>!7VJ5m&+;&w zq`{Mbb}HxpRL|+_ul=o_#AADo-vd2Q+2iRRI%kG!?6=TX+cn;Gx{LKp_h0g4Fgy8+ z65ax5U$4)Prm8VtJhUz8<(Cj|EMl2mEtIYCJQM1D9!<|41M^6lPm8l2(!6)seRG0)P7wwqYk%+UL9v=Q!z36Fa$d8&b^HKA-$sRO#>((@Ur4YfAnX zli#Dxg>@jm$7?(-avL&sAc1@8`6|z2dVxBLRjjZ79-s3kyzf(d+FhK6X^(E<(dXz1 zjs%`H%Cioi;;%Snx2Isfbc%@#`A0lH#``IeXR4fFtp9QH8NPlEUsv#R4LCW{UgBBS zr!a5!8BWoAgi*~N0xE7ZY!Hk-y))g#NEm~M^gqv>Nh%~)Rc(W`JC>nz4l zA5G`HCD$F`lt5$VR6MMnP;R~3`|u=2o;S5*HoqR3&R^=0C6D>_$aJ<+k1Sbhs2=I& zraNKwzzok8PVs*LUa~ykA%5S-Gn_B-HJu%M1@ZD-%-=YRxP$rodiYv7-GTEB81^#e z^h3|xKHj)my;kS1S-r;7%3q-W?Dk;hFqi!{D~i$#a+nxF{WhKDY~A8*=&9vQbP>%< zzx7C~IxgZ04B2fSZd>-id3vCqnP&Guo2Qn2aGpM>vrPTecjf+=&*yyv%+qtNrK;8Hp5*FT;mcfNbAByd)>&e>uwsh5uu$zof{iAaBNzEU&#Ysc`qjK79E+2<(`o~;c zzvW$0tZ%U#&4)hXWHs}StL|GiJ_PHLCKew*A4(i04NMy)%29HZbl-3FA=$#bqoV7U zgYoD={a&|x-bMNMr`b)r{av?qva;!DRI^&Oux{D2TKE+0tY>(~$tU2VCC zI)-+k&Hri*1L%8?ctW7JY&G-4(pq*!6ezZOo zh3mM%k5_6lUFKJ~50c!O0&(0))IGQJ-HX>oqv*||**-LBD&X5_6t_)BGi>!YGjNQ> zo?|46y$&be;qfo930BLpE{Z$%syiu%tz}*egZeBd2KChN7!IIu_C)u1spm_}tW2+B zR4deHX4%zubk(4?D^59swa|H zXe}9mR(db+11*-3uOjcLp<#6}k7Q}YU$Pj%Q*wLW5p|zp_EBF?Z1;W_9`#5mc+^kT zGhR4;j<6r(*YN)?;?Z5$Gb4Fg`XVQ`OxI9#juv~$&tmHG)ib%|t0(;A#|SxU|EtsN z(qv*~G#L=>O#&pbBSv0o^&e8dLjJ7Q& z?>D;pFsmaH*k68WQpMg09qq0_fSgfiMl3Z$xGkaCWrjF0&m#;qcZ+};Ws?2Pf z2z4HJxxM4O0<9wLK!DV*5Hn>L4>NzAsKDlX}! zj%y>_g6fT{D?*Y54ILrP)>0Q5X=~WbZmiSn=iv5Q$7)ZmuUe6oes$)e*rid+@Qnex z)|g^kG%t348*W=oM8Bx!{n1xrbMe4zd-G>my*5DsJgJ4est64&scJm0ee1p*POXH|K2tL+A0Uw{Wn|h4)zS#DCVc zRO?+V8aV-`*q{FqEBl{e6=wK&nXBboTYQTcVLnXitCBD&&sh>CRhXBANmb@0VN#`e zNtjeyUJ@qVZsmn3u5wfvDc&tC97D&hbw=t#(PW`KD4M*J2St-<@}OvPOCA(WHpzpc z$sc)8G#Mig3f(1}lP}^;uqjb6isM~QkHSj!)@fxT^LFI4_$n*9Ug6mtyxS`G$*@1; zn{oW%;A=N^>0D(OHmzw12XESXF}Qs0Ik6kSth)a$B2H$dwnOYm?J(Ajo@MyRdtLV$b;lVAn%b6fjmbp1kq~@i{LK!H}_qNhQWMO*Zq&} z8xO~Lmtyzbl9%9bJ`Xoa&eB5v-`i)zo8;)ly7V~x6YNuXD#}jqh}@z1C*+~lf$uY9 zi0aSNOE&Mx`6K8QhnnXXg8R+q=w~bY`b+BPOwm^y(+}zh|}# z<=y^-9=MA(-a)2~BW*R{jjzuBB<@CM)`}S{?)<2>RXUvZp4k#^hnh9DT)$2GJNDZ} zt>W_#LGo;=1LWlnPH!;g`2>GD)e;`9RKkoxw#Ryr!t)w@R549UgnYq1+bsesdum_qFtDi7kHtmJG zr1yc|_D>&%5y?;Oey#8G>@QAW;~Yu-Oxw>8Wpk8i`eGbqW4=0W`?Oi-xPJbLtk?gZ z*)k#v)j8_wz#L^dTyhj|ezq@i--p0a*T76`U?i?9t%IrP-)@HWrR!j?B}eg&@7U)T z^_cu5-WXnwm*^F!>E`pZ$A6WPY|K}mneXrW`6n$$tqsgkro$yiRqge|;3!2*D`2K( zM0In?QQ(bgpQqxe>jQ9qx?pg;|rQoPle7byIJ73kO z8~ck7Hz#FL>%~~gM*VnL%I0Uov6Sg>$x_wW-*3Jb&jRXVjs9G*d539vZ}S+_-qFVz z_Sv+EHq%6()_$$;^FC}AV!X;0UW|ooRF8*+Yz8(I3rUAd7RtjyX;<$y5Fy3&x_b7h zi}k8=!ahz}a@5_Btzv~hbw+4!rc)2Ouh{50*_Csi5A#(ede`TjmvX54($a2#ck z{gR_TJdWauT{9yt&a(NzGL9l27$;mb2Wp?vI~gus&Zhl=U|&15z=Rt-CduYrZH*Rhc&8$HGvo)Fdkp$55uj zWfb+{MNuoQO!z7|OPO-Za4cCWJxdKyO|Ef29-bPuo}4Q=ThKZIdbnS}F zo2T{p7mJvFh{+t`D2h%cTrWqayUu5G_3nyHR&f*CL0S!9~3;Bc}NxCR=qFE?yej9B#Fee_TdP7BO)Y zStjJ7F;n?9-_zO2V!k2SX_;YKW|(XaTCFBzPN!?ewq&PvuAA&+5#>eM$v&mzEYoy$ zve<7(c8Zbua%MMsX19!;;@a&pc2exb9g58DU6h^5uL7^K(>JH{I%YJFu;1KH=b|2l z<0ol$Ik%f{Znw-lE%~X*LOeBV7=9|h0=&vkb^;;CU~dF9uE&(Bjs=cCpu>&43%pZ|<+$x~ymcw#(dGqomzO{+AarPWUr=goNZ zEn~?>O^>IB&P++i%Xn%TPgOOr%$m2?5{z4#Y{abjFg#U$jd(Slx*JaVKbX8rl97^5 zm%LORmHpKOL(lV;)dtlYZo6y7Ef;-+tn3y>0sZ#Vv;6F|+Y^So)$VS{yHsx`yMvqB zR6D&NZgF^vI-&aQyu;omz8D7GSe6b0^W^uT7e>Hu@tEtsWb;-0KoM zCtof1kpB$r+lD3ZhQ57#zl4QWb&&4AYNwIqb^Jt3b*pc;_3 zBGHe&n7qd<)O|{*b zPfxYzhq2!qJe*W9a<qQDueu;HYiaNQO64RuMeGxFMHw?7RjKNh{yOKD?~sDsrpyVEVxo zQ=Pvp>ZHf>(@LGCBZAmRepu>k+{W^Qqh9Kq`w%h6qe@MZDk%MQQ|H%*K*KXyS1Onu zV}#j?pC8{{1&ZpZnvTnp1M56I9oM>>U9B~^nXwPK`8oJeH|{cj!g%sI{CIq}8mW6( z9Z%P+_HGwi=HwW~G3tm}Qq9B2`TLGBz*>Xl&x_b^1g$k<`12z68(V9rrkG_Fcfdqf z&t~Ih1%6gr_aj{z{R(65AjI^XSH8=JwC-iPkml55-RE!)w2e{DJL|UblVhHDu``qF zn{Qcbc%7%nJKsS+9)W9~;U{ylhA0=ly7!qEpC9Y2H~bJ0a>bGBN%TW6LSIMk<2819 zV_212s<~@F4E32pbv>hN1%q*FbtT{mGRb#gkIZh|L3`H2*UIEF7;$Ct4YJLbQQbfM znx!(r&Gi|cl3ms5toF25WXfw%-2>HI)Xu>+S3Rzr&Ol$E_R>8>(FblZAom?ue{5p3 zy@2)n3R~f-%&m~W;d992kUpr^L#o}>$nr*7(gpbL#(K8`inRZFU_ z57hfB$7<)bGmbWIV!Dd!K7NrJZ&$7AsfomI-ggbPsnd}7Wl?y~>@+0t+$t}xajlcL zqrC``UvF)6GIbk~U$1R+a&tuV2vN@tA|S4@dG#A_;xkL4r*CK7xW3EmmR0$&_VFjo zd0FMu9EW+(*BPC&xG&L)*CA5aN6wl%X*r+cI{I;@**fpMsE$^m{OUBWijnV5$j2Ny z_PnBSi>~&Ao(`a~&C|*x<~9B7u+H*zZ^-PmACfxDSCQO;ZT%8@Zg=t*%wzZQm{kRS zYrMQYvuaE8p!4(98x1c|MMn={m4d9tV1>&GGA^ur^q%WoZ+M0(%G*H1yu~p%k8G%_ zkzY?S@4MOI^@g{oqP!1e9Isq+$bn2g!#u%kWPTQSy^-tTxh|~RKESuumvZpi^)ny9 zN=CBm5o~M^s16%7!d^Ugy%FJ8doh2tC2xxAM%?h$%H848}2$Pq^7j62?j)=IxbF(zC384%pWPp&7k z*47V&xHB&b<&*NFQ2r<{3e6MpqEOuLXNk%KNQeV&+;tvRrFkWP(+^0i9$S?R~O<=KN}EFdP$6Xp!_6?|NJD1 z>-;1t{_~S44)c?!xXw?axXDkVc!=4r&+vWi%rp4SqT=46yePy?IZ?!YL3vSlb6DK3 z!yIhg5{J!)PMgoiJvd$?Fz%tr+ir1PUKHZLyePzpc~K}f<1N9AmLoHJjt6m~*LaBg zQT!x&bKN@TKbBYZYom9(#=S1yZ4`(3X;pmXCsEwwCs91)C()a8;$8-?HpblveiFSo z=h|5Xl3yFWIVbK8$QiB2odJ1Kh@0}F5MSj*Ar8xlB3AzAMWOgAFA8t|+i>TT3zE%&oJZ~lw*;3?Y}tFOFjoaz!~zqe3>_69rcHc_8@6LnoHc+5I- z);O}Zci3Gh&CGBeldQQUOq%bMgh?Z!But7KO2VYslHWMi%%~(SD0V0ZQ>>yY36tWA zk}#FW(y`vEBt40xH&HqK0+qu&XYMj8tgGjfUBN z@%YyR?*^Y1|E!+vX!evEtG9fmifw(Ricfu|Dz5dFDo*s3s@T+5s(8&;s+cRDtHRUN zY_B|T;^PfYc5<{=1RCqSO2VWFv>Z&a2CO7Z;zXWD%{wsmPVemc<@CNJvn(%LiuGcp z;1Q3Of=3Kn3LbH96_1?`?PoJ$;+z=7%T)|K+u2VGiXZQv?lJutc{HAuz&rT3gZO#a zA>KZp!w;c2*-n~%9p*-taMIRm?80RJ?-+jU(uB9z-pA=fSHqf+op-?##(Cny(Dw&U zi;ZK=+F0<4%g2IOk+GKr#*>o$B#N*7B#M#!B#M9iB#LeQB#K@AB#JG)B>L3LRr6Io z^>THI-kjr8FIR7)H|NB9xpHPUv3jl~OyaGQFp1ep!X)l12UD!ND+!Y#zLGF`GptWV zUX@{eD)Q`LSN}FP|E~Z!z-Y*}Q(I zops5Tg|p823cgdHb=HMJ)|?ZA=4&}ID89&vL3yN{7&JpmIqJLYP_wX{ZCB)w4@0cd z%ZWh|K~4<$xiMDV<3QDZ&?;>COjA}g+elXwcuu(u5~YZTg? z6f5v}woxt&;>COzVuf8!4B|%L8APm{Uq~)6n#2U1m7{p^a zF^JQ0Vi3RO!w_r8a$-=NmJ>rB4)m!MYjdDay;zsrJTY6W9m}_J6l=wVfj86m`rKF@ zmJ>tdJmPmaEGO^RQv6x~|vQ5xIpu zoexpb74QA!eY9M$9`=kz-MqcXoy+Tkvh8#P8dudDm&r0i^8HuP_A%C1xc<5_`CI7u zQ)t>wE#=7z`*?NO8+p4Vs-=Ds#TR}OHDmOXC}!}JsH|2>i8@n}yTIQD3%v!-;0YnT zE1WkG$D5CaqiIn~Dsj9iGgD5_DNj=lBF$b( zK@=-gJrQk&Tibd1MZID_Cx#r{+|)x3Hg00b!M{xmV%(heAcoC}AqS&2`yvO2HZkO2 z%{qo$Jb8ks_6Tup9S!qu>1S%~n1nkmyYYxUedGUrJYv#P@Q77Q!6Sw(1&`Ra6g*P5XJILx)=Cn{=qAQHV+ODK4uQ&5Hb9(lUHaz zvoLq@lV`Rv5AzEib?0MJ_R*r4O4O5w?^;w(@?Ytq;YAU7c(6q=d3do!G0Bsa({u7= z%UmK+%@wd7wz?7ayddx)s>0N*FhT-J}ki_`iMcyU`l0&!egPu4Hr zi|P6icrji-0x@5%J`n%qk}np_BR_IsI{A;0g?KUf77^V(Y9n^tauHo}Jy`ECG7l!( zE9+91=-);UCa<{A9bbbP{5WAABpzI`30=H^7XB7`c!x-M3-_ZiBg#=VE z)mG34J;P79Axn2V9^(1^)4Taz;8B|Uup&mW?|Cq?%7m@igC~83$7!@EcAkyU-tib7 z{_juq_7hHzKf>c*fN>Y=v1t&BzfJz%z^2}XJI?lCUq2_3DaJOLk1&=t(bfmJQ}`>i zls0sNKCwEW6TEty2#0vPje|GPZNV}u(qb&iUf`~TG`%1OZ(Bhcd1CCgJg)KYV!XdX z`x#|&jHkd5S$jSVWZm?fvU*m7XQ(zI^$_;Q)ika@ygajsG&-rXZmbV{de$48U;<{t zx6wmsFw|MKIy&PGF03Ma3m>4l$hW|hW|Xetv_hNdSdVn$*@QOULMy}H1S@Y7PuT6k zaSv58)DEkM=nr`p89alTF@zaKLsl?|c<|SgYgy3GA01 zi8jnTW7OL3(Vr=LlP9FkBmakF^6R{3(GdM{n%36iz$h*1IgQ*pi?!wckf^yeS!nJYq5Rz|%{o2vL#*A+&?x=IsbPQ?uB%Xvjj~<5IsV`1j zYs7o>-Q=|A;2PDra_-AG=Q%7QK3k0pQ;dvwR<3EM3)wT+DkBbBERns%|1owaSMDeB zb@3G4i=j(n-D{1Q@w~cAW%qTi?Lud_(`y!HJ(ipY<#7REl<6A5l=C$_IsSmrt$po+~RJjX=3+l6Y*NN)HJiy zYse2&<3F>qsA1N~b9|-JbQj;XrKiz&Jde0r<7#^v&2Kc~%ckae@9_>iICJYMe9gvL z7L*>XorT~!3^M?%kK;X9DfeD0Z~kB^bmPMD`2Ch7@AaOUZ` zMs4*Vo2M)0%*CW>yJ9!%**eoRbC*VHo$0DlZ^06|6P)quUobz}$78O%rCC>rClt5a z--jd(6K*u-;Z>cGhpdu>YmwYJe}b7PXGet+DeUHY zgE6Z*;w^C;H_TV2Mp2IlXQN1CG1nWbNW8uMFmN{4u=WCPr{<4%mNBbZSiu(WRBpeVika zr>SkW$J!h}d2f(Uz8ti340Ig#tRMS*k5(=L!{-a=qFcB86#JyxkjS;ner--JuNi$SMH6z7pd!r_&y=%6N+pw^cGmYxEX@AF7 zT+})~4q&p*DH zZkRce~rSe45;c^sxyrXD{Eco@}JyCrW**tD2U&I@j8Cxw!(Z@TAZUhUs zSUhK+F&>UMsYZAWLjK$qmR`aT}O@V5aX!)s)lNQ!Dcc;jUwrAIf_2kQN+D; z7U5jfAC+It@B>y1Ut;I&G3pHM{1-d(k9*noPFE9$8d*`VANts01Y(iMvg)o{!Ac7l zrs}Tzs)p*wvN~X%Jn$N|-KoZw>EuHnTgw&Nz7<-It|77!JO!ilc~o_DaXcMk*Z-Fo zUp%Fs=kyJ-PhvX-aLG@mWi*{&TcmF z)^i@dinnL^wm06BPhYCRKQt!J7K#`~pw?TU58 zIpX$fsQk(!H_v=9-Xi7Qzj@|^@zyBs_U6gA$D5K4Bw0^dh?AcELcxe5srr5Kc{_)UyhP-C1 zbMe%zKM%$F7Qc4J+7?fl{yY?GT0Gn7&qJ}6#j~CMJQQnKJlp9%FN?J+p6&FXm&KYD zzjnrY6;GM|^D>VrmF8L`Z-+}=8Pk^O&qJ{;#nXoTc_`MSc(&7@hhiOyUpr$Billt_`)|`0ur*D49POv=2Sz^uFlcCR*s`hAGs;t;os^WTIsq(14Qsq;9 zrOKLprOL1RN>!%8SE_ugr_@-D;wx4824AT;xGC18__j3%H^sUX-?rx9rdXTe+twW1 z6zfww+ZroWe5K~#rdXxo+twU$Q>;|+ZEKFWDORiawlzoG6f0JI+nOV8id8F~ZH;v+ zzEX3xx2-wirdYq?+twU$Q>zyc2@uE+16O`;wv>r+_Z641?=0_9NZKuUwqq|BW{Y-FTQQf5jVvO z7~i($h?`;+jAvV89gMHk9C1^uh4F1`j<_k-!}zu}RorwZRK4tkb61Y>^JsD(&ursX zwd2#cwU9gRE-<^*_l@wI~+3cYdM#T|ry!58med=k#5Il-gt za4#Wig>oR;g>=Kfn-9HC%DD@fZMou_be7O9SCNpnT=9*!Tt!3Pa>Y5`aupG!I@PP& zsx({kZmnV>U%9b*$y=`GY~FJ7uvM&K@@{V)wu*I3-tEo9RgriO+_$~4%E?=99==*T%Zhuq zHxFN}Iqb#iny18iCvQFHiLYYKlXrXb@KvmP@@;RdeDao?hp%GwlV_G8{Vi5qoDr*^ zeA}8QvWoRj-jb#=9vY?`Y7-A=HaVYBjw%RJbV@Fq`cdk zhp%F-ly7@uwUoEqJgb7SV#>R{c~%8u)s$z{?s0F`y~zRUq)tM0j9)!Ym(=u@+$A-= z-|do`-io`VruVsBQq$*MyQIqBrW&tZGSg>VyQHS~v~8&#)i+&I)6WXJq~>5IkGh+# zw&q}_SaahyT364`PWP1Q&q}M49IWI~Wz)7^j~bgUsX185qrRrAtvTW)kJ_59w&sYJ zJnCw?+A3yBH=pa0nuD7>Dr(wo^{A)mlA42?JZfpW+M0u#JnCq=+M0u#*3M!kPnPhg zpy|?Z4sPrV>8Dt!t=N!=k0$N-E%i;`kAdR^-km?6 z0K>1);t@Ri4m8VCHzPv&(ez$)F+Ax#=VEx$d&?)Vm?K!cokq&Yhmnw3eO?=%7uhE4 zo@bcyG}T8yzJtd(V@kvQ0!X%t)8=$kWonYK_kOU_J)Bk>BlD81bPXw^+CiVA946PO?8R5A*gLIMTgiHGy2hbM_7Uv&_T(X=>qq#h z^{%UfYq~C7mfJ0TEwOZ2a<}yLfu(n^-6@xGd^(S@HL_tDLy|YKcF22ZPA2c+32lu_ z{qHAHeeNew{p=@EtmP+BjO8a$Y~>;GIK-BmF}3I5X?%M3nCZeHd(MqRR-PM&Y(F;+ zjfmVhG*0|*JPa|?ON^CQwP=)QETkW;P3amRd8BJpaPO^u+cg&QNY@D9nre>L%ksVC zE0)@?S$+CmugjIad&`xjd&`xLd&`w|d&`worr8#(2PB?K z2~l;`k@Y>*yxpq9c0CM_<4_R92y&bKBF6l z#t2Wf^YLBXD?4pYOCN((k!Xy>T_KU(e359J?D9ntZATU%*K<$V`H zB^E6Ul~~mu)wdxY=T1)Tc8hzKdB*}zxEkJ?n?2TrLOqoig?c6>irui+us$1&=daj2 zjqBahzK2~kvl$$3V*Len(L|=6DG!-?t2|^eV&SZN*fqB4y%nPrZ^!t*oB9dd9fK z#nI;H(v@Nzi6`6CrNtO! zczfW<0(K7CSCen?#a+s*bx7UHX>wDxmwO#}eo!+L8{eRpPqx~da&4kdw%9O- zY_VaKonk!owOovsVz$vIGtQ>{tV@w%%A9gvdle1lkgbT%Uv`{D=a5}zGA}!eGie*m zesUv&4KK5+#yw_rQ!q!j#ONWs+9<82i1fPKC~x2=F?s;{d@s#LAK)ofULe)z?Cw+X z|IIMN^wY7p-&bzreQ&wqe47{fSaM{1&dgF|54vn>HS8Sy0`n7JsbVMI`)4x*FD;9u zIQ#aMET-~rv)IbN&0;JsFT`w*WnR1_#*Cz&MDf#2v^LGUQWsamJR@^!KI8T389(t3 zVK13tCr_E;CEAl$JH<$}BQKfaB4*6JWQv76qc!Gvjfp&^a{o^bsbVBAURXoUT566o}%lx~4hi22o zk(8s8$aBMv+QoIEh&+GqC`!+dMA3VGB#P?uBT?qT2T5NQj@5b%op0$LHBAhWJy;i+ z2SenId>A5Qk~& z{V2vy5vz4;RebL!F|z&e5n^{6#qyW28_g;zy(Ej}y(LGM_m?b|zl@wmiaNtSPKtG; zmmzofO45G0VkU37;-|}4DM(XI+TUg|)#b28;oHyRtQ_qYd*x`ic+ATm#hOwtiLs{C zPontB%LB%0Qa_1eC_jnfCO?T{BtMDbq2Vi6`*@f8>6JnBiD9@BFNs$M->=`_PDM`i ziw(WK>Bl+u%%mQJF{6=ESLw(3nu9-IJIj}*)K&Vpo$HCI?TV+>Ve(%CIf6-nRU>3M|rWTtON`gmde?4*u#ea=hq#^Id8H^ZCAXPBE5|C87WANEYHIs(m)rXv{kHiKnrBe+6f+Af<>MLYkiAH>5wQcc!(Wp zn^=WPCrLA+$CKxizeh{A(Y|!jMIehW`fg z-vN#B_0K?1y$R|m`tA_u$nl%t5Z>Rpjgga1XJXI#J+MCgCEjG5LWW|SU!lg8H~-R- z)3sV0`EYU%`+V72?(}_wM-PC*Zlb(2d3t6g>5zyu+emmD^3JJc`e2E?+0@G8&@1N4 zJ+vZTrJp^-h&jMFM~qeD{}?I@Y&UW`KN8Qu=|jQTCpC&#@z#feBRD+@aceh<`{&4Vp9X7*UNGwC(&YQekI8r?R>WNl;}1zOigmj%UI7C$LZb&t z#d)z_t{cJk4%RHz?MCo}1A@r4k5Jpo6Y;MEFJv(sWA-ETM7ll@!#)gAI)54p^M!`u zI7CY)7*S-p#~59o;pYo{rNgBdcRpO?J`Y^6o^C!|9AiGX#LK!h`3RQ7^Gr7}f@9SC zELo4mN;``ghpqAB_&f-l{wcCridCOsY-}NlPGyDhl%(5`!?BP~k|NPNu$QZoze*@c zoc9&{^g&Wd;y17KGYok?ij!WOknPxTnlP(7l zYucvCPnUm-r!qZ-zhX9kar%qUBQXN|3gamKEIZbynxO8iXLM2^j4T@^10MAiuJQK zOi!8OM&=nfV|tEuYi65H+hWQG@G|73Bjgp*NmC4$NliQe`L@m_&;4V0WD=y4}r?Un{Y2EXr zl0+B$Z8i%-x;}{2X0tG)>x+1jR>V-v zR16UYgv;Z>&|It^5kRVfDbS@;MjkGR&!3v)^Fk!#^8FOmnai>e+{w_Nhn+Vt|8aZ$bBdAF)X;JJ`U z6P`$p&sIL#aU(MxZ}9yboMF}fX=9eI@$mqoQ|l!2Ae0W%c)&h{obA00*&nMqj@~at z%rZX;X~PQz*S=qd3{Z0=w#qEL)8-25e$xBN>3F0ap1FC&1x@v)9gU^bY-4#x`hE6+ z+MHTyHQ%6?w(XYHN~!k`w57gBy+Ew*kw)6@Q|^fDmwHD*TRKrP)@8KKbtcXdZ=KFB z6}ePL)v)=!^W%{|Sy{-Dp4!q_v&bx!R4a-W$7;N!8($)6i`*{IZpBGzGduQXd?J-2$zm$j*7)|b zqB38pVk>{IT+J5v>N;i%Jarv21t~L*%~2e03Gi*JSSwvy>-Ep_M8np9R2i}^Pd@0| zWZC1oJVnr6?Nk4GwNJjrt9|k?UhR`#8M-Q}-$Lb2{3Obkcu9=2c|VEbng8H2?&E5| zmpt)|pGL(reiFqseiFqteiFquLsv`p@lBj<`$-hvcu9=2Y(I(O8y~+IZxtNA_Poz> z;=J0cjba=xdEy%{d14zcdEy!`cH6#s27Guz*E8Y!s&6xrzh9*R!N>9$#bVI7D#ilDsORo5Y8OoE-=dA30d*P zG@h)(Ox@4;evLaa4hM}4cw2y-sTgnevDWeoW}dqoPa7 zLh^@+RTJ1_o_%r?&)NSekbMU$*a_#q+``Wvp#$D>#5qztv5eSQ*|CAO{u{vKjU$f3 zyg1@$Opk+>c!ay;Z=pAIhs;~_l*Xp|Wax7@WwB8{cN`v{o^r;qS&bi>?@nPELQ-dE zJAcdiVc7iy^)|(u+U=aT=^7X|UTPv~YrwdIBXig@x;BIL?SZTIPtT^cyCBF4@id-c zdz<#&Cs7Q*YzphlR&n>5ttXQw*TQc@^U^dtVuDieXf9L=9_43B!K3_5DR`9M@f$(P z@06ke<$ub+6W6^;!J{m7DR|0aV4mY|k#i~f5d&|8YS?S=QaAB!=va|{dGZ-f#2vQU zY?_yjCmEH4NGx0qBJprJh{V&w&iJQWin!QMW?c94lqpX2lqufylqqiXlqvr6l&L7w zQ>Hk~Q>IwzI`%eg;G45PdKB&n<GC2YlWrY5r36|C$2G;f=9gPHQR`5kbV-y zd43YbczzPacYYGZc777Yb$${RaWRv675sVwoVt#0#wf`K_Qh-?jPZnGON zEpV@bw?uDNV()sl(VLZM!`^N5W+k3mA? z4e@THH|Jc#nvb=yj0pSM86%Z~M|@QZ9>rLt;1SoAfrsZOlw{8vXFOSoe!N+fl`g(4 z$`yQXiDFT&7=u}MYhxL!Hq50N9_G+i@!NIuJ5Thm*&k0bWPWV}Ke>7{>@C*QW`LZ9 zmWoW=>18$7&)CtdW(EDg>JQEXSdVrCzpdc6q0b|n)<4%FziP>4U=mAC(-Y6s^UD=; zJei*S^5xgvnUp8?Su6djgt*sW@~pWYX(Rjor@P)G6tyXpP4B5tT-$)Oc7MIA{V zt%y15SiH3IByh$avKM=JRG(!GUB}pGuU#0#CqwUpS_Fd_C8r+5BsujU4#}wpaYs%) zh&6KRL2Tirhj`jb%s%wx34A0*r5r@!nsN|{dGaE% zli7JD)h^B(Y0rY{=&nrsXGMJoySa*Uls9OLSG?DUPP|o*Lj5A*K3rn2I5`)!I z<=2*Ytd1+Uw!~$1RQa_fMyul*T3fF%*FKs2Eynmeu+eKU7uSl@)H;z#w(;plV8;D$ zcHRs8Pp&)+XXhQE#^gSJ)5&(b$duGiYPyHK(y(#d`7wzxOTr|cED4i1vLsAm$dWLL z9ZSL_ZY&9tc(Ei*swFN5Q#=W(BuuI{&Vxynps7y&o5z<#4DuiT{w!jUzsQL}{vjs@ z`GcGo#Q!-li0^Y^5WnZdAU@BBA@X-l4C3n?7`kd#BXg&zbeoUCyOD{{%R?rXFAtfx zzdU5}2<0J@pC}KRyhnM+A}^93iM&RBB=QpZk;p6LMiRY1ek5XjeS|&*8mLmnO`-@*bSyvx-XI>9mhkIo2fS z)~viz(-ww56Jv2b%1?2GYuxA7mpoQ;lo)S6m^{Tu&y7*OtbF^6-mYnXfQ_nV33FJX;6j|DsimvD#IQR~(v;U!Le+5HkcRmsjP%Hccv5zCJXE&p#K9=hgs z9(L+SERWiSm?r|$Uk!bZ&BYMQqc#z*g?NQ0S@Dc3en6^< za`0_8B6)`#Bc~gY*nj9#{^nu$ZbV}Dq3?D)KO(VrIhHMU&f(>{drj<{gJHW7iCxPX z{bJ8jMt_{alrs9`3?;|tukOZ*-eWWDX_#J#>>pLsvC6NE0joUD7_iD0jRC8?)EKbJ ze~kgFJlhzs%IA#%tGr?PSfigD16JkC%Ea2YH!k|y=9)uw#lddA*X*;ISIdzJ>~D{J z^7-47BXaF;kNo!H?a7(T=x>jF`(o|Mon!28kNp4i_HbS05V`h8*h#mEwW$YqG;sseQJ(%-}BnMys9zec#vO(jmOc-YBc|R+xc|Pn?T2kT>f-C%yRY2 zmyR!fxq9YL$B4b^O~;c5V3nwi&N1q|>P5$-bNSCP>RcXleAw4&K6GqR*ILbw68-jHi_by#l1#ser|B^H#qm zea&*Lb}r~wpgj&~*IAd}&y#x`(A!%%tg|k^AI*D?Rqt2w*0t_bJGEK1alewcJZ5Rv z+WmU5$E=7a{I{=P`xWAw<}B@)<>#uTF}U}z|58?LzZ$GxY4xkW`qj&o_j{+V+qgh` zWYDkh>Q}H=-e;~QXn@S+{TlIn)mb7enajrr>QSp-r}a^SJ}q;~K2Ff5C4b#8R_!{r zUwvIduDxnCVX6%DbFDvroLKd9f}g{fSoL#4zmBRut8vxpmejBMvl{(+slK;{>!bR) z)@O;53YL&-`xVe7g}T)_9=CXzD}uH{OuRoecG>bDvn-;cfYS#)cch2wz^J->zmqr+OKZ< zo*d=KKU~|?c2vKzsh*?ybxnOok)wRih~D}@A5VvCntuOvYpLOirq8h{laix+tfEay zj`B4(y;b5~r{0guR^1d_cO8;4j`B)cZ-sbB$~a1MhJ8rNI7&V8_bZq_x32ee*04{> zQSN=^DAliC>fil-9Hl+pzw&;#yz3Wmp?+@23HD`T$_xDeds?PZiS;I!;X0-E>_opxsXjX~T%**^{rw81zW2#f>KV0Po%HdEa_%3lOsZ$p z;ku;ycxt#RInNHDC-xrJbXN9hwz~L#>`l=BX77RCeQqq0CL`6BZIa7dw#n$R zWt*(0wQQ3SUCTC^KWy11*{fZf{kl`jHkqCK*9H-&Uj^EAYo~S+{4+cK{r2j$J+*#f zr^5T&ZH_Ir{$}6tlj6U!|DW3beQyWXyxW+2HU?sGYliQvAD(CZ@ZHYeRkFD+h8*-o z1jnDPFOE$yhu!;IhU&O%r>J^8xa*w?oBO6@qKDcs-LZ;g*c!qBvOn*u9Zr*h{CTrB|k?xm|t76gy2qFSR83 z!mj$|>Sw#|7bDF)wLUsB(l-5(Q_vG}6hGLNAK3N#o1um(X`zl`Z{!^GObd>$tPecz z`g>kqnofrO(Du$ukyhb`qSAqz}4Go-}vTMX*OU+Wx;yb8k#@zqj9a?Pol<`5W1MynWaH z1`Ok8oloKwI)p5X(c9RXqA|l!F8rx z?;7%Iv{Vc8x9wVF_O@M%%-yzYk(pa>@sjQS-1Rd*)Ay3O+m@~Nv$x*ruJ__kU1z`h zzg>&N_|@hbhta7`i>+f%f999p#}?79-{sz}#n!RMPg70xNV{ts#+?@TvEK#W>KglV zsl3JA@93S1bN%^JE!!N%omSU5j69d@`RH4gFMV&q4f~Uy2I%+OcL3e8|N2w^^H9YY}Y|)gsy^ zru}bQWoR4I`ni{gwuxyGZ4=WX+9sxX25nh6T(&VYb<6SU+s3W;vo)=+yPvgb-!d_+ zeal3&_AL|7QY`iMecz<7I{D7zmWuD^ZxmRFck}khZ?+D6YHQEF*6iaIUz7fP^&hq~ z{CT&_=vVuCzvK7!tADZ4jL&8Mi~XGMV0>ibp2zkXFKpG?zx&EQ?di(L^f&Bp|FQam z{oL>D)4p2$&v$>iZGZaQc6=On>*H@$-`mgsVE^iY?LLR)OZ(Ip-3t3N`$S(y?|1(s zvKL1C@AkPT*1EUrE9R%BnSZzEgZ|Av$$wq6-&gIwJ~De|UrkR|UA8~{#rAODwHDKL zKex~K>&ZX)a3`PqY1ccx`tSR3e3k!SD!p>S7(PEkUrhI{e@gnKFRVr1Pg?KI^`DSF z<+kfu_|A$a zw#WLV{pr{~b@++(pV{07yO#d2`r2sOwS3#$@Qe8&?fP&6*N)%M7ah-Ex~u8`?dEpU zF4Ln<*ZzdTuqWu@bw9JYg)cfc(J%aM-M{m@`k|Wne0AUMiSPIJeO9No&*cyH-?Y!$ zKfnC$dLsXM_m20Ef8HOud!P16^LgX%pY8MhY@|osF7!>GJp9R88&0%+EUUZkpCt-h z{-~nBUxlN<)sAf+1yUx;qsq|R{qFXu_%i&-eH0*5eON7UWl>RJIhjB8>)!Kw&(}s% z^$LW_-A8KU`u~QF06a40U#~^O{*#6^Zpuf)uWa}4>$QD9qNxAO<~I5oepvtKo==te z8M;KsopS)mE3D*^2Oq ztyFzwJ@?#xy&0`Y@6!E!zwXNa`+ZV@yQ}=)_w24Bz0j0x6_9@1Fh!}f;l_xb#D?DN@G1Ymw@*)bqxS=|`0Ujf)x0drIVzMI># z#)nk_%u_8T1`Kxq_?18E`e1kNr&o6~2H3Co@4Y{V_x-(OyYPOhpO@l&KZnux_J7gs zWq)Dkb^G6+y*t@4?fXBPVOAjZ^==}(Pi)IdG9n;IdF>-MvTX$5S*b6|Hn2a@hTbjBcMtdFZkNKsBTBBb3K z`-rfQ2)o=z=<5h+m)eI$1pU2)!x~{YhnVjy@V6}PdQOpE-c5Dj=hUXv!u0I@epPXN z8iC%Ur)T({8b1%>?V7K+zS0TB=_DFH7jTtzLn`Y7SV;;ElQhLgEl58gco;aTC0%$3yg?(~)T zS#zg$<~nD5agA!xs+OJ4c2#N-td_ygc2znxYX6obJc6G5ivDhXIW(_4v1f;#ST6TB zIP!g;zw{Bbzj-vhuT~{8)H401E4pvj=qLRB(w!XY_1Mw-tJQK>f0FQ7@7lU+)bd(? zX7MhsQO9}xTdj6^eSR*gpX=%KoXN2ouDnvMI?m_H%Yt(!{{E}!%IZU|QO%X>s+D#5 zVmk7q`>5o~b#=>muTR&NwR&cFN&p!@&3O13#eQn?@F!GaOl^i@$ja4t{gTG}zjm2k zzG^b7$@V9GDGb+y9 zU$v80rfI1xwPw-$eWugAsx$Xj?R?N_;w8-7`x|4=O0P3>fA#%4dr?qf?9eTj00%OR2K{D{2wB9OQyTSF`gO+h>DJ6t_8s{?4w8TS>r!)CZ9Ko7BKzx7bJ~Yh82xqWI_<-%4!^&CGVifPezkk|VO7XI zyRzrDD(U-fwCdjL)%k@fw>`A$Si=6B8)vFQ>Q|*z)!#T%4N|`fHe{6e=YRWuIEwb%3D(Q4xO|ouX+;g?!KCv6s zRS1v!>#k^eCxhbCJpP`OdgXuKYgBXIy2|9d*Qn&Yb@L?Wy*||)DL2>FE$gl}kEstW zBR;m@nmO_2+Dhg_u2ajMH`mrTA99^K4!v2eZ^pY^Qif39vvae$J~MSS{YYGC7uC&) zm6)xGjjb3)cpKe zH80n%LC?>hohd&jr)%Zs&(4&e(-o-sxtvk2$JF#4H9wyvKc_3u^K)%vJLTte4T^rQ zjeV!C;;P_G$Y*ED&*=)3{OqUml+U`={hY2q$yAZ+9iV?zAHE)Y*Gy zxkH-Iy!id9W&7>!SAShfJMsOxYBj)FuS-pbwaVbE*VV88D?Rh#cdk}9YnxQ-9|ze5)nAvI(>|=O=&wu5X;UZloh9P;*QM&TsdM_ya)s_k#b;i;2KlfmMD>{$ zzp~o=!c=t3cUCUpsRO^pv(zNpUZYmUf8|W|$+p+1GY+^?-5=*VUAKGGqov+c2d>I> zb;-IbTFO%gevRvDl=EJrn)BAxE9bpN9p_D*+}CII^Io6kQwKgOudRG+iZj!$)Y}2$ z*SM~lIr|;e+au%GxUQ}_?=>p9a$QYx-s{tK<+|CIbyu5LI?B%ixoX$V#(cN!4KOeyIu}x zo2++b$8Eo~nHhfz#_jICZ~i7VKbz!br~nZS|Mcz13YiJN{Ak zj+N)8F2AC5)%_`3L*SZF{5sIq9>$ur^Pu#d?3P z|Ah1@`Fn+ZJ|w+qJO9qQx7eo7^6327h+bPCTcI|IFq#SZn!1b|1dCxO2MtTc;v_8*Kk`n?KV>a2~__O-23|qv2JjepvStn?Iko zil@)wZ|nYDdi(H)>Em-d{rII_mG{5TbG@;9aB6>kWmo8*Uw(Hz(H&E(zsvbx^`m{N z-`h8hJN`lNVOs*mb7&HTso~e|G)neq*2OQT5dN z=g^$+#IE&wi+1A{@W-}4y}r%+c2AD1%_kQ3JaRv?)_>|$IA$oL&mU}d*W+eV)`xt~ zdP2W@+kAR?&$s(m>s$9-PQP?-Eb{u|r0bbqI=}zH?u3 z*SF(6dDoQX?_P4Zc-G0UV)|z`e{Qe({*#A}_s^aBna!X4=Pr`G>Y`)++2g+VPoMs| z&7YrEp?|c<;yv?hZO_zyw$_d-qx8;s^6uw5`kWEVS+?&(_8`s}HX#Z)5zE>sy)5?c-~h{+Z36JC)nZPoDaz&7VD;*~?F# z{<+PcU(f1g8}*;9wX?4P{C#}e%qI*pUy7Z>uhjat&!qm-_3?emf9ro{a~pkTA4mP< zL!Sdk*jxJ$>4`pV<8Q`wHOSY}|I%8H9EHx@`q;bzcE&IhU}$xAzr5KX)+KXK#HA9tN;@Et}1}^Y5NM`^K<0z z;m9eb0!Zs&`&EGb9Kfb%ygsiFmAw8ZH&;?E*8s{9^nMP2eH9<;Du6%Vc%vKn-{VpBK9Z#`F2(?77eT`;q^4yCFBZYMzn*uJZo%E^00>@9%c)KlQ>s z?=La$fBY-|`@HX!U7nHmd4F87c=fg)@9)R^!>2r0uP?{@Y0hAId4IPn{;3!Cd4Gv{ z|Knfp-{<{(-k-|o`@Em>*+0{6|6PsuZ!UDazuR^G)C>E(zr?)%@vrjl^Zq{X@AH0b zEyuM{u5Int_x)6d`gs4=!ty?Q{BL*MyV~EEdSRdUmzDRE7B1L{CGP!?d4JzN^Y1hN zKJ)K0zdysbs}cWa3(fpr>@@SIUf5^;WoG`Qg?;Adl%jI{vuPL7es5p>^q}!Rz*_cU z4YlpLKKr@;f4;eXf2uFdcYoNPet)N(*VD^c2;ZOVyM&W{cQy0x@84V2_}?p)zuDM-x4Zo5hkf>6R`z!_ zTzIdBefIwtv;Vrh?Dv2FWPiVIv46tvL!ZcBM)vo2BzfNc(Aobqhp?Q{f6D&5pZib! zu+RQW%>MhC|Bv@HzkTk%U~#^d)yL~1wV&F&TVZ-ezx?#+pWFQT^;7!GHtIiHYsd3o zx!dLA-R-}t+`rWG{B}L>zdzA0&5$lJ_wQ%@_qo587t*e$OI zKj*)n^B=#re%tl_asJuo{{3mbAG&ssD$co1^WD|Bf2rsCrFg%)+`m8BZ;9jnG=96V zO6N4}v;RK(@3a5Zc>uK$bIMUZ+Mb@xA6G^7pWFQT_0fFUM*U}N?d)dc@1Jseb!@*5 zR&VT0;ZyI#Shte!KUUw`zdT+&Uj5en|B0PJ{lwn*=kNS`v3j<8yQ=@|x2v!0XMeSy zd}RNByLxB@zjS{)uzi?(uYZ4__rxW8=KsR#_xAhJ>SFh|>-LrU?<=eJgjeJVugVi% z8xUTx``i95UJoa@5l(P3oZ!}g;F3M@d(*UVsru_ifKEOWC-mof`-pkDc@Ozc?Y{l1 z)q9uj>a_sAVZ4s$pJ6ms?4MyQ_FP-{pTBLsb)T@9cx0pJV;e6Y+6er}zVgiGHWJ^TYz+Rh{eEeq^A}Ah?ps^_JjyRk7+%;l z|J_=9^8OT@@f^WD``&|YPT{%5<_9(_abW%Mc*i|aqaflWiu<;U^`23DX$;T1J9f}L zy>MWB=?%eO8STq=duV>PxsKf`+qE#=TSTSvaqhC-t1}X{#*Ft(_h|;{i7C9^vuzlO zTlcz|uz#$6vj4v_AGkX&uD-VaKC}Dl4)M9MbkEz65wFI*_pOn6#5gt5zPE8)UWBxd>zDhHQ%mexyVH+~)&F@z~uKwFvePlh*Gv8^uyZDr{c8Q@Gp{dq9_^=NM?bf4He%mz^t8)c-nUO0MdW@a zVod8Z)-S9FUz$Vj+JF81cu9Lcx9#~NW!M%P@ERxO?*C;ZZ;Z%-T8wr?0z2Gy-nI{b2q0mLr!$3L zL0i5)OZ54{)i&iSGUZTcx zwJ{Gf(KViycgshvDKbpc{LpFXFQ%flRK)jnRDW$wMP#3*O4Y09!xmAqpP`th`Ag$F zMhu^(`D>if9AiT9z!Gp`Q6>}C>P*wy)7x81H#)_avaa6Za5{<&)N)7#zq>Aq~` zA;tik`tpya%M**6hgLOuH9IxW>pK44G&EL48|9K|HeJWJ=ECRie0OT2*a%kg&?eUH z`aG(~$zbErqbuZ*>BHxR9@&3=UchG^U)XQ2f{vTy*Q@Wd*F$UZu$wpWwlCQI?&rAM z#H(xX^VB+)Hc`n}&QetJQyF~r&a=_e?w<8|r~TZLuks!)z|&J|30ntSZOF(v4@`kiO@r*vz1_`$`PW`{^-`PJIbT~&_cW^gbs3?}9U+?E zu?+71^L^88T4tP)R$J2CZF=hWaaS+<470Da_{^x!$@?hnFHP7y(j;A+h3&76*ofpq zwg=Wjo@>*5ZL3})cGpQ-^$D@LPV!%k?hdgxkqm33@jYt2hIsw`tpD`5zD*tC`(u)` zrYYKtey-d8opls#vf*{BT-H%=a!IQ@Lp+|oGtjiTL%!o{o4#ptr*6Ejo~Qffp5>;; zR>An1Y>V9Kd(2Yrc*ZX0k9>^ovoyZu?(4{>R`m@dW54>?o}5VCrk}>q_y^n9kv|*e zr?vXLz^?bAP0wBW4#9Ni^7Bnj-mR?r{92p7ArcRBl&|Q!#@i4Og~tT9P0_w<#P?DC z!&H1^fA$)%&8XeSlj#m^N7W`e-*`W-F{bjU{LJQJPwl?_WPRk-a@xt#avXK@eY76a zePKL4>vGcFMc)(R_wji3pYK?Yn&`}T5>MUHuRGuSSxsqo(l_>h-+AVQzcnT#o{#%) zJHj>!y!n3qV|q>RLF4%4`@qv(^}Ts%_bPYNV|u@bwM8uRX8_UI_In3?oyX@&eLq*4 z`*Q6aG;#&;zpn;7nmjcopBwZZePAs9EVMs0UDg!4?N{F|_}C&tiuJzh&3DoI>eB6Q zrpSAyywj=AFYUO;&d2ht^_0ir#MD1ezsr95bJrst+dZ1T>2}!r&Ol$oKDGXNY_0$9 zeZ2d@zJJ^9obNL9Cn(yJ5ZA^eH+M=hrI~N6KU(SKEbaSV&fjKLh|?tBc;)}hRN;#3 zHF3@Ao88Ws*IMM>`X+N-t5*y4uehTX=5sTO;`_r2S;ReE^-R?%(CEA88Ccbz8z zUP*0ojcweL+MLfX|7CH`=O8Xy4KbXV_9w!8|G3Xp`%?qM$dpb`@$*ElT7=r|TG?56 zxD(sB>b_lxS8QG_rICZzN$L4FU$aOfi?^1Af3&arE;HZZ)p0~v0H8Ny2WR*PQ_^6T6J^MjcpIC*Sr^xtp3sJvA3pn*LHelDY5<8{_Zn$ z+pMbX`hI?Gy>wv8^La_H3}e+brqR5gIiuG7{G&PZ&}J}wM20`p*L*jVk0pP$=hj}@ zPObK_W6Y*`cXLLqyX)idAMGw4y>n`xGcMRY^>-@zJT-aaf{lvFJlpJ+*mZ2_X*KWl zkWcGAr!^O^eLNn2ZEp3wa@*}d-}PODMe`o^bXs>XoeIbu^oZ*S0LY$wA5t14g<5@K> zRw~rEShG;#Do3IHoRd1IqA#ZUQ_&|=oQl4g;#`7{YPCV{t~utN&X;5IKCZLlxFxdV z!X>if%q6npR%P~arQCaTp4r4*xiqK7oy(%em&>BYk;|gSi(1sWLsl~T+oDe;>& zCH~T;#829k_(zwrUc0d?bBXNwPOJC3zW?g%`mU_AFTtyM1&2qK^K(9v>!;Mtx%PhE zYI&TmOKN<%tiHyP%c91MTGZq9>0#YD|0@4^Rb4XUz9li^y(KZ@yd^Q?yQMJKvpJbj zsd`Pl%Ko4_I}TkUJ5F69JC0o_O8J6t~PcDg*a{Vl%d&7XVp|DM`-?F-uz!?VnOYJu-_+~rQH^VYZHtHPTf*Hw{I zKd!67+aK3e;r);2s_J#BVvXI$gE}`+LaA2ThMPFBG;W-}G;X}C%AHrH_))d)D63MO zxEx|UxEx~Kw;W=8r$x*@`Em6;-?Ihht%!%5_-oE*Aa=ottJFF1k~$|2Qs%7JXXG)Z zyo;;q)Oc-K)HrWh)c9~&)VNZUdKuMZ@57uI|7lhYW$laemconYmconMlz2-Okn6N5 z6}?sVrJ|!sN<|-)lykf1zwIg8lWt|9e|ln@Q^?Np{Oqs|6g+1e=x@$8P|uugaPQ}8 zBY%EuIHGRZ=G>Z?y3V;ZGS%kXnwe^|wT9~BGBWHuRW~sv?}IuIs#D=Xkt#f>RfPv7 zx8}*mH|Th-O0llwoCF2Olc3#r5>y*cf?nq*8Sj`MX7RbbQnrjH+qaAc+qaD7+P93x z+O}NJsl-MVo)|UO`zJ=Db)FdI)_J1zoM%>a+&Xrb<9`$!Pl9&iNlP|`ZH!GwkMEZ4{ z^rndGv(*FBt)tDD*3n~3>u4~hb#&Lh^?Gh=mHSIA)|pV~-1+1zOz2jL2{kJ*q45Z& zJPV`kh&W$nVpOd`gPt{LP_hOM8lIo#o}H8a+MXKiYl3awf_k349T)9wO|*p)TeOAF zTC|0VTC{~m=5DK=C5dixN7|&DItOZs=0IuD9H=mw1BJHYh|9hxG$%lrt>So@^U-7+ z0cwmRK!+^|4sDD*%%*dXwEPW-sYSHbu0^!fu0^!eu0^!drp0=WYnN+XPjwDdHh)ZC z&w=)$IZ$LY2Rd!Vk!L#e*(%1CnGR*f5unL90@N5sfDY#;;Q6v)w*AW9ANT6rds_Ma zTx;EYzPFF|qS{A!QSGCa zp^EzJgesn>6Y4l6uRU={`+BGJNX~{g6xncvA{%~CWWxzDY=?H9$1nCY$ieE(>U*oe z{p|4L(W=#z)uq*3=Q~X2i8>SdS7Ji0ulsHJ667NJ%mgiyMlJ-t6S@41a3yN1@LDvc_ zs5hFWUZ1Eax=yH~^E#o5`s;)$o~RS*I7O*48b^JtdT0|Hu87avWxwGEMK+ue!8U$U zeYnDwzVqekC!2qIwEEfJT=&wRMVe>7*3_(1Nk(4KBSZOmWawUx4ApCq)$>(c-IRJr zK*&KF0Ag_*MIGH(e*L(!u!2-?JBD=JI+`lJI+}mJI-1n zJI-4gd%ZG=tL}O?*6HwQ`<%ZG9WKUL4%GpXwYvwO}$2lt6$J`oeib8&vl#G(7hrXE>L8{6On9rrGN(_ z^GsPOpnfeXbgxB);r78S>lvkvc&1RWQkL8$i72h(oSccH@BKU*RDZP ztHHg8=BU)p-cPxHoNS)Fx((ZSKX$r@QT;S#QM{8m;kf>ey_4hJYw1p=uYGCJxz|9)cseQT$b`rrM(pV)uj zuC8`}_y2xke}22V*8Sc8`-%Pe?dp2>cmMA$caCkNiSnuxbumS$qzz_wk|OR{ZQv*D zJ?mDX`CjWgOS_8qvuD$}i|y)xU%E44-mJc~I?_*M^phFy+kaoKo?0b&WM3Js5zKQ& z;dTs+Um_V8$wV?Rrio-=bQ8r;jx+ib?K8Yh_EHte7 z=iJ}1jW>o&_&|vX2rzZ7&m z#igM0DK16b*GGobB}#6n(<*wTPOIpgI<2Cg>a?0Jiy8NBs*>_3RaTYwYTmKHhjZd6 zbxypb&WVenIhny6)@Lo|eK#v|NQO)F$nb?88BWk6L;G4}^^B)cso{q@CoYMtC^vKB z8Ffw^q|S+-R5(>@v0j-uAp_idHJpGoj%6Z|=rV|&&qJ-g;B8ZYd`#d0)!$A0Dq``H8g?)UaHNA||s;Yei8 zjJ8W&oU#;N9JCZ(oTbK_*AZ)YjK8ey>r;Dx*bzyp!_qnyt{{aRG$UW*FFYf+)~wp8Oic*B{K^qntPKiSOP zqt(wdyrHpHgO^OGTZsv6D>0#LB_?#O!c@;TMD~`ISL&5-yS)&n_TJ0dQ{y;5kqsv( zvf&6tHk=X3rcvGGF371+e`IbfYc6!JMTO$EsL*;_D)uW3>zX-FW~)^+Lk_eY&4GHO zInZk~2MUeksOSHP+62|ssnBxE47QmHWouEPb1f=VA48Src~l;gCCUtszEyZowh9lL zR^dU-EqT6qe=^vx%J|yO3j4)&LA}}OGwaj!#h3ui#u1>>I0AGTM}QJz2=d(=@yeVc zidE_qQLJ*Oh+>sIMWm(ddTmNA(XO)T<8a<@bxJ&-O^GkGDe;OnCH~Q%RH(q$`6lPX zFEJHf>Fb;ir>OGb5mi3i5ye;Tkc!PCB@YgWi612oidW%5;VL{RT7jothbXDMPN}5* zI;D~$>Xb@8sZ(mXC(rh{Mk8Bk%wGua?@>e|SW-|8PeXANyK{Rg}L!>XEG?^S!op6senG6 zGw2YZa2+BvtwE$(F`@H1AL@_EoSXUZf+`=*Q02oX(R_K$!AK%HCzaI(j?g2+3wmU@ zK#vUl$B>okiq(C|6RY-;CsyMnPpq;_o(MfZv}gGI{jhgz#`95k-ktyLg;npvivE(l zDfV)M`Ox(==vjjXHEYnI<#?KWx5-dPu^O5pLsNQW=u3|bt!a_f$3f)fc6D++j^!Gx zQ=`mfQKQ;rQKM*0YK8IDSrnb~qU4r!`iJnM;id4RUKL(ueukOf>&sKCET7r$vS;WM5F4QXj7dNO)7KNbFWs#fKKbYs8=I@ZRSPAOW{S$YP@--MYn31pq%SKvHGNF zRi6}L8%=z)njGJhXz&RPJVw;Ut7MHtD|nLbI{GK zkKZHXv$U61*PG0!PjL|2qB&4bGzSWa=3oUVk|Up?9FF4ExD<3e)n5v_p5ju_`4pF; z?(3CdME#ma@w^x7w0NXt?R9=yoU;sC{Im>OT&6{vS8KQ{wpuIwm2=`Nbxs_m&WV@Q zIdM@m=lChrVU-r^?~)6rsBz&8H7=Z>#)a~gxaxHed1M*1iXN%=t)g@4w2FSJ(`vdb zuYPcsRu(MhCh?UzCyr9*#7pX&xG0)){Cw%KN=x5)V6()BtH<3;@%+z8KD3i9#t#4Wmgh);A0afl{iJwK9pV(YrpYl`PpZ1#A)Z}HVK`WBBZgBHJO(W=&UxxaEw zmAY;dCyvspL&`cCFR63lqG(Rv*IIvG?(*vE)u*N;<6>M;;$vJ z;;d-ad#i)h>(O}#m#wyonXT(taEJm64p3l0;R-A$IGUwi(~uKlvsY4eof55UQ{n<` zO8lWsiE}h4RVyiclk?#hjm%kAcQ{3r509ww;f{E|Lz@R1u4=~BMUbHVcoI||PlBG~ zNlJ?fP-`%1%Ctnlb8`@PQH&+E-#i?@CN)T!pD#osbQpb5+uPof9{x zbK($nPF$nTiIY?~b*m{HloR3{m3&)Pe0WBe5Vz)f7Cf~O>92i%!z;0IdPIYC!SK_%&Q#y zqmuv1DhKE265<(MLfoQDh)-e(OZ92^qvX^e%-rw)f)q_WQnl<(2)+6MOFH z_3D@IU!?bX`CAtr*po^>+1DQ1&-l~kFKjI$|D4^AolExrG|EHc;hhYL{pRYH?WRq? zc2DutdhJoyH@EG&5A6EibbWcU`l{>216ySu*L~ifb7tC}$lq^!(%qBO(H@#V8R=vD z-qY@0`F)$SUvdf}O&mpjM}BATo=aM3e_u)#`lk)c1Jl5>cgp>LOe?Nn_mDfk@0U4t z)ATxB&p>}fF+8-`cHBAXr|xu%$M3XqeU?4?+w@9Fl&h#+jH%zdoT*n+w6aSwG}el2 zoMZ2qt~?&681%sY_S78sn|aZ7H)m|lvos`m56!Ko=UuKmv`l*6qR(r)>Z8?{_SBQQ~eFBRtzBRou3G z_jk*5hh64RJ%7RS-z&>>FD>`^xZJa(kI2cB7Y3Qqe|9QYKIx*-^Y`)SqKMM^>tBw_ zhRpi0hl(!g>pN4?ai^mr5e2}vocrGs>)(CWoYUR+9OLIR^3~msEcyu5$EWpm{KfY) z%TWORr=MZs2p?LU@tLLcKHe`)cRp%6?({dDS!z)eZ(?EeufW0zhyn}iAki%4$hx0P zQe;!n(^S8y=xd5iRd2P-q^rU@v8p!f#Hxy|6YJ`DT(S20opav1uL&QnsR{CUkXJ7K z-sdH-qS+;|qE-dg@!XzLomkH3_KJ+JLCStZfr@M>PK~V|(b*rc9Ace3Kh|cwk5v_0 zC)U-ma-Prikh7v(y?j*GBxrUCtf*Cib-YqD%=76xK2QB(mc0+#R-+{$y3{2^jk<(r zP?NA8>2>n|SeJET)EZy0Y)g!KmqU!2^@#JFihlKCf0=IZ}s{(7khCa;w z`R)hyJzTIg!r^L??|2xlC(XHzIc|B}?#0#kI#BM?uLD&t{W?(mlCPtl;fd+-*{63w zofdB_gBAxYgBG1@(JI#WQS%a5QL++io@LRg zQdTH)EDF`8M4Q@_s8WHF9lXOjpylr9hokw7d{$vdhXOU}P@X0oiqoU3=S1RSeC$Sr zbyl>w1Xh%~1Xgsb#HyUXd!^=-C{!uulvM`W)TTt0@swo`5;YasbTnA@n~w5Iwt4mT z+MZ52v2~<$-p!9zkELg8eO-ys;IKOU-oE}PJNYV|eRbFV^>h23W{g;;>i3rTr}{}y z`RDKYtfg(Q0u3(vDp2dPuL52BJmaa1LO5+si|WR^=i--r$8UZVS8;3cS5Y6yP(RF3 zac>i&>tsZ3Ju;%P9vKltKd@`WBUowyWLTd zqS9s(;!r#Z(I=jSm=jMzq=_dXzQmIdRpLpAC9x#s9AS%kXMJQTnTRuSakpe5;wUi@ zdz6@nLP|`;BPAvxlM)j#Y7V9^tg?AwztX!chU)^CZO`cC9qte z8ApH~;|Ne;3_(2`6HVuj>*>nB?M6+f_8z=DHcRYpgFCQy6!?w~-y!9WL9aSQ=vRjb zJ?jvm@AgD_PDat~<9#{PhL#mrP_Y6F`c+^-xh+`On?9@z{GECKusF!~cK3X7UK`ND zFv`ZXjuvBDM}0A^qqp|0*K;m0bN=X!2J0-uOa&Hns=$J36WvKK&>sa zM_Ff}(^v`=8cTsT=ch<#L-=l&SN6on6MM(^lkSO-^gitu_GY&6dy}uswlcIW32Kce zL9z3#mTlW7s5ZV&P;M+qKI=KGRrSa;6g|~vC|s}4G!#G8XPOSE*Q%}R=51nG-k)`9 zJTrIowp(iav@B}8wk&FVs7amI;J8qu;wyca^Wwjy@Z!9s@Z!0p@ZvTV-tkJ!u>MQm zd1&LD*S2rpPtw`tTgu1tmD^>Hk`;F?ffZ*hffZLRffYxov({@ga+*d4PW`B}c6*-K zQ15G8xU9a$kISOQnVQsjrG^VNGjll)Ozv9>FV0&EFP>WpFK$!e?N3@CR+Zzr@4_mN z?iVI!*5_I$&s(3G)vvwlWBFHtBbR?Acy;+#f{T}ZCH4A?EUQry^f9VVOrBj1F}_|7 zG5%c+F+SBJR~voP_Gkzw4ET&W`A$YxDME^ zUL6wSsO1pjtK|^muH_Kpv1Jg~Ycs8C65rK%aid1nu$dPxE`=8-E`=8#s`2J^89r0X z$z@fB!}LkkL|cd#oaj{eo^JaDXM&UM3oPB%+KfV zn>(@jg5eCprP-b>9=3*t+q8xv+q8zh+O&pR=5H;39;vNrrdn^OqN&!~scNeAb}E}{ zJx+D?3XQxycU3_g%loBHhEDa!(6AmE`qm>u`*CD>MTqj-RSc!}b0##e#Dv#gPoCHj)9AMlztvD294|C;sY@MeDcTXVI#z zlSOO2P8RQgJjbk2dOd9U7Q=wGkT@PQr~euyJes9$pJ z=S;-I9FwE}<&oo(<&op1<&opKWs%qGiuScw>B2f4 z-fWk_&q9a)H0kh|CLKPCrORs}+z^{j%X$bOsBz%{H7>NT#)axzamRAkZ8VqERHYc8C{*l895n_&?CbOdStjjj|}~{Co7+gLG$e^t&#kC|l&{K%8&vslNHkwwL*R?(tW(Y`;Rro4yr4&h z3-rj)e|xg=p6ub=4bN`;WHX08%eeb@)vl}VOD+_z#)aP1xKO(q7g|^1s#hLlfYzBQ z^-k<_LfiYZ-ha45wf}I5Dj#l%=F_Q+a%bdZRKB;%CxzBUTiSCL^sT^xq7_)sumTIJjb^Fm<*2#_CD+N&ck7I|nGCh-k)eG( zGMo@cme&U;KQ5z`l>wSpVnXdoOz2#R35B<2DpeGv?2;r(&m~Eeic69x?Up2SEA~mZ zFY%|9KdW=!-p2LP-pX}svxq0%6Z*fm-HCIpgsp2i?+53Y%d@ZSYX4}xaA-YnVdX7d z-tq3sVGH>#q4q7-syV;%c{2aH>yNtA7gyu;%yb`GT>rS;=(~~Aj4QBKzPo4+d}R(~ z4mI74^tA^zhCj5^R{dRpyB))ibvY)8zArgV)P3>1@TkiO&+QKQdmGcexwN`CbDS(& zj=m>TL{azUu4Swr?T#G1yC1jhF8#gpkZ13=wgPx$fAgmc$Zd0ar>BX&M?;dRdvnk3 z&Fgpf<+}Og)~s(p6|>V?P(+WKJ*+h_o0>hzO;ODWcmG-{r8|dKQDi$b&9@+ zb^`D#`#oF*yS%#e?$_=VaULFKnN)a~Yf|B1#z}>Tc_#&)a?a#;HugxdPnAze?^FG! zr2i>CB|l8@Df^?AmwVUjexB>BIU(=UIx|jM5;N{v5;Kll5;HDTWFFU~z4zu=$M|sf z<+QkN8MOFq8MHWU8MJszgLb@LF&ybA#DJ0xA8FFz8%;WVqDhA@^yuof6r-jkG3z?0 z-p{(esx#}lt}3_eBirzx18&YzNy5 zTaAjF?JoDS^lnmS#6!xAI7gWgzo;_Svnd%$p*l>x$9}Lnvr=`qEi+DAQa|IoB{Abf zMP}{lFjsO;i|Z6?g0hyyZ_A*?Y0IF+V;Z#O4$WbOxXXS0XPlC9I48k*H`#aHLI327EW9aF&IH7*>X#)a;cxaw6D*+wBd@*L3}d;a_fo5w$}CqBNn z8U3Txn{Hn_4p{;#u2}*rPFeyh?owjauHbN0PKle8@^e{R;v;QJ9HdQ&cT^}3Ew+#6 z=q`n>-KM8Iod|Du4v$<-?uqb$4iTEyAwu68L`$i;+=X>koS>0!*R$f0C9vY2C9vWv zCDy!}VH~5B+sbMNH)&JiBW+3?q)mx;G$?s$epq#VWxM9)J3-*=Yo$X*{G!Z=Ta+2` ziZUZkQDv;xNaP-c{D^z%w0LS6w76>-wD@cpv^Y+SR=cw6{grd#E3Hgl)}S~_of9vq zbK)We&hl=Pn>6W^JX7|al2b}LC4ZE3v95S%=k)oRrM`-I(w&`kYG*O}Np*grwV$PR zx@OJ`&qU>K{nepV3m~rEhnDOC~m~o&I zGb5bg9HDFaxI6py*$nSg+dM}&?Yto=F4HH)Tl%CpN}m)TX_MCHXxmZN?@xMB`2$JMxQ8MOFq8MHWU8MJszhqj(8)bd-M z6o08it~x2+(kI1N`lNVDhg3gXEg?shjJQWBdgk{U-zYQUn7J5z7XR4lwJ&V;;UKHn ze4p>G_h(PFM^0$f9@=biZD_33wV{_5?d2;z!x^o)`*x~rbQIBcD@{#ZHyVq$ZuHi= z?RwodZ!Mh1ul#=0xlrdkwa@vvP_7yms#fDd@ljlP6@%VeRS2cxb0UFCqcFGB&ao>1eL~;)U!O%xK(yQ zxpgA+tV4vZb%@Zn4iP$UPn2hK6x}{Al(R}`S%C!=E3lwn1s0UsilrX;P;MI!)j3dY zGzW@}=0L5{94NIFM|5P3kEbO8nrs&>^1FZ<;|S2<+yr0Q9RJDcm+np1Z&u&ge7}!` zhNG`mEbX+w|D3Pg368W9WSwy0-VToZG6MmDi@Wm{#5PlsISJ4ELdwI7pikPia%) zEe*;%*W#L(Ojr6Q=ff|md^kmw509ww;f^T2@w;D!l~?-CJa1TApOK^9sdjcM)UVZ7 zXkLp7rRz}D=SRp2F*z&sQ1si-&gO=CU*Zw%zQi-ylz2#kGOtx|jYcjjXLh1%sod9j z|KSwX{=*}xe7GZu@1C8j_c)us`g--+=C^!D>*?wfn}hLs>VEh4lWy+owCU{7#nq4z zKPWTe31vonq0ERkR2l2_16d^|=i!$+A1+em!$GQixJQ)_=S1`6)dIeV&USirR`1!o z&+vj?pWy;MGV~us#(uKl+?3zfc~2+VH2NJi~>@!@TM~41m$m(%7GB4Dr zBC|oA3f)JRqnhh!jGz1x}5#RiOV6zgUcbteaj)ncWT6}uMDeA z_h6eH3TLf1hqU-?8MJt88MOFo8MJsypSE7Vk>|AXGJdOb27@YCU|_{`^_j_w$@ZRqIpSa^5~|uL68?UgEYYP}|EgrGxDmap1f)_lGg!Ib}v% zr_6}Y6d3b5zg2Cwof308Trzh>cRo7&p-G1`H0kg}6y5NA(C@8|^YazP)!e66Mfk6F zRfK1cIu~A07rG+6{GDJhBX0oU;sC{Im>OT&6{v*EhIJ zD+`u0Eci;D6Gy3Y;w5!XTol7OKG$rx3nG2zq19~uG|U$^AN0CuomP_J5Ir(Hp+|-b z^vKY?7FoUSA&*34yVOT2r;1LG7s&6O)Lrz4LjWZbe2#2=b; zI75>TPsGsOwH-7+TD^2MTCp6z@n$M^ww(PMGvWkgM*N`6h%1yC@rEj+X64oUrOtLeBGF+fXhW=y7*grd*n{p?#c|vEZ zUc0y&GNE-PCRDD(gua!SP__zFy~ZHcN8}*XUZ=wYnsm59lMY{K(&3O;I`?P9(<$qt zzgpeX`!w$_91xqq%l<<9YFwy3f~y{7<8nZqDK7HYnNWNCm{n&&=}JtfJc22YP-r{G zaXAgD)}TSp8Z;Al{b?kBGRZ3ue?I2LXrBMlOuHlMEu+%*Eu+ZxEu+5n zEu*xyEnl>!uY8B*>FSRCeqv8zon)$eyGjaqWB8z;)3Gc2ADy0E+eh~6jlJ*giv8RV*5a@B``B82GWvIa?!M|icr^Go*Sdf6@|{Qg zU;JR7n;i4RxIeS!$1d1&W&dfPe{=A8<93GsruGTxGp<%YBmZ}o?fJh;*6NLpX?RuR z*6!IA9osMOl?$EIFRcDAd(zGCzV94ASUu}{%KPaP`)c2pn?L8%%ID0p{x=;rYI^Ep zNvWRlD|DAiMbWv{2bp4RKB&-l^Nhxzo)~KCu}JpRwU5+RiS}s&nB7H7-1$#)au=LzNGgsPf^KXg;+Hs&__Ch9jbLX<5}X>tETTMJ|(?R@hRzliciT8Q+&$)s8^+Ag?THjdM3zwvQCPB^ht4&J}I8kC&gVl zq`b2{8>#U9dDhd`WgS`` zuBqvf;RQW19H2*r?zPD3bq#qVF0zTV=fe}#)Z1oxKO$p7phm{s@KA}p;h&?&INT!yrE5r zQ?x1ZjW#7N(xA+%L|hYDk@eZ5tgP{iDj!Z!<-;SYe7GZ$uO6*&N1S)-Y&b)a4Ob|# z;Rr=G+z`p8m?1RtLP>@0W20Gq2T{Bh6J&wD1ztuTW zf2(Tjyqvf~ofEgHbK)Wu&b*$(KT(xa>7bkt=jamR8C^o$qDzQR;t1JSIjo&pp7+x4 zf6im~kPJ8Ik>LV8GSsg}hU&G*>QxNcA}X8VggPPq&?Uqtx`g;emk{5?6Xw+l4vEiW z<%}ZUP-Mduifs5nkqswAvDG6nPKfbnoeLMJap3?pF4V8ah4Q1g)FQuLyf2B+cwDr~ z?;7gXAwt(Ji4JW?^VC|_TyJ_Bj&<|4iO&&jqtA%8QDQ{fXs&hJ`Rw0t?nQwmN{dtd z5~a;4mME=Gv8ZUbUU9asgoiOT@3T55YHwFjosSbIsB_{Hbxs_l!kO1-_$R6&Djk#) z;v8KnZ_(YcwzvvR;n|Q*!TEQXld90iv#2boixI&Q)KPa-{gebOpB*qCb9<6iX z0yQoipvHyz)woc86qj1$*NgWh5gLz+R{32+-8w|*x*ZW!^>F0-$L@_8Z|q$eH)cO? zXV^Z`IjVgWO8d8(UOj50_13+Q_9EKPX99<6oPWIv4Ng&@$yf@M8B2jeV=2&S97Vmp zY+d)(lv^i6&#fw_vlF6qT|#`IONd9}3G4Ob|#;Ri)FoDjiA z>>E}yF|+-UD6J<=Gof>(enQ_$Oz2vLsa|mq@!RJj6jo;=;w!S@1VuI+p~!|aBH5Ix zp56sH73z=7jb#;v?zN~;ycQK&Z%O) zZKFwkM^I=i1=^g4;>o+GjE`-m?(yoct8co!*U#;5!_nMRs}+u{Qh2h{9m(t4CT>Qw zjs7CqMs*QwqqWv;=d;I=+M8mD(&7|Lls2bWqO>~2qN3e;^)+WzT92i9U)A|g`5aZy zF8ENsDj#l8<-;M-e0iOPFIrbPr89Ce9HB>s7xc(*fgTz9Z%_6|n`b**ecP>voQ^8% z^qntPKUr=4X!Y~zi`7ee|J?tupFOZj{PF6y?*C6#5A6G=_A9NZyj|VzzJ6fusPnHs zv7i0Y{^gPV^3U;~duf&ZTl?y|$Xjxucr`BcuEvGh)ws~Q5?8(QAOp0{OsRM9Wt|UK zsPf?sRX$vz%7cooYmmqG6;>gKb@i3irFXw} zSy7*b1jSxiT@PnLvzOnKc<)5@47!aYK(BEGH*6;Q_x4-kj@*=@xg|qWM!I=3;G-rnV;lkclLtMnf$%m9e!eawwW~l;~8O z5_M`*qDfuKdgj%t7sfiRQ=?vu9JVbrYF-vK>ei&r^BH>9%n#*E9ZFsbFB)D7FX~m{ zy=${gCpO=7$9_MtQv^;%D^!>4@_I8LjkAog7Ckj~v%5j~r*{lB?H1QwqSR?^1YCvKnumVbQHx?5?D3!M`#;<7 zmv;L1iy7ve`j)rl%KOhL=XAz2A!GK`OW&`a+OOTu$gOX?ZBP9Ae)iEd^Nnf9&vy59 zSy$1Mt_OxAmrJXQc52wwo%hPN97LtWvHR!Ww&fuDME3_#CbB>By_cn4u7v1iic3L1 zQ(R*`>GYSPzUm`CYO1+LCfh`Z{O;Ci(c&^_QRp&g(XAG3KJG)W^HnYQ6(I3E$7t8lNCB&-)p@#@tSkB^esjBSUq1Wavzbtez2xhp|z6ti3ud zid+UQDqRLG%GILH^CEiHisShVLVwP;Iwu-c=R}>+oa5(lhU4z^orjj!U)y|+pB=v2 zcm8dk$xBA!rZOW+RAxkls*Lr_N!*N$?qg-vY0>L4Xi@GmXwk41t!kdn6`OOSRIR*J z)+13;6@Fvs^NI~`k|_c!sDS^0vk;Ph)c7i@KhRh?b0Yi&2KA72T2 zUH+Az=;dDtnqT&n)U&xp?a}+7PKrBXbK^Fos9&EHb?cDkIUIfJnuU91=7cCxC3lon0$S81M1|3WJQ*+?-G5~x^;hrK=J|e19;S7tLv@;T zC{B|Owdv8-^PWb2MuBxsbQzmZHglp-bxyRZ!kK43bg2@#%j}0DbqUd;E+Hz6C9Fps zR2b=!Ivonsq(gn0bSN*DPBZGM#=4RVjYY?g{63Bas~Wvn<2$X?o(U<&#Q7#Y~Z*p!UzgZ6ZTcdVPk+W%# z7frUWBhJT*QkTMue${yMjE`=kGkzX7%bE$r>XV{XeNt2!Pda|KWth(+o)H?ysFDpW zDzc$IMK<)N##Yah#K-73KGtBJ7iBJm7qu>h7X_>F>gM%axj89{Rm(wT1%g)fNl|G$ z>G;WjVP21YI%u5XOG;F!O^G74DN&;?Wj&v&%8bTIzMg8i-MQJihgGQ1{1iH8^1T zS5wdR#Q5l3)Vre2idUAviW8Q=iq4f-b!(BKQgccas+2R!Y6)#>Q=-avN}f{~Rv$6X zAB|@mhFmC5jSH=*aiK0Hu6pKE$l&MdOR&_}8yoWw@Vs)AIP^K~? znp9>)jq!{;kuuE7vCkik^Yf4o^{MiqJXJnar^r{&hQ!6_$c+-~r07$h6s78uqFEhM z-Mrl^G-pJaI{BuoEzqPgBWjFhtj8dYXj$h&iLrjJ^PxgjJ`@@2V52LXLO`IyH z`X)}LQ#5g^oubiEaee$rwZ8jt!*+2t@3%TBy4NSg6Z)k1MV}Ne>5#S@_ny~FIU^pL zV|=qKM%<&!h;Nh`aZDuR_=(72EtS6WUAGSLWQSv$ZBs5A?`TzZO}& zx{0sDQcrzrqj6s=tdruCxJ#-6Bk8t-m_Kj z*Q0gss{u7xPk|R=DaakM6zDjX0+q&5)GLzs`Y9>5PKci4GUX;hw605t4|ECfNIYR) zTi}rR98=a4ctep5S17XK2SqlV5X*LGb5HqfQ%wGX1g-nLL@Wu)jweCK@g%4=mZV-q z5XB?&iz~TKh`x0R(Y-Dqe$XYvC-H=ubx!g~&W1PQb7EP8;R;1I{GiB&6Jpt3t{&Uk z+OX2`-A(uHYscMF*C)2CqWuaR=gK)3+E?R3_i9{dUX2UAD{d~4wxC~lF zE!O*1QK5BOP2H;I?QuNP&D)ze(Wq8FEc+96MstqO_8pG5(|5jH{nX9i{Ji?Y=5t=} zeCNk@S-hkqa%xkeMr}&8s7qOo@*4Smtj{_vs*SBqwxvbA%b-QYTC}S9KG#6biBh%l zQ<+E6s5&R=jOM)CjnaL6W|-^0wcLJcU+GtGPV8?}D>!fM&4}Cn?5=l-ZdWUeuMiz? zPrbA&!~q}g72=(b^9t+Lf<|rA`>9Tf%VKlyHl(;kpA^;WkmfZ4`qas3Wqp7$l^M~b zG9zk?X5LW=@DARdPvLHK0XZLR1(_SoR!IQH@JOb7g;ND6Qn0S6{De7Rq-BAK0_R zeqQ52_h*0V_{Z*V{?uW4`w;W$N) zOheIAeWs!CDKZVkPmyUlpguC8R+yub-Zrl1{aB~PE6bq8Kg*!SQ_G;mXIixRC<}MZ zQ;DD7UpXhfQs=}`>YR8vAJflsCV{|F&)f3q$HfN@u`_@*2UfBNRIxXH>1}z?21}$D& z1}&b`qE)T+a)0HV_)071mo+JlQs=}=>YTVJnv*?5!zypi{ej!pcSABc?fFR-H%7>@h zd@#po_(QmGfEpLtSK~tUt+~c4o5Q*!edoLG`O;lJOS(=~B@O!4ph49dG-z3a1_j5{ z)T@S=nkMx~5&DOmho!feOb*cNcyAv_iMby51lpNJQij8U?Wk$7+0wdb5XHw#B+iZ|jS*Jp) zT2v@liwa$9QK9x2DwXO2o##C0J0=^IRSwEl;X%_XJgB)f4?Ew6b0J^ZEXb>OZ*AlI zZE6Wxa~c$^L4$rZXi%>P4cd*TsptEcDg-^(xlnoAEVh{oy{mDdd^IjS5XF_}fAk)e zPs$1arRxx(aUCMmtwV&a+Y*)PiP3Y(5u@UgBSyO=M~q@ij+Q!oXipyd3AMwT`ReN8 z>ax|Ww`}I^y8ZW-{rRf>>B{P})wLOS&kb47rUDE4ylmHh(fYxE%{PV^_5zAj=mk_6 z&64jl8Y=gSDR%xlGev-2;|Ne`906*LBS51u1oe>-8GrtYn&^>hw9bZd71_|VA{%N~ zWWxuMY>}h5x(jkD)Zb!Euss#J*P=r4T2yGgE!Fsp#$gT8(sLyRdXA+)zp)hPHI@Q> z#!=LBe?(m})@+?EUdeT~czxH|;#FT~Q}9Aw0pNki{87#Up?)nYbgxB);1-*2ABz9@ulShjtEvznQAVd`U@#zO|^(w-y!p)}lh+I#l(%LyT`(gQb2^ zdUl{6e{Rc$9~AoyS17XKjYu|)Iwx0JPDLJw%#vmGh3>VeP`nluT5n5r$IeTB)SdF- ztCxOu^03yqWb1U7Yz?zTZ8J@RuH#A2vXl6_NQ)KRi>v8gLBa98f@)()v?`YJp4Iu# zcgqZS7Cuz3%7+tF`S3?HUtTXz$wz0KvSz>$dSrM(j|>;+k)i*#Wc~Ap=zRX?-}+Hu z-#S{3X&t?Kwc@{8RB`>aqsf?SM|bU8ujhKS*fJOJEZ7}8&F=@R@DFUZ@_VcEkKUc; zheDN@(5(^^YF1)G;}J|6^#tn9X;5`U)+s9*^sGUHk~L`1aBG@Fi-E(Ljy7`~_qz8} zdAlgIb-QTM$Lp=zMtQB<$0G1HzJys zc^6e{(4c1x8kDR-gNEDE@Wk`54ru$ldOv3{WI?wIET~q21>jTctV)8@I)bsV(};&kmI&w9eK_njSyIfIGrzIScu<_Al1@?i#Lf-* z%yQEw@Alu_vOitj@yhnNeYAJk+VOUW?fW?{sr_B<1RS@I>MmJJ{`-oZm~x~0lUEG8 z&C=tRQCd{bqp^tgFRsSVRu1Qfc3*q+{XW$$3T)jj%4^*&x@+AodTZHktupE}uLezo z2J3wjq0Bl>gkI}3F)AL{(7k%+s2l6ivaD0kZ8QgpjpjhB(Hy9BZjSLYQ^PEtzT^Ah z9@%Wd?)F6v)mgTV9%EWZg)yz8y_nWfT>IARv6|8Kyg5Eyapcppbx+j$2CZuJ4cgV9 zLCbAvqH=t$-<*S(+BTPzwFZif=0K~_9H?||j{bQA;%Pru;&VgSx>=L-r+M;xPkJbRN{nhB4Mw$(`sQw*71!Y?obfl`n>E1GGYu_?DYu_>oYu_^3 zYTI&j21ajn4pcaA#NW(;CZjn}Xfy|UZN(9li+g3{1SqprHYn=>G#N*L8siAi;k*Q2 z*!vt`*wf%gHVZz?%2#HY56@deX>D3VRc%^BLG!m(?%wQI-(p&CcVDNjz1@ADYCZ10 z*J};NcXQ^np||sXsgt1;Ju+0JM~1@U$nuH-eYML~rNVM1l%>Rkrj(daQ%k0pops{~ zRWhKMcD^bZP)H;Liil#UXA9zt9$B<@>U|cis5)7^&hngrzT#qGITl7)N=#@=LQcjkFBM8C#1&S)hK+R>mvH5xSNC7z}pA&E!r^4(ZNbuyHtM~2Gu$WUAy znL@tH6_zuhthhK{=3q3X#Dtn!GTpVO@_w|L;-k@A@on2b{db#3KJ4Bg@OIVbrxW|R zVUC((ZFl(j7gu9Kw4_UjvUCa2nI>U9L$%MHz4q!P$>V)?9cRA2ZIltwHY$i{n>*gR?RvCF1??i(raiZ4 zLx%{(=n$c!_CyLf2LIeo+yvu)FbU`lu>6wFD>KiCMFc5#Du0Im^3mGcP*zu zK@l;x%#3J9g9g>Kq~Y0=VLrNJ&ncW*KJpWjkKdnkJO7H(kOiG6u%HqJ7BmvgQqK`o zzU^Wkx~VgvCM70RrNo4~BAD__fsP`gVVNmVkOmFf(V#&!?P$vT(_SqlLwk*s4DFRs zGPKdd54Jwj^}W7V(T$SR$snZ zeQz^hkGpxWVWr@20=YVC?d9>h&Vh!bInZu22bzuMK&z1)^~~P7-k8>Joex#F%3EjU zL+z@3xImQ;cSQ5$6#>48&M@Un3Xaet!wY(3xIm8#{kI|;KmRwZU($CTT3z$n=Fq-a zy|n+&vA)+=!6ggYR$xKR3M}YXfd$1zv(#$_Vt1RIlKMg8?H)C6)%yySYxNa+*P=rC zF;vkNO0M&qhu9sH$;vtjWvlR@X%!yS+={1v5_VX1(0_ZPynaB@?ekGt zJD_C+7F4Xjf_@cPP;M)hdp0BDt3Jc(W$x!gsC)eFUVZDRGp2R)7}GimjAy*cf?ivZ@FdAF z?|)_4_?4Z-=kIOd`}59C(27%_&sYky8B2jKV=2&N97R1Jx2+G*X`Ki4w#he}dC;*6 z4~kadLEEi)@~n)GTgUw}E2H3e60{pnf@`%!T`k+qpVJstqR|`?x|`~c2pvvwMCfvgLqVtY8jxJxqNW%|%DkuQ zgy>qA5XI{f;ssqoToO+hTkWp*NX~{g+E-Yme{(imp~!|G6xnb>4BPZm7sEQopPrfL zZMv+n5ROWHfRWQofxB@2pwYID9vb{@c!W;47O zS0xR~R=7*(H=3nhp{QuOPN<^vIwAVky~`?|sQ00cQadazd4ZIPbA4@h|p~!|G z6xnb>1l#yo#^F3u`p(>M4xgTLDyh)D78Q!uqC)FhRH$5ss-BzTYNXUVTF=mr$Hm(c z;*It-(zb;7M3)fH#1ksjNcsKE*~k^~`BbY$+SG6OL9yR(LIj(?P2!R5ZF{nMZomEQ zAHzCnp7-%h*G1DL=sli*Aqmg7lKZY+snty8MGPKn0bXTq~kqJM2lyrE5rZ!{?L z8iNsnMm{R*4E&Pd9iyGG~ ziyAL2iyDV5hq_)pk*(V2(%yA-I=rY!hvziu@RlYW9*U(?s@;Zu$hq)AY)&t0NgSZY zh4$6BP<;efs;Y){*=5@=-tGxZKcC6#IwwHSaRewgjsT6u5unBxf_m1Et4WeV>tyJ* zeb(AUhMx7v(77HN`i~>a^E}Fr%MWFaNApTds9lK(ohvb+@EE2;TM--A8?8=z@LGe` z(QnIDmcDhA8q+$OjAX58#2woKjWycr7Zl9zn(Fy2F`})@LA( zYnCAq+SVaL*E&RKT89WdYY^4z2I6`99D~N|Y$#um4G$=?;RZ!Ed=bf}RMYe>$f;0& zWJWA2Ep)F%h2piS(0Y5S^1c!06+4pTUSGGH+*8XVweSO{c3wsmXOFIkj)OG{?-hR(@a-MM=(DI&)&;LmGBz^76)lZfQ9<6@vT>N^6 zr+H3kW6TkMhjN~{JJdfNl><5*VGwP1x7(6W?<4ueRZkRupN1@P_lViHbca%3 z9nJ9W)2X{b+wu3JCyBZf?XsJn{Nv~Ta7WUe-{pSSKX#`J4cnNmYudKiT4i}Z=Y0@& z$IDZv=c?(-KFI5bJ62==V0t`QJzjlpsyte~c~^1IQD1H5Y32WtWSqB$^~^k{mQJ~J z%n41UF0C%E=8j`!+wpg1iYe~y^tDv2(!_5U-sintR+@Y|IW@A< zT+b4Bk9d;WU31-Bb8DyLvhu3eKYiLR1Hf8L#EQ}q<>$NQltiMkuD#yj7d*M6`Wr9+#gJ~8M0 z&g!#t|9APG$+8vJf#z+6wVrufVP$9DR#?ZGua$hv-9`;ltO`6VG(6RyxDpp`Y zzX~iUw*||1PI;K)=h(H8tF>$$ea5toB4b)dgE6h6y7sNtGcTi$`D6H4iFG3MsY8Tj zb%@Zh4iVaJPn73h6x}|y=W_`CshkQds91po{VK4a+!ie36~kdIkiPSs?dp7CbFsTy z{mJWrl7M&{M}RWp2+(630V<3ksAprM>HKj$U3ui&7}tBH-bd(Hr;pIH4iWlpPZU`n zf(E9hH8lWkfdPiKcxU7G_33_YO8p>+Z8X9WT8fuxp zHH}>BdaaY7s}|9GJqenNCqa+#BxtoANuKAZ3fsljGS8vWNCwmy$$%~+8Bk;!2A)Rh zv+X=@mB`ol+P!ULUCU^${WYVs_AR5Ywk_B5Bk`|A1m@W@g>msF7PP6*FQ`?41^u>U ziOtz4C8ubcwQCgUv}L|3D;N|SOMy09Q1I;LaHZN;&Q5I~+cW!pJi~cSc`h9?pvFiB zv>3^N5+fPVVHATxwnv+F78E;wCfUq_aurxmumTH8Zpo78St`_)QN7G?s5O=XoyJn2 z&{ztzIX^|IhIX1P+is`Cvh8;IE8A|Ty0Yzgwf54g#`|_Q#%rq^k5*sW(-6nqX^oHU z>xbR55yL9;(oXk2Y}YoLi)b6oMYN6PTDP5_ArN{0r&yx&FvSw3k0}-vz0|92GS?gx z-R4-5_gS42g{gC*K6Or%slwT|%0G`Xb3zn3PrbPdLbRw$hzjEf$Is&r^JYQ4dt8;28vCgTc&pexjXi=9C6~++`-`3IZMol^N!eDjc#Ln%W zTA_GrIn?uQ?Po9AckbHy#;@HP$1YEL+9u17uMWkoC-%1$vi`E9t3&M{=hfkqkMHX0 zIX}8K>D^Oj#8XjubQ?yTq0ETB6&UkOj}8^GRGH;bpe7yK)1*Unk#vXMo4xN?&V6Dt z0$ykIb;u=q!Y`@ug}syTz;;JGp7_cq_EwR%tMp9ip?$_H`52pz=}hZ zSQYE3QmHv53RS8Ns0L0g$EQjGZE91Z$~a1%g&0;>^FODu%^b~;49)40p))-)w53H> z&wSBY8okvCQDIaL*-VHUbqP^rJYk;K&|iEkF0&cRQ)EMPifpJYimkkp(JCv6qP0~L zMJuW#no~!2Z0^dduS@UWfqT!M75`%8dHa#ovp?GW7VA!5+MkbXe(Fb?tLne{z-Gu^ zSl#Q-3GHrvs^>x>iOU(DTZ>Ncw~o?h=#GuR+5Xne%lY>XZAR@UBfn@XfnV7gq5rEd z?HaFjzb>slwVAg|t3TOV;-~hm`CUFy)03m5?a2N8-%srt(n{YGyDyRKXl{x-Q(C-;DgWJwN78i>03b+E!bi&!P^Vg-c8+tNMutsr{4HUuJ3b zhIz){lU{4}ud}pz^IfZVtS^%Xe74x%R&=|YhyJCTt)9n)w?xjFI!9)-sAh`%meG`C-&xWA3yf``mb49CsQYd z)ms1SYM$1|G54wRwOQI6N33x^OLu$twXw6O zGG3WCcAteWuf`E?NcgvRQE>RR&D0*Lhk1GqX5wYptJ^TAf3+^l&A~j5vuEqmHcA-B z;Ik3Vf7iXL8^-N*d=Yo}N>*q8FUuxxEQ5KC{b=>`3=!wb`#t38dWX6DTgzu&H=kPO zPPJ2-!`{8JuVnFkNa6L&fz|7WmS3M)wVvw8NA~|YIL0x5=B zE7B=la}bOp_a=fLtk=h*kU5yfk$E_RylZ~&-4IC&bF@8xcLE;em}{ z4$Tvf?B2{lFpi)@C-k+n?`nNzY(Cz0b^O`>KS!N=HH(*Hny<}`&h*?!d`7_Q-(%~k zx$e%@EIQU{d|%CD`{m>1IeEsFaGmG-Zsap}?~H3?#vuP?y|#OuUbUH$VJ7Wco1^}} zYO^rYt<6#SU(M>-l>0me&(V+LXqS&N?wiw(E!xb{@;KJrwMzY#`Tf#h%>KQ3`g@D` zPpwLrgJ2x%))UNer>|x4Z9TypBfe`{O!GLJ&#XQ)<#~+t_~A3Fuk8O%Z0;=81|D^f zX7Tv7tTsF^+Z@`tmQ{@BWt&G;l&ylKER zt~sZe+Vssh@_Nni$ei`kDyc{IU-z58amd#W=BmH1S;QUYwI6JC6cD+I(uQ{$?uhsC&Ds zT_9Rif*wam-635A5na&ZY6&JUuXub5rfkbG_rJHr4JNYa}UVvPD1PI86kFf+92MA&tU9b+_Z$`djcjC` zx1HW+^QxPh*nQNy@xJ2o$Xf2lNN1S*VzJKeyT1c~)sEJj<0{bWjXxUi%Puk>*?hCd zWzY6LzjZQuC3Yi=jL!3|^`PgbKKDQFD&-e5WUCwRE5VeZeAeBc3wvTckyZeF3^<2g z#u0KHseC-V`!;W`iH^5T1HX3t{$%wJ+e6^%>YktHn8Ui6MaSW_*Er^glsB_TIppvN zo%SmE{t2IpKD8N5pM{^V{=At*&mqqj*0#_1p1hlVWg_S@ryNA-E*#my{E4*Q|LQs>d!$JG8r>~PI> zj@Uw_Ij(Y^8bKdX54yc*o>|I#xJ#zAa{0t|Oup$f`oQkdKa6DfM9XD!{}qe=Y3{XE z>#SUO>(}g_dZoC_H=d(~>1$`id2RKXwd>>h>!AwU^!^k9 zN{k~wjd27hGL8UM#t_t>YhtJ4?w`r2N2pw-bvm@GNr$dA>Cn6;9e#+V8}7OqM(a7A z89Iv}axQ$J#)SjaxX`{D7piZ~#nYohjr(eZkL%L<7eCRWM!aZqKX1DAKj%T)Dm-Xf zg$FIG@StG@o_ggFQ`eyDIu~khoA)+zp?Wng)UU>c3!=F43IV-GWtQ@}S(L6rgvND< zP`3^dx^7EUkC*7WrO)a#Xj+2?J!{aQWepm1+?GZoDyziek^{xIjxPCqL95XmsB~Tq zUxRtojrNA)zuoQ&VI^kV7V)x0TWG6ATj-}nTWDnNw(?oomRgx=yOnmP+HR$#skU2b zYpU%it<@{87WH2rv+_QvbD_>T>x=Vnp*%GoEpp~!|S6xr~DA{$O< z$2NY8`mnlrYL(BC)j+#F$D*8BySOSD&~_vP8g3Pp%3eUJk-dN-qZsP70kJve{?xh9 zt{PXYp6mUE%GLS{ruB0}R)`LpaJ)U88=uGy3^ zg#)6v;;WHdSve6(kIHLh?nmP~M5tSb2wk@$@}2&Ea+{x!@ub*CwA+&-re-^)+ef`o z?W5JG_EBb3`{*#D{d)FpT~(CYth1rmRvGCmY^Yh04W%ox;etrEJg?({$P7{DcGRy$ zh3>VeP`nluT5m_?XUzO)HOJ9t7s+j_M*eO)nh(30#N#bh!-ej=nY7n=?s<_h8|qeM zL*t5UC|!{ay{oa+D-0rkt8CPJpw5Idl$daY5))2PV!{CtOz~CCP~ABVs*cEoWo?C? zHE2+>1`Qf+M|01rme-?-v#6&|$QnkUbw=(u&9F0&~LjweC8@g%4=o&>$NBssKIkKrzi`JWu|x8sgcN*DeaXzUn-vPK5_Ws_>vz6&{q_nnxjvCne`3D7bZ= zDC-Hd8&862<4MqKOOlt}j>%yj|Jugs>AjD;eFNxon*s21HHS~7ruWjMv+2=hRQo70 zs(o}A)jsNrXuqCw+t&ce5p^PT+AgbXB0{-3L}*!u2z9q7%5yM^ZXe~#ER2>FSWvM7 z3;I=HLAmW%I2m_X72N6eIJP($dsrU~NziIM2}+G8L8tK~s5F+Op0$a(t>Qn*trMYV z9U^qCLxjF{h|qa^qWC(Z*L2Q;mfPp3vZg`B3M}YXfd%EZVkz$oF*uH(wNA@EXsynY zptUATf;LL@w_N)Z!N1!7kL~oeFKl=0Q~TwA>ubzmjl0`7Gc(_{JqPNH=0Ky-94IxK z1HDFa0I7Pvg|3^ZCBs&p2&K{fzUL#EcUane%!J?4 za(0q@whUVQwhUUFwhUT4ra{Z<+psQdx7xmKo)}W%ByCC@q)mx)v?+0nE@i!PAxkM_ zR9sZ2#aqju#be8$#cRu;#dBJ;+BIPBubdNKY32N~O2tv?oOnr{6Bj9PmUo-nq)Dgb znX>PcoKn&$`J<$3`1|10(e#%tYI{B3`iW_gdnm;~=$UirB#W0LFLSDa*?oiAe&e0s?!{P&gB z_0^5;PnWyiY0(?FG5)^byjYTpt8|jgP_zC0Ny_j$)j}gf7F4Ui60O@|zo1`*enG*} zEcG!oRn?p$r#{AUpXxlQT7?H?tMH(16&@7cnkOH#qT^N-Ot0je1O>;Fpxt;9R2xr% zUfYo{&oZnZey|<*kL+zjd~c33sfSUQcANoyMlztxNCq?+$$%Q880vYLc-ktiqtH4L zy44{<#X3Z2T89XQwgghLxY`P*6#?Dt{!M8I%# zm44z`cT(WL8ONapqd6i}IMp8!s+{6L zrP23AL8tY~szs$a9l`Q@R%b-nb5=Va%82Tf8F7R%BW_V(%f4xZprCzi6$NX z(4@l|nsj&~hVE;tyZzmpM^=G99=-b_!lNY(y4RpV=NdHVT7w21$J5kn4Pv}9qmJV1 z{ix%EI-`y|>WoUB$tw=LqL7!$`8iyoNryi)>2QW79iE7x8=qq^oB>MT@lzI`TfOxE zv-fU0Rvg>9ZeH)FC~0U(b7=yLGk8ej!Z!9WlCjwa&vCe!!kk>woJLh);~^eu-R$?< zt!46&nGqSi_bPfs`x7h+yQnJn__D>{4spzvcK6~-d#gyhxyTD$&W0B#vf%@YYMMPCKSV`mFkkxvwIZ(j4^ADVP{ zh9(_85kvQFKgZ;ES5GUd<#^e$qMCD|el;$1uf~Pq)ws~Q5|?K6H0r<1hgU>o(_Q%R z4plz9M3oP3iRMdd5k?i!87r?t@CZFJd_j*4FVG`H|50Rz-LdU@yKR3S+sxAOtU|hI zZ|)wl*tq*G|IWGYlKtLz4**{sbl=zyh6mk$+D!nifm`-7f19sc%Afh1 zeRs6U_Rl$A_?))&?2?*@SAVDp&zLLn4>c|Az`f9YYaWruCUT6Hnob)l;=PiLsAb1L zV!ZD`_uRIWD|BN#Rm>l1`Xk13hQi16ys>S0E@UajmuCVPk;RM@(_B>Mq~1~IB$KLh zlBv`=iGeDdYBL(S2c?8~j>=d#pL@Y)bP4emT|)dMhLD>frYk}1?$$2l!6_A9phbo9 zwWv_L78MHDp(vSeaPY^OP&Rd34pT@V2qyWqOrJZdd$k4Xk*U0_*%V@N^Du_{od(_<@+->$AYqr@w@oZ!>%^Q|JFl{AL9;Ibr}Rsho5ND;UAiG_=P4N{-8%!u7Suc z@%axQDYN1^YhcBX*1(Eat$`IEQ({%F*G8{ODe)$y?44JY_>ndx9;8i)@5EE){Y)NG zWYh7K+-^Glk+bP|Ma~xK3pq`s`g58{z2`KM3eRaGHJ#J6QSn_nrTNBIH=cE$+uoOl zw#IQ}_d!0iTQjGl?MvMSyC3qh{kds(UEb{PzP#T5{j2W0-9>q=%DTvAHk7Q$hORG~ z-7eZcS0)y#F;lVGQs`RIQm9;wEj?*5^@jLfU82JuH0g9aV##)Rh^FoE5k0!{d=6Dl z&9Qtr>ZN^L=EQ5%Iq@HLPCQAS6Q5GyOlNTLpSok_(|Axyi09}M;xoF0c#AF}eiBE> zEa$WWyJc&Cuiw4Lo1eF>t1rA?d7Q5Msd3=}YFy}EjSICaag}Qqazs=P!}H376@BlR~ z)UU>c@}s!aBEMd|&xz1@T(nAi4Rz}fq3c#e{$94@{+)9#Y!&Wt@`k+Y{W_e#`OVkk z>W+AqNr>0+B&heY+2V5ljz9lzgiTOwyiHJREXjOsgv#TdDf{21Xhw~#dumRFj`!E zbgsmN!do#h+MVuR_-nt%`uFaV&EwP^1y5T?wK1)uPg=j(@X663ZMU^Mifi9`x(jlu z!HbRTzr=tNBN@2 z22r`HAiB5CI%vI2Ms(LBL;reY_<MDhxYZ;4y*V*{(rGQKiYR6+3!E;fBx9M-tWrGZY-&8oUD#@Uf3Su z$M(!_os;A?pZ(7DF118vsoxTQSHC6vs(wrOP5qXxzw?f|Ev2geldXIXUfD+bdHd)y zs(n-&)jnE|Y9ED1wU6#=wtr;N_krE3_I1D7@A=^e`@2Ww>AYqQ&)eKG+Hc=7s&C&i zdT-lux?(cz_e*vwoZk(1)$WG#e_yp*^)B1je#_oxd#z1|{9M+=5+xeZrbJKLlxR;4nJm*7$s`f*DQGE0F zgirJJlfITaw(4!4^9I{vU`|A|(;-4tIz;G5gQ%RjV)N@oN045)TYQrv{Ph5 zWom5Y3>TY+=gKQnqQ{uru`4CY)TTtE8kA|CLysD9I?r<`QI!u3s`8<}D86rPH2ag~ z+Gn%12!8|2zwBP+LtC+U+<6?Dk53OKYZ(XqI=ZiB9Cg3;W8=2t;`tMx$Mrq|ie2v$ zpy_o!K{@LZi(@jysPi%*>enSi-MWORH=atWou{0{d=6#oyNZ3)MLSO`+2?Dx?Q=5O2a-_9WFGb#)Y<& zxY8YQ)A3}@z1F*EY>92uT&(_=*hbyyv5nf(Vk_4??JBXeXtB(VLQgEZzqZJ}7JH-K zwQ-|tRqnLHK-Vf2MjFZT8VyCShZrrdhZq&>5YKm_P3wXddvbS+$vGohRc1t;%8cky zm9duRHwstf)~_22m%A)fCtoz@L) zcN5>=VRr38m(!x!bNbn*X8#h{4n63 zdA3H;>mf$V>mf$PI>h5{#%XPl?pYpI(=OOrS-zq+?27lF&1;0{ncDf2q3)0G$?$@Y z@5%6%kL$_GH3d0CCC7~(RA$Dn*2Ij*tce+4P-NDxeWprIX;H9ZPR^?|w7U*kRI5YF zj^b&x!@0g;=McXUUBAHLq-a#16n*NGqD^hma^_X57SL;%7!|AJuN>%4XrRtn0RE1MLit5MCoDePQM4_~o zQDI%eU;BGYhBpyhs&%Levw$^xU7Vvw-{N9hR?Y}L0eyPotPkqaB>%Z+E&#rgv z>0kEmdwFR89^O$_R{`5vZ)*vE@7T{~2S26i>YwMe7Ww0rwY-gixK}G@Y-33Kq<)?_ zJrOpll^b$pTzvnAuo~mg;{`1aC!;+-v}=)ZSi2S(SG8-AH+{8hk#~EwYw^ar%-*iW zo6TGNtZ|F}wxqh*GLJf!-bbBU*x2#C#b1jOn% z0%CU@0kJ%efY=^KK&+1<7-N4N0qb9J1hr%0H#S!F`HJ8AQJUWce#=H{4{XN5N2|Zu zSAM&<&(@4%k~$U6X-Ke2JP8>io`lR%tAaR9|ByozSjZ*{EaVjh7BWmUOI``qj=H-l zIVU6T)XE2ECnFB&kr9{l$cR#UWW+2zG9s8B8S$(Z+3suUb!vfK+D{`^&6Td4=gWSj z$1BERoujnZpp|{L^TgUKod@#Xs+EoN-kP@_YYAT^JM4F=urAz6Bh!{OY%r{oMf(nR zDbd8*>IWN>KDTw%UrjZ>s#vmCJ8#-f6mPK?X0_jJME|U4uU1~?*4jkV)>gio$#a%l z=Fryd+st>|HVZ7b%P6B(Z!EQ&T6~=CsaH?!#(_0zari08VW-YL`|64H{z>#awZIOaQXYgUopnh!j+xyO_4j#>YO{oy>KM|i)*{K&p{)SyRp`puhvpKOOrHnjQM|Enn-C|6cj>w})M>Nu; zBSLA?5w$ewh-8{{L^n-3B3>-ryz(F8T)olPF4a_8X^wY;sBsb7)VPRhYFxxKH7+8V z8W%B4jf?12i|czkt@=MU{$pR#<9;mcXN$)3RX#c%P8+p4YjakrQsq`}rswRW^Ndh) zwj*a;Iz8q0A`W}g9(M|Myk|2qK99?JYS-h@!pZAlAA6nbo?C8Lsx-6K?BBcnOx{ED zVm}S&^Lo$TdoiBvrx)w)_}`V{p7pAq;B(eH)~Ao`Yh5zMer(V5#QN!BsIE2>L}}{u zRBCFS}aWR~bXZIt@>h&OVe;yO-P24QHl3?;Ki8%lGf3H*t98 zt~FbF*K(ix!LBrIE#>zMJTy9A#ciqZif{VNWdvts=^ZymeWdiEb}U*oHOoEg2tZ+8;> zp;lzrorK4-*}bVMjk zI--^)9g$3vj_9UIN5qSzYhBas8tGClB3x{}sagl;TtqcBdlApnxQJwGT*NRnE}~aG zuHjzzzx8YFzTWt)jRt?UKYm{MiLJl;_wsenZ*1kyPY~AKoAYD8y67kIh8xjNOPBg< z&3#t?)my~RSD*B|7W|~(*ZrF1}Xl7XJ6$uQ)* zF$b3R6%S_xYwj-gUO632>$pA8`EFh6gkJkN?W)1qIM#NrHf9mUXPb}_6Kp)h!!FW<)TrC~? z!f%=W$?kF4-y7z6>S>$ky=9x&wN;zBwpyqgE9b3a^A@_wTgUpfS|9F)n%*;W-zp1# ztL8MP-n6%dT8#`%RN&jqQ7*A z=)Lwtt@HGz{z_Tsr}mL0D$4o2T0U=>S^^*X&Q5@w*iT>gdv*MrtB<@MRq^r?3%#Pi zg5N~5lw%D&SJShXda6uB|LG9Xi|vU@epWZ)EcaK+LO&_6&_i`uQVZZaep{{IgShXp z%aDuGR!r=1VxRS^9V$v{ukVOYL} zma+FR%m2NthCkVB&9%AJv>$4%U3*^g`TH;WD%5X>9`Crl)Sa^t_C80|OKn}~{@UN> z;(H4G-jBiF!}q+#^V+yILn%6WFVu(zyVxaG5f{4qmW%zYz|cQ_+kxNO&Ufqg5G`r+ z%}6tk*=_w)!4|D`>APxtt!+LRc&WQ=vw-Ki&+MPe_TS6)7UQe^cdpo17b`@NIR{zt zlFdeX`%{i<{db&WzjKsB)R=RSDK8p>|G8{4qu2YdF7=kFZwdB9M)wGNAR~KZ8a2nY z8L#*KJ~d*<{$uB;i8%o(jUzyxaRlfyjsRuG5R`JpsLy&8&XO+6Jg8NL2kolxpkx&u zblsX~UV)7|t~FZp@zS!AQxX&$Pl9&iNlkqg}| z)2g4@d2J`&GoC z=jGK8H7>NT#)an9xX`*1SGiIlGsNUA{IbkPW>DqBJ5>4b5>-CDC7Lg-Nbrm3jFs+R zseNj6n33N!1{!@fwHdykXEVG&j|~0Ckd@;u`j7C-GF4 zR37DhDG&Nq;X&CdJZM^l2Q|0h8FzI|$FaW7&~|U>SRKrXP;dJ^jU^)V+j75Yi3RQI z^cU*YAi7{T5kBbNB&${I6CU;_`;OsSJC&FEZOVkYM=ixOspxxZCv>i4CzP(jRIVkd zNLtiKWAvu~%3P>ljSCM@uPUR$xK93M{BLnx&lgiO;R`1xhZHp>I7h)UHQ{_Vvi{ggCOa z4nX;F86|q{XF5I@{V}x@YFDxoI#*&s;jNj<5fp{D^|>-nOhhm9pllWUplTH!6y2I9 zjX>zQy=SE)C^()3?Z%U!+ISN5+J@w=-9hul&SChwiXPfOult)p9@tQa!b(;>uAo?fWt>-nAB9{A?|} zc-&fe@jW%(bZmg1MOUA>r=_HLm_8}KrB8}i>67A5@ubW-PAk7#cIWDAdux!tRgIs= z)cMP&w^aG?6jeU_LzNG&P~~k+ zR|hwZ-~D?Ia}lOb%FPm+Kw?z1VBW;Abb!>#HHHhYOKty(K38e~->cKf3de@{w` zWivdXOo)Hz65=Pig!qdtA$}82m{u!zNPHgC+53&xZd&ofD-`X9KPa-{2~lk2NQ@`M z_-L6cHba!T@BlS?p?)V7Wwt!eNKeN>!Os?sAvX>nu<`7Bjf%7n7w z;&h&4(UcMsYHG>!R~vmFcHh{a7d8f;j?adld}-tJ$96LMm+lL@%j`{qv06@qesqXX zj}8&q(I6^knfCc_uBS2?Dr*aO$-_>kX;$+Q+)K-J{H%U9qa~+6C9xD} zBbEY1#8H&y|pg}ttG^nN>P2NB4 z)si!`*GSILUKu$<3q6cl`@+WBC&|5=bxz7Jw@I(JY?EHDaVx>{Q_`m`pOXE9t=dd? zR!nQ(HkMu@KwohLZPm791JoC11GE@JP_Asrd-W=|JRYPyRHj3_nsn$|lMc;m(%}!W zbP;2QUHl>C!Vkv(cmH!I<7ZP5<$juS;Q{0StHFi#)woc7Yc6IJrWHx6lh)(2j=Gxi zpluZ%G_AsemQ{GrumVrHa)_yG&~=#$wYSZC+qqD^8W-wUz002ra{vhH0W7_1}$sQpyReQX$(ZYt-UGbK)2Bx zC^nh{twwX8(mEV3ZExYD?r*l|;>h;T-?jf9_iG)8_SK8oenWm|pEbkw)=_0l>u8a2 z%u)B;c3eI(3q9y>188dt6sbXjE;VSbn^yl~e`0iUBeeCKVXc>|AhKGG+`2hxl$nSn zN=!r;B_`sG5);uziHVq_#6%=gVj@08Fr`&4aVesj%6G94ku+$CMH)0jAq^Vhj|L5q zM}vl#qd`NosYP>amH*fM+iQF*aQv?J+V49bd=xfsC0%D7s@>MDmTD`nyz4~wZFWdw zs*|+p`3v3rbnMV4!}so+Hr|*`eE0mDy`D&4(#D)ap_fvkc^+=tOXInqp@%-Vy8`~z z-zjipz4X@3GY&RAnECa7$M(67M=FK>n$kq})g9}LN7gGtPkm*+^UD5qWS&&JKWJH@ zH%6AozF^&M=nLj~p7#C0&uaGy?I`rh$P(8p@pfpLaewQzcF$(@{Pgtmer9^QKk0mT zt-IP??El%X5=$*JVmsem>8>|znbFyKduQTBJE?f7yI@a$*`B}6lQSOMcgSco19 zEJT);Eb|I%j4QRq{>zair68ijQV>gGDTpGm6vU5M3L-}=1u-L*f@o2P;+5@TIkZ^= zzb(t}tvc+U*xW(62B=w&oR)>?Q6r9?mW3EngT>#aF~5&>I;N|0Bl2>aSiEJMShi)G z#Nt+M=BpKBhK^)t-QP=gXx;xy4AFXsiN|>iNNrQ*LOE^X;qF|hNsSAIMRBE-IeETL z)v=4RQX&+kLxh%eh)_{GqHuw7lpMMb`Jm2bQoQ-=sG=@6l!c0}`iHq&wR@ROSF##pL)avma)3JAP^IWQ|ln6yd#qd1yq9q+7RMd`W+@CnjaoiSkV6*{b)Cj?{h!RQjVNI z3wrw)o&}|T49|kb*ZwT!+}S#(kGd~2;SH_w(=JS?T8RmDMlhur6CFilgFIWJAPpL{ zqd|je+R?CQdY-lGz2jpk-%c4&N6oidY-2zZkqjsyilLkVh$gKf8|o+%p&1<_6r@9h zp4t<|XQNR;DGSPJA8+%#h-MU6P)jS8+!{ogID*#N$ZgPC5jjD99r%q_hy8n_y~oF0 zNc%kn;Tb<|iTHcKx>0a+`i!YPn6nF0~xf&*ch?XkDwinEGyN zk1`d?(4s;$T2v@1hAOSg&{MtazgbZ!4@y$uK|?A$sHYWAz6T4%)Z4v0S4>WTM&bxi zMH~S-h#@Fv0@Tqay3Mswrb01VRH#Uc3T4GmrTG9o#l*XOoQ9HAc+ijv59(>fGvC87 z9Ul!pxo4-}4*Pp_j`qHqJ*5m#@{TDWSI?Z91D(9@_*3n2z>nI|o zb^5=3>*eU*HYeqpD3hU-HnH|JWT;4w428v!r8xn8#l^flFQ6Qk^U))H->yY^xm}C&XPXww5gC7P6?I%yWg2>3g9cS-(4d&MH1YXiP)y2! zQrgDDJWrvKXb#lTih~n9)7d_shd;3ut9tLhNly`6u)7F;@85qk%^T4iXd;>eO+<2( zV}0AqgHFmkM3gr1bvq9#QsF^Ot$EVOk8WB=x;*xyn0OMj5>J9k+K}XZ(pDXL`)xIm zx8GI?dHXH<-)%jfS8OrkHnd0|w`-AJZPz0G*rvtwL|OZOT_R}P&r1Yt`+bQZRtM#3 zjW}Pw8o6w55xixi8b49<+IEd^dRGdH(V{{{T2v@2hAOhETvAlZgOXa*QM>SQxI?3M8COa%2X&x ziwad~QK7IHs_6WcDk|kcNip#`&!=ceg$MPt;_)@gnDdUFvzA*&BQ?+bm0L#2NrL>8)ry)Z{dSoapjx5ay=qoPf<#_>RDKVibB_`C=ifKFpGaYN~ z<8+#z+h}p#(KFBdDbU)wp8|ER`zg@#dY_`4Es3*jBLArIG7-wxAwtDEL@2U7QFNZ3 zDkx<^IqmaKUMrv(1s2rOie)@?NOWnnb7gE9Ewpc$o^RhW{oJ0@91N%w&xw^Ez-+1Pan=(q(9rV zsFNRDRb?8yzEup|OoJ*kXi!XBn)v)M^ij%zQrgDDJWrvKXb#lTii7i7)7ie;jn6xJ zM4fV=iD(Wq5zT=nB00*jo=US#JVPgC9@L}4gNjsmP*ZE3H1eaH){!oc{U|1$1g*rA zpprHud7rdZN8Wy0P2}yjRYKl=&HneRlU8<^x2@1KDbYM{w(XhuZo{ExYVPb^*2kPe zp^s9c$UgE>LAt}xRdUfrRrTX(-nv5nEKx=F(YVq&_|WIpWB;;Kw}<_kFy7jn>6j4@ z-si;sr~Agfe`IfS`mO)(;Gl{{WAImEIWAxaBSIWyd&t6ReSr@!Y-{dM-tOI zGbH=G_kGBHo9$lmg|Rp4j9s_&NSTX%QsbfrqqydAWbD6MGauV~FeRerbcpD)dPIjd znt5%1?pm~aW6|%KjcJ})Bz)NY@~$es(p}wqwA<9`@~q)id*{TyWA14vO8)LOb1jcL z9p7Bd&K0AY{EnG88>O>*w=I+ZU@CuLHTU;c`#-aLe}{T6rVmp-oj#n;xeYd|Js0`L zVo*66rFTN;dF0$?ZL8PKmdj`8h92GbjODf-%sKRWafwmKFUNVip$C`dHpgB#nB?_? zS!Vwa8|8e{{k!|$jkD^=uh;WaHk}?HMoB|&x37YjpH$17a4GeZ&n6E2wEt1^=J*~p zr#eQOrSSv1)0Hye9UI#`vI=nMr>|_x^QxcoIcXdR;n0hMBbknGF&jDbnrG;mZwMJ; z%Vv**NU#4!GL`;o=l?#kJGS|ZBlGF!_Q!SN^NYtevS597I+J_N)*7$%YxkdZ*Y`Zb zuqz9*PUnr_v+kzNwp{4H+V8B(t~AUz?RSQI*Vfqw7t{K+cUd8(_3Q6i|I8F{(R6U3 zf6MBAM`*kD!;SYW*Sj12s=QNuw$~iFE6dIIEZ2G;b-V0yCU6&yQj{NOv1`m9eHa;W zMURXaqen*k(IX=^>5&n)^vH-}dSt}3II?+dIL5I$YX-YTvXqGkro=?-Qeq-%DKQbR zl$eNAN=(EkB_^U%9VVY88|GbnTsDl=?)NJ{k0&dZzVBw(Pdgp2?Y9~}3kfkPo`gsg zPeS~OCCT>OOhZRYHq+465}BsXV#bE2ja{id%hV{dMr~S;8r80g8bxbTr}cQv zQ^Lav)@ka3T+t~nO4g`jw)3LlweX@|72f%7s%hP*;a@o)3RUGpk*a(sP?4{kVTq&b zqE^;w+1APmE>mmkI?c1_Su+o$tIvP3^{inZ!CH9H@LG6LuL|#cU+%On7=GfXxS!iu zzn9$?_UFxBR~Jt|;>l^z>pEyr>pE!Bsy=NwTM&OW>WR62%hafPUDT+1UDT*tlUlu= zNEMy(qGZi{m)AdNcrCoBSA};xCorug@{<^z`~4kE>8#d4_p9ww^z-G9`t#+(I@dn; zG@ZWcm_HMW{+ON#)qhOSglBv#&s44!v}&BuTgt@vkVekkg%}T64>9`JBTj1s^sAS{ z^7;Vfu7MTJu7MS`>aZT#4xAHvZ?T`Id2U}%>zT`TGgf*d-htg5e`IybQ~Sqn%Gv%K z=j>KDSJ(@ybY4xqI%j*_2j%%$4Zl;*YZkv{&pl`Uz39ql$x8UhA6D`^5B+w-B`e{b ze^_agm52*}SgB;Ca+O71RjB|{FDR1}LDol(SFev8PtzsWuN!lPr_3l@x6a9HUNpTX zX4I_0%*o?v6?xmLgSuz6Pe0c+cX*}(9ub)eNX!k_iv$@SXd)?2qI}NJ(&In%^>po{OccaLA zs;(!Vl?qGMiC?d6rQOex-Z#w*^U8kz*Q0J+GcDZ?R3q!3o(Ah^&=89>Xox)VH0kM{ z=@|G)zuV(@?=j#sx?bW#S*mW8Tx{ z=`_0Ll@|(DsByN_qTO}SqFN2w`EKE99WeZ)&UoN-wLs2@K9w0!r7|O0RAnsZSf#pP zuFoYS)kfiv%IB~|8hN?Oe6loXiL#pv$q{-p3LyBpU}-1ti7!+t$!cz5Nv>fJI{ zZ(~AbN=#@=i3vriFr_oY)3J&&qnh59?5L){B}R2UmPZ3*T~(Eq$h}KNy}Bx&5FoL(XBoyYSkx2quQk9 zjG z&9%jp618emqEBr~6sb#D&c}L{1S%~vqg|bRww)OzuZbC5D>A3q7ac2Rg*@+~;C0ZV z-F48SS{>TF|Mk?GGwNwHXVg<>&RA2AK5ykWAU*EBw*UGLJ71NZR-rXcgOyuFGi_T% z1#MfUU)#3In~dAFn$E1psB4L$z1o&2+N*DgqP-fIDB{&w9yO42_g~E}$HUY{G?D>TMlqE0d$dx^WYPL9lSQk#Oct&6GFiL_q*)&2$7PCqJ_gMzF`;%PCUmaE zgu+`g&DZ^>vme7xzU*)Rd)WQ5_xH7y>e!qJP3sV$XdNQ-tV4vFHHgZ2g(%*xvKlP% zjm>HMeeGp3bgxH-{`JW413fbQA&yL;eo3{TGNJjn9GKTss9lK(ohvb+@Rm&MO_^3M z^>!+iYL+Pl3XY{fv#}JYG?oHg#!-}WeEaGHt(KWkaJx*koe4cFF`;fHCbS;Gl;(1@ z9gz?6ERL!*Xwb6;4NBIaLBs86xUc@$W;^_jut&39MwjfJo7eXEPJ!hrVwnNeMlzt) zNCp%d$$&1S7}7l#)0$U_DO$5jc8b>U5>vFMmzdNvUanH9E?QMOd3;XoS7yZ{*1(FV ztbr8|S_3PdrNkOvvG3wlDJ9+%Ss&)UpHkvS+LU;ZHYL6jOPTLZQpiC$75)(!X>uz3 zL5m9gYf+(h9jbC|qvr!~6c+(o}if65X6)#g_)vVl7rKXhRBc(j9TDfhrB_5=0OMEAm^0w`G ze%QYm{<&3WzWaGvyT5|?&Wmit4Q7cYs-%BSJQ zd)C5>SFME?Z&Tw$trv@#T8Bw@0BidJH#4D5;@fQU~%~}a>N$Kzsg$$Wj zZ}^8M9iE{{hfhS)9ok&ge5PuDZ;#*gG{}YSPJd&7bD;RB3JiOo?Pv~E9L<4VBRR^| zghs{V+9@-l?AR>0nGw}1GvX1-jChLzV_H+-D+-w>uPN{nO*;HTlMc_&q{Am-={S!+ zt$A+Q$=KK3Pqw$}amUXisvz2))#wwC^?b=-9|E?(ntn08O4yE;aAY;5>vEhmzbh8 zyu=i(=_MvLjhDxVR35EH3-j2V+OEurH>h*sA?loXjXEcuq{5kw7xABVmHRdiN(u2C zT|#_Dmk@8!CB#qS2WpkONh_t65=hog!oAu;r#UZw3Zrv()#^u>6jzu!!uO* z@C#Kwyh4=^pHSp0XFM`WR7EyeOYfeW&Hh}rCtjp(PrON;6R%R?)T_u+4@wEiJ1V(4 zuNmXRBfMk#VEt~vX)Sik{{EBgj(uAFM06Ujr!;tg1`TT0ph4LhG^jY9 zrkvrUD<+g(CPeR1IdeN9KA=m8U+5CzEAfPB#es*!=c2sUz&8}x@CrpX{6UcoPl#h< zxAe63Y5i8!v;vwkp?W1I)UL#Y%9WT8?Tmb9o^^cN~QMDo8MkpyS;K9kLT^b z!drUoG6&YE&%w=NJo$NhBS76AphcdsO^an)^W<%8UFN_Zbvgdg*vF^s@q{%MMtfyD zx9RWg99W}%f8%jHd0mh5C#OZ8u}zB#eeO?Q=D;5N@AJE6^EZ~opLJi_pO^O5mtSnf z{4e(BN_TaStK>`Vq-P0-cCT8?`@64dmv&2bcPq59cxs8XmUEICBh+Q<%y;k%y)u07 zzA5F6*~I65-&F6VIfp_or9|^NtG2!Lt<~H=^`1NQ(C6m6|FXR;hy8n--r8Lbc=o}} zxBtW9>^I%NyZ>Fi|3*TEzMIlS_8YqqhF<&1{OFbS;*oh(?Wjk~3cWG1MD_)<&qH6_ z?%#Cw*mj3`fAF)~y+S(*y)v@I^~%?M{PIZVZ=QSD{r;{VxxCl${TLm`x+iZr&C7#M z6iDLLvfWydRm>7ydb+q>eYc%Pvp?q+Q|r%pH8%KW`#N>0Z|7v1eVZ~WMf?lhSgj5H zx@&cNXtUA>RvEvxdg^8O+`c-oI{0D#y+_^u*!Pd^JL6kT5AE*-DtXn*7M# zZ(29&FKK3cVokkF`Lwh`EjcK~-_vo__x!VfUW7%n4@Ww!9;EIZdN866U-C$(H2ZR- zQ|ime8X>-)mY)8e<(y%JUv~!Yp55W;+3%$L$NLdejVv~8XX$zCJ|(I@?!LDu|Il*w z*Os#%SoZ$eW=MVI|4+6OImD66RpS3H1kBgR5wHg$j({ByF$DR@Jz5t_M6o(qB8t_` z5|NgUN;zg!SIrfpZLue{Uzrx=t%DX-u7eiEYSGTC#8I!Y`8ro@%863dInk&(C+dvm zoX;&z>$X_G$+=LV8W)OF<3eFdT;)v2Xki_+idrn&R#Bm4T20-i`4GKo#qV?`V0`zn zIwu-c=R}>+obz>~Y2F@w@@2p7@{|3$-Y&82~~il^=maJcGh9|goDoSR(oZi^>fH-&3vcHShaqv z&xiUy*5|`(KE~%OR|y(5%@`@lq-2TMoVyDto}y2R=5`=mOjsk+SE4gt z>U(8kJaRq6c-4A{@eDm;-MT1OaLS5u_40aN%c0pdu%gyzR%(ptEKj?g__SWy*L&gp zd0gj1i>iF+Pn8eNDe{$br$)9%sbx;o8JmB$bD~mpPSmQxnPx_GsS?5Se25}-3DKf1 zAu5a|%==bDeL0tg=5j6#rR7|8_4U$bhkV8EuwRGs8tUTYoYXZt@qB%+>u*c#p@?f% zzg@HTFLa+-tNXp}YpGp&{JgdHna%iJ?Vt9uJ>PG()G~c~-rBlmEnl!F_qoM=p37Tm zmz!VR#@^5MZS3=k?Rztdw|kM~i|ulCh{ z_uun(6dc*#hnt{$7h=80G3TTX*v|QbF+S<fvOcF(coU+hu-oJW~mqb9hmpJ z-}*7*L+kGg@Aq=HA|rD6Uag@Iy^q`V9x)@TeQJ}a_K7d|Oap!or>psj#qBqid;Luh zHFL+@kEmw8FwgLKbZlCE)+>_;#06=^?38O z{rB1XXgSz!`1g6EylmOWuh`$_{Fm(aJg-LclTW6$b>x6JEH?YGS5OYOJJ<4f(g%-?zYZS(e0 z`)%`e>i@$D7@w_er!WgB$w()N|Gcb<*{;%C9Qg8P8?QA~6XCDT| z{eL#E->y~sylty^ciUF+>Hpi%zW8y=HW|CMwJsjpwpGSyEn6K&XKh=hCTZI$ap`~U z(QC%l|G&SBdS3DEbFFvULOQ;!HLe`n{73bt`p3I@UEgowZyT#ut&C6g&v&i(uKN9D zsOjgo>nUI3e`M?CzQbtTl`>r!*yk=T-+5DN5C3Y_9vQz?d*u2S?WOnpPy2J9d*w>~ zxzs-W8`VDj9o0VlAJKmK^enk_|EE27)kc{O#VE3&CPg-s7Ri>+WY^5h%ULCjLTOeV zZ#Jkgy5C*@sjbkK78R;$OO?MLBqCzv6cMp0r$8w!Jw2yD8F3Wl91^dVGF!Zc%53q< zDzho*EsfdeEHXN(yw;`NTtMtij1$tMShGX-@pyPR9Mq z=Bf|vjOpWk=HK5*{=(+t@7NsnBm3Sy=i^JW3UfBJq{v3JQe;C{YHa1a6p=GWt(Cb@ zV9U6_3m58B<3f2+Txm8!S5Xl*&qOFnhX^g{5TT;hMCskp)BJd`&xy}07ydT+>YSa6 zdTSZd?;AL@?D;(T+wdlauPu`v+bZx|J3&2s)h3rNJrDZ(Se^&|rKlA*ZhoR?QDXibj{m9-_~KJsaGQ2YM*@{ID7 z1`TP@pc@Suv=UEK&N>lU5&e{@P*uxVzMTqnX;GoF7^*aHIp$V zK??(k-#$b8e$$-aHshx`TaciKc$=V!SevB#K&LhFeot^rSB%icQtN0XrgiiY(>j`H z-+H-vB#P9oh)S9$lc5wnGE}5ThQi{=;%eNo!cr!bRlkngoe51TF`=fGO!FO-(|q;B z)@GjBI!(Qu4r6cS6lf-v0+qy4po=()a@J^{&*oYwlcAt?v3XZARHa9T(&EU{+=0I0 zVq!l2L|IBqXiA9*HML}#?=hU_v*9QAEuX!yy$4^|&V@JC$H*7DoCo!&@Sq$O9#o^i zQ_e2LrFM~WuB0*<`qCpqX?kR6E{;qg@1+V$nNU_-JkN76no?pyO)Z(mU9!`hm+p9; zubUrqjKFHo6<_G)d?-nk4^^r1p)f_ha%O6uGe^~xsnBA(*uM)E3e=)PcQI6HK0;41 zaW>CIC`p9}4XN;;o|ZhE)|zI;?`-UM*T#WIHoH0OM693xrX1)bnge}AbD)bzj&gn= z%Cw7lXr;`Af|Qt0lM)lkieOU6R-=+q8Wa=}i}TEhb~I>EO-mYfcuw=wEz47{?d`YE zELZXK`k9KBoCKA`lc11z64Vh(QqB$S^A(CIGohn)adXIYWD8m zN*T>5>cr^sv#0%B?@oO`y-yEc=;pgxh90l~?DJM*`ypkSXUbOnFkhV;`eFFqJsTk$ z_IDB;?RiJTR0s3+V|yfJiRlqPJNdA`AIi^&ys(Hc^vXp$AyP9Y=IzGz#u86VkBloT zLr*NNk3F(pBD(E&G(Gi}ktDWnrW`SSa>r_gH}5>-TL0GUdR8CWinTZlQPyr#m)+^> zzi3PQvqsfL%&iWQ=9XoEpKP}7X~hgoi_ume?mvvOJ2N}jz4jdGG^*2fY!8eS5izD^ zzdz|$X}z|3XkXp7v$zk;!yemPBE8p#itA5SYYkesvS;7ht#xJ{Vp?auA*OX^8e&@K z?ULl}mh$m8-r?i!a1w+SAbDC+ncaht@%h ze`(RCH4WaSmFM!h2ES70#G}+X@g;RmyeN`$+%Z3`zwX+O!{h#h$6Y(|@v`5Kc--F~ z^TKu{zU@ch^P1{lvS0CFvSV?-yWi(+F71#k-M#klJP}^_@jMa!`tdvwp8at=QMn={ z!^YJCWBe*pl3%qc@oQ~Ld{vtg|I?sUtV^eUlk(v&8g)rtFXJhyeE5heAKnqk$2rMy zkLb1L=UFavBL$j|r9j!S6zDgW0+q&5lq-t(x`%VOx@)4_2+?|6j^0LyALtU|Bk_c3 z4S|Qm=a{^HAl56g;T4K(_=6%Fo)F7+XuDFT^Jo$4Ps3dW(|s|q6ev2D0`109pwc)B z?V85@r%XuPj?8wO3DLSPA%37sh>ye*YSuJ^0#i18BR>D-^%h>C$c8^Cvf&A_Y{UIG z)2ikxyW#3pe^<##$ehhcgz9yO(7Fy03fCb**BV6SdV?`+WG3SFDdl;dZH)MYvK{df zWk&o)fibN=@D+tzl~*9-2~9fuLz52A(4@mBBI&rHZdxPV?(b`R+~41JROx*tyX=OV zvf%}aZ1{j88y=v@hW^#ql&dOyqRfb=#AVv;jChPPBc7wohzBV!Dppma5>q<7L?QR) zH6Q+=Nrz`>(%}#;&q=h#j8DMs;lvrc5A_xHb?5WHv7AGzwe&d zKgagI#z)(AMuOXvl+qBtHE7VY1`SHqph3g! zY5p;*KhE3xaxQdNyZxRb*`(W{eUv)1f2q6PY@9W;k2c$lJBD^qVU*p`T}1oaMmNOL z*0sw&-kozRXJ*tILAH5x6KS)}WN|jzG{T9q*Cr}WTM;AMtA3NMh@e_j#MLOOr8+rU z>q|`0N?&4%*7*`sw91#5A~l|lbkKH0{hY6rplS^o^sGUHk~L`1a1EM!Hq-mSX36jM zyZ?Qrcc^%$^Sz(#ah$WMJrvoZy>i`*m+n6+mY%)Lf<@Y}cx^oG_kEsdTzYoe<0)IT zSGINAevcZ5&<`*A73|OLeZ;Tq&izAM<#}zs|Jc^CbN{c=FT1o~Qy1IX&+{evs2PP% zPlb)5sPJK|v(LGyU9?L3*dd~QJ*^DUC9OZPQG{0JGq^2#X+C$`w&mDZ${eBRbo#z! zET?@f|0q*oqZt1f@A!y_rEZnTSW5ePT2YLpYF5g5B7Nzf!8Po(eeBRO?$UnVD@2xv z2v?@UMp0D6J*?BRMsTapK6Z#`Ur#I1araBRBmc<$dHilKU0);1 zZAa>3S0ZdvKaMOBwHo1++vfS}#gFB-)wJ;syKgLR>^nad(sBe?Y8hSBt3j4pj+|*7 z=Fv_^g_Y{fwJ%eV{i!~Dm(%{l+`3&|bQ(>i+adkQ?g|D=VZPT`S_SS8e zdjo4k^hTKqJJq1_`wou#xiBC7zwCF5_!}ed*w?;eWEw;ETa)h6GEd&VWp*sKZ<*bR zZCk$3-Ll?(V!H)jn+8q-`+bQdMh8nIF}heH(a=dbYSoOT+hRv*uQDadiioYdQ=&R; zN))L zXU>D7RCrL30#7+(5y!MCm9$p2rIG^6lv=t>^A>v4h`X)l;7%Kdb3QbvYCqH$#rIbm zksfy6^!unzX5-S~Cq8fS-16?r?hE_#X0PjJ+j}>sBl2m|p*BrAw5CT_j?Totn0!3f zUzrkBYEz<4ZAw(CL8+FHQ$?nHC{ZJy=;h;W_CtM9_WQOU4{B^>-UjO!7rHq&DqI^k5pr$ZsCG@<HR*?7VvL_)AZOK0lr(Le(G76X6RV z#}k#aG;usC!=(OECdF&?N%01KQdF)(s#j;^3QZYNrcU0>Ya%qM%!nGJ7$6dKaw9u|adb?eV^kmjt`*(?i{*EU>2k|86BbKCG zb)ud+Rnud~MrA>C6vY{tMHZ-Nk zhOX4u$~lUN);7+fwK5k9RO3Q@YFsEUic2L^j><}jP*haR&$BjK(jh`cZHaQr5S5}i zVlp*#Dtbqn98x1covaY=lUu$qB>C#+;-eTivo`q1F8W$Q<<3e2#TzT(Bsw$_6)KX3psi2&u zu5Ny|GY^mYyF;ILKiI0ylYW;A=U=8(RGt0z+uKJQQSGCKsP^gqsP?bf{-?j#pNRI; z8PI4wF7ZU_bBQNXuS-0U`d#8t(sQ|vCqLJyMK{OX)MjNyl&{Q)Hz+gWAeT%oN{4@F(%~7JbofLJ-B)%>Yx#XTF_l`*gYs2)(7Fl_>Q>=F&k8){ zsw1*8nrpnwi1H({<*tl)gEAu?qRfcbC@`kA2fm_^q4Jq6yhM`@|InnvGc@V&i5R;1 z>i%@jX!yyO-Oo15`LO$?!W)sc)#EuEUZBW^4=A$X0g7zsUyZF?v5+Yuve;lH-e6W{ z#8Z?R@fc-BJV%)k4^m*%s;W|NN$Kzsh1{FheE5eZ9iE{{hfl=NvAbtlPu1M1wXKqx z@}PMY9#pQvgRWJ0P_hC~x!#DZiBNZ$4$Vho$nA7^fF>ROph<^!#L}g81>O*wee#L| zKTzYs1Jt#G>y@z4a>ZE#aejrn6>cYO>5!B)6{sC zD*nx$mXhLO(Y0e96H-!qOP>_4(kI2A;z{Ro^V2GgmB5$wF5sVRwBa*fZ-Pb_7rLAi z4^rpEf7Chg9(7K9N13x+S*cWlgY}eGu;=-7S6;j=x>DJd7q43jFW#rdn^rCOnOcU` znhoCUW2sH?Eq$BfRr;j(Q#|Rn@7@z|yMI6P^W?7Fi``qR!Jb(S_*4JRx%uDDb(iet z_xd*wJhabwVt*bkeCM3qDD&3dQTW(C=~;KHZ}Fjh=CAM0a35{?=|1nl*?n&NLbq%i zD$qY{b8MQK>xNkIhi!7(5S{+8O>P_F;UBh{+eUvxk;lj~Gm$g8iaZT75p7M(M4B}* z69E*N?^&LEV1HgG)sbTLraaPE^1IZw_}e;ci>IxF79WeJWnbQO=J|I2{_@B6){e)v z!-e07s{Lu-3CUZ!gm{fEA)ca3h<9ibma8h}yrMHF9#ba8kMv3LCw)@Uz-kJEa0_{n#+ z&*z2B=*P_CFRMLgCdaOc8J}DeGoH96X8dn0%;oG)234$v2kWk7XZkL2rQ8pA?wb1n zPhRsg;Mr?x%$Nm%k=oBVy&{B9zR_tJwCfmdi+*%j3H-4&JmE=`79=bMe zd~qDt`2hNI%t)BFt@F;=jF7@{+83Kcv()S;!!!7iudGXDxQ*)sdzb(co#oj`@_6kzr8F+4b}AF1XI(1@r3C-ARbbG#$>yXq-^+xA{$VeP`nluTGygNZ;xqAtN;OjIBPkoXB0ity^&4KH$c8^Cvf&94Y<>&) z!+tIGdB2`|GMgE?*j?x@+S{*c-D;CZ;AI|Eufl`URd`Uh3J;1_;3;SExJoEjd6^RJ zx6hNOp~NG!De)6+O1wvdGOa-H8jWm~S0VU|Dj%Mr%7>4r^5Gp3eBbsfX#RfB>1@(n zyZPkU_Msiv{U=BE^+T(qj_seret()Pbow^V1N*ys_PBekU3E3*$5YnIk0-5_A5U8= zKc2Wo{&KxVerun7NAE1t;j@}_c%&vB{-#NXH^tH^ReMu^NV)KX*eW2eKJfrGF0`-4 zh3X@??pQVVW>(2vZaC95wvLu#T1T-lt)t7B)=^*k*2@__uKpMlStdiL?K9LiGBm75 zhQ9U4(0&|Qn$uB!TrSA-Iht2uLhVXS=v;{jg~u@Y?9X(T$k#$zuB6@R=bxMa4aX6n z*f;|88ApH`V+hJwov0g;2i!->Waw6p3_a_Sp>sVl^dCp2Rc{R1PnpnsT+YgCAk?nJ zgwB*b$1T~@$|Ifx4abw9+;|f78cR~n|B;o+ z;7w&V;&end+s20071>a~A{+h?$(Ggw_&{V%$twcXuSJFKwWv_M78P2LpyFQE>FmfY z+c)$2-RX3G-fmwEElz6|9U^qCLxiSvh|seJQMqm)p1032XuQluR99rf2Nc=x21Pdf zB9cw1rWw5;r9%CY88NT4(7hHFir1n->+PxXek0B+u*4}lw@aL!bCx(2=Pd2C`=jkl zf82dxtAh{ht7#2hJ4fU#qs;a#qr>(sqq_Djqp`Lvr?)}IDQ<}*MuAHtF^XIwiBaeh ziH2g!BPOzZtVfmkG}K+@)6jgGPs0<+d@BBtj-~L6`ql5erbuH*UT5JEdSv*59vNPs zM~42}ljS#w%aX!vDw3ywzsg>`g~f` z=Un6fH7@k7#)aC|xX`*1S2;69RYrq#bnZ>v?4M=(;T^3j-(Bs8m#FgLEzx{Bl~LNW zDH(YpI+y142fm<3h8O6Oq5t+|<4q*fs_3-$9u0Bx;QiWbtNLk9k588$J}tmR3plKxAIcDm<~!#Dvb3m{53oCe9p9=Z<^@?WNr#&(B)T1<{_fpk@UY zRII>)dKFkuZ8S@{4v4ByP;!|JeYehO+sRP79vRx#Bf}Ho$kJ*6<;P`|yc$6BN=&F- zi3y!6F`@9*Ou34pl%11A={YBfQgKcarQMvQu3`_n=XNK|GdrhxV*Ac++g+je`rGV& zwX>`Kj-_cm{ltFjS%0#1pOy431Sm9yAn%j9zPrsLIS=-!6V;x*)55W-;V<^*vc0c# zp9&a1KYcQ9tM*FOQQ|;yqs5O!SjYcw{%t!|G z7{yS|)Ge!r;pt;f%xw0XvYk+Ji>$b-olv$C6H1R@N;4$dj>rP(yu`S!t3iXFHE2+> z1`Qf+Lo@E{o>mT@+g$3uY@YS7e~*W&X^310?`NQDRugk3DhVYf^sB^#ij|nqvI0v4@z&%6I*pmm7J2G;MRF8uYJ&NJPE3eCqb{RNDgfs z_Qcj}ebx4Pa>wvRd*7qaQXJV&e8qPDw{y0B_qO}I`?X)^eb7C%|E7OG*Il>oegE!v zd@cBK_uImE&UH8KyWiT+{nN&;&e>a7o#LL|X7ZqWWIuml?WBMA-*T3F_B&7d|DUhU zbyw{B-*mUSAKw4BZiTdDPdx4qdp_=UcGP{@@27ocef7}3bJ)*>dH<#3a92dGPvXWO z_S}y>iGF|Bb3gVZhW}yD54UHz5^p;)NaIkMi)i1b;@q8!sHw(9^iku&i=(*Gx*feo zRfe9QPprZ^vH7Rtem@^d*C9gVIz*^jhX`G_A>w@AbQW`ZdiR_u$;TLXtrk2oT@CT9 zW{o~2LcuyjC|HLG1?v!@U=5;jB|yY(kwH*(nFg(E(4cb-8Z@p!gTC9+q}d(yw#^gK zRmSK?sXb6^v^~&jGzThe!@)VtX&vFGOnlzbXC9BLoX$??qox$Z(^v{r8B2jCV<}K% z97Q?%5=~pgbW~cVLAx3>Xjp>=Eo;!A>9#a!?nAw8V|{cjF#1ty4-^}1540N1fl6C& z??&b)PEY0R^?RAT<@E%leT-a>W0^8DJWOs_MK`dLrZRxcvwZKAQ3ZKAA}ZK9`E zZKmgGrlX5GXT*mQ#k_}?DCqH63N#Q)fhyuC%9V8MXpm~9OooD@e5p)^s`SWES{zwg zX`-*1)y3v$l;>TPrNo4$l$cOcOQ!i--lFnZPJw1x#_*g1mBdn@i#UpMY$o2ci-7Z9 zDwClgJu+0KM~2ek$Q1Hfs<4y^WyQtmJjbFbB_`C=l4*YGb(+tHpVWNMi9Cqh3u zM5srH2<>PPm9tFy{5RKAnGBV+i{-nLp*B4-R2N5<<{I=B7en)0gR+#E(3BDrYHG>E z8q_rN)x2MPIp<9&P)jTY`iP}K5pfjdd_kmX7yD32nF;MEF`*!;jC8abd zC?W>unG@}3(4d-jG`U5HI*|ssOtx1h7wrW0aHsQrZ?~Pd zLBG~)gMO>o2K`dA4faOVY~y-^KEKhRjhhYH_^f&xep5niAG}oA&+`~IMwU7mg$T3M zGV!K;%S55}EfbsCw@k!p-!gHmeal3*_AL|h+P6$3Y~Au0AKSM~byTNiKb7rgVD8%v z#RvTjI?wF39?WPh?7vs+Zx_28_U{|^&pyxT)_ICFuGf1L$Zy@>x}Ul)%<`|z zqJ#A=?bSl_7Sf$y?OQC(^SF&`%v@}K{-~b5(9PGEhkj`B{PT9Bdn2Wp&)l@~y9?cX z)oSRC;d}SZFW;CleYW;ZRnMF=ME6We6V)^0t%O6bynP=n|7Kr(Z8^zr1M~al_L*1G zs4-GR_sf(ds!urYJoLyd^N^n`e?6_*1GE`!|B)lg`pk1<{g-C@pZeO3pVagO+KBFl zkt3!bT2~G~n3wsysprHR72@#u?drqj_F`03Vri=^DCRZM(Bt(g!{t_E`(bW@7*C6= z3F(ux2D@$JZ~vL!YWuy7x1V*tzl#X<^^vl~_{LcM41V#px5pzJMc1wc>64Kl)<>os zF?~TLL>*GE3dHub7;E{FBF64iKG?lQ{YMYbc5Dxf6cIhpDj(hN=Uad2{<}MGe=hd_ zJ>P%r-|_E#-k&mm+0V*8uo3XdyKGS7jL=zVmMnG8n0@{1sLH#Qw^RCP_1NYN2Ji9P z0R293KdtZculH>x;gx;u?;n3)e|O#cZx6GxegnDBef!M#qdksxcc<30=xgguzvFed zlfA6hJN8>|-dWyP1opYlU>6&nj&tp{_7#~yb?Uag^zHRGZgZ0{~)Z10G$9Lwt zzUnvF#H-I^``ghhLhf(gX}dSWqiCzUW*v`+Zm;1Em~u><=c7xuW_rQaNn2X-EVOI4 zI_gQ)?C%cQ@B4MPEBQ3`hkNbzr-C2s|D)|G_|9sto3grxckOon(W=`=7MX@>*XNRB z#tNs={FHg^_8<20)Y*KTIcU8&xy{?SxV$V4RUwz$%}jBBJJqYR5U%IjGvt&FGq_&K%cs^WR5SvHAY^bdBI+@@1a?WB27teU6{6 zrIm8~KdsOG{bI-cDTg7dkMnwK?fu;>@G0tme3r(51FI3otnLc%=-?|#{toP+8vK~l zfE_+X4SWoFeaXW=g}lD>+~l>vr^kqXuo%<*yZ7zf+I5>vYPgb{M*#<63^&v2T9uXR(JEyU&wCDZdAzHJym+DU*nUnCZKoxV zh&xTP8;zpP<|EoK`^w8#d8g+ghCOz}Zs`LXz52M;&ye^@h;hx>f8wW5UiJU3RZTBh znrG0o@9EBL4BlP$Y{{|>Psh1-TOZoo&0y=AXG=cR(oU&p=@v5~OSV&(w^-t0^gSqQ zjER1)+_2*My~Rb}H}~BB{M-JFr_EYMN;nlYb($U3MEtQ9s(I#QsmISkyLPLio>b+v z&zyXy{o!7_{pVw+`e#l))b8-E-R{%#8-w=ipE>zZ+fSL-ZvS!qn|R+cXPzd~kEEJ5 zpP!%@tXcES$+C*)6h0g8cR@_oVg|CdXHL$_x`v)!VjQ%3Sn(c+C5xVZhsam`8ko<3 zz368r{FK5Y`%_-UnOBFyiKU;d=6gU_*bjDd}sNt7;0 zyM;YIMJ+_e^_qRE9M@|;H*syK)-Ty^-jmskY8o{@CO_uc*XzfB?e|;x9WJ{@?|E&0 zVA0=ie(`&`{4S{{eJ1c*njYJChrI^B+Sl{_TK@aKAK}Hj-|+iD^mK6=+kD%jod zDr0lMeekQ5_Feu|A9pWmXWp?l%8_W@Id+#PykoJ_M?`)$m+XSC z2m3nBaO(b5U*B?v;eXi)q95!8(dTxW=-bs+L#KQ4Pgey*wIM6Jhos{i^0dEQ##bbN zU#a(RQUq4`bagO~_3VkgWOv`JGh1)!ZJzbh?d=~e_PZ|+UjM(k|I^=aRPT*_8Jqf=C;hnl`X8TcoCb zz0I?Jy1h;I8+!Ye<@~38-alSxRkx|PdDivzHap?(_3KG~GeFFphG(zVmY-nFJ+k}F zVeD=jd~KXvDc@;0Kg;Z^itX+-{E$9Ay-hw11!%8Go#qz2b+1gF=1Hjtm%Y~K!x*FL z&4ZlfITbx|dSBkA)l%lGhdmlYkJY=^a4ABbjdZ;>FW-$eSiknYhG*}CGPz=JP&YAR z8LZp(R<|Wvr(BA*o8PBKRH%8Y+mfZ`6w!9N+|LKj-)jy5#e?9G#CTKRN$rVTz`_indk8=j7H?Y4HE z4z{j&x7&wW+9?$+-C{JjWIKhi+Y;A&q%}swa`db<;@U)~sHD@}oI{)+>ma|;?Jxb_ z4ZpX;-&}Xjy!x7b&v&{%t2Pt7WaYCw$=Z8lo9CI_FG^&5w+lR)SDf8OxKi;7>SpS-=;z!ao*?eghV4i)M9s7&zet%}Ceh)17zOm7t zpDFaJ^{IWw?+5?Q{`;sOH9oMf{O1R@0_gXIKk9S!=r@1q$C>{7rT_1#{a-#79qt|X zPf*A&n{2iAJ||BOUiM?(Z{PR*{X^fkx8C)vYCpig_I@C54*l?jomBYVW@c2a4(-~l zKFyelS$-+vK5OswDi;=l1rmdmIA zZ~D6m8Dx_k*503F!oib3!d+_6N_g_WmG8>SgpLFMxLKR-a}^#hkKa z`ziC~)%iIT(cfKq1!6x#x_W+q|Q2y8h^Zgyi z{fvm;uWq>!tnss>Gb_Qcv-OYwszt-Oy94h+pY0%UT}dwOf9g>lE|z zlIo|-Yq$TH@38*0?uPwywYy?FqMpd=f8^Hef%%)r2HVzrAKKE)&~W18X+QUVY}wyW z5-Ge7?X0Y8=;YksbGsBE6tcYd1@yc zvHSAU-rZ6bUu#ue?)lxG{!`Qdxs3WPzoFb`V|)j$-`#X%UojuF){MTS1+4HXDqzg$ z_3nSFU8^tk>8Fs!Om+kD%johEXHDW z-=JEz@A9YmxO-7M^Nzhyu0YqFV|RJNrT2;AYs`BL@1^#2t$8eNJ=?LpsyOQtf82gZ z)llx&R&VC18((?OkGnU0XGPo_RO|NO(zAWsy}9Y#a=x!3@(rqW`}%ZG{^_cKs5V4z z_mF4r&ba()U;EAi@^t+-sKzM*D}1^-n2&@wRa5f~s&T!|vwph0&D+KXudn|G)wsUq zNk8ts-t=aFR*oH|Svfd45xG^Q@n4Z{eTe8%gBEn9-H~E##6At(F54)8Q zxz;rzvB56|1Z6#HDqE46p|-D$>F?Okcy-qnWfU9hwNkL@;*2UdkV>reGh`}4Bh zMo_y)mk7oljv^TQJc?jcK?K3u?zWvs@D+jwcK5^gwtDoe``vUos-s3`Uh?=TH)2cA-LzT2!bn zhAPcd=qV=t<~a%_sqmm76&}>nl4o4WA7?yo*_iP4ySY$)UOy+&l8;qUN<0Y~i6=oF zu_Wa@L9}TX`%p}o2^}dhp(rILv=zamkhMl7r8FogA|~e<6zyoxpqh3xWlwJ%eaa*# zrj;+1Nl;5X2})^4lKL_FXsOkd0cAuopovHZ)KG`vXPfKscNO@Z1c&yfng=!)A$*T|9)y~B8RqHZTPAGmfs{YT_d?_@5j2(-L!XP?X&Lv5Hhr*M~0#Xau+(SFB^S0Z?lDusc5b@mfcm;6uUiSBS4QnX-T`w7DPA|+>X;4E<>-2w2>-2d0*2{6dU8X^UWfrtiKYng!K`9C> z=%yu08pY8`%V?FyZxj+sfi_|(P(^);JC-LN+4yBTuG#Nfh^J$iX>0U%o7U*pHm%Wr z^;=8lhuik!QtR#db*c4s{k+tAT)&qyFVVerhQYT}yOhaLiXIs%(j!A*ab$7TLs?-d z6Uu6rKXcWmOlV4p2{pB3n$I&&^Hn=d=L{$&k^zN8GN6bkhH|!OpU37ZDU(HOr%V>D zs4`i+&eHgdzT#qGFO(llaswGtc!@CPQUv9)baOsplqw&pQsqNoihSkF zL`-WJWk=POsnDVp6$;d%LU%D#Y3xT&F>zKSKTZ{%+6N7(*a!8r_5@*Z+Cj?fN}$yJmm?Xsb-O?9T&R|9WW^>0SHE zZ$N!+^{LmWN49G97yI+0)vUkRA73APvBz1I?d_-baGDwsz)9}f^S!eDGOt4Hu&MQS zR(N46k3aV+cxC&89@yW0F^i9HW3F+FrK@YXReZi}t9X0cR`K(;t#S{1yH?ZFSuyHb zqG+$SC5rayTcT*M#wCh)b(TlH)I$3o%`L~n)IMc0bgV~)w)M!+yB--n5J#4dk5PV| zI&7&H$hDs`p?M`H)UL#Y&Xt%@cuS`F$+~F`($3Fv22>o$fNCQdP-!Fss*GYN=Xc_- z9$B=0%QlNvb(t($>t(Wd4@k2-%8$zw=}gSHma4>r+Lf5lxe^l!Z^<-2(>Sd>IQ3lX z4(rmmGAAOA>ky%69U}CsLxh?&h{}0|DBiAq87$&wc6{$mnGD_Qk)eM*GWb53?Was=J}w95H5F=CVnXLiOenl16Z>YSl}o*wwo5h3lmZ3EQlQyb3RD_PfiB}H z$~nG$^?_E)OenZrrrOSgo|TwTw-OUtk6=o3IogiM2YD7p)fzPDS%U^8YtW$Kb~GsW zahI>{-L~C8ZQokETxFbv5zV)X24`W!6Oyf)vNR^}QWozmeYnsoSw zCLNxkNrz9w&{5S+J;1f+_ujp*{muM*f2ZxXXAatQCUmdFgxZyu(6|y4%2r`2k0u!f zMC2vZUZ%qbH0kgLO*;HSlMWAwrE~v$V1G0!q0t{wF8m-iQ|6;~JV1>L?W=L2`UtMP z_lW%(G?5z5Z4;^NoF-DwIZdpJm#gTv-RBm?PwZ5lzrVp(?)_}yvHh1jN2YcDKHu-R z%tHkC8$6ePV>e>FLYw%$+5B052K0g1{-J#}wf3dGo&sIk^Zy~%US`Mc8th-1br0>V zsk4sJ`V0HsNiv7izMd-Udpk8&)}nSTzqI(V6ggD%u*8TpH5j?|_`aPHKC+15r*@tC zQ4$+2b{es5Gw*0y72e?n>dn^GuVEZ|QoPp;bE92U0HaSE4`GTCG{VYMwPNT8{JLY7vulEzSqoIer9yG-4Xw68Q z+hX3jM*pNt^Xj^8#Lq1>zZGHd-um~Zc*NhvqS0G(`$T)w_@3*br#`pU+kaUFc-XI) z`EC`r?7@56`d33--sh^pa_^078a+4ViS;pRgf^8_?pL(0(Jv!SWUsXK^9OJC zx`yk~Y#WP)H_h!6?M-7{H1yP4i?=`bdkwsD{KE3oGyB6%clHhKyMD}jZsgMFwJA@m zfB9~ZW2+Qy_tnC4`+DfPi+29vOY5tla(H0h{oeZRS@(PK`{(TB_5A<&Pa7e;?CX%< z?2~-o-L{{%P(u2=DvHRtVwFKHJ?O#r^wxZ%?l8K{Jr@d%UK(j4`=(WtAJ>%OVwwpa~r(H3jIH}_acEI$?Nx-FiH)!W)_&(%+kwAH zuVo%o+$JyKKV=@&tipq;Rd`T$Yo0V?qvKI^bu;#;oRkCw$CIGlcoI|_Pl8_Ckd)&J zdTrr1WeT(!OMy;fDbQ#v1^R45k;X1m*~&Z8_R(Wh`zSH0eKZ)=KI*I6{#{$Adt)nT z&usU~1N+L)aKE-c)6vxB?wb8mBZqHl9sR|$j{0I+M|&}?qrCR5r?Ww^DqNz8Q{@s( zoJyBy;#9jtqod+-%}0)}Sz(l7Y-+PIC7Rc!#22(F@eyrGd`E*atxWNnn93;ko0JcK zQRTx^RQd1`RX)5UijVoOX?4_YM!TF(rd;@d8W-AE<3jUlTxea1t6Zs&8DjDl`Y-e0 z6{>uAhbkXlqRNN2MDwK;34RfsvCK4E@KDmE$h@kMPSfRb)0O zQ=xk;TcLR^D)b&hr4{AXVtdYmvZJC?+Fxi|g$Fg);8{9L{L@3``ZWmt zX@2Zyrl<2!m%4M^KHn=aZ3Ao8Y9qHE^RhWDZP?Ht3+8~*MxzX{df zO7+m<%J}^+`kQwAEkC#4-(_{h?kD$iTNmtP?G?MH;+p;2zcZZWI&BNzS$lq(cR0`a zPrE(qJNx=S`unq9_uuy$Jp5kxf%SjePJ+|6@n7~i={+OP?9Xtq`^=ue|9jc~3^&b} zKL3{K^kKih!DH@8e{R^%504hR)ql;?_$RuJ&f9*ZO~7vbL|- z-)`E!hd=*mf4gKqJ1q-mRp%D<_vZbN{qeg4e(7(_JhB#FZLp$e)CZPP{9N>S+mheX zLLcyypPLmP*%SG$kIm2QcF3Rkx;^t}_UE5F(tO$Tzpz$)$4QBf^ZV0y;wRfrdVYWR zkDo4oYMYG(-Yu@aRkKfdIWA+_lVcMmATDl%$yfZaqeYjwQ=e5 zv8{jp{y!bN)uN|T-uX>w)ZzBeuYw^nWc&10MG`~BeA~$_LcC7XJSnNYT->aU7W}~6vSsu&# zn+JV<#^0Dajeghc4Nuo>OmMxg{m-SR zvog>pwe8}uz*jHkabimCzwu`qBYbQ3MEM-VS>kQ_d1H&%I{gh=^fyxOw?!;lYVoYk z_?2Z8za7d)hMvEc^gmVnhj`nrZDHMO=0#^!@UCrPSNB_=NjQ6ZZ!C)V=z5$-*k;SC zHdo;Hnw&)`Usc$tW%v3_@`L^R$wIWufAh~a0<6_4wp?oUtUi~2R{E@1Hvi2( zTaG^4vwmY9emtqAelV3D&Z>fOCHO3!UH*J*SnKn#)`xw5nw96XZ9XGWB1$6z1JRF1#&jEZfhOOZ*Ki+rPi_g=GJRHJ9DO+ zTd(!(%&Bf}{j8brJ6mm;*68^>`dQ;t`IFJo{y!O=e8?wHJ%M$a>4?JTwtd8UHa~N# zlv=7zsbs1MgDF7o%-L0jOfds zjs@#{I+ps-PakeQSgOeWYW2bcyOI3tv)khtsp;oqms+2XW%lv;zTV?^tGi~#S&dP9V^GQP9MayPIVR2I@ML{*1g9^)qPl8 zIGgeY0+bg=fcoMHP+%MZDvTg-`^PGAAc$1rKoF_KfgnIp~n)Oo^DA2pw_<{D_}v(d(NQ{GwCU;1ox@gZxerO&3vYjD%s*ZRFGyZYny z{bqhLYWFen9UC+HIe_7ILH~c<7Q?u4H>NxGe?PRynoqxQ~EinE4K4t*VV1*^Znk(+M(}HKf5?YP@hvB=lXA36nbPn`{sS@@&4GA&3Ctq z`_|re-&ln4Gj6`~<;(6j``NIv?@?{nh?ail{O=y8+0VVtPxZJz&aVy@{;`kMLv;SB zuf#nnKH4$9&AENXc5189EuiKxC<_rGs^R^yy(^|Q1UwMV&=R(dK+hx`{$BgWmSg&afBtz_jF}KUbUEZTFGj_>^g` zz<%lXA6tDn*3B>8ad;f?{OIR|cdzq@&vqO8TX}!BKIFXTypkSgr{=%4do8_tON)#aDpq%$DVg~ zdy;?b{S*IbpXjyHFi!r}yvff^`*(aK{J76g{!`+hS5dpME`Rda55p(=d6r%G=lCa% z|G!5#AJ1~S2EyIx{@(q=;*#%)9csl#_TQnF89wU=`}E-y`1E&n|NMWx@BLf$Np8*Y zEYQ%x*Y;f>pXVd~`}X~NeO2r`@x4lTaSFSSE#K?Uulq{-(Bi#*ZT&Z^!Zz2fJ~w+} z|Ci6E461!Q+k56`VBU6rHQV{^IuH7v`AGS->BrSFM&Hfa{_u8)Hv4ENjfm6TcK*bi z!P#e9@R!=!965Kh-&kpm{lX3_ze~gQ?Y-!KJkq|l9{jTZyN`Gtyz9?RH*0vr*hMi@ z5}ushW4n@E?#@Eub9+DA(|BFL71r zbJX6gmD1cY^@GpF=FIgJ)?U%jWH$KKh)c zeJq~0e|p_6wV$5YoBmFzFQ&h52$+A$*b-XGe`nhnw9`HlYidg^Z62GPwk1~BmRdS}#LYitwBGf1bz=TIf41np zPet6=(9-7fbK9&pJ`I^^`ZOQv%j2il@X)uD6)xW!;Wr!k_{7f_jW-4O%*Si{iC10I zT6;Jf{~^6}w)U~aa7RJA_P_6~FtxyNTS5c@HrReY!;*fd=S*syC_78LJk{nCO{b4` z+n$#wzPa7gR|lnbm%q>MdY_Zx^qzZ&uhjUu%1`g+h{sXYZmY$o>4^^|qX+fK=;Qs9 z`P|TN)=iSF$E#xqt0bTm}S#spk z=e)Jsf97jkKEL2IcAn1;Ca3FmyJvC9Z92HsD8!gpFd`YPzQJ;l5BZ`#KA9Q*Kdk0*Ygs^?B$RTyrv@i}>Jw|zEyvb;AA zY&_wy(O0?s6FfdnBXUbhY_mk^-}4o~BfHIhjGFtP8*|gB_9cS)cAAnz*}~VMJWu}r z?A-_4Cf9Wy_7_fUIdUA^iJio$jvXgXq9~H+h*GL7S+Sj%DlBNbi^cZdd+)tt`!0Y* z2Nsne0D=So5F|j61iK)JO`;0bs6xw{sox$8{`JvKKN1WrO%E*0s@ANb zHsWB*huy0aZT^8g;cFk(xvZ*vJ>>AEOCGoO)hPM0v~zh@1daQgAxdoxXB=;Mu4eqM z^z+xpLwWf!$1Q%jGRG_V3y1oTclvf`$P4|T=k7&#snxqz!gE}|=kk21xa=$8IreRy z%k!n=`lWb2SDTC@U?N4NcKUO4D7|LS_VQ@{O2BjXfc%>!`<0eG?kC?tQ<8{PcL=G? zc>nfEluP#*{uDl|Sv$?GT*kd9$sT(q=F0;R$&W{po&;vDSH|W8WH#d;c3(gYgKwnl<=` zk$!WFm0!jCbNjyEv+4VMuMYZNtn_iee`87?_xU%b^zk~C(!Vo4(@XEneW84REwcZE z!!J(RFZSXZeq(;U<(1h2ID76}0lo#|>(Vdv7S5|%^FL}w`uidiGA^3dxdJb8;Y;|2 zuXaseiLEzN^mV!y^9+6uPSNtOUIjEn^OGE*2d?@n(JTN!(ry# zU78beJmCR(--w>JA5Ns`xjFdZuVpNMZ9Ovf3|`+o@?2C*NQo5^zk>G7Ee+3a`&b^l zy~ba9?Y8|N$101dncaH}`}yyTd^6kgmuvZpX~gDvE!+36rPX68|K@M?OZhi19n<`? z?%%xJct2mSjU$2H(0*;}^slYmV@dZe{%cp4zrN;UFV@-#oo!~~iPy5Oez6w5l&12A z>^GP6K36++uIWwS*D^-mnpu}G%k-V8@tXYey{_*Z-aPtdjibEM9(X>MyX)YkzCZD2 z-4egjcE1$DU;2Au-z+`wLezirxB9zY_lVa66NsOPE_7vK-D}vcy%OBNu=Qhq^!3yF zm(qH^k@ed3YhLO2KUeEb$p7oH?PKZpr7*ww{>?r1Z+PA7em(GFy8rvL>hz6W&Ha3{vS{-t&QYk6him5%?vN%vb>c|GXA=eK{10KY`~>aDK4aQEk{6+0Q1 zGj7!%cNgdjXLm2$Q#LMVypQPBZqInf(5qL@3nL(YKYsb}-Mz&*TN&%H@~eBtRwUXt z;WNFudrki64wsj!*LdXrtkr8QDSy`LHI}Sbw_amBJy&vG`Ip|rTKT8nk-5p0uabxQ z+!~H$=%s3CCElBReGAGLg});j)Vt)d=ETeW2U%@j8Qt~5>(Bj;)`TaF%OCFx zTrcGRO3I!1*0L|`JpUaJHR%^YzwN8(D{tG2y)A{`o6;9=$ve{jBxm3LB44+g`XcY1 z{YgrFe!sq0tzPaOu2epV%xLvPqDdT>4!}DtDy&$u3zlFduSEqG% z?_2YI!0$|}e09%P?*Cre`Mtvx-+q6?aP^Ae8MNo@?f2{t3|GG`SH5j{?pt%$|GyQz zK+^3wr7M&>ExVNXe7-fbR+7zyhg)OUOnk3uU=3yZEgLGvU7Qjv!lGm$WdPX>nN{LAGgZLJD1n!JIZTp z9pyDHj`EU(abJ)I=kkty6M0F6qr4=;QC`w9?m-gbTwYS*C@)EIl$RVh%1dXB`<>J{ zmzNwn%1Z_vYy!|u+-9s1L}Mqk8o>OXw@|E3Y;?R&!Jb z6wSqD5mv0Uc9q{sqP+ebJI)=k_I-P?{UNXNTTXL0_P@NfL$yS;MdZqLdCApSujw7< z?vX1;<&rB$<&rB$dCApS8|u?>N#x48yyVJJUUKCqFS#1)P$My}AARLqUUKCqFS&A* zmt2iCukjnVid;FDms~l@ORgN{rLV^N+Nh8Ff?PS5ms~l@ORgN{C0Et*#bkTXQC@Q8 zC@;Bkl$TuTWfDZL#z@A?xt=Chj#^Ky9MyZ|z!6b2#?DyyVJJUUKCq zFS#09N^&)p!sa1YuEdC8TdyyVJJ zUJ~HgTFOhV99<(<>Mbw1a+H@`Im+t^M|sJWqrBuwYe@~cax{|U%2DsOFFfH~-N}`s zyyVJJ-ueyX%DKGlLHC@?ORgN{C0CB_lPgDg$(5r%b=6T`a^)y5xpI`3Tsg{1t{mk> zJIYJ09OWffj`ET#N6%Jkbc}IvMP72{C@;Bkly@xk@{T1@x&9pGH9n5=>N7`q$1Q6e zTUztEN3I;@C0A&9$(5rzlPgDgji_UDO39U@yyVJJUUKCqFS&A*wex{Im%mfwJ$v3 zioBgYkSl%U4!Lrams~j-nf9Q2&gCUn`cz(W<>)@Sa+J47Zoj+gioE2?NXSdB9OWff zj`9}&9WAsg@{%j#C+}GM$~)GGo`qh=jsiPCmzP|TFnP(9qrBwGQC@Q8C~xzSE9df(D@S?Bm7~1m%2D3ReQQas zT#=G1M`KQ|9F0S*a&>4WzV-98yYiANM|sJWqrBwGQNI=^ZI$O-k(XRKx<;-Xd`juzS#dB>Vw-m&(TcdQXTJJwPiX?d#y#%Pvzj4*k}QZMgV5-T6_8eyqA z>vhNWqP*JV*g95E$vgfpFS&A*ms~l@ORgN{txl{2w>)wsCmA7Ej>e5#IqJ*W*J{Jc zZcCC!c~WxaC@;Bkl($y7I_%5&N#FS&Abja)g( zTMwZ6y69?OctVfJORgN{rLP?2t=~{BUre?K-P4cqk}F4fC%iW2B&fEbNN&GRcpSz+ z-U)l|9PbHh>AYH?W2W2*``}!;6WZ6g5+-z;b9pB;hw)bK1TLK`cS7o&k0sH$a*e;E zyvD?F+>6e~Ept9D&AA-pid6O3)M%rPoYW{t^{79hY$Z+ZMBgdu7k1UriKckMODVvrbsV-f9 zB)fF^(VC^pk1i`+exw}jZqxE3>!r(&yq7LNmbrBKkwi3&+O+&gf9djrfYRkhiGFe((&bltxPqV5QHq}wKPi4v{G|9v$327}x;1i!p7>vW z#fPiqSA3w$ulPWhU-1E5e#Hm6^(#ImYD04?wfK;>e#Hm6 z^(#KmtzYq>HLc&cRmF$vtzYqhZvBc6bn91qpj*GpulPW>sNfBTfgE1-TD|J7)y_|^(#KmtzYqhZvBc6bZb(4kUsUBkiy~vt$tGc zr0OTdPpW=W{G{rqF6F1sO1tK-{76FS^3(IB%TG&7mmkjPwo+Pt#RsbViVt-86(8vG zD?a2ZzxF=5{E82B`4u1N@+&^jKG5Y?e4xv(_&}Fm@qsSC;$uSl6d&mFD?ZTWSA3w$ zulPWhU-2=a#flGf`4u1N@+&^j{E82B`4u1N@+&?jtWNQPF2CXfU4F#}y8Ma{ zbomt@6V|KvK$lN!3q^pH%&%_(|2zv*lNOpw+K+YFd89hx76)KG5Y?e2}*C z!$tn$L)!WkAL!PPrt5R9AI(y_^(#Imyr1F&U4A6h^CYZk`5DpD<)gY55f&XnCdOSNnl3zuFH{R(^7OzW9)qU-5x1Kbo`8wY@Y| z>9)7{n3xq5AL#NcKG5Yy(&Q&0P0P>7p!H_D?ZTWSA3w$ulPWhU-8je<5zr0%dhxAmtXOL zF2CXfU4F$!TaRDyAuYe+16_W_2fF-<4>W#y&v>*Kn;H)^ep2!*#ZPK{rT9sWueAJ% z56}27Ex+OeU4H7``{h@BI4{5A!)UgC#Rt0liVt-86(8vGD?ZTWSA39)^3yuy7a!8L zxA;J}e#Hm6?JYjgZEx{WDZ{V$kd|NZfiAz|16_VJU-Ok;@lk1Yz4(xpU-5x1Ka$($ z%8#U!EsNfBTfgE1-TDy|D^ay^`Er-iVyevmzH1gfiAz|16_W77F~YDhu&}faPds>A#MGN4|MBSe4z1? zmS6E<6w6O;_lpl{`4u1N@+&^j$MP#aQ03>U=Zg<%`4u1N@+&^jtzYqhZvBdna>uXukd|NZfiAz|16_W_2O2-M zQ@_@-srI7rlWMONKdJUg@snzqwET*Xw$}CHLt1{t2fF-<4|Mr854!w{kM^2-#fP-~ ziVt-86(8vGD?ZTWSA4Yh@hd*05@dg<~jGCfy*B)Rv?kK~|TZCZYMxODkxQR&KGYan0w6(5e}hr8#C4{7-oAL#Nc zKG3aS@qxwxH~h-AsrsSuld7K-KdJgj@sn1Tiw``@Pwm_pulPW>e#Hm6^(#KmtzYrcUdONa zkd|NZfiAz|16_W_2fF-Q=!BUZZn^ib*YQ=`(A7aYi*|4m79siq_cZN#NUN~(vYdPb^6((;23wAwYL@zLc6 zAEnC=K1!D#e3ULf=lv@`+)9@pe3ULf_$Xa|@KL(_-~+9`N2o&@`I1k zV_-I;w@KL(_;G=Z;!AI%xD?Ugwe%1O-@k8S$#ZQW#6hA3`Qv9Uq2NmU4eDv7s zP3y7enQYF+V>t7-YMAEnEW{U}|2>__SH zD?VzaUH5d;@+&@EFTdggU4F#}y8McdT6=sv)wKNBkJ9BgG2pv^wIAs6tNlQiU+o9F z{EClyXV2wQe7N5B79Z%=ulPW>e#M8<+uq`1qBi_1{EH81>sNfBTfgE1-TDt)!;qS9~~@U-5x1zv2U3e#M8U%dhyDKv(gB zF2CXfU4F#}y8Ma{rIug)s|l1AAL#NcKG5Y?e4xv(_)z=uD?TRBUVNa-ulPWhU-5x1 zzv4qLlwbX;39V6lpv$lLK$l>@+&?_OZhpvUwlZ* zulPWhU-5x1zv9EQtzYqhF2BlrdEsA8D=oijHgx&X2%alGI(sf%e#M9D_R3w_c(`AF#RsbViVt-86(61|zv5%U>J%U7@+&^jd zq~%w97~S$KKG5Y?e4xv(_&}Fm@qsSC;={A$SA3w$j|?h*;;;29K3s48iVsrO_SSw( zcrC>Ty8Ma{bomt@=<+K*NNf2)wmQh&wDl`KTyGsKq3E`^5`k`e^`!iIplSKFrmnYs z#fM|-SA3vbzv9E)@+&_2G`_`$wET(>bomt@=<+K*lu~}hM@z-8_>h)g@qsSC;safN z#fRFKU-8k}<5zr0%dhxAmtXOLF2CYKtIMzWXfNPbd`Qc$_&}Fm@qsSC;zRG@Cp8}J z&8Ef|ji1!`O7W8#Unzc4<7*VkulVSw^m_RfAFh{Q@qsSC;safN#fLImzv2U3e#J-I zsGNzv@+&@EFTdi0bd(=1@)RG^wzv2|muK;TZhMOl($)4BAC)%zSemBgSA4i$e#Hm6 z{E81!T7Jbxr52y!Lt1{t2fF-7UY{#JBaJpPP1{~imu~%9W6yOwiVw%~D?ZTWSA4iz zexAaw_>i`K#Rt0eD?ZSzU-5x^>sNfVH2jJWY55f&=<+K*(B)TrsAc&TAFVZh#fP-~ ziVt-86(8vGD?YRqKdJV%^-c938b7K2lj0}UGgADd`cJQwU-8ji?Dg_1K3p%q;safN z#Rt0liVyc%zv2U3e#Hm6{PczLCjMH#;=?Gme#J*e)w9KiwCycE(B)Trpv$lLAPwbL zd{jE{D?X&$Bul$M+RQVMju9lzDh)g@qsSC;safN#fRGACsn`JwyE}_@snz=6hEo&a4f¦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