impy-array 2.4.0__tar.gz → 2.4.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.
Files changed (64) hide show
  1. {impy_array-2.4.0 → impy_array-2.4.1}/PKG-INFO +13 -8
  2. {impy_array-2.4.0 → impy_array-2.4.1}/README.md +12 -7
  3. {impy_array-2.4.0 → impy_array-2.4.1}/impy/__init__.py +1 -1
  4. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_transform.py +37 -35
  5. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/imgarray.py +79 -47
  6. {impy_array-2.4.0 → impy_array-2.4.1}/impy/io.py +2 -3
  7. {impy_array-2.4.0 → impy_array-2.4.1}/impy/utils/gauss.py +27 -22
  8. {impy_array-2.4.0 → impy_array-2.4.1}/.gitignore +0 -0
  9. {impy_array-2.4.0 → impy_array-2.4.1}/LICENSE +0 -0
  10. {impy_array-2.4.0 → impy_array-2.4.1}/impy/__main__.py +0 -0
  11. {impy_array-2.4.0 → impy_array-2.4.1}/impy/_const.py +0 -0
  12. {impy_array-2.4.0 → impy_array-2.4.1}/impy/_types.py +0 -0
  13. {impy_array-2.4.0 → impy_array-2.4.1}/impy/array_api.py +0 -0
  14. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/__init__.py +0 -0
  15. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_imshow.py +0 -0
  16. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/__init__.py +0 -0
  17. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_corr.py +0 -0
  18. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_deconv.py +0 -0
  19. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_docs.py +0 -0
  20. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_filters.py +0 -0
  21. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_glcm.py +0 -0
  22. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_linalg.py +0 -0
  23. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_misc.py +0 -0
  24. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_plot.py +0 -0
  25. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_process_numba.py +0 -0
  26. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_skimage.py +0 -0
  27. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/_utils/_structures.py +0 -0
  28. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/axesmixin.py +0 -0
  29. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/bases/__init__.py +0 -0
  30. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/bases/metaarray.py +0 -0
  31. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/bases/overload.py +0 -0
  32. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/big.py +0 -0
  33. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/label.py +0 -0
  34. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/labeledarray.py +0 -0
  35. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/lazy.py +0 -0
  36. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/phasearray.py +0 -0
  37. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/specials.py +0 -0
  38. {impy_array-2.4.0 → impy_array-2.4.1}/impy/arrays/tiled.py +0 -0
  39. {impy_array-2.4.0 → impy_array-2.4.1}/impy/axes/__init__.py +0 -0
  40. {impy_array-2.4.0 → impy_array-2.4.1}/impy/axes/_axes.py +0 -0
  41. {impy_array-2.4.0 → impy_array-2.4.1}/impy/axes/_axes_tuple.py +0 -0
  42. {impy_array-2.4.0 → impy_array-2.4.1}/impy/axes/_axis.py +0 -0
  43. {impy_array-2.4.0 → impy_array-2.4.1}/impy/axes/_slicer.py +0 -0
  44. {impy_array-2.4.0 → impy_array-2.4.1}/impy/binder.py +0 -0
  45. {impy_array-2.4.0 → impy_array-2.4.1}/impy/collections.py +0 -0
  46. {impy_array-2.4.0 → impy_array-2.4.1}/impy/core.py +0 -0
  47. {impy_array-2.4.0 → impy_array-2.4.1}/impy/correlation.py +0 -0
  48. {impy_array-2.4.0 → impy_array-2.4.1}/impy/frame/__init__.py +0 -0
  49. {impy_array-2.4.0 → impy_array-2.4.1}/impy/frame/frames.py +0 -0
  50. {impy_array-2.4.0 → impy_array-2.4.1}/impy/lazy/__init__.py +0 -0
  51. {impy_array-2.4.0 → impy_array-2.4.1}/impy/lazy/core.py +0 -0
  52. {impy_array-2.4.0 → impy_array-2.4.1}/impy/lazy/random.py +0 -0
  53. {impy_array-2.4.0 → impy_array-2.4.1}/impy/random.py +0 -0
  54. {impy_array-2.4.0 → impy_array-2.4.1}/impy/roi.py +0 -0
  55. {impy_array-2.4.0 → impy_array-2.4.1}/impy/utils/__init__.py +0 -0
  56. {impy_array-2.4.0 → impy_array-2.4.1}/impy/utils/axesop.py +0 -0
  57. {impy_array-2.4.0 → impy_array-2.4.1}/impy/utils/deco.py +0 -0
  58. {impy_array-2.4.0 → impy_array-2.4.1}/impy/utils/misc.py +0 -0
  59. {impy_array-2.4.0 → impy_array-2.4.1}/impy/utils/slicer.py +0 -0
  60. {impy_array-2.4.0 → impy_array-2.4.1}/impy/utils/utilcls.py +0 -0
  61. {impy_array-2.4.0 → impy_array-2.4.1}/impy/viewer/__init__.py +0 -0
  62. {impy_array-2.4.0 → impy_array-2.4.1}/impy/viewer/utils.py +0 -0
  63. {impy_array-2.4.0 → impy_array-2.4.1}/impy/viewer/viewer.py +0 -0
  64. {impy_array-2.4.0 → impy_array-2.4.1}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: impy-array
