Skip to content

Commit

Permalink
Simplified slicing in source classes
Browse files Browse the repository at this point in the history
Bug fixes in image resizing and dimension orders
Pinned some versions in conda envs
  • Loading branch information
folterj committed Feb 12, 2024
1 parent 013c87f commit e52085f
Show file tree
Hide file tree
Showing 14 changed files with 101 additions and 118 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#### Version 0.6.8
- Simplified slicing in source classes
- Bug fixes in image resizing and dimension orders
- Pinned some versions in conda envs

#### Version 0.6.7
- Fixed bugs in asdask() and GeneratorSource
- Pinned some versions in pip requirements
Expand Down
25 changes: 13 additions & 12 deletions OmeSliCC/BioSource.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from OmeSliCC import XmlDict
from OmeSliCC.OmeSource import OmeSource
from OmeSliCC.image_util import redimension_data


class BioSource(OmeSource):
Expand Down Expand Up @@ -52,28 +53,28 @@ def __init__(self,
target_pixel_size=target_pixel_size,
source_info_required=source_info_required)

self.dimension_order = 'yx'
if self.get_nchannels() > 1:
self.dimension_order += 'c'

def _find_metadata(self):
self._get_ome_metadate()

def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1,
c: int = None, z: int = None, t: int = None) -> np.ndarray:
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
if x1 < 0 or y1 < 0:
x1, y1 = self.sizes[level]
if t is None:
t = 0
if z is None:
z = 0
xywh = (x0, y0, x1 - x0, y1 - y0)
image = self.reader.read(series=self.indexes[level], XYWH=xywh, rescale=False, # don't 'rescale' to 0-1!
c=c, z=z, t=t)
ndim0 = image.ndim
image = np.expand_dims(image, 0)
if ndim0 == 2:
image = np.expand_dims(image, 0)
else:
image = np.moveaxis(image, -1, 0)
image = np.expand_dims(image, 0)
return image
# don't 'rescale' to 0-1!
image = self.reader.read(series=self.indexes[level], XYWH=xywh, c=c, z=z, t=t, rescale=False)
out = redimension_data(image, self.dimension_order, self.get_dimension_order())
return out

def close(self):
self.reader.close()
Expand Down
25 changes: 12 additions & 13 deletions OmeSliCC/OmeSource.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,11 @@ def render(self, image: np.ndarray, t: int = 0, z: int = 0, channels: list = [])

return new_image

