pytme 0.2.0b0__cp311-cp311-macosx_14_0_arm64.whl → 0.2.1__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.
Files changed (42) hide show
  1. {pytme-0.2.0b0.data → pytme-0.2.1.data}/scripts/match_template.py +473 -140
  2. {pytme-0.2.0b0.data → pytme-0.2.1.data}/scripts/postprocess.py +107 -49
  3. {pytme-0.2.0b0.data → pytme-0.2.1.data}/scripts/preprocessor_gui.py +4 -1
  4. {pytme-0.2.0b0.dist-info → pytme-0.2.1.dist-info}/METADATA +2 -2
  5. pytme-0.2.1.dist-info/RECORD +73 -0
  6. scripts/extract_candidates.py +117 -85
  7. scripts/match_template.py +473 -140
  8. scripts/match_template_filters.py +458 -169
  9. scripts/postprocess.py +107 -49
  10. scripts/preprocessor_gui.py +4 -1
  11. scripts/refine_matches.py +364 -160
  12. tme/__version__.py +1 -1
  13. tme/analyzer.py +278 -148
  14. tme/backends/__init__.py +1 -0
  15. tme/backends/cupy_backend.py +20 -13
  16. tme/backends/jax_backend.py +218 -0
  17. tme/backends/matching_backend.py +25 -10
  18. tme/backends/mlx_backend.py +13 -9
  19. tme/backends/npfftw_backend.py +22 -12
  20. tme/backends/pytorch_backend.py +20 -9
  21. tme/density.py +85 -64
  22. tme/extensions.cpython-311-darwin.so +0 -0
  23. tme/matching_data.py +86 -60
  24. tme/matching_exhaustive.py +245 -166
  25. tme/matching_optimization.py +137 -69
  26. tme/matching_utils.py +1 -1
  27. tme/orientations.py +175 -55
  28. tme/preprocessing/__init__.py +2 -0
  29. tme/preprocessing/_utils.py +188 -0
  30. tme/preprocessing/composable_filter.py +31 -0
  31. tme/preprocessing/compose.py +51 -0
  32. tme/preprocessing/frequency_filters.py +378 -0
  33. tme/preprocessing/tilt_series.py +1017 -0
  34. tme/preprocessor.py +17 -7
  35. tme/structure.py +4 -1
  36. pytme-0.2.0b0.dist-info/RECORD +0 -66
  37. {pytme-0.2.0b0.data → pytme-0.2.1.data}/scripts/estimate_ram_usage.py +0 -0
  38. {pytme-0.2.0b0.data → pytme-0.2.1.data}/scripts/preprocess.py +0 -0
  39. {pytme-0.2.0b0.dist-info → pytme-0.2.1.dist-info}/LICENSE +0 -0
  40. {pytme-0.2.0b0.dist-info → pytme-0.2.1.dist-info}/WHEEL +0 -0
  41. {pytme-0.2.0b0.dist-info → pytme-0.2.1.dist-info}/entry_points.txt +0 -0
  42. {pytme-0.2.0b0.dist-info → pytme-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,378 @@
1
+ """ Defines Fourier frequency filters.
2
+
3
+ Copyright (c) 2024 European Molecular Biology Laboratory
4
+
5
+ Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
6
+ """
7
+ from math import log, sqrt
8
+ from typing import Tuple, Dict
9
+
10
+ import numpy as np
11
+ from numpy.typing import NDArray
12
+ from scipy.ndimage import mean as ndimean
13
+ from scipy.ndimage import map_coordinates
14
+
15
+ from ._utils import fftfreqn, crop_real_fourier, shift_fourier
16
+ from ..backends import backend
17
+
18
+
19
+ class BandPassFilter:
20
+ """
21
+ This class provides methods to generate bandpass filters in Fourier space,
22
+ either by directly specifying the frequency cutoffs (discrete_bandpass) or
23
+ by using Gaussian functions (gaussian_bandpass).
24
+
25
+ Parameters:
26
+ -----------
27
+ lowpass : float, optional
28
+ The lowpass cutoff, defaults to None.
29
+ highpass : float, optional
30
+ The highpass cutoff, defaults to None.
31
+ sampling_rate : Tuple[float], optional
32
+ The sampling rate in Fourier space, defaults to 1.
33
+ use_gaussian : bool, optional
34
+ Whether to use Gaussian bandpass filter, defaults to True.
35
+ return_real_fourier : bool, optional
36
+ Whether to return only the real Fourier space, defaults to False.
37
+ shape_is_real_fourier : bool, optional
38
+ Whether the shape represents the real Fourier space, defaults to False.
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ lowpass: float = None,
44
+ highpass: float = None,
45
+ sampling_rate: Tuple[float] = 1,
46
+ use_gaussian: bool = True,
47
+ return_real_fourier: bool = False,
48
+ shape_is_real_fourier: bool = False,
49
+ ):
50
+ self.lowpass = lowpass
51
+ self.highpass = highpass
52
+ self.use_gaussian = use_gaussian
53
+ self.return_real_fourier = return_real_fourier
54
+ self.shape_is_real_fourier = shape_is_real_fourier
55
+ self.sampling_rate = sampling_rate
56
+
57
+ @staticmethod
58
+ def discrete_bandpass(
59
+ shape: Tuple[int],
60
+ lowpass: float,
61
+ highpass: float,
62
+ sampling_rate: Tuple[float],
63
+ return_real_fourier: bool = False,
64
+ shape_is_real_fourier: bool = False,
65
+ **kwargs,
66
+ ) -> NDArray:
67
+ """
68
+ Generate a bandpass filter using discrete frequency cutoffs.
69
+
70
+ Parameters:
71
+ -----------
72
+ shape : tuple of int
73
+ The shape of the bandpass filter.
74
+ lowpass : float
75
+ The lowpass cutoff in units of sampling rate.
76
+ highpass : float
77
+ The highpass cutoff in units of sampling rate.
78
+ return_real_fourier : bool, optional
79
+ Whether to return only the real Fourier space, defaults to False.
80
+ sampling_rate : float
81
+ The sampling rate in Fourier space.
82
+ shape_is_real_fourier : bool, optional
83
+ Whether the shape represents the real Fourier space, defaults to False.
84
+ **kwargs : dict
85
+ Additional keyword arguments.
86
+
87
+ Returns:
88
+ --------
89
+ NDArray
90
+ The bandpass filter in Fourier space.
91
+ """
92
+ if shape_is_real_fourier:
93
+ return_real_fourier = False
94
+
95
+ grid = fftfreqn(
96
+ shape=shape,
97
+ sampling_rate=0.5,
98
+ shape_is_real_fourier=shape_is_real_fourier,
99
+ compute_euclidean_norm=True,
100
+ )
101
+
102
+ lowpass = 0 if lowpass is None else lowpass
103
+ highpass = 1e10 if highpass is None else highpass
104
+
105
+ highcut = grid.max()
106
+ if lowpass > 0:
107
+ highcut = np.max(2 * sampling_rate / lowpass)
108
+ lowcut = np.max(2 * sampling_rate / highpass)
109
+
110
+ bandpass_filter = ((grid <= highcut) & (grid >= lowcut)) * 1.0
111
+
112
+ bandpass_filter = shift_fourier(
113
+ data=bandpass_filter, shape_is_real_fourier=shape_is_real_fourier
114
+ )
115
+
116
+ if return_real_fourier:
117
+ bandpass_filter = crop_real_fourier(bandpass_filter)
118
+
119
+ return bandpass_filter
120
+
121
+ @staticmethod
122
+ def gaussian_bandpass(
123
+ shape: Tuple[int],
124
+ lowpass: float,
125
+ highpass: float,
126
+ sampling_rate: float,
127
+ return_real_fourier: bool = False,
128
+ shape_is_real_fourier: bool = False,
129
+ **kwargs,
130
+ ) -> NDArray:
131
+ """
132
+ Generate a bandpass filter using Gaussian functions.
133
+
134
+ Parameters:
135
+ -----------
136
+ shape : tuple of int
137
+ The shape of the bandpass filter.
138
+ lowpass : float
139
+ The lowpass cutoff in units of sampling rate.
140
+ highpass : float
141
+ The highpass cutoff in units of sampling rate.
142
+ sampling_rate : float
143
+ The sampling rate in Fourier space.
144
+ return_real_fourier : bool, optional
145
+ Whether to return only the real Fourier space, defaults to False.
146
+ shape_is_real_fourier : bool, optional
147
+ Whether the shape represents the real Fourier space, defaults to False.
148
+ **kwargs : dict
149
+ Additional keyword arguments.
150
+
151
+ Returns:
152
+ --------
153
+ NDArray
154
+ The bandpass filter in Fourier space.
155
+ """
156
+ if shape_is_real_fourier:
157
+ return_real_fourier = False
158
+
159
+ grid = fftfreqn(
160
+ shape=shape,
161
+ sampling_rate=0.5,
162
+ shape_is_real_fourier=shape_is_real_fourier,
163
+ compute_euclidean_norm=True,
164
+ )
165
+ grid = -backend.square(grid)
166
+
167
+ lowpass_filter, highpass_filter = 1, 1
168
+ norm = float(sqrt(2 * log(2)))
169
+ upper_sampling = float(backend.max(backend.multiply(2, sampling_rate)))
170
+
171
+ if lowpass is not None:
172
+ lowpass = float(lowpass)
173
+ lowpass = backend.maximum(lowpass, backend.eps(backend._float_dtype))
174
+ if highpass is not None:
175
+ highpass = float(highpass)
176
+ highpass = backend.maximum(highpass, backend.eps(backend._float_dtype))
177
+
178
+ if lowpass is not None:
179
+ lowpass = upper_sampling / (lowpass * norm)
180
+ lowpass = backend.multiply(2, backend.square(lowpass))
181
+ lowpass_filter = backend.exp(backend.divide(grid, lowpass))
182
+ if highpass is not None:
183
+ highpass = upper_sampling / (highpass * norm)
184
+ highpass = backend.multiply(2, backend.square(highpass))
185
+ highpass_filter = 1 - backend.exp(backend.divide(grid, highpass))
186
+
187
+ bandpass_filter = backend.multiply(lowpass_filter, highpass_filter)
188
+ bandpass_filter = shift_fourier(
189
+ data=bandpass_filter, shape_is_real_fourier=shape_is_real_fourier
190
+ )
191
+
192
+ if return_real_fourier:
193
+ bandpass_filter = crop_real_fourier(bandpass_filter)
194
+
195
+ return bandpass_filter
196
+
197
+ def __call__(self, **kwargs):
198
+ func_args = vars(self)
199
+ func_args.update(kwargs)
200
+
201
+ func = self.discrete_bandpass
202
+ if func_args.get("use_gaussian"):
203
+ func = self.gaussian_bandpass
204
+
205
+ mask = func(**func_args)
206
+
207
+ return {
208
+ "data": backend.to_backend_array(mask),
209
+ "sampling_rate": func_args.get("sampling_rate", 1),
210
+ "is_multiplicative_filter": True,
211
+ }
212
+
213
+
214
+ class LinearWhiteningFilter:
215
+ """
216
+ This class provides methods to compute the spectrum of the input data and
217
+ apply linear whitening to the Fourier coefficients.
218
+
219
+ Parameters:
220
+ -----------
221
+ **kwargs : Dict, optional
222
+ Additional keyword arguments.
223
+
224
+
225
+ References
226
+ ----------
227
+ .. [1] de Teresa-Trueba, I.; Goetz, S. K.; Mattausch, A.; Stojanovska, F.; Zimmerli, C. E.;
228
+ Toro-Nahuelpan, M.; Cheng, D. W. C.; Tollervey, F.; Pape, C.; Beck, M.; Diz-Munoz,
229
+ A.; Kreshuk, A.; Mahamid, J.; Zaugg, J. B. Nat. Methods 2023, 20, 284–294.
230
+ .. [2] M. L. Chaillet, G. van der Schot, I. Gubins, S. Roet,
231
+ R. C. Veltkamp, and F. Förster, Int. J. Mol. Sci. 24,
232
+ 13375 (2023)
233
+ """
234
+
235
+ def __init__(self, **kwargs):
236
+ pass
237
+
238
+ @staticmethod
239
+ def _compute_spectrum(
240
+ data_rfft: NDArray, n_bins: int = None, batch_dimension: int = None
241
+ ) -> Tuple[NDArray, NDArray]:
242
+ """
243
+ Compute the spectrum of the input data.
244
+
245
+ Parameters:
246
+ -----------
247
+ data_rfft : NDArray
248
+ The Fourier transform of the input data.
249
+ n_bins : int, optional
250
+ The number of bins for computing the spectrum, defaults to None.
251
+ batch_dimension : int, optional
252
+ Batch dimension to average over.
253
+
254
+ Returns:
255
+ --------
256
+ bins : NDArray
257
+ Array containing the bin indices for the spectrum.
258
+ radial_averages : NDArray
259
+ Array containing the radial averages of the spectrum.
260
+ """
261
+ shape = tuple(x for i, x in enumerate(data_rfft.shape) if i != batch_dimension)
262
+
263
+ max_bins = max(max(shape[:-1]) // 2 + 1, shape[-1])
264
+ n_bins = max_bins if n_bins is None else n_bins
265
+ n_bins = int(min(n_bins, max_bins))
266
+
267
+ bins = fftfreqn(
268
+ shape=shape,
269
+ sampling_rate=0.5,
270
+ shape_is_real_fourier=True,
271
+ compute_euclidean_norm=True,
272
+ )
273
+ bins = backend.to_numpy_array(bins)
274
+
275
+ # Implicit lowpass to nyquist
276
+ bins = np.floor(bins * (n_bins - 1) + 0.5).astype(int)
277
+ fft_shift_axes = tuple(
278
+ i for i in range(data_rfft.ndim - 1) if i != batch_dimension
279
+ )
280
+ fourier_spectrum = np.fft.fftshift(data_rfft, axes=fft_shift_axes)
281
+ fourier_spectrum = np.abs(fourier_spectrum)
282
+ np.square(fourier_spectrum, out=fourier_spectrum)
283
+
284
+ radial_averages = ndimean(
285
+ fourier_spectrum, labels=bins, index=np.arange(n_bins)
286
+ )
287
+ np.sqrt(radial_averages, out=radial_averages)
288
+ np.reciprocal(radial_averages, out=radial_averages)
289
+ np.divide(radial_averages, radial_averages.max(), out=radial_averages)
290
+
291
+ return bins, radial_averages
292
+
293
+ @staticmethod
294
+ def _interpolate_spectrum(
295
+ spectrum: NDArray,
296
+ shape: Tuple[int],
297
+ shape_is_real_fourier: bool = True,
298
+ order: int = 1,
299
+ ) -> NDArray:
300
+ """
301
+ References
302
+ ----------
303
+ .. [1] M. L. Chaillet, G. van der Schot, I. Gubins, S. Roet,
304
+ R. C. Veltkamp, and F. Förster, Int. J. Mol. Sci. 24,
305
+ 13375 (2023)
306
+ """
307
+ grid = fftfreqn(
308
+ shape=shape,
309
+ sampling_rate=.5,
310
+ shape_is_real_fourier=shape_is_real_fourier,
311
+ compute_euclidean_norm=True,
312
+ )
313
+ grid = backend.to_numpy_array(grid)
314
+ np.multiply(grid, (spectrum.shape[0] - 1), out = grid) + 0.5
315
+ spectrum = map_coordinates(spectrum, grid.reshape(1, -1), order=order)
316
+ return spectrum.reshape(grid.shape)
317
+
318
+ def __call__(
319
+ self,
320
+ data: NDArray = None,
321
+ data_rfft: NDArray = None,
322
+ n_bins: int = None,
323
+ batch_dimension: int = None,
324
+ order: int = 1,
325
+ **kwargs: Dict,
326
+ ) -> Dict:
327
+ """
328
+ Apply linear whitening to the data and return the result.
329
+
330
+ Parameters:
331
+ -----------
332
+ data : NDArray, optional
333
+ The input data, defaults to None.
334
+ data_rfft : NDArray, optional
335
+ The Fourier transform of the input data, defaults to None.
336
+ n_bins : int, optional
337
+ The number of bins for computing the spectrum, defaults to None.
338
+ batch_dimension : int, optional
339
+ Batch dimension to average over.
340
+ order : int, optional
341
+ Interpolation order to use.
342
+ **kwargs : Dict
343
+ Additional keyword arguments.
344
+
345
+ Returns:
346
+ --------
347
+ Dict
348
+ Filter data and associated parameters.
349
+ """
350
+ if data_rfft is None:
351
+ data_rfft = np.fft.rfftn(backend.to_numpy_array(data))
352
+
353
+ data_rfft = backend.to_numpy_array(data_rfft)
354
+
355
+ bins, radial_averages = self._compute_spectrum(
356
+ data_rfft, n_bins, batch_dimension
357
+ )
358
+
359
+ if order is None:
360
+ cutoff = bins < radial_averages.size
361
+ filter_mask = np.zeros(data_rfft.shape, radial_averages.dtype)
362
+ filter_mask[cutoff] = radial_averages[bins[cutoff]]
363
+ else:
364
+ filter_mask = self._interpolate_spectrum(
365
+ spectrum=radial_averages,
366
+ shape=data_rfft.shape,
367
+ shape_is_real_fourier=True,
368
+ )
369
+
370
+ filter_mask = np.fft.ifftshift(
371
+ filter_mask,
372
+ axes=tuple(i for i in range(data_rfft.ndim - 1) if i != batch_dimension)
373
+ )
374
+
375
+ return {
376
+ "data": backend.to_backend_array(filter_mask),
377
+ "is_multiplicative_filter": True,
378
+ }