3
- Version: 2.4.0
3
+ Version: 2.4.1
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>
@@ -76,9 +76,11 @@ Description-Content-Type: text/markdown
76
76
 
77
77
  # A numpy extension for efficient and powerful image analysis workflow
78
78
 
79
- `impy` is an all-in-one image analysis library, equipped with parallel processing, GPU support, GUI based tools and so on.
79
+ `impy` is an all-in-one image analysis library, equipped with parallel processing, GPU
80
+ support, GUI-based tools and so on.
80
81
 
81
- The core array, `ImgArray`, is a subclass of `numpy.ndarray`, tagged with information such as
82
+ The core array, `ImgArray`, is a subclass of `numpy.ndarray`, tagged with information
83
+ such as:
82
84
 
83
85
  - image axes
84
86
  - scale of each axis
@@ -109,7 +111,10 @@ git clone https://github.com/hanjinliu/impy
109
111
 
110
112
  ### Code as fast as you speak
111
113
 
112
- Almost all the functions, such as filtering, deconvolution, labeling, single molecule detection, and even those pure `numpy` functions, are aware of image metadata. They "know" which dimension corresponds to `"z"` axis, which axes they should iterate along or where to save the image. As a result, **your code will be very concise**:
114
+ Almost all the functions, such as filtering, deconvolution, labeling, single molecule
115
+ detection, and even those pure `numpy` functions, are aware of image metadata. They
116
+ "know" which dimension corresponds to `"z"` axis, which axes they should iterate along
117
+ or where to save the image. As a result, **your code will be very concise**:
113
118
 
114
119
  ```python
115
120
  import impy as ip
@@ -124,7 +129,7 @@ img_prj.imsave(f"Max-{img.name}") # Save in the same place. Don't spend tim
124
129
 
125
130
  ### Supports many file formats
126
131
 
127
- `impy` automatically chooses proper reader/writer according to the extension.
132
+ `impy` automatically chooses the proper reader/writer according to the extension.
128
133
 
129
134
  - Tiff file (".tif", ".tiff")
130
135
  - MRC file (".mrc", ".rec", ".st", ".map", ".map.gz")
@@ -183,8 +188,8 @@ img.imfilter(param=1.0)
183
188
 
184
189
  ### Command line usage
185
190
 
186
- `impy` also supports command line based image analysis. All method of `ImgArray` is available
187
- from commad line, such as
191
+ `impy` also supports command-line-based image analysis. All methods of `ImgArray` are
192
+ available from the command line, such as
188
193
 
189
194
  ```shell
190
195
  impy path/to/image.tif ./output.tif --method gaussian_filter --sigma 2.0
@@ -199,7 +204,7 @@ out = img.gaussian_filter(sigma=2.0)
199
204
  out.imsave("./output.tif")
200
205
  ```
201
206
 
202
- For more complex procedure, it is possible to send image directly to `IPython`
207
+ For more complex procedures, it is possible to send images directly to `IPython`
203
208
 
204
209
  ```
205
210
  impy path/to/image.tif -i
@@ -4,9 +4,11 @@
4
4
 
5
5
  # A numpy extension for efficient and powerful image analysis workflow
6
6
 
7
- `impy` is an all-in-one image analysis library, equipped with parallel processing, GPU support, GUI based tools and so on.
7
+ `impy` is an all-in-one image analysis library, equipped with parallel processing, GPU
8
+ support, GUI-based tools and so on.
8
9
 
9
- The core array, `ImgArray`, is a subclass of `numpy.ndarray`, tagged with information such as
10
+ The core array, `ImgArray`, is a subclass of `numpy.ndarray`, tagged with information
11
+ such as:
10
12
 
11
13
  - image axes
12
14
  - scale of each axis
@@ -37,7 +39,10 @@ git clone https://github.com/hanjinliu/impy
37
39
 
38
40
  ### Code as fast as you speak
39
41
 
40
- Almost all the functions, such as filtering, deconvolution, labeling, single molecule detection, and even those pure `numpy` functions, are aware of image metadata. They "know" which dimension corresponds to `"z"` axis, which axes they should iterate along or where to save the image. As a result, **your code will be very concise**:
42
+ Almost all the functions, such as filtering, deconvolution, labeling, single molecule
43
+ detection, and even those pure `numpy` functions, are aware of image metadata. They
44
+ "know" which dimension corresponds to `"z"` axis, which axes they should iterate along
45
+ or where to save the image. As a result, **your code will be very concise**:
41
46
 