def asarray(self, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1,
c: int = None, z: int = None, t: int = None,
pixel_size: list = []) -> np.ndarray:
def asarray(self, pixel_size: list = [], **slicing) -> np.ndarray:
# expects x0, x1, y0, y1, ...
x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
# allow custom pixel size
if pixel_size:
level, factor, _ = get_best_level_factor(self, pixel_size)
Expand All @@ -329,21 +331,19 @@ def asarray(self, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1,
ox1, oy1 = np.round(np.divide([x1, y1], factor)).astype(int)
else:
ox0, oy0, ox1, oy1 = x0, y0, x1, y1
image0 = self._asarray_level(level, ox0, oy0, ox1, oy1, c, z, t)
image0 = self._asarray_level(level, x0=ox0, y0=oy0, x1=ox1, y1=oy1, c=c, z=z, t=t)
if np.mean(factor) != 1:
size1 = x1 - x0, y1 - y0
image = image_resize(image0, size1, dimension_order=self.get_dimension_order())
else:
image = image0
return image

def asarray_um(self, x_um: float, y_um: float, w_um: float, h_um: float,
c: int = None, z: int = None, t: int = None):
def asarray_um(self, **slicing):
pixel_size = self.get_pixel_size_micrometer()[:2]
x0, y0 = np.divide([x_um, y_um], pixel_size)
w, h = np.divide([w_um, h_um], pixel_size)
x1, y1 = x0 + w, y0 + h
return self.asarray(x0, y0, x1, y1, c, z, t)
slicing['x0'], slicing['x1'] = np.divide([slicing.get('x0'), slicing.get('x1')], pixel_size[0])
slicing['y0'], slicing['y1'] = np.divide([slicing.get('y0'), slicing.get('y1')], pixel_size[1])
return self.asarray(**slicing)

def asdask(self, chunk_size: tuple) -> da.Array:
chunk_shape = list(np.flip(chunk_size))
Expand Down Expand Up @@ -398,7 +398,7 @@ def produce_chunks(self, chunk_size: tuple) -> tuple:
x0, y0 = chunkx * chunk_size[0], chunky * chunk_size[1]
x1, y1 = min((chunkx + 1) * chunk_size[0], w), min((chunky + 1) * chunk_size[1], h)
indices = 0, 0, 0, y0, x0
yield indices, self.asarray(x0, y0, x1, y1)
yield indices, self.asarray(x0=x0, x1=x1, y0=y0, y1=y1)

def get_metadata(self) -> dict:
return self.metadata
Expand All @@ -409,8 +409,7 @@ def create_xml_metadata(self, output_filename: str, combine_rgb: bool = True, py
def _find_metadata(self):
raise NotImplementedError('Implement method in subclass')

def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1,
c: int = None, z: int = None, t: int = None) -> np.ndarray:
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
raise NotImplementedError('Implement method in subclass')

def close(self):
Expand Down
32 changes: 5 additions & 27 deletions OmeSliCC/OmeZarrSource.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from OmeSliCC.OmeSource import OmeSource
from OmeSliCC.color_conversion import hexrgb_to_rgba, int_to_rgba
from OmeSliCC.image_util import get_numpy_slicing, redimension_data


class OmeZarrSource(OmeSource):
Expand Down Expand Up @@ -104,32 +105,9 @@ def get_source_dask(self):
def _get_output_dask(self):
return self.levels[0]

def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1,
c: int = None, z: int = None, t: int = None) -> np.ndarray:
if x1 < 0 or y1 < 0:
x1, y1 = self.sizes[level]
xyzct = self.sizes_xyzct[level]
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
image = self.levels[level]
dimension_order = self.dimension_order
if 'z' not in dimension_order:
image = np.expand_dims(image, 0)
if dimension_order.endswith('c'):
image = np.moveaxis(image, -1, 1)
elif 'c' not in dimension_order:
image = np.expand_dims(image, 0)
if 't' not in dimension_order:
image = np.expand_dims(image, 0)
if t is not None:
t0, t1 = t, t + 1
else:
t0, t1 = 0, xyzct[4]
if c is not None:
c0, c1 = c, c + 1
else:
c0, c1 = 0, xyzct[3]
if z is not None:
z0, z1 = z, z + 1
else:
z0, z1 = 0, xyzct[2]
image = image[t0:t1, c0:c1, z0:z1, y0:y1, x0:x1]
return np.asarray(image)
slicing = get_numpy_slicing(dimension_order, **slicing)
out = redimension_data(image[slicing], dimension_order, self.get_dimension_order())
return out
21 changes: 10 additions & 11 deletions OmeSliCC/OmeroSource.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from OmeSliCC import Omero
from OmeSliCC.OmeSource import OmeSource
from OmeSliCC.color_conversion import *
from OmeSliCC.image_util import redimension_data
from OmeSliCC.omero_metadata import create_ome_metadata_from_omero
from OmeSliCC.util import *

Expand Down Expand Up @@ -40,8 +41,6 @@ def __init__(self,
zsize = get_default(image_object.getSizeZ(), 1)
nchannels = np.sum([channel.getLogicalChannel().getSamplesPerPixel() for channel in image_object.getChannels()])
pixel_type = np.dtype(image_object.getPixelsType())
# currently only support/output yxc - allow default value
#self.dimension_order = image_object.getPrimaryPixels().getDimensionOrder().getValue().lower()

self.pixels_store = self.omero.create_pixels_store(image_object)
for resolution in self.pixels_store.getResolutionDescriptions():
Expand Down Expand Up @@ -72,6 +71,9 @@ def __init__(self,
target_pixel_size=target_pixel_size,
source_info_required=source_info_required)

# currently only support/output yxc
self.dimension_order = 'yxc'

def _find_metadata(self):
image_object = self.image_object
self.source_pixel_size = [(get_default(image_object.getPixelSizeX(), 0), 'µm'),
Expand All @@ -98,9 +100,10 @@ def get_thumbnail(self, target_size: tuple, precise: bool = False) -> np.ndarray
image = np.array(PIL.Image.open(image_stream))
return image

def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1,
c: int = None, z: int = None, t: int = None) -> np.ndarray:
xyzct = list(self.sizes_xyzct[level]).copy()
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
x0, x1 = slicing.get('x0', 0), slicing.get('x1', -1)
y0, y1 = slicing.get('y0', 0), slicing.get('y1', -1)
c, t, z = slicing.get('c'), slicing.get('t'), slicing.get('z')
if x1 < 0 or y1 < 0:
x1, y1 = self.sizes[level]
if t is None:
Expand All @@ -110,8 +113,6 @@ def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -

pixels_store = self.pixels_store
w, h = x1 - x0, y1 - y0
xyzct[0] = w
xyzct[1] = h
if c is not None:
channels = [c]
else:
Expand All @@ -124,10 +125,8 @@ def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -
tile = np.frombuffer(tile0, dtype=image.dtype).reshape(h, w)
image[..., c] = tile

image = np.expand_dims(image, 0)
image = np.moveaxis(image, -1, 0)
image = np.expand_dims(image, 0)
return image
out = redimension_data(image, self.dimension_order, self.get_dimension_order())
return out

def close(self):
self.pixels_store.close()
19 changes: 5 additions & 14 deletions OmeSliCC/PlainImageSource.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,7 @@ def unload(self):
self.arrays = []
self.loaded = False

def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1,
c: int = None, z: int = None, t: int = None) -> np.ndarray:
if x1 < 0 or y1 < 0:
x1, y1 = self.sizes[level]

