Skip to content
Open
2 changes: 1 addition & 1 deletion .github/lint/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ runs:
using: "composite"
steps:
- run: |
pip install fawltydeps==0.12.0
pip install fawltydeps
fawltydeps --check --detailed --verbose
shell: bash -l {0}
working-directory: ${{ inputs.directory }}
Expand Down
16 changes: 7 additions & 9 deletions core/lls_core/deconvolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
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
import bioio_czi
from skimage.io import imread
from aicspylibczi import CziFile
from numpy.typing import NDArray
import os
import numpy as np
Expand Down Expand Up @@ -62,12 +62,10 @@ def read_psf(psf_paths: Collection[Path],
for psf in psf_paths:
if psf.exists() and psf.is_file():
if psf.suffix == ".czi":
psf_czi = CziFile(psf.__str__())
psf_aics = psf_czi.read_image()
psf_czi = BioImage(psf.__str__(), reader=bioio_czi.Reader)
psf_aics = psf_czi.data
# make sure shape is 3D
psf_aics = psf_aics[0][0] # np.expand_dims(psf_aics[0],axis=0)
# if len(psf_aics[0])>=1:
#psf_channels = len(psf_aics[0])
psf_aics = psf_aics[0][0]
assert len(
psf_aics.shape) == 3, f"PSF should be a 3D image (shape of 3), but got {psf_aics.shape}"
# pad psf to multiple of 16 for decon
Expand All @@ -79,8 +77,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)
Expand Down
16 changes: 8 additions & 8 deletions core/lls_core/models/deskew.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import numpy as np

if TYPE_CHECKING:
from aicsimageio.types import PhysicalPixelSizes
from bioio import PhysicalPixelSizes

class DefinedPixelSizes(FieldAccessModel):
"""
Expand Down Expand Up @@ -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:
Expand All @@ -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"}:
Expand All @@ -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
Expand Down
34 changes: 17 additions & 17 deletions core/lls_core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
3 changes: 2 additions & 1 deletion core/lls_core/writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ def flush(self):
str(path),
data = images_array,
bigtiff=True,
resolution=(1./self.lattice.dx, 1./self.lattice.dy, "MICROMETER"),
resolution=(1./self.lattice.dx, 1./self.lattice.dy),
resolutionunit="MICROMETER",
metadata={'spacing': self.lattice.new_dz, 'unit': 'um', 'axes': 'TZCYX'},
imagej=True
)
Expand Down
16 changes: 11 additions & 5 deletions core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ classifiers = [
]
requires-python = ">=3.8"
dependencies = [
"aicsimageio>=4.6.3",
# 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",
"bioio>=3.0.0",
"bioio-bioformats>=1.3.0",
"bioio-czi>=2.4.0",
"bioio-ome-tiff>=1.4.0",
"bioio-tifffile==1.3.0",
"click",
"dask",
"dask[distributed]",
Expand Down Expand Up @@ -158,7 +159,12 @@ ignore_unused = [

# Used for the deployment, but never imported
"build",
"twine"
"twine",

#bioio packages are needed but not directly imported
"bioio-bioformats",
"bioio-czi",
"bioio-ome-tiff"
]
output_format = "human_detailed"

Expand Down
14 changes: 8 additions & 6 deletions core/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -14,8 +14,9 @@ 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)
# Save image as a tif file in home directory
b = BioImage(raw, physical_pixel_sizes={ax: 1. for ax in 'ZYX'})
b.save(path)
assert path.exists()


Expand All @@ -27,8 +28,9 @@ def create_data(dir: 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(input_file)
# Save image as a tif file in home directory
b = BioImage(raw, physical_pixel_sizes={ax: 1. for ax in 'ZYX'})
b.save(input_file)
assert input_file.exists()

config: dict[str, str] = {
Expand All @@ -47,7 +49,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"""
Expand Down
42 changes: 26 additions & 16 deletions core/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from pathlib import Path
from napari_workflows import Workflow
from pytest import FixtureRequest
from bioio_tifffile import reader as tiffreader
from bioio import BioImage


from .params import parameterized
Expand Down Expand Up @@ -51,22 +53,30 @@ def test_save(minimal_image_path: str, args: dict):


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
with tempfile.TemporaryDirectory() as tempdir:
for slice in (
LatticeData.parse_obj(
{
# use BioImage rather than just Path to ensure tifffile
# is used instead of bioformats, which prevents dask use
# due to a bug:
# https://github.com/bioio-devs/bioio-bioformats/issues/40
"input_image": BioImage(
root / "raw.tif", reader=tiffreader.Reader
),
"deconvolution": {
"psf": [root / "psf.tif"],
},
"crop": CropParams(
roi_list=[[[0, 0], [0, 110], [95, 0], [95, 110]]]
),
"save_dir": tempdir,
}
)
.process()
.slices
):
assert slice.data.ndim == 3


def test_process_time_range(multi_channel_time: Path):
Expand Down
4 changes: 2 additions & 2 deletions core/tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions core/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
2 changes: 1 addition & 1 deletion plugin/napari_lattice/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 7 additions & 7 deletions plugin/napari_lattice/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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

Expand Down
14 changes: 11 additions & 3 deletions plugin/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ classifiers = [
]
requires-python = ">=3.8"
dependencies = [
"aicsimageio>=4.6.3",
"bioio>=3.0.0",
"bioio-bioformats>=1.3.0",
"bioio-czi>=2.4.0",
"bioio-ome-tiff>=1.4.0",
"bioio-tifffile==1.3.0",
"dask[distributed]",
# This isn't used directly, but we need to pin this version
"fsspec>=2022.8.2",
Expand All @@ -45,7 +49,6 @@ dependencies = [
# The lower bound is because we need this Python 3.8 fix: https://github.com/hanjinliu/magic-class/pull/108
"magic-class>=0.7.5",
"magicgui<0.8.0",
"napari-aicsimageio>=0.7.2",
"napari-workflow-inspector",
"napari-workflows>=0.2.8",
"napari[all]>=0.4.11",
Expand Down Expand Up @@ -99,7 +102,12 @@ ignore_unused = [
# This is pinned but unused
"fsspec",
"imageio",
"ome-types"
"ome-types",

#bioio packages are needed but not directly imported
"bioio-bioformats",
"bioio-czi",
"bioio-ome-tiff"
]
output_format = "human_detailed"

Expand Down
Loading
Loading