impy-array 2.4.4__py3-none-any.whl → 2.4.6__py3-none-any.whl
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/__init__.py +30 -10
- impy/arrays/_utils/_misc.py +3 -0
- impy/arrays/big.py +0 -1
- impy/arrays/imgarray.py +50 -3
- impy/arrays/labeledarray.py +58 -9
- impy/arrays/lazy.py +0 -13
- impy/arrays/tiled.py +67 -20
- impy/core.py +154 -133
- impy/io/__init__.py +13 -0
- impy/io/_mrc.py +119 -0
- impy/io/_nd2.py +55 -0
- impy/io/_npy.py +77 -0
- impy/io/_registry.py +351 -0
- impy/io/_tiff.py +200 -0
- impy/io/_utils.py +122 -0
- impy/io/_zarr.py +49 -0
- impy/viewer/__init__.py +1 -1
- {impy_array-2.4.4.dist-info → impy_array-2.4.6.dist-info}/METADATA +2 -1
- {impy_array-2.4.4.dist-info → impy_array-2.4.6.dist-info}/RECORD +22 -15
- impy/io.py +0 -749
- {impy_array-2.4.4.dist-info → impy_array-2.4.6.dist-info}/WHEEL +0 -0
- {impy_array-2.4.4.dist-info → impy_array-2.4.6.dist-info}/entry_points.txt +0 -0
- {impy_array-2.4.4.dist-info → impy_array-2.4.6.dist-info}/licenses/LICENSE +0 -0
impy/__init__.py
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
__version__ = "2.4.
|
|
1
|
+
__version__ = "2.4.6"
|
|
2
2
|
__author__ = "Hanjin Liu"
|
|
3
3
|
__email__ = "liuhanjin-sc@g.ecc.u-tokyo.ac.jp"
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
6
7
|
|
|
7
|
-
from ._const import Const, SetConst, use # noqa
|
|
8
|
+
from impy._const import Const, SetConst, use # noqa
|
|
8
9
|
|
|
9
|
-
from .collections import DataList, DataDict # noqa
|
|
10
|
-
from .core import * # noqa
|
|
11
|
-
from .binder import bind # noqa
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from
|
|
15
|
-
from . import
|
|
16
|
-
from .axes import slicer # noqa
|
|
10
|
+
from impy.collections import DataList, DataDict # noqa
|
|
11
|
+
from impy.core import * # noqa
|
|
12
|
+
from impy.binder import bind # noqa
|
|
13
|
+
from impy.correlation import * # noqa
|
|
14
|
+
from impy.arrays import ImgArray, LazyImgArray, BigImgArray, Label # noqa
|
|
15
|
+
from impy import random, io, lazy # noqa
|
|
16
|
+
from impy.axes import slicer # noqa
|
|
17
17
|
|
|
18
18
|
# Inheritance
|
|
19
19
|
# -----------
|
|
@@ -41,3 +41,23 @@ from numpy import ( # noqa
|
|
|
41
41
|
complex64, complex128,
|
|
42
42
|
bool_,
|
|
43
43
|
)
|
|
44
|
+
|
|
45
|
+
# lazy loading
|
|
46
|
+
|
|
47
|
+
_VIEWER_CACHE = None
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
from impy.viewer import napariViewers
|
|
50
|
+
|
|
51
|
+
gui: "napariViewers"
|
|
52
|
+
|
|
53
|
+
def __getattr__(key):
|
|
54
|
+
global _VIEWER_CACHE
|
|
55
|
+
|
|
56
|
+
if key == "gui":
|
|
57
|
+
from impy.viewer import napariViewers
|
|
58
|
+
|
|
59
|
+
if _VIEWER_CACHE is None:
|
|
60
|
+
_VIEWER_CACHE = napariViewers()
|
|
61
|
+
return _VIEWER_CACHE
|
|
62
|
+
else:
|
|
63
|
+
raise AttributeError(f"module 'impy' has no attribute '{key}'")
|
impy/arrays/_utils/_misc.py
CHANGED
|
@@ -41,6 +41,9 @@ def make_rotated_axis(src, dst):
|
|
|
41
41
|
n = int(round(d))
|
|
42
42
|
return np.linspace(src, src + dr / d * (n - 1), n)
|
|
43
43
|
|
|
44
|
+
def make_rotated_axis_by_vec(src, unit_vec, n: int):
|
|
45
|
+
return np.linspace(src, src + unit_vec * (n - 1), n)
|
|
46
|
+
|
|
44
47
|
def make_pad(pad_width, dims, all_axes, **kwargs):
|
|
45
48
|
"""More flexible padding than `np.pad`."""
|
|
46
49
|
pad_width_ = []
|
impy/arrays/big.py
CHANGED
|
@@ -31,7 +31,6 @@ class BigImgArray(LazyImgArray):
|
|
|
31
31
|
fft = wrap_method(LazyImgArray.fft)
|
|
32
32
|
ifft = wrap_method(LazyImgArray.ifft)
|
|
33
33
|
power_spectra = wrap_method(LazyImgArray.power_spectra)
|
|
34
|
-
tiled_lowpass_filter = wrap_method(LazyImgArray.tiled_lowpass_filter)
|
|
35
34
|
proj = wrap_method(LazyImgArray.proj)
|
|
36
35
|
binning = wrap_method(LazyImgArray.binning)
|
|
37
36
|
track_drift = wrap_method(LazyImgArray.track_drift)
|
impy/arrays/imgarray.py
CHANGED
|
@@ -217,13 +217,13 @@ class ImgArray(LabeledArray):
|
|
|
217
217
|
c_axes=complement_axes(dims, self.axes),
|
|
218
218
|
kwargs=dict(matrix=mx, order=order, mode=mode, cval=cval),
|
|
219
219
|
)
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
@_docs.write_docs
|
|
222
222
|
@dims_to_spatial_axes
|
|
223
223
|
@same_dtype(asfloat=True)
|
|
224
224
|
@check_input_and_output
|
|
225
225
|
def shift(
|
|
226
|
-
self, translation, order: int = 3, mode: PaddingMode = "constant",
|
|
226
|
+
self, translation, order: int = 3, mode: PaddingMode = "constant",
|
|
227
227
|
cval: float = 0, prefilter: bool | None = None, dims: Dims = None,
|
|
228
228
|
update: bool = False,
|
|
229
229
|
) -> ImgArray:
|
|
@@ -329,7 +329,9 @@ class ImgArray(LabeledArray):
|
|
|
329
329
|
out.set_scale({a: self.scale[a]/scale for a, scale in zip(self.axes, zoom_)})
|
|
330
330
|
return out
|
|
331
331
|
|
|
332
|
-
|
|
332
|
+
def log(self, eps: float = 1e-8) -> ImgArray:
|
|
333
|
+
"""Calculate natural logarithm of the image, with a small epsilon."""
|
|
334
|
+
return np.log(self.as_float() + eps)
|
|
333
335
|
|
|
334
336
|
@_docs.write_docs
|
|
335
337
|
@dims_to_spatial_axes
|
|
@@ -902,6 +904,51 @@ class ImgArray(LabeledArray):
|
|
|
902
904
|
return xp.asnumpy(out)
|
|
903
905
|
|
|
904
906
|
|
|
907
|
+
@_docs.write_docs
|
|
908
|
+
@dims_to_spatial_axes
|
|
909
|
+
@same_dtype(asfloat=True)
|
|
910
|
+
@check_input_and_output
|
|
911
|
+
def bandpass_filter(
|
|
912
|
+
self,
|
|
913
|
+
cuton: nDFloat = 0.02,
|
|
914
|
+
cutoff: nDFloat = 0.2,
|
|
915
|
+
order: float = 2,
|
|
916
|
+
*,
|
|
917
|
+
dims: Dims = None,
|
|
918
|
+
update: bool = False
|
|
919
|
+
):
|
|
920
|
+
"""
|
|
921
|
+
Butterworth band-pass filter.
|
|
922
|
+
|
|
923
|
+
Parameters
|
|
924
|
+
----------
|
|
925
|
+
cuton, cutoff : float or array-like
|
|
926
|
+
Cuton and Cutoff frequency. Fequency domain between these two values will be
|
|
927
|
+
passed.
|
|
928
|
+
order : float, default is 2
|
|
929
|
+
Steepness of cutoff.
|
|
930
|
+
{dims}{update}
|
|
931
|
+
|
|
932
|
+
Returns
|
|
933
|
+
-------
|
|
934
|
+
ImgArray
|
|
935
|
+
Filtered image
|
|
936
|
+
"""
|
|
937
|
+
from ._utils._skimage import _get_ND_butterworth_filter
|
|
938
|
+
cuton = check_nd(cuton, len(dims))
|
|
939
|
+
cutoff = check_nd(cutoff, len(dims))
|
|
940
|
+
spatial_shape = self.sizesof(dims)
|
|
941
|
+
spatial_axes = [self.axisof(a) for a in dims]
|
|
942
|
+
weight_on = _get_ND_butterworth_filter(spatial_shape, cuton, order, True, True)
|
|
943
|
+
weight_off = _get_ND_butterworth_filter(spatial_shape, cutoff, order, False, True)
|
|
944
|
+
weight = weight_off * weight_on
|
|
945
|
+
input = xp.asarray(self.value)
|
|
946
|
+
if len(dims) < self.ndim:
|
|
947
|
+
weight = add_axes(self.axes, self.shape, weight, dims)
|
|
948
|
+
out = xp.fft.irfftn(xp.asarray(weight)*xp.fft.rfftn(input, axes=spatial_axes),
|
|
949
|
+
s=spatial_shape, axes=spatial_axes)
|
|
950
|
+
return xp.asnumpy(out)
|
|
951
|
+
|
|
905
952
|
@_docs.write_docs
|
|
906
953
|
@dims_to_spatial_axes
|
|
907
954
|
@same_dtype(asfloat=True)
|
impy/arrays/labeledarray.py
CHANGED
|
@@ -848,8 +848,8 @@ class LabeledArray(MetaArray):
|
|
|
848
848
|
Parameters
|
|
849
849
|
----------
|
|
850
850
|
pixel : int or array-like, default is 1
|
|
851
|
-
Number of pixels to remove. If an array is given, each axis will be cropped
|
|
852
|
-
using each value respectively.
|
|
851
|
+
Number of pixels to remove. If an array is given, each axis will be cropped
|
|
852
|
+
with different pixels, using each value respectively.
|
|
853
853
|
{dims}
|
|
854
854
|
|
|
855
855
|
Returns
|
|
@@ -869,16 +869,65 @@ class LabeledArray(MetaArray):
|
|
|
869
869
|
out = self[fmt[sl]]
|
|
870
870
|
return out
|
|
871
871
|
|
|
872
|
+
def crop_subregion(
|
|
873
|
+
self,
|
|
874
|
+
centers: Coords,
|
|
875
|
+
shape: tuple[int, int],
|
|
876
|
+
angles: float | Sequence[float] = 0,
|
|
877
|
+
*,
|
|
878
|
+
order=3,
|
|
879
|
+
) -> Self:
|
|
880
|
+
"""
|
|
881
|
+
Crop a subragion of an image.
|
|
882
|
+
|
|
883
|
+
Parameters
|
|
884
|
+
----------
|
|
885
|
+
centers : sequence of (float, float)
|
|
886
|
+
Center coordinates in pixels.
|
|
887
|
+
shape : (int, int)
|
|
888
|
+
Shape of the subregion.
|
|
889
|
+
angles : float or sequence of float, default is 0
|
|
890
|
+
Clockwise angle of the subregion.
|
|
891
|
+
{dims}
|
|
892
|
+
"""
|
|
893
|
+
if order > 1:
|
|
894
|
+
self_filt = self.spline_filter(order).value
|
|
895
|
+
else:
|
|
896
|
+
self_filt = self.value
|
|
897
|
+
centers = np.asarray(centers)
|
|
898
|
+
if np.isscalar(angles):
|
|
899
|
+
angles = [angles] * len(centers)
|
|
900
|
+
else:
|
|
901
|
+
if len(centers) != len(angles):
|
|
902
|
+
raise ValueError("Length of centers and angles must be the same.")
|
|
903
|
+
cropped_imgs = []
|
|
904
|
+
for center, angle in zip(centers, angles, strict=True):
|
|
905
|
+
center = np.asarray(center)
|
|
906
|
+
rad = np.deg2rad(angle)
|
|
907
|
+
ey = np.array([np.sin(rad), np.cos(rad)])
|
|
908
|
+
ex = np.array([np.cos(rad), -np.sin(rad)])
|
|
909
|
+
ny, nx = shape
|
|
910
|
+
origin = center - ey * (nx - 1) / 2 - ex * (ny - 1) / 2
|
|
911
|
+
ax0 = _misc.make_rotated_axis_by_vec(origin, ex, nx)
|
|
912
|
+
ax1 = _misc.make_rotated_axis_by_vec(origin, ey, ny)
|
|
913
|
+
all_coords = ax0[:, np.newaxis] + ax1[np.newaxis] - origin
|
|
914
|
+
all_coords = np.moveaxis(all_coords, -1, 0)
|
|
915
|
+
cropped_img = ndi.map_coordinates(self_filt, all_coords, prefilter=False, order=order)
|
|
916
|
+
cropped_imgs.append(cropped_img)
|
|
917
|
+
cropped_imgs = np.stack(cropped_imgs, axis=0).view(self.__class__)
|
|
918
|
+
return cropped_imgs._set_info(self, new_axes="p" + self.axes)
|
|
919
|
+
|
|
872
920
|
@_docs.write_docs
|
|
873
921
|
@check_input_and_output
|
|
874
922
|
@dims_to_spatial_axes
|
|
875
|
-
def rotated_crop(self, origin, dst1, dst2, dims=2) -> Self:
|
|
923
|
+
def rotated_crop(self, origin, dst1, dst2, *, crop_labels: bool = True, dims=2) -> Self:
|
|
876
924
|
"""
|
|
877
|
-
Crop the image at four courners of an rotated rectangle. Currently only supports
|
|
878
|
-
yx-plane. An rotated rectangle is specified with positions of a
|
|
879
|
-
and `dst2`, i.e., vectors (dst1-origin) and
|
|
880
|
-
origin
|
|
881
|
-
|
|
925
|
+
Crop the image at four courners of an rotated rectangle. Currently only supports
|
|
926
|
+
rotation within yx-plane. An rotated rectangle is specified with positions of a
|
|
927
|
+
origin and two destinations `dst1` and `dst2`, i.e., vectors (dst1-origin) and
|
|
928
|
+
(dst2-origin) represent a rotated rectangle. Let origin be the origin of a
|
|
929
|
+
xy-plane, the rotation direction from dst1 to dst2 must be counter-clockwise,
|
|
930
|
+
or the cropped image will be reversed.
|
|
882
931
|
|
|
883
932
|
Parameters
|
|
884
933
|
----------
|
|
@@ -907,7 +956,7 @@ class LabeledArray(MetaArray):
|
|
|
907
956
|
)
|
|
908
957
|
cropped_img = cropped_img.view(self.__class__)
|
|
909
958
|
cropped_img.axes = self.axes
|
|
910
|
-
if self.labels is not None:
|
|
959
|
+
if crop_labels and self.labels is not None:
|
|
911
960
|
try:
|
|
912
961
|
lbl = self.labels
|
|
913
962
|
cropped_labels = np.empty(lbl.shape[:-2] + all_coords.shape[1:], dtype=lbl.dtype)
|
impy/arrays/lazy.py
CHANGED
|
@@ -1041,19 +1041,6 @@ class LazyImgArray(AxesMixin):
|
|
|
1041
1041
|
|
|
1042
1042
|
return out
|
|
1043
1043
|
|
|
1044
|
-
@dims_to_spatial_axes
|
|
1045
|
-
@check_input_and_output_lazy
|
|
1046
|
-
def tiled_lowpass_filter(self, cutoff: float = 0.2, order: int = 2, overlap: int = 16, *,
|
|
1047
|
-
dims: Dims = None, update: bool = False) -> LazyImgArray:
|
|
1048
|
-
warnings.warn(
|
|
1049
|
-
"`tiled_lowpass_filter` is deprecated. Please use "
|
|
1050
|
-
f"`img.tiled({overlap=}).lowpass_filter({cutoff=}, {order=})` instead.",
|
|
1051
|
-
DeprecationWarning,
|
|
1052
|
-
)
|
|
1053
|
-
return self.tiled(
|
|
1054
|
-
chunks=self.chunksizesof(dims), overlap=overlap, dims=dims,
|
|
1055
|
-
).lowpass_filter(cutoff, order)
|
|
1056
|
-
|
|
1057
1044
|
@_docs.copy_docs(ImgArray.proj)
|
|
1058
1045
|
@same_dtype
|
|
1059
1046
|
def proj(self, axis: str = None, method: str = "mean") -> LazyImgArray:
|
impy/arrays/tiled.py
CHANGED
|
@@ -24,10 +24,10 @@ class TiledAccessor(Generic[_T]):
|
|
|
24
24
|
class _PartialTiledImage(Generic[_T]):
|
|
25
25
|
def __init__(self, img: _T):
|
|
26
26
|
self._img = weakref.ref(img)
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
def __call__(
|
|
29
|
-
self,
|
|
30
|
-
chunks: tuple[int, ...] | Literal["auto"] = "auto",
|
|
29
|
+
self,
|
|
30
|
+
chunks: tuple[int, ...] | Literal["auto"] = "auto",
|
|
31
31
|
overlap: int | tuple[int, ...] = 32,
|
|
32
32
|
boundary: Boundary | list[Boundary] = "reflect",
|
|
33
33
|
dims: Dims = None,
|
|
@@ -78,13 +78,13 @@ class TiledImage(Generic[_T]):
|
|
|
78
78
|
else:
|
|
79
79
|
img_repr = repr(img)
|
|
80
80
|
return f"TiledImage<chunks={self.chunks}, depth={self.depth}, boundary={self.boundary}> of \n{img_repr}"
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
def _deref_image(self) -> _T:
|
|
83
83
|
img = self._img()
|
|
84
84
|
if img is None:
|
|
85
85
|
raise RuntimeError("Image has been deleted")
|
|
86
86
|
return img
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
def _map_overlap(self, func: Callable[[np.ndarray], np.ndarray], *args, **kwargs) -> np.ndarray:
|
|
89
89
|
from .imgarray import ImgArray
|
|
90
90
|
from .lazy import LazyImgArray
|
|
@@ -92,14 +92,14 @@ class TiledImage(Generic[_T]):
|
|
|
92
92
|
img = self._deref_image()
|
|
93
93
|
if isinstance(img, ImgArray):
|
|
94
94
|
from dask import array as da
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
input = da.from_array(img.value, chunks=self._chunks)
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
def _func(arr: np.ndarray, *args, **kwargs) -> np.ndarray:
|
|
99
99
|
return func(arr, *args, **kwargs).astype(arr.dtype, copy=False)
|
|
100
100
|
out: np.ndarray = xp.asnumpy(
|
|
101
101
|
da.map_overlap(
|
|
102
|
-
_func,
|
|
102
|
+
_func,
|
|
103
103
|
input,
|
|
104
104
|
*args,
|
|
105
105
|
depth=self.depth,
|
|
@@ -116,7 +116,7 @@ class TiledImage(Generic[_T]):
|
|
|
116
116
|
func,
|
|
117
117
|
c_axes="",
|
|
118
118
|
depth=self.depth,
|
|
119
|
-
boundary=self.boundary,
|
|
119
|
+
boundary=self.boundary,
|
|
120
120
|
dtype=img.dtype,
|
|
121
121
|
args=args,
|
|
122
122
|
kwargs=kwargs,
|
|
@@ -138,7 +138,33 @@ class TiledImage(Generic[_T]):
|
|
|
138
138
|
Steepness of cutoff.
|
|
139
139
|
"""
|
|
140
140
|
return self._map_overlap(_lowpass, cutoff=cutoff, order=order)
|
|
141
|
-
|
|
141
|
+
|
|
142
|
+
def highpass_filter(self, cutoff: float = 0.2, order: int = 2) -> _T:
|
|
143
|
+
"""
|
|
144
|
+
Tile-wise butterworth highpass filter.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
cutoff : float or array-like, default is 0.2
|
|
149
|
+
Cutoff frequency.
|
|
150
|
+
order : float, default is 2
|
|
151
|
+
Steepness of cutoff.
|
|
152
|
+
"""
|
|
153
|
+
return self._map_overlap(_highpass, cutoff=cutoff, order=order)
|
|
154
|
+
|
|
155
|
+
def bandpass_filter(self, cuton: float, cutoff: float, order: int = 2) -> _T:
|
|
156
|
+
"""
|
|
157
|
+
Tile-wise butterworth bandpass filter.
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
cuton, cutoff : float or array-like
|
|
162
|
+
Cuton and cutoff frequency.
|
|
163
|
+
order : float, default is 2
|
|
164
|
+
Steepness of cutoff.
|
|
165
|
+
"""
|
|
166
|
+
return self._map_overlap(_bandpass, low_cutoff=cuton, high_cutoff=cutoff, order=order)
|
|
167
|
+
|
|
142
168
|
def lucy(
|
|
143
169
|
self,
|
|
144
170
|
psf: np.ndarray | Callable[[tuple[int, ...]], np.ndarray],
|
|
@@ -147,7 +173,7 @@ class TiledImage(Generic[_T]):
|
|
|
147
173
|
) -> _T:
|
|
148
174
|
"""
|
|
149
175
|
Deconvolution of N-dimensional image, using Richardson-Lucy's algorithm.
|
|
150
|
-
|
|
176
|
+
|
|
151
177
|
Parameters
|
|
152
178
|
----------
|
|
153
179
|
psf : ndarray or callable
|
|
@@ -156,30 +182,30 @@ class TiledImage(Generic[_T]):
|
|
|
156
182
|
niter : int, default is 50.
|
|
157
183
|
Number of iterations.
|
|
158
184
|
eps : float, default is 1e-5
|
|
159
|
-
During deconvolution, division by small values in the convolve image
|
|
160
|
-
of estimation and PSF may cause divergence. Therefore, division by
|
|
185
|
+
During deconvolution, division by small values in the convolve image
|
|
186
|
+
of estimation and PSF may cause divergence. Therefore, division by
|
|
161
187
|
values under `eps` is substituted to zero.
|
|
162
188
|
"""
|
|
163
189
|
img = self._deref_image()
|
|
164
190
|
scale = tuple(img.scale.values())
|
|
165
|
-
|
|
191
|
+
|
|
166
192
|
def func(arr: np.ndarray):
|
|
167
193
|
psf_ft, psf_ft_conj = _deconv.check_psf(arr.shape, scale, psf)
|
|
168
194
|
return _deconv.richardson_lucy(arr, psf_ft, psf_ft_conj, niter, eps)
|
|
169
|
-
|
|
195
|
+
|
|
170
196
|
return self._map_overlap(func)
|
|
171
|
-
|
|
197
|
+
|
|
172
198
|
def gaussian_filter(self, sigma: float = 1.0, fourier: bool = False) -> _T:
|
|
173
199
|
"""
|
|
174
200
|
Run Gaussian filter (Gaussian blur).
|
|
175
|
-
|
|
201
|
+
|
|
176
202
|
Parameters
|
|
177
203
|
----------
|
|
178
204
|
{sigma}{fourier}
|
|
179
205
|
"""
|
|
180
206
|
filter_func = _filters.gaussian_filter_fourier if fourier else _filters.gaussian_filter
|
|
181
207
|
return self._map_overlap(filter_func, sigma=sigma)
|
|
182
|
-
|
|
208
|
+
|
|
183
209
|
def dog_filter(
|
|
184
210
|
self,
|
|
185
211
|
low_sigma: float = 1.0,
|
|
@@ -189,7 +215,7 @@ class TiledImage(Generic[_T]):
|
|
|
189
215
|
"""
|
|
190
216
|
Run Difference of Gaussian filter. This function does not support `update`
|
|
191
217
|
argument because intensity can be negative.
|
|
192
|
-
|
|
218
|
+
|
|
193
219
|
Parameters
|
|
194
220
|
----------
|
|
195
221
|
low_sigma : scalar or array of scalars, default is 1.
|
|
@@ -197,7 +223,7 @@ class TiledImage(Generic[_T]):
|
|
|
197
223
|
high_sigma : scalar or array of scalars, default is x1.6 of low_sigma.
|
|
198
224
|
higher standard deviation(s) of Gaussian.
|
|
199
225
|
{fourier}
|
|
200
|
-
"""
|
|
226
|
+
"""
|
|
201
227
|
if high_sigma is None:
|
|
202
228
|
high_sigma = low_sigma * 1.6
|
|
203
229
|
filter_func = _filters.dog_filter_fourier if fourier else _filters.dog_filter
|
|
@@ -222,3 +248,24 @@ def _lowpass(arr, cutoff, order=2):
|
|
|
222
248
|
ft = xp.asarray(weight) * xp.fft.rfftn(arr)
|
|
223
249
|
ift = xp.fft.irfftn(ft, s=shape)
|
|
224
250
|
return xp.asnumpy(ift)
|
|
251
|
+
|
|
252
|
+
def _highpass(arr, cutoff, order=2):
|
|
253
|
+
arr = xp.asarray(arr)
|
|
254
|
+
shape = arr.shape
|
|
255
|
+
_cutoff = check_nd(cutoff, len(shape))
|
|
256
|
+
weight = _get_ND_butterworth_filter(shape, _cutoff, order, True, True)
|
|
257
|
+
ft = xp.asarray(weight) * xp.fft.rfftn(arr)
|
|
258
|
+
ift = xp.fft.irfftn(ft, s=shape)
|
|
259
|
+
return xp.asnumpy(ift)
|
|
260
|
+
|
|
261
|
+
def _bandpass(arr, low_cutoff, high_cutoff, order=2):
|
|
262
|
+
arr = xp.asarray(arr)
|
|
263
|
+
shape = arr.shape
|
|
264
|
+
_low_cutoff = check_nd(low_cutoff, len(shape))
|
|
265
|
+
_high_cutoff = check_nd(high_cutoff, len(shape))
|
|
266
|
+
low_weight = _get_ND_butterworth_filter(shape, _low_cutoff, order, False, True)
|
|
267
|
+
high_weight = _get_ND_butterworth_filter(shape, _high_cutoff, order, True, True)
|
|
268
|
+
weight = low_weight * high_weight
|
|
269
|
+
ft = xp.asarray(weight) * xp.fft.rfftn(arr)
|
|
270
|
+
ift = xp.fft.irfftn(ft, s=shape)
|
|
271
|
+
return xp.asnumpy(ift)
|