diff --git a/core/lls_core/cmds/__main__.py b/core/lls_core/cmds/__main__.py index c5560d3..d567fb7 100644 --- a/core/lls_core/cmds/__main__.py +++ b/core/lls_core/cmds/__main__.py @@ -36,20 +36,17 @@ class CliDeskewDirection(StrEnum): "input_image": ["input_image"], "angle": ["angle"], "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], + "physical_pixel_sizes": ["physical_pixel_sizes"], + "roi_list": ["crop", "roi_list"], + "roi_subset": ["crop", "roi_subset"], + "z_range": ["crop", "z_range"], "decon_processing": ["deconvolution", "decon_processing"], "psf": ["deconvolution", "psf"], - "psf_num_iter": ["deconvolution", "psf_num_iter"], + "decon_num_iter": ["deconvolution", "decon_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], + "time_range": ["time_range"], + "channel_range": ["channel_range"], "save_dir": ["save_dir"], "save_name": ["save_name"], "save_type": ["save_type"], @@ -144,30 +141,25 @@ 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 Z, Y and X pixel dimensions respectively", default=( + physical_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=( 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."), - 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), - + roi_list: List[Path] = field_from_model(CropParams, "roi_list", description="Path pointing to regions of interest to crop to, in ImageJ ROI Manager format."), #Option([], help="A list of paths pointing to regions of interest to crop to, in ImageJ format."), + roi_subset: List[int] = field_from_model(CropParams, "roi_subset", description="List of integers describing a subset of ROIs to process. Defaults to processing all ROIs"), + z_range: Optional[Tuple[int,int]] = Option(None, help="The index of the first Z slice (inclusive) and the last z slice (exclusive) to use. All other Z slices will be discarded. Defaults to all slices.", show_default=False), + 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="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"), + decon_num_iter: int = field_from_model(DeconvolutionParams, "decon_num_iter", description="Number of iterations of deconvolution to perform", rich_help_panel="Deconvolution"), + background: str = field_from_model(DeconvolutionParams, "background", description="Mean background value for deconvolution background subtraction, can also be 'auto' for automatic bg detection. Defaults to 0.", rich_help_panel="Deconvolution"), - 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(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"), - + time_range: Optional[Tuple[int,int]] = Option(None, help="Start frame (inclusive) and stop frame (exclusive) for time cropping - Defaults to full range.", rich_help_panel="Output"), + channel_range: Optional[Tuple[int,int]] = Option(None,help="Index of the first channel (inclusive) and last slice (exclusive) to use. Defaults to full range.", rich_help_panel="Output"), + 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"), diff --git a/core/lls_core/models/deconvolution.py b/core/lls_core/models/deconvolution.py index c628afe..6bfc32a 100644 --- a/core/lls_core/models/deconvolution.py +++ b/core/lls_core/models/deconvolution.py @@ -22,7 +22,7 @@ class DeconvolutionParams(FieldAccessModel): default=[], description="List of Point Spread Functions to use for deconvolution. Each of which should be a 3D array. Each PSF can also be provided as a `str` path, in which case they will be loaded from disk as images." ) - psf_num_iter: NonNegativeInt = Field( + decon_num_iter: NonNegativeInt = Field( default=10, description="Number of iterations to perform in deconvolution" ) diff --git a/core/lls_core/models/lattice_data.py b/core/lls_core/models/lattice_data.py index 185cf73..2512d8a 100644 --- a/core/lls_core/models/lattice_data.py +++ b/core/lls_core/models/lattice_data.py @@ -59,6 +59,7 @@ def read_image(cls, values: dict): from lls_core.types import is_pathlike from pathlib import Path input_image = values.get("input_image") + logger.info(f"Processing File {input_image}") # this is handy for debugging if is_pathlike(input_image): if values.get("save_name") is None: values["save_name"] = Path(values["input_image"]).stem @@ -345,7 +346,7 @@ def _process_crop(self) -> Iterable[ImageSlice]: deconv_args: dict[Any, Any] = {} if self.deconvolution is not None: deconv_args = dict( - num_iter = self.deconvolution.psf_num_iter, + num_iter = self.deconvolution.decon_num_iter, psf = self.deconvolution.psf[slice.channel].to_numpy(), decon_processing=self.deconvolution.decon_processing ) @@ -390,13 +391,13 @@ def _process_non_crop(self) -> Iterable[ImageSlice]: dxdata=self.dx, dzpsf=self.dz, dxpsf=self.dx, - num_iter=self.deconvolution.psf_num_iter + num_iter=self.deconvolution.decon_num_iter ) else: data = skimage_decon( vol_zyx=data, psf=self.deconvolution.psf[slice.channel].to_numpy(), - num_iter=self.deconvolution.psf_num_iter, + num_iter=self.deconvolution.decon_num_iter, clip=False, filter_epsilon=0, boundary='nearest' diff --git a/core/tests/test_arg_parser.py b/core/tests/test_arg_parser.py index 26bb1e2..1505be0 100644 --- a/core/tests/test_arg_parser.py +++ b/core/tests/test_arg_parser.py @@ -10,9 +10,9 @@ def test_voxel_parsing(): parser = command.make_parser(ctx) args, _, _ = parser.parse_args(args=[ "process", - "input", + "input-image", "--save-name", "output", "--save-type", "tiff", - "--pixel-sizes", "1", "1", "1" + "--physical-pixel-sizes", "1", "1", "1" ]) - assert args["pixel_sizes"] == ("1", "1", "1") + assert args["physical_pixel_sizes"] == ("1", "1", "1") diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py index 7d96b33..38867ab 100644 --- a/core/tests/test_cli.py +++ b/core/tests/test_cli.py @@ -62,7 +62,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", "--time-range", "0", "1"], assert_tiff], ] ) def test_batch_deskew(flags: List[str], check_fn: Callable[[Path], None]):