impy-array 2.4.3__tar.gz → 2.4.4__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.3 → impy_array-2.4.4}/PKG-INFO +8 -10
- {impy_array-2.4.3 → impy_array-2.4.4}/README.md +7 -9
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/__init__.py +1 -1
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/array_api.py +14 -22
- impy_array-2.4.4/impy/arrays/__init__.py +17 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_corr.py +37 -37
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_deconv.py +4 -13
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_misc.py +2 -2
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_transform.py +2 -2
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/bases/metaarray.py +140 -137
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/bases/overload.py +14 -14
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/big.py +1 -1
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/labeledarray.py +212 -212
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/lazy.py +230 -233
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/axes/_axes.py +2 -2
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/correlation.py +119 -119
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/io.py +1 -1
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/random.py +1 -1
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/viewer/utils.py +1 -1
- impy_array-2.4.3/impy/arrays/__init__.py +0 -17
- {impy_array-2.4.3 → impy_array-2.4.4}/.gitignore +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/LICENSE +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/__main__.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/_const.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/_types.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_imshow.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/__init__.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_docs.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_filters.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_glcm.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_linalg.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_plot.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_process_numba.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_skimage.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/_utils/_structures.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/axesmixin.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/bases/__init__.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/imgarray.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/label.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/phasearray.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/specials.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/arrays/tiled.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/axes/__init__.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/axes/_axes_tuple.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/axes/_axis.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/axes/_slicer.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/binder.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/collections.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/core.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/frame/__init__.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/frame/frames.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/lazy/__init__.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/lazy/core.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/lazy/random.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/roi.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/utils/__init__.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/utils/axesop.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/utils/deco.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/utils/gauss.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/utils/misc.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/utils/slicer.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/utils/utilcls.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/viewer/__init__.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/impy/viewer/viewer.py +0 -0
- {impy_array-2.4.3 → impy_array-2.4.4}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: impy-array
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.4
|
|
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>
|
|
@@ -74,13 +74,10 @@ Description-Content-Type: text/markdown
|
|
|
74
74
|
[](https://pypistats.org/packages/impy-array)
|
|
75
75
|
[](https://badge.fury.io/py/impy-array)
|
|
76
76
|
|
|
77
|
-
#
|
|
77
|
+
# impy
|
|
78
78
|
|
|
79
|
-
`impy` is an all-in-one image analysis library
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
The core array, `ImgArray`, is a subclass of `numpy.ndarray`, tagged with information
|
|
83
|
-
such as:
|
|
79
|
+
`impy` is an all-in-one multi-dimensional image analysis library. The core array,
|
|
80
|
+
`ImgArray`, is a subclass of `numpy.ndarray`, tagged with information such as:
|
|
84
81
|
|
|
85
82
|
- image axes
|
|
86
83
|
- scale of each axis
|
|
@@ -125,7 +122,7 @@ img["z=3;t=0"].imshow() # Plot image slice at z=3 and t=0.
|
|
|
125
122
|
img["y=N//4:N//4*3"].imshow() # `N` for the size of the axis.
|
|
126
123
|
img_fil = img.gaussian_filter(sigma=2) # Paralell batch denoising. No more for loop!
|
|
127
124
|
img_prj = np.max(img_fil, axis="z") # Z-projection (numpy is aware of image axes!).
|
|
128
|
-
img_prj.imsave(
|
|
125
|
+
img_prj.imsave("image_max.tif") # Save in the same place. Don't spend time on searching for the directory!
|
|
129
126
|
```
|
|
130
127
|
|
|
131
128
|
### Supports many file formats
|
|
@@ -165,8 +162,8 @@ ip.Const["RESOURCE"] = "cupy" # <- globally use GPU
|
|
|
165
162
|
### Seamless interface between `napari`
|
|
166
163
|
|
|
167
164
|
[napari](https://github.com/napari/napari) is an interactive viewer for multi-dimensional
|
|
168
|
-
images. `impy` has a **simple and efficient interface** with it, via the object `ip.gui`.
|
|
169
|
-
Since `ImgArray` is tagged with image metadata, you don't have to care about axes or
|
|
165
|
+
images. `impy` has a **simple and efficient interface** with it, via the object `ip.gui`.
|
|
166
|
+
Since `ImgArray` is tagged with image metadata, you don't have to care about axes or
|
|
170
167
|
scales. Just run
|
|
171
168
|
|
|
172
169
|
```python
|
|
@@ -214,6 +211,7 @@ For more complex procedures, it is possible to send images directly to `IPython`
|
|
|
214
211
|
```
|
|
215
212
|
impy path/to/image.tif -i
|
|
216
213
|
```
|
|
214
|
+
|
|
217
215
|
```python
|
|
218
216
|
thr = img.gaussian_filter().threshold()
|
|
219
217
|
```
|
|
@@ -2,13 +2,10 @@
|
|
|
2
2
|
[](https://pypistats.org/packages/impy-array)
|
|
3
3
|
[](https://badge.fury.io/py/impy-array)
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# impy
|
|
6
6
|
|
|
7
|
-
`impy` is an all-in-one image analysis library
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
The core array, `ImgArray`, is a subclass of `numpy.ndarray`, tagged with information
|
|
11
|
-
such as:
|
|
7
|
+
`impy` is an all-in-one multi-dimensional image analysis library. The core array,
|
|
8
|
+
`ImgArray`, is a subclass of `numpy.ndarray`, tagged with information such as:
|
|
12
9
|
|
|
13
10
|
- image axes
|
|
14
11
|
- scale of each axis
|
|
@@ -53,7 +50,7 @@ img["z=3;t=0"].imshow() # Plot image slice at z=3 and t=0.
|
|
|
53
50
|
img["y=N//4:N//4*3"].imshow() # `N` for the size of the axis.
|
|
54
51
|
img_fil = img.gaussian_filter(sigma=2) # Paralell batch denoising. No more for loop!
|
|
55
52
|
img_prj = np.max(img_fil, axis="z") # Z-projection (numpy is aware of image axes!).
|
|
56
|
-
img_prj.imsave(
|
|
53
|
+
img_prj.imsave("image_max.tif") # Save in the same place. Don't spend time on searching for the directory!
|
|
57
54
|
```
|
|
58
55
|
|
|
59
56
|
### Supports many file formats
|
|
@@ -93,8 +90,8 @@ ip.Const["RESOURCE"] = "cupy" # <- globally use GPU
|
|
|
93
90
|
### Seamless interface between `napari`
|
|
94
91
|
|
|
95
92
|
[napari](https://github.com/napari/napari) is an interactive viewer for multi-dimensional
|
|
96
|
-
images. `impy` has a **simple and efficient interface** with it, via the object `ip.gui`.
|
|
97
|
-
Since `ImgArray` is tagged with image metadata, you don't have to care about axes or
|
|
93
|
+
images. `impy` has a **simple and efficient interface** with it, via the object `ip.gui`.
|
|
94
|
+
Since `ImgArray` is tagged with image metadata, you don't have to care about axes or
|
|
98
95
|
scales. Just run
|
|
99
96
|
|
|
100
97
|
```python
|
|
@@ -142,6 +139,7 @@ For more complex procedures, it is possible to send images directly to `IPython`
|
|
|
142
139
|
```
|
|
143
140
|
impy path/to/image.tif -i
|
|
144
141
|
```
|
|
142
|
+
|
|
145
143
|
```python
|
|
146
144
|
thr = img.gaussian_filter().threshold()
|
|
147
145
|
```
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
1
|
from functools import wraps
|
|
3
2
|
import numpy as np
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
from scipy import ndimage as scipy_ndi, fft as scipy_fft
|
|
8
|
-
from types import ModuleType
|
|
9
3
|
|
|
10
4
|
def cupy_dispatcher(function):
|
|
11
5
|
@wraps(function)
|
|
@@ -16,24 +10,21 @@ def cupy_dispatcher(function):
|
|
|
16
10
|
return xp.asnumpy(out)
|
|
17
11
|
return func
|
|
18
12
|
|
|
19
|
-
# CUDA <= ver.8 does not have gradient
|
|
13
|
+
# CUDA <= ver.8 does not have gradient
|
|
20
14
|
def _gradient(a, axis=None):
|
|
21
15
|
out = np.gradient(a.get(), axis=axis)
|
|
22
16
|
return xp.asarray(out)
|
|
23
17
|
|
|
24
|
-
class XP:
|
|
18
|
+
class XP:
|
|
25
19
|
def __init__(self):
|
|
26
20
|
self.state = ""
|
|
27
21
|
self._reset_namespace()
|
|
28
22
|
self.setNumpy()
|
|
29
|
-
|
|
30
|
-
def __getattr__(self, key: str):
|
|
31
|
-
return getattr(self._module, key)
|
|
32
|
-
|
|
23
|
+
|
|
33
24
|
def _reset_namespace(self):
|
|
34
25
|
self._signal = None
|
|
35
26
|
self._fft = None
|
|
36
|
-
|
|
27
|
+
|
|
37
28
|
@property
|
|
38
29
|
def signal(self):
|
|
39
30
|
if self._signal is not None:
|
|
@@ -47,7 +38,7 @@ class XP:
|
|
|
47
38
|
else:
|
|
48
39
|
raise ValueError(self.state)
|
|
49
40
|
return self._signal
|
|
50
|
-
|
|
41
|
+
|
|
51
42
|
@property
|
|
52
43
|
def fft(self):
|
|
53
44
|
if self._fft is not None:
|
|
@@ -61,13 +52,13 @@ class XP:
|
|
|
61
52
|
else:
|
|
62
53
|
raise ValueError(self.state)
|
|
63
54
|
return self._fft
|
|
64
|
-
|
|
55
|
+
|
|
65
56
|
def setNumpy(self) -> None:
|
|
66
57
|
from scipy import ndimage as scipy_ndi
|
|
67
58
|
|
|
68
59
|
if self.state == "numpy":
|
|
69
60
|
return
|
|
70
|
-
|
|
61
|
+
|
|
71
62
|
self._reset_namespace()
|
|
72
63
|
self._module = np
|
|
73
64
|
self.linalg = np.linalg
|
|
@@ -117,11 +108,12 @@ class XP:
|
|
|
117
108
|
self.argmin = np.argmin
|
|
118
109
|
self.pad = np.pad
|
|
119
110
|
self.isnan = np.isnan
|
|
120
|
-
|
|
111
|
+
self.eye = np.eye
|
|
112
|
+
|
|
121
113
|
self.state = "numpy"
|
|
122
114
|
from ._const import Const
|
|
123
115
|
Const["SCHEDULER"] = "threads"
|
|
124
|
-
|
|
116
|
+
|
|
125
117
|
def setCupy(self) -> None:
|
|
126
118
|
if self.state == "cupy":
|
|
127
119
|
return
|
|
@@ -131,10 +123,10 @@ class XP:
|
|
|
131
123
|
if dtype is None:
|
|
132
124
|
return out
|
|
133
125
|
return out.astype(dtype)
|
|
134
|
-
|
|
126
|
+
|
|
135
127
|
from cupyx.scipy import ndimage as cp_ndi
|
|
136
128
|
from cupy import linalg as cp_linalg
|
|
137
|
-
|
|
129
|
+
|
|
138
130
|
self._reset_namespace()
|
|
139
131
|
self._module = cp
|
|
140
132
|
self.linalg = cp_linalg
|
|
@@ -187,10 +179,10 @@ class XP:
|
|
|
187
179
|
self.argmin = cp.argmin
|
|
188
180
|
self.pad = cp.pad
|
|
189
181
|
self.isnan = cp.isnan
|
|
182
|
+
self.eye = cp.eye
|
|
190
183
|
self.state = "cupy"
|
|
191
|
-
|
|
184
|
+
|
|
192
185
|
from ._const import Const
|
|
193
186
|
Const["SCHEDULER"] = "single-threaded"
|
|
194
187
|
|
|
195
188
|
xp = XP()
|
|
196
|
-
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from impy.arrays.imgarray import ImgArray
|
|
2
|
+
from impy.arrays.labeledarray import LabeledArray
|
|
3
|
+
from impy.arrays.label import Label
|
|
4
|
+
from impy.arrays.phasearray import PhaseArray
|
|
5
|
+
from impy.arrays.specials import PropArray
|
|
6
|
+
from impy.arrays.lazy import LazyImgArray
|
|
7
|
+
from impy.arrays.big import BigImgArray
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ImgArray",
|
|
11
|
+
"LabeledArray",
|
|
12
|
+
"Label",
|
|
13
|
+
"PhaseArray",
|
|
14
|
+
"PropArray",
|
|
15
|
+
"LazyImgArray",
|
|
16
|
+
"BigImgArray",
|
|
17
|
+
]
|
|
@@ -10,8 +10,8 @@ from ...array_api import xp
|
|
|
10
10
|
# TODO: if max_shifts is small, DFT should be used in place of FFT
|
|
11
11
|
|
|
12
12
|
def _pcc_and_power_spec(
|
|
13
|
-
f0:
|
|
14
|
-
f1:
|
|
13
|
+
f0: np.ndarray,
|
|
14
|
+
f1: np.ndarray,
|
|
15
15
|
max_shifts: tuple[float, ...] | None = None,
|
|
16
16
|
):
|
|
17
17
|
product = f0 * f1.conj()
|
|
@@ -23,20 +23,20 @@ def _pcc_and_power_spec(
|
|
|
23
23
|
return power, product
|
|
24
24
|
|
|
25
25
|
def draw_pcc_landscape(
|
|
26
|
-
f0:
|
|
27
|
-
f1:
|
|
26
|
+
f0: np.ndarray,
|
|
27
|
+
f1: np.ndarray,
|
|
28
28
|
max_shifts: tuple[float, ...] | None = None,
|
|
29
29
|
):
|
|
30
30
|
power = _pcc_and_power_spec(f0, f1, max_shifts)[0]
|
|
31
31
|
return xp.fft.fftshift(power)
|
|
32
32
|
|
|
33
33
|
def subpixel_pcc(
|
|
34
|
-
f0:
|
|
35
|
-
f1:
|
|
34
|
+
f0: np.ndarray,
|
|
35
|
+
f1: np.ndarray,
|
|
36
36
|
upsample_factor: int,
|
|
37
37
|
max_shifts: tuple[float, ...] | None = None,
|
|
38
38
|
):
|
|
39
|
-
power, product = _pcc_and_power_spec(f0, f1, max_shifts)
|
|
39
|
+
power, product = _pcc_and_power_spec(f0, f1, max_shifts)
|
|
40
40
|
maxima = xp.unravel_index(xp.argmax(power), power.shape)
|
|
41
41
|
midpoints = xp.array([np.fix(axis_size / 2) for axis_size in power.shape])
|
|
42
42
|
|
|
@@ -58,13 +58,13 @@ def subpixel_pcc(
|
|
|
58
58
|
).conj()
|
|
59
59
|
# Locate maximum and map back to original pixel grid
|
|
60
60
|
power = abs2(cross_correlation)
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
if max_shifts is not None:
|
|
63
63
|
max_shifts = xp.asarray(max_shifts)
|
|
64
64
|
_upsampled_left_shifts = (shifts + max_shifts) * upsample_factor
|
|
65
65
|
_upsampled_right_shifts = (max_shifts - shifts) * upsample_factor
|
|
66
66
|
power = crop_by_max_shifts(power, _upsampled_left_shifts, _upsampled_right_shifts)
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
maxima = xp.unravel_index(xp.argmax(power), power.shape)
|
|
69
69
|
maxima = xp.asarray(maxima, dtype=np.float32) - dftshift
|
|
70
70
|
shifts = shifts + maxima / upsample_factor
|
|
@@ -74,11 +74,11 @@ def subpixel_pcc(
|
|
|
74
74
|
return shifts, pcc
|
|
75
75
|
|
|
76
76
|
def _upsampled_dft(
|
|
77
|
-
data:
|
|
78
|
-
upsampled_region_size: np.ndarray,
|
|
79
|
-
upsample_factor: int,
|
|
80
|
-
axis_offsets:
|
|
81
|
-
) ->
|
|
77
|
+
data: np.ndarray,
|
|
78
|
+
upsampled_region_size: np.ndarray,
|
|
79
|
+
upsample_factor: int,
|
|
80
|
+
axis_offsets: np.ndarray
|
|
81
|
+
) -> np.ndarray:
|
|
82
82
|
# if people pass in an integer, expand it to a list of equal-sized sections
|
|
83
83
|
upsampled_region_size = [upsampled_region_size,] * data.ndim
|
|
84
84
|
|
|
@@ -92,14 +92,14 @@ def _upsampled_dft(
|
|
|
92
92
|
data = xp.tensordot(kernel, data, axes=(1, -1))
|
|
93
93
|
return data
|
|
94
94
|
|
|
95
|
-
def abs2(a:
|
|
95
|
+
def abs2(a: np.ndarray) -> np.ndarray:
|
|
96
96
|
return a.real**2 + a.imag**2
|
|
97
97
|
|
|
98
|
-
def crop_by_max_shifts(power:
|
|
98
|
+
def crop_by_max_shifts(power: np.ndarray, left, right):
|
|
99
99
|
shifted_power = xp.fft.fftshift(power)
|
|
100
100
|
centers = tuple(s//2 for s in power.shape)
|
|
101
101
|
slices = tuple(
|
|
102
|
-
slice(max(c - int(shiftl), 0), min(c + int(shiftr) + 1, s), None)
|
|
102
|
+
slice(max(c - int(shiftl), 0), min(c + int(shiftr) + 1, s), None)
|
|
103
103
|
for c, shiftl, shiftr, s in zip(centers, left, right, power.shape)
|
|
104
104
|
)
|
|
105
105
|
return xp.fft.ifftshift(shifted_power[slices])
|
|
@@ -107,8 +107,8 @@ def crop_by_max_shifts(power: xp.ndarray, left, right):
|
|
|
107
107
|
# Normalized cross correlation
|
|
108
108
|
|
|
109
109
|
def _draw_ncc_landscape_no_crop(
|
|
110
|
-
img0:
|
|
111
|
-
img1:
|
|
110
|
+
img0: np.ndarray,
|
|
111
|
+
img1: np.ndarray,
|
|
112
112
|
max_shifts: tuple[float, ...] | None = None,
|
|
113
113
|
):
|
|
114
114
|
ndim = img1.ndim
|
|
@@ -116,30 +116,30 @@ def _draw_ncc_landscape_no_crop(
|
|
|
116
116
|
max_shifts = tuple(max_shifts)
|
|
117
117
|
pad_width, sl = _get_padding_params(img0.shape, img1.shape, max_shifts)
|
|
118
118
|
padimg = xp.pad(img0[sl], pad_width=pad_width, mode="constant", constant_values=0)
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
corr = xp.signal.fftconvolve(
|
|
121
121
|
padimg, img1[(slice(None, None, -1),)*ndim], mode="valid"
|
|
122
122
|
)[(slice(1, -1, None),)*ndim]
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
_win_sum = _window_sum_2d if ndim == 2 else _window_sum_3d
|
|
125
125
|
win_sum1 = _win_sum(padimg, img1.shape)
|
|
126
126
|
win_sum2 = _win_sum(padimg**2, img1.shape)
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
template_mean = xp.mean(img1)
|
|
129
129
|
template_volume = xp.prod(xp.array(img1.shape))
|
|
130
130
|
template_ssd = xp.sum((img1 - template_mean)**2)
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
var = (win_sum2 - win_sum1**2 / template_volume) * template_ssd
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
# zero division happens when perfectly matched
|
|
135
135
|
response = xp.zeros_like(corr)
|
|
136
136
|
mask = var > 0
|
|
137
137
|
response[mask] = (corr - win_sum1 * template_mean)[mask] / _safe_sqrt(var, fill=np.inf)[mask]
|
|
138
138
|
return response
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
def draw_ncc_landscape(
|
|
141
|
-
img0:
|
|
142
|
-
img1:
|
|
141
|
+
img0: np.ndarray,
|
|
142
|
+
img1: np.ndarray,
|
|
143
143
|
max_shifts: tuple[float, ...] | None = None,
|
|
144
144
|
):
|
|
145
145
|
response = _draw_ncc_landscape_no_crop(img0, img1, max_shifts)
|
|
@@ -149,12 +149,12 @@ def draw_ncc_landscape(
|
|
|
149
149
|
pad_width_eff = tuple((s - int(m) * 2 - 1)//2 for m, s in zip(max_shifts, response.shape))
|
|
150
150
|
sl_res = tuple(slice(w, -w, None) for w in pad_width_eff)
|
|
151
151
|
response_center = response[sl_res]
|
|
152
|
-
|
|
152
|
+
|
|
153
153
|
return response_center
|
|
154
154
|
|
|
155
155
|
def subpixel_ncc(
|
|
156
|
-
img0:
|
|
157
|
-
img1:
|
|
156
|
+
img0: np.ndarray,
|
|
157
|
+
img1: np.ndarray,
|
|
158
158
|
upsample_factor: int,
|
|
159
159
|
max_shifts: tuple[float, ...] | None = None,
|
|
160
160
|
):
|
|
@@ -167,7 +167,7 @@ def subpixel_ncc(
|
|
|
167
167
|
response_center = response[sl_res]
|
|
168
168
|
maxima = xp.unravel_index(xp.argmax(response_center), response_center.shape)
|
|
169
169
|
midpoints = xp.asarray(response_center.shape) // 2
|
|
170
|
-
|
|
170
|
+
|
|
171
171
|
if upsample_factor > 1:
|
|
172
172
|
coords = _create_mesh(upsample_factor, maxima, max_shifts, midpoints, pad_width_eff, xp.state)
|
|
173
173
|
local_response = xp.ndi.map_coordinates(
|
|
@@ -179,7 +179,7 @@ def subpixel_ncc(
|
|
|
179
179
|
else:
|
|
180
180
|
zncc = response[maxima]
|
|
181
181
|
shifts = xp.array(maxima) - midpoints
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
return xp.asarray(shifts, dtype=np.float32), zncc
|
|
184
184
|
|
|
185
185
|
# Identical to skimage.feature.template, but compatible between numpy and cupy.
|
|
@@ -202,7 +202,7 @@ def _window_sum_3d(image, window_shape):
|
|
|
202
202
|
|
|
203
203
|
return window_sum
|
|
204
204
|
|
|
205
|
-
def _safe_sqrt(a:
|
|
205
|
+
def _safe_sqrt(a: np.ndarray, fill=0):
|
|
206
206
|
out = xp.full(a.shape, fill, dtype=np.float32)
|
|
207
207
|
out = xp.zeros_like(a)
|
|
208
208
|
mask = a > 0
|
|
@@ -211,7 +211,7 @@ def _safe_sqrt(a: xp.ndarray, fill=0):
|
|
|
211
211
|
|
|
212
212
|
@lru_cache(maxsize=12)
|
|
213
213
|
def _get_padding_params(
|
|
214
|
-
shape0: tuple[int, ...],
|
|
214
|
+
shape0: tuple[int, ...],
|
|
215
215
|
shape1: tuple[int, ...],
|
|
216
216
|
max_shifts: tuple[int, ...] | None,
|
|
217
217
|
) -> tuple[list[tuple[int, ...]], list[slice] | slice]:
|
|
@@ -230,13 +230,13 @@ def _get_padding_params(
|
|
|
230
230
|
pad_width.append((0,) * 2)
|
|
231
231
|
sl.append(slice(-w_int, w_int, None))
|
|
232
232
|
sl = tuple(sl)
|
|
233
|
-
|
|
233
|
+
|
|
234
234
|
return pad_width, sl
|
|
235
235
|
|
|
236
236
|
|
|
237
237
|
def _create_mesh(
|
|
238
238
|
upsample_factor: int,
|
|
239
|
-
maxima: tuple[int, ...],
|
|
239
|
+
maxima: tuple[int, ...],
|
|
240
240
|
max_shifts: tuple[int, ...] | None,
|
|
241
241
|
midpoints: tuple[int, ...],
|
|
242
242
|
pad_width_eff: tuple[int, ...],
|
|
@@ -256,7 +256,7 @@ def _create_mesh(
|
|
|
256
256
|
local_shifts = ([-upsample_factor, upsample_factor],) * len(maxima)
|
|
257
257
|
mesh = xp.meshgrid(
|
|
258
258
|
*[xp.arange(s0, s1+1)/upsample_factor + m + w
|
|
259
|
-
for (s0, s1), m, w in zip(local_shifts, maxima, pad_width_eff)],
|
|
259
|
+
for (s0, s1), m, w in zip(local_shifts, maxima, pad_width_eff)],
|
|
260
260
|
indexing="ij",
|
|
261
261
|
)
|
|
262
262
|
return xp.stack(mesh, axis=0)
|
|
@@ -4,15 +4,6 @@ from functools import partial
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from impy.array_api import xp
|
|
6
6
|
|
|
7
|
-
try:
|
|
8
|
-
gradient = xp.gradient
|
|
9
|
-
except AttributeError:
|
|
10
|
-
# CUDA <= ver.8 does not have gradient
|
|
11
|
-
import numpy
|
|
12
|
-
def gradient(a, axis=None):
|
|
13
|
-
out = numpy.gradient(a.get(), axis=axis)
|
|
14
|
-
return xp.asarray(out)
|
|
15
|
-
|
|
16
7
|
__all__ = ["wiener", "richardson_lucy", "richardson_lucy_tv", "check_psf"]
|
|
17
8
|
|
|
18
9
|
def wiener(obs, psf_ft, psf_ft_conj, lmd):
|
|
@@ -30,7 +21,7 @@ def richardson_lucy(obs, psf_ft, psf_ft_conj, niter, eps):
|
|
|
30
21
|
obs = xp.asarray(obs)
|
|
31
22
|
fft = xp.fft.rfftn
|
|
32
23
|
ifft = partial(xp.fft.irfftn, s=obs.shape)
|
|
33
|
-
conv = factor = xp.empty(obs.shape, dtype=
|
|
24
|
+
conv = factor = xp.empty(obs.shape, dtype=np.float32) # placeholder
|
|
34
25
|
estimated = xp.real(ifft(fft(obs) * psf_ft)) # initialization
|
|
35
26
|
|
|
36
27
|
for _ in range(niter):
|
|
@@ -45,16 +36,16 @@ def richardson_lucy_tv(obs, psf_ft, psf_ft_conj, max_iter, lmd, tol, eps):
|
|
|
45
36
|
fft = xp.fft.rfftn
|
|
46
37
|
ifft = partial(xp.fft.irfftn, s=obs.shape)
|
|
47
38
|
est_old = ifft(fft(obs) * psf_ft).real
|
|
48
|
-
est_new = xp.empty(obs.shape, dtype=
|
|
39
|
+
est_new = xp.empty(obs.shape, dtype=np.float32)
|
|
49
40
|
conv = factor = norm = gg = xp.empty(obs.shape, dtype=np.float32) # placeholder
|
|
50
41
|
|
|
51
42
|
for _ in range(max_iter):
|
|
52
43
|
conv[:] = ifft(fft(est_old) * psf_ft).real
|
|
53
44
|
factor[:] = ifft(fft(_safe_div(obs, conv, eps=eps)) * psf_ft_conj).real
|
|
54
45
|
est_new[:] = est_old * factor
|
|
55
|
-
grad = gradient(est_old)
|
|
46
|
+
grad = xp.gradient(est_old)
|
|
56
47
|
norm[:] = xp.sqrt(sum(g**2 for g in grad))
|
|
57
|
-
gg[:] = sum(gradient(_safe_div(grad[i], norm, eps=1e-8), axis=i)
|
|
48
|
+
gg[:] = sum(xp.gradient(_safe_div(grad[i], norm, eps=1e-8), axis=i)
|
|
58
49
|
for i in range(obs.ndim))
|
|
59
50
|
est_new /= (1 - lmd * gg)
|
|
60
51
|
gain = xp.sum(xp.abs(est_new - est_old))/xp.sum(xp.abs(est_old))
|
|
@@ -69,7 +69,7 @@ def make_pad(pad_width, dims, all_axes, **kwargs):
|
|
|
69
69
|
|
|
70
70
|
return pad_width_
|
|
71
71
|
|
|
72
|
-
def dft(img:
|
|
72
|
+
def dft(img: np.ndarray, exps: list[np.ndarray] = None):
|
|
73
73
|
"""Discrete Fourier Transform."""
|
|
74
74
|
img = xp.asarray(img)
|
|
75
75
|
for ker in reversed(exps):
|
|
@@ -77,7 +77,7 @@ def dft(img: xp.ndarray, exps: list[xp.ndarray] = None):
|
|
|
77
77
|
img = xp.tensordot(xp.asarray(ker), img, axes=(1, -1))
|
|
78
78
|
return img
|
|
79
79
|
|
|
80
|
-
def inpaint_mean(img:
|
|
80
|
+
def inpaint_mean(img: np.ndarray, mask: np.ndarray):
|
|
81
81
|
"""Inpaint missing values with mean of surrounding pixels."""
|
|
82
82
|
img = xp.asarray(img)
|
|
83
83
|
mask = xp.asarray(mask)
|
|
@@ -343,9 +343,9 @@ def get_fourier_filter(size: int, filter_name: str):
|
|
|
343
343
|
cosine_filter = xp.fft.fftshift(xp.sin(freq))
|
|
344
344
|
fourier_filter *= cosine_filter
|
|
345
345
|
elif filter_name == "hamming":
|
|
346
|
-
fourier_filter *= xp.fft.fftshift(xp.hamming(size))
|
|
346
|
+
fourier_filter *= xp.fft.fftshift(xp._module.hamming(size))
|
|
347
347
|
elif filter_name == "hann":
|
|
348
|
-
fourier_filter *= xp.fft.fftshift(xp.hanning(size))
|
|
348
|
+
fourier_filter *= xp.fft.fftshift(xp._module.hanning(size))
|
|
349
349
|
elif filter_name is None:
|
|
350
350
|
fourier_filter[:] = 1
|
|
351
351
|
else:
|