- add doc strings
This commit is contained in:
+366
-99
@@ -47,9 +47,15 @@ class Imread:
|
|||||||
TODO: argmax, argmin, nanmax, nanmin, nanmean, nansum, nanstd, nanvar, std, var
|
TODO: argmax, argmin, nanmax, nanmin, nanmean, nansum, nanstd, nanvar, std, var
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def reader_name(self) -> builtins.str: ...
|
def reader_name(self) -> builtins.str:
|
||||||
|
r"""
|
||||||
|
the name of the reader used to open the file
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def transform(self) -> None: ...
|
def transform(self) -> None:
|
||||||
|
r"""
|
||||||
|
get the transformation matrix (not yet implemented)
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def path(self) -> pathlib.Path:
|
def path(self) -> pathlib.Path:
|
||||||
r"""
|
r"""
|
||||||
@@ -71,7 +77,10 @@ class Imread:
|
|||||||
the shape of the view
|
the shape of the view
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def slice(self) -> builtins.list[builtins.str]: ...
|
def slice(self) -> builtins.list[builtins.str]:
|
||||||
|
r"""
|
||||||
|
the current slice applied to the view
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def size(self) -> builtins.int:
|
def size(self) -> builtins.int:
|
||||||
r"""
|
r"""
|
||||||
@@ -83,25 +92,40 @@ class Imread:
|
|||||||
the number of dimensions in the view
|
the number of dimensions in the view
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def T(self) -> Imread: ...
|
def T(self) -> Imread:
|
||||||
|
r"""
|
||||||
|
transposed view (alias for transpose(None))
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def dtype(self) -> numpy.dtype: ...
|
def dtype(self) -> numpy.dtype:
|
||||||
|
r"""
|
||||||
|
the numpy dtype of the view
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def z_stack(self) -> builtins.bool: ...
|
def z_stack(self) -> builtins.bool:
|
||||||
|
r"""
|
||||||
|
whether the view contains a z-stack (more than one z slice)
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def zstack(self) -> builtins.bool:
|
def zstack(self) -> builtins.bool:
|
||||||
r"""
|
r"""
|
||||||
backwards compatibility
|
backwards compatibility
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def time_series(self) -> builtins.bool: ...
|
def time_series(self) -> builtins.bool:
|
||||||
|
r"""
|
||||||
|
whether the view contains a time series (more than one time point)
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def timeseries(self) -> builtins.bool:
|
def timeseries(self) -> builtins.bool:
|
||||||
r"""
|
r"""
|
||||||
backwards compatibility
|
backwards compatibility
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def pixel_size(self) -> typing.Optional[builtins.float]: ...
|
def pixel_size(self) -> typing.Optional[builtins.float]:
|
||||||
|
r"""
|
||||||
|
the pixel size in micrometers
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def pxsize_um(self) -> typing.Optional[builtins.float]:
|
def pxsize_um(self) -> typing.Optional[builtins.float]:
|
||||||
r"""
|
r"""
|
||||||
@@ -113,9 +137,15 @@ class Imread:
|
|||||||
backwards compatibility
|
backwards compatibility
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def delta_z(self) -> typing.Optional[builtins.float]: ...
|
def delta_z(self) -> typing.Optional[builtins.float]:
|
||||||
|
r"""
|
||||||
|
the z-step size in micrometers
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def time_interval(self) -> typing.Optional[builtins.float]: ...
|
def time_interval(self) -> typing.Optional[builtins.float]:
|
||||||
|
r"""
|
||||||
|
the time interval between frames in seconds
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def timeinterval(self) -> typing.Optional[builtins.float]:
|
def timeinterval(self) -> typing.Optional[builtins.float]:
|
||||||
r"""
|
r"""
|
||||||
@@ -127,15 +157,24 @@ class Imread:
|
|||||||
backwards compatibility
|
backwards compatibility
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def objective_name(self) -> typing.Optional[builtins.str]: ...
|
def objective_name(self) -> typing.Optional[builtins.str]:
|
||||||
|
r"""
|
||||||
|
the name of the objective
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def magnification(self) -> typing.Optional[builtins.float]: ...
|
def magnification(self) -> typing.Optional[builtins.float]:
|
||||||
|
r"""
|
||||||
|
the total magnification (objective × tube lens)
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def tube_lens_name(self) -> typing.Optional[builtins.str]: ...
|
def tube_lens_name(self) -> typing.Optional[builtins.str]:
|
||||||
|
r"""
|
||||||
|
the name of the tube lens
|
||||||
|
"""
|
||||||
def __new__(
|
def __new__(
|
||||||
cls,
|
cls,
|
||||||
path: str | pathlib.Path | Imread | bytes,
|
path: str | pathlib.Path | Imread | bytes,
|
||||||
dtype: numpy.typing.DTypeLike = None,
|
dtype: typing.Optional[numpy.typing.DTypeLike] = None,
|
||||||
axes: builtins.str = "cztyx",
|
axes: builtins.str = "cztyx",
|
||||||
reader: typing.Optional[builtins.str] = None,
|
reader: typing.Optional[builtins.str] = None,
|
||||||
) -> Imread:
|
) -> Imread:
|
||||||
@@ -145,20 +184,29 @@ class Imread:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_positions(
|
def get_positions(
|
||||||
path: str | pathlib.Path | Imread | bytes,
|
path: str | pathlib.Path | Imread | bytes,
|
||||||
) -> builtins.set[builtins.int]: ...
|
) -> builtins.set[builtins.int]:
|
||||||
|
r"""
|
||||||
|
get all available positions (series) in the file
|
||||||
|
"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def kill_vm() -> None:
|
def kill_vm() -> None:
|
||||||
r"""
|
r"""
|
||||||
only remains for backwards compatibility
|
only remains for backwards compatibility
|
||||||
"""
|
"""
|
||||||
def reshape(self, order: builtins.str, copy: builtins.bool) -> typing.Any: ...
|
def reshape(self, order: builtins.str, copy: builtins.bool) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
reshape the view with a new axis order
|
||||||
|
"""
|
||||||
def with_transform(
|
def with_transform(
|
||||||
self,
|
self,
|
||||||
channels: builtins.bool = True,
|
channels: builtins.bool = True,
|
||||||
drift: builtins.bool = False,
|
drift: builtins.bool = False,
|
||||||
file: typing.Optional[typing.Any] = None,
|
file: typing.Optional[typing.Any] = None,
|
||||||
bead_files: typing.Optional[typing.Any] = None,
|
bead_files: typing.Optional[typing.Any] = None,
|
||||||
) -> Imread: ...
|
) -> Imread:
|
||||||
|
r"""
|
||||||
|
return a new view with transformations applied (channel alignment, drift correction)
|
||||||
|
"""
|
||||||
def squeeze(self) -> numpy.ndarray | int | float: ...
|
def squeeze(self) -> numpy.ndarray | int | float: ...
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
r"""
|
r"""
|
||||||
@@ -178,69 +226,219 @@ class Imread:
|
|||||||
"""
|
"""
|
||||||
def __array__(
|
def __array__(
|
||||||
self,
|
self,
|
||||||
dtype: numpy.typing.DTypeLike = None,
|
dtype: typing.Optional[numpy.typing.DTypeLike] = None,
|
||||||
copy: typing.Optional[builtins.bool] = None,
|
copy: typing.Optional[builtins.bool] = None,
|
||||||
) -> typing.Any: ...
|
) -> typing.Any:
|
||||||
def __contains__(self, _item: typing.Any) -> builtins.bool: ...
|
r"""
|
||||||
def __lt__(self, other: typing.Any) -> typing.Any: ...
|
convert to a numpy array, optionally with a different dtype
|
||||||
def __le__(self, other: typing.Any) -> typing.Any: ...
|
"""
|
||||||
def __eq__(self, other: typing.Any) -> typing.Any: ...
|
def __contains__(self, _item: typing.Any) -> builtins.bool:
|
||||||
def __ne__(self, other: typing.Any) -> typing.Any: ...
|
r"""
|
||||||
def __gt__(self, other: typing.Any) -> typing.Any: ...
|
check if an item is contained in the view (not implemented)
|
||||||
def __ge__(self, other: typing.Any) -> typing.Any: ...
|
"""
|
||||||
def __add__(self, other: typing.Any) -> typing.Any: ...
|
def __lt__(self, other: typing.Any) -> typing.Any:
|
||||||
def __radd__(self, other: typing.Any) -> typing.Any: ...
|
r"""
|
||||||
def __sub__(self, other: typing.Any) -> typing.Any: ...
|
element-wise less than comparison
|
||||||
def __rsub__(self, other: typing.Any) -> typing.Any: ...
|
"""
|
||||||
def __mul__(self, other: typing.Any) -> typing.Any: ...
|
def __le__(self, other: typing.Any) -> typing.Any:
|
||||||
def __rmul__(self, other: typing.Any) -> typing.Any: ...
|
r"""
|
||||||
def __truediv__(self, other: typing.Any) -> typing.Any: ...
|
element-wise less than or equal comparison
|
||||||
def __rtruediv__(self, other: typing.Any) -> typing.Any: ...
|
"""
|
||||||
def __floordiv__(self, other: typing.Any) -> typing.Any: ...
|
def __eq__(self, other: typing.Any) -> typing.Any:
|
||||||
def __rfloordiv__(self, other: typing.Any) -> typing.Any: ...
|
r"""
|
||||||
def __mod__(self, other: typing.Any) -> typing.Any: ...
|
element-wise equality comparison
|
||||||
def __rmod__(self, other: typing.Any) -> typing.Any: ...
|
"""
|
||||||
def __matmul__(self, other: typing.Any) -> typing.Any: ...
|
def __ne__(self, other: typing.Any) -> typing.Any:
|
||||||
def __rmatmul__(self, other: typing.Any) -> typing.Any: ...
|
r"""
|
||||||
def __and__(self, other: typing.Any) -> typing.Any: ...
|
element-wise not equal comparison
|
||||||
def __rand__(self, other: typing.Any) -> typing.Any: ...
|
"""
|
||||||
def __or__(self, other: typing.Any) -> typing.Any: ...
|
def __gt__(self, other: typing.Any) -> typing.Any:
|
||||||
def __ror__(self, other: typing.Any) -> typing.Any: ...
|
r"""
|
||||||
def __xor__(self, other: typing.Any) -> typing.Any: ...
|
element-wise greater than comparison
|
||||||
def __rxor__(self, other: typing.Any) -> typing.Any: ...
|
"""
|
||||||
def __lshift__(self, other: typing.Any) -> typing.Any: ...
|
def __ge__(self, other: typing.Any) -> typing.Any:
|
||||||
def __rlshift__(self, other: typing.Any) -> typing.Any: ...
|
r"""
|
||||||
def __rshift__(self, other: typing.Any) -> typing.Any: ...
|
element-wise greater than or equal comparison
|
||||||
def __rrshift__(self, other: typing.Any) -> typing.Any: ...
|
"""
|
||||||
def __neg__(self) -> typing.Any: ...
|
def __add__(self, other: typing.Any) -> typing.Any:
|
||||||
def __pos__(self) -> typing.Any: ...
|
r"""
|
||||||
def __abs__(self) -> typing.Any: ...
|
element-wise addition
|
||||||
def __invert__(self) -> typing.Any: ...
|
"""
|
||||||
def __enter__(self) -> Imread: ...
|
def __radd__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise addition (reflected)
|
||||||
|
"""
|
||||||
|
def __sub__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise subtraction
|
||||||
|
"""
|
||||||
|
def __rsub__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise subtraction (reflected)
|
||||||
|
"""
|
||||||
|
def __mul__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise multiplication
|
||||||
|
"""
|
||||||
|
def __rmul__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise multiplication (reflected)
|
||||||
|
"""
|
||||||
|
def __truediv__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise true division
|
||||||
|
"""
|
||||||
|
def __rtruediv__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise true division (reflected)
|
||||||
|
"""
|
||||||
|
def __floordiv__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise floor division
|
||||||
|
"""
|
||||||
|
def __rfloordiv__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise floor division (reflected)
|
||||||
|
"""
|
||||||
|
def __mod__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise modulo
|
||||||
|
"""
|
||||||
|
def __rmod__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise modulo (reflected)
|
||||||
|
"""
|
||||||
|
def __matmul__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise matrix multiplication
|
||||||
|
"""
|
||||||
|
def __rmatmul__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise matrix multiplication (reflected)
|
||||||
|
"""
|
||||||
|
def __and__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise bitwise AND
|
||||||
|
"""
|
||||||
|
def __rand__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise bitwise AND (reflected)
|
||||||
|
"""
|
||||||
|
def __or__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise bitwise OR
|
||||||
|
"""
|
||||||
|
def __ror__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise bitwise OR (reflected)
|
||||||
|
"""
|
||||||
|
def __xor__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise bitwise XOR
|
||||||
|
"""
|
||||||
|
def __rxor__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise bitwise XOR (reflected)
|
||||||
|
"""
|
||||||
|
def __lshift__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise left shift
|
||||||
|
"""
|
||||||
|
def __rlshift__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise left shift (reflected)
|
||||||
|
"""
|
||||||
|
def __rshift__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise right shift
|
||||||
|
"""
|
||||||
|
def __rrshift__(self, other: typing.Any) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise right shift (reflected)
|
||||||
|
"""
|
||||||
|
def __neg__(self) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise negation
|
||||||
|
"""
|
||||||
|
def __pos__(self) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise positive
|
||||||
|
"""
|
||||||
|
def __abs__(self) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise absolute value
|
||||||
|
"""
|
||||||
|
def __invert__(self) -> typing.Any:
|
||||||
|
r"""
|
||||||
|
element-wise bitwise inversion
|
||||||
|
"""
|
||||||
|
def __enter__(self) -> Imread:
|
||||||
|
r"""
|
||||||
|
context manager entry
|
||||||
|
"""
|
||||||
def __exit__(
|
def __exit__(
|
||||||
self,
|
self,
|
||||||
exc_type: typing.Optional[typing.Any] = None,
|
exc_type: typing.Optional[typing.Any] = None,
|
||||||
exc_val: typing.Optional[typing.Any] = None,
|
exc_val: typing.Optional[typing.Any] = None,
|
||||||
exc_tb: typing.Optional[typing.Any] = None,
|
exc_tb: typing.Optional[typing.Any] = None,
|
||||||
) -> None: ...
|
) -> None:
|
||||||
def __getnewargs__(self) -> tuple[builtins.list[builtins.int]]: ...
|
r"""
|
||||||
def __copy__(self) -> Imread: ...
|
context manager exit
|
||||||
def __deepcopy__(self) -> Imread: ...
|
"""
|
||||||
def copy(self) -> Imread: ...
|
def __getnewargs__(self) -> tuple[builtins.list[builtins.int]]:
|
||||||
def __iter__(self) -> Imread: ...
|
r"""
|
||||||
def __next__(self) -> typing.Optional[typing.Any]: ...
|
arguments for pickling
|
||||||
def __len__(self) -> builtins.int: ...
|
"""
|
||||||
def __repr__(self) -> builtins.str: ...
|
def __copy__(self) -> Imread:
|
||||||
def __str__(self) -> builtins.str: ...
|
r"""
|
||||||
|
shallow copy of the view
|
||||||
|
"""
|
||||||
|
def __deepcopy__(self) -> Imread:
|
||||||
|
r"""
|
||||||
|
deep copy of the view (same as shallow copy for this type)
|
||||||
|
"""
|
||||||
|
def copy(self) -> Imread:
|
||||||
|
r"""
|
||||||
|
create a copy of the view
|
||||||
|
"""
|
||||||
|
def __iter__(self) -> Imread:
|
||||||
|
r"""
|
||||||
|
iterate over the first axis
|
||||||
|
"""
|
||||||
|
def __next__(self) -> typing.Optional[typing.Any]:
|
||||||
|
r"""
|
||||||
|
get the next item in the iteration
|
||||||
|
"""
|
||||||
|
def __len__(self) -> builtins.int:
|
||||||
|
r"""
|
||||||
|
number of elements in the first axis
|
||||||
|
"""
|
||||||
|
def __repr__(self) -> builtins.str:
|
||||||
|
r"""
|
||||||
|
string representation with a summary of the image
|
||||||
|
"""
|
||||||
|
def __str__(self) -> builtins.str:
|
||||||
|
r"""
|
||||||
|
the file path as a string
|
||||||
|
"""
|
||||||
def get_frame(
|
def get_frame(
|
||||||
self, c: builtins.int, z: builtins.int, t: builtins.int
|
self, c: builtins.int, z: builtins.int, t: builtins.int
|
||||||
) -> typing.Any:
|
) -> typing.Any:
|
||||||
r"""
|
r"""
|
||||||
retrieve a single frame at czt, sliced accordingly
|
retrieve a single frame at czt, sliced accordingly
|
||||||
"""
|
"""
|
||||||
def flatten(self) -> typing.Any: ...
|
def flatten(self) -> typing.Any:
|
||||||
def to_bytes(self) -> builtins.list[builtins.int]: ...
|
r"""
|
||||||
def tobytes(self) -> builtins.list[builtins.int]: ...
|
flatten the view into a 1D numpy array
|
||||||
|
"""
|
||||||
|
def to_bytes(self) -> builtins.list[builtins.int]:
|
||||||
|
r"""
|
||||||
|
convert the view to bytes
|
||||||
|
"""
|
||||||
|
def tobytes(self) -> builtins.list[builtins.int]:
|
||||||
|
r"""
|
||||||
|
convert the view to bytes (alias for to_bytes)
|
||||||
|
"""
|
||||||
def get_ax(self, axis: int | str) -> builtins.int:
|
def get_ax(self, axis: int | str) -> builtins.int:
|
||||||
r"""
|
r"""
|
||||||
find the position of an axis
|
find the position of an axis
|
||||||
@@ -259,18 +457,32 @@ class Imread:
|
|||||||
r"""
|
r"""
|
||||||
collect data into a numpy array
|
collect data into a numpy array
|
||||||
"""
|
"""
|
||||||
def exposure_time(
|
def exposure_time(self, channel: builtins.int) -> typing.Optional[builtins.float]:
|
||||||
self, channel: builtins.int
|
r"""
|
||||||
) -> typing.Optional[builtins.float]: ...
|
the exposure time for a given channel in seconds
|
||||||
def binning(self, channel: builtins.int) -> typing.Optional[builtins.int]: ...
|
"""
|
||||||
|
def binning(self, channel: builtins.int) -> typing.Optional[builtins.int]:
|
||||||
|
r"""
|
||||||
|
the binning for a given channel
|
||||||
|
"""
|
||||||
def laser_wavelengths(
|
def laser_wavelengths(
|
||||||
self, channel: builtins.int
|
self, channel: builtins.int
|
||||||
) -> typing.Optional[builtins.float]: ...
|
) -> typing.Optional[builtins.float]:
|
||||||
def laser_power(self, channel: builtins.int) -> typing.Optional[builtins.float]: ...
|
r"""
|
||||||
def filter_set_name(
|
the laser wavelength for a given channel in nanometers
|
||||||
self, channel: builtins.int
|
"""
|
||||||
) -> typing.Optional[builtins.str]: ...
|
def laser_power(self, channel: builtins.int) -> typing.Optional[builtins.float]:
|
||||||
def gain(self, channel: builtins.int) -> typing.Optional[builtins.float]: ...
|
r"""
|
||||||
|
the laser power for a given channel as a fraction
|
||||||
|
"""
|
||||||
|
def filter_set_name(self, channel: builtins.int) -> typing.Optional[builtins.str]:
|
||||||
|
r"""
|
||||||
|
the name of the filter set for a given channel
|
||||||
|
"""
|
||||||
|
def gain(self, channel: builtins.int) -> typing.Optional[builtins.float]:
|
||||||
|
r"""
|
||||||
|
the detector gain for a given channel
|
||||||
|
"""
|
||||||
def summary(self) -> builtins.str:
|
def summary(self) -> builtins.str:
|
||||||
r"""
|
r"""
|
||||||
gives a helpful summary of the recorded experiment
|
gives a helpful summary of the recorded experiment
|
||||||
@@ -297,7 +509,10 @@ class Imread:
|
|||||||
colors: typing.Optional[typing.Sequence[builtins.str]] = None,
|
colors: typing.Optional[typing.Sequence[builtins.str]] = None,
|
||||||
overwrite: builtins.bool = False,
|
overwrite: builtins.bool = False,
|
||||||
bar: builtins.bool = True,
|
bar: builtins.bool = True,
|
||||||
) -> None: ...
|
) -> None:
|
||||||
|
r"""
|
||||||
|
save the view as a TIFF file
|
||||||
|
"""
|
||||||
def save_as_movie(
|
def save_as_movie(
|
||||||
self,
|
self,
|
||||||
file: builtins.str | os.PathLike | pathlib.Path,
|
file: builtins.str | os.PathLike | pathlib.Path,
|
||||||
@@ -308,7 +523,10 @@ class Imread:
|
|||||||
overwrite: builtins.bool = False,
|
overwrite: builtins.bool = False,
|
||||||
register: builtins.bool = False,
|
register: builtins.bool = False,
|
||||||
no_scaling: builtins.bool = False,
|
no_scaling: builtins.bool = False,
|
||||||
) -> None: ...
|
) -> None:
|
||||||
|
r"""
|
||||||
|
save the view as a movie file (MP4)
|
||||||
|
"""
|
||||||
def set_cache_size(self, size: builtins.int) -> None:
|
def set_cache_size(self, size: builtins.int) -> None:
|
||||||
r"""
|
r"""
|
||||||
backwards compatibility
|
backwards compatibility
|
||||||
@@ -328,7 +546,7 @@ class Imread:
|
|||||||
def max(
|
def max(
|
||||||
self,
|
self,
|
||||||
axis: int | str = None,
|
axis: int | str = None,
|
||||||
dtype: numpy.typing.DTypeLike = None,
|
dtype: typing.Optional[numpy.typing.DTypeLike] = None,
|
||||||
out: typing.Any = None,
|
out: typing.Any = None,
|
||||||
keepdims: bool = False,
|
keepdims: bool = False,
|
||||||
initial: int | float = None,
|
initial: int | float = None,
|
||||||
@@ -340,7 +558,7 @@ class Imread:
|
|||||||
def min(
|
def min(
|
||||||
self,
|
self,
|
||||||
axis: int | str = None,
|
axis: int | str = None,
|
||||||
dtype: numpy.typing.DTypeLike = None,
|
dtype: typing.Optional[numpy.typing.DTypeLike] = None,
|
||||||
out: typing.Any = None,
|
out: typing.Any = None,
|
||||||
keepdims: bool = False,
|
keepdims: bool = False,
|
||||||
initial: int | float = None,
|
initial: int | float = None,
|
||||||
@@ -352,7 +570,7 @@ class Imread:
|
|||||||
def mean(
|
def mean(
|
||||||
self,
|
self,
|
||||||
axis: int | str = None,
|
axis: int | str = None,
|
||||||
dtype: numpy.typing.DTypeLike = None,
|
dtype: typing.Optional[numpy.typing.DTypeLike] = None,
|
||||||
out: typing.Any = None,
|
out: typing.Any = None,
|
||||||
keepdims: bool = False,
|
keepdims: bool = False,
|
||||||
) -> Imread | numpy.typing.NDArray | int | float:
|
) -> Imread | numpy.typing.NDArray | int | float:
|
||||||
@@ -362,7 +580,7 @@ class Imread:
|
|||||||
def sum(
|
def sum(
|
||||||
self,
|
self,
|
||||||
axis: int | str = None,
|
axis: int | str = None,
|
||||||
dtype: numpy.typing.DTypeLike = None,
|
dtype: typing.Optional[numpy.typing.DTypeLike] = None,
|
||||||
out: typing.Any = None,
|
out: typing.Any = None,
|
||||||
keepdims: bool = False,
|
keepdims: bool = False,
|
||||||
initial: int | float = None,
|
initial: int | float = None,
|
||||||
@@ -373,18 +591,39 @@ class Imread:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
class Shape:
|
class Shape:
|
||||||
|
r"""
|
||||||
|
represents the shape of an image with named dimensions (c, z, t, y, x)
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def c(self) -> builtins.int: ...
|
def c(self) -> builtins.int:
|
||||||
|
r"""
|
||||||
|
the number of channels
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def z(self) -> builtins.int: ...
|
def z(self) -> builtins.int:
|
||||||
|
r"""
|
||||||
|
the number of z slices
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def t(self) -> builtins.int: ...
|
def t(self) -> builtins.int:
|
||||||
|
r"""
|
||||||
|
the number of time points
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def y(self) -> builtins.int: ...
|
def y(self) -> builtins.int:
|
||||||
|
r"""
|
||||||
|
the number of pixels along y
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def x(self) -> builtins.int: ...
|
def x(self) -> builtins.int:
|
||||||
|
r"""
|
||||||
|
the number of pixels along x
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def axes(self) -> builtins.str: ...
|
def axes(self) -> builtins.str:
|
||||||
|
r"""
|
||||||
|
the axis order as a string (e.g., "CZTYX")
|
||||||
|
"""
|
||||||
def __new__(
|
def __new__(
|
||||||
cls,
|
cls,
|
||||||
order: builtins.str,
|
order: builtins.str,
|
||||||
@@ -393,9 +632,18 @@ class Shape:
|
|||||||
t: builtins.int = 1,
|
t: builtins.int = 1,
|
||||||
y: builtins.int = 1,
|
y: builtins.int = 1,
|
||||||
x: builtins.int = 1,
|
x: builtins.int = 1,
|
||||||
) -> Shape: ...
|
) -> Shape:
|
||||||
def __str__(self) -> builtins.str: ...
|
r"""
|
||||||
def __repr__(self) -> builtins.str: ...
|
create a new shape with the given dimensions
|
||||||
|
"""
|
||||||
|
def __str__(self) -> builtins.str:
|
||||||
|
r"""
|
||||||
|
string representation
|
||||||
|
"""
|
||||||
|
def __repr__(self) -> builtins.str:
|
||||||
|
r"""
|
||||||
|
detailed representation for debugging
|
||||||
|
"""
|
||||||
def __getnewargs__(
|
def __getnewargs__(
|
||||||
self,
|
self,
|
||||||
) -> tuple[
|
) -> tuple[
|
||||||
@@ -405,12 +653,24 @@ class Shape:
|
|||||||
builtins.int,
|
builtins.int,
|
||||||
builtins.int,
|
builtins.int,
|
||||||
builtins.int,
|
builtins.int,
|
||||||
]: ...
|
]:
|
||||||
|
r"""
|
||||||
|
arguments for pickling
|
||||||
|
"""
|
||||||
def __getitem__(
|
def __getitem__(
|
||||||
self, idx: str | int | None | Ellipsis | slice | list[int] | tuple[int]
|
self, idx: str | int | None | Ellipsis | slice | list[int] | tuple[int]
|
||||||
) -> typing.Optional[int | list[int]]: ...
|
) -> typing.Optional[int | list[int]]:
|
||||||
def __len__(self) -> builtins.int: ...
|
r"""
|
||||||
def to_list(self) -> builtins.list[builtins.int]: ...
|
get dimension size by index or axis name
|
||||||
|
"""
|
||||||
|
def __len__(self) -> builtins.int:
|
||||||
|
r"""
|
||||||
|
number of dimensions in the shape
|
||||||
|
"""
|
||||||
|
def to_list(self) -> builtins.list[builtins.int]:
|
||||||
|
r"""
|
||||||
|
convert shape to a list of dimension sizes in order
|
||||||
|
"""
|
||||||
|
|
||||||
def batch_to_tiff(
|
def batch_to_tiff(
|
||||||
files_in: typing.Sequence[builtins.str | os.PathLike | pathlib.Path],
|
files_in: typing.Sequence[builtins.str | os.PathLike | pathlib.Path],
|
||||||
@@ -422,5 +682,12 @@ def batch_to_tiff(
|
|||||||
overwrite: builtins.bool = False,
|
overwrite: builtins.bool = False,
|
||||||
bar: builtins.bool = True,
|
bar: builtins.bool = True,
|
||||||
message: typing.Optional[builtins.str] = None,
|
message: typing.Optional[builtins.str] = None,
|
||||||
) -> None: ...
|
) -> None:
|
||||||
def main() -> None: ...
|
r"""
|
||||||
|
batch convert multiple image files to TIFF format
|
||||||
|
"""
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
r"""
|
||||||
|
main entry point for the command-line interface
|
||||||
|
"""
|
||||||
|
|||||||
+22
@@ -32,12 +32,18 @@ pub trait Ax {
|
|||||||
)]
|
)]
|
||||||
#[strum(ascii_case_insensitive)]
|
#[strum(ascii_case_insensitive)]
|
||||||
pub enum Axis {
|
pub enum Axis {
|
||||||
|
/// channel
|
||||||
C,
|
C,
|
||||||
|
/// z slice
|
||||||
Z,
|
Z,
|
||||||
|
/// time
|
||||||
T,
|
T,
|
||||||
|
/// y coordinate
|
||||||
Y,
|
Y,
|
||||||
|
/// x coordinate
|
||||||
X,
|
X,
|
||||||
#[strum(serialize = "N")]
|
#[strum(serialize = "N")]
|
||||||
|
/// a new axis created by slicing, can be operated on but has no reader data
|
||||||
New,
|
New,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,10 +120,15 @@ impl Ax for usize {
|
|||||||
Clone, Debug, Serialize, Deserialize, EnumString, AsRefStr, Display, PartialEq, Eq, Hash,
|
Clone, Debug, Serialize, Deserialize, EnumString, AsRefStr, Display, PartialEq, Eq, Hash,
|
||||||
)]
|
)]
|
||||||
#[strum(ascii_case_insensitive)]
|
#[strum(ascii_case_insensitive)]
|
||||||
|
/// an operation to reduce an axis
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
|
/// take the max along the axis
|
||||||
Max,
|
Max,
|
||||||
|
/// take the min along the axis
|
||||||
Min,
|
Min,
|
||||||
|
/// take the sum along the axis
|
||||||
Sum,
|
Sum,
|
||||||
|
/// take the mean along the axis
|
||||||
Mean,
|
Mean,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +240,7 @@ impl IntoIterator for &Slice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the size of a view along the axes it contains
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Shape {
|
pub struct Shape {
|
||||||
/// size c (# channels)
|
/// size c (# channels)
|
||||||
@@ -241,6 +253,7 @@ pub struct Shape {
|
|||||||
pub y: usize,
|
pub y: usize,
|
||||||
/// size x (horizontal)
|
/// size x (horizontal)
|
||||||
pub x: usize,
|
pub x: usize,
|
||||||
|
/// the axes that are present in the view, in order
|
||||||
pub order: Vec<Axis>,
|
pub order: Vec<Axis>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,6 +343,7 @@ impl From<Shape> for HashMap<Axis, usize> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// iterator over the sizes along the axes of a [`Shape`]
|
||||||
pub struct ShapeIter {
|
pub struct ShapeIter {
|
||||||
shape: Shape,
|
shape: Shape,
|
||||||
index: usize,
|
index: usize,
|
||||||
@@ -344,6 +358,7 @@ impl Iterator for ShapeIter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// iterator over the sizes along the axes of a borrowed [`Shape`]
|
||||||
pub struct ShapeIterBorrow<'a> {
|
pub struct ShapeIterBorrow<'a> {
|
||||||
shape: &'a Shape,
|
shape: &'a Shape,
|
||||||
index: usize,
|
index: usize,
|
||||||
@@ -371,6 +386,7 @@ impl IntoIterator for Shape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Shape {
|
impl Shape {
|
||||||
|
/// create an empty shape with all sizes set to 1 and no axes
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
c: 1,
|
c: 1,
|
||||||
@@ -382,6 +398,7 @@ impl Shape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// iterate over the sizes along the axes, in order
|
||||||
pub fn iter(&self) -> ShapeIterBorrow<'_> {
|
pub fn iter(&self) -> ShapeIterBorrow<'_> {
|
||||||
ShapeIterBorrow {
|
ShapeIterBorrow {
|
||||||
shape: self,
|
shape: self,
|
||||||
@@ -389,18 +406,22 @@ impl Shape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the number of axes in the shape
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.order.len()
|
self.order.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether the shape has no axes
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.order.is_empty()
|
self.order.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the sizes along the axes, in order
|
||||||
pub fn to_vec(&self) -> Vec<usize> {
|
pub fn to_vec(&self) -> Vec<usize> {
|
||||||
self.order.iter().map(|axis| self[axis]).collect()
|
self.order.iter().map(|axis| self[axis]).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the sizes along the axes as a map from axis to size
|
||||||
pub fn to_hashmap(&self) -> HashMap<Axis, usize> {
|
pub fn to_hashmap(&self) -> HashMap<Axis, usize> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
for axis in self.order.iter() {
|
for axis in self.order.iter() {
|
||||||
@@ -409,6 +430,7 @@ impl Shape {
|
|||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set the size of an axis
|
||||||
pub fn set_axis(&mut self, axis: &Axis, value: usize) {
|
pub fn set_axis(&mut self, axis: &Axis, value: usize) {
|
||||||
match axis {
|
match axis {
|
||||||
Axis::C => self.c = value,
|
Axis::C => self.c = value,
|
||||||
|
|||||||
+322
@@ -0,0 +1,322 @@
|
|||||||
|
use crate::axes::{Axis, Operation};
|
||||||
|
use indexmap::{Equivalent, IndexMap};
|
||||||
|
use ndarray::{ArrayD, SliceInfoElem};
|
||||||
|
use std::any::Any;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::{Arc, Mutex, OnceLock};
|
||||||
|
|
||||||
|
use crate::readers::Frame;
|
||||||
|
|
||||||
|
/// maximum number of frames held in the cache
|
||||||
|
const DEFAULT_FRAME_CACHE_SIZE: usize = 128;
|
||||||
|
|
||||||
|
/// maximum number of materialized arrays held in the cache
|
||||||
|
const DEFAULT_ARRAY_CACHE_SIZE: usize = 32;
|
||||||
|
|
||||||
|
/// identity of the reader a frame was read from
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub(crate) struct ReaderKey {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) path: PathBuf,
|
||||||
|
pub(crate) series: usize,
|
||||||
|
pub(crate) position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// borrowed view of [`ReaderKey`] for cache lookups without allocation.
|
||||||
|
/// hashes byte-identically to [`ReaderKey`] (str/String and Path/PathBuf hash
|
||||||
|
/// the same), so it only matches the same reader identity.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) struct ReaderKeyRef<'a> {
|
||||||
|
pub(crate) name: &'a str,
|
||||||
|
pub(crate) path: &'a Path,
|
||||||
|
pub(crate) series: usize,
|
||||||
|
pub(crate) position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for ReaderKeyRef<'_> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.name.hash(state);
|
||||||
|
self.path.hash(state);
|
||||||
|
self.series.hash(state);
|
||||||
|
self.position.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Equivalent<ReaderKey> for ReaderKeyRef<'_> {
|
||||||
|
fn equivalent(&self, key: &ReaderKey) -> bool {
|
||||||
|
self.name == key.name
|
||||||
|
&& self.path == key.path.as_path()
|
||||||
|
&& self.series == key.series
|
||||||
|
&& self.position == key.position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReaderKeyRef<'_> {
|
||||||
|
pub(crate) fn to_owned(&self) -> ReaderKey {
|
||||||
|
ReaderKey {
|
||||||
|
name: self.name.to_string(),
|
||||||
|
path: self.path.to_path_buf(),
|
||||||
|
series: self.series,
|
||||||
|
position: self.position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) type FrameKey = (ReaderKey, usize, usize, usize);
|
||||||
|
|
||||||
|
/// borrowed view of [`FrameKey`] for cache lookups without allocation.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) struct FrameKeyRef<'a> {
|
||||||
|
pub(crate) reader: ReaderKeyRef<'a>,
|
||||||
|
pub(crate) c: usize,
|
||||||
|
pub(crate) z: usize,
|
||||||
|
pub(crate) t: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for FrameKeyRef<'_> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.reader.hash(state);
|
||||||
|
self.c.hash(state);
|
||||||
|
self.z.hash(state);
|
||||||
|
self.t.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Equivalent<FrameKey> for FrameKeyRef<'_> {
|
||||||
|
fn equivalent(&self, key: &FrameKey) -> bool {
|
||||||
|
let (rk, c, z, t) = key;
|
||||||
|
self.reader.equivalent(rk) && self.c == *c && self.z == *z && self.t == *t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FrameKeyRef<'_> {
|
||||||
|
pub(crate) fn to_owned(&self) -> FrameKey {
|
||||||
|
(self.reader.to_owned(), self.c, self.z, self.t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// identity of a materialized array in the process-wide cache
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub(crate) struct ArrayKey {
|
||||||
|
pub(crate) reader: ReaderKey,
|
||||||
|
pub(crate) dtype: &'static str,
|
||||||
|
pub(crate) slice: Vec<SliceInfoElem>,
|
||||||
|
pub(crate) axes: Vec<Axis>,
|
||||||
|
pub(crate) operations: Vec<(Axis, Operation)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// borrowed view of [`ArrayKey`] for cache lookups without allocation.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) struct ArrayKeyRef<'a> {
|
||||||
|
pub(crate) reader: ReaderKeyRef<'a>,
|
||||||
|
pub(crate) dtype: &'static str,
|
||||||
|
pub(crate) slice: &'a [SliceInfoElem],
|
||||||
|
pub(crate) axes: &'a [Axis],
|
||||||
|
pub(crate) operations: &'a IndexMap<Axis, Operation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for ArrayKeyRef<'_> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.reader.hash(state);
|
||||||
|
self.dtype.hash(state);
|
||||||
|
self.slice.hash(state);
|
||||||
|
self.axes.hash(state);
|
||||||
|
self.operations.len().hash(state);
|
||||||
|
for (ax, op) in self.operations.iter() {
|
||||||
|
ax.hash(state);
|
||||||
|
op.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Equivalent<ArrayKey> for ArrayKeyRef<'_> {
|
||||||
|
fn equivalent(&self, key: &ArrayKey) -> bool {
|
||||||
|
self.reader.equivalent(&key.reader)
|
||||||
|
&& self.dtype == key.dtype
|
||||||
|
&& self.slice == key.slice
|
||||||
|
&& self.axes == key.axes
|
||||||
|
&& self.operations.len() == key.operations.len()
|
||||||
|
&& self
|
||||||
|
.operations
|
||||||
|
.iter()
|
||||||
|
.zip(&key.operations)
|
||||||
|
.all(|((ax, op), (key_ax, key_op))| ax == key_ax && op == key_op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArrayKeyRef<'_> {
|
||||||
|
pub(crate) fn to_owned(&self) -> ArrayKey {
|
||||||
|
ArrayKey {
|
||||||
|
reader: self.reader.to_owned(),
|
||||||
|
dtype: self.dtype,
|
||||||
|
slice: self.slice.to_vec(),
|
||||||
|
axes: self.axes.to_vec(),
|
||||||
|
operations: self
|
||||||
|
.operations
|
||||||
|
.iter()
|
||||||
|
.map(|(ax, op)| (*ax, op.clone()))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// process-wide LRU cache of frames, shared between all views and threads
|
||||||
|
static GLOBAL_FRAME_CACHE: OnceLock<FrameCache> = OnceLock::new();
|
||||||
|
|
||||||
|
/// thread-safe LRU cache of frames read from the underlying reader
|
||||||
|
pub(crate) struct FrameCache {
|
||||||
|
inner: Mutex<FrameCacheInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FrameCacheInner {
|
||||||
|
map: IndexMap<FrameKey, Arc<Frame>>,
|
||||||
|
capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FrameCache {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(DEFAULT_FRAME_CACHE_SIZE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FrameCache {
|
||||||
|
pub(crate) fn new(capacity: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Mutex::new(FrameCacheInner {
|
||||||
|
map: IndexMap::with_capacity(capacity),
|
||||||
|
capacity,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn global() -> &'static FrameCache {
|
||||||
|
GLOBAL_FRAME_CACHE.get_or_init(FrameCache::default)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn capacity(&self) -> usize {
|
||||||
|
self.inner.lock().unwrap().capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_capacity(&self, capacity: usize) {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.capacity = capacity;
|
||||||
|
while inner.map.len() > inner.capacity {
|
||||||
|
inner.map.shift_remove_index(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get<Q>(&self, key: &Q) -> Option<Arc<Frame>>
|
||||||
|
where
|
||||||
|
Q: ?Sized + Hash + Equivalent<FrameKey>,
|
||||||
|
{
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
if let Some(idx) = inner.map.get_index_of(key) {
|
||||||
|
let (key, frame) = inner.map.shift_remove_index(idx).unwrap();
|
||||||
|
inner.map.insert(key, frame.clone());
|
||||||
|
Some(frame)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert(&self, key: FrameKey, frame: Arc<Frame>) {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.map.insert(key, frame);
|
||||||
|
while inner.map.len() > inner.capacity {
|
||||||
|
inner.map.shift_remove_index(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn len(&self) -> usize {
|
||||||
|
self.inner.lock().unwrap().map.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for FrameCache {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("FrameCache").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// process-wide LRU cache of materialized arrays, shared between all views and threads
|
||||||
|
static GLOBAL_ARRAY_CACHE: OnceLock<ArrayCache> = OnceLock::new();
|
||||||
|
|
||||||
|
/// thread-safe LRU cache of materialized arrays produced by `as_array_dyn`
|
||||||
|
pub(crate) struct ArrayCache {
|
||||||
|
inner: Mutex<ArrayCacheInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArrayCacheInner {
|
||||||
|
map: IndexMap<ArrayKey, Arc<dyn Any + Send + Sync>>,
|
||||||
|
capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ArrayCache {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(DEFAULT_ARRAY_CACHE_SIZE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArrayCache {
|
||||||
|
pub(crate) fn new(capacity: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Mutex::new(ArrayCacheInner {
|
||||||
|
map: IndexMap::with_capacity(capacity),
|
||||||
|
capacity,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn global() -> &'static ArrayCache {
|
||||||
|
GLOBAL_ARRAY_CACHE.get_or_init(ArrayCache::default)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn capacity(&self) -> usize {
|
||||||
|
self.inner.lock().unwrap().capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_capacity(&self, capacity: usize) {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.capacity = capacity;
|
||||||
|
while inner.map.len() > inner.capacity {
|
||||||
|
inner.map.shift_remove_index(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get<T, Q>(&self, key: &Q) -> Option<Arc<ArrayD<T>>>
|
||||||
|
where
|
||||||
|
Q: ?Sized + Hash + Equivalent<ArrayKey>,
|
||||||
|
T: Any + Send + Sync,
|
||||||
|
{
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
if let Some(idx) = inner.map.get_index_of(key) {
|
||||||
|
let (key, array) = inner.map.shift_remove_index(idx).unwrap();
|
||||||
|
inner.map.insert(key, array.clone());
|
||||||
|
array.downcast::<ArrayD<T>>().ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert<T: Any + Send + Sync>(&self, key: ArrayKey, array: ArrayD<T>) {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.map.insert(key, Arc::new(array));
|
||||||
|
while inner.map.len() > inner.capacity {
|
||||||
|
inner.map.shift_remove_index(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn len(&self) -> usize {
|
||||||
|
self.inner.lock().unwrap().map.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for ArrayCache {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("ArrayCache").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ use phf::phf_map;
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// a static map from color names (like `"red"` or single letters like `"r"`) to hex strings
|
||||||
pub static COLORS: phf::Map<&'static str, &'static str> = phf_map! {
|
pub static COLORS: phf::Map<&'static str, &'static str> = phf_map! {
|
||||||
"b" => "#0000FF",
|
"b" => "#0000FF",
|
||||||
"g" => "#008000",
|
"g" => "#008000",
|
||||||
@@ -162,6 +163,7 @@ pub static COLORS: phf::Map<&'static str, &'static str> = phf_map! {
|
|||||||
"yellowgreen" => "#9ACD32",
|
"yellowgreen" => "#9ACD32",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// a color with red, green and blue components, parsed from a hex string (`#RRGGBB`) or a name in [`COLORS`]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
r: u8,
|
r: u8,
|
||||||
@@ -196,6 +198,7 @@ impl Display for Color {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
|
/// the color as an RGB vector `[r, g, b]`
|
||||||
pub fn to_rgb(&self) -> Vec<u8> {
|
pub fn to_rgb(&self) -> Vec<u8> {
|
||||||
vec![self.r, self.g, self.b]
|
vec![self.r, self.g, self.b]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,121 +1,171 @@
|
|||||||
use strum::IntoStaticStr;
|
use strum::IntoStaticStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// the error type used throughout the crate
|
||||||
#[derive(Debug, Error, IntoStaticStr)]
|
#[derive(Debug, Error, IntoStaticStr)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// an io error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
|
/// an ndarray shape error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Shape(#[from] ndarray::ShapeError),
|
Shape(#[from] ndarray::ShapeError),
|
||||||
#[cfg(feature = "bioformats_java")]
|
#[cfg(feature = "bioformats_java")]
|
||||||
|
/// an error from the j4rs java bridge
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
J4rs(#[from] j4rs::errors::J4RsError),
|
J4rs(#[from] j4rs::errors::J4RsError),
|
||||||
|
/// an infallible conversion
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Infallible(#[from] std::convert::Infallible),
|
Infallible(#[from] std::convert::Infallible),
|
||||||
|
/// an integer parse error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ParseIntError(#[from] std::num::ParseIntError),
|
ParseIntError(#[from] std::num::ParseIntError),
|
||||||
|
/// an ome metadata error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Ome(#[from] ome_metadata::error::Error),
|
Ome(#[from] ome_metadata::error::Error),
|
||||||
#[cfg(feature = "bioformats_java")]
|
#[cfg(feature = "bioformats_java")]
|
||||||
|
/// an error while downloading (e.g. the bioformats jar)
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Downloader(#[from] downloader::Error),
|
Downloader(#[from] downloader::Error),
|
||||||
|
/// an error parsing an enum string with strum
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Strum(#[from] strum::ParseError),
|
Strum(#[from] strum::ParseError),
|
||||||
#[cfg(feature = "tiffwrite")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
|
/// an indicatif progress bar template error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TemplateError(#[from] indicatif::style::TemplateError),
|
TemplateError(#[from] indicatif::style::TemplateError),
|
||||||
#[cfg(feature = "tiffwrite")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
|
/// an error from the tiffwrite crate
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TiffWrite(#[from] tiffwrite::error::Error),
|
TiffWrite(#[from] tiffwrite::error::Error),
|
||||||
#[cfg(feature = "tiffseq")]
|
#[cfg(feature = "tiffseq")]
|
||||||
|
/// a yaml (de)serialization error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
SerdeYaml(#[from] serde_yaml::Error),
|
SerdeYaml(#[from] serde_yaml::Error),
|
||||||
#[cfg(any(feature = "tiffseq", feature = "tiff"))]
|
#[cfg(any(feature = "tiffseq", feature = "tiff"))]
|
||||||
|
/// an error from the tiff crate
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Tiff(#[from] tiff::TiffError),
|
Tiff(#[from] tiff::TiffError),
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
|
/// a postcard (de)serialization error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
PostCard(#[from] postcard::Error),
|
PostCard(#[from] postcard::Error),
|
||||||
#[cfg(feature = "czi")]
|
#[cfg(feature = "czi")]
|
||||||
|
/// an error from the libczi binding
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
LibCzi(#[from] libczirw_sys::error::Error),
|
LibCzi(#[from] libczirw_sys::error::Error),
|
||||||
|
/// a regex error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
RegexError(#[from] regex::Error),
|
RegexError(#[from] regex::Error),
|
||||||
#[cfg(feature = "czi")]
|
#[cfg(feature = "czi")]
|
||||||
|
/// an xmltree error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
XmlTree(#[from] xmltree::Error),
|
XmlTree(#[from] xmltree::Error),
|
||||||
#[cfg(feature = "czi")]
|
#[cfg(feature = "czi")]
|
||||||
|
/// an xmltree parse error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
XmlTreeParse(#[from] xmltree::ParseError),
|
XmlTreeParse(#[from] xmltree::ParseError),
|
||||||
#[cfg(feature = "czi")]
|
#[cfg(feature = "czi")]
|
||||||
|
/// a czi-specific error
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Czi(#[from] crate::readers::czi::CziError),
|
Czi(#[from] crate::readers::czi::CziError),
|
||||||
#[cfg(feature = "movie")]
|
#[cfg(feature = "movie")]
|
||||||
|
/// an error joining a tokio task
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TokioJoin(#[from] tokio::task::JoinError),
|
TokioJoin(#[from] tokio::task::JoinError),
|
||||||
#[cfg(feature = "bioformats_rust")]
|
#[cfg(feature = "bioformats_rust")]
|
||||||
|
/// an error from the bioformats rust crate
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
BioFormats(#[from] bioformats::error::BioFormatsError),
|
BioFormats(#[from] bioformats::error::BioFormatsError),
|
||||||
|
|
||||||
|
/// the axis string could not be parsed
|
||||||
#[error("invalid axis: {0}")]
|
#[error("invalid axis: {0}")]
|
||||||
InvalidAxis(String),
|
InvalidAxis(String),
|
||||||
|
/// the axis was not found in the axes
|
||||||
#[error("axis {0} not found in axes {1}")]
|
#[error("axis {0} not found in axes {1}")]
|
||||||
AxisNotFound(String, String),
|
AxisNotFound(String, String),
|
||||||
|
/// a conversion error
|
||||||
#[error("conversion error: {0}")]
|
#[error("conversion error: {0}")]
|
||||||
TryInto(String),
|
TryInto(String),
|
||||||
|
/// the target file already exists
|
||||||
#[error("file already exists {0}")]
|
#[error("file already exists {0}")]
|
||||||
FileAlreadyExists(String),
|
FileAlreadyExists(String),
|
||||||
|
/// could not download ffmpeg
|
||||||
#[error("could not download ffmpeg: {0}")]
|
#[error("could not download ffmpeg: {0}")]
|
||||||
FfmpegDownload(String),
|
FfmpegDownload(String),
|
||||||
|
/// an ffmpeg error
|
||||||
#[error("FFmpeg error: {0}")]
|
#[error("FFmpeg error: {0}")]
|
||||||
Ffmpeg(String),
|
Ffmpeg(String),
|
||||||
|
/// the index is out of bounds
|
||||||
#[error("index {0} out of bounds {1}")]
|
#[error("index {0} out of bounds {1}")]
|
||||||
OutOfBounds(isize, isize),
|
OutOfBounds(isize, isize),
|
||||||
|
/// the axis was not included in the view
|
||||||
#[error("axis {0} has length {1}, but was not included")]
|
#[error("axis {0} has length {1}, but was not included")]
|
||||||
OutOfBoundsAxis(String, usize),
|
OutOfBoundsAxis(String, usize),
|
||||||
|
/// the dimensionality of the data does not match
|
||||||
#[error("dimensionality mismatch: {0} != {0}")]
|
#[error("dimensionality mismatch: {0} != {0}")]
|
||||||
DimensionalityMismatch(usize, usize),
|
DimensionalityMismatch(usize, usize),
|
||||||
|
/// the axis already has an operation
|
||||||
#[error("axis {0}: {1} is already operated on!")]
|
#[error("axis {0}: {1} is already operated on!")]
|
||||||
AxisAlreadyOperated(usize, String),
|
AxisAlreadyOperated(usize, String),
|
||||||
|
/// not enough free dimensions
|
||||||
#[error("not enough free dimensions")]
|
#[error("not enough free dimensions")]
|
||||||
NotEnoughFreeDimensions,
|
NotEnoughFreeDimensions,
|
||||||
|
/// cannot cast a pixel value to the requested type
|
||||||
#[error("cannot cast {0} to {1}")]
|
#[error("cannot cast {0} to {1}")]
|
||||||
Cast(String, String),
|
Cast(String, String),
|
||||||
|
/// the view is empty
|
||||||
#[error("empty view")]
|
#[error("empty view")]
|
||||||
EmptyView,
|
EmptyView,
|
||||||
|
/// the color string could not be parsed
|
||||||
#[error("invalid color: {0}")]
|
#[error("invalid color: {0}")]
|
||||||
InvalidColor(String),
|
InvalidColor(String),
|
||||||
|
/// no image or pixels found in the metadata
|
||||||
#[error("no image or pixels found")]
|
#[error("no image or pixels found")]
|
||||||
NoImageOrPixels,
|
NoImageOrPixels,
|
||||||
|
/// the attenuation value is invalid
|
||||||
#[error("invalid attenuation value: {0}")]
|
#[error("invalid attenuation value: {0}")]
|
||||||
InvalidAttenuation(String),
|
InvalidAttenuation(String),
|
||||||
|
/// the file name is invalid
|
||||||
#[error("not a valid file name")]
|
#[error("not a valid file name")]
|
||||||
InvalidFileName,
|
InvalidFileName,
|
||||||
|
/// the file has no parent directory
|
||||||
#[error("file has no parent")]
|
#[error("file has no parent")]
|
||||||
NoParent,
|
NoParent,
|
||||||
|
/// the pixel type is unknown
|
||||||
#[error("unknown pixel type {0}")]
|
#[error("unknown pixel type {0}")]
|
||||||
UnknownPixelType(String),
|
UnknownPixelType(String),
|
||||||
|
/// cannot compute the mean of an empty axis
|
||||||
#[error("no mean")]
|
#[error("no mean")]
|
||||||
NoMean,
|
NoMean,
|
||||||
|
/// the tiff file lock is poisoned
|
||||||
#[error("tiff is locked")]
|
#[error("tiff is locked")]
|
||||||
TiffLock,
|
TiffLock,
|
||||||
|
/// this feature is not implemented
|
||||||
#[error("not implemented: {0}")]
|
#[error("not implemented: {0}")]
|
||||||
NotImplemented(String),
|
NotImplemented(String),
|
||||||
|
/// a string could not be parsed
|
||||||
#[error("cannot parse: {0}")]
|
#[error("cannot parse: {0}")]
|
||||||
Parse(String),
|
Parse(String),
|
||||||
|
/// cannot convert the libczi pixel type
|
||||||
#[error("cannot convert libczi pixel type: {0}")]
|
#[error("cannot convert libczi pixel type: {0}")]
|
||||||
Conversion(String),
|
Conversion(String),
|
||||||
|
/// no reader could open the file
|
||||||
#[error("no reader found for {0}, tried: {1}")]
|
#[error("no reader found for {0}, tried: {1}")]
|
||||||
NoReader(String, String),
|
NoReader(String, String),
|
||||||
|
/// the reader cannot open the file
|
||||||
#[error("reader {0} cannot open file {1} because {2}")]
|
#[error("reader {0} cannot open file {1} because {2}")]
|
||||||
InvalidReader(String, String, String),
|
InvalidReader(String, String, String),
|
||||||
|
/// the file does not exist
|
||||||
#[error("file does not exist: {0}")]
|
#[error("file does not exist: {0}")]
|
||||||
FileDoesNotExist(String),
|
FileDoesNotExist(String),
|
||||||
|
/// cannot remove axes that have a size != 1
|
||||||
#[error("cannot remove axes {0}, size {1} != 1")]
|
#[error("cannot remove axes {0}, size {1} != 1")]
|
||||||
SizeMismatch(String, usize),
|
SizeMismatch(String, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
/// the name of the error variant as a static string
|
||||||
pub fn variant_name(&self) -> &'static str {
|
pub fn variant_name(&self) -> &'static str {
|
||||||
self.into()
|
self.into()
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -43,22 +43,34 @@
|
|||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
/// axis handling: axis enum, slicing and shape
|
||||||
pub mod axes;
|
pub mod axes;
|
||||||
|
/// process-wide LRU caches for frames and materialized arrays
|
||||||
|
mod cache;
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
mod py;
|
mod py;
|
||||||
|
/// min/max/sum/mean operations along an axis
|
||||||
pub mod stats;
|
pub mod stats;
|
||||||
|
/// the main data structure: an on-disk image that can be sliced without loading it fully
|
||||||
pub mod view;
|
pub mod view;
|
||||||
|
|
||||||
|
/// named colors and color conversion
|
||||||
pub mod colors;
|
pub mod colors;
|
||||||
|
/// the error type used throughout the crate
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
/// ome metadata helpers
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
#[cfg(feature = "movie")]
|
#[cfg(feature = "movie")]
|
||||||
|
/// saving views as movies
|
||||||
pub mod movie;
|
pub mod movie;
|
||||||
|
/// readers for the different supported image formats
|
||||||
pub mod readers;
|
pub mod readers;
|
||||||
#[cfg(feature = "tiffwrite")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
|
/// saving views as tiff files
|
||||||
pub mod tiffwrite;
|
pub mod tiffwrite;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
/// main entry point for the application
|
||||||
pub mod main {
|
pub mod main {
|
||||||
#[cfg(feature = "tiffwrite")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
use crate::axes::{Axis, Operation};
|
use crate::axes::{Axis, Operation};
|
||||||
@@ -172,6 +184,7 @@ pub mod main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the command line interface, run `ndbioimage --help` for an overview of the commands
|
||||||
pub fn main(args: Option<Vec<String>>) -> Result<(), Error> {
|
pub fn main(args: Option<Vec<String>>) -> Result<(), Error> {
|
||||||
let cli = if let Some(args) = args {
|
let cli = if let Some(args) = args {
|
||||||
Cli::parse_from(args)
|
Cli::parse_from(args)
|
||||||
|
|||||||
@@ -20,10 +20,14 @@ impl Metadata for Ome {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// helper trait to extract useful information from ome metadata
|
||||||
pub trait Metadata {
|
pub trait Metadata {
|
||||||
|
/// the instrument used to acquire the image
|
||||||
fn get_instrument(&self) -> Option<&Instrument>;
|
fn get_instrument(&self) -> Option<&Instrument>;
|
||||||
|
/// the first image in the ome structure
|
||||||
fn get_image(&self) -> Option<&Image>;
|
fn get_image(&self) -> Option<&Image>;
|
||||||
|
|
||||||
|
/// the pixels of the image
|
||||||
fn get_pixels(&self) -> Option<&Pixels> {
|
fn get_pixels(&self) -> Option<&Pixels> {
|
||||||
if let Some(image) = self.get_image() {
|
if let Some(image) = self.get_image() {
|
||||||
Some(&image.pixels)
|
Some(&image.pixels)
|
||||||
@@ -32,6 +36,7 @@ pub trait Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the objective used to acquire the image
|
||||||
fn get_objective(&self) -> Option<&Objective> {
|
fn get_objective(&self) -> Option<&Objective> {
|
||||||
let objective_id = self.get_image()?.objective_settings.as_ref()?.id.clone();
|
let objective_id = self.get_image()?.objective_settings.as_ref()?.id.clone();
|
||||||
self.get_instrument()?
|
self.get_instrument()?
|
||||||
@@ -40,6 +45,7 @@ pub trait Metadata {
|
|||||||
.find(|o| o.id == objective_id)
|
.find(|o| o.id == objective_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the tube lens used to acquire the image
|
||||||
fn get_tube_lens(&self) -> Option<&Objective> {
|
fn get_tube_lens(&self) -> Option<&Objective> {
|
||||||
self.get_instrument()?
|
self.get_instrument()?
|
||||||
.objective
|
.objective
|
||||||
@@ -160,6 +166,7 @@ pub trait Metadata {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the binning of the detector for a channel (1, 2, 4 or 8)
|
||||||
fn binning(&self, channel: usize) -> Option<usize> {
|
fn binning(&self, channel: usize) -> Option<usize> {
|
||||||
match self
|
match self
|
||||||
.get_pixels()?
|
.get_pixels()?
|
||||||
@@ -178,6 +185,7 @@ pub trait Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the excitation wavelength of the laser for a channel in nm
|
||||||
fn laser_wavelengths(&self, channel: usize) -> Result<Option<f64>, Error> {
|
fn laser_wavelengths(&self, channel: usize) -> Result<Option<f64>, Error> {
|
||||||
Ok(
|
Ok(
|
||||||
if let Some(pixels) = self.get_pixels()
|
if let Some(pixels) = self.get_pixels()
|
||||||
@@ -195,6 +203,7 @@ pub trait Metadata {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the laser power (fraction of the maximum) for a channel
|
||||||
fn laser_powers(&self, channel: usize) -> Result<Option<f64>, Error> {
|
fn laser_powers(&self, channel: usize) -> Result<Option<f64>, Error> {
|
||||||
if let Some(pixels) = self.get_pixels()
|
if let Some(pixels) = self.get_pixels()
|
||||||
&& let Some(channel) = pixels.channel.get(channel)
|
&& let Some(channel) = pixels.channel.get(channel)
|
||||||
@@ -211,10 +220,12 @@ pub trait Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the name of the objective
|
||||||
fn objective_name(&self) -> Option<String> {
|
fn objective_name(&self) -> Option<String> {
|
||||||
Some(self.get_objective()?.model.as_ref()?.clone())
|
Some(self.get_objective()?.model.as_ref()?.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the total magnification: objective magnification times tube lens magnification
|
||||||
fn magnification(&self) -> Option<f64> {
|
fn magnification(&self) -> Option<f64> {
|
||||||
Some(
|
Some(
|
||||||
(self.get_objective()?.nominal_magnification? as f64)
|
(self.get_objective()?.nominal_magnification? as f64)
|
||||||
@@ -222,10 +233,12 @@ pub trait Metadata {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the name of the tube lens
|
||||||
fn tube_lens_name(&self) -> Option<String> {
|
fn tube_lens_name(&self) -> Option<String> {
|
||||||
self.get_tube_lens()?.model.clone()
|
self.get_tube_lens()?.model.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the name of the filter set for a channel
|
||||||
fn filter_set_name(&self, channel: usize) -> Option<String> {
|
fn filter_set_name(&self, channel: usize) -> Option<String> {
|
||||||
let filter_set_id = self
|
let filter_set_id = self
|
||||||
.get_pixels()?
|
.get_pixels()?
|
||||||
@@ -244,6 +257,7 @@ pub trait Metadata {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the gain of the detector for a channel
|
||||||
fn gain(&self, channel: usize) -> Option<f64> {
|
fn gain(&self, channel: usize) -> Option<f64> {
|
||||||
self.get_pixels()
|
self.get_pixels()
|
||||||
.and_then(|p| p.channel.get(channel))
|
.and_then(|p| p.channel.get(channel))
|
||||||
@@ -257,18 +271,21 @@ pub trait Metadata {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether the image is a z-stack (more than one z slice)
|
||||||
fn is_zstack(&self) -> Result<bool, Error> {
|
fn is_zstack(&self) -> Result<bool, Error> {
|
||||||
self.get_pixels()
|
self.get_pixels()
|
||||||
.map(|p| Ok(p.size_z > 1))
|
.map(|p| Ok(p.size_z > 1))
|
||||||
.unwrap_or_else(|| Err(Error::NoImageOrPixels))
|
.unwrap_or_else(|| Err(Error::NoImageOrPixels))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether the image is a time lapse (more than one time point)
|
||||||
fn is_time_lapse(&self) -> Result<bool, Error> {
|
fn is_time_lapse(&self) -> Result<bool, Error> {
|
||||||
self.get_pixels()
|
self.get_pixels()
|
||||||
.map(|p| Ok(p.size_t > 1))
|
.map(|p| Ok(p.size_t > 1))
|
||||||
.unwrap_or_else(|| Err(Error::NoImageOrPixels))
|
.unwrap_or_else(|| Err(Error::NoImageOrPixels))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a multi-line summary of the most relevant metadata, one field per line
|
||||||
fn summary(&self) -> Result<String, Error> {
|
fn summary(&self) -> Result<String, Error> {
|
||||||
let size_c = if let Some(pixels) = self.get_pixels() {
|
let size_c = if let Some(pixels) = self.get_pixels() {
|
||||||
pixels.channel.len()
|
pixels.channel.len()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use ordered_float::OrderedFloat;
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// options for creating a movie from a view
|
||||||
pub struct MovieOptions {
|
pub struct MovieOptions {
|
||||||
velocity: f64,
|
velocity: f64,
|
||||||
brightness: Vec<f64>,
|
brightness: Vec<f64>,
|
||||||
@@ -38,6 +39,7 @@ impl Default for MovieOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MovieOptions {
|
impl MovieOptions {
|
||||||
|
/// create movie options with the given parameters
|
||||||
pub fn new(
|
pub fn new(
|
||||||
velocity: f64,
|
velocity: f64,
|
||||||
brightness: Vec<f64>,
|
brightness: Vec<f64>,
|
||||||
@@ -67,18 +69,22 @@ impl MovieOptions {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set the frames per second of the movie
|
||||||
pub fn set_velocity(&mut self, velocity: f64) {
|
pub fn set_velocity(&mut self, velocity: f64) {
|
||||||
self.velocity = velocity;
|
self.velocity = velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set the brightness scale factors for each channel
|
||||||
pub fn set_brightness(&mut self, brightness: Vec<f64>) {
|
pub fn set_brightness(&mut self, brightness: Vec<f64>) {
|
||||||
self.brightness = brightness;
|
self.brightness = brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set the display scale (zoom) factor for the movie
|
||||||
pub fn set_scale(&mut self, scale: f64) {
|
pub fn set_scale(&mut self, scale: f64) {
|
||||||
self.scale = scale;
|
self.scale = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set the color lookup table for the movie
|
||||||
pub fn set_colors(&mut self, colors: &[String]) -> Result<(), Error> {
|
pub fn set_colors(&mut self, colors: &[String]) -> Result<(), Error> {
|
||||||
let colors = colors
|
let colors = colors
|
||||||
.iter()
|
.iter()
|
||||||
@@ -88,6 +94,7 @@ impl MovieOptions {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set whether an existing movie file should be overwritten
|
||||||
pub fn set_overwrite(&mut self, overwrite: bool) {
|
pub fn set_overwrite(&mut self, overwrite: bool) {
|
||||||
self.overwrite = overwrite;
|
self.overwrite = overwrite;
|
||||||
}
|
}
|
||||||
@@ -142,6 +149,7 @@ where
|
|||||||
R: Reader,
|
R: Reader,
|
||||||
Self: 'static,
|
Self: 'static,
|
||||||
{
|
{
|
||||||
|
/// save the view as a movie file with the given options
|
||||||
pub fn save_as_movie<P>(&self, path: P, options: &MovieOptions) -> Result<(), Error>
|
pub fn save_as_movie<P>(&self, path: P, options: &MovieOptions) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ impl PyView {
|
|||||||
override_type(type_repr="str | pathlib.Path | Imread | bytes", imports=("pathlib"))
|
override_type(type_repr="str | pathlib.Path | Imread | bytes", imports=("pathlib"))
|
||||||
)]
|
)]
|
||||||
path: Bound<'py, PyAny>,
|
path: Bound<'py, PyAny>,
|
||||||
#[gen_stub(override_type(type_repr = "numpy.typing.DTypeLike", imports=("numpy", "numpy.typing")
|
#[gen_stub(override_type(type_repr = "typing.Optional[numpy.typing.DTypeLike]", imports=("typing", "numpy", "numpy.typing")
|
||||||
))]
|
))]
|
||||||
dtype: Option<Bound<'py, PyAny>>,
|
dtype: Option<Bound<'py, PyAny>>,
|
||||||
axes: &str,
|
axes: &str,
|
||||||
@@ -229,6 +229,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get all available positions (series) in the file
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn get_positions<'py>(
|
fn get_positions<'py>(
|
||||||
py: Python,
|
py: Python,
|
||||||
@@ -244,16 +245,19 @@ impl PyView {
|
|||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn kill_vm() {}
|
fn kill_vm() {}
|
||||||
|
|
||||||
|
/// the name of the reader used to open the file
|
||||||
#[getter]
|
#[getter]
|
||||||
fn reader_name(&self) -> String {
|
fn reader_name(&self) -> String {
|
||||||
self.view.reader_name().to_string()
|
self.view.reader_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// reshape the view with a new axis order
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn reshape<'py>(&self, order: &str, copy: bool) -> PyResult<Bound<'py, PyAny>> {
|
fn reshape<'py>(&self, order: &str, copy: bool) -> PyResult<Bound<'py, PyAny>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// return a new view with transformations applied (channel alignment, drift correction)
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[pyo3(signature = (channels = true, drift = false, file = None, bead_files = None))]
|
#[pyo3(signature = (channels = true, drift = false, file = None, bead_files = None))]
|
||||||
fn with_transform<'py>(
|
fn with_transform<'py>(
|
||||||
@@ -266,6 +270,7 @@ impl PyView {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get the transformation matrix (not yet implemented)
|
||||||
#[getter]
|
#[getter]
|
||||||
fn get_transform(&self) -> PyResult<()> {
|
fn get_transform(&self) -> PyResult<()> {
|
||||||
todo!()
|
todo!()
|
||||||
@@ -533,12 +538,13 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// convert to a numpy array, optionally with a different dtype
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[pyo3(signature = (dtype = None, copy = None))]
|
#[pyo3(signature = (dtype = None, copy = None))]
|
||||||
fn __array__<'py>(
|
fn __array__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
#[gen_stub(override_type(type_repr = "numpy.typing.DTypeLike", imports=("numpy", "numpy.typing")
|
#[gen_stub(override_type(type_repr = "typing.Optional[numpy.typing.DTypeLike]", imports=("typing", "numpy", "numpy.typing")
|
||||||
))]
|
))]
|
||||||
dtype: Option<Bound<'py, PyAny>>,
|
dtype: Option<Bound<'py, PyAny>>,
|
||||||
copy: Option<bool>,
|
copy: Option<bool>,
|
||||||
@@ -550,10 +556,12 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// check if an item is contained in the view (not implemented)
|
||||||
fn __contains__(&self, _item: Bound<PyAny>) -> PyResult<bool> {
|
fn __contains__(&self, _item: Bound<PyAny>) -> PyResult<bool> {
|
||||||
Err(PyNotImplementedError::new_err("contains not implemented"))
|
Err(PyNotImplementedError::new_err("contains not implemented"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise less than comparison
|
||||||
fn __lt__<'py>(
|
fn __lt__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -565,6 +573,7 @@ impl PyView {
|
|||||||
np.getattr("less")?.call1((&a, &b))
|
np.getattr("less")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise less than or equal comparison
|
||||||
fn __le__<'py>(
|
fn __le__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -576,6 +585,7 @@ impl PyView {
|
|||||||
np.getattr("less_equal")?.call1((&a, &b))
|
np.getattr("less_equal")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise equality comparison
|
||||||
fn __eq__<'py>(
|
fn __eq__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -587,6 +597,7 @@ impl PyView {
|
|||||||
np.getattr("equal")?.call1((&a, &b))
|
np.getattr("equal")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise not equal comparison
|
||||||
fn __ne__<'py>(
|
fn __ne__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -598,6 +609,7 @@ impl PyView {
|
|||||||
np.getattr("not_equal")?.call1((&a, &b))
|
np.getattr("not_equal")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise greater than comparison
|
||||||
fn __gt__<'py>(
|
fn __gt__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -609,6 +621,7 @@ impl PyView {
|
|||||||
np.getattr("greater")?.call1((&a, &b))
|
np.getattr("greater")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise greater than or equal comparison
|
||||||
fn __ge__<'py>(
|
fn __ge__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -620,6 +633,7 @@ impl PyView {
|
|||||||
np.getattr("greater_equal")?.call1((&a, &b))
|
np.getattr("greater_equal")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise addition
|
||||||
fn __add__<'py>(
|
fn __add__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -631,6 +645,7 @@ impl PyView {
|
|||||||
np.getattr("add")?.call1((&a, &b))
|
np.getattr("add")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise addition (reflected)
|
||||||
fn __radd__<'py>(
|
fn __radd__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -642,6 +657,7 @@ impl PyView {
|
|||||||
np.getattr("add")?.call1((&a, &b))
|
np.getattr("add")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise subtraction
|
||||||
fn __sub__<'py>(
|
fn __sub__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -653,6 +669,7 @@ impl PyView {
|
|||||||
np.getattr("subtract")?.call1((&a, &b))
|
np.getattr("subtract")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise subtraction (reflected)
|
||||||
fn __rsub__<'py>(
|
fn __rsub__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -664,6 +681,7 @@ impl PyView {
|
|||||||
np.getattr("subtract")?.call1((&a, &b))
|
np.getattr("subtract")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise multiplication
|
||||||
fn __mul__<'py>(
|
fn __mul__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -675,6 +693,7 @@ impl PyView {
|
|||||||
np.getattr("multiply")?.call1((&a, &b))
|
np.getattr("multiply")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise multiplication (reflected)
|
||||||
fn __rmul__<'py>(
|
fn __rmul__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -686,6 +705,7 @@ impl PyView {
|
|||||||
np.getattr("multiply")?.call1((&a, &b))
|
np.getattr("multiply")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise true division
|
||||||
fn __truediv__<'py>(
|
fn __truediv__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -697,6 +717,7 @@ impl PyView {
|
|||||||
np.getattr("true_divide")?.call1((&a, &b))
|
np.getattr("true_divide")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise true division (reflected)
|
||||||
fn __rtruediv__<'py>(
|
fn __rtruediv__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -708,6 +729,7 @@ impl PyView {
|
|||||||
np.getattr("true_divide")?.call1((&a, &b))
|
np.getattr("true_divide")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise floor division
|
||||||
fn __floordiv__<'py>(
|
fn __floordiv__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -719,6 +741,7 @@ impl PyView {
|
|||||||
np.getattr("floor_divide")?.call1((&a, &b))
|
np.getattr("floor_divide")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise floor division (reflected)
|
||||||
fn __rfloordiv__<'py>(
|
fn __rfloordiv__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -730,6 +753,7 @@ impl PyView {
|
|||||||
np.getattr("floor_divide")?.call1((&a, &b))
|
np.getattr("floor_divide")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise modulo
|
||||||
fn __mod__<'py>(
|
fn __mod__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -741,6 +765,7 @@ impl PyView {
|
|||||||
np.getattr("remainder")?.call1((&a, &b))
|
np.getattr("remainder")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise modulo (reflected)
|
||||||
fn __rmod__<'py>(
|
fn __rmod__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -788,6 +813,7 @@ impl PyView {
|
|||||||
np.getattr("power")?.call1((&a, &b))
|
np.getattr("power")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise matrix multiplication
|
||||||
fn __matmul__<'py>(
|
fn __matmul__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -799,6 +825,7 @@ impl PyView {
|
|||||||
np.getattr("matmul")?.call1((&a, &b))
|
np.getattr("matmul")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise matrix multiplication (reflected)
|
||||||
fn __rmatmul__<'py>(
|
fn __rmatmul__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -810,6 +837,7 @@ impl PyView {
|
|||||||
np.getattr("matmul")?.call1((&a, &b))
|
np.getattr("matmul")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise bitwise AND
|
||||||
fn __and__<'py>(
|
fn __and__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -821,6 +849,7 @@ impl PyView {
|
|||||||
np.getattr("bitwise_and")?.call1((&a, &b))
|
np.getattr("bitwise_and")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise bitwise AND (reflected)
|
||||||
fn __rand__<'py>(
|
fn __rand__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -832,6 +861,7 @@ impl PyView {
|
|||||||
np.getattr("bitwise_and")?.call1((&a, &b))
|
np.getattr("bitwise_and")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise bitwise OR
|
||||||
fn __or__<'py>(
|
fn __or__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -843,6 +873,7 @@ impl PyView {
|
|||||||
np.getattr("bitwise_or")?.call1((&a, &b))
|
np.getattr("bitwise_or")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise bitwise OR (reflected)
|
||||||
fn __ror__<'py>(
|
fn __ror__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -854,6 +885,7 @@ impl PyView {
|
|||||||
np.getattr("bitwise_or")?.call1((&a, &b))
|
np.getattr("bitwise_or")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise bitwise XOR
|
||||||
fn __xor__<'py>(
|
fn __xor__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -865,6 +897,7 @@ impl PyView {
|
|||||||
np.getattr("bitwise_xor")?.call1((&a, &b))
|
np.getattr("bitwise_xor")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise bitwise XOR (reflected)
|
||||||
fn __rxor__<'py>(
|
fn __rxor__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -876,6 +909,7 @@ impl PyView {
|
|||||||
np.getattr("bitwise_xor")?.call1((&a, &b))
|
np.getattr("bitwise_xor")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise left shift
|
||||||
fn __lshift__<'py>(
|
fn __lshift__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -887,6 +921,7 @@ impl PyView {
|
|||||||
np.getattr("left_shift")?.call1((&a, &b))
|
np.getattr("left_shift")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise left shift (reflected)
|
||||||
fn __rlshift__<'py>(
|
fn __rlshift__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -898,6 +933,7 @@ impl PyView {
|
|||||||
np.getattr("left_shift")?.call1((&a, &b))
|
np.getattr("left_shift")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise right shift
|
||||||
fn __rshift__<'py>(
|
fn __rshift__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -909,6 +945,7 @@ impl PyView {
|
|||||||
np.getattr("right_shift")?.call1((&a, &b))
|
np.getattr("right_shift")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise right shift (reflected)
|
||||||
fn __rrshift__<'py>(
|
fn __rrshift__<'py>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
@@ -920,34 +957,40 @@ impl PyView {
|
|||||||
np.getattr("right_shift")?.call1((&a, &b))
|
np.getattr("right_shift")?.call1((&a, &b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise negation
|
||||||
fn __neg__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
fn __neg__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
||||||
let np = PyModule::import(py, "numpy")?;
|
let np = PyModule::import(py, "numpy")?;
|
||||||
let a = self.as_array(py)?;
|
let a = self.as_array(py)?;
|
||||||
np.getattr("negative")?.call1((&a,))
|
np.getattr("negative")?.call1((&a,))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise positive
|
||||||
fn __pos__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
fn __pos__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
||||||
let np = PyModule::import(py, "numpy")?;
|
let np = PyModule::import(py, "numpy")?;
|
||||||
let a = self.as_array(py)?;
|
let a = self.as_array(py)?;
|
||||||
np.getattr("positive")?.call1((&a,))
|
np.getattr("positive")?.call1((&a,))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise absolute value
|
||||||
fn __abs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
fn __abs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
||||||
let np = PyModule::import(py, "numpy")?;
|
let np = PyModule::import(py, "numpy")?;
|
||||||
let a = self.as_array(py)?;
|
let a = self.as_array(py)?;
|
||||||
np.getattr("absolute")?.call1((&a,))
|
np.getattr("absolute")?.call1((&a,))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// element-wise bitwise inversion
|
||||||
fn __invert__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
fn __invert__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
||||||
let np = PyModule::import(py, "numpy")?;
|
let np = PyModule::import(py, "numpy")?;
|
||||||
let a = self.as_array(py)?;
|
let a = self.as_array(py)?;
|
||||||
np.getattr("invert")?.call1((&a,))
|
np.getattr("invert")?.call1((&a,))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// context manager entry
|
||||||
fn __enter__<'py>(slf: PyRef<'py, Self>) -> PyResult<PyRef<'py, Self>> {
|
fn __enter__<'py>(slf: PyRef<'py, Self>) -> PyResult<PyRef<'py, Self>> {
|
||||||
Ok(slf)
|
Ok(slf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// context manager exit
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[pyo3(signature = (exc_type=None, exc_val=None, exc_tb=None))]
|
#[pyo3(signature = (exc_type=None, exc_val=None, exc_tb=None))]
|
||||||
fn __exit__(
|
fn __exit__(
|
||||||
@@ -959,10 +1002,12 @@ impl PyView {
|
|||||||
self.close()
|
self.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// arguments for pickling
|
||||||
pub(crate) fn __getnewargs__(&self) -> PyResult<(Vec<u8>,)> {
|
pub(crate) fn __getnewargs__(&self) -> PyResult<(Vec<u8>,)> {
|
||||||
Ok((to_stdvec(self).map_err(Error::from)?,))
|
Ok((to_stdvec(self).map_err(Error::from)?,))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// shallow copy of the view
|
||||||
fn __copy__(&self) -> Self {
|
fn __copy__(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view: self.view.clone(),
|
view: self.view.clone(),
|
||||||
@@ -972,6 +1017,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// deep copy of the view (same as shallow copy for this type)
|
||||||
fn __deepcopy__(&self) -> Self {
|
fn __deepcopy__(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view: self.view.clone(),
|
view: self.view.clone(),
|
||||||
@@ -981,6 +1027,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// create a copy of the view
|
||||||
fn copy(&self) -> Self {
|
fn copy(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view: self.view.clone(),
|
view: self.view.clone(),
|
||||||
@@ -990,6 +1037,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// iterate over the first axis
|
||||||
fn __iter__(&self) -> Self {
|
fn __iter__(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view: self.view.clone(),
|
view: self.view.clone(),
|
||||||
@@ -999,6 +1047,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get the next item in the iteration
|
||||||
fn __next__<'py>(&mut self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyAny>>> {
|
fn __next__<'py>(&mut self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyAny>>> {
|
||||||
let shape = self.view.shape();
|
let shape = self.view.shape();
|
||||||
if shape.is_empty() || (self.index == shape[0]) {
|
if shape.is_empty() || (self.index == shape[0]) {
|
||||||
@@ -1030,14 +1079,17 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// number of elements in the first axis
|
||||||
fn __len__(&self) -> PyResult<usize> {
|
fn __len__(&self) -> PyResult<usize> {
|
||||||
Ok(self.view.len())
|
Ok(self.view.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// string representation with a summary of the image
|
||||||
fn __repr__(&self) -> PyResult<String> {
|
fn __repr__(&self) -> PyResult<String> {
|
||||||
Ok(self.view.summary()?)
|
Ok(self.view.summary()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the file path as a string
|
||||||
fn __str__(&self) -> PyResult<String> {
|
fn __str__(&self) -> PyResult<String> {
|
||||||
Ok(self.view.path().display().to_string())
|
Ok(self.view.path().display().to_string())
|
||||||
}
|
}
|
||||||
@@ -1119,6 +1171,7 @@ impl PyView {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// flatten the view into a 1D numpy array
|
||||||
fn flatten<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
fn flatten<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
||||||
Ok(match self.dtype {
|
Ok(match self.dtype {
|
||||||
PixelType::I8 => self.view.flatten::<i8>()?.into_pyarray(py).into_any(),
|
PixelType::I8 => self.view.flatten::<i8>()?.into_pyarray(py).into_any(),
|
||||||
@@ -1137,6 +1190,7 @@ impl PyView {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// convert the view to bytes
|
||||||
fn to_bytes(&self) -> PyResult<Vec<u8>> {
|
fn to_bytes(&self) -> PyResult<Vec<u8>> {
|
||||||
Ok(match self.dtype {
|
Ok(match self.dtype {
|
||||||
PixelType::I8 => self.view.to_bytes::<i8>()?,
|
PixelType::I8 => self.view.to_bytes::<i8>()?,
|
||||||
@@ -1155,6 +1209,7 @@ impl PyView {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// convert the view to bytes (alias for to_bytes)
|
||||||
fn tobytes(&self) -> PyResult<Vec<u8>> {
|
fn tobytes(&self) -> PyResult<Vec<u8>> {
|
||||||
self.to_bytes()
|
self.to_bytes()
|
||||||
}
|
}
|
||||||
@@ -1192,6 +1247,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the current slice applied to the view
|
||||||
#[getter]
|
#[getter]
|
||||||
fn slice(&self) -> PyResult<Vec<String>> {
|
fn slice(&self) -> PyResult<Vec<String>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
@@ -1283,6 +1339,7 @@ impl PyView {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// transposed view (alias for transpose(None))
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[getter]
|
#[getter]
|
||||||
fn T(&self) -> PyResult<PyView> {
|
fn T(&self) -> PyResult<PyView> {
|
||||||
@@ -1308,6 +1365,7 @@ impl PyView {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the numpy dtype of the view
|
||||||
#[gen_stub(override_return_type(type_repr = "numpy.dtype", imports=("numpy")))]
|
#[gen_stub(override_return_type(type_repr = "numpy.dtype", imports=("numpy")))]
|
||||||
#[getter]
|
#[getter]
|
||||||
fn get_dtype<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyArrayDescr>> {
|
fn get_dtype<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyArrayDescr>> {
|
||||||
@@ -1332,6 +1390,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set the dtype of the view
|
||||||
#[gen_stub(skip)]
|
#[gen_stub(skip)]
|
||||||
#[setter]
|
#[setter]
|
||||||
fn set_dtype(&mut self, py: Python, dtype: Bound<'_, PyAny>) -> PyResult<()> {
|
fn set_dtype(&mut self, py: Python, dtype: Bound<'_, PyAny>) -> PyResult<()> {
|
||||||
@@ -1451,6 +1510,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get the mean overall or along a given axis
|
||||||
#[gen_stub(skip)]
|
#[gen_stub(skip)]
|
||||||
#[pyo3(signature = (axis=None, dtype=None, out=None, keepdims=false, *, r#where=true), text_signature = "axis: str | int"
|
#[pyo3(signature = (axis=None, dtype=None, out=None, keepdims=false, *, r#where=true), text_signature = "axis: str | int"
|
||||||
)]
|
)]
|
||||||
@@ -1553,6 +1613,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether the view contains a z-stack (more than one z slice)
|
||||||
#[getter]
|
#[getter]
|
||||||
fn z_stack(&self) -> PyResult<bool> {
|
fn z_stack(&self) -> PyResult<bool> {
|
||||||
if let Some(s) = self.view.size_ax(Axis::Z) {
|
if let Some(s) = self.view.size_ax(Axis::Z) {
|
||||||
@@ -1572,6 +1633,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether the view contains a time series (more than one time point)
|
||||||
#[getter]
|
#[getter]
|
||||||
fn time_series(&self) -> PyResult<bool> {
|
fn time_series(&self) -> PyResult<bool> {
|
||||||
if let Some(s) = self.view.size_ax(Axis::T) {
|
if let Some(s) = self.view.size_ax(Axis::T) {
|
||||||
@@ -1591,6 +1653,7 @@ impl PyView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the pixel size in micrometers
|
||||||
#[getter]
|
#[getter]
|
||||||
fn pixel_size(&self) -> PyResult<Option<f64>> {
|
fn pixel_size(&self) -> PyResult<Option<f64>> {
|
||||||
Ok(self.ome.pixel_size()?)
|
Ok(self.ome.pixel_size()?)
|
||||||
@@ -1608,11 +1671,13 @@ impl PyView {
|
|||||||
Ok(self.ome.delta_z()?.map(|p| p / 1000.))
|
Ok(self.ome.delta_z()?.map(|p| p / 1000.))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the z-step size in micrometers
|
||||||
#[getter]
|
#[getter]
|
||||||
fn delta_z(&self) -> PyResult<Option<f64>> {
|
fn delta_z(&self) -> PyResult<Option<f64>> {
|
||||||
Ok(self.ome.delta_z()?)
|
Ok(self.ome.delta_z()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the time interval between frames in seconds
|
||||||
#[getter]
|
#[getter]
|
||||||
fn time_interval(&self) -> PyResult<Option<f64>> {
|
fn time_interval(&self) -> PyResult<Option<f64>> {
|
||||||
Ok(self.ome.time_interval()?)
|
Ok(self.ome.time_interval()?)
|
||||||
@@ -1624,6 +1689,7 @@ impl PyView {
|
|||||||
Ok(self.ome.time_interval()?)
|
Ok(self.ome.time_interval()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the exposure time for a given channel in seconds
|
||||||
fn exposure_time(&self, channel: usize) -> PyResult<Option<f64>> {
|
fn exposure_time(&self, channel: usize) -> PyResult<Option<f64>> {
|
||||||
Ok(self.ome.exposure_time(channel)?)
|
Ok(self.ome.exposure_time(channel)?)
|
||||||
}
|
}
|
||||||
@@ -1636,37 +1702,45 @@ impl PyView {
|
|||||||
.collect::<Result<Vec<_>, Error>>()?)
|
.collect::<Result<Vec<_>, Error>>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the binning for a given channel
|
||||||
fn binning(&self, channel: usize) -> Option<usize> {
|
fn binning(&self, channel: usize) -> Option<usize> {
|
||||||
self.ome.binning(channel)
|
self.ome.binning(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the laser wavelength for a given channel in nanometers
|
||||||
fn laser_wavelengths(&self, channel: usize) -> PyResult<Option<f64>> {
|
fn laser_wavelengths(&self, channel: usize) -> PyResult<Option<f64>> {
|
||||||
Ok(self.ome.laser_wavelengths(channel)?)
|
Ok(self.ome.laser_wavelengths(channel)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the laser power for a given channel as a fraction
|
||||||
fn laser_power(&self, channel: usize) -> PyResult<Option<f64>> {
|
fn laser_power(&self, channel: usize) -> PyResult<Option<f64>> {
|
||||||
Ok(self.ome.laser_powers(channel)?)
|
Ok(self.ome.laser_powers(channel)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the name of the objective
|
||||||
#[getter]
|
#[getter]
|
||||||
fn objective_name(&self) -> Option<String> {
|
fn objective_name(&self) -> Option<String> {
|
||||||
self.ome.objective_name()
|
self.ome.objective_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the total magnification (objective × tube lens)
|
||||||
#[getter]
|
#[getter]
|
||||||
fn magnification(&self) -> Option<f64> {
|
fn magnification(&self) -> Option<f64> {
|
||||||
self.ome.magnification()
|
self.ome.magnification()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the name of the tube lens
|
||||||
#[getter]
|
#[getter]
|
||||||
fn tube_lens_name(&self) -> Option<String> {
|
fn tube_lens_name(&self) -> Option<String> {
|
||||||
self.ome.tube_lens_name()
|
self.ome.tube_lens_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the name of the filter set for a given channel
|
||||||
fn filter_set_name(&self, channel: usize) -> Option<String> {
|
fn filter_set_name(&self, channel: usize) -> Option<String> {
|
||||||
self.ome.filter_set_name(channel)
|
self.ome.filter_set_name(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the detector gain for a given channel
|
||||||
fn gain(&self, channel: usize) -> Option<f64> {
|
fn gain(&self, channel: usize) -> Option<f64> {
|
||||||
self.ome.gain(channel)
|
self.ome.gain(channel)
|
||||||
}
|
}
|
||||||
@@ -1730,6 +1804,7 @@ impl PyView {
|
|||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// save the view as a TIFF file
|
||||||
#[cfg(feature = "tiffwrite")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
#[pyo3(signature = (file, colors = None, overwrite = false, bar = true))]
|
#[pyo3(signature = (file, colors = None, overwrite = false, bar = true))]
|
||||||
fn save_as_tiff(
|
fn save_as_tiff(
|
||||||
@@ -1770,6 +1845,7 @@ impl PyView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// save the view as a movie file (MP4)
|
||||||
#[cfg(feature = "movie")]
|
#[cfg(feature = "movie")]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[pyo3(signature = (file, speed = 1.0, brightness = None, scale = 1.0, colors = None, overwrite = false, register = false, no_scaling = false)
|
#[pyo3(signature = (file, speed = 1.0, brightness = None, scale = 1.0, colors = None, overwrite = false, register = false, no_scaling = false)
|
||||||
@@ -1813,21 +1889,22 @@ submit! {
|
|||||||
def __pow__(other: Imread | numpy.typing.NDArray | int | float, mod: typing.Any = None, /) -> numpy.typing.NDArray: ...
|
def __pow__(other: Imread | numpy.typing.NDArray | int | float, mod: typing.Any = None, /) -> numpy.typing.NDArray: ...
|
||||||
def __rpow__(other: Imread | numpy.typing.NDArray | int | float, mod: typing.Any = None, /) -> numpy.typing.NDArray: ...
|
def __rpow__(other: Imread | numpy.typing.NDArray | int | float, mod: typing.Any = None, /) -> numpy.typing.NDArray: ...
|
||||||
|
|
||||||
def max(axis: int | str = None, dtype: numpy.typing.DTypeLike = None, out: typing.Any = None, keepdims: bool = False, initial: int | float = None, where: bool = True) -> Imread | numpy.typing.NDArray | int | float:
|
def max(axis: int | str = None, dtype: typing.Optional[numpy.typing.DTypeLike] = None, out: typing.Any = None, keepdims: bool = False, initial: int | float = None, where: bool = True) -> Imread | numpy.typing.NDArray | int | float:
|
||||||
""" Return the maximum along a given axis. Arguments beyond axis are not implemented """
|
""" Return the maximum along a given axis. Arguments beyond axis are not implemented """
|
||||||
|
|
||||||
def min(axis: int | str = None, dtype: numpy.typing.DTypeLike = None, out: typing.Any = None, keepdims: bool = False, initial: int | float = None, where: bool = True) -> Imread | numpy.typing.NDArray | int | float:
|
def min(axis: int | str = None, dtype: typing.Optional[numpy.typing.DTypeLike] = None, out: typing.Any = None, keepdims: bool = False, initial: int | float = None, where: bool = True) -> Imread | numpy.typing.NDArray | int | float:
|
||||||
""" Return the minimum along a given axis. Arguments beyond axis are not implemented """
|
""" Return the minimum along a given axis. Arguments beyond axis are not implemented """
|
||||||
|
|
||||||
def mean(axis: int | str = None, dtype: numpy.typing.DTypeLike = None, out: typing.Any = None, keepdims: bool = False) -> Imread | numpy.typing.NDArray | int | float:
|
def mean(axis: int | str = None, dtype: typing.Optional[numpy.typing.DTypeLike] = None, out: typing.Any = None, keepdims: bool = False) -> Imread | numpy.typing.NDArray | int | float:
|
||||||
""" Return the mean along a given axis. Arguments beyond axis are not implemented """
|
""" Return the mean along a given axis. Arguments beyond axis are not implemented """
|
||||||
|
|
||||||
def sum(axis: int | str = None, dtype: numpy.typing.DTypeLike = None, out: typing.Any = None, keepdims: bool = False, initial: int | float = None, where: bool = True) -> Imread | numpy.typing.NDArray | int | float:
|
def sum(axis: int | str = None, dtype: typing.Optional[numpy.typing.DTypeLike] = None, out: typing.Any = None, keepdims: bool = False, initial: int | float = None, where: bool = True) -> Imread | numpy.typing.NDArray | int | float:
|
||||||
""" Return the sum along a given axis. Arguments beyond axis are not implemented """
|
""" Return the sum along a given axis. Arguments beyond axis are not implemented """
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// batch convert multiple image files to TIFF format
|
||||||
#[cfg(feature = "tiffwrite")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[gen_stub_pyfunction(module = "ndbioimage.ndbioimage_rs")]
|
#[gen_stub_pyfunction(module = "ndbioimage.ndbioimage_rs")]
|
||||||
@@ -1852,6 +1929,7 @@ fn batch_to_tiff(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// represents the shape of an image with named dimensions (c, z, t, y, x)
|
||||||
#[gen_stub_pyclass]
|
#[gen_stub_pyclass]
|
||||||
#[pyclass(
|
#[pyclass(
|
||||||
subclass,
|
subclass,
|
||||||
@@ -1868,6 +1946,7 @@ struct PyShape {
|
|||||||
#[gen_stub_pymethods]
|
#[gen_stub_pymethods]
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyShape {
|
impl PyShape {
|
||||||
|
/// create a new shape with the given dimensions
|
||||||
#[new]
|
#[new]
|
||||||
#[pyo3(signature = (order, c = 1, z = 1, t = 1, y = 1, x = 1))]
|
#[pyo3(signature = (order, c = 1, z = 1, t = 1, y = 1, x = 1))]
|
||||||
fn new(order: String, c: usize, z: usize, t: usize, y: usize, x: usize) -> PyResult<Self> {
|
fn new(order: String, c: usize, z: usize, t: usize, y: usize, x: usize) -> PyResult<Self> {
|
||||||
@@ -1887,35 +1966,42 @@ impl PyShape {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the number of channels
|
||||||
#[getter]
|
#[getter]
|
||||||
fn get_c(&self) -> usize {
|
fn get_c(&self) -> usize {
|
||||||
self.inner.c
|
self.inner.c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the number of z slices
|
||||||
#[getter]
|
#[getter]
|
||||||
fn get_z(&self) -> usize {
|
fn get_z(&self) -> usize {
|
||||||
self.inner.z
|
self.inner.z
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the number of time points
|
||||||
#[getter]
|
#[getter]
|
||||||
fn get_t(&self) -> usize {
|
fn get_t(&self) -> usize {
|
||||||
self.inner.t
|
self.inner.t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the number of pixels along y
|
||||||
#[getter]
|
#[getter]
|
||||||
fn get_y(&self) -> usize {
|
fn get_y(&self) -> usize {
|
||||||
self.inner.y
|
self.inner.y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the number of pixels along x
|
||||||
#[getter]
|
#[getter]
|
||||||
fn get_x(&self) -> usize {
|
fn get_x(&self) -> usize {
|
||||||
self.inner.x
|
self.inner.x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// string representation
|
||||||
fn __str__(&self) -> String {
|
fn __str__(&self) -> String {
|
||||||
format!("{}", self.inner)
|
format!("{}", self.inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// detailed representation for debugging
|
||||||
fn __repr__(&self) -> String {
|
fn __repr__(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"Shape({}, {}, {}, {}, {})",
|
"Shape({}, {}, {}, {}, {})",
|
||||||
@@ -1923,6 +2009,7 @@ impl PyShape {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// arguments for pickling
|
||||||
fn __getnewargs__(&self) -> (String, usize, usize, usize, usize, usize) {
|
fn __getnewargs__(&self) -> (String, usize, usize, usize, usize, usize) {
|
||||||
(
|
(
|
||||||
self.inner
|
self.inner
|
||||||
@@ -1939,6 +2026,7 @@ impl PyShape {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get dimension size by index or axis name
|
||||||
#[gen_stub(override_return_type(type_repr="typing.Optional[int | list[int]]", imports=("typing")
|
#[gen_stub(override_return_type(type_repr="typing.Optional[int | list[int]]", imports=("typing")
|
||||||
))]
|
))]
|
||||||
fn __getitem__<'py>(
|
fn __getitem__<'py>(
|
||||||
@@ -2034,10 +2122,12 @@ impl PyShape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// number of dimensions in the shape
|
||||||
fn __len__(&self) -> usize {
|
fn __len__(&self) -> usize {
|
||||||
self.inner.order.len()
|
self.inner.order.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// convert shape to a list of dimension sizes in order
|
||||||
fn to_list(&self) -> Vec<usize> {
|
fn to_list(&self) -> Vec<usize> {
|
||||||
vec![
|
vec![
|
||||||
self.inner.c,
|
self.inner.c,
|
||||||
@@ -2048,6 +2138,7 @@ impl PyShape {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the axis order as a string (e.g., "CZTYX")
|
||||||
#[getter]
|
#[getter]
|
||||||
fn axes(&self) -> String {
|
fn axes(&self) -> String {
|
||||||
self.inner
|
self.inner
|
||||||
@@ -2081,6 +2172,7 @@ pub fn generate_stub(dest_path: String) -> PyResult<()> {
|
|||||||
.generate()?)
|
.generate()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// main entry point for the command-line interface
|
||||||
#[gen_stub_pyfunction(module = "ndbioimage.ndbioimage_rs")]
|
#[gen_stub_pyfunction(module = "ndbioimage.ndbioimage_rs")]
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn main() -> PyResult<()> {
|
fn main() -> PyResult<()> {
|
||||||
|
|||||||
+65
-4
@@ -14,33 +14,45 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
/// czi file reader
|
||||||
#[cfg(feature = "czi")]
|
#[cfg(feature = "czi")]
|
||||||
pub mod czi;
|
pub mod czi;
|
||||||
|
|
||||||
|
/// bioformats reader (pure rust)
|
||||||
#[cfg(feature = "bioformats_rust")]
|
#[cfg(feature = "bioformats_rust")]
|
||||||
pub mod bioformats_rust;
|
pub mod bioformats_rust;
|
||||||
|
|
||||||
|
/// bioformats reader (java bindings)
|
||||||
#[cfg(feature = "bioformats_java")]
|
#[cfg(feature = "bioformats_java")]
|
||||||
pub mod bioformats_java;
|
pub mod bioformats_java;
|
||||||
|
|
||||||
|
/// tiff sequence reader
|
||||||
#[cfg(feature = "tiffseq")]
|
#[cfg(feature = "tiffseq")]
|
||||||
pub mod tiffseq;
|
pub mod tiffseq;
|
||||||
|
|
||||||
|
/// single tiff file reader
|
||||||
#[cfg(feature = "tiff")]
|
#[cfg(feature = "tiff")]
|
||||||
pub mod tiff;
|
pub mod tiff;
|
||||||
|
|
||||||
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^([CZTSP])\D+(\d+)$").unwrap());
|
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^([CZTSP])\D+(\d+)$").unwrap());
|
||||||
|
|
||||||
|
/// dimensions of the image data
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct Dimensions {
|
pub struct Dimensions {
|
||||||
|
/// number of channels
|
||||||
pub c: Option<usize>,
|
pub c: Option<usize>,
|
||||||
|
/// number of z slices
|
||||||
pub z: Option<usize>,
|
pub z: Option<usize>,
|
||||||
|
/// number of time points
|
||||||
pub t: Option<usize>,
|
pub t: Option<usize>,
|
||||||
|
/// series index
|
||||||
pub s: Option<usize>,
|
pub s: Option<usize>,
|
||||||
|
/// position index
|
||||||
pub p: Option<usize>,
|
pub p: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dimensions {
|
impl Dimensions {
|
||||||
|
/// create dimensions with the given series and position
|
||||||
pub fn new(series: usize, position: usize) -> Self {
|
pub fn new(series: usize, position: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
c: None,
|
c: None,
|
||||||
@@ -51,6 +63,7 @@ impl Dimensions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// parse a path and extract dimensions from the directory structure
|
||||||
pub fn parse_path<P>(path: P) -> Result<(PathBuf, Self), Error>
|
pub fn parse_path<P>(path: P) -> Result<(PathBuf, Self), Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@@ -86,26 +99,40 @@ impl Dimensions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pixel types (u)int(8/16/32) or float(32/64), (u/i)(64/128) are not included in bioformats
|
/// pixel type enum
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub enum PixelType {
|
pub enum PixelType {
|
||||||
|
/// signed 8-bit integer
|
||||||
I8,
|
I8,
|
||||||
|
/// unsigned 8-bit integer
|
||||||
U8,
|
U8,
|
||||||
|
/// signed 16-bit integer
|
||||||
I16,
|
I16,
|
||||||
|
/// unsigned 16-bit integer
|
||||||
U16,
|
U16,
|
||||||
|
/// signed 32-bit integer
|
||||||
I32,
|
I32,
|
||||||
|
/// unsigned 32-bit integer
|
||||||
U32,
|
U32,
|
||||||
|
/// 32-bit float
|
||||||
F32,
|
F32,
|
||||||
|
/// 64-bit float
|
||||||
F64,
|
F64,
|
||||||
|
/// signed 64-bit integer
|
||||||
I64,
|
I64,
|
||||||
|
/// unsigned 64-bit integer
|
||||||
U64,
|
U64,
|
||||||
|
/// signed 128-bit integer
|
||||||
I128,
|
I128,
|
||||||
|
/// unsigned 128-bit integer
|
||||||
U128,
|
U128,
|
||||||
|
/// 128-bit float (emulated)
|
||||||
F128,
|
F128,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PixelType {
|
impl PixelType {
|
||||||
|
/// number of bytes per pixel for this type
|
||||||
pub fn bytes_per_pixel(&self) -> usize {
|
pub fn bytes_per_pixel(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
PixelType::I8 | PixelType::U8 => 1,
|
PixelType::I8 | PixelType::U8 => 1,
|
||||||
@@ -117,37 +144,55 @@ impl PixelType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Struct containing frame data in one of eight pixel types. Cast to `Array2<T>` using try_into.
|
/// array data with a specific pixel type
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ArrayT<D: Dimension> {
|
pub enum ArrayT<D: Dimension> {
|
||||||
|
/// signed 8-bit integer array
|
||||||
I8(Array<i8, D>),
|
I8(Array<i8, D>),
|
||||||
|
/// unsigned 8-bit integer array
|
||||||
U8(Array<u8, D>),
|
U8(Array<u8, D>),
|
||||||
|
/// signed 16-bit integer array
|
||||||
I16(Array<i16, D>),
|
I16(Array<i16, D>),
|
||||||
|
/// unsigned 16-bit integer array
|
||||||
U16(Array<u16, D>),
|
U16(Array<u16, D>),
|
||||||
|
/// signed 32-bit integer array
|
||||||
I32(Array<i32, D>),
|
I32(Array<i32, D>),
|
||||||
|
/// unsigned 32-bit integer array
|
||||||
U32(Array<u32, D>),
|
U32(Array<u32, D>),
|
||||||
|
/// 32-bit float array
|
||||||
F32(Array<f32, D>),
|
F32(Array<f32, D>),
|
||||||
|
/// 64-bit float array
|
||||||
F64(Array<f64, D>),
|
F64(Array<f64, D>),
|
||||||
|
/// signed 64-bit integer array
|
||||||
I64(Array<i64, D>),
|
I64(Array<i64, D>),
|
||||||
|
/// unsigned 64-bit integer array
|
||||||
U64(Array<u64, D>),
|
U64(Array<u64, D>),
|
||||||
|
/// signed 128-bit integer array
|
||||||
I128(Array<i128, D>),
|
I128(Array<i128, D>),
|
||||||
|
/// unsigned 128-bit integer array
|
||||||
U128(Array<u128, D>),
|
U128(Array<u128, D>),
|
||||||
F128(Array<f64, D>), // f128 is nightly
|
/// 128-bit float array (emulated as f64)
|
||||||
|
F128(Array<f64, D>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// type alias for a single frame (2D image)
|
||||||
pub type Frame = ArrayT<Ix2>;
|
pub type Frame = ArrayT<Ix2>;
|
||||||
|
|
||||||
|
/// trait for reading image files
|
||||||
pub trait Reader: Clone + Sized + Debug + Send + Hash + Into<DynReader> {
|
pub trait Reader: Clone + Sized + Debug + Send + Hash + Into<DynReader> {
|
||||||
|
/// create a new reader for the given path, series, and position
|
||||||
fn new<P>(path: P, series: usize, position: usize) -> Result<Self, Error>
|
fn new<P>(path: P, series: usize, position: usize) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>;
|
P: AsRef<Path>;
|
||||||
|
|
||||||
|
/// the name of the reader type
|
||||||
fn reader_name(&self) -> &'static str {
|
fn reader_name(&self) -> &'static str {
|
||||||
type_name::<Self>()
|
type_name::<Self>()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: read from file if present
|
// TODO: read from file if present
|
||||||
|
/// get the ome metadata for the image
|
||||||
fn metadata(&self) -> Result<Ome, Error>;
|
fn metadata(&self) -> Result<Ome, Error>;
|
||||||
|
|
||||||
/// get a sliceable view on the image file
|
/// get a sliceable view on the image file
|
||||||
@@ -161,18 +206,25 @@ pub trait Reader: Clone + Sized + Debug + Send + Hash + Into<DynReader> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve fame at channel c, slize z and time t.
|
/// retrieve frame at channel c, slice z and time t
|
||||||
#[allow(clippy::if_same_then_else)]
|
#[allow(clippy::if_same_then_else)]
|
||||||
fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame, Error>;
|
fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame, Error>;
|
||||||
|
|
||||||
|
/// the path to the image file
|
||||||
fn path(&self) -> &Path;
|
fn path(&self) -> &Path;
|
||||||
|
/// the series index
|
||||||
fn series(&self) -> usize;
|
fn series(&self) -> usize;
|
||||||
|
/// the position index
|
||||||
fn position(&self) -> usize;
|
fn position(&self) -> usize;
|
||||||
|
/// the shape of the image data
|
||||||
fn shape(&self) -> &Shape;
|
fn shape(&self) -> &Shape;
|
||||||
|
/// the pixel type of the image data
|
||||||
fn pixel_type(&self) -> &PixelType;
|
fn pixel_type(&self) -> &PixelType;
|
||||||
|
/// get all available positions for a given series
|
||||||
fn get_available_positions<P>(path: P, series: usize) -> Result<HashSet<usize>, Error>
|
fn get_available_positions<P>(path: P, series: usize) -> Result<HashSet<usize>, Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>;
|
P: AsRef<Path>;
|
||||||
|
/// get all available series
|
||||||
fn get_available_series<P>(path: P) -> Result<HashSet<usize>, Error>
|
fn get_available_series<P>(path: P) -> Result<HashSet<usize>, Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>;
|
P: AsRef<Path>;
|
||||||
@@ -365,16 +417,22 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// dynamic reader that can handle multiple file formats
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
|
||||||
pub enum DynReader {
|
pub enum DynReader {
|
||||||
|
/// tiff file reader
|
||||||
#[cfg(feature = "tiff")]
|
#[cfg(feature = "tiff")]
|
||||||
Tiff(tiff::TiffReader),
|
Tiff(tiff::TiffReader),
|
||||||
|
/// tiff sequence reader
|
||||||
#[cfg(feature = "tiffseq")]
|
#[cfg(feature = "tiffseq")]
|
||||||
TiffSeq(tiffseq::TiffSeqReader),
|
TiffSeq(tiffseq::TiffSeqReader),
|
||||||
|
/// czi file reader
|
||||||
#[cfg(feature = "czi")]
|
#[cfg(feature = "czi")]
|
||||||
Czi(czi::CziReader),
|
Czi(czi::CziReader),
|
||||||
|
/// bioformats reader (pure rust)
|
||||||
#[cfg(feature = "bioformats_rust")]
|
#[cfg(feature = "bioformats_rust")]
|
||||||
BioFormatsRust(bioformats_rust::BioFormatsRustReader),
|
BioFormatsRust(bioformats_rust::BioFormatsRustReader),
|
||||||
|
/// bioformats reader (java bindings)
|
||||||
#[cfg(feature = "bioformats_java")]
|
#[cfg(feature = "bioformats_java")]
|
||||||
BioFormatsJava(bioformats_java::BioFormatsJavaReader),
|
BioFormatsJava(bioformats_java::BioFormatsJavaReader),
|
||||||
}
|
}
|
||||||
@@ -626,6 +684,7 @@ impl Reader for DynReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DynReader {
|
impl DynReader {
|
||||||
|
/// create a dynreader by selecting the reader type explicitly
|
||||||
pub fn from_path_select_reader<P, R>(path: P, reader: R) -> Result<DynReader, Error>
|
pub fn from_path_select_reader<P, R>(path: P, reader: R) -> Result<DynReader, Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@@ -674,6 +733,7 @@ impl DynReader {
|
|||||||
Ok(reader)
|
Ok(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get all available positions, optionally selecting a specific reader type
|
||||||
pub fn get_available_positions_select_reader<P, R>(
|
pub fn get_available_positions_select_reader<P, R>(
|
||||||
path: P,
|
path: P,
|
||||||
series: usize,
|
series: usize,
|
||||||
@@ -709,6 +769,7 @@ impl DynReader {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get all available series, optionally selecting a specific reader type
|
||||||
pub fn get_available_series_select_reader<P, R>(
|
pub fn get_available_series_select_reader<P, R>(
|
||||||
path: P,
|
path: P,
|
||||||
reader: Option<R>,
|
reader: Option<R>,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use thread_local::ThreadLocal;
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
|
/// reader for czi (zeiss) image files
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct CziReader {
|
pub struct CziReader {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -121,8 +122,10 @@ impl From<Option<String>> for Version {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// errors specific to czi file reading
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum CziError {
|
pub enum CziError {
|
||||||
|
/// czi file has no valid blocks
|
||||||
#[error("czi file has no valid blocks")]
|
#[error("czi file has no valid blocks")]
|
||||||
NoValidBlocks,
|
NoValidBlocks,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use thread_local::ThreadLocal;
|
|||||||
use tiff::decoder::{Decoder, DecodingResult};
|
use tiff::decoder::{Decoder, DecodingResult};
|
||||||
use tiff::tags::Tag;
|
use tiff::tags::Tag;
|
||||||
|
|
||||||
|
/// reader for single tiff image files
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct TiffReader {
|
pub struct TiffReader {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use std::str::FromStr;
|
|||||||
use tiff::decoder::{Decoder, DecodingResult};
|
use tiff::decoder::{Decoder, DecodingResult};
|
||||||
use tiff::tags::Tag;
|
use tiff::tags::Tag;
|
||||||
|
|
||||||
|
/// reader for sequences of tiff files (one file per z/time plane)
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct TiffSeqReader {
|
pub struct TiffSeqReader {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|||||||
@@ -3,11 +3,16 @@ use ndarray::{Array, ArrayD, ArrayView, Axis, Dimension, RemoveAxis};
|
|||||||
|
|
||||||
/// a trait to define the min, max, sum and mean operations along an axis
|
/// a trait to define the min, max, sum and mean operations along an axis
|
||||||
pub trait MinMax {
|
pub trait MinMax {
|
||||||
|
/// the type of the result after reducing one axis
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
|
/// the max of the array along `axis`
|
||||||
fn max(self, axis: usize) -> Result<Self::Output, Error>;
|
fn max(self, axis: usize) -> Result<Self::Output, Error>;
|
||||||
|
/// the min of the array along `axis`
|
||||||
fn min(self, axis: usize) -> Result<Self::Output, Error>;
|
fn min(self, axis: usize) -> Result<Self::Output, Error>;
|
||||||
|
/// the sum of the array along `axis`
|
||||||
fn sum(self, axis: usize) -> Result<Self::Output, Error>;
|
fn sum(self, axis: usize) -> Result<Self::Output, Error>;
|
||||||
|
/// the mean of the array along `axis`
|
||||||
fn mean(self, axis: usize) -> Result<Self::Output, Error>;
|
fn mean(self, axis: usize) -> Result<Self::Output, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::sync::{Arc, Condvar, Mutex};
|
use std::sync::{Arc, Condvar, Mutex};
|
||||||
use tiffwrite::{Bytes, Colors, Compression, IJTiffFile};
|
use tiffwrite::{Bytes, Colors, Compression, IJTiffFile};
|
||||||
|
|
||||||
|
/// options for saving tiff files
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TiffOptions {
|
pub struct TiffOptions {
|
||||||
bar: Option<ProgressBar>,
|
bar: Option<ProgressBar>,
|
||||||
@@ -34,6 +35,7 @@ impl Default for TiffOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TiffOptions {
|
impl TiffOptions {
|
||||||
|
/// create tiff options with the given parameters
|
||||||
pub fn new(
|
pub fn new(
|
||||||
bar: Option<ProgressBar>,
|
bar: Option<ProgressBar>,
|
||||||
compression: Option<Compression>,
|
compression: Option<Compression>,
|
||||||
@@ -77,6 +79,7 @@ impl TiffOptions {
|
|||||||
self.compression = Compression::Deflate
|
self.compression = Compression::Deflate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set the color lookup table for the tiff
|
||||||
pub fn set_colors(&mut self, colors: &[String]) -> Result<(), Error> {
|
pub fn set_colors(&mut self, colors: &[String]) -> Result<(), Error> {
|
||||||
let colors = colors
|
let colors = colors
|
||||||
.iter()
|
.iter()
|
||||||
@@ -86,6 +89,7 @@ impl TiffOptions {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set whether an existing tiff file should be overwritten
|
||||||
pub fn set_overwrite(&mut self, overwrite: bool) {
|
pub fn set_overwrite(&mut self, overwrite: bool) {
|
||||||
self.overwrite = overwrite;
|
self.overwrite = overwrite;
|
||||||
}
|
}
|
||||||
@@ -180,6 +184,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// batch convert multiple files to tiff format
|
||||||
pub fn batch_to_tiff(
|
pub fn batch_to_tiff(
|
||||||
files_in: &[PathBuf],
|
files_in: &[PathBuf],
|
||||||
files_out: &[PathBuf],
|
files_out: &[PathBuf],
|
||||||
|
|||||||
+14
-319
@@ -1,9 +1,10 @@
|
|||||||
use crate::axes::{Ax, Axis, Operation, Shape, Slice, SliceInfoElemDef, slice_info};
|
use crate::axes::{Ax, Axis, Operation, Shape, Slice, SliceInfoElemDef, slice_info};
|
||||||
|
use crate::cache::{ArrayCache, ArrayKeyRef, FrameCache, FrameKeyRef, ReaderKeyRef};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::metadata::Metadata;
|
use crate::metadata::Metadata;
|
||||||
use crate::readers::{Dimensions, DynReader, Frame, Reader};
|
use crate::readers::{Dimensions, DynReader, Frame, Reader};
|
||||||
use crate::stats::MinMax;
|
use crate::stats::MinMax;
|
||||||
use indexmap::{Equivalent, IndexMap};
|
use indexmap::IndexMap;
|
||||||
use itertools::{Itertools, iproduct};
|
use itertools::{Itertools, iproduct};
|
||||||
use ndarray::{
|
use ndarray::{
|
||||||
Array, Array0, Array1, Array2, ArrayD, Dimension, IntoDimension, Ix0, Ix1, Ix2, Ix5, IxDyn,
|
Array, Array0, Array1, Array2, ArrayD, Dimension, IntoDimension, Ix0, Ix1, Ix2, Ix5, IxDyn,
|
||||||
@@ -13,15 +14,15 @@ use num::traits::ToBytes;
|
|||||||
use num::{Bounded, FromPrimitive, ToPrimitive, Zero};
|
use num::{Bounded, FromPrimitive, ToPrimitive, Zero};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::serde_as;
|
use serde_with::serde_as;
|
||||||
use std::any::{Any, type_name};
|
use std::any::type_name;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::iter::Sum;
|
use std::iter::Sum;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{AddAssign, Deref, Div};
|
use std::ops::{AddAssign, Deref, Div};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Mutex, OnceLock};
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn idx_bnd(idx: isize, bnd: isize) -> Result<isize, Error> {
|
fn idx_bnd(idx: isize, bnd: isize) -> Result<isize, Error> {
|
||||||
if idx < -bnd {
|
if idx < -bnd {
|
||||||
@@ -47,6 +48,7 @@ fn slc_bnd(idx: isize, bnd: isize) -> Result<isize, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a trait for numeric types that can be used as pixel values
|
||||||
pub trait Number:
|
pub trait Number:
|
||||||
'static
|
'static
|
||||||
+ Send
|
+ Send
|
||||||
@@ -74,318 +76,6 @@ impl<T> Number for T where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// maximum number of frames held in the cache
|
|
||||||
const DEFAULT_FRAME_CACHE_SIZE: usize = 128;
|
|
||||||
|
|
||||||
/// maximum number of materialized arrays held in the cache
|
|
||||||
const DEFAULT_ARRAY_CACHE_SIZE: usize = 2;
|
|
||||||
|
|
||||||
/// identity of the reader a frame was read from
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
struct ReaderKey {
|
|
||||||
name: String,
|
|
||||||
path: PathBuf,
|
|
||||||
series: usize,
|
|
||||||
position: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// borrowed view of [`ReaderKey`] for cache lookups without allocation.
|
|
||||||
/// hashes byte-identically to [`ReaderKey`] (str/String and Path/PathBuf hash
|
|
||||||
/// the same), so it only matches the same reader identity.
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
struct ReaderKeyRef<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
path: &'a Path,
|
|
||||||
series: usize,
|
|
||||||
position: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for ReaderKeyRef<'_> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.name.hash(state);
|
|
||||||
self.path.hash(state);
|
|
||||||
self.series.hash(state);
|
|
||||||
self.position.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Equivalent<ReaderKey> for ReaderKeyRef<'_> {
|
|
||||||
fn equivalent(&self, key: &ReaderKey) -> bool {
|
|
||||||
self.name == key.name
|
|
||||||
&& self.path == key.path.as_path()
|
|
||||||
&& self.series == key.series
|
|
||||||
&& self.position == key.position
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReaderKeyRef<'_> {
|
|
||||||
fn to_owned(&self) -> ReaderKey {
|
|
||||||
ReaderKey {
|
|
||||||
name: self.name.to_string(),
|
|
||||||
path: self.path.to_path_buf(),
|
|
||||||
series: self.series,
|
|
||||||
position: self.position,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type FrameKey = (ReaderKey, usize, usize, usize);
|
|
||||||
|
|
||||||
/// borrowed view of [`FrameKey`] for cache lookups without allocation.
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
struct FrameKeyRef<'a> {
|
|
||||||
reader: ReaderKeyRef<'a>,
|
|
||||||
c: usize,
|
|
||||||
z: usize,
|
|
||||||
t: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for FrameKeyRef<'_> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.reader.hash(state);
|
|
||||||
self.c.hash(state);
|
|
||||||
self.z.hash(state);
|
|
||||||
self.t.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Equivalent<FrameKey> for FrameKeyRef<'_> {
|
|
||||||
fn equivalent(&self, key: &FrameKey) -> bool {
|
|
||||||
let (rk, c, z, t) = key;
|
|
||||||
self.reader.equivalent(rk) && self.c == *c && self.z == *z && self.t == *t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FrameKeyRef<'_> {
|
|
||||||
fn to_owned(&self) -> FrameKey {
|
|
||||||
(self.reader.to_owned(), self.c, self.z, self.t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// identity of a materialized array in the process-wide cache
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
struct ArrayKey {
|
|
||||||
reader: ReaderKey,
|
|
||||||
dtype: &'static str,
|
|
||||||
slice: Vec<SliceInfoElem>,
|
|
||||||
axes: Vec<Axis>,
|
|
||||||
operations: Vec<(Axis, Operation)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// borrowed view of [`ArrayKey`] for cache lookups without allocation.
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
struct ArrayKeyRef<'a> {
|
|
||||||
reader: ReaderKeyRef<'a>,
|
|
||||||
dtype: &'static str,
|
|
||||||
slice: &'a [SliceInfoElem],
|
|
||||||
axes: &'a [Axis],
|
|
||||||
operations: &'a IndexMap<Axis, Operation>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for ArrayKeyRef<'_> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.reader.hash(state);
|
|
||||||
self.dtype.hash(state);
|
|
||||||
self.slice.hash(state);
|
|
||||||
self.axes.hash(state);
|
|
||||||
self.operations.len().hash(state);
|
|
||||||
for (ax, op) in self.operations.iter() {
|
|
||||||
ax.hash(state);
|
|
||||||
op.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Equivalent<ArrayKey> for ArrayKeyRef<'_> {
|
|
||||||
fn equivalent(&self, key: &ArrayKey) -> bool {
|
|
||||||
self.reader.equivalent(&key.reader)
|
|
||||||
&& self.dtype == key.dtype
|
|
||||||
&& self.slice == key.slice
|
|
||||||
&& self.axes == key.axes
|
|
||||||
&& self.operations.len() == key.operations.len()
|
|
||||||
&& self
|
|
||||||
.operations
|
|
||||||
.iter()
|
|
||||||
.zip(&key.operations)
|
|
||||||
.all(|((ax, op), (key_ax, key_op))| ax == key_ax && op == key_op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArrayKeyRef<'_> {
|
|
||||||
fn to_owned(&self) -> ArrayKey {
|
|
||||||
ArrayKey {
|
|
||||||
reader: self.reader.to_owned(),
|
|
||||||
dtype: self.dtype,
|
|
||||||
slice: self.slice.to_vec(),
|
|
||||||
axes: self.axes.to_vec(),
|
|
||||||
operations: self
|
|
||||||
.operations
|
|
||||||
.iter()
|
|
||||||
.map(|(ax, op)| (*ax, op.clone()))
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// process-wide LRU cache of frames, shared between all views and threads
|
|
||||||
static GLOBAL_FRAME_CACHE: OnceLock<FrameCache> = OnceLock::new();
|
|
||||||
|
|
||||||
/// thread-safe LRU cache of frames read from the underlying reader
|
|
||||||
struct FrameCache {
|
|
||||||
inner: Mutex<FrameCacheInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FrameCacheInner {
|
|
||||||
map: IndexMap<FrameKey, Arc<Frame>>,
|
|
||||||
capacity: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FrameCache {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(DEFAULT_FRAME_CACHE_SIZE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FrameCache {
|
|
||||||
fn new(capacity: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Mutex::new(FrameCacheInner {
|
|
||||||
map: IndexMap::with_capacity(capacity),
|
|
||||||
capacity,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn global() -> &'static FrameCache {
|
|
||||||
GLOBAL_FRAME_CACHE.get_or_init(FrameCache::default)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn capacity(&self) -> usize {
|
|
||||||
self.inner.lock().unwrap().capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_capacity(&self, capacity: usize) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.capacity = capacity;
|
|
||||||
while inner.map.len() > inner.capacity {
|
|
||||||
inner.map.shift_remove_index(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get<Q>(&self, key: &Q) -> Option<Arc<Frame>>
|
|
||||||
where
|
|
||||||
Q: ?Sized + Hash + Equivalent<FrameKey>,
|
|
||||||
{
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
if let Some(idx) = inner.map.get_index_of(key) {
|
|
||||||
let (key, frame) = inner.map.shift_remove_index(idx).unwrap();
|
|
||||||
inner.map.insert(key, frame.clone());
|
|
||||||
Some(frame)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert(&self, key: FrameKey, frame: Arc<Frame>) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.map.insert(key, frame);
|
|
||||||
while inner.map.len() > inner.capacity {
|
|
||||||
inner.map.shift_remove_index(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.inner.lock().unwrap().map.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for FrameCache {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("FrameCache").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// process-wide LRU cache of materialized arrays, shared between all views and threads
|
|
||||||
static GLOBAL_ARRAY_CACHE: OnceLock<ArrayCache> = OnceLock::new();
|
|
||||||
|
|
||||||
/// thread-safe LRU cache of materialized arrays produced by `as_array_dyn`
|
|
||||||
struct ArrayCache {
|
|
||||||
inner: Mutex<ArrayCacheInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ArrayCacheInner {
|
|
||||||
map: IndexMap<ArrayKey, Arc<dyn Any + Send + Sync>>,
|
|
||||||
capacity: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ArrayCache {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(DEFAULT_ARRAY_CACHE_SIZE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArrayCache {
|
|
||||||
fn new(capacity: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Mutex::new(ArrayCacheInner {
|
|
||||||
map: IndexMap::with_capacity(capacity),
|
|
||||||
capacity,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn global() -> &'static ArrayCache {
|
|
||||||
GLOBAL_ARRAY_CACHE.get_or_init(ArrayCache::default)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn capacity(&self) -> usize {
|
|
||||||
self.inner.lock().unwrap().capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_capacity(&self, capacity: usize) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.capacity = capacity;
|
|
||||||
while inner.map.len() > inner.capacity {
|
|
||||||
inner.map.shift_remove_index(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get<T, Q>(&self, key: &Q) -> Option<Arc<ArrayD<T>>>
|
|
||||||
where
|
|
||||||
Q: ?Sized + Hash + Equivalent<ArrayKey>,
|
|
||||||
T: Any + Send + Sync,
|
|
||||||
{
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
if let Some(idx) = inner.map.get_index_of(key) {
|
|
||||||
let (key, array) = inner.map.shift_remove_index(idx).unwrap();
|
|
||||||
inner.map.insert(key, array.clone());
|
|
||||||
array.downcast::<ArrayD<T>>().ok()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert<T: Any + Send + Sync>(&self, key: ArrayKey, array: ArrayD<T>) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.map.insert(key, Arc::new(array));
|
|
||||||
while inner.map.len() > inner.capacity {
|
|
||||||
inner.map.shift_remove_index(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.inner.lock().unwrap().map.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for ArrayCache {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("ArrayCache").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// sliceable view on an image file
|
/// sliceable view on an image file
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@@ -634,6 +324,7 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
self.shape()[0]
|
self.shape()[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether the view has no pixels in the first dimension
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.shape()[0] == 0
|
self.shape()[0] == 0
|
||||||
}
|
}
|
||||||
@@ -643,6 +334,7 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
self.shape().into_iter().product()
|
self.shape().into_iter().product()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the size of the view along an axis, or `None` if the axis is not present
|
||||||
pub fn size_ax(&self, ax: Axis) -> Option<usize> {
|
pub fn size_ax(&self, ax: Axis) -> Option<usize> {
|
||||||
self.axes()
|
self.axes()
|
||||||
.iter()
|
.iter()
|
||||||
@@ -745,6 +437,7 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
.with_operations(self.operations.clone()))
|
.with_operations(self.operations.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// reduce an axis with an operation (max, min, sum or mean), returning a view of lower dimensionality
|
||||||
pub fn operate<A: Ax>(
|
pub fn operate<A: Ax>(
|
||||||
&self,
|
&self,
|
||||||
axis: A,
|
axis: A,
|
||||||
@@ -1450,6 +1143,7 @@ where
|
|||||||
|
|
||||||
/// trait to define a function to retrieve the only item in a 0d array
|
/// trait to define a function to retrieve the only item in a 0d array
|
||||||
pub trait Item {
|
pub trait Item {
|
||||||
|
/// the single item in the 0d array, cast to `T`
|
||||||
fn item<T>(&self) -> Result<T, Error>
|
fn item<T>(&self) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: Number,
|
T: Number,
|
||||||
@@ -1459,6 +1153,7 @@ pub trait Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Reader> View<Ix5, R> {
|
impl<R: Reader> View<Ix5, R> {
|
||||||
|
/// create a view on the image at `path`, parsing series and position from the file name
|
||||||
pub fn from_path<P>(path: P) -> Result<Self, Error>
|
pub fn from_path<P>(path: P) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@@ -1492,6 +1187,7 @@ impl<D: Dimension, R: Reader> Display for View<D, R> {
|
|||||||
|
|
||||||
/// trait to convert numbers to bytes
|
/// trait to convert numbers to bytes
|
||||||
pub trait ToBytesVec {
|
pub trait ToBytesVec {
|
||||||
|
/// the number as a vector of bytes in native endianness
|
||||||
fn to_bytes_vec(&self) -> Vec<u8>;
|
fn to_bytes_vec(&self) -> Vec<u8>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1516,12 +1212,11 @@ to_bytes_vec_impl!(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::axes::{Axis, Operation};
|
use crate::axes::{Axis, Operation};
|
||||||
|
use crate::cache::{ArrayCache, ArrayKey, ArrayKeyRef, FrameCache, ReaderKey, ReaderKeyRef};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::readers::{DynReader, Frame, Reader};
|
use crate::readers::{DynReader, Frame, Reader};
|
||||||
use crate::stats::MinMax;
|
use crate::stats::MinMax;
|
||||||
use crate::view::{
|
use crate::view::Item;
|
||||||
ArrayCache, ArrayKey, ArrayKeyRef, FrameCache, Item, ReaderKey, ReaderKeyRef,
|
|
||||||
};
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use ndarray::{Array, Array4, Array5, NewAxis};
|
use ndarray::{Array, Array4, Array5, NewAxis};
|
||||||
use ndarray::{Array2, ArrayD, IxDyn, SliceInfoElem, s};
|
use ndarray::{Array2, ArrayD, IxDyn, SliceInfoElem, s};
|
||||||
|
|||||||
Reference in New Issue
Block a user