imops 0.8.1__cp311-cp311-musllinux_1_1_i686.whl → 0.8.3__cp311-cp311-musllinux_1_1_i686.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.

Potentially problematic release.


This version of imops might be problematic. Click here for more details.

Files changed (53) hide show
  1. _build_utils.py +87 -0
  2. imops/__init__.py +1 -0
  3. imops/__version__.py +1 -1
  4. imops/backend.py +14 -10
  5. imops/box.py +20 -29
  6. imops/crop.py +18 -2
  7. imops/interp1d.py +16 -13
  8. imops/measure.py +12 -9
  9. imops/morphology.py +155 -35
  10. imops/numeric.py +376 -0
  11. imops/pad.py +41 -5
  12. imops/radon.py +9 -7
  13. imops/src/_backprojection.c +83 -83
  14. imops/src/_backprojection.cpython-311-i386-linux-musl.so +0 -0
  15. imops/src/_fast_backprojection.c +96 -96
  16. imops/src/_fast_backprojection.cpython-311-i386-linux-musl.so +0 -0
  17. imops/src/_fast_measure.c +96 -96
  18. imops/src/_fast_measure.cpython-311-i386-linux-musl.so +0 -0
  19. imops/src/_fast_morphology.c +2847 -1587
  20. imops/src/_fast_morphology.cpython-311-i386-linux-musl.so +0 -0
  21. imops/src/_fast_morphology.pyx +315 -131
  22. imops/src/_fast_numeric.c +20545 -4996
  23. imops/src/_fast_numeric.cpython-311-i386-linux-musl.so +0 -0
  24. imops/src/_fast_numeric.pyx +208 -30
  25. imops/src/_fast_radon.c +96 -96
  26. imops/src/_fast_radon.cpython-311-i386-linux-musl.so +0 -0
  27. imops/src/_fast_zoom.c +5945 -3342
  28. imops/src/_fast_zoom.cpython-311-i386-linux-musl.so +0 -0
  29. imops/src/_fast_zoom.pyx +1 -0
  30. imops/src/_measure.c +83 -83
  31. imops/src/_measure.cpython-311-i386-linux-musl.so +0 -0
  32. imops/src/_morphology.c +2768 -1553
  33. imops/src/_morphology.cpython-311-i386-linux-musl.so +0 -0
  34. imops/src/_morphology.pyx +315 -131
  35. imops/src/_numeric.c +20532 -4983
  36. imops/src/_numeric.cpython-311-i386-linux-musl.so +0 -0
  37. imops/src/_numeric.pyx +208 -30
  38. imops/src/_radon.c +83 -83
  39. imops/src/_radon.cpython-311-i386-linux-musl.so +0 -0
  40. imops/src/_zoom.c +5932 -3329
  41. imops/src/_zoom.cpython-311-i386-linux-musl.so +0 -0
  42. imops/src/_zoom.pyx +1 -0
  43. imops/utils.py +113 -13
  44. imops/zoom.py +9 -9
  45. {imops-0.8.1.dist-info → imops-0.8.3.dist-info}/METADATA +40 -19
  46. imops-0.8.3.dist-info/RECORD +61 -0
  47. {imops-0.8.1.dist-info → imops-0.8.3.dist-info}/WHEEL +1 -1
  48. imops-0.8.3.dist-info/top_level.txt +2 -0
  49. _pyproject_build.py +0 -49
  50. imops/_numeric.py +0 -124
  51. imops-0.8.1.dist-info/RECORD +0 -61
  52. imops-0.8.1.dist-info/top_level.txt +0 -2
  53. {imops-0.8.1.dist-info → imops-0.8.3.dist-info}/LICENSE +0 -0
imops/morphology.py CHANGED
@@ -6,59 +6,133 @@ from scipy.ndimage import generate_binary_structure
6
6
  from skimage.morphology import binary_dilation as scipy_binary_dilation, binary_erosion as scipy_binary_erosion
7
7
 
8
8
  from .backend import BackendLike, Cython, Scipy, resolve_backend
9
+ from .box import add_margin, box_to_shape, mask_to_box, shape_to_box
10
+ from .crop import crop_to_box
11
+ from .pad import restore_crop
9
12
  from .src._fast_morphology import (
10
13
  _binary_dilation as cython_fast_binary_dilation,
11
14
  _binary_erosion as cython_fast_binary_erosion,
12
15
  )
13
16
  from .src._morphology import _binary_dilation as cython_binary_dilation, _binary_erosion as cython_binary_erosion
14
- from .utils import composition_args, normalize_num_threads
17
+ from .utils import composition_args, morphology_composition_args, normalize_num_threads
15
18
 
16
19
 
