impy-array 2.4.10__tar.gz → 2.5.1__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.10 → impy_array-2.5.1}/PKG-INFO +1 -1
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/__init__.py +1 -1
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_misc.py +14 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/axesmixin.py +52 -51
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/bases/metaarray.py +2 -5
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/imgarray.py +50 -6
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/labeledarray.py +3 -4
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/lazy.py +6 -8
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/core.py +5 -7
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/_mrc.py +20 -18
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/lazy/core.py +3 -2
- {impy_array-2.4.10 → impy_array-2.5.1}/.gitignore +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/LICENSE +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/README.md +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/__main__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/_const.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/_types.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/array_api.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_imshow.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_corr.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_deconv.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_docs.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_filters.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_glcm.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_linalg.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_plot.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_process_numba.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_skimage.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_structures.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/_utils/_transform.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/bases/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/bases/overload.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/big.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/label.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/phasearray.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/specials.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/arrays/tiled.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/axes/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/axes/_axes.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/axes/_axes_tuple.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/axes/_axis.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/axes/_slicer.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/binder.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/collections.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/correlation.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/frame/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/frame/frames.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/_czi.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/_nd2.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/_npy.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/_registry.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/_tiff.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/_utils.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/io/_zarr.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/lazy/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/lazy/random.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/random.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/roi.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/utils/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/utils/axesop.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/utils/deco.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/utils/gauss.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/utils/misc.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/utils/slicer.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/utils/utilcls.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/viewer/__init__.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/viewer/utils.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/impy/viewer/viewer.py +0 -0
- {impy_array-2.4.10 → impy_array-2.5.1}/pyproject.toml +0 -0
|
@@ -93,3 +93,17 @@ def inpaint_mean(img: np.ndarray, mask: np.ndarray):
|
|
|
93
93
|
border = expanded_i ^ mask_i
|
|
94
94
|
out[mask_i] = xp.mean(img[border])
|
|
95
95
|
return out
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def fft_crop(freq: np.ndarray, new_shape: tuple[int, ...]) -> np.ndarray:
|
|
99
|
+
"""Crop the Fourier transformed array to the new_shape."""
|
|
100
|
+
cur_freq = freq
|
|
101
|
+
for i, (n, s) in enumerate(zip(new_shape, freq.shape)):
|
|
102
|
+
if i != len(new_shape) - 1:
|
|
103
|
+
# rfftn non last axis
|
|
104
|
+
left = n // 2
|
|
105
|
+
right = s - left
|
|
106
|
+
cur_freq = np.concatenate([cur_freq[:left], cur_freq[right:]], axis=i)
|
|
107
|
+
else:
|
|
108
|
+
cur_freq = cur_freq[..., :n]
|
|
109
|
+
return cur_freq
|
|
@@ -24,28 +24,28 @@ if TYPE_CHECKING:
|
|
|
24
24
|
|
|
25
25
|
class AxesMixin:
|
|
26
26
|
"""Abstract class with shape, ndim and axes are defined."""
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
_INHERIT = object()
|
|
29
29
|
_axes: Axes
|
|
30
30
|
ndim: int
|
|
31
31
|
shape: tuple[int, ...]
|
|
32
32
|
dtype: np.dtype
|
|
33
33
|
value: Any
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
@property
|
|
36
36
|
def shape_info(self) -> str:
|
|
37
37
|
shape_info = ", ".join([f"{s}({o})" for s, o in zip(self.shape, self.axes)])
|
|
38
38
|
return shape_info
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
@property
|
|
41
41
|
def spatial_shape(self) -> tuple[int, ...]:
|
|
42
42
|
return tuple(self.sizeof(a) for a in "zyx" if a in self.axes)
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
@property
|
|
45
45
|
def axes(self) -> Axes:
|
|
46
46
|
"""Axes of the array."""
|
|
47
47
|
return self._axes
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
@axes.setter
|
|
50
50
|
def axes(self, value: Iterable[AxisLike] | None):
|
|
51
51
|
if value is None:
|
|
@@ -58,7 +58,7 @@ class AxesMixin:
|
|
|
58
58
|
f"array (ndim={self.ndim}) and axes ({value})"
|
|
59
59
|
)
|
|
60
60
|
self._axes = axes
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
@property
|
|
63
63
|
def metadata(self) -> dict[str, Any]:
|
|
64
64
|
raise NotImplementedError()
|
|
@@ -66,16 +66,16 @@ class AxesMixin:
|
|
|
66
66
|
@property
|
|
67
67
|
def scale(self) -> ScaleView:
|
|
68
68
|
return self.axes.scale
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
@scale.setter
|
|
71
71
|
def scale(self, value: dict):
|
|
72
72
|
if not isinstance(value, dict):
|
|
73
73
|
raise TypeError(f"Cannot set scale using {type(value)}.")
|
|
74
74
|
return self.set_scale(value)
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
@property
|
|
77
77
|
def scale_unit(self) -> str:
|
|
78
|
-
units =
|
|
78
|
+
units = {a.unit for a in self.axes if str(a) in "zyx"}
|
|
79
79
|
if len(units) == 0:
|
|
80
80
|
return self.axes[-1].unit
|
|
81
81
|
elif len(units) == 1:
|
|
@@ -83,21 +83,21 @@ class AxesMixin:
|
|
|
83
83
|
else:
|
|
84
84
|
warn(f"Inconsistent spatial unit: {units}.")
|
|
85
85
|
return list(units)[-1]
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
@scale_unit.setter
|
|
88
88
|
def scale_unit(self, unit) -> None:
|
|
89
89
|
unit = str(unit)
|
|
90
90
|
for a in self.axes:
|
|
91
91
|
if str(a) in ["z", "y", "x"]:
|
|
92
92
|
a.unit = unit
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
def _repr_dict_(self) -> dict[str, Any]:
|
|
95
95
|
raise NotImplementedError()
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
def __repr__(self) -> str:
|
|
98
98
|
info = "\n".join(f"{k:^16}: {v}" for k, v in self._repr_dict_().items())
|
|
99
99
|
return f"{self.__class__.__name__} of\n{info}\n"
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
def _repr_html_(self) -> str:
|
|
102
102
|
strs = []
|
|
103
103
|
for k, v in self._repr_dict_().items():
|
|
@@ -128,23 +128,22 @@ class AxesMixin:
|
|
|
128
128
|
return symbol
|
|
129
129
|
else:
|
|
130
130
|
return self.axes.find(symbol)
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
|
|
132
|
+
|
|
133
133
|
def sizeof(self, axis: AxisLike) -> int:
|
|
134
134
|
return getattr(self.shape, str(axis))
|
|
135
|
-
|
|
135
|
+
|
|
136
136
|
def sizesof(self, axes: str) -> tuple[int, ...]:
|
|
137
137
|
return tuple(self.sizeof(a) for a in axes)
|
|
138
|
-
|
|
138
|
+
|
|
139
139
|
def set_axes(self, axes) -> Self:
|
|
140
|
-
"""
|
|
141
|
-
Set axes of the array and return the updated one.
|
|
140
|
+
"""Set axes of the array and return the updated one.
|
|
142
141
|
|
|
143
142
|
Parameters
|
|
144
143
|
----------
|
|
145
144
|
axes : AxesLike
|
|
146
145
|
New axes.
|
|
147
|
-
|
|
146
|
+
|
|
148
147
|
Returns
|
|
149
148
|
-------
|
|
150
149
|
Self
|
|
@@ -164,10 +163,10 @@ class AxesMixin:
|
|
|
164
163
|
scales of common axes are copied.
|
|
165
164
|
unit : str, optional
|
|
166
165
|
Scale unit if needs update.
|
|
167
|
-
kwargs :
|
|
166
|
+
kwargs :
|
|
168
167
|
This enables function call like set_scale(x=0.1, y=0.1).
|
|
169
|
-
"""
|
|
170
|
-
|
|
168
|
+
"""
|
|
169
|
+
|
|
171
170
|
if isinstance(other, MutableMapping):
|
|
172
171
|
# voxel-scale can be set with one keyword.
|
|
173
172
|
if "zyx" in other:
|
|
@@ -186,21 +185,23 @@ class AxesMixin:
|
|
|
186
185
|
# check if all the keys are contained in axes.
|
|
187
186
|
for a, val in other.items():
|
|
188
187
|
if a not in self.axes:
|
|
189
|
-
raise ImageAxesError(f"Image does not have axis {a}.")
|
|
188
|
+
raise ImageAxesError(f"Image does not have axis {a}.")
|
|
190
189
|
elif not np.isscalar(val):
|
|
191
190
|
raise TypeError(f"Cannot set non-numeric value as scales.")
|
|
192
191
|
|
|
193
192
|
for k, v in other.items():
|
|
194
193
|
self.axes[k].scale = v
|
|
195
|
-
|
|
194
|
+
|
|
196
195
|
elif kwargs:
|
|
197
196
|
self.set_scale(dict(kwargs))
|
|
198
|
-
|
|
197
|
+
|
|
199
198
|
elif hasattr(other, "scale"):
|
|
200
199
|
self.set_scale(
|
|
201
200
|
{a: s for a, s in other.scale.items() if a in self.axes} # type: ignore
|
|
202
201
|
)
|
|
203
|
-
|
|
202
|
+
if unit is None and hasattr(other, "scale_unit"):
|
|
203
|
+
unit = other.scale_unit
|
|
204
|
+
|
|
204
205
|
elif isinstance(other, (list, tuple)):
|
|
205
206
|
for a, s in zip(self.axes, other):
|
|
206
207
|
a.scale = s
|
|
@@ -208,27 +209,26 @@ class AxesMixin:
|
|
|
208
209
|
raise TypeError(
|
|
209
210
|
f"'other' must be str or axes supported object, but got {type(other)}"
|
|
210
211
|
)
|
|
211
|
-
|
|
212
|
+
|
|
212
213
|
if unit is not None:
|
|
213
214
|
self.scale_unit = unit
|
|
214
|
-
|
|
215
|
+
|
|
215
216
|
return self
|
|
216
|
-
|
|
217
|
+
|
|
217
218
|
def set_axis_label(
|
|
218
219
|
self,
|
|
219
220
|
_dict: MutableMapping[str, Iterable[Any]] = None,
|
|
220
221
|
/,
|
|
221
222
|
**kwargs
|
|
222
223
|
) -> Self:
|
|
223
|
-
"""
|
|
224
|
-
Set labels to an axis (axes).
|
|
224
|
+
"""Set labels to an axis (axes).
|
|
225
225
|
|
|
226
226
|
Parameters
|
|
227
227
|
----------
|
|
228
228
|
_dict : mapping, optional
|
|
229
229
|
Mapping from axis to its labels. Labels must have the same length as its
|
|
230
230
|
dimensionality.
|
|
231
|
-
|
|
231
|
+
|
|
232
232
|
Examples
|
|
233
233
|
--------
|
|
234
234
|
>>> img.set_axis_label(c=["Blue", "Green", "Red"])
|
|
@@ -242,31 +242,32 @@ class AxesMixin:
|
|
|
242
242
|
for k, v in _dict.items():
|
|
243
243
|
self.axes[self.axes.find(k)].labels = v
|
|
244
244
|
return self
|
|
245
|
-
|
|
245
|
+
|
|
246
246
|
@overload
|
|
247
247
|
def iter(
|
|
248
248
|
self,
|
|
249
249
|
axes: AxesLike,
|
|
250
|
-
israw: Literal[False] = False,
|
|
250
|
+
israw: Literal[False] = False,
|
|
251
251
|
exclude: Dims = "",
|
|
252
252
|
) -> Iterator[tuple[Slices, np.ndarray]]:
|
|
253
253
|
...
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
@overload
|
|
256
256
|
def iter(
|
|
257
257
|
self,
|
|
258
258
|
axes: AxesLike,
|
|
259
|
-
israw: Literal[True] = False,
|
|
259
|
+
israw: Literal[True] = False,
|
|
260
260
|
exclude: Dims = "",
|
|
261
261
|
) -> Iterator[tuple[Slices, Self]]:
|
|
262
262
|
...
|
|
263
|
-
|
|
263
|
+
|
|
264
264
|
def iter(self, axes, israw = False, exclude = ""):
|
|
265
|
-
"""
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
"""Iteration along axes.
|
|
266
|
+
|
|
267
|
+
If axes="tzc", then equivalent to following pseudo code:
|
|
268
|
+
|
|
268
269
|
.. code-block::
|
|
269
|
-
|
|
270
|
+
|
|
270
271
|
for t in all_t:
|
|
271
272
|
for z in all_z:
|
|
272
273
|
for c in all_c:
|
|
@@ -279,19 +280,19 @@ class AxesMixin:
|
|
|
279
280
|
israw : bool, default is False
|
|
280
281
|
If True, MetaArray will be returned. If False, np.ndarray will be returned.
|
|
281
282
|
exclude : str, optional
|
|
282
|
-
Which axes will be excluded in output. For example, self.axes="tcyx" and
|
|
283
|
-
exclude="c" then the axes of output will be "tyx" and slice is also correctly
|
|
283
|
+
Which axes will be excluded in output. For example, self.axes="tcyx" and
|
|
284
|
+
exclude="c" then the axes of output will be "tyx" and slice is also correctly
|
|
284
285
|
arranged.
|
|
285
|
-
|
|
286
|
+
|
|
286
287
|
Yields
|
|
287
288
|
-------
|
|
288
289
|
slice and (np.ndarray or AxesMixin)
|
|
289
290
|
slice and a subimage=self[sl]
|
|
290
|
-
"""
|
|
291
|
+
"""
|
|
291
292
|
iterlist = switch_slice(
|
|
292
|
-
axes=axes,
|
|
293
|
+
axes=axes,
|
|
293
294
|
all_axes=self.axes,
|
|
294
|
-
ifin=[range(s) for s in self.shape],
|
|
295
|
+
ifin=[range(s) for s in self.shape],
|
|
295
296
|
ifnot=[(slice(None),)] * self.ndim
|
|
296
297
|
)
|
|
297
298
|
exclude = list(exclude)
|
|
@@ -302,11 +303,11 @@ class AxesMixin:
|
|
|
302
303
|
if len(exclude) == 0:
|
|
303
304
|
outsl = sl
|
|
304
305
|
else:
|
|
305
|
-
outsl = tuple(s for i, s in enumerate(sl)
|
|
306
|
+
outsl = tuple(s for i, s in enumerate(sl)
|
|
306
307
|
if self.axes[i] not in exclude)
|
|
307
308
|
yield outsl, selfview[sl]
|
|
308
309
|
c += 1
|
|
309
|
-
|
|
310
|
+
|
|
310
311
|
# if iterlist = []
|
|
311
312
|
if c == 0:
|
|
312
313
|
outsl = (slice(None),) * (self.ndim - len(exclude))
|
|
@@ -342,4 +343,4 @@ def _getitem(self: NamedTuple, key: slice) -> tuple[int, ...]:
|
|
|
342
343
|
def _getitem(self: NamedTuple, key, /):
|
|
343
344
|
if isinstance(key, (str, Axis)):
|
|
344
345
|
return self._asdict()[key]
|
|
345
|
-
return tuple.__getitem__(self, key)
|
|
346
|
+
return tuple.__getitem__(self, key)
|
|
@@ -170,8 +170,7 @@ class MetaArray(AxesMixin, np.ndarray):
|
|
|
170
170
|
return self[tuple(slices)]
|
|
171
171
|
|
|
172
172
|
def isel(self, indexer=None, /, **kwargs: dict[str, Any]) -> Self:
|
|
173
|
-
"""
|
|
174
|
-
A index based indexing method, mimicking ``xarray.isel``.
|
|
173
|
+
"""A index based indexing method, mimicking ``xarray.isel``.
|
|
175
174
|
|
|
176
175
|
Example
|
|
177
176
|
-------
|
|
@@ -259,9 +258,7 @@ class MetaArray(AxesMixin, np.ndarray):
|
|
|
259
258
|
|
|
260
259
|
@classmethod
|
|
261
260
|
def implements(cls, numpy_function):
|
|
262
|
-
"""
|
|
263
|
-
Add functions to NP_DISPATCH so that numpy functions can be overloaded.
|
|
264
|
-
"""
|
|
261
|
+
"""Add functions to NP_DISPATCH so that numpy functions can be overloaded."""
|
|
265
262
|
def decorator(func):
|
|
266
263
|
cls.NP_DISPATCH[numpy_function] = func
|
|
267
264
|
func.__name__ = numpy_function.__name__
|
|
@@ -340,7 +340,7 @@ class ImgArray(LabeledArray):
|
|
|
340
340
|
def binning(
|
|
341
341
|
self,
|
|
342
342
|
binsize: int = 2,
|
|
343
|
-
method = "mean",
|
|
343
|
+
method: str | Callable[[np.ndarray], Any] = "mean",
|
|
344
344
|
*,
|
|
345
345
|
check_edges: bool = True,
|
|
346
346
|
dims: Dims = None
|
|
@@ -390,6 +390,54 @@ class ImgArray(LabeledArray):
|
|
|
390
390
|
)
|
|
391
391
|
return out
|
|
392
392
|
|
|
393
|
+
@_docs.write_docs
|
|
394
|
+
@dims_to_spatial_axes
|
|
395
|
+
@same_dtype
|
|
396
|
+
def fourier_crop(
|
|
397
|
+
self,
|
|
398
|
+
binsize: float = 2,
|
|
399
|
+
dims: Dims = None,
|
|
400
|
+
) -> ImgArray:
|
|
401
|
+
"""Crop image in Fourier space.
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
binsize : float, default is 2
|
|
406
|
+
Crop factor. If 2, the output image will have half the shape of the input
|
|
407
|
+
image.
|
|
408
|
+
{dims}
|
|
409
|
+
|
|
410
|
+
Returns
|
|
411
|
+
-------
|
|
412
|
+
ImgArray
|
|
413
|
+
Cropped image.
|
|
414
|
+
"""
|
|
415
|
+
if binsize == 1:
|
|
416
|
+
return self
|
|
417
|
+
if binsize < 1:
|
|
418
|
+
raise ValueError("`binsize` must be 1 or larger.")
|
|
419
|
+
new_shape = list(self.shape)
|
|
420
|
+
new_scale = dict(self.scale)
|
|
421
|
+
for i, a in enumerate(self.axes):
|
|
422
|
+
if a in dims:
|
|
423
|
+
new_shape[i] = int(self.shape[i] / binsize)
|
|
424
|
+
new_scale[a] = self.scale[a] * binsize
|
|
425
|
+
spatial_axes = [self.axisof(a) for a in dims]
|
|
426
|
+
input = xp.asarray(self.value)
|
|
427
|
+
|
|
428
|
+
out = xp.fft.irfftn(
|
|
429
|
+
_misc.fft_crop(xp.fft.rfftn(input, axes=spatial_axes), new_shape),
|
|
430
|
+
s=new_shape,
|
|
431
|
+
axes=spatial_axes,
|
|
432
|
+
)
|
|
433
|
+
out = xp.asnumpy(out)
|
|
434
|
+
|
|
435
|
+
out_img = out.view(self.__class__)
|
|
436
|
+
out_img._set_info(self)
|
|
437
|
+
out_img.axes = self.axes.copy() # _set_info does not pass copy so new axes must be defined here.
|
|
438
|
+
out_img.set_scale(new_scale)
|
|
439
|
+
return out_img
|
|
440
|
+
|
|
393
441
|
@_docs.write_docs
|
|
394
442
|
@dims_to_spatial_axes
|
|
395
443
|
def radial_profile(
|
|
@@ -4063,8 +4111,6 @@ class ImgArray(LabeledArray):
|
|
|
4063
4111
|
|
|
4064
4112
|
if along is None:
|
|
4065
4113
|
along = find_first_appeared("tpzcia", include=self.axes)
|
|
4066
|
-
elif len(along) != 1:
|
|
4067
|
-
raise ValueError("`along` must be single character.")
|
|
4068
4114
|
if not isinstance(upsample_factor, int):
|
|
4069
4115
|
raise TypeError(f"upsample-factor must be integer but got {type(upsample_factor)}")
|
|
4070
4116
|
if max_shift is not None:
|
|
@@ -4074,7 +4120,7 @@ class ImgArray(LabeledArray):
|
|
|
4074
4120
|
c_axes = complement_axes(along, self.axes)
|
|
4075
4121
|
last_img = None
|
|
4076
4122
|
img_fft = self.fft(shift=False, dims=c_axes)
|
|
4077
|
-
for i, (_, img) in enumerate(img_fft.iter(along)):
|
|
4123
|
+
for i, (_, img) in enumerate(img_fft.iter([along])):
|
|
4078
4124
|
img = xp.asarray(img)
|
|
4079
4125
|
if last_img is not None:
|
|
4080
4126
|
result[i] = xp.asnumpy(
|
|
@@ -4150,8 +4196,6 @@ class ImgArray(LabeledArray):
|
|
|
4150
4196
|
|
|
4151
4197
|
if along is None:
|
|
4152
4198
|
along = find_first_appeared("tpzcia", include=self.axes, exclude=dims)
|
|
4153
|
-
elif len(along) != 1:
|
|
4154
|
-
raise ValueError("`along` must be single character.")
|
|
4155
4199
|
|
|
4156
4200
|
if shift is None:
|
|
4157
4201
|
# determine 'ref'
|
|
@@ -273,8 +273,7 @@ class LabeledArray(MetaArray):
|
|
|
273
273
|
dtype: DTypeLike = None,
|
|
274
274
|
overwrite: bool = True,
|
|
275
275
|
) -> None:
|
|
276
|
-
"""
|
|
277
|
-
Save image at the same directory as the original image by default.
|
|
276
|
+
"""Save image at the same directory as the original image by default.
|
|
278
277
|
|
|
279
278
|
For tif file format, if the image contains wrong axes for ImageJ (= except for tzcyx),
|
|
280
279
|
then it will converted automatically if possible. For mrc file format, only zyx and yx is
|
|
@@ -290,7 +289,7 @@ class LabeledArray(MetaArray):
|
|
|
290
289
|
Whether to overwrite the file if it already exists.
|
|
291
290
|
|
|
292
291
|
"""
|
|
293
|
-
save_path = Path(save_path)
|
|
292
|
+
save_path = Path(save_path).expanduser()
|
|
294
293
|
if self.ndim < 2:
|
|
295
294
|
raise ValueError("Cannot save <2D array as an image.")
|
|
296
295
|
|
|
@@ -310,7 +309,7 @@ class LabeledArray(MetaArray):
|
|
|
310
309
|
" >>> img.source = \"...\"\n"
|
|
311
310
|
"or specify absolute path like\n"
|
|
312
311
|
" >>> img.imsave(\"/path/to/XXX.tif\")"
|
|
313
|
-
|
|
312
|
+
)
|
|
314
313
|
save_path = self.source.parent / save_path
|
|
315
314
|
|
|
316
315
|
if not overwrite and save_path.exists():
|
|
@@ -339,7 +339,7 @@ class LazyImgArray(AxesMixin):
|
|
|
339
339
|
|
|
340
340
|
@_docs.copy_docs(LabeledArray.imsave)
|
|
341
341
|
def imsave(self, save_path: str | Path, *, dtype = None, overwrite: bool = True):
|
|
342
|
-
save_path = Path(save_path)
|
|
342
|
+
save_path = Path(save_path).expanduser()
|
|
343
343
|
if self.ndim < 2:
|
|
344
344
|
raise ValueError("Cannot save <2D array as an image.")
|
|
345
345
|
|
|
@@ -776,12 +776,13 @@ class LazyImgArray(AxesMixin):
|
|
|
776
776
|
sigma: nDFloat = 1.0,
|
|
777
777
|
*,
|
|
778
778
|
dims: Dims = None,
|
|
779
|
+
fourier: bool = False,
|
|
779
780
|
update: bool = False
|
|
780
781
|
) -> LazyImgArray:
|
|
781
782
|
c_axes = complement_axes(dims, self.axes)
|
|
782
783
|
depth = _ceilint(sigma*4)
|
|
783
784
|
return self._apply_map_overlap(
|
|
784
|
-
|
|
785
|
+
_filters.gaussian_filter_fourier if fourier else _filters.gaussian_filter,
|
|
785
786
|
c_axes=c_axes,
|
|
786
787
|
depth=depth,
|
|
787
788
|
kwargs=dict(sigma=sigma),
|
|
@@ -795,13 +796,14 @@ class LazyImgArray(AxesMixin):
|
|
|
795
796
|
low_sigma: nDFloat = 1.0,
|
|
796
797
|
high_sigma: nDFloat = 2.0,
|
|
797
798
|
*,
|
|
799
|
+
fourier: bool = False,
|
|
798
800
|
dims: Dims = None,
|
|
799
801
|
update: bool = False
|
|
800
802
|
) -> LazyImgArray:
|
|
801
803
|
c_axes = complement_axes(dims, self.axes)
|
|
802
804
|
depth = _ceilint(high_sigma*4)
|
|
803
805
|
return self._apply_map_overlap(
|
|
804
|
-
_filters.dog_filter,
|
|
806
|
+
_filters.dog_filter_fourier if fourier else _filters.dog_filter,
|
|
805
807
|
c_axes=c_axes,
|
|
806
808
|
depth=depth,
|
|
807
809
|
kwargs=dict(low_sigma=low_sigma, high_sigma=high_sigma),
|
|
@@ -1317,8 +1319,6 @@ class LazyImgArray(AxesMixin):
|
|
|
1317
1319
|
def track_drift(self, along: str = None, upsample_factor: int = 10) -> DaskArray:
|
|
1318
1320
|
if along is None:
|
|
1319
1321
|
along = find_first_appeared("tpzc<i", include=self.axes)
|
|
1320
|
-
elif len(along) != 1:
|
|
1321
|
-
raise ValueError("`along` must be single character.")
|
|
1322
1322
|
|
|
1323
1323
|
dims = complement_axes(along, self.axes)
|
|
1324
1324
|
chunks = switch_slice(dims, self.axes, ifin=self.shape, ifnot=1)
|
|
@@ -1370,8 +1370,6 @@ class LazyImgArray(AxesMixin):
|
|
|
1370
1370
|
) -> LazyImgArray:
|
|
1371
1371
|
if along is None:
|
|
1372
1372
|
along = find_first_appeared("tpzcia", include=self.axes, exclude=dims)
|
|
1373
|
-
elif len(along) != 1:
|
|
1374
|
-
raise ValueError("`along` must be single character.")
|
|
1375
1373
|
|
|
1376
1374
|
from impy.frame import MarkerFrame
|
|
1377
1375
|
|
|
@@ -1387,7 +1385,7 @@ class LazyImgArray(AxesMixin):
|
|
|
1387
1385
|
dims = _dims
|
|
1388
1386
|
elif not isinstance(ref, self.__class__):
|
|
1389
1387
|
ref = self[ref]
|
|
1390
|
-
if ref.axes != along + dims:
|
|
1388
|
+
if ref.axes != [along] + dims:
|
|
1391
1389
|
raise ValueError(f"Arguments `along`({along}) + `dims`({dims}) do not match "
|
|
1392
1390
|
f"axes of `ref`({ref.axes})")
|
|
1393
1391
|
|
|
@@ -501,9 +501,8 @@ def imread(
|
|
|
501
501
|
name: str | None = None,
|
|
502
502
|
squeeze: bool = False,
|
|
503
503
|
) -> ImgArray:
|
|
504
|
-
"""
|
|
505
|
-
|
|
506
|
-
wildcards or ``"$"`` in ``path``.
|
|
504
|
+
"""Load image(s) from a path.
|
|
505
|
+
You can read list of images from directories with wildcards or ``"$"`` in ``path``.
|
|
507
506
|
|
|
508
507
|
Parameters
|
|
509
508
|
----------
|
|
@@ -536,7 +535,7 @@ def imread(
|
|
|
536
535
|
Wall time: 3.01 ms
|
|
537
536
|
|
|
538
537
|
"""
|
|
539
|
-
path = str(path)
|
|
538
|
+
path = str(Path(path).expanduser())
|
|
540
539
|
is_memmap = (key is not None)
|
|
541
540
|
|
|
542
541
|
if "$" in path:
|
|
@@ -593,11 +592,10 @@ def imread(
|
|
|
593
592
|
if k in "zyx":
|
|
594
593
|
self.axes[k].unit = scale_unit[k]
|
|
595
594
|
|
|
596
|
-
return self.
|
|
595
|
+
return self.as_img_type(dtype) # arrange in tzcyx-order
|
|
597
596
|
|
|
598
597
|
def _imread_glob(path: str, squeeze: bool = False, **kwargs) -> ImgArray:
|
|
599
|
-
"""
|
|
600
|
-
Read images recursively from a directory, and stack them into one ImgArray.
|
|
598
|
+
"""Read images recursively from a directory, and stack them into one ImgArray.
|
|
601
599
|
|
|
602
600
|
Parameters
|
|
603
601
|
----------
|
|
@@ -7,12 +7,10 @@ import numpy as np
|
|
|
7
7
|
from impy.io._registry import IO
|
|
8
8
|
from impy.io._utils import rechunk_to_ones, MemmapArrayWriter, ImpyArray, ImageData, ImageMetadata
|
|
9
9
|
|
|
10
|
-
from impy.axes import ImageAxesError
|
|
11
|
-
|
|
12
10
|
if TYPE_CHECKING:
|
|
13
11
|
from mrcfile.mrcobject import MrcObject
|
|
14
12
|
|
|
15
|
-
@IO.mark_reader(".mrc", ".rec", ".st", ".map", ".mrc.gz", ".map.gz")
|
|
13
|
+
@IO.mark_reader(".mrc", ".rec", ".st", ".map", ".mrc.gz", ".map.gz", ".mrcs")
|
|
16
14
|
def _(path: str, memmap: bool = False) -> ImageData:
|
|
17
15
|
"""The MRC format reader"""
|
|
18
16
|
import mrcfile
|
|
@@ -29,7 +27,7 @@ def _(path: str, memmap: bool = False) -> ImageData:
|
|
|
29
27
|
return ImageData.from_metadata(image, meta)
|
|
30
28
|
|
|
31
29
|
|
|
32
|
-
@IO.mark_header_reader(".mrc", ".rec", ".st", ".map", ".mrc.gz", ".map.gz")
|
|
30
|
+
@IO.mark_header_reader(".mrc", ".rec", ".st", ".map", ".mrc.gz", ".map.gz", ".mrcs")
|
|
33
31
|
def _(path: str) -> ImageData:
|
|
34
32
|
"""The MRC header reader"""
|
|
35
33
|
import mrcfile
|
|
@@ -39,12 +37,14 @@ def _(path: str) -> ImageData:
|
|
|
39
37
|
|
|
40
38
|
return meta
|
|
41
39
|
|
|
42
|
-
@IO.mark_writer(".mrc", ".rec", ".st", ".map")
|
|
40
|
+
@IO.mark_writer(".mrc", ".rec", ".st", ".map", ".mrcs")
|
|
43
41
|
def _(path: str, img: ImpyArray, lazy: bool = False):
|
|
44
42
|
"""The MRC writer."""
|
|
45
43
|
|
|
46
44
|
import mrcfile
|
|
47
45
|
|
|
46
|
+
input_axes = [str(a) for a in img.axes]
|
|
47
|
+
|
|
48
48
|
if img.scale_unit == "nm":
|
|
49
49
|
voxel_size = tuple(np.array(img.scale)[::-1] * 10)
|
|
50
50
|
elif img.scale_unit in ("ang", "Å", "angstrom"):
|
|
@@ -56,6 +56,8 @@ def _(path: str, img: ImpyArray, lazy: bool = False):
|
|
|
56
56
|
voxel_size = (1.0, 1.0, 1.0)
|
|
57
57
|
if len(voxel_size) == 2:
|
|
58
58
|
voxel_size = (1.0,) + voxel_size
|
|
59
|
+
elif len(voxel_size) > 3:
|
|
60
|
+
voxel_size = voxel_size[:3]
|
|
59
61
|
|
|
60
62
|
if img.dtype == "bool":
|
|
61
63
|
img = img.astype(np.int8)
|
|
@@ -79,20 +81,16 @@ def _(path: str, img: ImpyArray, lazy: bool = False):
|
|
|
79
81
|
return None
|
|
80
82
|
|
|
81
83
|
# get voxel_size
|
|
82
|
-
if img.axes not in ("zyx", "yx"):
|
|
83
|
-
raise ImageAxesError(
|
|
84
|
-
f"Can only save zyx- or yx- image as a mrc file, but image has {img.axes} "
|
|
85
|
-
"axes."
|
|
86
|
-
)
|
|
87
84
|
if Path(path).exists():
|
|
88
|
-
|
|
89
|
-
mrc.set_data(img.value)
|
|
90
|
-
mrc.voxel_size = voxel_size
|
|
91
|
-
|
|
85
|
+
ctx_mgr = mrcfile.open(path, mode="r+")
|
|
92
86
|
else:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
87
|
+
ctx_mgr = mrcfile.new(path)
|
|
88
|
+
with ctx_mgr as mrc:
|
|
89
|
+
mrc.set_data(img.value)
|
|
90
|
+
mrc.voxel_size = voxel_size
|
|
91
|
+
if input_axes == ["t", "y", "x"]:
|
|
92
|
+
mrc.set_image_stack()
|
|
93
|
+
|
|
96
94
|
return None
|
|
97
95
|
|
|
98
96
|
_MRC_MODE = {
|
|
@@ -115,7 +113,11 @@ def _parse_mrcfile(mrc: MrcObject) -> ImageMetadata:
|
|
|
115
113
|
metadata = {}
|
|
116
114
|
scale = dict.fromkeys(axes, 1.0)
|
|
117
115
|
for a in axes:
|
|
118
|
-
|
|
116
|
+
if a in "zyx":
|
|
117
|
+
this_scale = mrc.voxel_size[a] / 10
|
|
118
|
+
if this_scale == 0:
|
|
119
|
+
this_scale = 1.0
|
|
120
|
+
scale[a] = this_scale
|
|
119
121
|
try:
|
|
120
122
|
orig_x, orig_y, orig_z = mrc.header["origin"].item()
|
|
121
123
|
except Exception:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
import re
|
|
5
6
|
import glob
|
|
6
7
|
from typing import TYPE_CHECKING, Any, Callable, Sequence
|
|
@@ -244,7 +245,7 @@ def imread(
|
|
|
244
245
|
-------
|
|
245
246
|
LazyImgArray
|
|
246
247
|
"""
|
|
247
|
-
path = str(path)
|
|
248
|
+
path = str(Path(path).expanduser())
|
|
248
249
|
if "*" in path:
|
|
249
250
|
return _imread_glob(path, chunks=chunks, squeeze=squeeze)
|
|
250
251
|
if not os.path.exists(path):
|
|
@@ -277,7 +278,7 @@ def imread(
|
|
|
277
278
|
if k in "zyx":
|
|
278
279
|
self.axes[k].unit = spatial_scale_unit.get(k)
|
|
279
280
|
|
|
280
|
-
return self
|
|
281
|
+
return self
|
|
281
282
|
|
|
282
283
|
|
|
283
284
|
def _imread_glob(path: str, squeeze: bool = False, **kwargs) -> LazyImgArray:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|