-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/dask poc #192
Feature/dask poc #192
Changes from 46 commits
4cbe166
b24e385
bcb9e72
c144548
c721cfb
2546373
cada2e7
dda7767
366062e
d09e8ff
d9cc16c
336218e
443c565
3edad53
dc89c44
4209f89
0d5a795
a18c33c
09cb6bc
f93b179
02bbb2f
6de1732
cb1a4c7
00f8b41
489d5bd
cb39a08
f17861f
8f44127
f358779
ccd299f
611a0a6
3fde9e5
167404a
61a7440
8347659
56c807c
e0a07a1
3eb6616
b3aa401
9d18ea5
62f641d
79f31f4
a913166
1b5ce35
b7a0eb3
19831b6
ed938d1
607b85c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
from typing import Any, Tuple | ||
|
||
import numpy as np | ||
import skimage.transform | ||
from dask import array as da | ||
|
||
# This module contributed by Andreas Eisenbarth @aeisenbarth | ||
# See https://github.com/toloudis/ome-zarr-py/pull/1 | ||
|
||
|
||
def resize( | ||
image: da.Array, output_shape: Tuple[int, ...], *args: Any, **kwargs: Any | ||
) -> da.Array: | ||
r""" | ||
Wrapped copy of "skimage.transform.resize" | ||
Resize image to match a certain size. | ||
:type image: :class:`dask.array` | ||
:param image: The dask array to resize | ||
:type output_shape: tuple | ||
:param output_shape: The shape of the resize array | ||
:type \*args: list | ||
:param \*args: Arguments of skimage.transform.resize | ||
:type \*\*kwargs: dict | ||
:param \*\*kwargs: Keyword arguments of skimage.transform.resize | ||
:return: Resized image. | ||
""" | ||
factors = np.array(output_shape) / np.array(image.shape).astype(float) | ||
# Rechunk the input blocks so that the factors achieve an output | ||
# blocks size of full numbers. | ||
better_chunksize = tuple( | ||
np.maximum(1, np.round(np.array(image.chunksize) * factors) / factors).astype( | ||
int | ||
) | ||
) | ||
image_prepared = image.rechunk(better_chunksize) | ||
block_output_shape = tuple( | ||
np.floor(np.array(better_chunksize) * factors).astype(int) | ||
) | ||
|
||
# Map overlap | ||
def resize_block(image_block: da.Array, block_info: dict) -> da.Array: | ||
return skimage.transform.resize( | ||
image_block, block_output_shape, *args, **kwargs | ||
).astype(image_block.dtype) | ||
|
||
output_slices = tuple(slice(0, d) for d in output_shape) | ||
output = da.map_blocks( | ||
resize_block, image_prepared, dtype=image.dtype, chunks=block_output_shape | ||
)[output_slices] | ||
return output.rechunk(image.chunksize).astype(image.dtype) | ||
|
||
|
||
def downscale_nearest(image: da.Array, factors: Tuple[int, ...]) -> da.Array: | ||
""" | ||
Primitive downscaling by integer factors using stepped slicing. | ||
:type image: :class:`dask.array` | ||
:param image: The dask array to resize | ||
:type factors: tuple | ||
:param factors: Sequence of integers factors for each dimension. | ||
:return: Resized image. | ||
""" | ||
if not len(factors) == image.ndim: | ||
raise ValueError( | ||
f"Dimension mismatch: {image.ndim} image dimensions, " | ||
f"{len(factors)} scale factors" | ||
) | ||
if not ( | ||
all(isinstance(f, int) and 0 < f <= d for f, d in zip(factors, image.shape)) | ||
): | ||
raise ValueError( | ||
f"All scale factors must not be greater than the dimension length: " | ||
f"({tuple(factors)}) <= ({tuple(image.shape)})" | ||
) | ||
slices = tuple(slice(None, None, factor) for factor in factors) | ||
return image[slices] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,8 +7,9 @@ | |
import os | ||
from collections.abc import MutableMapping | ||
from dataclasses import dataclass | ||
from typing import Callable, Iterator, List | ||
from typing import Any, Callable, Iterator, List, Tuple, Union | ||
|
||
import dask.array as da | ||
import numpy as np | ||
import zarr | ||
from scipy.ndimage import zoom | ||
|
@@ -19,10 +20,14 @@ | |
resize, | ||
) | ||
|
||
from .dask_utils import resize as dask_resize | ||
from .io import parse_url | ||
|
||
LOGGER = logging.getLogger("ome_zarr.scale") | ||
|
||
ListOfArrayLike = Union[List[da.Array], List[np.ndarray]] | ||
ArrayLike = Union[da.Array, np.ndarray] | ||
|
||
|
||
@dataclass | ||
class Scaler: | ||
|
@@ -125,6 +130,30 @@ def __create_group( | |
series.append({"path": path}) | ||
return grp | ||
|
||
def resize_image(self, image: ArrayLike) -> ArrayLike: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is different from all the others on When a scaler instance is passed in to The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess that none of these issues is a blocker - Improving the Probably the most important fix should be handling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would agree that dask versions of gaussian, laplacian, etc could be done in later PRs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Had a quick look and I am already handling |
||
""" | ||
Resize a numpy array OR a dask array to a smaller array (not pyramid) | ||
""" | ||
if isinstance(image, da.Array): | ||
|
||
def _resize(image: ArrayLike, out_shape: Tuple, **kwargs: Any) -> ArrayLike: | ||
return dask_resize(image, out_shape, **kwargs) | ||
|
||
else: | ||
_resize = resize | ||
|
||
# only down-sample in X and Y dimensions for now... | ||
new_shape = list(image.shape) | ||
new_shape[-1] = np.ceil(float(image.shape[-1]) / self.downscale) | ||
new_shape[-2] = np.ceil(float(image.shape[-2]) / self.downscale) | ||
out_shape = tuple(new_shape) | ||
|
||
dtype = image.dtype | ||
image = _resize( | ||
image.astype(float), out_shape, order=1, mode="reflect", anti_aliasing=False | ||
) | ||
return image.astype(dtype) | ||
|
||
def nearest(self, base: np.ndarray) -> List[np.ndarray]: | ||
""" | ||
Downsample using :func:`skimage.transform.resize`. | ||
|
@@ -133,9 +162,21 @@ def nearest(self, base: np.ndarray) -> List[np.ndarray]: | |
""" | ||
return self._by_plane(base, self.__nearest) | ||
|
||
def __nearest(self, plane: np.ndarray, sizeY: int, sizeX: int) -> np.ndarray: | ||
def __nearest( | ||
self, plane: Union[np.ndarray, da.Array], sizeY: int, sizeX: int | ||
sbesson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) -> np.ndarray: | ||
"""Apply the 2-dimensional transformation.""" | ||
return resize( | ||
if isinstance(plane, da.Array): | ||
|
||
def _resize( | ||
image: ArrayLike, output_shape: Tuple, **kwargs: Any | ||
) -> ArrayLike: | ||
return dask_resize(image, output_shape, **kwargs) | ||
|
||
else: | ||
_resize = resize | ||
|
||
return _resize( | ||
plane, | ||
output_shape=(sizeY // self.downscale, sizeX // self.downscale), | ||
order=0, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps in the future these could be some kind of general Array (once that is more fleshed out).