imops 0.8.1__cp311-cp311-win_amd64.whl → 0.8.3__cp311-cp311-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.cp311-win_amd64.pyd +0 -0
- imops/src/_fast_backprojection.c +96 -96
- imops/src/_fast_backprojection.cp311-win_amd64.pyd +0 -0
- imops/src/_fast_measure.c +96 -96
- imops/src/_fast_measure.cp311-win_amd64.pyd +0 -0
- imops/src/_fast_morphology.c +2847 -1587
- imops/src/_fast_morphology.cp311-win_amd64.pyd +0 -0
- imops/src/_fast_morphology.pyx +315 -131
- imops/src/_fast_numeric.c +20545 -4996
- imops/src/_fast_numeric.cp311-win_amd64.pyd +0 -0
- imops/src/_fast_numeric.pyx +208 -30
- imops/src/_fast_radon.c +96 -96
- imops/src/_fast_radon.cp311-win_amd64.pyd +0 -0
- imops/src/_fast_zoom.c +5945 -3342
- imops/src/_fast_zoom.cp311-win_amd64.pyd +0 -0
- imops/src/_fast_zoom.pyx +1 -0
- imops/src/_measure.c +83 -83
- imops/src/_measure.cp311-win_amd64.pyd +0 -0
- imops/src/_morphology.c +2768 -1553
- imops/src/_morphology.cp311-win_amd64.pyd +0 -0
- imops/src/_morphology.pyx +315 -131
- imops/src/_numeric.c +20532 -4983
- imops/src/_numeric.cp311-win_amd64.pyd +0 -0
- imops/src/_numeric.pyx +208 -30
- imops/src/_radon.c +83 -83
- imops/src/_radon.cp311-win_amd64.pyd +0 -0
- imops/src/_zoom.c +5932 -3329
- imops/src/_zoom.cp311-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
|
Binary file
|
imops/src/_zoom.pyx
CHANGED
imops/utils.py
CHANGED
|
@@ -1,39 +1,92 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from contextlib import contextmanager
|
|
2
3
|
from itertools import permutations
|
|
3
|
-
from typing import Callable, Optional, Sequence, Union
|
|
4
|
+
from typing import Callable, Optional, Sequence, Tuple, Union
|
|
4
5
|
from warnings import warn
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
|
|
8
|
-
from .backend import
|
|
9
|
+
from .backend import BACKEND_NAME2ENV_NUM_THREADS_VAR_NAME, SINGLE_THREADED_BACKENDS, Backend
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
AxesLike = Union[int, Sequence[int]]
|
|
12
13
|
AxesParams = Union[float, Sequence[float]]
|
|
13
14
|
|
|
14
15
|
ZOOM_SRC_DIM = 4
|
|
16
|
+
# TODO: define imops-specific environment variable like `OMP_NUM_THREADS`?
|
|
17
|
+
IMOPS_NUM_THREADS = None
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
def
|
|
20
|
+
def set_num_threads(num_threads: int) -> int:
|
|
21
|
+
assert isinstance(num_threads, int) or num_threads is None, 'Number of threads must be int value or None.'
|
|
22
|
+
global IMOPS_NUM_THREADS
|
|
23
|
+
current = IMOPS_NUM_THREADS
|
|
24
|
+
IMOPS_NUM_THREADS = num_threads
|
|
25
|
+
return current
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@contextmanager
|
|
29
|
+
def imops_num_threads(num_threads: int):
|
|
30
|
+
previous = set_num_threads(num_threads)
|
|
31
|
+
try:
|
|
32
|
+
yield
|
|
33
|
+
finally:
|
|
34
|
+
set_num_threads(previous)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def normalize_num_threads(num_threads: int, backend: Backend, warn_stacklevel: int = 1) -> int:
|
|
38
|
+
"""Calculate the effective number of threads"""
|
|
39
|
+
|
|
40
|
+
global IMOPS_NUM_THREADS
|
|
18
41
|
if backend.name in SINGLE_THREADED_BACKENDS:
|
|
19
42
|
if num_threads != -1:
|
|
20
|
-
warn(
|
|
43
|
+
warn(
|
|
44
|
+
f'"{backend.name}" backend is single-threaded. Setting `num_threads` has no effect.',
|
|
45
|
+
stacklevel=warn_stacklevel,
|
|
46
|
+
)
|
|
21
47
|
return 1
|
|
48
|
+
|
|
49
|
+
env_num_threads_var_name = BACKEND_NAME2ENV_NUM_THREADS_VAR_NAME[backend.name]
|
|
50
|
+
# here we also handle the case `env_num_threads_var_name`=" " gracefully
|
|
51
|
+
env_num_threads = os.environ.get(env_num_threads_var_name, '').strip()
|
|
52
|
+
env_num_threads = int(env_num_threads) if env_num_threads else None
|
|
53
|
+
# TODO: maybe let user set the absolute maximum number of threads?
|
|
54
|
+
num_available_cpus = len(os.sched_getaffinity(0))
|
|
55
|
+
|
|
56
|
+
max_num_threads = min(filter(bool, [IMOPS_NUM_THREADS, env_num_threads, num_available_cpus]))
|
|
57
|
+
|
|
22
58
|
if num_threads >= 0:
|
|
23
59
|
# FIXME
|
|
24
60
|
if backend.name == 'Numba':
|
|
25
61
|
warn(
|
|
26
62
|
'Setting `num_threads` has no effect with "Numba" backend. '
|
|
27
|
-
'Use `NUMBA_NUM_THREADS` environment variable.'
|
|
63
|
+
'Use `NUMBA_NUM_THREADS` environment variable.',
|
|
64
|
+
stacklevel=warn_stacklevel,
|
|
28
65
|
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
66
|
+
return num_threads
|
|
67
|
+
|
|
68
|
+
if num_threads > max_num_threads:
|
|
69
|
+
if max_num_threads == IMOPS_NUM_THREADS:
|
|
70
|
+
warn(
|
|
71
|
+
f'Required number of threads ({num_threads}) is greater than `IMOPS_NUM_THREADS` '
|
|
72
|
+
f'({IMOPS_NUM_THREADS}). Using {IMOPS_NUM_THREADS} threads.',
|
|
73
|
+
stacklevel=warn_stacklevel,
|
|
74
|
+
)
|
|
75
|
+
elif max_num_threads == env_num_threads:
|
|
76
|
+
warn(
|
|
77
|
+
f'Required number of threads ({num_threads}) is greater than `{env_num_threads_var_name}` '
|
|
78
|
+
f'({env_num_threads}). Using {env_num_threads} threads.',
|
|
79
|
+
stacklevel=warn_stacklevel,
|
|
80
|
+
)
|
|
81
|
+
else:
|
|
82
|
+
warn(
|
|
83
|
+
f'Required number of threads ({num_threads}) is greater than number of available CPU-s '
|
|
84
|
+
f'({num_available_cpus}). Using {num_available_cpus} threads.',
|
|
85
|
+
stacklevel=warn_stacklevel,
|
|
86
|
+
)
|
|
87
|
+
return min(num_threads, max_num_threads)
|
|
88
|
+
|
|
89
|
+
return max_num_threads + num_threads + 1
|
|
37
90
|
|
|
38
91
|
|
|
39
92
|
def get_c_contiguous_permutaion(array: np.ndarray) -> Optional[np.ndarray]:
|
|
@@ -104,3 +157,50 @@ def composition_args(f: Callable, g: Callable) -> Callable:
|
|
|
104
157
|
return f(g(*args), *args[1:])
|
|
105
158
|
|
|
106
159
|
return inner
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def morphology_composition_args(f, g) -> Callable:
|
|
163
|
+
def wrapper(
|
|
164
|
+
image: np.ndarray,
|
|
165
|
+
footprint: np.ndarray,
|
|
166
|
+
output: np.ndarray,
|
|
167
|
+
num_threads: int,
|
|
168
|
+
):
|
|
169
|
+
temp = np.empty_like(image, dtype=bool)
|
|
170
|
+
temp = g(image, footprint, temp, num_threads)
|
|
171
|
+
|
|
172
|
+
return f(temp, footprint, output, num_threads)
|
|
173
|
+
|
|
174
|
+
return wrapper
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def build_slices(start: Sequence[int], stop: Sequence[int] = None, step: Sequence[int] = None) -> Tuple[slice, ...]:
|
|
178
|
+
"""
|
|
179
|
+
Returns a tuple of slices built from `start` and `stop` with `step`.
|
|
180
|
+
|
|
181
|
+
Examples
|
|
182
|
+
--------
|
|
183
|
+
>>> build_slices([1, 2, 3], [4, 5, 6])
|
|
184
|
+
(slice(1, 4), slice(2, 5), slice(3, 6))
|
|
185
|
+
>>> build_slices([10, 11])
|
|
186
|
+
(slice(10), slice(11))
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
check_len(*filter(lambda x: x is not None, [start, stop, step]))
|
|
190
|
+
|
|
191
|
+
if stop is None and step is None:
|
|
192
|
+
return tuple(map(slice, start))
|
|
193
|
+
|
|
194
|
+
args = [
|
|
195
|
+
start,
|
|
196
|
+
stop if stop is not None else [None for _ in start],
|
|
197
|
+
step if step is not None else [None for _ in start],
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
return tuple(map(slice, *args))
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def check_len(*args) -> None:
|
|
204
|
+
lengths = list(map(len, args))
|
|
205
|
+
if any(length != lengths[0] for length in lengths):
|
|
206
|
+
raise ValueError(f'Arguments of equal length are required: {", ".join(map(str, lengths))}')
|
imops/zoom.py
CHANGED
|
@@ -81,7 +81,7 @@ def zoom(
|
|
|
81
81
|
"""
|
|
82
82
|
Rescale `x` according to `scale_factor` along the `axis`.
|
|
83
83
|
|
|
84
|
-
Uses a fast parallelizable implementation for fp32 / fp64 (and int16-32-64 if order == 0) inputs,
|
|
84
|
+
Uses a fast parallelizable implementation for fp32 / fp64 (and bool-int16-32-64 if order == 0) inputs,
|
|
85
85
|
ndim <= 4 and order = 0 or 1.
|
|
86
86
|
|
|
87
87
|
Parameters
|
|
@@ -120,6 +120,7 @@ def zoom(
|
|
|
120
120
|
if callable(fill_value):
|
|
121
121
|
fill_value = fill_value(x)
|
|
122
122
|
|
|
123
|
+
# TODO: does `fill_value/cval` change anythng?
|
|
123
124
|
return _zoom(x, scale_factor, order=order, cval=fill_value, num_threads=num_threads, backend=backend)
|
|
124
125
|
|
|
125
126
|
|
|
@@ -135,7 +136,7 @@ def zoom_to_shape(
|
|
|
135
136
|
"""
|
|
136
137
|
Rescale `x` to match `shape` along the `axis`.
|
|
137
138
|
|
|
138
|
-
Uses a fast parallelizable implementation for fp32 / fp64 (and int16-32-64 if order == 0) inputs,
|
|
139
|
+
Uses a fast parallelizable implementation for fp32 / fp64 (and bool-int16-32-64 if order == 0) inputs,
|
|
139
140
|
ndim <= 4 and order = 0 or 1.
|
|
140
141
|
|
|
141
142
|
Parameters
|
|
@@ -197,7 +198,7 @@ def _zoom(
|
|
|
197
198
|
backend: BackendLike = None,
|
|
198
199
|
) -> np.ndarray:
|
|
199
200
|
"""
|
|
200
|
-
Faster parallelizable version of `scipy.ndimage.zoom` for fp32 / fp64 (and int16-32-64 if order == 0) inputs.
|
|
201
|
+
Faster parallelizable version of `scipy.ndimage.zoom` for fp32 / fp64 (and bool-int16-32-64 if order == 0) inputs.
|
|
201
202
|
|
|
202
203
|
Works faster only for ndim <= 4. Shares interface with `scipy.ndimage.zoom`
|
|
203
204
|
except for
|
|
@@ -207,7 +208,7 @@ def _zoom(
|
|
|
207
208
|
|
|
208
209
|
See `https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.zoom.html`
|
|
209
210
|
"""
|
|
210
|
-
backend = resolve_backend(backend)
|
|
211
|
+
backend = resolve_backend(backend, warn_stacklevel=4)
|
|
211
212
|
if backend.name not in ('Scipy', 'Numba', 'Cython'):
|
|
212
213
|
raise ValueError(f'Unsupported backend "{backend.name}".')
|
|
213
214
|
|
|
@@ -215,7 +216,7 @@ def _zoom(
|
|
|
215
216
|
dtype = input.dtype
|
|
216
217
|
cval = np.dtype(dtype).type(cval)
|
|
217
218
|
zoom = fill_by_indices(np.ones(input.ndim, 'float64'), zoom, range(input.ndim))
|
|
218
|
-
num_threads = normalize_num_threads(num_threads, backend)
|
|
219
|
+
num_threads = normalize_num_threads(num_threads, backend, warn_stacklevel=4)
|
|
219
220
|
|
|
220
221
|
if backend.name == 'Scipy':
|
|
221
222
|
return scipy_zoom(
|
|
@@ -227,7 +228,7 @@ def _zoom(
|
|
|
227
228
|
or (
|
|
228
229
|
dtype not in (np.float32, np.float64)
|
|
229
230
|
if order == 1
|
|
230
|
-
else dtype not in (np.float32, np.float64, np.int16, np.int32, np.int64)
|
|
231
|
+
else dtype not in (bool, np.float32, np.float64, np.int16, np.int32, np.int64)
|
|
231
232
|
)
|
|
232
233
|
or ndim > 4
|
|
233
234
|
or output is not None
|
|
@@ -235,10 +236,9 @@ def _zoom(
|
|
|
235
236
|
or grid_mode
|
|
236
237
|
):
|
|
237
238
|
warn(
|
|
238
|
-
'Fast zoom is only supported for ndim<=4, dtype=fp32 or fp64 (and int16-32-64 if order == 0),
|
|
239
|
-
"order=0 or 1, mode='constant', grid_mode=False. Falling back to scipy's implementation.",
|
|
239
|
+
'Fast zoom is only supported for ndim<=4, dtype=fp32 or fp64 (and bool-int16-32-64 if order == 0), '
|
|
240
|
+
"output=None, order=0 or 1, mode='constant', grid_mode=False. Falling back to scipy's implementation.",
|
|
240
241
|
)
|
|
241
|
-
|
|
242
242
|
return scipy_zoom(
|
|
243
243
|
input, zoom, output=output, order=order, mode=mode, cval=cval, prefilter=prefilter, grid_mode=grid_mode
|
|
244
244
|
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: imops
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
4
4
|
Summary: Efficient parallelizable algorithms for multidimensional arrays to speed up your data pipelines
|
|
5
5
|
Home-page: https://github.com/neuro-ml/imops
|
|
6
|
-
Download-URL: https://github.com/neuro-ml/imops/archive/v0.8.
|
|
6
|
+
Download-URL: https://github.com/neuro-ml/imops/archive/v0.8.3.tar.gz
|
|
7
7
|
Author: maxme1, vovaf709, talgat
|
|
8
8
|
Author-email: maxme1 <maxs987@gmail.com>, vovaf709 <vovaf709@yandex.ru>, talgat <saparov2130@gmail.com>
|
|
9
9
|
License: MIT License
|
|
@@ -44,12 +44,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
44
44
|
Requires-Python: >=3.6
|
|
45
45
|
Description-Content-Type: text/markdown
|
|
46
46
|
License-File: LICENSE
|
|
47
|
-
Requires-Dist: scipy
|
|
47
|
+
Requires-Dist: scipy <2.0.0,>=1.0
|
|
48
|
+
Requires-Dist: scikit-image
|
|
48
49
|
Requires-Dist: connected-components-3d
|
|
49
50
|
Requires-Dist: fastremap
|
|
50
51
|
Requires-Dist: dataclasses ; python_version < "3.7"
|
|
51
|
-
Requires-Dist: numpy
|
|
52
|
-
Requires-Dist: numpy
|
|
52
|
+
Requires-Dist: numpy <2.0.0,>=1.7 ; python_version < "3.8"
|
|
53
|
+
Requires-Dist: numpy <2.0.0,>=1.22 ; python_version >= "3.8"
|
|
53
54
|
Provides-Extra: all
|
|
54
55
|
Requires-Dist: numba ; extra == 'all'
|
|
55
56
|
Provides-Extra: numba
|
|
@@ -62,7 +63,9 @@ Requires-Dist: numba ; extra == 'numba'
|
|
|
62
63
|
|
|
63
64
|
# Imops
|
|
64
65
|
|
|
65
|
-
Efficient parallelizable algorithms for multidimensional arrays to speed up your data pipelines.
|
|
66
|
+
Efficient parallelizable algorithms for multidimensional arrays to speed up your data pipelines.
|
|
67
|
+
- [Documentation](https://neuro-ml.github.io/imops/)
|
|
68
|
+
- [Benchmarks](https://neuro-ml.github.io/imops/benchmarks/)
|
|
66
69
|
|
|
67
70
|
# Install
|
|
68
71
|
|
|
@@ -71,15 +74,33 @@ pip install imops # default install with Cython backend
|
|
|
71
74
|
pip install imops[numba] # additionally install Numba backend
|
|
72
75
|
```
|
|
73
76
|
|
|
77
|
+
# How fast is it?
|
|
78
|
+
|
|
79
|
+
Time comparisons (ms) for Intel(R) Xeon(R) Silver 4114 CPU @ 2.20GHz using 8 threads. All inputs are C-contiguous NumPy arrays. For morphology functions `bool` dtype is used and `float64` for all others.
|
|
80
|
+
| function / backend | Scipy() | Cython(fast=False) | Cython(fast=True) | Numba() |
|
|
81
|
+
|:----------------------:|:-----------:|:----------------------:|:---------------------:|:-----------:|
|
|
82
|
+
| `zoom(..., order=0)` | 2072 | 1114 | **867** | 3590 |
|
|
83
|
+
| `zoom(..., order=1)` | 6527 | 596 | **575** | 3757 |
|
|
84
|
+
| `interp1d` | 780 | 149 | **146** | 420 |
|
|
85
|
+
| `radon` | 59711 | 5982 | **4837** | - |
|
|
86
|
+
| `inverse_radon` | 52928 | 8254 | **6535** | - |
|
|
87
|
+
| `binary_dilation` | 2207 | 310 | **298** | - |
|
|
88
|
+
| `binary_erosion` | 2296 | 326 | **304** | - |
|
|
89
|
+
| `binary_closing` | 4158 | 544 | **469** | - |
|
|
90
|
+
| `binary_opening` | 4410 | 567 | **522** | - |
|
|
91
|
+
| `center_of_mass` | 2237 | **64** | **64** | - |
|
|
92
|
+
|
|
93
|
+
We use [`airspeed velocity`](https://asv.readthedocs.io/en/stable/) to benchmark our code. For detailed results visit [benchmark page](https://neuro-ml.github.io/imops/benchmarks/).
|
|
94
|
+
|
|
74
95
|
# Features
|
|
75
96
|
|
|
76
|
-
|
|
97
|
+
### Fast Radon transform
|
|
77
98
|
|
|
78
99
|
```python
|
|
79
100
|
from imops import radon, inverse_radon
|
|
80
101
|
```
|
|
81
102
|
|
|
82
|
-
|
|
103
|
+
### Fast 0/1-order zoom
|
|
83
104
|
|
|
84
105
|
```python
|
|
85
106
|
from imops import zoom, zoom_to_shape
|
|
@@ -90,20 +111,20 @@ y = zoom(x, 2, axis=[0, 1])
|
|
|
90
111
|
# without the need to compute the scale factor
|
|
91
112
|
z = zoom_to_shape(x, (4, 120, 67))
|
|
92
113
|
```
|
|
93
|
-
Works faster only for `ndim<=
|
|
94
|
-
|
|
114
|
+
Works faster only for `ndim<=4, dtype=float32 or float64 (and bool-int16-32-64 if order == 0), output=None, order=0 or 1, mode='constant', grid_mode=False`
|
|
115
|
+
### Fast 1d linear interpolation
|
|
95
116
|
|
|
96
117
|
```python
|
|
97
118
|
from imops import interp1d # same as `scipy.interpolate.interp1d`
|
|
98
119
|
```
|
|
99
|
-
Works faster only for `ndim<=3, dtype=float32 or float64, order=1
|
|
100
|
-
|
|
120
|
+
Works faster only for `ndim<=3, dtype=float32 or float64, order=1`
|
|
121
|
+
### Fast binary morphology
|
|
101
122
|
|
|
102
123
|
```python
|
|
103
124
|
from imops import binary_dilation, binary_erosion, binary_opening, binary_closing
|
|
104
125
|
```
|
|
105
126
|
These functions mimic `scikit-image` counterparts
|
|
106
|
-
|
|
127
|
+
### Padding
|
|
107
128
|
|
|
108
129
|
```python
|
|
109
130
|
from imops import pad, pad_to_shape
|
|
@@ -116,7 +137,7 @@ y = pad(x, 10, axis=[0, 1])
|
|
|
116
137
|
z = pad_to_shape(x, (4, 120, 67), ratio=0.25)
|
|
117
138
|
```
|
|
118
139
|
|
|
119
|
-
|
|
140
|
+
### Cropping
|
|
120
141
|
|
|
121
142
|
```python
|
|
122
143
|
from imops import crop_to_shape
|
|
@@ -128,7 +149,7 @@ from imops import crop_to_shape
|
|
|
128
149
|
z = crop_to_shape(x, (4, 120, 67), ratio=0.25)
|
|
129
150
|
```
|
|
130
151
|
|
|
131
|
-
|
|
152
|
+
### Labeling
|
|
132
153
|
|
|
133
154
|
```python
|
|
134
155
|
from imops import label
|
|
@@ -138,7 +159,7 @@ labeled, num_components = label(x, background=1, return_num=True)
|
|
|
138
159
|
```
|
|
139
160
|
|
|
140
161
|
# Backends
|
|
141
|
-
For
|
|
162
|
+
For all heavy image routines except `label` you can specify which backend to use. Backend can be specified by a string or by an instance of `Backend` class. The latter allows you to customize some backend options:
|
|
142
163
|
```python
|
|
143
164
|
from imops import Cython, Numba, Scipy, zoom
|
|
144
165
|
|
|
@@ -159,10 +180,9 @@ with imops_backend('Cython'): # sets Cython backend via context manager
|
|
|
159
180
|
```
|
|
160
181
|
Note that for `Numba` backend setting `num_threads` argument has no effect for now and you should use `NUMBA_NUM_THREADS` environment variable.
|
|
161
182
|
Available backends:
|
|
162
|
-
|
|
|
163
|
-
|
|
183
|
+
| function / backend | Scipy | Cython | Numba |
|
|
184
|
+
|:-------------------:|:---------:|:---------:|:---------:|
|
|
164
185
|
| `zoom` | ✓ | ✓ | ✓ |
|
|
165
|
-
| `zoom_to_shape` | ✓ | ✓ | ✓ |
|
|
166
186
|
| `interp1d` | ✓ | ✓ | ✓ |
|
|
167
187
|
| `radon` | ✗ | ✓ | ✗ |
|
|
168
188
|
| `inverse_radon` | ✗ | ✓ | ✗ |
|
|
@@ -170,6 +190,7 @@ Available backends:
|
|
|
170
190
|
| `binary_erosion` | ✓ | ✓ | ✗ |
|
|
171
191
|
| `binary_closing` | ✓ | ✓ | ✗ |
|
|
172
192
|
| `binary_opening` | ✓ | ✓ | ✗ |
|
|
193
|
+
| `center_of_mass` | ✓ | ✓ | ✗ |
|
|
173
194
|
|
|
174
195
|
# Acknowledgements
|
|
175
196
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
_build_utils.py,sha256=bxDoFEVfZgjl-yxMv5a-A7jncCD362m4RTpn5SyRgB4,3262
|
|
2
|
+
imops/__init__.py,sha256=QAqK6FnQnOjdDwSXzhnJNB2ur3cGUNbP3ZdLc_VoIEY,514
|
|
3
|
+
imops/__version__.py,sha256=lPOlZg8rjdgN8DyBl7I1rkzxiXOhafW_7RzlMGnvNo8,23
|
|
4
|
+
imops/_configs.py,sha256=6hBY2O4YSaQC_avvqI-dmhZkPMl4p39KQIPtqx5MAh0,734
|
|
5
|
+
imops/backend.py,sha256=8x6Zq9hetOt9_RDcytNNBJZBLNXgQd7ob2dh4WQuuqM,2598
|
|
6
|
+
imops/box.py,sha256=NbYsPKQ8fCGxSm5up0TyqERJ-5iWmnzHLo4Smv9eesk,1865
|
|
7
|
+
imops/crop.py,sha256=hCgwxDgNdIo9m0gleMB3oOUNA7tQurhYNhE5S39tMlE,4055
|
|
8
|
+
imops/interp1d.py,sha256=CSPJ77isqGOJTN-_C--iyz-3qJhpL4dE9i5VdJgSFDE,8400
|
|
9
|
+
imops/measure.py,sha256=YJ5hBDH6iW1hgqm6fi6FIURBKmeZjVfr7LSINOHgFrM,8657
|
|
10
|
+
imops/morphology.py,sha256=KG3RCCicsX7WHsBj4mH6iaJ0dy6tW9cGJJvD4fDisBQ,12723
|
|
11
|
+
imops/numeric.py,sha256=yiSjPTUD2w177LOBD_39_BHh9n8uKbSsHJDchNVsKRY,13650
|
|
12
|
+
imops/pad.py,sha256=OXy_r4X7hOPBU4NSH3B-8ZpxfcY1t6FFMgMSopULpv8,9314
|
|
13
|
+
imops/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
imops/radon.py,sha256=TWsgZvWRapBbU7MVr1fRdpVXCvi6-kt8fxroyQc44ek,9211
|
|
15
|
+
imops/testing.py,sha256=Qiy_Qn7bd7MIXoaZr4T_6r0jQWVRJFwR2z12c_Zwt30,1708
|
|
16
|
+
imops/utils.py,sha256=nfrMUU0OhPgK5UapD9GNC1StPxX3cKbob54kiuf4_qs,6890
|
|
17
|
+
imops/zoom.py,sha256=wl8wt7yoX5V4Mn7sMefV1Fy9KEetqMEdRHLdhT90o6o,9955
|
|
18
|
+
imops/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
imops/src/_backprojection.c,sha256=pq-WMMTwAzP0sRV-nlq_kw3u-1153p7XOHNcJCeE7Ak,1025929
|
|
20
|
+
imops/src/_backprojection.cp311-win_amd64.pyd,sha256=xBP0EMYkuvREVU5HDakoIPGqCZly7NxEtectp0edEpM,164352
|
|
21
|
+
imops/src/_backprojection.pyx,sha256=xngEJtJ6czjoBRBKL6tu5S7nt6ZgcsE82xDGdCIfagE,2724
|
|
22
|
+
imops/src/_fast_backprojection.c,sha256=hj9t8h-ty5sea8Y17q1FOSZYcpqpBmJpDYHrqrWaHpM,1037428
|
|
23
|
+
imops/src/_fast_backprojection.cp311-win_amd64.pyd,sha256=935jT47j4nexxJbgecmkEYmeYrOaGWJVAlL2gbx7bHg,164864
|
|
24
|
+
imops/src/_fast_backprojection.pyx,sha256=xngEJtJ6czjoBRBKL6tu5S7nt6ZgcsE82xDGdCIfagE,2724
|
|
25
|
+
imops/src/_fast_measure.c,sha256=q1fqR5jiZqTNzWfgC2SLLwxGbVKXUPVqbkevgDgKKEQ,1337706
|
|
26
|
+
imops/src/_fast_measure.cp311-win_amd64.pyd,sha256=FrFwuSR0bGoMlPtQSGgMxTPOIXLXalraUXh8L2Aph_Q,244736
|
|
27
|
+
imops/src/_fast_measure.pyx,sha256=JzZ26PptkVT08OemSFaJakc4jf7xYocPqV8jfrYi_wA,2737
|
|
28
|
+
imops/src/_fast_morphology.c,sha256=hbqJO4nQ37rKilTrhJUsIqBnFCtNYnX1MKvbyBKiivU,972949
|
|
29
|
+
imops/src/_fast_morphology.cp311-win_amd64.pyd,sha256=Dk0jlX1tBb-HaTkKsibKOT9PWBpGFAquk-INNHSgbvs,135680
|
|
30
|
+
imops/src/_fast_morphology.pyx,sha256=QwFrPQ6M-bJDVQNCCtKK7FR3KOr5cZSx_CBvxh9THXM,9538
|
|
31
|
+
imops/src/_fast_numeric.c,sha256=-tsn5KpF0n3djJtyL0fgGWCl3IPzaGtK25VqHfLQOJk,2108485
|
|
32
|
+
imops/src/_fast_numeric.cp311-win_amd64.pyd,sha256=Y-IIV833gaiTA6YafU0-U6VsWph3wpOOd-O9B6GBMZo,369152
|
|
33
|
+
imops/src/_fast_numeric.pyx,sha256=c8q3KlL8mDe0wsz72ooeqhffk1iqvA0Org7NWW5RbYo,8221
|
|
34
|
+
imops/src/_fast_radon.c,sha256=abfo2kaOik5cGzHUaSgYalvFs8MQ8OxKRXGQIBhuPEU,1191419
|
|
35
|
+
imops/src/_fast_radon.cp311-win_amd64.pyd,sha256=jID7DdZzg2jEEJ7-ROKupTqnF_hd9HtVDvRszpnMrJM,216064
|
|
36
|
+
imops/src/_fast_radon.pyx,sha256=pu8EZplCjIDz9HqBcAit8nj2lDgXpmdhU61_4CfdGWI,2987
|
|
37
|
+
imops/src/_fast_zoom.c,sha256=npw8isY-LZYGf5zE-i_LhHcSoRlucLraNGHfd4eefwI,2146413
|
|
38
|
+
imops/src/_fast_zoom.cp311-win_amd64.pyd,sha256=1BXek1FxoL6dJD-bPmHs43E4reAztdsOw84MbdqDGRo,380928
|
|
39
|
+
imops/src/_fast_zoom.pyx,sha256=Dd5MDelALAh4-3QhnzfzKTB5Gmi2lVERrbyYj-q8kVQ,23091
|
|
40
|
+
imops/src/_measure.c,sha256=u3o9ozqfWnrn0PxRrY5l7LEvZEEzkfj4JMuTn0MKcSw,1324916
|
|
41
|
+
imops/src/_measure.cp311-win_amd64.pyd,sha256=5kdQWADwJ2fuLPoq8Gdd_I6tKYQpjTskq8jg29UlOWQ,243712
|
|
42
|
+
imops/src/_measure.pyx,sha256=JzZ26PptkVT08OemSFaJakc4jf7xYocPqV8jfrYi_wA,2737
|
|
43
|
+
imops/src/_morphology.c,sha256=ntlYexi4kFK85mMvUkWSlKMf-Fwix05M18NvVEVyYAQ,960908
|
|
44
|
+
imops/src/_morphology.cp311-win_amd64.pyd,sha256=l162NQ0Cqz0bGsugR6NEBmrBxCpyQUzN6p6EBU-V-9Y,135168
|
|
45
|
+
imops/src/_morphology.pyx,sha256=QwFrPQ6M-bJDVQNCCtKK7FR3KOr5cZSx_CBvxh9THXM,9538
|
|
46
|
+
imops/src/_numba_zoom.py,sha256=OmMj7mCFkV2P0FaWVXFuYpfzMBQ3Ks60YiHQURes5wQ,16857
|
|
47
|
+
imops/src/_numeric.c,sha256=pxoAtM5-ZGaBVyWd8HBARI7_LnX6_n7kNt6vS1p_JmI,2091727
|
|
48
|
+
imops/src/_numeric.cp311-win_amd64.pyd,sha256=p4bUA0QN-Lj_TtjvrvKVUKg1_TTyQsETFpfBlHWneRY,368128
|
|
49
|
+
imops/src/_numeric.pyx,sha256=c8q3KlL8mDe0wsz72ooeqhffk1iqvA0Org7NWW5RbYo,8221
|
|
50
|
+
imops/src/_radon.c,sha256=TdmwLBra4NsH9qtX--0OC6XaTfzN8ApZxDovOC3J9kU,1178982
|
|
51
|
+
imops/src/_radon.cp311-win_amd64.pyd,sha256=oeTifE7jamc2uhCSGa19-oMSd66SpbvGR8TC0E7NRmM,215040
|
|
52
|
+
imops/src/_radon.pyx,sha256=pu8EZplCjIDz9HqBcAit8nj2lDgXpmdhU61_4CfdGWI,2987
|
|
53
|
+
imops/src/_zoom.c,sha256=Yli60QHVVNFUJ2ycnbEW_Hbnz15-tRcG_J_xg_rCL4U,2126517
|
|
54
|
+
imops/src/_zoom.cp311-win_amd64.pyd,sha256=-eOP35e3g0KIj052jxxRN5BJfj8fjNkb8C7Bz072LKM,380416
|
|
55
|
+
imops/src/_zoom.pyx,sha256=Dd5MDelALAh4-3QhnzfzKTB5Gmi2lVERrbyYj-q8kVQ,23091
|
|
56
|
+
imops-0.8.3.dist-info/LICENSE,sha256=x7oyYZPKDTt8KoTitcNJLBWhueke5YEOopdr0XjyvBc,1085
|
|
57
|
+
imops-0.8.3.dist-info/METADATA,sha256=tDl7xwD4lJ6Yx_X7wYRKFuv-_QmBlXMHivDQCOnT_K8,9048
|
|
58
|
+
imops-0.8.3.dist-info/WHEEL,sha256=badvNS-y9fEq0X-qzdZYvql_JFjI7Xfw-wR8FsjoK0I,102
|
|
59
|
+
imops-0.8.3.dist-info/top_level.txt,sha256=jd2uO-zsYvKeqkZbjEjtkGqCca0IEXEiEuoyr3nsHB0,19
|
|
60
|
+
imops-0.8.3.dist-info/RECORD,,
|
_pyproject_build.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import shutil
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
from setuptools import Extension
|
|
5
|
-
from setuptools.command.build_py import build_py
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class NumpyImport:
|
|
9
|
-
def __repr__(self):
|
|
10
|
-
import numpy as np
|
|
11
|
-
|
|
12
|
-
return np.get_include()
|
|
13
|
-
|
|
14
|
-
__fspath__ = __repr__
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class PyprojectBuild(build_py):
|
|
18
|
-
def run(self):
|
|
19
|
-
self.run_command('build_ext')
|
|
20
|
-
return super().run()
|
|
21
|
-
|
|
22
|
-
def initialize_options(self):
|
|
23
|
-
super().initialize_options()
|
|
24
|
-
|
|
25
|
-
name = 'imops'
|
|
26
|
-
args = ['-fopenmp']
|
|
27
|
-
|
|
28
|
-
if self.distribution.ext_modules is None:
|
|
29
|
-
self.distribution.ext_modules = []
|
|
30
|
-
|
|
31
|
-
# Cython extension and .pyx source file names must be the same to compile
|
|
32
|
-
# https://stackoverflow.com/questions/8024805/cython-compiled-c-extension-importerror-dynamic-module-does-not-define-init-fu
|
|
33
|
-
modules = ['backprojection', 'measure', 'morphology', 'numeric', 'radon', 'zoom']
|
|
34
|
-
for module in modules:
|
|
35
|
-
src_dir = Path(__file__).parent / name / 'src'
|
|
36
|
-
shutil.copyfile(src_dir / f'_{module}.pyx', src_dir / f'_fast_{module}.pyx')
|
|
37
|
-
|
|
38
|
-
for module in modules:
|
|
39
|
-
for prefix, additional_args in zip(['', 'fast_'], [[], ['-ffast-math']]):
|
|
40
|
-
self.distribution.ext_modules.append(
|
|
41
|
-
Extension(
|
|
42
|
-
f'{name}.src._{prefix}{module}',
|
|
43
|
-
[f'{name}/src/_{prefix}{module}.pyx'],
|
|
44
|
-
include_dirs=[NumpyImport()],
|
|
45
|
-
extra_compile_args=args + additional_args,
|
|
46
|
-
extra_link_args=args + additional_args,
|
|
47
|
-
define_macros=[('NPY_NO_DEPRECATED_API', 'NPY_1_7_API_VERSION')],
|
|
48
|
-
)
|
|
49
|
-
)
|
imops/_numeric.py
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
from warnings import warn
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
|
|
5
|
-
from .backend import BackendLike, resolve_backend
|
|
6
|
-
from .src._fast_numeric import (
|
|
7
|
-
_parallel_pointwise_mul as cython_fast_parallel_pointwise_mul,
|
|
8
|
-
_parallel_sum as cython_fast_parallel_sum,
|
|
9
|
-
)
|
|
10
|
-
from .src._numeric import _parallel_pointwise_mul as cython_parallel_pointwise_mul, _parallel_sum as cython_parallel_sum
|
|
11
|
-
from .utils import normalize_num_threads
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _sum(nums: np.ndarray, num_threads: int = -1, backend: BackendLike = None) -> float:
|
|
15
|
-
"""
|
|
16
|
-
Parallel sum of flat numpy array
|
|
17
|
-
|
|
18
|
-
Parameters
|
|
19
|
-
----------
|
|
20
|
-
nums: np.ndarray
|
|
21
|
-
1-dimensional array
|
|
22
|
-
num_threads: int
|
|
23
|
-
the number of threads to use for computation. Default = the cpu count. If negative value passed
|
|
24
|
-
cpu count + num_threads + 1 threads will be used
|
|
25
|
-
backend: BackendLike
|
|
26
|
-
which backend to use. `cython` and `scipy` (primarly for benchmarking) are available,
|
|
27
|
-
`cython` is used by default
|
|
28
|
-
|
|
29
|
-
Returns
|
|
30
|
-
-------
|
|
31
|
-
sum: float
|
|
32
|
-
|
|
33
|
-
Examples
|
|
34
|
-
--------
|
|
35
|
-
>>> s = _sum(x, num_threads=1)
|
|
36
|
-
>>> s = _sum(x, num_threads=8, backend=Cython(fast=True)) # ffast-math compiled version
|
|
37
|
-
"""
|
|
38
|
-
ndim = nums.ndim
|
|
39
|
-
|
|
40
|
-
if ndim != 1:
|
|
41
|
-
raise ValueError(f'Input must be 1-dimensional instead of {ndim}-dimensional.')
|
|
42
|
-
|
|
43
|
-
backend = resolve_backend(backend)
|
|
44
|
-
if backend.name not in ('Cython', 'Scipy'):
|
|
45
|
-
raise ValueError(f'Unsupported backend "{backend.name}"')
|
|
46
|
-
|
|
47
|
-
num_threads = normalize_num_threads(num_threads, backend)
|
|
48
|
-
|
|
49
|
-
if backend.name == 'Scipy':
|
|
50
|
-
return nums.sum()
|
|
51
|
-
|
|
52
|
-
if backend.name == 'Cython':
|
|
53
|
-
src_parallel_sum = cython_fast_parallel_sum if backend.fast else cython_parallel_sum
|
|
54
|
-
|
|
55
|
-
return src_parallel_sum(nums, num_threads)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def _mul(nums1: np.ndarray, nums2: np.ndarray, num_threads: int = -1, backend: BackendLike = None) -> np.ndarray:
|
|
59
|
-
"""
|
|
60
|
-
Parallel pointwise multiplication of 2 numpy arrays (aka x * y). Works faster only for ndim <= 3.
|
|
61
|
-
|
|
62
|
-
Parameters
|
|
63
|
-
----------
|
|
64
|
-
nums1: np.ndarray
|
|
65
|
-
nums2: np.ndarray
|
|
66
|
-
num_threads: int
|
|
67
|
-
the number of threads to use for computation. Default = the cpu count. If negative value passed
|
|
68
|
-
cpu count + num_threads + 1 threads will be used
|
|
69
|
-
backend: BackendLike
|
|
70
|
-
which backend to use. `cython` and `scipy` (primarly for benchmarking) are available,
|
|
71
|
-
`cython` is used by default
|
|
72
|
-
|
|
73
|
-
Returns
|
|
74
|
-
-------
|
|
75
|
-
multiplied: np.ndarray
|
|
76
|
-
result of nums1 * nums2
|
|
77
|
-
|
|
78
|
-
Examples
|
|
79
|
-
--------
|
|
80
|
-
>>> mul = _mul(nums1, nums2, num_threads=8)
|
|
81
|
-
>>> mul = _mul(np.ones((2, 3)), np.ones((1, 3))) # broadcasting, mul.shape == (2, 3)
|
|
82
|
-
>>> mul = _mul(nums1, nums2, backend=Cython(fast=True)) # ffast-math compiled version
|
|
83
|
-
"""
|
|
84
|
-
if not nums1.size and not nums2.size:
|
|
85
|
-
return np.array([], dtype=nums1.dtype)
|
|
86
|
-
if bool(nums1.size) ^ bool(nums2.size):
|
|
87
|
-
raise ValueError('One of the arrays is empty, hence pointwise multiplication cannot be performed.')
|
|
88
|
-
if len(nums1.shape) != len(nums2.shape):
|
|
89
|
-
raise ValueError('Both arrays must have the same number of dimensions for pointwise multiplication.')
|
|
90
|
-
for dim1, dim2 in zip(nums1.shape, nums2.shape):
|
|
91
|
-
if dim1 != dim2 and dim1 != 1 and dim2 != 1:
|
|
92
|
-
raise ValueError(f'Arrays of shapes {nums1.shape} and {nums2.shape} are not broadcastable.')
|
|
93
|
-
|
|
94
|
-
if nums1.ndim > 3:
|
|
95
|
-
warn('Parallel pointwise multiplication is only supported for ndim<=3. Falling back to naive x * y.')
|
|
96
|
-
|
|
97
|
-
return nums1 * nums2
|
|
98
|
-
|
|
99
|
-
backend = resolve_backend(backend)
|
|
100
|
-
if backend.name not in ('Cython', 'Scipy'):
|
|
101
|
-
raise ValueError(f'Unsupported backend "{backend.name}"')
|
|
102
|
-
|
|
103
|
-
num_threads = normalize_num_threads(num_threads, backend)
|
|
104
|
-
|
|
105
|
-
if backend.name == 'Scipy':
|
|
106
|
-
return nums1 * nums2
|
|
107
|
-
|
|
108
|
-
if backend.name == 'Cython':
|
|
109
|
-
src_parallel_pointwise_mul = (
|
|
110
|
-
cython_fast_parallel_pointwise_mul if backend.fast else cython_parallel_pointwise_mul
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
n_dummy = 3 - nums1.ndim
|
|
114
|
-
|
|
115
|
-
if n_dummy:
|
|
116
|
-
nums1 = nums1[(None,) * n_dummy]
|
|
117
|
-
nums2 = nums2[(None,) * n_dummy]
|
|
118
|
-
|
|
119
|
-
out = src_parallel_pointwise_mul(nums1, nums2, np.maximum(nums1.shape, nums2.shape), num_threads)
|
|
120
|
-
|
|
121
|
-
if n_dummy:
|
|
122
|
-
out = out[(0,) * n_dummy]
|
|
123
|
-
|
|
124
|
-
return out
|