pytme 0.2.1__cp311-cp311-macosx_14_0_arm64.whl → 0.2.3__cp311-cp311-macosx_14_0_arm64.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.
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/match_template.py +219 -216
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/postprocess.py +86 -54
- pytme-0.2.3.data/scripts/preprocess.py +132 -0
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/preprocessor_gui.py +181 -94
- pytme-0.2.3.dist-info/METADATA +92 -0
- pytme-0.2.3.dist-info/RECORD +75 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/WHEEL +1 -1
- pytme-0.2.1.data/scripts/preprocess.py → scripts/eval.py +1 -1
- scripts/extract_candidates.py +20 -13
- scripts/match_template.py +219 -216
- scripts/match_template_filters.py +154 -95
- scripts/postprocess.py +86 -54
- scripts/preprocess.py +95 -56
- scripts/preprocessor_gui.py +181 -94
- scripts/refine_matches.py +265 -61
- tme/__init__.py +0 -1
- tme/__version__.py +1 -1
- tme/analyzer.py +458 -813
- tme/backends/__init__.py +40 -11
- tme/backends/_jax_utils.py +187 -0
- tme/backends/cupy_backend.py +109 -226
- tme/backends/jax_backend.py +230 -152
- tme/backends/matching_backend.py +445 -384
- tme/backends/mlx_backend.py +32 -59
- tme/backends/npfftw_backend.py +240 -507
- tme/backends/pytorch_backend.py +30 -151
- tme/density.py +248 -371
- tme/extensions.cpython-311-darwin.so +0 -0
- tme/matching_data.py +328 -284
- tme/matching_exhaustive.py +195 -1499
- tme/matching_optimization.py +143 -106
- tme/matching_scores.py +887 -0
- tme/matching_utils.py +287 -388
- tme/memory.py +377 -0
- tme/orientations.py +78 -21
- tme/parser.py +3 -4
- tme/preprocessing/_utils.py +61 -32
- tme/preprocessing/composable_filter.py +7 -4
- tme/preprocessing/compose.py +7 -3
- tme/preprocessing/frequency_filters.py +49 -39
- tme/preprocessing/tilt_series.py +44 -72
- tme/preprocessor.py +560 -526
- tme/structure.py +491 -188
- tme/types.py +5 -3
- pytme-0.2.1.dist-info/METADATA +0 -73
- pytme-0.2.1.dist-info/RECORD +0 -73
- tme/helpers.py +0 -881
- tme/matching_constrained.py +0 -195
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/estimate_ram_usage.py +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/LICENSE +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/entry_points.txt +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/top_level.txt +0 -0
tme/backends/cupy_backend.py
CHANGED
@@ -1,35 +1,34 @@
|
|
1
|
-
""" Backend using cupy
|
2
|
-
template matching.
|
1
|
+
""" Backend using cupy for template matching.
|
3
2
|
|
4
3
|
Copyright (c) 2023 European Molecular Biology Laboratory
|
5
4
|
|
6
5
|
Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
|
7
6
|
"""
|
8
|
-
|
9
7
|
import warnings
|
10
|
-
from
|
8
|
+
from importlib.util import find_spec
|
11
9
|
from contextlib import contextmanager
|
10
|
+
from typing import Tuple, Callable, List
|
12
11
|
|
13
12
|
import numpy as np
|
14
|
-
from numpy.typing import NDArray
|
15
13
|
|
16
14
|
from .npfftw_backend import NumpyFFTWBackend
|
17
|
-
from ..types import CupyArray
|
15
|
+
from ..types import CupyArray, NDArray, shm_type
|
18
16
|
|
19
17
|
PLAN_CACHE = {}
|
18
|
+
TEXTURE_CACHE = {}
|
20
19
|
|
21
20
|
|
22
21
|
class CupyBackend(NumpyFFTWBackend):
|
23
22
|
"""
|
24
|
-
A cupy
|
23
|
+
A cupy-based matching backend.
|
25
24
|
"""
|
26
25
|
|
27
26
|
def __init__(
|
28
27
|
self,
|
29
|
-
float_dtype=None,
|
30
|
-
complex_dtype=None,
|
31
|
-
int_dtype=None,
|
32
|
-
overflow_safe_dtype=None,
|
28
|
+
float_dtype: type = None,
|
29
|
+
complex_dtype: type = None,
|
30
|
+
int_dtype: type = None,
|
31
|
+
overflow_safe_dtype: type = None,
|
33
32
|
**kwargs,
|
34
33
|
):
|
35
34
|
import cupy as cp
|
@@ -54,14 +53,35 @@ class CupyBackend(NumpyFFTWBackend):
|
|
54
53
|
self.affine_transform = affine_transform
|
55
54
|
self.maximum_filter = maximum_filter
|
56
55
|
|
57
|
-
|
58
|
-
|
56
|
+
itype = f"int{self.datatype_bytes(int_dtype) * 8}"
|
57
|
+
ftype = f"float{self.datatype_bytes(float_dtype) * 8}"
|
59
58
|
self._max_score_over_rotations = self._array_backend.ElementwiseKernel(
|
60
|
-
f"{
|
61
|
-
f"{
|
59
|
+
f"{ftype} internal_scores, {ftype} scores, {itype} rot_index",
|
60
|
+
f"{ftype} out1, {itype} rotations",
|
62
61
|
"if (internal_scores < scores) {out1 = scores; rotations = rot_index;}",
|
63
62
|
"max_score_over_rotations",
|
64
63
|
)
|
64
|
+
self.norm_scores = cp.ElementwiseKernel(
|
65
|
+
f"{ftype} arr, {ftype} exp_sq, {ftype} sq_exp, {ftype} n_obs, {ftype} eps",
|
66
|
+
f"{ftype} out",
|
67
|
+
"""
|
68
|
+
// tmp1 = E(X)^2; tmp2 = E(X^2)
|
69
|
+
float tmp1 = sq_exp / n_obs;
|
70
|
+
float tmp2 = exp_sq / n_obs;
|
71
|
+
tmp1 *= tmp1;
|
72
|
+
|
73
|
+
tmp2 = sqrt(max(tmp2 - tmp1, 0.0));
|
74
|
+
// out = (tmp2 < eps) ? 0.0 : arr / (tmp2 * n_obs);
|
75
|
+
tmp1 = arr;
|
76
|
+
if (tmp2 < eps){
|
77
|
+
tmp1 = 0;
|
78
|
+
}
|
79
|
+
tmp2 *= n_obs;
|
80
|
+
out = tmp1 / tmp2;
|
81
|
+
""",
|
82
|
+
"norm_scores",
|
83
|
+
)
|
84
|
+
self.texture_available = find_spec("voltools") is not None
|
65
85
|
|
66
86
|
def to_backend_array(self, arr: NDArray) -> CupyArray:
|
67
87
|
current_device = self._array_backend.cuda.device.get_device_id()
|
@@ -78,35 +98,15 @@ class CupyBackend(NumpyFFTWBackend):
|
|
78
98
|
def to_cpu_array(self, arr: NDArray) -> NDArray:
|
79
99
|
return self.to_numpy_array(arr)
|
80
100
|
|
81
|
-
def
|
82
|
-
|
83
|
-
) -> CupyArray:
|
84
|
-
return shm
|
101
|
+
def from_sharedarr(self, arr: CupyArray) -> CupyArray:
|
102
|
+
return arr
|
85
103
|
|
86
104
|
@staticmethod
|
87
|
-
def
|
88
|
-
arr: CupyArray, shared_memory_handler: type = None
|
89
|
-
) -> CupyArray:
|
105
|
+
def to_sharedarr(arr: CupyArray, shared_memory_handler: type = None) -> shm_type:
|
90
106
|
return arr
|
91
107
|
|
92
|
-
def
|
93
|
-
|
94
|
-
Returns a byte-aligned array of zeros with specified shape and dtype.
|
95
|
-
|
96
|
-
Parameters
|
97
|
-
----------
|
98
|
-
shape : Tuple[int]
|
99
|
-
Desired shape for the array.
|
100
|
-
dtype : type
|
101
|
-
Desired data type for the array.
|
102
|
-
|
103
|
-
Returns
|
104
|
-
-------
|
105
|
-
NDArray
|
106
|
-
Byte-aligned array of zeros with specified shape and dtype.
|
107
|
-
"""
|
108
|
-
arr = self._array_backend.zeros(shape, dtype=dtype)
|
109
|
-
return arr
|
108
|
+
def zeros(self, *args, **kwargs):
|
109
|
+
return self._array_backend.zeros(*args, **kwargs)
|
110
110
|
|
111
111
|
def unravel_index(self, indices, shape):
|
112
112
|
return self._array_backend.unravel_index(indices=indices, dims=shape)
|
@@ -114,10 +114,10 @@ class CupyBackend(NumpyFFTWBackend):
|
|
114
114
|
def unique(self, ar, axis=None, *args, **kwargs):
|
115
115
|
if axis is None:
|
116
116
|
return self._array_backend.unique(ar=ar, axis=axis, *args, **kwargs)
|
117
|
-
warnings.warn("Axis argument not yet supported in CupY, falling back to NumPy.")
|
118
117
|
|
118
|
+
warnings.warn("Axis argument not yet supported in CupY, falling back to NumPy.")
|
119
119
|
ret = np.unique(ar=self.to_numpy_array(ar), axis=axis, *args, **kwargs)
|
120
|
-
if
|
120
|
+
if not isinstance(ret, tuple):
|
121
121
|
return self.to_backend_array(ret)
|
122
122
|
return tuple(self.to_backend_array(k) for k in ret)
|
123
123
|
|
@@ -130,34 +130,9 @@ class CupyBackend(NumpyFFTWBackend):
|
|
130
130
|
inverse_fast_shape: Tuple[int] = None,
|
131
131
|
**kwargs,
|
132
132
|
) -> Tuple[Callable, Callable]:
|
133
|
-
|
134
|
-
Build rfftn and irfftn functions.
|
135
|
-
|
136
|
-
Parameters
|
137
|
-
----------
|
138
|
-
fast_shape : tuple
|
139
|
-
Tuple of integers corresponding to fast convolution shape
|
140
|
-
(see :py:meth:`CupyBackend.compute_convolution_shapes`).
|
141
|
-
fast_ft_shape : tuple
|
142
|
-
Tuple of integers corresponding to the shape of the fourier
|
143
|
-
transform array (see :py:meth:`CupyBackend.compute_convolution_shapes`).
|
144
|
-
real_dtype : dtype
|
145
|
-
Numpy dtype of the inverse fourier transform.
|
146
|
-
complex_dtype : dtype
|
147
|
-
Numpy dtype of the fourier transform.
|
148
|
-
inverse_fast_shape : tuple, optional
|
149
|
-
Output shape of the inverse Fourier transform. By default fast_shape.
|
150
|
-
**kwargs: dict, optional
|
151
|
-
Unused keyword arguments.
|
152
|
-
|
153
|
-
Returns
|
154
|
-
-------
|
155
|
-
tuple
|
156
|
-
Tupple containing callable rfft and irfft object.
|
157
|
-
"""
|
158
|
-
cache = self._array_backend.fft.config.get_plan_cache()
|
159
|
-
cache.set_size(2)
|
133
|
+
import cupyx.scipy.fft as cufft
|
160
134
|
|
135
|
+
cache = self._array_backend.fft.config.get_plan_cache()
|
161
136
|
current_device = self._array_backend.cuda.device.get_device_id()
|
162
137
|
|
163
138
|
previous_transform = [fast_shape, fast_ft_shape]
|
@@ -166,20 +141,18 @@ class CupyBackend(NumpyFFTWBackend):
|
|
166
141
|
|
167
142
|
real_diff, cmplx_diff = True, True
|
168
143
|
if len(fast_shape) == len(previous_transform[0]):
|
169
|
-
real_diff =
|
144
|
+
real_diff = fast_shape == previous_transform[0]
|
170
145
|
if len(fast_ft_shape) == len(previous_transform[1]):
|
171
|
-
cmplx_diff =
|
172
|
-
self.sum(self.subtract(fast_ft_shape, previous_transform[1])) != 0
|
173
|
-
)
|
146
|
+
cmplx_diff = fast_ft_shape == previous_transform[1]
|
174
147
|
|
175
148
|
if real_diff or cmplx_diff:
|
176
149
|
cache.clear()
|
177
150
|
|
178
|
-
def rfftn(arr: CupyArray, out: CupyArray) ->
|
179
|
-
|
151
|
+
def rfftn(arr: CupyArray, out: CupyArray) -> CupyArray:
|
152
|
+
return cufft.rfftn(arr, s=fast_shape)
|
180
153
|
|
181
|
-
def irfftn(arr: CupyArray, out: CupyArray) ->
|
182
|
-
|
154
|
+
def irfftn(arr: CupyArray, out: CupyArray) -> CupyArray:
|
155
|
+
return cufft.irfftn(arr, s=fast_shape)
|
183
156
|
|
184
157
|
PLAN_CACHE[current_device] = [fast_shape, fast_ft_shape]
|
185
158
|
|
@@ -187,14 +160,12 @@ class CupyBackend(NumpyFFTWBackend):
|
|
187
160
|
|
188
161
|
def compute_convolution_shapes(
|
189
162
|
self, arr1_shape: Tuple[int], arr2_shape: Tuple[int]
|
190
|
-
) -> Tuple[
|
191
|
-
|
192
|
-
convolution_shape, fast_shape, fast_ft_shape = ret
|
163
|
+
) -> Tuple[List[int], List[int], List[int]]:
|
164
|
+
from cupyx.scipy.fft import next_fast_len
|
193
165
|
|
194
|
-
|
195
|
-
|
196
|
-
fast_shape[-1]
|
197
|
-
fast_ft_shape[-1] += is_odd
|
166
|
+
convolution_shape = [int(x + y - 1) for x, y in zip(arr1_shape, arr2_shape)]
|
167
|
+
fast_shape = [next_fast_len(x, real=True) for x in convolution_shape]
|
168
|
+
fast_ft_shape = list(fast_shape[:-1]) + [fast_shape[-1] // 2 + 1]
|
198
169
|
|
199
170
|
return convolution_shape, fast_shape, fast_ft_shape
|
200
171
|
|
@@ -217,169 +188,81 @@ class CupyBackend(NumpyFFTWBackend):
|
|
217
188
|
out = self.var(a, *args, **kwargs)
|
218
189
|
return self._array_backend.sqrt(out)
|
219
190
|
|
220
|
-
def
|
191
|
+
def _get_texture(self, arr: CupyArray, order: int = 3, prefilter: bool = False):
|
192
|
+
key = id(arr)
|
193
|
+
if key in TEXTURE_CACHE:
|
194
|
+
return TEXTURE_CACHE[key]
|
195
|
+
|
196
|
+
from voltools import StaticVolume
|
197
|
+
|
198
|
+
# Only keep template and potential corresponding mask in cache
|
199
|
+
if len(TEXTURE_CACHE) >= 2:
|
200
|
+
TEXTURE_CACHE.clear()
|
201
|
+
|
202
|
+
interpolation = "filt_bspline"
|
203
|
+
if order == 1:
|
204
|
+
interpolation = "linear"
|
205
|
+
elif order == 3 and not prefilter:
|
206
|
+
interpolation = "bspline"
|
207
|
+
|
208
|
+
current_device = self._array_backend.cuda.device.get_device_id()
|
209
|
+
TEXTURE_CACHE[key] = StaticVolume(
|
210
|
+
arr, interpolation=interpolation, device=f"gpu:{current_device}"
|
211
|
+
)
|
212
|
+
|
213
|
+
return TEXTURE_CACHE[key]
|
214
|
+
|
215
|
+
def _rigid_transform(
|
221
216
|
self,
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
out_mask: CupyArray = None,
|
229
|
-
order: int = 3,
|
217
|
+
data: CupyArray,
|
218
|
+
matrix: CupyArray,
|
219
|
+
output: CupyArray,
|
220
|
+
prefilter: bool,
|
221
|
+
order: int,
|
222
|
+
cache: bool = False,
|
230
223
|
) -> None:
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
arr : CupyArray
|
241
|
-
The input array to be rotated.
|
242
|
-
arr_mask : CupyArray, optional
|
243
|
-
The mask of `arr` that will be equivalently rotated.
|
244
|
-
rotation_matrix : CupyArray
|
245
|
-
The rotation matrix to apply [d x d].
|
246
|
-
translation : CupyArray
|
247
|
-
The translation to apply [d].
|
248
|
-
use_geometric_center : bool, optional
|
249
|
-
Whether the rotation should be centered around the geometric
|
250
|
-
or mass center. Default is mass center.
|
251
|
-
out : CupyArray, optional
|
252
|
-
The output array to write the rotation of `arr` to.
|
253
|
-
out_mask : CupyArray, optional
|
254
|
-
The output array to write the rotation of `arr_mask` to.
|
255
|
-
order : int, optional
|
256
|
-
Spline interpolation order. Has to be in the range 0-5.
|
257
|
-
|
258
|
-
Notes
|
259
|
-
-----
|
260
|
-
Only a box of size arr, arr_mask will be consisdered for interpolation
|
261
|
-
in out, out_mask.
|
262
|
-
"""
|
263
|
-
|
264
|
-
rotate_mask = arr_mask is not None
|
265
|
-
return_type = (out is None) + 2 * rotate_mask * (out_mask is None)
|
266
|
-
translation = self.zeros(arr.ndim) if translation is None else translation
|
267
|
-
|
268
|
-
center = self.divide(arr.shape, 2)
|
269
|
-
if not use_geometric_center:
|
270
|
-
center = self.center_of_mass(arr, cutoff=0)
|
271
|
-
|
272
|
-
rotation_matrix_inverted = self.linalg.inv(
|
273
|
-
rotation_matrix.astype(self._overflow_safe_dtype)
|
274
|
-
).astype(self._float_dtype)
|
275
|
-
transformed_center = rotation_matrix_inverted @ center.reshape(-1, 1)
|
276
|
-
transformed_center = transformed_center.reshape(-1)
|
277
|
-
base_offset = self.subtract(center, transformed_center)
|
278
|
-
offset = self.subtract(base_offset, translation)
|
279
|
-
|
280
|
-
out = self.zeros_like(arr) if out is None else out
|
281
|
-
out_slice = tuple(slice(0, stop) for stop in arr.shape)
|
282
|
-
|
283
|
-
# Applying the prefilter leads to the creation of artifacts in the mask.
|
224
|
+
out_slice = tuple(slice(0, stop) for stop in data.shape)
|
225
|
+
if data.ndim == 3 and cache and self.texture_available:
|
226
|
+
# Device memory pool (should) come to rescue performance
|
227
|
+
temp = self.empty(data.shape, data.dtype)
|
228
|
+
texture = self._get_texture(data, order=order, prefilter=prefilter)
|
229
|
+
texture.affine(transform_m=matrix, profile=False, output=temp)
|
230
|
+
output[out_slice] = temp
|
231
|
+
return None
|
232
|
+
|
284
233
|
self.affine_transform(
|
285
|
-
input=
|
286
|
-
matrix=
|
287
|
-
offset=offset,
|
234
|
+
input=data,
|
235
|
+
matrix=matrix,
|
288
236
|
mode="constant",
|
289
|
-
output=
|
237
|
+
output=output[out_slice],
|
290
238
|
order=order,
|
291
|
-
prefilter=
|
239
|
+
prefilter=prefilter,
|
292
240
|
)
|
293
241
|
|
294
|
-
if rotate_mask:
|
295
|
-
out_mask = self.zeros_like(arr_mask) if out_mask is None else out_mask
|
296
|
-
out_mask_slice = tuple(slice(0, stop) for stop in arr_mask.shape)
|
297
|
-
self.affine_transform(
|
298
|
-
input=arr_mask,
|
299
|
-
matrix=rotation_matrix_inverted,
|
300
|
-
offset=offset,
|
301
|
-
mode="constant",
|
302
|
-
output=out_mask[out_mask_slice],
|
303
|
-
order=order,
|
304
|
-
prefilter=False,
|
305
|
-
)
|
306
|
-
|
307
|
-
match return_type:
|
308
|
-
case 0:
|
309
|
-
return None
|
310
|
-
case 1:
|
311
|
-
return out
|
312
|
-
case 2:
|
313
|
-
return out_mask
|
314
|
-
case 3:
|
315
|
-
return out, out_mask
|
316
|
-
|
317
242
|
def get_available_memory(self) -> int:
|
318
243
|
with self._array_backend.cuda.Device():
|
319
|
-
(
|
320
|
-
free_memory,
|
321
|
-
available_memory,
|
322
|
-
) = self._array_backend.cuda.runtime.memGetInfo()
|
244
|
+
free_memory, _ = self._array_backend.cuda.runtime.memGetInfo()
|
323
245
|
return free_memory
|
324
246
|
|
325
247
|
@contextmanager
|
326
248
|
def set_device(self, device_index: int):
|
327
|
-
"""
|
328
|
-
Set the active GPU device as a context.
|
329
|
-
|
330
|
-
This method sets the active GPU device for operations within the context.
|
331
|
-
|
332
|
-
Parameters
|
333
|
-
----------
|
334
|
-
device_index : int
|
335
|
-
Index of the GPU device to be set as active.
|
336
|
-
|
337
|
-
Yields
|
338
|
-
------
|
339
|
-
None
|
340
|
-
Operates as a context manager, yielding None and providing
|
341
|
-
the set GPU context for enclosed operations.
|
342
|
-
"""
|
343
249
|
with self._array_backend.cuda.Device(device_index):
|
344
250
|
yield
|
345
251
|
|
346
252
|
def device_count(self) -> int:
|
347
|
-
"""
|
348
|
-
Return the number of available GPU devices.
|
349
|
-
|
350
|
-
Returns
|
351
|
-
-------
|
352
|
-
int
|
353
|
-
Number of available GPU devices.
|
354
|
-
"""
|
355
253
|
return self._array_backend.cuda.runtime.getDeviceCount()
|
356
254
|
|
357
255
|
def max_score_over_rotations(
|
358
256
|
self,
|
359
|
-
|
360
|
-
|
361
|
-
|
257
|
+
scores: CupyArray,
|
258
|
+
max_scores: CupyArray,
|
259
|
+
rotations: CupyArray,
|
362
260
|
rotation_index: int,
|
363
|
-
):
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
Parameters
|
369
|
-
----------
|
370
|
-
score_space : CupyArray
|
371
|
-
The score space to compare against internal_scores.
|
372
|
-
internal_scores : CupyArray
|
373
|
-
The internal scores to update with maximum scores.
|
374
|
-
internal_rotations : CupyArray
|
375
|
-
The internal rotations corresponding to the maximum scores.
|
376
|
-
rotation_index : int
|
377
|
-
The index representing the current rotation.
|
378
|
-
"""
|
379
|
-
self._max_score_over_rotations(
|
380
|
-
internal_scores,
|
381
|
-
score_space,
|
261
|
+
) -> Tuple[CupyArray, CupyArray]:
|
262
|
+
return self._max_score_over_rotations(
|
263
|
+
max_scores,
|
264
|
+
scores,
|
382
265
|
rotation_index,
|
383
|
-
|
384
|
-
|
266
|
+
max_scores,
|
267
|
+
rotations,
|
385
268
|
)
|