42
47
  ```python
43
48
  import impy as ip
@@ -52,7 +57,7 @@ img_prj.imsave(f"Max-{img.name}") # Save in the same place. Don't spend tim
52
57
 
53
58
  ### Supports many file formats
54
59
 
55
- `impy` automatically chooses proper reader/writer according to the extension.
60
+ `impy` automatically chooses the proper reader/writer according to the extension.
56
61
 
57
62
  - Tiff file (".tif", ".tiff")
58
63
  - MRC file (".mrc", ".rec", ".st", ".map", ".map.gz")
@@ -111,8 +116,8 @@ img.imfilter(param=1.0)
111
116
 
112
117
  ### Command line usage
113
118
 
114
- `impy` also supports command line based image analysis. All method of `ImgArray` is available
115
- from commad line, such as
119
+ `impy` also supports command-line-based image analysis. All methods of `ImgArray` are
120
+ available from the command line, such as
116
121
 
117
122
  ```shell
118
123
  impy path/to/image.tif ./output.tif --method gaussian_filter --sigma 2.0
@@ -127,7 +132,7 @@ out = img.gaussian_filter(sigma=2.0)
127
132
  out.imsave("./output.tif")
128
133
  ```
129
134
 
130
- For more complex procedure, it is possible to send image directly to `IPython`
135
+ For more complex procedures, it is possible to send images directly to `IPython`
131
136
 
132
137
  ```
133
138
  impy path/to/image.tif -i
@@ -1,4 +1,4 @@
1
- __version__ = "2.4.0"
1
+ __version__ = "2.4.1"
2
2
  __author__ = "Hanjin Liu"
3
3
  __email__ = "liuhanjin-sc@g.ecc.u-tokyo.ac.jp"
4
4
 
@@ -10,9 +10,9 @@ if TYPE_CHECKING:
10
10
  from impy import ImgArray
11
11
 
12
12
  __all__ = [
13
- "compose_affine_matrix",
13
+ "compose_affine_matrix",
14
14
  "decompose_affine_matrix",
15
- "affinefit",
15
+ "affinefit",
16
16
  "check_matrix",
17
17
  "warp",
18
18
  ]
@@ -24,8 +24,8 @@ class AffineTransformationParameters(NamedTuple):
24
24
  shear: np.ndarray
25
25
 
