impy-array 2.4.6__tar.gz → 2.4.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {impy_array-2.4.6 → impy_array-2.4.8}/PKG-INFO +3 -2
- {impy_array-2.4.6 → impy_array-2.4.8}/README.md +1 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/__init__.py +1 -1
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/_const.py +10 -10
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_corr.py +3 -1
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_skimage.py +14 -3
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/imgarray.py +26 -15
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/labeledarray.py +72 -15
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/lazy.py +256 -46
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/axes/_axis.py +40 -40
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/core.py +13 -21
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/io/_mrc.py +16 -9
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/io/_tiff.py +15 -7
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/lazy/core.py +3 -10
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/random.py +4 -4
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/utils/gauss.py +4 -1
- {impy_array-2.4.6 → impy_array-2.4.8}/pyproject.toml +1 -1
- {impy_array-2.4.6 → impy_array-2.4.8}/.gitignore +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/LICENSE +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/__main__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/_types.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/array_api.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_imshow.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_deconv.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_docs.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_filters.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_glcm.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_linalg.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_misc.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_plot.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_process_numba.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_structures.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/_utils/_transform.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/axesmixin.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/bases/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/bases/metaarray.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/bases/overload.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/big.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/label.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/phasearray.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/specials.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/arrays/tiled.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/axes/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/axes/_axes.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/axes/_axes_tuple.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/axes/_slicer.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/binder.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/collections.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/correlation.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/frame/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/frame/frames.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/io/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/io/_nd2.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/io/_npy.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/io/_registry.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/io/_utils.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/io/_zarr.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/lazy/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/lazy/random.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/roi.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/utils/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/utils/axesop.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/utils/deco.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/utils/misc.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/utils/slicer.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/utils/utilcls.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/viewer/__init__.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/viewer/utils.py +0 -0
- {impy_array-2.4.6 → impy_array-2.4.8}/impy/viewer/viewer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: impy-array
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.8
|
|
4
4
|
Summary: Speed up coding/extending image analysis in Python.
|
|
5
5
|
Project-URL: Download, https://github.com/hanjinliu/impy
|
|
6
6
|
Author-email: Hanjin Liu <liuhanjin-sc@g.ecc.u-tokyo.ac.jp>
|
|
@@ -131,6 +131,7 @@ img_prj.imsave("image_max.tif") # Save in the same place. Don't spend tim
|
|
|
131
131
|
`impy` automatically chooses the proper reader/writer according to the extension.
|
|
132
132
|
|
|
133
133
|
- Tiff file (".tif", ".tiff")
|
|
134
|
+
- LSM file (".lsm")
|
|
134
135
|
- MRC file (".mrc", ".rec", ".st", ".map", ".map.gz")
|
|
135
136
|
- Zarr file (".zarr")
|
|
136
137
|
- ND2 file (".nd2")
|
|
@@ -58,6 +58,7 @@ img_prj.imsave("image_max.tif") # Save in the same place. Don't spend tim
|
|
|
58
58
|
`impy` automatically chooses the proper reader/writer according to the extension.
|
|
59
59
|
|
|
60
60
|
- Tiff file (".tif", ".tiff")
|
|
61
|
+
- LSM file (".lsm")
|
|
61
62
|
- MRC file (".mrc", ".rec", ".st", ".map", ".map.gz")
|
|
62
63
|
- Zarr file (".zarr")
|
|
63
64
|
- ND2 file (".nd2")
|
|
@@ -8,13 +8,13 @@ MAX_GB_LIMIT = memory.total / 2 * 1e-9
|
|
|
8
8
|
|
|
9
9
|
class GlobalConstant(MutableMapping[str, Any]):
|
|
10
10
|
_const: dict[str, Any]
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
def __init__(self, **kwargs):
|
|
13
13
|
object.__setattr__(self, "_const", dict(**kwargs))
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def __len__(self) -> int:
|
|
16
16
|
return len(self._const)
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
def __iter__(self):
|
|
19
19
|
raise StopIteration
|
|
20
20
|
|
|
@@ -47,15 +47,15 @@ class GlobalConstant(MutableMapping[str, Any]):
|
|
|
47
47
|
dask.config.set(scheduler=v)
|
|
48
48
|
else:
|
|
49
49
|
raise RuntimeError("Cannot set new keys.")
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
self._const[k] = v
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
__getattr__ = __getitem__
|
|
54
54
|
__setattr__ = __setitem__
|
|
55
55
|
|
|
56
56
|
def __delitem__(self, v):
|
|
57
57
|
raise RuntimeError("Cannot delete any items.")
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
def __repr__(self):
|
|
60
60
|
return (
|
|
61
61
|
f"""
|
|
@@ -65,12 +65,12 @@ class GlobalConstant(MutableMapping[str, Any]):
|
|
|
65
65
|
SCHEDULER : {self['SCHEDULER']}
|
|
66
66
|
"""
|
|
67
67
|
)
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
def asdict(self) -> dict[str, Any]:
|
|
70
70
|
return self._const.copy()
|
|
71
71
|
|
|
72
72
|
Const = GlobalConstant(
|
|
73
|
-
MAX_GB = MAX_GB_LIMIT
|
|
73
|
+
MAX_GB = MAX_GB_LIMIT * 0.8,
|
|
74
74
|
ID_AXIS = "N",
|
|
75
75
|
RESOURCE = "numpy",
|
|
76
76
|
SCHEDULER = "threads",
|
|
@@ -80,12 +80,12 @@ class SetConst:
|
|
|
80
80
|
n_ongoing = 0
|
|
81
81
|
_locked_keys: set[str] = set()
|
|
82
82
|
_old_dict: dict[str, Any] = dict()
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
def __init__(self, dict_: dict[str, Any] | None =None, **kwargs):
|
|
85
85
|
dict_ = dict_ or {}
|
|
86
86
|
dict_.update(kwargs)
|
|
87
87
|
self._kwargs = dict_
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
def __enter__(self):
|
|
90
90
|
self.__class__.n_ongoing += 1
|
|
91
91
|
if self.__class__.n_ongoing == 1:
|
|
@@ -34,7 +34,7 @@ def subpixel_pcc(
|
|
|
34
34
|
f0: np.ndarray,
|
|
35
35
|
f1: np.ndarray,
|
|
36
36
|
upsample_factor: int,
|
|
37
|
-
max_shifts: tuple[float, ...] | None = None,
|
|
37
|
+
max_shifts: float | tuple[float, ...] | None = None,
|
|
38
38
|
):
|
|
39
39
|
power, product = _pcc_and_power_spec(f0, f1, max_shifts)
|
|
40
40
|
maxima = xp.unravel_index(xp.argmax(power), power.shape)
|
|
@@ -60,6 +60,8 @@ def subpixel_pcc(
|
|
|
60
60
|
power = abs2(cross_correlation)
|
|
61
61
|
|
|
62
62
|
if max_shifts is not None:
|
|
63
|
+
if np.isscalar(max_shifts):
|
|
64
|
+
max_shifts = (max_shifts,) * f0.ndim
|
|
63
65
|
max_shifts = xp.asarray(max_shifts)
|
|
64
66
|
_upsampled_left_shifts = (shifts + max_shifts) * upsample_factor
|
|
65
67
|
_upsampled_right_shifts = (max_shifts - shifts) * upsample_factor
|
|
@@ -6,8 +6,19 @@ from functools import reduce, lru_cache
|
|
|
6
6
|
|
|
7
7
|
# same as the function in skimage.filters._fft_based (available in scikit-image >= 0.19)
|
|
8
8
|
@lru_cache(maxsize=4)
|
|
9
|
-
def _get_ND_butterworth_filter(
|
|
10
|
-
|
|
9
|
+
def _get_ND_butterworth_filter(
|
|
10
|
+
shape: tuple[int, ...],
|
|
11
|
+
cutoff: float,
|
|
12
|
+
order: int,
|
|
13
|
+
high_pass: bool,
|
|
14
|
+
real: bool,
|
|
15
|
+
):
|
|
16
|
+
if cutoff == 0:
|
|
17
|
+
if high_pass:
|
|
18
|
+
wfilt = np.ones(shape, dtype=np.float32)
|
|
19
|
+
else:
|
|
20
|
+
wfilt = np.zeros(shape, dtype=np.float32)
|
|
21
|
+
return wfilt
|
|
11
22
|
ranges = []
|
|
12
23
|
for d, fc in zip(shape, cutoff):
|
|
13
24
|
axis = np.arange(-(d - 1) // 2, (d - 1) // 2 + 1, dtype=np.float32) / (d*fc)
|
|
@@ -19,4 +30,4 @@ def _get_ND_butterworth_filter(shape: tuple[int, ...], cutoff: float, order: int
|
|
|
19
30
|
wfilt = 1 / (1 + q2**order)
|
|
20
31
|
if high_pass:
|
|
21
32
|
wfilt = 1 - wfilt
|
|
22
|
-
return wfilt
|
|
33
|
+
return wfilt
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import warnings
|
|
3
|
+
from functools import partial
|
|
4
|
+
from itertools import product
|
|
5
|
+
from typing import TYPE_CHECKING, Literal, Sequence, Iterable, Callable, overload, Any
|
|
6
|
+
|
|
3
7
|
import numpy as np
|
|
4
8
|
from numpy.typing import DTypeLike
|
|
5
|
-
from functools import partial
|
|
6
9
|
from scipy import ndimage as ndi
|
|
7
|
-
from typing import TYPE_CHECKING, Literal, Sequence, Iterable, Callable, overload, Any
|
|
8
10
|
|
|
9
11
|
from impy.arrays.labeledarray import LabeledArray
|
|
10
12
|
from impy.arrays.label import Label
|
|
@@ -247,12 +249,11 @@ class ImgArray(LabeledArray):
|
|
|
247
249
|
cval = cval(self.value)
|
|
248
250
|
|
|
249
251
|
prefilter = prefilter or order > 1
|
|
250
|
-
mtx = _transform.compose_affine_matrix(translation=translation)
|
|
251
252
|
|
|
252
253
|
return self._apply_dask(
|
|
253
|
-
_transform.
|
|
254
|
+
_transform.shift,
|
|
254
255
|
c_axes=complement_axes(dims, self.axes),
|
|
255
|
-
kwargs=dict(
|
|
256
|
+
kwargs=dict(shift=translation, order=order, mode=mode, cval=cval,
|
|
256
257
|
prefilter=prefilter)
|
|
257
258
|
)
|
|
258
259
|
|
|
@@ -3986,9 +3987,9 @@ class ImgArray(LabeledArray):
|
|
|
3986
3987
|
along: AxisLike | None = None,
|
|
3987
3988
|
show_drift: bool = False,
|
|
3988
3989
|
upsample_factor: int = 10,
|
|
3990
|
+
max_shift: nDFloat | None = None,
|
|
3989
3991
|
) -> MarkerFrame:
|
|
3990
|
-
"""
|
|
3991
|
-
Calculate yx-directional drift using the method equivalent to
|
|
3992
|
+
"""Calculate yx-directional drift using the method equivalent to
|
|
3992
3993
|
``skimage.registration.phase_cross_correlation``.
|
|
3993
3994
|
|
|
3994
3995
|
Parameters
|
|
@@ -3999,6 +4000,8 @@ class ImgArray(LabeledArray):
|
|
|
3999
4000
|
If True, plot the result.
|
|
4000
4001
|
upsample_factor : int, default is 10
|
|
4001
4002
|
Up-sampling factor when calculating phase cross correlation.
|
|
4003
|
+
max_shift : tuple of float, optional
|
|
4004
|
+
Maximum shift in spatial directions.
|
|
4002
4005
|
|
|
4003
4006
|
Returns
|
|
4004
4007
|
-------
|
|
@@ -4013,7 +4016,9 @@ class ImgArray(LabeledArray):
|
|
|
4013
4016
|
raise ValueError("`along` must be single character.")
|
|
4014
4017
|
if not isinstance(upsample_factor, int):
|
|
4015
4018
|
raise TypeError(f"upsample-factor must be integer but got {type(upsample_factor)}")
|
|
4016
|
-
|
|
4019
|
+
if max_shift is not None:
|
|
4020
|
+
if np.isscalar(max_shift):
|
|
4021
|
+
max_shift = np.full(self.ndim - 1, max_shift)
|
|
4017
4022
|
result = np.zeros((self.sizeof(along), self.ndim-1), dtype=np.float32)
|
|
4018
4023
|
c_axes = complement_axes(along, self.axes)
|
|
4019
4024
|
last_img = None
|
|
@@ -4022,7 +4027,12 @@ class ImgArray(LabeledArray):
|
|
|
4022
4027
|
img = xp.asarray(img)
|
|
4023
4028
|
if last_img is not None:
|
|
4024
4029
|
result[i] = xp.asnumpy(
|
|
4025
|
-
_corr.subpixel_pcc(
|
|
4030
|
+
_corr.subpixel_pcc(
|
|
4031
|
+
last_img,
|
|
4032
|
+
img,
|
|
4033
|
+
max_shifts=max_shift,
|
|
4034
|
+
upsample_factor=upsample_factor,
|
|
4035
|
+
)[0]
|
|
4026
4036
|
)
|
|
4027
4037
|
last_img = img
|
|
4028
4038
|
else:
|
|
@@ -4047,15 +4057,17 @@ class ImgArray(LabeledArray):
|
|
|
4047
4057
|
*,
|
|
4048
4058
|
zero_ave: bool = True,
|
|
4049
4059
|
along: AxisLike | None = None,
|
|
4060
|
+
max_shift: nDFloat | None = None,
|
|
4050
4061
|
order: int = 1,
|
|
4051
4062
|
mode: str = "constant",
|
|
4052
4063
|
cval: float = 0,
|
|
4053
4064
|
dims: Dims = 2,
|
|
4054
4065
|
update: bool = False,
|
|
4055
4066
|
) -> ImgArray:
|
|
4056
|
-
"""
|
|
4057
|
-
|
|
4058
|
-
is not given, then it will be determined using
|
|
4067
|
+
"""Drift correction using iterative Affine translation.
|
|
4068
|
+
|
|
4069
|
+
If translation vectors ``shift`` is not given, then it will be determined using
|
|
4070
|
+
``track_drift`` method of ImgArray.
|
|
4059
4071
|
|
|
4060
4072
|
Parameters
|
|
4061
4073
|
----------
|
|
@@ -4082,7 +4094,7 @@ class ImgArray(LabeledArray):
|
|
|
4082
4094
|
Examples
|
|
4083
4095
|
--------
|
|
4084
4096
|
Drift correction of multichannel image using the first channel as the reference.
|
|
4085
|
-
|
|
4097
|
+
>>> img.drift_correction(ref=img["c=0"])
|
|
4086
4098
|
"""
|
|
4087
4099
|
|
|
4088
4100
|
if along is None:
|
|
@@ -4097,7 +4109,6 @@ class ImgArray(LabeledArray):
|
|
|
4097
4109
|
elif not isinstance(ref, ImgArray):
|
|
4098
4110
|
ref = self[ref]
|
|
4099
4111
|
if ref.axes != [along] + dims:
|
|
4100
|
-
from itertools import product
|
|
4101
4112
|
_c_axes = complement_axes([along] + dims, str(ref.axes))
|
|
4102
4113
|
fmt = slicer.get_formatter(_c_axes)
|
|
4103
4114
|
out = np.empty_like(self)
|
|
@@ -4109,7 +4120,7 @@ class ImgArray(LabeledArray):
|
|
|
4109
4120
|
)
|
|
4110
4121
|
return out
|
|
4111
4122
|
|
|
4112
|
-
shift = ref.track_drift(along=along).values
|
|
4123
|
+
shift = ref.track_drift(along=along, max_shift=max_shift).values
|
|
4113
4124
|
|
|
4114
4125
|
else:
|
|
4115
4126
|
shift = np.asarray(shift, dtype=np.float32)
|
|
@@ -3,7 +3,7 @@ import numpy as np
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
import itertools
|
|
5
5
|
from functools import partial
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Sequence, MutableMapping, Protocol
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal, Sequence, MutableMapping, Protocol
|
|
7
7
|
from warnings import warn
|
|
8
8
|
from scipy import ndimage as ndi
|
|
9
9
|
|
|
@@ -23,7 +23,7 @@ from impy.array_api import xp
|
|
|
23
23
|
|
|
24
24
|
if TYPE_CHECKING:
|
|
25
25
|
from typing_extensions import Self
|
|
26
|
-
from numpy.typing import ArrayLike, DTypeLike
|
|
26
|
+
from numpy.typing import ArrayLike, DTypeLike, NDArray
|
|
27
27
|
from impy.frame import PathFrame
|
|
28
28
|
from impy.roi import RoiList
|
|
29
29
|
|
|
@@ -401,10 +401,18 @@ class LabeledArray(MetaArray):
|
|
|
401
401
|
out._set_info(self)
|
|
402
402
|
return out
|
|
403
403
|
|
|
404
|
-
def as_float(self) -> Self:
|
|
405
|
-
if
|
|
404
|
+
def as_float(self, *, depth: int = 32) -> Self:
|
|
405
|
+
if depth == 16:
|
|
406
|
+
dtype = np.float16
|
|
407
|
+
elif depth == 32:
|
|
408
|
+
dtype = np.float32
|
|
409
|
+
elif depth == 64:
|
|
410
|
+
dtype = np.float64
|
|
411
|
+
else:
|
|
412
|
+
raise ValueError(f"depth must be 16, 32, or 64, but got {depth}")
|
|
413
|
+
if self.dtype == dtype:
|
|
406
414
|
return self
|
|
407
|
-
out = self.value.astype(
|
|
415
|
+
out = self.value.astype(dtype).view(self.__class__)
|
|
408
416
|
out._set_info(self)
|
|
409
417
|
return out
|
|
410
418
|
|
|
@@ -417,8 +425,12 @@ class LabeledArray(MetaArray):
|
|
|
417
425
|
return self.as_uint16()
|
|
418
426
|
elif dtype == "uint8":
|
|
419
427
|
return self.as_uint8()
|
|
428
|
+
elif dtype == "float16":
|
|
429
|
+
return self.as_float(depth=16)
|
|
420
430
|
elif dtype == "float32":
|
|
421
|
-
return self.as_float()
|
|
431
|
+
return self.as_float(depth=32)
|
|
432
|
+
elif dtype == "float64":
|
|
433
|
+
return self.as_float(depth=64)
|
|
422
434
|
elif dtype == "bool":
|
|
423
435
|
return self.astype("bool")
|
|
424
436
|
elif dtype == "float64":
|
|
@@ -576,6 +588,7 @@ class LabeledArray(MetaArray):
|
|
|
576
588
|
*,
|
|
577
589
|
order: int = 3,
|
|
578
590
|
prefilter: bool | None = None,
|
|
591
|
+
how: Literal["pack", "equal"] = "pack",
|
|
579
592
|
) -> PropArray:
|
|
580
593
|
"""
|
|
581
594
|
Measure line profile (kymograph) iteratively for every slice of image. This
|
|
@@ -590,7 +603,12 @@ class LabeledArray(MetaArray):
|
|
|
590
603
|
`a = [[y0, x0], [y1, x1], ..., [yn, xn]]`
|
|
591
604
|
b : array-like, optional
|
|
592
605
|
Destination coordinate. If specified, `a` must be the source coordinate.
|
|
593
|
-
{order}
|
|
606
|
+
{order}{prefilter}
|
|
607
|
+
how : "pack" or "equal", default "pack"
|
|
608
|
+
How to sample points on the line. "pack" means that the points are sampled
|
|
609
|
+
at 1-px interval from the start point. "equal" means that the points are
|
|
610
|
+
sampled equally at the interval larger but neareset to 1-px so that the
|
|
611
|
+
points covers the entire line.
|
|
594
612
|
|
|
595
613
|
Returns
|
|
596
614
|
-------
|
|
@@ -616,7 +634,12 @@ class LabeledArray(MetaArray):
|
|
|
616
634
|
a = np.asarray(a, dtype=np.float32)
|
|
617
635
|
_, ndim = a.shape
|
|
618
636
|
seg = SegmentedLine(a)
|
|
619
|
-
|
|
637
|
+
if how == "pack":
|
|
638
|
+
coords = np.stack(seg.arange(), axis=0)
|
|
639
|
+
elif how == "equal":
|
|
640
|
+
coords = np.stack(seg.linspace(int(seg.length())), axis=0)
|
|
641
|
+
else:
|
|
642
|
+
raise ValueError(f"No option matches how={how}")
|
|
620
643
|
|
|
621
644
|
if ndim == self.ndim:
|
|
622
645
|
dims = self.axes
|
|
@@ -632,7 +655,11 @@ class LabeledArray(MetaArray):
|
|
|
632
655
|
axes=c_axes+[new_axis], propname="reslice")
|
|
633
656
|
|
|
634
657
|
out.set_scale(self)
|
|
635
|
-
|
|
658
|
+
axis = self.axes[dims[-1]]
|
|
659
|
+
if how == "equal":
|
|
660
|
+
out.set_scale({new_axis: axis.scale * seg.interv}, unit=axis.unit)
|
|
661
|
+
else:
|
|
662
|
+
out.set_scale({new_axis: axis.scale}, unit=axis.unit)
|
|
636
663
|
return out
|
|
637
664
|
|
|
638
665
|
@_docs.write_docs
|
|
@@ -1576,31 +1603,61 @@ class SegmentedLine:
|
|
|
1576
1603
|
dist = np.sqrt(np.sum(vec**2, axis=1))
|
|
1577
1604
|
dist_sum = np.sum(dist)
|
|
1578
1605
|
npoints = int(dist_sum)
|
|
1579
|
-
interv = dist_sum / npoints
|
|
1606
|
+
self.interv = dist_sum / npoints
|
|
1580
1607
|
|
|
1581
|
-
self.length = dist_sum
|
|
1582
1608
|
self.vec = vec
|
|
1583
1609
|
self.dist = dist
|
|
1584
1610
|
self.nodes = nodes
|
|
1585
|
-
self.interv = interv
|
|
1586
1611
|
|
|
1587
1612
|
def sample_points(self) -> np.ndarray:
|
|
1588
1613
|
res = 0
|
|
1589
1614
|
out = [self.nodes[0:1]]
|
|
1590
1615
|
npoints = 1
|
|
1616
|
+
interv = self.length() / int(self.length())
|
|
1591
1617
|
for v, d, p in zip(self.vec, self.dist, self.nodes[:-1]):
|
|
1592
1618
|
res0 = res
|
|
1593
|
-
d_int, res = divmod(d + res,
|
|
1594
|
-
idx =
|
|
1619
|
+
d_int, res = divmod(d + res, interv)
|
|
1620
|
+
idx = interv * (np.arange(d_int) + 1) - res0
|
|
1595
1621
|
xs = idx[:, np.newaxis] * v[np.newaxis]/d + p
|
|
1596
1622
|
out.append(xs)
|
|
1597
1623
|
npoints += xs.shape[0]
|
|
1598
1624
|
|
|
1599
|
-
if npoints <= int(self.length):
|
|
1625
|
+
if npoints <= int(self.length()):
|
|
1600
1626
|
out.append(self.nodes[-1:])
|
|
1601
1627
|
|
|
1602
1628
|
return np.concatenate(out, axis=0)
|
|
1603
1629
|
|
|
1630
|
+
def length(self) -> np.float64:
|
|
1631
|
+
return np.sum(self.lengths())
|
|
1632
|
+
|
|
1633
|
+
def lengths(self) -> NDArray[np.float64]:
|
|
1634
|
+
return np.sqrt(np.sum(np.diff(self.nodes, axis=0) ** 2, axis=1))
|
|
1635
|
+
|
|
1636
|
+
def linspace(self, num: int) -> tuple[NDArray[np.float64], ...]:
|
|
1637
|
+
"""Return a tuple of x and y coordinates of np.linspace along the line."""
|
|
1638
|
+
tnots = np.cumsum(
|
|
1639
|
+
np.concatenate([[0], self.lengths()], dtype=np.float64)
|
|
1640
|
+
)
|
|
1641
|
+
teval = np.linspace(0, tnots[-1], num)
|
|
1642
|
+
return tuple(
|
|
1643
|
+
np.interp(teval, tnots, self.nodes[:, i])
|
|
1644
|
+
for i in range(self.nodes.shape[1])
|
|
1645
|
+
)
|
|
1646
|
+
|
|
1647
|
+
def arange(
|
|
1648
|
+
self, step: float = 1.0
|
|
1649
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
1650
|
+
tnots = np.cumsum(
|
|
1651
|
+
np.concatenate([[0], self.lengths()], dtype=np.float64)
|
|
1652
|
+
)
|
|
1653
|
+
length = tnots[-1]
|
|
1654
|
+
num, rem = divmod(length, step)
|
|
1655
|
+
teval = np.linspace(0, length - rem, int(num + 1))
|
|
1656
|
+
return tuple(
|
|
1657
|
+
np.interp(teval, tnots, self.nodes[:, i])
|
|
1658
|
+
for i in range(self.nodes.shape[1])
|
|
1659
|
+
)
|
|
1660
|
+
|
|
1604
1661
|
def _count_list_depth(x) -> int:
|
|
1605
1662
|
n = 0
|
|
1606
1663
|
out = x
|