imops 0.8.1__cp310-cp310-win_amd64.whl → 0.8.3__cp310-cp310-win_amd64.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.
- _build_utils.py +87 -0
- imops/__init__.py +1 -0
- imops/__version__.py +1 -1
- imops/backend.py +14 -10
- imops/box.py +20 -29
- imops/crop.py +18 -2
- imops/interp1d.py +16 -13
- imops/measure.py +12 -9
- imops/morphology.py +155 -35
- imops/numeric.py +376 -0
- imops/pad.py +41 -5
- imops/radon.py +9 -7
- imops/src/_backprojection.c +83 -83
- imops/src/_backprojection.cp310-win_amd64.pyd +0 -0
- imops/src/_fast_backprojection.c +96 -96
- imops/src/_fast_backprojection.cp310-win_amd64.pyd +0 -0
- imops/src/_fast_measure.c +96 -96
- imops/src/_fast_measure.cp310-win_amd64.pyd +0 -0
- imops/src/_fast_morphology.c +2847 -1587
- imops/src/_fast_morphology.cp310-win_amd64.pyd +0 -0
- imops/src/_fast_morphology.pyx +315 -131
- imops/src/_fast_numeric.c +20545 -4996
- imops/src/_fast_numeric.cp310-win_amd64.pyd +0 -0
- imops/src/_fast_numeric.pyx +208 -30
- imops/src/_fast_radon.c +96 -96
- imops/src/_fast_radon.cp310-win_amd64.pyd +0 -0
- imops/src/_fast_zoom.c +5945 -3342
- imops/src/_fast_zoom.cp310-win_amd64.pyd +0 -0
- imops/src/_fast_zoom.pyx +1 -0
- imops/src/_measure.c +83 -83
- imops/src/_measure.cp310-win_amd64.pyd +0 -0
- imops/src/_morphology.c +2768 -1553
- imops/src/_morphology.cp310-win_amd64.pyd +0 -0
- imops/src/_morphology.pyx +315 -131
- imops/src/_numeric.c +20532 -4983
- imops/src/_numeric.cp310-win_amd64.pyd +0 -0
- imops/src/_numeric.pyx +208 -30
- imops/src/_radon.c +83 -83
- imops/src/_radon.cp310-win_amd64.pyd +0 -0
- imops/src/_zoom.c +5932 -3329
- imops/src/_zoom.cp310-win_amd64.pyd +0 -0
- imops/src/_zoom.pyx +1 -0
- imops/utils.py +113 -13
- imops/zoom.py +9 -9
- {imops-0.8.1.dist-info → imops-0.8.3.dist-info}/METADATA +40 -19
- imops-0.8.3.dist-info/RECORD +60 -0
- {imops-0.8.1.dist-info → imops-0.8.3.dist-info}/WHEEL +1 -1
- imops-0.8.3.dist-info/top_level.txt +2 -0
- _pyproject_build.py +0 -49
- imops/_numeric.py +0 -124
- imops-0.8.1.dist-info/RECORD +0 -60
- imops-0.8.1.dist-info/top_level.txt +0 -2
- {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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
+
output = output[(0,) * n_dummy]
|
|
97
|
+
|
|
98
|
+
return output.astype(bool, copy=False)
|
|
99
|
+
|
|
100
|
+
return wrapped
|
|
101
|
+
|
|
60
102
|
|
|
61
|
-
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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):
|
|
153
|
-
Cython(fast=True):
|
|
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,
|
|
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
|
|
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
|
-
|
|
286
|
+
closed: np.ndarray
|
|
179
287
|
the result of morphological closing
|
|
180
288
|
|
|
181
289
|
Examples
|
|
182
290
|
--------
|
|
183
|
-
>>>
|
|
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):
|
|
194
|
-
Cython(fast=True):
|
|
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,
|
|
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
|
|
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
|
-
|
|
339
|
+
opened: np.ndarray
|
|
220
340
|
the result of morphological opening
|
|
221
341
|
|
|
222
342
|
Examples
|
|
223
343
|
--------
|
|
224
|
-
>>>
|
|
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)
|