17
20
  def morphology_op_wrapper(
18
21
  op_name: str, backend2src_op: Callable[[np.ndarray, np.ndarray, int], np.ndarray]
19
22
  ) -> Callable:
20
23
  def wrapped(
21
- image: np.ndarray, footprint: np.ndarray = None, num_threads: int = -1, backend: BackendLike = None
24
+ image: np.ndarray,
25
+ footprint: np.ndarray = None,
26
+ output: np.ndarray = None,
27
+ boxed: bool = False,
28
+ num_threads: int = -1,
29
+ backend: BackendLike = None,
22
30
  ) -> np.ndarray:
23
- backend = resolve_backend(backend)
31
+ backend = resolve_backend(backend, warn_stacklevel=4)
24
32
  if backend.name not in {x.name for x in backend2src_op.keys()}:
25
- raise ValueError(f'Unsupported backend "{backend.name}"')
33
+ raise ValueError(f'Unsupported backend "{backend.name}".')
26
34
 
27
35
  ndim = image.ndim
28
- num_threads = normalize_num_threads(num_threads, backend)
36
+ num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=4)
29
37
 
30
38
  if footprint is None:
31
39
  footprint = generate_binary_structure(ndim, 1)
32
40
  elif not footprint.size:
33
- raise RuntimeError('Footprint must not be empty')
41
+ raise RuntimeError('Footprint must not be empty.')
42
+
43
+ if output is None:
44
+ output = np.empty_like(image, dtype=bool)
45
+ elif output.shape != image.shape:
46
+ raise ValueError('Input image and output image shapes must be the same.')
47
+ elif not output.data.c_contiguous:
48
+ # TODO: Implement morphology for `output` of arbitrary layout
49
+ raise ValueError('`output` must be a C-contiguous array.')
34
50
 
35
51
  src_op = backend2src_op[backend]
36
52
 
37
53
  if backend.name == 'Scipy':
38
- return src_op(image, footprint)
54
+ if boxed:
55
+ raise ValueError('`boxed==True` is incompatible with "Scipy" backend.')
56
+ output = src_op(image, footprint)
57
+
58
+ return output
39
59
 
40
60
  if ndim > 3:
41
61
  warn(
42
62
  f"Fast {' '.join(op_name.split('_'))} is only supported for ndim<=3. "
43
- "Falling back to scipy's implementation."
63
+ "Falling back to scipy's implementation.",
64
+ stacklevel=3,
44
65
  )
45
- src_op(image, footprint)
66
+ output = backend2src_op[Scipy()](image, footprint)
67
+
68
+ return output
46
69
 
47
- if footprint.ndim != ndim:
70
+ if footprint.ndim != image.ndim:
48
71
  raise ValueError('Input image and footprint number of dimensions must be the same.')
49
72
 
73
+ if not image.any():
74
+ warn(f'{op_name} is applied to the fully False mask (mask.any() == False).', stacklevel=3)
75
+ output.fill(False)
76
+
77
+ return output
78
+
79
+ if image.all():
80
+ warn(f'{op_name} is applied to the fully True mask (mask.all() == True).', stacklevel=3)
81
+ output.fill(True)
82
+
83
+ return output
84
+
50
85
  n_dummy = 3 - ndim
51
86
 
52
87
  if n_dummy:
53
88
  image = image[(None,) * n_dummy]
89
+ output = output[(None,) * n_dummy]
54
90
  footprint = footprint[(None,) * n_dummy]
55
91
 
56
- out = src_op(image.astype(bool), footprint.astype(bool), num_threads)
92
+ src_op_args = (image.astype(bool, copy=False), footprint.astype(bool, copy=False), output, num_threads)
93
+ output = boxed_morphology(src_op, op_name)(*src_op_args) if boxed else src_op(*src_op_args)
57
94
 
58
95
  if n_dummy:
59
- out = out[(0,) * n_dummy]
96
+ output = output[(0,) * n_dummy]
97
+
98
+ return output.astype(bool, copy=False)
99
+
100
+ return wrapped
101
+
60
102
 