26
26
  def warp(
27
- img: xp.ndarray,
28
- matrix: xp.ndarray,
27
+ img: np.ndarray,
28
+ matrix: np.ndarray,
29
29
  mode="constant",
30
30
  cval=0,
31
31
  output_shape=None,
@@ -35,13 +35,13 @@ def warp(
35
35
  ):
36
36
  img = xp.asarray(img, dtype=img.dtype)
37
37
  matrix = xp.asarray(matrix)
38
- out = xp.ndi.affine_transform(img, matrix, cval=cval, mode=mode, output_shape=output_shape,
38
+ out = xp.ndi.affine_transform(img, matrix, cval=cval, mode=mode, output_shape=output_shape,
39
39
  offset=offset, order=order, prefilter=prefilter)
40
40
  return out
41
41
 
42
- def shift(img: xp.ndarray, shift: xp.ndarray, cval=0, mode="constant", order=1, prefilter=False):
42
+ def shift(img: np.ndarray, shift: np.ndarray, cval=0, mode="constant", order=1, prefilter=False):
43
43
  img = xp.asarray(img, dtype=img.dtype)
44
- out = xp.ndi.shift(img, shift, cval=cval, mode=mode,
44
+ out = xp.ndi.shift(img, shift, cval=cval, mode=mode,
45
45
  order=order, prefilter=prefilter)
46
46
  return out
47
47
 
@@ -71,10 +71,10 @@ def compose_affine_matrix(
71
71
  translation = [translation] * ndim
72
72
  if rotation is not None:
73
73
  rotation = np.rad2deg(rotation)
74
-
74
+
75
75
  af = Affine(scale=scale, translate=translation, rotate=rotation, shear=shear)
76
76
  mx = af.affine_matrix
77
-
77
+
78
78
  return mx
79
79
 
80
80
 
@@ -83,12 +83,12 @@ def decompose_affine_matrix(matrix: np.ndarray):
83
83
  if ndim == 2:
84
84
  from skimage.transform import AffineTransform
85
85
  af = AffineTransform(matrix=matrix)
86
- out = AffineTransformationParameters(translation=af.translation, rotation=af.rotation,
86
+ out = AffineTransformationParameters(translation=af.translation, rotation=af.rotation,
87
87
  scale=af.scale, shear=af.shear)
88
88
  else:
89
89
  from napari.utils.transforms import Affine
90
90
  af = Affine(affine_matrix=matrix)
91
- out = AffineTransformationParameters(translation=af.translate, rotation=af.rotate,
91
+ out = AffineTransformationParameters(translation=af.translate, rotation=af.rotate,
92
92
  scale=af.scale, shear=af.shear)
93
93
  return out
94
94
 
@@ -104,8 +104,10 @@ def affinefit(img, imgref, bins=256, order=1):
104
104
  from scipy.stats import entropy
105
105
  from scipy.optimize import minimize
106
106
  from skimage.transform import warp
107
-
108
- as_3x3_matrix = lambda mtx: np.vstack((mtx.reshape(2,3), [0., 0., 1.]))
107
+
108
+ def as_3x3_matrix(mtx: np.ndarray):
109
+ return np.vstack((mtx.reshape(2,3), [0., 0., 1.]))
110
+
109
111
  def normalized_mutual_information(img, imgref):
110
112
  """
111
113
  Y(A,B) = (H(A)+H(B))/H(A,B)
@@ -117,15 +119,15 @@ def affinefit(img, imgref, bins=256, order=1):
117
119
  e2 = entropy(np.sum(hist, axis=1))
118
120
  e12 = entropy(np.ravel(hist)) # mutual entropy
119
121
  return (e1 + e2)/e12
120
-
122
+
121
123
  def cost_nmi(mtx, img, imgref):
122
124
  mtx = as_3x3_matrix(mtx)
123
125
  img_transformed = warp(img, mtx, order=order)
124
126
  return -normalized_mutual_information(img_transformed, imgref)
125
-
127
+
126
128
  mtx0 = np.array([[1., 0., 0.],
127
129
  [0., 1., 0.]]) # aberration makes little difference
128
-
130
+
129
131
  result = minimize(
130
132
  cost_nmi, mtx0, args=(np.asarray(img), np.asarray(imgref)), method="Powell"
131
133
  )
@@ -134,24 +136,24 @@ def affinefit(img, imgref, bins=256, order=1):
134
136
 
135
137
 
136
138
  def check_matrix(matrices: list[np.ndarray | float]):
137
- """Check Affine transformation matrix."""
139
+ """Check Affine transformation matrix."""
138
140
  mtx = []
139
141
  for m in matrices:
140
- if np.isscalar(m):
142
+ if np.isscalar(m):
141
143
  if m == 1:
142
144
  mtx.append(m)
143
145
  else:
144
146
  raise ValueError(f"Only `1` is ok, but got {m}")
145
-
147
+
146
148
  elif m.shape != (3, 3) or not np.allclose(m[2,:2], 0):
147
149
  raise ValueError(f"Wrong Affine transformation matrix:\n{m}")
148
-
150
+
149
151
  else:
150
152
  mtx.append(m)
151
153
  return mtx
152
154
 
153
155
  def polar2d(
154
- img: xp.ndarray,
156
+ img: np.ndarray,
155
157
  rmax: int,
156
158
  dtheta: float = 0.1, # radian
157
159
  order: int = 1,
@@ -170,14 +172,14 @@ def polar2d(
170
172
  return out
171
173
 
172
174
  def normalize_radon_input(
173
- self: ImgArray,
174
- dims: Axes,
175
- central_axis: AxisLike,
175
+ self: ImgArray,
176
+ dims: Axes,
177
+ central_axis: AxisLike,
176
178
  degrees: Sequence[float] | float,
177
- ):
179
+ ):
178
180
  ndim = len(dims)
179
181
  squeeze = not hasattr(degrees, "__iter__")
180
-
182
+
181
183
  if squeeze:
182
184
  degrees = [degrees]
183
185
  if ndim != self.ndim:
@@ -213,7 +215,7 @@ def normalize_radon_input(
213
215
  central_axis: np.ndarray
214
216
  if central_axis.shape != (ndim,):
215
217
  raise ValueError(f"Image is {ndim}D but central_axis is {central_axis.ndim}D.")
216
-
218
+
217
219
  # construct Affine transform matrices
218
220
  height = int(np.ceil(np.linalg.norm(self.shape)))
219
221
  output_shape = (height, self.shape[1], self.shape[2])
@@ -222,11 +224,11 @@ def normalize_radon_input(
222
224
  )
223
225
  else:
224
226
  raise ValueError("Only 2D or 3D input is supported.")
225
-
227
+
226
228
  return params, output_shape, squeeze
227
229
 
228
230
  def normalize_iradon_input(
229
- self: ImgArray,
231
+ self: ImgArray,
230
232
  central_axis: AxisLike,
231
233
  height_axis: AxisLike,
232
234
  degree_axis: AxisLike,
@@ -250,7 +252,7 @@ def normalize_iradon_input(
250
252
  output_shape = (height, self.shape[-1])
251
253
  return central_axis, degree_axis, output_shape, new_axes
252
254
 
253
- def radon_single(img: xp.ndarray, mtx: np.ndarray, order: int = 3, output_shape=None):
255
+ def radon_single(img: np.ndarray, mtx: np.ndarray, order: int = 3, output_shape=None):
254
256
  """Radon transform of 2D image."""
255
257
  img_rot = warp(img, mtx, order=order, output_shape=output_shape, prefilter=False)
256
258
  return xp.sum(img_rot, axis=0)
@@ -290,13 +292,13 @@ def _get_rotation_matrices_for_radon_3d(
290
292
  return np.einsum("ij,njk,kl->nil", tr_0, rotation, tr_1)
291
293
 
292
294
  # This function is mostly ported from `skimage.transform`.
293
- # The most important difference is that this implementation support arbitrary
295
+ # The most important difference is that this implementation support arbitrary
294
296
  # output shape.
295
297
  def iradon(
296
- img: xp.ndarray,
297
- degrees: xp.ndarray,
298
+ img: np.ndarray,
299
+ degrees: np.ndarray,
298
300
  output_shape: tuple[int, int],
299
- filter_func: xp.ndarray,
301
+ filter_func: np.ndarray,
300
302
  interp: Callable[[np.ndarray, np.ndarray], np.ndarray],
301
303
  ):
302
304
  angles_count = len(degrees)
@@ -322,7 +324,7 @@ def iradon(
322
324
 
323
325
  return reconstructed * np.pi / (2 * angles_count)
324
326
 
325
- # This function is almost ported from `skimage.transform`, but create a ramp
327
+ # This function is almost ported from `skimage.transform`, but create a ramp
326
328
  # filter in a simpler way.
327
329
  def get_fourier_filter(size: int, filter_name: str):
328
330
  # ramp filter
@@ -221,60 +221,78 @@ class ImgArray(LabeledArray):
221
221
  @_docs.write_docs
222
222
  @dims_to_spatial_axes
223
223
  @same_dtype(asfloat=True)
224
- def rescale(
224
+ def zoom(
225
225
  self,
226
- scale: nDFloat = 1/16,
226
+ zoom: nDFloat,
227
227
  *,
228
- order: int = None,
229
- mode: PaddingMode = "reflect",
228
+ order: int = 3,
229
+ mode: PaddingMode = "constant",
230
230
  cval: float = 0.0,
231
+ same_shape: bool = False,
231
232
  dims: Dims = None,
232
233
  ) -> ImgArray:
233
234
  """
234
- Rescale image by applying the diagonal component of Affine matrix.
235
+ Zoom image by applying the diagonal component of Affine matrix.
235
236
 
236
237
  Parameters
237
238
  ----------
238
- scale : float, optional
239
+ scale : float
239
240
  Relative scale of the new image. scale 1/2 means that the shape of
240
241
  the output image will be (N/2, ...).
241
242
  {order}{mode}{cval}{dims}
243
+ same_shape : bool, default is False
244
+ If True, the output image will have the same shape as the input image.
242
245
 
243
246
  Returns
244
247
  -------
245
248
  ImgArray
246
249
  Rescaled image.
247
250
  """
248
- from skimage.transform import rescale
249
- scale_is_seq = hasattr(scale, "__iter__")
251
+ zoom_is_seq = hasattr(zoom, "__iter__")
250
252
 
251
253
  # Check if output is too large.
252
254
  gb = -1
253
- if not scale_is_seq and scale > 1:
254
- gb = np.prod(self.shape) * (scale ** len(dims)) / 2**30
255
- elif scale_is_seq and np.prod(list(scale)) > 1:
256
- gb = np.prod(self.shape) * np.prod(list(scale)) / 2**30
255
+ if not zoom_is_seq and zoom > 1:
256
+ gb = np.prod(self.shape) * (zoom ** len(dims)) / 2**30 * self.itemsize
257
+ elif zoom_is_seq and np.prod(list(zoom)) > 1:
258
+ gb = np.prod(self.shape) * np.prod(list(zoom)) / 2**30 * self.itemsize
257
259
  if gb > Const["MAX_GB"]:
258
260
  raise MemoryError(f"Output image is too large: {gb} GB")
259
261
 
260
- if scale_is_seq:
261
- scale = list(scale)
262
- if len(scale) != len(dims):
262
+ if zoom_is_seq:
263
+ zoom = list(zoom)
264
+ if len(zoom) != len(dims):
263
265
  raise ValueError(
264
266
  "scale must have the same length as the spatial dimensions."
265
267
  )
266
- it = iter(scale)
267
- scale_ = [next(it) if a in dims else 1 for a in self.axes]
268
+ it = iter(zoom)
269
+ zoom_ = [next(it) if a in dims else 1 for a in self.axes]
268
270
  else:
269
- scale_ = [scale if a in dims else 1 for a in self.axes]
270
- out = rescale(
271
- self.value, scale_, order=order, mode=mode, cval=cval, anti_aliasing=False
272
- )
273
- out = out.view(self.__class__)._set_info(self)
271
+ zoom_ = [zoom if a in dims else 1 for a in self.axes]
272
+
273
+ if not same_shape:
274
+ if mode == "constant":
275
+ mode = "grid-constant"
276
+ out = ndi.zoom(
277
+ self.value, zoom_, order=order, mode=mode, cval=cval,
278
+ prefilter=order > 1, grid_mode=True,
279
+ ).view(self.__class__)._set_info(self)
280
+ else:
281
+ center = [(s - 1) / 2 for s in self.sizesof(dims)]
282
+ mesh = np.meshgrid(
283
+ *[
284
+ (np.arange(_s) - _c) / _z + _c
285
+ for _s, _z, _c in zip(self.sizesof(dims), zoom_, center)
286
+ ],
287
+ indexing="ij",
288
+ )
289
+ out = self.map_coordinates(mesh, order=order, mode=mode, cval=cval, dims=dims)
274
290
  out.axes = self.axes.copy()
275
- out.set_scale({a: self.scale[a]/scale for a, scale in zip(self.axes, scale_)})
291
+ out.set_scale({a: self.scale[a]/scale for a, scale in zip(self.axes, zoom_)})
276
292
  return out
277
293
 
294
+ rescale = zoom # backward compatibility
295
+
278
296
  @_docs.write_docs
279
297
  @dims_to_spatial_axes
280
298
  @same_dtype
@@ -465,10 +483,10 @@ class ImgArray(LabeledArray):
465
483
  out.metadata["GaussianParameters"] = params
466
484
  return out
467
485
 
468
- rough = self.rescale(scale).value.astype(np.float32)
486
+ rough = self.zoom(scale, mode="reflect", order=1).value.astype(np.float32)
469
487
  if mask is not None:
470
- from ..core import asarray as ip_asarray
471
- mask = ip_asarray(mask).rescale(scale) > 0
488
+ from impy.core import asarray as ip_asarray
489
+ mask = ip_asarray(mask).zoom(scale, mode="reflect", order=1) > 0
472
490
  gaussian = GaussianBackground(p0)
473
491
  gaussian.fit(rough, method=method, mask=mask)
474
492
  gaussian.rescale(1/scale)
@@ -480,12 +498,18 @@ class ImgArray(LabeledArray):
480
498
 
481
499
  @same_dtype(asfloat=True)
482
500
  @check_input_and_output
483
- def gauss_correction(self, ref: ImgArray=None, scale: float = 1/16, median_radius: float = 15):
501
+ def gauss_correction(
502
+ self,
503
+ ref: ImgArray | None = None,
504
+ scale: float = 1/16,
505
+ median_radius: float = 15,
506
+ ):
484
507
  """
485
- Correct unevenly distributed excitation light using Gaussian fitting. This method subtracts
486
- background intensity at the same time. If input image is uint, then output value under 0 will
487
- replaced with 0. If you want to quantify background, it is necessary to first convert input
488
- image to float image.
508
+ Correct unevenly distributed excitation light using Gaussian fitting.
509
+
510
+ This method subtracts background intensity at the same time. If input image is
511
+ uint, then output value under 0 will replaced with 0. If you want to quantify
512
+ background, it is necessary to first convert input image to float image.
489
513
 
490
514
  Parameters
491
515
  ----------
@@ -1069,7 +1093,6 @@ class ImgArray(LabeledArray):
1069
1093
  kwargs=dict(footprint=disk, mode=mode, cval=cval)
1070
1094
  )
1071
1095
 
1072
-
1073
1096
  @_docs.write_docs
1074
1097
  @dims_to_spatial_axes
1075
1098
  @check_input_and_output(only_binary=True)
@@ -1321,7 +1344,9 @@ class ImgArray(LabeledArray):
1321
1344
  @check_input_and_output
1322
1345
  def entropy_filter(self, radius: nDFloat = 5, *, dims: Dims = None) -> ImgArray:
1323
1346
  """
1324
- Running entropy filter. This filter is useful for detecting change in background distribution.
1347
+ Running entropy filter.
1348
+
1349
+ This filter is useful for detecting change in background distribution.
1325
1350
 
1326
1351
  Parameters
1327
1352
  ----------
@@ -1413,8 +1438,10 @@ class ImgArray(LabeledArray):
1413
1438
  def kalman_filter(self, gain: float = 0.8, noise_var: float = 0.05, *, along: str = "t",
1414
1439
  dims: Dims = None, update: bool = False) -> ImgArray:
1415
1440
  """
1416
- Kalman filter for image smoothing. This function is same as "Kalman Stack Filter" in ImageJ but support
1417
- batch processing. This filter is useful for preprocessing of particle tracking.
1441
+ Kalman filter for image smoothing.
1442
+
1443
+ This function is same as "Kalman Stack Filter" in ImageJ but support batch
1444
+ processing. This filter is useful for preprocessing of particle tracking.
1418
1445
 
1419
1446
  Parameters
1420
1447
  ----------
@@ -1450,8 +1477,10 @@ class ImgArray(LabeledArray):
1450
1477
  @check_input_and_output
1451
1478
  def focus_map(self, radius: int = 1, *, dims: Dims = 2) -> PropArray:
1452
1479
  """
1453
- Compute focus map using variance of Laplacian method. yx-plane with higher variance is likely a
1454
- focal plane because sharper image causes higher value of Laplacian on the edges.
1480
+ Compute focus map using variance of Laplacian method.
1481
+
1482
+ yx-plane with higher variance is likely a focal plane because sharper image
1483
+ causes higher value of Laplacian on the edges.
1455
1484
 
1456
1485
  Parameters
1457
1486
  ----------
@@ -1626,8 +1655,10 @@ class ImgArray(LabeledArray):
1626
1655
  dims: Dims = None,
1627
1656
  ) -> ImgArray:
1628
1657
  """
1629
- Run Difference of Gaussian filter. This function does not support `update`
1630
- argument because intensity can be negative.
1658
+ Run Difference of Gaussian filter.
1659
+
1660
+ This function does not support `update` argument because intensity can be
1661
+ negative.
1631
1662
 
1632
1663
  Parameters
1633
1664
  ----------
@@ -2809,11 +2840,12 @@ class ImgArray(LabeledArray):
2809
2840
  dtype = np.complex128 if double_precision else np.complex64
2810
2841
 
2811
2842
  # Calculate exp(-ikx)
2812
- # To minimize floating error, the A term in exp(-2*pi*i*A) should be in the range of
2813
- # 0 <= A < 1.
2814
- exps: list[xp.ndarray] = \
2815
- [xp.exp(-2j * np.pi * xp.mod(wave_num(sl, s, uf) * xp.arange(s)/s, 1.), dtype=dtype)
2816
- for sl, s, uf in zip(slices, self.sizesof(dims), upsample_factor)]
2843
+ # To minimize floating error, the A term in exp(-2*pi*i*A) should be in the
2844
+ # range of 0 <= A < 1.
2845
+ exps: list[np.ndarray] = [
2846
+ xp.exp(-2j * np.pi * xp.mod(wave_num(sl, s, uf) * xp.arange(s)/s, 1.), dtype=dtype)
2847
+ for sl, s, uf in zip(slices, self.sizesof(dims), upsample_factor)
2848
+ ]
2817
2849
 
2818
2850
  # Calculate chunk size for proper output shapes
2819
2851
  out_chunks = np.ones(self.ndim, dtype=np.int64)
@@ -3859,7 +3891,7 @@ class ImgArray(LabeledArray):
3859
3891
 
3860
3892
  out = self.view(np.ndarray).astype(np.float32)
3861
3893
  lowerlim, upperlim = _check_clip_range(in_range, self.value)
3862
- out = rescale_intensity(out, in_range=(lowerlim, upperlim), out_range="dtype")
3894
+ out = rescale_intensity(out, in_range=(lowerlim, upperlim), out_range=dtype)
3863
3895
  out = out.view(self.__class__)
3864
3896
  return out
3865
3897
 
@@ -4538,13 +4570,13 @@ def _specify_one(center, radius, shape:tuple) -> tuple[slice]:
4538
4570
 
4539
4571
  def check_filter_func(f):
4540
4572
  if f is None:
4541
- f = lambda x: True
4573
+ return lambda x: True
4542
4574
  elif not callable(f):
4543
4575
  raise TypeError("`filt` must be callable.")
4544
4576
  return f
4545
4577
 
4546
4578
 
4547
- def wave_num(sl: slice, s: int, uf: int) -> xp.ndarray:
4579
+ def wave_num(sl: slice, s: int, uf: int) -> np.ndarray:
4548
4580
  """
4549
4581
  A function that makes wave number vertical vector. Returned vector will
4550
4582
  be [k/s, (k + 1/uf)/s, (k + 2/uf)/s, ...] (where k = sl.start)
@@ -373,7 +373,6 @@ def _(path: str, img: ImpyArray, lazy: bool = False):
373
373
  img_dask = _rechunk_to_ones(img.value)
374
374
  writer = _MemmapArrayWriter(path, mmap.offset, img.shape, img_dask.chunksize)
375
375
  da.store(img_dask, writer)
376
- mmap.flush()
377
376
  return
378
377
 
379
378
  from tifffile import imwrite
@@ -471,7 +470,6 @@ def _(path: str, img: ImpyArray, lazy: bool = False):
471
470
  img_dask = _rechunk_to_ones(img.value)
472
471
  writer = _MemmapArrayWriter(path, mrc_mmap.data.offset, img.shape, img_dask.chunksize)
473
472
  da.store(img_dask, writer)
474
- mrc_mmap.flush()
475
473
  return None
476
474
 
477
475
  # get voxel_size
@@ -739,12 +737,13 @@ class _MemmapArrayWriter:
739
737
  # sl = (0:1, 16:32, 0:1000, 0:1000)
740
738
 
741
739
  offset = np.sum([sl[i].start * arr.strides[i] for i in range(self._border + 1)])
740
+ arr_ravel = arr.ravel()
742
741
  mmap = np.memmap(
743
742
  self._path,
744
743
  mode="r+",
745
744
  offset=self._offset + offset,
745
+ shape=arr_ravel.shape,
746
746
  dtype=arr.dtype,
747
747
  )
748
- arr_ravel = arr.ravel()
749
748
  mmap[:arr_ravel.size] = arr_ravel
750
749
  mmap.flush()
@@ -21,11 +21,11 @@ def diagonal_gaussian(r, *params):
21
21
  sg = params[ndim:-2]
22
22
  z_value = np.array([(x0 - mu0)/sg0 for x0, mu0, sg0 in zip(r, mu, sg)])
23
23
  return a * np.exp(-np.sum(z_value**2, axis=0) / 2) + b
24
-
24
+
25
25
  class Gaussian:
26
26
  def mu_inrange(self, low, high):
27
27
  return np.logical_and(low<=self.mu, self.mu<=high).all()
28
-
28
+
29
29
  def sg_inrange(self, low, high):
30
30
  sg_ = np.abs(self.sg)
31
31
  return np.logical_and(low<=sg_, sg_<=high).all()
@@ -36,40 +36,40 @@ class DiagonalGaussian(Gaussian):
36
36
  self.mu = self.sg = self.a = self.b = None
37
37
  else:
38
38
  self.mu, self.sg, self.a, self.b = params
39
-
39
+
40
40
  @property
41
41
  def mu(self):
42
42
  return self._mu
43
-
43
+
44
44
  @mu.setter
45
45
  def mu(self, value):
46
46
  if value is None:
47
47
  self._mu = None
48
48
  else:
49
49
  self._mu = np.asarray(value)
50
-
50
+
51
51
  @property
52
52
  def sg(self):
53
53
  return self._sg
54
-
54
+
55
55
  @sg.setter
56
56
  def sg(self, value):
57
57
  if value is None:
58
58
  self._sg = None
59
59
  else:
60
60
  self._sg = np.asarray(value)
61
-
62
- @property
61
+
62
+ @property
63
63
  def params(self):
64
64
  """Get flattened parameters."""
65
65
  return tuple(self.mu) + tuple(self.sg) + (self.a, self.b)
66
-
66
+
67
67
  @params.setter
68
68
  def params(self, params:tuple):
69
69
  self.a, self.b = params[-2:]
70
70
  self.mu = params[:self.ndim]
71
71
  self.sg = params[self.ndim:-2]
72
-
72
+
73
73
  def asdict(self) -> dict[str, Any]:
74
74
  return {
75
75
  "mu": self.mu.tolist(),
@@ -77,12 +77,17 @@ class DiagonalGaussian(Gaussian):
77
77
  "A": self.a,
78
78
  "B": self.b,
79
79
  }
80
-
80
+
81
81
  @property
82
82
  def ndim(self):
83
83
  return self.mu.size
84
-
85
- def fit(self, data: np.ndarray, method: str = "Powell", mask: np.ndarray | None = None):
84
+
85
+ def fit(
86
+ self,
87
+ data: np.ndarray,
88
+ method: str = "Powell",
89
+ mask: np.ndarray | None = None,
90
+ ):
86
91
  from scipy.optimize import minimize
87
92
  if self.mu is None or self.sg is None or self.a is None or self.b is None:
88
93
  if mask is not None:
@@ -92,7 +97,7 @@ class DiagonalGaussian(Gaussian):
92
97
  else:
93
98
  self._estimate_params(data)
94
99
  r = np.indices(data.shape)
95
-
100
+
96
101
  if mask is None:
97
102
  result = minimize(
98
103
  square,
@@ -112,30 +117,30 @@ class DiagonalGaussian(Gaussian):
112
117
  method=method
113
118
  )
114
119
  self.params = result.x
115
-
120
+
116
121
  return result
117
-
122
+
118
123
  def rescale(self, scale: float):
119
124
  self.mu *= scale
120
125
  self.sg *= scale
121
126
  return None
122
-
127
+
123
128
  def shift(self, dxdy):
124
129
  self.mu += np.array(dxdy)
125
130
  return None
126
-
131
+
127
132
  def generate(self, shape:tuple) -> np.ndarray:
128
133
  r = np.indices(shape, dtype=np.float32)
129
134
  return diagonal_gaussian(r, *self.params)
130
-
131
- def _estimate_params(self, data:np.ndarray):
135
+
136
+ def _estimate_params(self, data: np.ndarray):
132
137
  pass
133
138
 
134
139
  class GaussianParticle(DiagonalGaussian):
135
140
  def __init__(self, params=None, initial_sg=1):
136
141
  super().__init__(params)
137
142
  self.initial_sg = initial_sg
138
-
143
+
139
144
  def _estimate_params(self, data:np.ndarray):
140
145
  # n-dim argmax
141
146
  self.mu = np.array(np.unravel_index(np.argmax(data), data.shape), dtype="float32")
@@ -151,4 +156,4 @@ class GaussianBackground(DiagonalGaussian):
151
156
  self.sg = np.array(data.shape, dtype="float32")
152
157
  self.b, p95 = np.percentile(data, [5, 95])
153
158
  self.a = p95 - self.b
154
- return None
159
+ return None
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