impy-array 2.5.1__tar.gz → 2.5.3__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.5.1 → impy_array-2.5.3}/PKG-INFO +1 -1
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/__init__.py +1 -1
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/array_api.py +2 -2
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_corr.py +3 -3
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/imgarray.py +2 -2
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/labeledarray.py +0 -10
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/lazy.py +0 -10
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/specials.py +32 -33
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/core.py +29 -7
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/frame/frames.py +49 -60
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/_zarr.py +2 -4
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/viewer/utils.py +70 -73
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/viewer/viewer.py +107 -109
- {impy_array-2.5.1 → impy_array-2.5.3}/.gitignore +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/LICENSE +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/README.md +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/__main__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/_const.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/_types.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_imshow.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_deconv.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_docs.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_filters.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_glcm.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_linalg.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_misc.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_plot.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_process_numba.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_skimage.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_structures.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/_utils/_transform.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/axesmixin.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/bases/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/bases/metaarray.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/bases/overload.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/big.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/label.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/phasearray.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/arrays/tiled.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/axes/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/axes/_axes.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/axes/_axes_tuple.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/axes/_axis.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/axes/_slicer.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/binder.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/collections.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/correlation.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/frame/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/_czi.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/_mrc.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/_nd2.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/_npy.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/_registry.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/_tiff.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/io/_utils.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/lazy/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/lazy/core.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/lazy/random.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/random.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/roi.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/utils/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/utils/axesop.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/utils/deco.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/utils/gauss.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/utils/misc.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/utils/slicer.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/utils/utilcls.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/impy/viewer/__init__.py +0 -0
- {impy_array-2.5.1 → impy_array-2.5.3}/pyproject.toml +0 -0
|
@@ -97,7 +97,7 @@ class XP:
|
|
|
97
97
|
self.angle = np.angle
|
|
98
98
|
self.abs = np.abs
|
|
99
99
|
self.mod = np.mod
|
|
100
|
-
self.
|
|
100
|
+
self.trunc = np.trunc
|
|
101
101
|
self.round = np.round
|
|
102
102
|
self.gradient = np.gradient
|
|
103
103
|
self.tensordot = np.tensordot
|
|
@@ -165,7 +165,7 @@ class XP:
|
|
|
165
165
|
self.angle = cp.angle
|
|
166
166
|
self.abs = cp.abs
|
|
167
167
|
self.mod = cp.mod
|
|
168
|
-
self.
|
|
168
|
+
self.trunc = cp.trunc
|
|
169
169
|
self.round = cp.round
|
|
170
170
|
try:
|
|
171
171
|
self.gradient = cp.gradient
|
|
@@ -38,16 +38,16 @@ def subpixel_pcc(
|
|
|
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)
|
|
41
|
-
midpoints = xp.array([np.
|
|
41
|
+
midpoints = xp.array([np.trunc(axis_size / 2) for axis_size in power.shape])
|
|
42
42
|
|
|
43
43
|
shifts = xp.asarray(maxima, dtype=np.float32)
|
|
44
44
|
shifts[shifts > midpoints] -= xp.array(power.shape)[shifts > midpoints]
|
|
45
45
|
# Initial shift estimate in upsampled grid
|
|
46
|
-
shifts = xp.
|
|
46
|
+
shifts = xp.trunc(shifts * upsample_factor) / upsample_factor
|
|
47
47
|
if upsample_factor > 1:
|
|
48
48
|
upsampled_region_size = np.ceil(upsample_factor * 1.5)
|
|
49
49
|
# Center of output array at dftshift + 1
|
|
50
|
-
dftshift = xp.
|
|
50
|
+
dftshift = xp.trunc(upsampled_region_size / 2.0)
|
|
51
51
|
upsample_factor = xp.array(upsample_factor, dtype=np.float32)
|
|
52
52
|
# Matrix multiply DFT around the current shift estimate
|
|
53
53
|
sample_region_offset = dftshift - shifts*upsample_factor
|
|
@@ -2675,7 +2675,7 @@ class ImgArray(LabeledArray):
|
|
|
2675
2675
|
out = pd.concat(out, axis=0)
|
|
2676
2676
|
|
|
2677
2677
|
out = MarkerFrame(out.reindex(columns=list(coords._axes)),
|
|
2678
|
-
columns=str(coords._axes), dtype=np.float32)
|
|
2678
|
+
columns=str(coords._axes), dtype=np.float32)
|
|
2679
2679
|
out.set_scale(coords.scale)
|
|
2680
2680
|
|
|
2681
2681
|
return out
|
|
@@ -3801,7 +3801,7 @@ class ImgArray(LabeledArray):
|
|
|
3801
3801
|
|
|
3802
3802
|
def regionprops(
|
|
3803
3803
|
self,
|
|
3804
|
-
properties: Iterable[str] | str = ("
|
|
3804
|
+
properties: Iterable[str] | str = ("intensity_mean",),
|
|
3805
3805
|
*,
|
|
3806
3806
|
extra_properties: Iterable[Callable] | None = None,
|
|
3807
3807
|
) -> DataDict[str, PropArray]:
|
|
@@ -302,16 +302,6 @@ class LabeledArray(MetaArray):
|
|
|
302
302
|
ext = ".tif"
|
|
303
303
|
save_path = save_path.parent / (save_path.name + ext)
|
|
304
304
|
|
|
305
|
-
if not Path(save_path).is_absolute():
|
|
306
|
-
if self.source is None:
|
|
307
|
-
raise ValueError(
|
|
308
|
-
"Image directory path is unknown. Set by \n"
|
|
309
|
-
" >>> img.source = \"...\"\n"
|
|
310
|
-
"or specify absolute path like\n"
|
|
311
|
-
" >>> img.imsave(\"/path/to/XXX.tif\")"
|
|
312
|
-
)
|
|
313
|
-
save_path = self.source.parent / save_path
|
|
314
|
-
|
|
315
305
|
if not overwrite and save_path.exists():
|
|
316
306
|
raise FileExistsError(f"File {save_path!r} already exists.")
|
|
317
307
|
if self.metadata is None:
|
|
@@ -352,16 +352,6 @@ class LazyImgArray(AxesMixin):
|
|
|
352
352
|
ext = ".tif"
|
|
353
353
|
save_path = save_path.parent / (save_path.name + ext)
|
|
354
354
|
|
|
355
|
-
if not Path(save_path).is_absolute():
|
|
356
|
-
if self.source is None:
|
|
357
|
-
raise ValueError(
|
|
358
|
-
"Image directory path is unknown. Set by \n"
|
|
359
|
-
" >>> img.source = \"...\"\n"
|
|
360
|
-
"or specify absolute path like\n"
|
|
361
|
-
" >>> img.imsave(\"/path/to/XXX.tif\")"
|
|
362
|
-
)
|
|
363
|
-
save_path = self.source.parent / save_path
|
|
364
|
-
|
|
365
355
|
if not overwrite and save_path.exists():
|
|
366
356
|
raise FileExistsError(f"File {save_path!r} already exists.")
|
|
367
357
|
if dtype is None:
|
|
@@ -20,23 +20,23 @@ SCALAR_PROP = (
|
|
|
20
20
|
class PropArray(MetaArray):
|
|
21
21
|
additional_props = ["_source", "_metadata", "_name", "propname"]
|
|
22
22
|
propname: str
|
|
23
|
-
def __new__(cls, obj, *, name=None, axes=None, source=None,
|
|
23
|
+
def __new__(cls, obj, *, name=None, axes=None, source=None,
|
|
24
24
|
metadata=None, propname=None, dtype=None):
|
|
25
25
|
if propname is None:
|
|
26
26
|
propname = "User_Defined"
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
if dtype is None and propname in SCALAR_PROP:
|
|
29
29
|
dtype = np.float32
|
|
30
30
|
elif dtype is None:
|
|
31
31
|
dtype = object
|
|
32
32
|
else:
|
|
33
33
|
dtype = dtype
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
self = super().__new__(cls, obj, name, axes, source, metadata, dtype=dtype)
|
|
36
36
|
self.propname = propname
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
return self
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
def _repr_dict_(self):
|
|
41
41
|
return {
|
|
42
42
|
"name": self.name,
|
|
@@ -44,7 +44,7 @@ class PropArray(MetaArray):
|
|
|
44
44
|
"dtype": self.dtype,
|
|
45
45
|
"source": self.source,
|
|
46
46
|
"property name": self.propname}
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
def plot(self, along=None, cmap="jet", cmap_range=(0, 1)):
|
|
49
49
|
"""
|
|
50
50
|
Plot all the results with x-axis defined by `along`.
|
|
@@ -57,7 +57,7 @@ class PropArray(MetaArray):
|
|
|
57
57
|
Colormap of each graph.
|
|
58
58
|
cmap_range : tuple, default is (0, 1)
|
|
59
59
|
Range of float for colormap iteration.
|
|
60
|
-
"""
|
|
60
|
+
"""
|
|
61
61
|
import matplotlib.pyplot as plt
|
|
62
62
|
if self.dtype == object:
|
|
63
63
|
raise TypeError(f"Cannot call plot_profile for {self.propname} "
|
|
@@ -67,21 +67,21 @@ class PropArray(MetaArray):
|
|
|
67
67
|
along = find_first_appeared("tzp<yxc", include=self.axes)
|
|
68
68
|
except ValueError:
|
|
69
69
|
along = self.axes[-1]
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
iteraxes = self.axes.drop(along)
|
|
72
72
|
cmap = plt.get_cmap(cmap)
|
|
73
73
|
positions = np.linspace(*cmap_range, self.size//self.sizeof(along), endpoint=False)
|
|
74
74
|
x = np.arange(self.sizeof(along))*self.scale[along]
|
|
75
75
|
for i, (sl, y) in enumerate(self.iter(iteraxes)):
|
|
76
76
|
plt.plot(x, y, color=cmap(positions[i]))
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
plt.title(f"{self.propname}")
|
|
79
79
|
plt.xlabel(along)
|
|
80
80
|
plt.show()
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
return self
|
|
83
|
-
|
|
84
|
-
def hist(self, along: str = "N", bins: int = None, cmap: str = "jet",
|
|
83
|
+
|
|
84
|
+
def hist(self, along: str = "N", bins: int = None, cmap: str = "jet",
|
|
85
85
|
cmap_range: tuple[float, float] = (0., 1.)) -> PropArray:
|
|
86
86
|
"""
|
|
87
87
|
Plot histogram.
|
|
@@ -96,31 +96,31 @@ class PropArray(MetaArray):
|
|
|
96
96
|
Colormap of each graph.
|
|
97
97
|
cmap_range : tuple, default is (0, 1)
|
|
98
98
|
Range of float for colormap iteration.
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
Returns
|
|
101
101
|
-------
|
|
102
102
|
self
|
|
103
|
-
"""
|
|
103
|
+
"""
|
|
104
104
|
import matplotlib.pyplot as plt
|
|
105
105
|
|
|
106
106
|
if self.dtype == object:
|
|
107
107
|
raise TypeError(f"Cannot call plot_profile for {self.propname} "
|
|
108
108
|
"because dtype == object.")
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
if along is None:
|
|
111
111
|
along = find_first_appeared("pxyzt<c", include=self.axes)
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
iteraxes = self.axes.drop(along)
|
|
114
114
|
cmap = plt.get_cmap(cmap)
|
|
115
115
|
positions = np.linspace(*cmap_range, self.size//self.sizeof(along), endpoint=False)
|
|
116
116
|
for i, (sl, y) in enumerate(self.iter(iteraxes)):
|
|
117
117
|
plt.hist(y, color=cmap(positions[i]), bins=bins, alpha=0.5)
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
plt.title(f"{self.propname}")
|
|
120
120
|
plt.show()
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
return self
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
def curve_fit(self, f: Callable, p0 = None, dims = "t", return_fit: bool = True) -> DataDict[str, PropArray]:
|
|
125
125
|
"""
|
|
126
126
|
Run scipy.optimize.curve_fit for each dimesion.
|
|
@@ -130,12 +130,12 @@ class PropArray(MetaArray):
|
|
|
130
130
|
f : callable
|
|
131
131
|
Model function.
|
|
132
132
|
p0 : array or callable, optional
|
|
133
|
-
Initial parameter. Callable object that estimate initial paramter can also
|
|
133
|
+
Initial parameter. Callable object that estimate initial paramter can also
|
|
134
134
|
be passed here.
|
|
135
135
|
dims : str, by default "t"
|
|
136
136
|
Along which axes fitting algorithms are conducted.
|
|
137
137
|
return_fit : bool, by default True
|
|
138
|
-
If fitting trajectories are returned. If the input array is large, this will
|
|
138
|
+
If fitting trajectories are returned. If the input array is large, this will
|
|
139
139
|
save much memory.
|
|
140
140
|
|
|
141
141
|
Returns
|
|
@@ -144,23 +144,23 @@ class PropArray(MetaArray):
|
|
|
144
144
|
params : Fitting parameters
|
|
145
145
|
covs : Covariances
|
|
146
146
|
fit : fitting trajectories (if return_fit==True)
|
|
147
|
-
"""
|
|
147
|
+
"""
|
|
148
148
|
from scipy import optimize as opt
|
|
149
149
|
|
|
150
150
|
c_axes = complement_axes(dims, all_axes=self.axes)
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
if len(dims)!=1:
|
|
153
153
|
raise NotImplementedError("Only 1-dimensional fitting is implemented.")
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
n_params = len(signature(f).parameters)-1
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
params = np.empty(self.sizesof(c_axes) + (n_params,), dtype=np.float32)
|
|
158
158
|
errs = np.empty(self.sizesof(c_axes) + (n_params,), dtype=np.float32)
|
|
159
159
|
if return_fit:
|
|
160
160
|
fit = np.empty(self.sizesof(c_axes) + (self.sizeof(dims),), dtype=self.dtype)
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
xdata = np.arange(self.sizeof(dims))*self.scale[dims]
|
|
163
|
-
|
|
163
|
+
|
|
164
164
|
for sl, data in self.iter(c_axes, exclude=dims):
|
|
165
165
|
p0_ = p0 if not callable(p0) else p0(data)
|
|
166
166
|
result = opt.curve_fit(f, xdata, data, p0_)
|
|
@@ -168,22 +168,22 @@ class PropArray(MetaArray):
|
|
|
168
168
|
errs[sl] = np.sqrt(np.diag(cov))
|
|
169
169
|
if return_fit:
|
|
170
170
|
fit[sl] = f(xdata, *(result[0]))
|
|
171
|
-
|
|
171
|
+
|
|
172
172
|
# set infos
|
|
173
173
|
params = params.view(self.__class__)
|
|
174
174
|
errs = errs.view(self.__class__)
|
|
175
175
|
params._set_info(self, new_axes=self.axes.drop(dims).extend("m"))
|
|
176
176
|
errs._set_info(self, new_axes=self.axes.drop(dims).extend("m"))
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
if return_fit:
|
|
179
179
|
fit = fit.view(self.__class__)
|
|
180
180
|
fit._set_info(self, new_axes=self.axes.drop(dims).extend(dims))
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
if return_fit:
|
|
183
183
|
return DataDict(params=params, errs=errs, fit=fit)
|
|
184
184
|
else:
|
|
185
185
|
return DataDict(params=params, errs=errs)
|
|
186
|
-
|
|
186
|
+
|
|
187
187
|
def as_frame(self, colname: str = "f") -> AxesFrame:
|
|
188
188
|
"""
|
|
189
189
|
N-dimensional data to DataFrame. The intensity data is stored in the `colname` column.
|
|
@@ -197,7 +197,7 @@ class PropArray(MetaArray):
|
|
|
197
197
|
-------
|
|
198
198
|
AxesFrame
|
|
199
199
|
DataFrame with PropArray data.
|
|
200
|
-
"""
|
|
200
|
+
"""
|
|
201
201
|
from ..frame import AxesFrame
|
|
202
202
|
if colname in self.axes:
|
|
203
203
|
raise ImageAxesError(f"Axis {colname} already exists.")
|
|
@@ -210,4 +210,3 @@ class PropArray(MetaArray):
|
|
|
210
210
|
df = AxesFrame(data_dict, columns=new_axes)
|
|
211
211
|
df.set_scale(self)
|
|
212
212
|
return df
|
|
213
|
-
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from contextlib import suppress
|
|
2
3
|
import os
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
import re
|
|
@@ -489,12 +490,12 @@ def broadcast_arrays(*arrays: MetaArray) -> list[MetaArray]:
|
|
|
489
490
|
out = [a.broadcast_to(shape, axes) for a in arrays]
|
|
490
491
|
return out
|
|
491
492
|
|
|
492
|
-
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
493
|
+
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
493
494
|
# Imread functions
|
|
494
|
-
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
495
|
+
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
495
496
|
|
|
496
497
|
def imread(
|
|
497
|
-
path: str,
|
|
498
|
+
path: str | Path,
|
|
498
499
|
dtype: DTypeLike = None,
|
|
499
500
|
key: AxesTargetedSlicer | None = None,
|
|
500
501
|
*,
|
|
@@ -502,6 +503,7 @@ def imread(
|
|
|
502
503
|
squeeze: bool = False,
|
|
503
504
|
) -> ImgArray:
|
|
504
505
|
"""Load image(s) from a path.
|
|
506
|
+
|
|
505
507
|
You can read list of images from directories with wildcards or ``"$"`` in ``path``.
|
|
506
508
|
|
|
507
509
|
Parameters
|
|
@@ -535,12 +537,30 @@ def imread(
|
|
|
535
537
|
Wall time: 3.01 ms
|
|
536
538
|
|
|
537
539
|
"""
|
|
538
|
-
|
|
540
|
+
if str(path).startswith("~"):
|
|
541
|
+
path = str(Path(path).expanduser())
|
|
542
|
+
else:
|
|
543
|
+
path = str(path)
|
|
544
|
+
|
|
545
|
+
# if a directory of a ome.tiff is given, read the first image (which will eventually
|
|
546
|
+
# read all the positions) and use the directory name as source/name.
|
|
547
|
+
if (_fp := Path(path)).is_dir() and _fp.suffix != ".zarr":
|
|
548
|
+
_first_tif = next(_fp.glob("*.ome.tif"), None)
|
|
549
|
+
if _first_tif is None:
|
|
550
|
+
raise ValueError(
|
|
551
|
+
"If a directory is given to imread, only directory of ome.tif is "
|
|
552
|
+
"supported. Please use wildcard like `path/to/dir/*.tif` to read "
|
|
553
|
+
"images in a directory."
|
|
554
|
+
)
|
|
555
|
+
out = imread(_first_tif, dtype=dtype, key=key, name=_fp.name, squeeze=squeeze)
|
|
556
|
+
out.source = _fp
|
|
557
|
+
return out
|
|
558
|
+
|
|
539
559
|
is_memmap = (key is not None)
|
|
540
560
|
|
|
541
561
|
if "$" in path:
|
|
542
562
|
return _imread_stack(path, dtype=dtype, key=key, squeeze=squeeze)
|
|
543
|
-
elif "*" in path:
|
|
563
|
+
elif "*" in path or "?" in path or "[" in path:
|
|
544
564
|
return _imread_glob(path, dtype=dtype, key=key, squeeze=squeeze)
|
|
545
565
|
elif not Path(path).exists():
|
|
546
566
|
raise FileNotFoundError(f"No such file or directory: {path}")
|
|
@@ -575,7 +595,8 @@ def imread(
|
|
|
575
595
|
# In case the image is in yxc-order. This sometimes happens.
|
|
576
596
|
self: ImgArray = np.moveaxis(self, -1, -3)
|
|
577
597
|
if labels is not None:
|
|
578
|
-
|
|
598
|
+
with suppress(ValueError):
|
|
599
|
+
self.set_axis_label(c=labels)
|
|
579
600
|
|
|
580
601
|
if dtype is None:
|
|
581
602
|
dtype = self.dtype
|
|
@@ -619,8 +640,9 @@ def _imread_glob(path: str, squeeze: bool = False, **kwargs) -> ImgArray:
|
|
|
619
640
|
out: ImgArray = np.stack(imgs, axis="p")
|
|
620
641
|
if squeeze:
|
|
621
642
|
out = np.squeeze(out)
|
|
643
|
+
sep = "*" if "*" in path else "?"
|
|
622
644
|
try:
|
|
623
|
-
base = os.path.split(path.split(
|
|
645
|
+
base = os.path.split(path.split(sep)[0])[0]
|
|
624
646
|
out.source = base
|
|
625
647
|
except Exception:
|
|
626
648
|
pass
|
|
@@ -18,11 +18,11 @@ else:
|
|
|
18
18
|
|
|
19
19
|
class AxesFrame(pd.DataFrame):
|
|
20
20
|
_metadata = ["_axes"]
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
@property
|
|
23
23
|
def _constructor(self):
|
|
24
24
|
return AxesFrame
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
def __init__(self, data=None, columns=None, **kwargs):
|
|
27
27
|
if isinstance(columns, (str, Axes)):
|
|
28
28
|
kwargs["columns"] = [a for a in columns]
|
|
@@ -32,17 +32,17 @@ class AxesFrame(pd.DataFrame):
|
|
|
32
32
|
columns = str(data._axes)
|
|
33
33
|
else:
|
|
34
34
|
kwargs["columns"] = columns
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
super().__init__(data, **kwargs)
|
|
37
37
|
self._axes = Axes(self.columns)
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
def _get_coords_cols(self):
|
|
40
40
|
return [a for a in self.columns if len(a) == 1]
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
def get_coords(self):
|
|
43
43
|
return self[self.columns[self.columns.isin([a for a in self.columns if len(a) == 1])]]
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
|
|
45
|
+
|
|
46
46
|
def __getitem__(self, k):
|
|
47
47
|
# sl_tuple = solve_slicer(key, self.axes)
|
|
48
48
|
# drops: list[int] = []
|
|
@@ -56,7 +56,7 @@ class AxesFrame(pd.DataFrame):
|
|
|
56
56
|
# binary = np.logical_and(binary, (arr - s0) % step == 0)
|
|
57
57
|
# elif step < 0:
|
|
58
58
|
# raise NotImplementedError
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
# else:
|
|
61
61
|
# binary = arr == sl
|
|
62
62
|
# drops.append(i)
|
|
@@ -72,9 +72,9 @@ class AxesFrame(pd.DataFrame):
|
|
|
72
72
|
for each in k.split(";"):
|
|
73
73
|
self = self.__getitem__(each.strip())
|
|
74
74
|
return self
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
elif "=" in k:
|
|
77
|
-
axis, sl =
|
|
77
|
+
axis, sl = (a.strip() for a in k.split("="))
|
|
78
78
|
sl = str_to_slice(sl)
|
|
79
79
|
if isinstance(sl, int):
|
|
80
80
|
out = self[self[axis]==sl]
|
|
@@ -87,19 +87,19 @@ class AxesFrame(pd.DataFrame):
|
|
|
87
87
|
elif "" == k:
|
|
88
88
|
return self
|
|
89
89
|
else:
|
|
90
|
-
out = super().__getitem__(k)
|
|
90
|
+
out = super().__getitem__(k)
|
|
91
91
|
else:
|
|
92
92
|
out = super().__getitem__(k)
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
if isinstance(out, AxesFrame):
|
|
95
95
|
out._axes = Axes(out._get_coords_cols())
|
|
96
96
|
out.set_scale(self)
|
|
97
97
|
return out
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
@property
|
|
100
100
|
def col_axes(self):
|
|
101
101
|
return self._axes
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
@col_axes.setter
|
|
104
104
|
def col_axes(self, value):
|
|
105
105
|
naxes = self.shape[1]
|
|
@@ -113,11 +113,11 @@ class AxesFrame(pd.DataFrame):
|
|
|
113
113
|
f"array (ndim={naxes}) and axes ({value})"
|
|
114
114
|
)
|
|
115
115
|
self._axes = axes
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
@property
|
|
118
118
|
def scale(self):
|
|
119
119
|
return self._axes.scale
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
def set_scale(self, other=None, **kwargs) -> None:
|
|
122
122
|
"""
|
|
123
123
|
Set scales of each axis.
|
|
@@ -127,7 +127,7 @@ class AxesFrame(pd.DataFrame):
|
|
|
127
127
|
other : dict, AxesFrame or MetaArray, optional
|
|
128
128
|
New scales. If dict, it should be like {"x": 0.1, "y": 0.1}. If MetaArray, only
|
|
129
129
|
scales of common axes are copied.
|
|
130
|
-
kwargs :
|
|
130
|
+
kwargs :
|
|
131
131
|
This enables function call like set_scale(x=0.1, y=0.1).
|
|
132
132
|
|
|
133
133
|
"""
|
|
@@ -142,40 +142,29 @@ class AxesFrame(pd.DataFrame):
|
|
|
142
142
|
# check if all the keys are contained in axes.
|
|
143
143
|
for a, val in other.items():
|
|
144
144
|
if a not in self._axes:
|
|
145
|
-
raise ImageAxesError(f"Image does not have axis {a}.")
|
|
145
|
+
raise ImageAxesError(f"Image does not have axis {a}.")
|
|
146
146
|
elif not np.isscalar(val):
|
|
147
147
|
raise TypeError(f"Cannot set non-numeric value as scales.")
|
|
148
|
-
|
|
148
|
+
|
|
149
149
|
for k, v in other.items():
|
|
150
150
|
self.col_axes[k].scale = v
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
elif hasattr(other, "scale"):
|
|
153
153
|
self.set_scale({a: s for a, s in other.scale.items() if a in self._axes})
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
elif kwargs:
|
|
156
156
|
self.set_scale(dict(kwargs))
|
|
157
|
-
|
|
157
|
+
|
|
158
158
|
else:
|
|
159
159
|
raise TypeError(f"'other' must be str or MetaArray, but got {type(other)}")
|
|
160
|
-
|
|
160
|
+
|
|
161
161
|
return None
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
"""
|
|
165
|
-
t or c -> uint16
|
|
166
|
-
p -> uint32
|
|
167
|
-
z, y, x -> float32
|
|
168
|
-
"""
|
|
169
|
-
dtype = lambda a: np.uint16 if a in ["t", "c"] else (np.uint32 if a == Const["ID_AXIS"] else np.float32)
|
|
170
|
-
out = self.__class__(self.astype({a: dtype(a) for a in self.col_axes}))
|
|
171
|
-
out._axes = self._axes
|
|
172
|
-
return out
|
|
173
|
-
|
|
174
|
-
|
|
162
|
+
|
|
163
|
+
|
|
175
164
|
def split(self, axis="c") -> list[AxesFrame]:
|
|
176
165
|
"""
|
|
177
166
|
Split DataFrame according to its indices. For example, if self is an DataFrame with columns
|
|
178
|
-
"t", "c", "y", "x" and it is split along "c" axis, then output is a list of DataFrame with
|
|
167
|
+
"t", "c", "y", "x" and it is split along "c" axis, then output is a list of DataFrame with
|
|
179
168
|
columns "t", "y", "x".
|
|
180
169
|
|
|
181
170
|
Parameters
|
|
@@ -204,7 +193,7 @@ class AxesFrame(pd.DataFrame):
|
|
|
204
193
|
----------
|
|
205
194
|
axes : str
|
|
206
195
|
Along which axes to iterate.
|
|
207
|
-
|
|
196
|
+
|
|
208
197
|
Yields
|
|
209
198
|
-------
|
|
210
199
|
tuple and AxesFrame
|
|
@@ -214,10 +203,10 @@ class AxesFrame(pd.DataFrame):
|
|
|
214
203
|
outsl = [slice(None)] * len(self.col_axes)
|
|
215
204
|
cols = [a for a in self.col_axes if a not in axes]
|
|
216
205
|
groupkeys = [a for a in axes]
|
|
217
|
-
|
|
206
|
+
|
|
218
207
|
if len(groupkeys) == 0:
|
|
219
208
|
yield (slice(None),), self
|
|
220
|
-
|
|
209
|
+
|
|
221
210
|
else:
|
|
222
211
|
if len(groupkeys) == 1:
|
|
223
212
|
groupkeys = groupkeys[0]
|
|
@@ -227,21 +216,21 @@ class AxesFrame(pd.DataFrame):
|
|
|
227
216
|
sl = (sl,)
|
|
228
217
|
[outsl.__setitem__(i, s) for i, s in zip(indices, sl)]
|
|
229
218
|
yield tuple(outsl), af
|
|
230
|
-
|
|
219
|
+
|
|
231
220
|
def sort(self):
|
|
232
221
|
ids = self._axes.argsort()
|
|
233
222
|
return self[[self._axes[i] for i in ids]]
|
|
234
|
-
|
|
223
|
+
|
|
235
224
|
def proj(self, axis=None):
|
|
236
225
|
if axis is None:
|
|
237
226
|
axis = complement_axes("yx", self._axes)
|
|
238
227
|
cols = [a for a in self.col_axes if a not in axis]
|
|
239
228
|
return self[cols]
|
|
240
|
-
|
|
229
|
+
|
|
241
230
|
def tp_no_verbose(func):
|
|
242
231
|
"""
|
|
243
232
|
Temporary suppress logging in trackpy.
|
|
244
|
-
"""
|
|
233
|
+
"""
|
|
245
234
|
@wraps(func)
|
|
246
235
|
def wrapper(self, *args, **kwargs):
|
|
247
236
|
tp.quiet(suppress=True)
|
|
@@ -277,19 +266,19 @@ class MarkerFrame(AxesFrame):
|
|
|
277
266
|
-------
|
|
278
267
|
TrackFrame
|
|
279
268
|
Result of particle tracking.
|
|
280
|
-
"""
|
|
281
|
-
|
|
269
|
+
"""
|
|
270
|
+
|
|
282
271
|
linked = tp.link(pd.DataFrame(self), search_range=search_range, t_column="t", memory=memory, **kwargs)
|
|
283
|
-
|
|
272
|
+
|
|
284
273
|
linked.rename(columns = {"particle":Const["ID_AXIS"]}, inplace=True)
|
|
285
274
|
linked = linked.reindex(columns=[a for a in Const["ID_AXIS"]+str(self.col_axes)])
|
|
286
|
-
|
|
275
|
+
|
|
287
276
|
track = TrackFrame(linked, columns="".join(linked.columns.tolist()))
|
|
288
277
|
track.set_scale(self)
|
|
289
278
|
if min_dwell > 0:
|
|
290
279
|
out = track.filter_stubs(min_dwell)
|
|
291
280
|
else:
|
|
292
|
-
out = track
|
|
281
|
+
out = track
|
|
293
282
|
out.index = np.arange(len(out))
|
|
294
283
|
return out
|
|
295
284
|
|
|
@@ -298,7 +287,7 @@ class TrackFrame(AxesFrame):
|
|
|
298
287
|
df = pd.DataFrame(self, copy=True, dtype=np.float32)
|
|
299
288
|
df.rename(columns = {"t":"frame", Const["ID_AXIS"]:"particle"}, inplace=True)
|
|
300
289
|
return df
|
|
301
|
-
|
|
290
|
+
|
|
302
291
|
@tp_no_verbose
|
|
303
292
|
def track_drift(self, smoothing=0):
|
|
304
293
|
df = self._renamed_df()
|
|
@@ -307,25 +296,25 @@ class TrackFrame(AxesFrame):
|
|
|
307
296
|
ori = pd.DataFrame({"y":[0.], "x":[0.]}, dtype=np.float32)
|
|
308
297
|
shift = pd.concat([ori, shift], axis=0)
|
|
309
298
|
return MarkerFrame(shift)
|
|
310
|
-
|
|
299
|
+
|
|
311
300
|
@tp_no_verbose
|
|
312
301
|
def msd(self, max_lagt:int=100, detail:bool=False):
|
|
313
302
|
df = self._renamed_df()
|
|
314
|
-
return tp.motion.msd(df, self.scale["x"], self.scale["t"],
|
|
303
|
+
return tp.motion.msd(df, self.scale["x"], self.scale["t"],
|
|
315
304
|
max_lagtime=max_lagt, detail=detail)
|
|
316
|
-
|
|
305
|
+
|
|
317
306
|
@tp_no_verbose
|
|
318
307
|
def imsd(self, max_lagt=100):
|
|
319
308
|
df = self._renamed_df()
|
|
320
|
-
return tp.motion.imsd(df, self.scale["x"], self.scale["t"],
|
|
309
|
+
return tp.motion.imsd(df, self.scale["x"], self.scale["t"],
|
|
321
310
|
max_lagtime=max_lagt)
|
|
322
|
-
|
|
311
|
+
|
|
323
312
|
@tp_no_verbose
|
|
324
313
|
def emsd(self, max_lagt=100, detail=False):
|
|
325
314
|
df = self._renamed_df()
|
|
326
|
-
return tp.motion.emsd(df, self.scale["x"], self.scale["t"],
|
|
315
|
+
return tp.motion.emsd(df, self.scale["x"], self.scale["t"],
|
|
327
316
|
max_lagtime=max_lagt, detail=detail)
|
|
328
|
-
|
|
317
|
+
|
|
329
318
|
@tp_no_verbose
|
|
330
319
|
def filter_stubs(self, min_dwell=3):
|
|
331
320
|
df = self._renamed_df()
|
|
@@ -334,8 +323,8 @@ class TrackFrame(AxesFrame):
|
|
|
334
323
|
df = df.astype({"t":np.uint16, Const["ID_AXIS"]:np.uint32})
|
|
335
324
|
out = TrackFrame(df, columns=self.col_axes)
|
|
336
325
|
out.set_scale(self)
|
|
337
|
-
return out
|
|
338
|
-
|
|
326
|
+
return out
|
|
327
|
+
|
|
339
328
|
def as_path(self):
|
|
340
329
|
df = self[[a for a in self._axes if a != "t"]]
|
|
341
330
|
return PathFrame(df, columns=df._axes)
|
|
@@ -346,4 +335,4 @@ class PathFrame(AxesFrame):
|
|
|
346
335
|
dims = [a for a in "zyx" if a in self._axes]
|
|
347
336
|
path_len = lambda df: np.sum(np.sqrt(sum(np.diff(df[a])**2 for a in dims)))
|
|
348
337
|
c_axes = complement_axes(dims, self._axes)
|
|
349
|
-
return self.groupby(list(c_axes)).apply(path_len)
|
|
338
|
+
return self.groupby(list(c_axes)).apply(path_len)
|