61
- return out.astype(bool)
103
+ def boxed_morphology(func, op_name) -> Callable:
104
+ # TODO: for consistency support exotic footprints which alter border pixels in Scikit-Image different from the
105
+ # current implementation, e.g. footrint [[1, 1], [1, 0]] sets border pixel to 1 for `binary_erosion`
106
+ def wrapped(
107
+ image: np.ndarray,
108
+ footprint: np.ndarray,
109
+ output: np.ndarray,
110
+ num_threads: int,
111
+ ) -> np.ndarray:
112
+ box_delta = np.asarray(footprint.shape) // 2
113
+
114
+ image_box = shape_to_box(image.shape)
115
+ tight_box = mask_to_box(image)
116
+ supp_box = add_margin(tight_box, 2 * box_delta)
117
+
118
+ # TODO: generalize to "anisotropic" images
119
+ # TODO: make separate class for `Box` and implement comparison operators?
120
+ if (supp_box[0] < image_box[0]).any() or (image_box[1] < supp_box[1]).any():
121
+ return func(image, footprint, output, num_threads)
122
+
123
+ final_crop_box = add_margin(tight_box, box_delta)
124
+
125
+ supp_image = crop_to_box(image, supp_box)
126
+ supp_output = np.empty_like(supp_image, dtype=bool)
127
+
128
+ cropped = crop_to_box(
129
+ func(supp_image, footprint, supp_output, num_threads),
130
+ add_margin(shape_to_box(box_to_shape(supp_box)), -box_delta), # crop border values of supp_box
131
+ )
132
+
133
+ output = restore_crop(cropped, final_crop_box, image.shape, False)
134
+
135
+ return output
62
136
 
63
137
  return wrapped
64
138
 