def _asarray_level(self, level: int, **slicing) -> np.ndarray:
nframes = self.image.n_frames
if self.loaded:
image = self.arrays[level]
Expand All @@ -110,12 +106,7 @@ def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -
else:
image = np.array(self.image)

if 'z' not in self.dimension_order:
image = np.expand_dims(image, 0) # add Z
if 'c' in self.dimension_order:
image = np.moveaxis(image, -1, 0) # move C to front
else:
image = np.expand_dims(image, 0) # add C
image = np.expand_dims(image, 0) # add T
image = image[..., y0:y1, x0:x1]
return image
dimension_order = self.dimension_order
slicing = get_numpy_slicing(dimension_order, **slicing)
out = redimension_data(image[slicing], dimension_order, self.get_dimension_order())
return out
22 changes: 6 additions & 16 deletions OmeSliCC/TiffSource.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,21 +203,11 @@ def clear_decompress(self):
del array
self.arrays = []

def _asarray_level(self, level: int, x0: float = 0, y0: float = 0, x1: float = -1, y1: float = -1,
c: int = None, z: int = None, t: int = None) -> np.ndarray:
def _asarray_level(self, level: int, **slicing) -> np.ndarray:
if self.decompressed:
array = self.arrays[level]
return array[y0:y1, x0:x1]

data = da.from_zarr(self.tiff.aszarr(level=level))
x0, x1, y0, y1 = np.round([x0, x1, y0, y1]).astype(int)
slices = []
for axis in self.dimension_order:
if axis == 'x':
slices.append(slice(x0, x1))
elif axis == 'y':
slices.append(slice(y0, y1))
else:
slices.append(slice(None))
out = redimension_data(data[tuple(slices)], self.dimension_order, self.get_dimension_order())
data = self.arrays[level]
else:
data = da.from_zarr(self.tiff.aszarr(level=level))
slices = get_numpy_slicing(self.dimension_order, **slicing)
out = redimension_data(data[slices], self.dimension_order, self.get_dimension_order())
return out
14 changes: 6 additions & 8 deletions OmeSliCC/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,6 @@ def save_image_as_tiff(source: OmeSource, image: np.ndarray, output_filename: st
compression = output_params.get('compression')
combine_rgb = output_params.get('combine_rgb', True)

# move color channels to end:
image, dimension_order = source.get_yxc_image(image)

npyramid_add = output_params.get('npyramid_add', 0)
pyramid_downsample = output_params.get('pyramid_downsample')
if npyramid_add > 0:
Expand All @@ -225,7 +222,7 @@ def save_image_as_tiff(source: OmeSource, image: np.ndarray, output_filename: st
resolution, resolution_unit = get_resolution_from_pixel_size(source.get_pixel_size())

save_tiff(output_filename, image, metadata=metadata, xml_metadata=xml_metadata,
dimension_order=dimension_order,
dimension_order=source.get_dimension_order(),
resolution=resolution, resolution_unit=resolution_unit, tile_size=tile_size,
compression=compression, combine_rgb=combine_rgb, pyramid_sizes_add=pyramid_sizes_add)

Expand All @@ -241,8 +238,8 @@ def save_tiff(filename: str, image: np.ndarray, metadata: dict = None, xml_metad
nchannels = 1
if 'c' in dimension_order:
c_index = dimension_order.index('c')
if c_index < image.ndim:
nchannels = image.shape[c_index]
nchannels = image.shape[c_index]

image = ensure_unsigned_image(image)

if tile_size is not None and isinstance(tile_size, int):
Expand All @@ -251,10 +248,11 @@ def save_tiff(filename: str, image: np.ndarray, metadata: dict = None, xml_metad
split_channels = not (combine_rgb and nchannels == 3)
if nchannels == 3 and not split_channels:
photometric = PHOTOMETRIC.RGB
c_index = dimension_order.index('c')
image = np.moveaxis(image, c_index, -1)
dimension_order = dimension_order.replace('c', '') + 'c'
else:
photometric = PHOTOMETRIC.MINISBLACK
image = np.moveaxis(image, -1, 0)
dimension_order = 'cyx'

if resolution is not None:
# tifffile only supports x/y pyramid resolution
Expand Down
Loading

0 comments on commit e52085f

Please sign in to comment.