From c7fc56daf0c87c04ad6d54e97f4d078283a2042d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Sep 2025 05:48:13 +0000 Subject: [PATCH 1/4] Initial plan From e4ce70ec6f576549e82aa86d6412f46383874f87 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Sep 2025 06:02:56 +0000 Subject: [PATCH 2/4] Replace aicsimageio with bioio in imports and class references Co-authored-by: pr4deepr <13831458+pr4deepr@users.noreply.github.com> --- core/lls_core/deconvolution.py | 6 +++--- core/lls_core/models/deskew.py | 16 +++++++-------- core/lls_core/types.py | 34 ++++++++++++++++---------------- core/pyproject.toml | 4 ++-- core/tests/test_cli.py | 8 ++++---- core/tests/utils.py | 4 ++-- plugin/napari_lattice/fields.py | 2 +- plugin/napari_lattice/reader.py | 14 ++++++------- plugin/pyproject.toml | 4 ++-- plugin/tests/test_dock_widget.py | 6 +++--- 10 files changed, 49 insertions(+), 49 deletions(-) diff --git a/core/lls_core/deconvolution.py b/core/lls_core/deconvolution.py index f42863cb..4a03bf8a 100644 --- a/core/lls_core/deconvolution.py +++ b/core/lls_core/deconvolution.py @@ -7,7 +7,7 @@ import logging import importlib.util from typing import Collection, Iterable,Union,Literal, Optional, TYPE_CHECKING -from aicsimageio.aics_image import AICSImage +from bioio import BioImage from skimage.io import imread from aicspylibczi import CziFile from numpy.typing import NDArray @@ -79,8 +79,8 @@ def read_psf(psf_paths: Collection[Path], if len(psf_aics_data.shape) != 3: raise ValueError(f"PSF should be a 3D image (shape of 3), but got {psf_aics.shape}") else: - #Use AICSImageIO - psf_aics = AICSImage(str(psf)) + #Use BioIO + psf_aics = BioImage(str(psf)) psf_aics_data = psf_aics.data[0][0] psf_aics_data = pad_image_nearest_multiple( img=psf_aics_data, nearest_multiple=16) diff --git a/core/lls_core/models/deskew.py b/core/lls_core/models/deskew.py index a8cb1c11..64e5563b 100644 --- a/core/lls_core/models/deskew.py +++ b/core/lls_core/models/deskew.py @@ -16,7 +16,7 @@ import numpy as np if TYPE_CHECKING: - from aicsimageio.types import PhysicalPixelSizes + from bioio import PhysicalPixelSizes class DefinedPixelSizes(FieldAccessModel): """ @@ -161,7 +161,7 @@ def convert_skew(cls, v: Any): @validator("physical_pixel_sizes", pre=True, always=True) def convert_pixels(cls, v: Any, values: dict[Any, Any]): - from aicsimageio.types import PhysicalPixelSizes + from bioio import PhysicalPixelSizes if isinstance(v, PhysicalPixelSizes): v = DefinedPixelSizes.from_physical(v) elif isinstance(v, tuple) and len(v) == 3: @@ -176,15 +176,15 @@ def convert_pixels(cls, v: Any, values: dict[Any, Any]): @root_validator(pre=True) def read_image(cls, values: dict): - from aicsimageio import AICSImage + from bioio import BioImage from os import fspath img = values["input_image"] - aics: AICSImage | None = None + aics: BioImage | None = None if is_pathlike(img): - aics = AICSImage(fspath(img)) - elif isinstance(img, AICSImage): + aics = BioImage(fspath(img)) + elif isinstance(img, BioImage): aics = img elif isinstance(img, DataArray): if set(img.dims) >= {"Z", "Y", "X"}: @@ -198,9 +198,9 @@ def read_image(cls, values: dict): else: raise ValueError("Only 3D numpy arrays are currently supported. If you have a different shape, please use a DataArray and name your dimensions C, T, Z, Y and/or Z.") else: - raise ValueError("Value of input_image was neither a path, an AICSImage, or array-like.") + raise ValueError("Value of input_image was neither a path, a BioImage, or array-like.") - # If the image was convertible to AICSImage, we should use the metadata from there + # If the image was convertible to BioImage, we should use the metadata from there if aics: values["input_image"] = aics.xarray_dask_data # Take pixel sizes from the image metadata, but only if they're defined diff --git a/core/lls_core/types.py b/core/lls_core/types.py index 7cfe3a2e..075331c1 100644 --- a/core/lls_core/types.py +++ b/core/lls_core/types.py @@ -6,7 +6,7 @@ import numpy as np from numpy.typing import NDArray from xarray import DataArray -from aicsimageio import AICSImage +from bioio import BioImage from os import fspath, PathLike as OriginalPathLike # This is a superset of os.PathLike @@ -19,20 +19,20 @@ def is_pathlike(x: Any) -> TypeGuard[PathLike]: 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!") +ImageLike: TypeAlias = Union[PathLike, BioImage, 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 = BioImage(fspath(img)) + except TypeError: + pass + if isinstance(img, BioImage): + 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/pyproject.toml b/core/pyproject.toml index 1fba7b23..ad6c2b2c 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -35,8 +35,8 @@ classifiers = [ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)" ] requires-python = ">=3.8" -dependencies = [ - "aicsimageio>=4.6.3", +dependencies = [ + "bioio>=3.0.0", # 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", diff --git a/core/tests/test_cli.py b/core/tests/test_cli.py index 38867aba..f51096fd 100644 --- a/core/tests/test_cli.py +++ b/core/tests/test_cli.py @@ -2,7 +2,7 @@ from typing import Callable, List import pytest -from aicsimageio.aics_image import AICSImage +from bioio import BioImage from npy2bdv import BdvEditor import numpy as np from pathlib import Path @@ -15,7 +15,7 @@ def create_image(path: Path): raw = np.zeros((5, 5, 5)) raw[2, 4, 2] = 10 # Save image as a tif filw in home directory - AICSImage(raw).save(path) + BioImage(raw).save(path) assert path.exists() @@ -28,7 +28,7 @@ def create_data(dir: Path) -> Path: 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) + BioImage(raw).save(input_file) assert input_file.exists() config: dict[str, str] = { @@ -47,7 +47,7 @@ def assert_tiff(output_dir: Path): results = list(output_dir.glob("*.tif")) assert len(results) > 0 for result in results: - AICSImage(result).get_image_data() + BioImage(result).get_image_data() def assert_h5(output_dir: Path): """Checks that a valid H5 was generated""" diff --git a/core/tests/utils.py b/core/tests/utils.py index c9744a03..daff8b4d 100644 --- a/core/tests/utils.py +++ b/core/tests/utils.py @@ -3,7 +3,7 @@ from typer.testing import CliRunner from lls_core.cmds.__main__ import app import npy2bdv -from aicsimageio import AICSImage +from bioio import BioImage def invoke(args: Sequence[str]): CliRunner().invoke(app, args, catch_exceptions=False) @@ -13,5 +13,5 @@ def valid_image_path(path: Path) -> bool: npy2bdv.npy2bdv.BdvEditor(str(path)).read_view() return True else: - AICSImage(path).get_image_data() + BioImage(path).get_image_data() return True diff --git a/plugin/napari_lattice/fields.py b/plugin/napari_lattice/fields.py index 862e41c2..7291e9e4 100644 --- a/plugin/napari_lattice/fields.py +++ b/plugin/napari_lattice/fields.py @@ -402,7 +402,7 @@ def _get_kwargs(self) -> DeskewKwargs: """ Returns the LatticeData fields that the Deskew tab can provide """ - from aicsimageio.types import PhysicalPixelSizes + from bioio import PhysicalPixelSizes DeskewParams.update_forward_refs() params = lattice_params_from_napari( imgs=self.img_layer.value, diff --git a/plugin/napari_lattice/reader.py b/plugin/napari_lattice/reader.py index cbe90615..e271b490 100644 --- a/plugin/napari_lattice/reader.py +++ b/plugin/napari_lattice/reader.py @@ -13,18 +13,18 @@ import os import numpy as np from napari.layers import Image -from aicsimageio.aics_image import AICSImage +from bioio import BioImage from typing import List, Optional, Tuple, Collection, TYPE_CHECKING, TypedDict -from aicsimageio.types import PhysicalPixelSizes +from bioio import PhysicalPixelSizes from lls_core.models.deskew import DefinedPixelSizes from logging import getLogger logger = getLogger(__name__) if TYPE_CHECKING: - from aicsimageio.types import ImageLike + from bioio import ImageLike from xarray import DataArray class NapariImageParams(TypedDict): @@ -74,7 +74,7 @@ def lattice_params_from_napari( save_names.append(save_name) if 'aicsimage' in img.metadata.keys(): - img_data_aics: AICSImage = img.metadata['aicsimage'] + img_data_aics: BioImage = 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 physical_pixel_sizes is None and all(img_data_aics.physical_pixel_sizes): @@ -191,13 +191,13 @@ def bdv_h5_reader(path): layer_type = "image" # optional, default is "image" return [(images, add_kwargs, layer_type)] -def tiff_reader(path: ImageLike) -> List[Tuple[AICSImage, dict, str]]: +def tiff_reader(path: ImageLike) -> List[Tuple[BioImage, 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 + Specifying tiff_reader to have better control over tifffile related errors when using BioImage """ try: - image = AICSImage(path) + image = BioImage(path) except Exception as e: raise Exception("Error reading TIFF. Try upgrading tifffile library: pip install tifffile --upgrade.") from e diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index 07e5a259..5974059f 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -35,8 +35,8 @@ classifiers = [ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)" ] requires-python = ">=3.8" -dependencies = [ - "aicsimageio>=4.6.3", +dependencies = [ + "bioio>=3.0.0", "dask[distributed]", # This isn't used directly, but we need to pin this version "fsspec>=2022.8.2", diff --git a/plugin/tests/test_dock_widget.py b/plugin/tests/test_dock_widget.py index 6fde3b6a..d8a058ec 100644 --- a/plugin/tests/test_dock_widget.py +++ b/plugin/tests/test_dock_widget.py @@ -9,7 +9,7 @@ from magicclass._gui._gui_modes import ErrorMode import pytest from lls_core.sample import resources -from aicsimageio.aics_image import AICSImage +from bioio import BioImage from napari_lattice.fields import PixelSizeSource from tempfile import TemporaryDirectory @@ -35,7 +35,7 @@ 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, ) + yield BioImage(image_path, ) def set_debug(cls: MagicTemplate): """ @@ -48,7 +48,7 @@ def _handler(e: Exception, parent: Widget): for child in cls.__magicclass_children__: set_debug(child) -def test_dock_widget(make_napari_viewer: Callable[[], Viewer], image_data: AICSImage): +def test_dock_widget(make_napari_viewer: Callable[[], Viewer], image_data: BioImage): # make viewer and add an image layer using our fixture viewer = make_napari_viewer() From 880ba6dcaaf4a31c652ee63de03e800bb95789b0 Mon Sep 17 00:00:00 2001 From: Pradeep Date: Tue, 2 Dec 2025 17:14:49 +1100 Subject: [PATCH 3/4] fix issues with workflow_path when running tests locally in windows --- core/tests/test_workflows.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/core/tests/test_workflows.py b/core/tests/test_workflows.py index 28b1edb1..4da995f9 100644 --- a/core/tests/test_workflows.py +++ b/core/tests/test_workflows.py @@ -97,9 +97,12 @@ def test_table_workflow(lls7_t1_ch1: Path, table_workflow: Workflow): def test_argument_order(rbc_tiny: Path): # Tests that only the first unfilled argument is passed an array with tempfile.TemporaryDirectory() as tmpdir: + # Get the path relative to this test file + workflow_path = Path(__file__).parent / "workflows" / "argument_order" / "test_workflow.yml" + params = LatticeData( input_image = rbc_tiny, - workflow = "core/tests/workflows/argument_order/test_workflow.yml", + workflow = str(workflow_path), save_dir = tmpdir ) for output in params.process_workflow().process(): @@ -109,9 +112,12 @@ def test_sum_preview(rbc_tiny: Path): import numpy as np # Tests that we can sum the preview result. This is required for the plugin with tempfile.TemporaryDirectory() as tmpdir: + # Get the path relative to this test file + workflow_path = Path(__file__).parent / "workflows" / "binarisation" / "workflow.yml" + params = LatticeData( input_image = rbc_tiny, - workflow = "core/tests/workflows/binarisation/workflow.yml", + workflow = str(workflow_path), save_dir = tmpdir ) previews = list(params.process_workflow().roi_previews()) @@ -122,9 +128,12 @@ def test_crop_workflow(lls7_t1_ch1: Path): # Tests that crop workflows only process each ROI lazily with tempfile.TemporaryDirectory() as tmpdir: + # Get the path relative to this test file + workflow_path = Path(__file__).parent / "workflows" / "binarisation" / "workflow.yml" + params = LatticeData( input_image = lls7_t1_ch1, - workflow = "core/tests/workflows/binarisation/workflow.yml", + workflow = str(workflow_path), save_dir = tmpdir, crop=CropParams( roi_list=[ From 07dcce6b5099c6f7bd1760a56210f99a2c6d259f Mon Sep 17 00:00:00 2001 From: Pradeep Date: Tue, 2 Dec 2025 17:23:08 +1100 Subject: [PATCH 4/4] Fix test to use bioio for pixel size instead of aicsimageio --- core/tests/test_validation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tests/test_validation.py b/core/tests/test_validation.py index d4cb50cb..b0e455a9 100644 --- a/core/tests/test_validation.py +++ b/core/tests/test_validation.py @@ -48,7 +48,7 @@ def test_allow_trailing_slash(): def test_infer_czi_pixel_sizes(rbc_tiny: Path): mock = PropertyMock() - with patch("aicsimageio.AICSImage.physical_pixel_sizes", new=mock): + with patch("bioio.BioImage.physical_pixel_sizes", new=mock): DeskewParams(input_image=rbc_tiny) - # The AICSImage should be queried for the pixel sizes + # The BioImage should be queried for the pixel sizes assert mock.called