@@ -74,7 +148,12 @@ _binary_dilation = morphology_op_wrapper(
74
148
 
75
149
 
76
150
  def binary_dilation(
77
- image: np.ndarray, footprint: np.ndarray = None, num_threads: int = -1, backend: BackendLike = None
151
+ image: np.ndarray,
152
+ footprint: np.ndarray = None,
153
+ output: np.ndarray = None,
154
+ boxed: bool = False,
155
+ num_threads: int = -1,
156
+ backend: BackendLike = None,
78
157
  ) -> np.ndarray:
79
158
  """
80
159
  Fast parallelizable binary morphological dilation of an image
@@ -84,7 +163,14 @@ def binary_dilation(
84
163
  image: np.ndarray
85
164
  input image
86
165
  footprint: np.ndarray
87
- the neighborhood expressed as a 2-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
166
+ the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
167
+ output: np.ndarray
168
+ array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
169
+ array is created
170
+ boxed: bool
171
+ if True, dilation is performed on cropped image which may speed up computation depedning on how localized True
172
+ pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
173
+ exotic (has even shape or center pixel is False)
88
174
  num_threads: int
89
175
  the number of threads to use for computation. Default = the cpu count. If negative value passed
90
176
  cpu count + num_threads + 1 threads will be used
@@ -100,8 +186,7 @@ def binary_dilation(
100
186
  --------
101
187
  >>> dilated = binary_dilation(x)
102
188
  """
103
-
104
- return _binary_dilation(image, footprint, num_threads, backend)
189
+ return _binary_dilation(image, footprint, output, boxed, num_threads, backend)
105
190
 
106
191
 
107
192
  _binary_erosion = morphology_op_wrapper(
@@ -115,7 +200,12 @@ _binary_erosion = morphology_op_wrapper(
115
200
 
116
201
 
117
202
  def binary_erosion(
118
- image: np.ndarray, footprint: np.ndarray = None, num_threads: int = -1, backend: BackendLike = None
203
+ image: np.ndarray,
204
+ footprint: np.ndarray = None,
205
+ output: np.ndarray = None,
206
+ boxed: bool = False,
207
+ num_threads: int = -1,
208
+ backend: BackendLike = None,
119
209
  ) -> np.ndarray:
120
210
  """
121
211
  Fast parallelizable binary morphological erosion of an image
@@ -125,7 +215,14 @@ def binary_erosion(
125
215
  image: np.ndarray
126
216
  input image
127
217
  footprint: np.ndarray
128
- the neighborhood expressed as a 2-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
218
+ the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
219
+ output: np.ndarray
220
+ array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
221
+ array is created
222
+ boxed: bool
223
+ if True, erosion is performed on cropped image which may speed up computation depedning on how localized True
224
+ pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
225
+ exotic (has even shape or center pixel is False)
129
226
  num_threads: int
130
227
  the number of threads to use for computation. Default = the cpu count. If negative value passed
131
228
  cpu count + num_threads + 1 threads will be used
@@ -141,22 +238,26 @@ def binary_erosion(
141
238
  --------
142
239
  >>> eroded = binary_erosion(x)
143
240
  """
144
-
145
- return _binary_erosion(image, footprint, num_threads, backend)
241
+ return _binary_erosion(image, footprint, output, boxed, num_threads, backend)
146
242
 
147
243
 
148
244
  _binary_closing = morphology_op_wrapper(
149
245
  'binary_closing',
150
246
  {
151
247
  Scipy(): composition_args(scipy_binary_erosion, scipy_binary_dilation),
152
- Cython(fast=False): composition_args(cython_binary_erosion, cython_binary_dilation),
153
- Cython(fast=True): composition_args(cython_fast_binary_erosion, cython_fast_binary_dilation),
248
+ Cython(fast=False): morphology_composition_args(cython_binary_erosion, cython_binary_dilation),
249
+ Cython(fast=True): morphology_composition_args(cython_fast_binary_erosion, cython_fast_binary_dilation),
154
250
  },
155
251
  )
156
252
 
157
253
 
158
254
  def binary_closing(
159
- image: np.ndarray, footprint: np.ndarray = None, num_threads: int = -1, backend: BackendLike = None
255
+ image: np.ndarray,
256
+ footprint: np.ndarray = None,
257
+ output: np.ndarray = None,
258
+ boxed: bool = False,
259
+ num_threads: int = -1,
260
+ backend: BackendLike = None,
160
261
  ) -> np.ndarray:
161
262
  """
162
263
  Fast parallelizable binary morphological closing of an image
@@ -166,7 +267,14 @@ def binary_closing(
166
267
  image: np.ndarray
167
268
  input image
168
269
  footprint: np.ndarray
169
- the neighborhood expressed as a 2-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
270
+ the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
271
+ output: np.ndarray
272
+ array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
273
+ array is created
274
+ boxed: bool
275
+ if True, closing is performed on cropped image which may speed up computation depedning on how localized True
276
+ pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
277
+ exotic (has even shape or center pixel is False)
170
278
  num_threads: int
171
279
  the number of threads to use for computation. Default = the cpu count. If negative value passed
172
280
  cpu count + num_threads + 1 threads will be used
@@ -175,29 +283,34 @@ def binary_closing(
175
283
 
176
284
  Returns
177
285
  -------
178
- closing: np.ndarray
286
+ closed: np.ndarray
179
287
  the result of morphological closing
180
288
 
181
289
  Examples
182
290
  --------
183
- >>> closing = binary_closing(x)
291
+ >>> closed = binary_closing(x)
184
292
  """
185
293
 
186
- return _binary_closing(image, footprint, num_threads, backend)
294
+ return _binary_closing(image, footprint, output, boxed, num_threads, backend)
187
295
 
188
296
 
189
297
  _binary_opening = morphology_op_wrapper(
190
298
  'binary_opening',
191
299
  {
192
300
  Scipy(): composition_args(scipy_binary_dilation, scipy_binary_erosion),
193
- Cython(fast=False): composition_args(cython_binary_dilation, cython_binary_erosion),
194
- Cython(fast=True): composition_args(cython_fast_binary_dilation, cython_fast_binary_erosion),
301
+ Cython(fast=False): morphology_composition_args(cython_binary_dilation, cython_binary_erosion),
302
+ Cython(fast=True): morphology_composition_args(cython_fast_binary_dilation, cython_fast_binary_erosion),
195
303
  },
196
304
  )
197
305
 
198
306
 
199
307
  def binary_opening(
200
- image: np.ndarray, footprint: np.ndarray = None, num_threads: int = -1, backend: BackendLike = None
308
+ image: np.ndarray,
309
+ footprint: np.ndarray = None,
310
+ output: np.ndarray = None,
311
+ boxed: bool = False,
312
+ num_threads: int = -1,
313
+ backend: BackendLike = None,
201
314
  ) -> np.ndarray:
202
315
  """
203
316
  Fast parallelizable binary morphological opening of an image
@@ -207,7 +320,14 @@ def binary_opening(
207
320
  image: np.ndarray
208
321
  input image
209
322
  footprint: np.ndarray
210
- the neighborhood expressed as a 2-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
323
+ the neighborhood expressed as a n-D array of 1's and 0's. If None, use a cross-shaped footprint (connectivity=1)
324
+ output: np.ndarray
325
+ array of the same shape as input, into which the output is placed (must be C-contiguous). By default, a new
326
+ array is created
327
+ boxed: bool
328
+ if True, opening is performed on cropped image which may speed up computation depedning on how localized True
329
+ pixels are. This may induce differences with Scikit-Image implementation at border pixels if footprint is
330
+ exotic (has even shape or center pixel is False)
211
331
  num_threads: int
212
332
  the number of threads to use for computation. Default = the cpu count. If negative value passed
213
333
  cpu count + num_threads + 1 threads will be used
@@ -216,12 +336,12 @@ def binary_opening(
216
336
 
217
337
  Returns
218
338
  -------
219
- opening: np.ndarray
339
+ opened: np.ndarray
220
340
  the result of morphological opening
221
341
 
222
342
  Examples
223
343
  --------
224
- >>> opening = binary_opening(x)
344
+ >>> opened = binary_opening(x)
225
345
  """
226
346
 
227
- return _binary_opening(image, footprint, num_threads, backend)
347
+ return _binary_opening(image, footprint, output, boxed, num_threads, backend)