Skip to content
Snippets Groups Projects
Commit 0a0ff940 authored by Alán Muñoz's avatar Alán Muñoz
Browse files

refactor(ImageDir): add support for omero exports

parent 59ddddbc
No related branches found
No related tags found
No related merge requests found
...@@ -50,7 +50,7 @@ def get_image_class(source: t.Union[str, int, t.Dict[str, str], PosixPath]): ...@@ -50,7 +50,7 @@ def get_image_class(source: t.Union[str, int, t.Dict[str, str], PosixPath]):
class ImageLocal: class ImageLocal:
def __init__(self, path: str, dimorder=None): def __init__(self, path: str, dimorder=None):
self.path = path self.path = path
self.image_id = str(path) self._id = str(path)
meta = dict() meta = dict()
try: try:
...@@ -125,7 +125,7 @@ class ImageLocal: ...@@ -125,7 +125,7 @@ class ImageLocal:
return self._meta return self._meta
def get_data_lazy_local(self) -> da.Array: def get_data_lazy_local(self) -> da.Array:
"""Return 5D dask array. For lazy-loading local multidimensional tiff files""" """Return 5D dask array. For lazy-loading multidimensional tiff files"""
if not hasattr(self, "formatted_img"): if not hasattr(self, "formatted_img"):
if not hasattr(self, "ids"): # Standard dimension order if not hasattr(self, "ids"): # Standard dimension order
...@@ -161,45 +161,69 @@ class ImageLocal: ...@@ -161,45 +161,69 @@ class ImageLocal:
return self._formatted_img return self._formatted_img
class ImageDirectory(ImageLocal): class ImageDir(ImageLocal):
""" """
Image class for case where all images are split in one or multiple folders with time-points and channels as independent files. Image class for case where all images are split in one or multiple folders with time-points and channels as independent files.
It inherits from Imagelocal so we only override methods that are critical. It inherits from Imagelocal so we only override methods that are critical.
Assumptions: Assumptions:
- Assumes individual folders for individual channels. If only one path provided it assumes it to be brightfield. - One folders per position.
- Assumes that images are flat. - Images are flat.
- Channel, Time, z-stack and the others are determined by filenames.
- Provides Dimorder as TCZYX - Provides Dimorder as TCZYX
""" """
def __init__(self, path: t.Union[str, t.Dict[str, str]]): def __init__(self, path: t.Union[str, PosixPath]):
if isinstance(path, str):
path = {"Brightfield": path}
self.path = path # Assume they are naturally sorted
self.image_id = str(path) self.path = Path(path)
self._meta = dict(channels=path.keys(), name=list(path.values())[0]) self.image_id = str(self.path.stem)
# Parse name if necessary filenames = list(self.path.glob("*.tiff"))
# Build lazy-loading array using dask?
def get_data_lazy_local(self) -> da.Array: # Deduct order from filenames
"""Return 5D dask array. For lazy-loading local multidimensional tiff files""" self.dimorder = "".join(
map(lambda x: x[0], filenames[0].stem.split("_")[1:])
)
img = da.stack([imread(v) for v in self.path.values()]) dim_value = list(
if ( map(
img.ndim < 5 lambda f: filename_to_dict_indices(f.stem),
): # Files do not include z-stack: Add and swap with time dimension. self.path.glob("*.tiff"),
img = da.stack((img,)).swapaxes(0, 2) )
)
maxes = [
max(map(lambda x: x[dim], dim_value)) for dim in self.dimorder
]
mins = [min(map(lambda x: x[dim], dim_value)) for dim in self.dimorder]
self._dim_shapes = [
max_val - min_val + 1 for max_val, min_val in zip(maxes, mins)
]
# TODO check whether x and y swap is necessary # Set internal metadata from shape and channel order
self.dimorder += "xy"
# Use images to redefine axes # Use images to redefine axes
for i, dim in enumerate(("t", "c", "z", "y", "x")):
self._meta["size_" + dim] = img.shape[i] self._meta = {
"size_" + dim: shape
for dim, shape in zip(self.dimorder, self._dim_shapes)
}
def get_data_lazy(self) -> da.Array:
"""Return 5D dask array. For lazy-loading local multidimensional tiff files"""
img = imread(str(self.path / "*.tiff"))
# If extra channels, pick the first stack of the last dimensions
while len(img.shape) > 3:
img = img[..., 0]
self._meta["size_x"], self._meta["size_y"] = img.shape[-2:]
reshaped = da.reshape(img, (*self._dim_shapes, *img.shape[1:]))
self._formatted_img = da.rechunk( self._formatted_img = da.rechunk(
img, reshaped,
chunks=(1, 1, 1, self._meta["size_y"], self._meta["size_x"]), chunks=(1, 1, 1, self._meta["size_y"], self._meta["size_x"]),
) )
return self._formatted_img return self._formatted_img
...@@ -338,3 +362,10 @@ def get_data_lazy(image) -> da.Array: ...@@ -338,3 +362,10 @@ def get_data_lazy(image) -> da.Array:
t_stacks.append(da.stack(c_stacks)) t_stacks.append(da.stack(c_stacks))
return da.stack(t_stacks) return da.stack(t_stacks)
def filename_to_dict_indices(stem: str):
return {
dim_number[0]: int(dim_number[1:])
for dim_number in stem.split("_")[1:]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment