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.
Files changed (52) hide show
  1. {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/match_template.py +219 -216
  2. {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/postprocess.py +86 -54
  3. pytme-0.2.3.data/scripts/preprocess.py +132 -0
  4. {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/preprocessor_gui.py +181 -94
  5. pytme-0.2.3.dist-info/METADATA +92 -0
  6. pytme-0.2.3.dist-info/RECORD +75 -0
  7. {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/WHEEL +1 -1
  8. pytme-0.2.1.data/scripts/preprocess.py → scripts/eval.py +1 -1
  9. scripts/extract_candidates.py +20 -13
  10. scripts/match_template.py +219 -216
  11. scripts/match_template_filters.py +154 -95
  12. scripts/postprocess.py +86 -54
  13. scripts/preprocess.py +95 -56
  14. scripts/preprocessor_gui.py +181 -94
  15. scripts/refine_matches.py +265 -61
  16. tme/__init__.py +0 -1
  17. tme/__version__.py +1 -1
  18. tme/analyzer.py +458 -813
  19. tme/backends/__init__.py +40 -11
  20. tme/backends/_jax_utils.py +187 -0
  21. tme/backends/cupy_backend.py +109 -226
  22. tme/backends/jax_backend.py +230 -152
  23. tme/backends/matching_backend.py +445 -384
  24. tme/backends/mlx_backend.py +32 -59
  25. tme/backends/npfftw_backend.py +240 -507
  26. tme/backends/pytorch_backend.py +30 -151
  27. tme/density.py +248 -371
  28. tme/extensions.cpython-311-darwin.so +0 -0
  29. tme/matching_data.py +328 -284
  30. tme/matching_exhaustive.py +195 -1499
  31. tme/matching_optimization.py +143 -106
  32. tme/matching_scores.py +887 -0
  33. tme/matching_utils.py +287 -388
  34. tme/memory.py +377 -0
  35. tme/orientations.py +78 -21
  36. tme/parser.py +3 -4
  37. tme/preprocessing/_utils.py +61 -32
  38. tme/preprocessing/composable_filter.py +7 -4
  39. tme/preprocessing/compose.py +7 -3
  40. tme/preprocessing/frequency_filters.py +49 -39
  41. tme/preprocessing/tilt_series.py +44 -72
  42. tme/preprocessor.py +560 -526
  43. tme/structure.py +491 -188
  44. tme/types.py +5 -3
  45. pytme-0.2.1.dist-info/METADATA +0 -73
  46. pytme-0.2.1.dist-info/RECORD +0 -73
  47. tme/helpers.py +0 -881
  48. tme/matching_constrained.py +0 -195
  49. {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/estimate_ram_usage.py +0 -0
  50. {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/LICENSE +0 -0
  51. {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/entry_points.txt +0 -0
  52. {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,5 @@
1
1
  #!python
2
- """ Simplify picking adequate filtering and masking parameters using a GUI.
3
- Exposes tme.preprocessor.Preprocessor and tme.fitter_utils member functions
4
- to achieve this aim.
2
+ """ GUI for identifying adequate template matching filter and masks.
5
3
 
6
4
  Copyright (c) 2023 European Molecular Biology Laboratory
7
5
 
@@ -12,17 +10,20 @@ import argparse
12
10
  from typing import Tuple, Callable, List
13
11
  from typing_extensions import Annotated
14
12
 
13
+ import napari
15
14
  import numpy as np
16
15
  import pandas as pd
17
- import napari
16
+ from scipy.fft import next_fast_len
18
17
  from napari.layers import Image
19
18
  from napari.utils.events import EventedList
20
-
21
19
  from magicgui import widgets
22
20
  from qtpy.QtWidgets import QFileDialog
23
21
  from numpy.typing import NDArray
24
22
 
23
+ from tme.backends import backend
25
24
  from tme import Preprocessor, Density
25
+ from tme.preprocessing import BandPassFilter
26
+ from tme.preprocessing.tilt_series import CTF
26
27
  from tme.matching_utils import create_mask, load_pickle
27
28
 
28
29
  preprocessor = Preprocessor()
@@ -35,19 +36,57 @@ def gaussian_filter(template: NDArray, sigma: float, **kwargs: dict) -> NDArray:
35
36
 
36
37
  def bandpass_filter(
37
38
  template: NDArray,
38
- minimum_frequency: float,
39
- maximum_frequency: float,
40
- gaussian_sigma: float,
41
- **kwargs: dict,
39
+ lowpass_angstrom: float = 30,
40
+ highpass_angstrom: float = 140,
41
+ hard_edges: bool = False,
42
+ sampling_rate=None,
42
43
  ) -> NDArray:
43
- return preprocessor.bandpass_filter(
44
- template=template,
45
- minimum_frequency=minimum_frequency,
46
- maximum_frequency=maximum_frequency,
47
- sampling_rate=1,
48
- gaussian_sigma=gaussian_sigma,
49
- **kwargs,
44
+ bpf = BandPassFilter(
45
+ lowpass=lowpass_angstrom,
46
+ highpass=highpass_angstrom,
47
+ sampling_rate=np.max(sampling_rate),
48
+ use_gaussian=not hard_edges,
49
+ shape_is_real_fourier=True,
50
+ return_real_fourier=True,
50
51
  )
52
+ template_ft = np.fft.rfftn(template, s=template.shape)
53
+
54
+ mask = bpf(shape=template_ft.shape)["data"]
55
+ np.multiply(template_ft, mask, out=template_ft)
56
+ return np.fft.irfftn(template_ft, s=template.shape).real
57
+
58
+
59
+ def ctf_filter(
60
+ template: NDArray,
61
+ defocus_angstrom: float = 30000,
62
+ acceleration_voltage: float = 300,
63
+ spherical_aberration: float = 2.7,
64
+ amplitude_contrast: float = 0.07,
65
+ phase_shift: float = 0,
66
+ defocus_angle: float = 0,
67
+ sampling_rate=None,
68
+ flip_phase: bool = False,
69
+ ) -> NDArray:
70
+ fast_shape = [next_fast_len(x) for x in np.multiply(template.shape, 2)]
71
+ template_pad = backend.topleft_pad(template, fast_shape)
72
+ template_ft = np.fft.rfftn(template_pad, s=template_pad.shape)
73
+ ctf = CTF(
74
+ angles=[0],
75
+ shape=fast_shape,
76
+ defocus_x=[defocus_angstrom],
77
+ acceleration_voltage=acceleration_voltage * 1e3,
78
+ spherical_aberration=spherical_aberration * 1e7,
79
+ amplitude_contrast=amplitude_contrast,
80
+ phase_shift=[phase_shift],
81
+ defocus_angle=[defocus_angle],
82
+ sampling_rate=np.max(sampling_rate),
83
+ return_real_fourier=True,
84
+ flip_phase=flip_phase,
85
+ )
86
+ np.multiply(template_ft, ctf()["data"], out=template_ft)
87
+ template_pad = np.fft.irfftn(template_ft, s=template_pad.shape).real
88
+ template = backend.topleft_pad(template_pad, template.shape)
89
+ return template
51
90
 
52
91
 
53
92
  def difference_of_gaussian_filter(
@@ -93,14 +132,6 @@ def local_gaussian_filter(
93
132
  )
94
133
 
95
134
 
96
- def ntree(
97
- template: NDArray,
98
- sigma_range: Tuple[float, float],
99
- **kwargs: dict,
100
- ) -> NDArray:
101
- return preprocessor.ntree_filter(template=template, sigma_range=sigma_range)
102
-
103
-
104
135
  def mean(
105
136
  template: NDArray,
106
137
  width: int,
@@ -109,61 +140,6 @@ def mean(
109
140
  return preprocessor.mean_filter(template=template, width=width)
110
141
 
111
142
 
112
- def resolution_sphere(
113
- template: NDArray,
114
- cutoff_angstrom: float,
115
- highpass: bool = False,
116
- sampling_rate=None,
117
- ) -> NDArray:
118
- if cutoff_angstrom == 0:
119
- return template
120
-
121
- cutoff_frequency = np.max(2 * sampling_rate / cutoff_angstrom)
122
-
123
- min_freq, max_freq = 0, cutoff_frequency
124
- if highpass:
125
- min_freq, max_freq = cutoff_frequency, 1e10
126
-
127
- mask = preprocessor.bandpass_mask(
128
- shape=template.shape,
129
- minimum_frequency=min_freq,
130
- maximum_frequency=max_freq,
131
- omit_negative_frequencies=False,
132
- )
133
-
134
- template_ft = np.fft.fftn(template)
135
- np.multiply(template_ft, mask, out=template_ft)
136
- return np.fft.ifftn(template_ft).real
137
-
138
-
139
- def resolution_gaussian(
140
- template: NDArray,
141
- cutoff_angstrom: float,
142
- highpass: bool = False,
143
- sampling_rate=None,
144
- ) -> NDArray:
145
- if cutoff_angstrom == 0:
146
- return template
147
-
148
- grid = preprocessor.fftfreqn(
149
- shape=template.shape, sampling_rate=sampling_rate / sampling_rate.max()
150
- )
151
-
152
- sigma_fourier = np.divide(
153
- np.max(2 * sampling_rate / cutoff_angstrom), np.sqrt(2 * np.log(2))
154
- )
155
-
156
- mask = np.exp(-(grid**2) / (2 * sigma_fourier**2))
157
- if highpass:
158
- mask = 1 - mask
159
-
160
- mask = np.fft.ifftshift(mask)
161
-
162
- template_ft = np.fft.fftn(template)
163
- np.multiply(template_ft, mask, out=template_ft)
164
- return np.fft.ifftn(template_ft).real
165
-
166
-
167
143
  def wedge(
168
144
  template: NDArray,
169
145
  tilt_start: float,
@@ -213,6 +189,10 @@ def compute_power_spectrum(template: NDArray) -> NDArray:
213
189
  return np.fft.fftshift(np.log(np.abs(np.fft.fftn(template))))
214
190
 
215
191
 
192
+ def invert_contrast(template: NDArray) -> NDArray:
193
+ return template * -1
194
+
195
+
216
196
  def widgets_from_function(function: Callable, exclude_params: List = ["self"]):
217
197
  """
218
198
  Creates list of magicui widgets by inspecting function typing ann
@@ -268,14 +248,13 @@ WRAPPED_FUNCTIONS = {
268
248
  "gaussian_filter": gaussian_filter,
269
249
  "bandpass_filter": bandpass_filter,
270
250
  "edge_gaussian_filter": edge_gaussian_filter,
271
- "ntree_filter": ntree,
272
251
  "local_gaussian_filter": local_gaussian_filter,
273
252
  "difference_of_gaussian_filter": difference_of_gaussian_filter,
274
253
  "mean_filter": mean,
275
254
  "wedge_filter": wedge,
276
255
  "power_spectrum": compute_power_spectrum,
277
- "resolution_gaussian": resolution_gaussian,
278
- "resolution_sphere": resolution_sphere,
256
+ "ctf": ctf_filter,
257
+ "invert_contrast": invert_contrast,
279
258
  }
280
259
 
281
260
  EXCLUDED_FUNCTIONS = [
@@ -421,6 +400,7 @@ def sphere_mask(
421
400
  center_y: float,
422
401
  center_z: float,
423
402
  radius: float,
403
+ sigma_decay: float = 0,
424
404
  **kwargs,
425
405
  ) -> NDArray:
426
406
  return create_mask(
@@ -428,6 +408,7 @@ def sphere_mask(
428
408
  shape=template.shape,
429
409
  center=(center_x, center_y, center_z),
430
410
  radius=radius,
411
+ sigma_decay=sigma_decay,
431
412
  )
432
413
 
433
414
 
@@ -439,6 +420,7 @@ def ellipsod_mask(
439
420
  radius_x: float,
440
421
  radius_y: float,
441
422
  radius_z: float,
423
+ sigma_decay: float = 0,
442
424
  **kwargs,
443
425
  ) -> NDArray:
444
426
  return create_mask(
@@ -446,6 +428,7 @@ def ellipsod_mask(
446
428
  shape=template.shape,
447
429
  center=(center_x, center_y, center_z),
448
430
  radius=(radius_x, radius_y, radius_z),
431
+ sigma_decay=sigma_decay,
449
432
  )
450
433
 
451
434
 
@@ -457,6 +440,7 @@ def box_mask(
457
440
  height_x: int,
458
441
  height_y: int,
459
442
  height_z: int,
443
+ sigma_decay: float = 0,
460
444
  **kwargs,
461
445
  ) -> NDArray:
462
446
  return create_mask(
@@ -464,6 +448,7 @@ def box_mask(
464
448
  shape=template.shape,
465
449
  center=(center_x, center_y, center_z),
466
450
  height=(height_x, height_y, height_z),
451
+ sigma_decay=sigma_decay,
467
452
  )
468
453
 
469
454
 
@@ -476,6 +461,7 @@ def tube_mask(
476
461
  inner_radius: float,
477
462
  outer_radius: float,
478
463
  height: int,
464
+ sigma_decay: float = 0,
479
465
  **kwargs,
480
466
  ) -> NDArray:
481
467
  return create_mask(
@@ -486,6 +472,7 @@ def tube_mask(
486
472
  inner_radius=inner_radius,
487
473
  outer_radius=outer_radius,
488
474
  height=height,
475
+ sigma_decay=sigma_decay,
489
476
  )
490
477
 
491
478
 
@@ -533,13 +520,23 @@ def wedge_mask(
533
520
 
534
521
 
535
522
  def threshold_mask(
536
- template: NDArray, standard_deviation: float = 5.0, invert: bool = False, **kwargs
523
+ template: NDArray,
524
+ invert: bool = False,
525
+ standard_deviation: float = 5.0,
526
+ sigma: float = 0.0,
527
+ **kwargs,
537
528
  ) -> NDArray:
538
529
  template_mean = template.mean()
539
530
  template_deviation = standard_deviation * template.std()
540
531
  upper = template_mean + template_deviation
541
532
  lower = template_mean - template_deviation
542
- mask = np.logical_and(template > lower, template < upper)
533
+ mask = np.logical_or(template <= lower, template >= upper)
534
+
535
+ if sigma != 0:
536
+ mask_filter = preprocessor.gaussian_filter(template=mask * 1.0, sigma=sigma)
537
+ mask = np.add(mask, (1 - mask) * mask_filter)
538
+ mask[mask < np.exp(-np.square(sigma))] = 0
539
+
543
540
  if invert:
544
541
  np.invert(mask, out=mask)
545
542
 
@@ -633,6 +630,7 @@ class MaskWidget(widgets.Container):
633
630
 
634
631
  data = active_layer.data.copy()
635
632
  cutoff = np.quantile(data, self.percentile_range_edit.value / 100)
633
+ cutoff = max(cutoff, np.finfo(np.float32).resolution)
636
634
  data[data < cutoff] = 0
637
635
 
638
636
  center_of_mass = Density.center_of_mass(np.abs(data), 0)
@@ -890,6 +888,7 @@ class PointCloudWidget(widgets.Container):
890
888
 
891
889
  self.viewer = viewer
892
890
  self.dataframes = {}
891
+ self.selected_category = -1
893
892
 
894
893
  self.import_button = widgets.PushButton(
895
894
  name="Import", text="Import Point Cloud"
@@ -902,10 +901,98 @@ class PointCloudWidget(widgets.Container):
902
901
  self.export_button.clicked.connect(self._export_point_cloud)
903
902
  self.export_button.enabled = False
904
903
 
904
+ self.annotation_container = widgets.Container(name="Label", layout="horizontal")
905
+ self.positive_button = widgets.PushButton(name="Positive", text="Positive")
906
+ self.negative_button = widgets.PushButton(name="Negative", text="Negative")
907
+ self.positive_button.clicked.connect(self._set_positive)
908
+ self.negative_button.clicked.connect(self._set_negative)
909
+ self.annotation_container.append(self.positive_button)
910
+ self.annotation_container.append(self.negative_button)
911
+
912
+ self.face_color_select = widgets.ComboBox(
913
+ name="Color", choices=["Label", "Score"], value=None, nullable=True
914
+ )
915
+ self.face_color_select.changed.connect(self._update_face_color_mode)
916
+
905
917
  self.append(self.import_button)
906
918
  self.append(self.export_button)
919
+ self.append(self.annotation_container)
920
+ self.append(self.face_color_select)
921
+
907
922
  self.viewer.layers.selection.events.changed.connect(self._update_buttons)
908
923
 
924
+ self.viewer.layers.events.inserted.connect(self._initialize_points_layer)
925
+
926
+ def _update_face_color_mode(self, event: str = None):
927
+ for layer in self.viewer.layers:
928
+ if not isinstance(layer, napari.layers.Points):
929
+ continue
930
+
931
+ layer.face_color = "white"
932
+ if event == "Label":
933
+ if len(layer.properties.get("detail", ())) == 0:
934
+ continue
935
+ layer.face_color = "detail"
936
+ layer.face_color_cycle = {
937
+ -1: "grey",
938
+ 0: "red",
939
+ 1: "green",
940
+ }
941
+ layer.face_color_mode = "cycle"
942
+ elif event == "Score":
943
+ if len(layer.properties.get("score_scaled", ())) == 0:
944
+ continue
945
+ layer.face_color = "score_scaled"
946
+ layer.face_colormap = "turbo"
947
+ layer.face_color_mode = "colormap"
948
+
949
+ layer.refresh_colors()
950
+
951
+ return None
952
+
953
+ def _set_positive(self, event):
954
+ self.selected_category = 1 if self.selected_category != 1 else -1
955
+ self._update_annotation_buttons()
956
+
957
+ def _set_negative(self, event):
958
+ self.selected_category = 0 if self.selected_category != 0 else -1
959
+ self._update_annotation_buttons()
960
+
961
+ def _update_annotation_buttons(self):
962
+ selected_style = "background-color: darkgrey"
963
+ default_style = "background-color: none"
964
+
965
+ self.positive_button.native.setStyleSheet(
966
+ selected_style if self.selected_category == 1 else default_style
967
+ )
968
+ self.negative_button.native.setStyleSheet(
969
+ selected_style if self.selected_category == 0 else default_style
970
+ )
971
+
972
+ def _initialize_points_layer(self, event):
973
+ layer = event.value
974
+ if not isinstance(layer, napari.layers.Points):
975
+ return
976
+ if len(layer.properties) == 0:
977
+ layer.properties = {"detail": [-1]}
978
+
979
+ if "detail" not in layer.properties:
980
+ layer["detail"] = [-1]
981
+
982
+ layer.mouse_drag_callbacks.append(self._on_click)
983
+ return None
984
+
985
+ def _on_click(self, layer, event):
986
+ if layer.mode == "add":
987
+ layer.current_properties["detail"][-1] = self.selected_category
988
+ elif layer.mode == "select":
989
+ for index in layer.selected_data:
990
+ layer.properties["detail"][index] = self.selected_category
991
+
992
+ # TODO: Check whether current face color is the desired one already
993
+ self._update_face_color_mode(self.face_color_select.value)
994
+ layer.refresh_colors()
995
+
909
996
  def _update_buttons(self, event):
910
997
  is_pointcloud = isinstance(
911
998
  self.viewer.layers.selection.active, napari.layers.Points
@@ -951,9 +1038,7 @@ class PointCloudWidget(widgets.Container):
951
1038
 
952
1039
  if "score" in merged_data.columns:
953
1040
  merged_data["score"] = merged_data["score"].fillna(1)
954
- if "detail" in merged_data.columns:
955
- merged_data["detail"] = merged_data["detail"].fillna(2)
956
-
1041
+ merged_data["detail"] = layer.properties["detail"]
957
1042
  merged_data.to_csv(filename, sep="\t", index=False)
958
1043
 
959
1044
  def _get_load_path(self, event):
@@ -977,7 +1062,7 @@ class PointCloudWidget(widgets.Container):
977
1062
  dataframe["score"] = 1
978
1063
 
979
1064
  if "detail" not in dataframe.columns:
980
- dataframe["detail"] = -2
1065
+ dataframe["detail"] = -1
981
1066
 
982
1067
  point_properties = {
983
1068
  "score": np.array(dataframe["score"].values),
@@ -991,8 +1076,6 @@ class PointCloudWidget(widgets.Container):
991
1076
  points,
992
1077
  size=10,
993
1078
  properties=point_properties,
994
- face_color="score_scaled",
995
- face_colormap="turbo",
996
1079
  name=layer_name,
997
1080
  )
998
1081
  self.dataframes[layer_name] = dataframe
@@ -1025,9 +1108,14 @@ class MatchingWidget(widgets.Container):
1025
1108
  def _load_data(self, filename):
1026
1109
  data = load_pickle(filename)
1027
1110
 
1028
- _ = self.viewer.add_image(data=data[2], name="Rotations", colormap="orange")
1111
+ metadata = {"origin": data[-1][1], "sampling_rate": data[-1][2]}
1112
+ _ = self.viewer.add_image(
1113
+ data=data[2], name="Rotations", colormap="orange", metadata=metadata
1114
+ )
1029
1115
 
1030
- _ = self.viewer.add_image(data=data[0], name="Scores", colormap="turbo")
1116
+ _ = self.viewer.add_image(
1117
+ data=data[0], name="Scores", colormap="turbo", metadata=metadata
1118
+ )
1031
1119
 
1032
1120
 
1033
1121
  def main():
@@ -1045,11 +1133,10 @@ def main():
1045
1133
  widget=alignment_widget, name="Alignment", area="right"
1046
1134
  )
1047
1135
  viewer.window.add_dock_widget(widget=mask_widget, name="Mask", area="right")
1136
+ viewer.window.add_dock_widget(widget=export_widget, name="Export", area="right")
1048
1137
  viewer.window.add_dock_widget(widget=point_cloud, name="PointCloud", area="left")
1049
1138
  viewer.window.add_dock_widget(widget=matching_widget, name="Matching", area="left")
1050
1139
 
1051
- viewer.window.add_dock_widget(widget=export_widget, name="Export", area="right")
1052
-
1053
1140
  napari.run()
1054
1141
 
1055
1142
 
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.1
2
+ Name: pytme
3
+ Version: 0.2.3
4
+ Summary: Python Template Matching Engine
5
+ Author: Valentin Maurer
6
+ Author-email: Valentin Maurer <valentin.maurer@embl-hamburg.de>
7
+ License: Proprietary
8
+ Project-URL: Homepage, https://github.com/KosinskiLab/pyTME
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: mrcfile >=1.4.3
15
+ Requires-Dist: numpy <2.0,>=1.22.2
16
+ Requires-Dist: pyfftw >=0.13.1
17
+ Requires-Dist: pytest >=6.2.5
18
+ Requires-Dist: PyYAML >=6.0
19
+ Requires-Dist: scikit-image >=0.19.0
20
+ Requires-Dist: scikit-learn >=1.2.1
21
+ Requires-Dist: scipy >=1.9.1
22
+ Requires-Dist: pybind11
23
+ Requires-Dist: psutil
24
+ Requires-Dist: tifffile
25
+ Requires-Dist: h5py
26
+ Provides-Extra: all
27
+ Requires-Dist: cupy ; extra == 'all'
28
+ Requires-Dist: voltools ==0.6.0 ; extra == 'all'
29
+ Requires-Dist: torch ; extra == 'all'
30
+ Requires-Dist: jax ; extra == 'all'
31
+ Requires-Dist: jaxlib ; extra == 'all'
32
+ Provides-Extra: cupy
33
+ Requires-Dist: cupy-cuda12x ; extra == 'cupy'
34
+ Requires-Dist: voltools ==0.6.0 ; extra == 'cupy'
35
+ Provides-Extra: jax
36
+ Requires-Dist: jax ; extra == 'jax'
37
+ Requires-Dist: jaxlib ; extra == 'jax'
38
+ Provides-Extra: pytorch
39
+ Requires-Dist: torch ; extra == 'pytorch'
40
+ Requires-Dist: torchvision ; extra == 'pytorch'
41
+
42
+ # Python Template Matching Engine (PyTME)
43
+
44
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/KosinskiLab/pyTME/main.yml?label=CI)](https://github.com/KosinskiLab/pyTME/actions)
45
+ [![PyPI](https://img.shields.io/pypi/v/pytme.svg)](https://pypi.org/project/pytme/)
46
+
47
+ **[Documentation](https://kosinskilab.github.io/pyTME/)** | **[Installation](https://kosinskilab.github.io/pyTME/quickstart/installation.html)** | **[API](https://kosinskilab.github.io/pyTME/reference/index.html)**
48
+
49
+ PyTME is a Python library for data-intensive n-dimensional template matching using CPUs and GPUs.
50
+
51
+ With its [backend-agnostic design](https://kosinskilab.github.io/pyTME/reference/backends.html), the same code can be run on diverse hardware platforms using a best-of-breed approach. The underyling abstract backend specification allows for adding new backends to benefit from gains in performance and capabilities without modifying the library's core routines. The implementation of template matching scores is modular and provides developers with a flexible framework for rapid prototyping. Furthermore, pyTME supports a unique callback capability through [analyzers](https://kosinskilab.github.io/pyTME/reference/analyzer.html), which allows for injection of custom code, enabling real-time processing and manipulation of results.
52
+
53
+ PyTME includes a [graphical user interface](https://kosinskilab.github.io/pyTME/preprocessing/gui_example.html) that provides simplified mask creation, interactive filter exploration, result visualization, and manual refinement capabilities. This GUI serves as an accessible entry point to the library's core functionalities, allowing users to efficiently interact with and analyze their data.
54
+
55
+ Finally, pyTME offers specialized tools for cryogenic electron microscopy data, such as wedge masks, CTF correction, as well as [means for handling structural data](https://kosinskilab.github.io/pyTME/reference/data_structure.html). Through dedicated [integrations](https://kosinskilab.github.io/pyTME/quickstart/integrations.html), the output of pyTME seamlessly integrates with commonly used cryogenic electron microscopy software such as RELION, Dynamo and IMOD.
56
+
57
+ Running into bugs or missing a feature? Help us improve the project by opening an [issue](https://github.com/KosinskiLab/pyTME/issues).
58
+
59
+ ## Installation
60
+
61
+ We recommend installation using one of the following methods
62
+
63
+ | Method | Command |
64
+ |----------|---------------------------------------------------------|
65
+ | PyPi | `pip install pytme` |
66
+ | Source | `pip install git+https://github.com/KosinskiLab/pyTME` |
67
+ | Docker | `docker build -t pytme -f docker/Dockerfile_GPU .` |
68
+
69
+ You can find alternative installation methods in the [documentation](https://kosinskilab.github.io/pyTME/quickstart/installation.html).
70
+
71
+
72
+ ## User Guide
73
+
74
+ Learn how to get started with
75
+
76
+ - [Preprocessing:](https://kosinskilab.github.io/pyTME/quickstart/preprocessing/motivation.html) Picking the right mask and filter for template matching.
77
+ - [Template matching:](https://kosinskilab.github.io/pyTME/quickstart/matching/motivation.html) Find your template of interest.
78
+ - [Postprocessing](https://kosinskilab.github.io/pyTME/quickstart/postprocessing/motivation.html) Analyze template matching results and downstream integrations.
79
+
80
+ ## How to Cite
81
+
82
+ If pyTME contributed significantly to your research, please cite the corresponding publication on [SoftwareX](https://www.sciencedirect.com/science/article/pii/S2352711024000074).
83
+
84
+ ```bibtex
85
+ @article{Maurer:2024aa,
86
+ author = {Maurer, Valentin J. and Siggel, Marc and Kosinski, Jan},
87
+ journal = {SoftwareX},
88
+ pages = {101636},
89
+ title = {PyTME (Python Template Matching Engine): A fast, flexible, and multi-purpose template matching library for cryogenic electron microscopy data},
90
+ volume = {25},
91
+ year = {2024}}
92
+ ```
@@ -0,0 +1,75 @@
1
+ pytme-0.2.3.data/scripts/estimate_ram_usage.py,sha256=R1NDpFajcF-MonJ4a43SfDlA-nxBYwK7D2quzCdsVFM,2767
2
+ pytme-0.2.3.data/scripts/match_template.py,sha256=JJbZjxSdggKBHhEy-5lRwI6RslNzLAmZ9RPqwAnhkys,39750
3
+ pytme-0.2.3.data/scripts/postprocess.py,sha256=50PwDfOWe2Fdws4J5K-k2SgM55fARlAWCnIsv-l0i-4,24414
4
+ pytme-0.2.3.data/scripts/preprocess.py,sha256=fJMVyHJwiXorr8P2L47bVWSkduJYS3H5r-zrQpp4jjA,3782
5
+ pytme-0.2.3.data/scripts/preprocessor_gui.py,sha256=mlxh-5GNaN7quEbd_Wso0R4gJ7DOJioL3rio2CCXuqw,39239
6
+ scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ scripts/estimate_ram_usage.py,sha256=rN7haobnHg3YcgGJIp81FNiCzy8-saJGeEurQlmQmNQ,2768
8
+ scripts/eval.py,sha256=ebJVLxbRlB6TI5YHNr0VavZ4lmaRdf8QVafyiDhh_oU,2528
9
+ scripts/extract_candidates.py,sha256=DAfNyuauogWvSdRWIbtH44tsk9buLn13JrL1zJjJGLE,8373
10
+ scripts/match_template.py,sha256=p4iS2_bp3ZdSgf0jB1t0SOojgCj-0KfwgRXHnLmfAHE,39751
11
+ scripts/match_template_filters.py,sha256=Gj4a1b_S5NWp_dfFEPFn0D7jGf-qYgBbnTvZZ4bwqOQ,42036
12
+ scripts/postprocess.py,sha256=FdA4AZgsbz8enYT6q46akWX4O5Hdxrun4PUdomYhrPA,24415
13
+ scripts/preprocess.py,sha256=kZQd8SyMyLpHJzj59qSxtjFg2GMdhRuc5OQqoqj8Nh8,3783
14
+ scripts/preprocessor_gui.py,sha256=_UaZyUViI8Xj-IDw18xjnyolB3ROMTFoLS-3Wqm7Uh0,39240
15
+ scripts/refine_matches.py,sha256=Y17Ku_t0W9vglPNF2oU5EFrqoedJIm3lCGl-hXRHvjc,21920
16
+ tme/__init__.py,sha256=liARhKZ565F2Y02iWRbFzfrTCkL5You3YaUSiqUkees,202
17
+ tme/__version__.py,sha256=PNiDER4qM19h9zdsdfgKt2_dT4WgYK7EguJ8RU2qA_g,22
18
+ tme/analyzer.py,sha256=kYxtzBCp02OJrlMME8Njh-9wIZAvsuhTHNAveNplssY,50466
19
+ tme/density.py,sha256=BRM2jCDLvKDWDyzKhfc_APB4k398m9G5SnwFD1lRq8I,84204
20
+ tme/extensions.cpython-311-darwin.so,sha256=KM0UcTYdq0Gib8rC6yVt7KnXrVvPNcWS-cvuYW2aPso,392496
21
+ tme/matching_data.py,sha256=nEKG3DMwHwLjcO08sQUp6FiIkOLwtXS_36Y557nOvJQ,25319
22
+ tme/matching_exhaustive.py,sha256=D5r53AyGNnGGsRgiNRo3L7YCrMeVCAgjlEurzDbLCv4,19483
23
+ tme/matching_memory.py,sha256=bmCAUYyXWEet-1XXhldtc0irio2ytMSsAzWYyFI5LNM,11273
24
+ tme/matching_optimization.py,sha256=Y8HfecXiOvAHXM1viBaQ_aXljqqTnGwlOlFe0MJpDRQ,45082
25
+ tme/matching_scores.py,sha256=CECxl2Lh0TMLfZYnoCJXy3euGf8i9J0eHsAD7sqvWGU,30962
26
+ tme/matching_utils.py,sha256=nh4D090eIrTO2d_Tjyr7FnLrTx3hRXie9mt5zJUFsZ4,39998
27
+ tme/memory.py,sha256=6xeIMAncQkgYDi6w-PIYgFEWRTUPu0_OTCeRO0p9r9Q,11029
28
+ tme/orientations.py,sha256=KsYXJuLRLYXRHsDjP9_Tn1jXxIVPSaYkw1wRrWH3nUQ,26027
29
+ tme/parser.py,sha256=fNiCAdsWI4ql92F1Ob4suiVzpjUOBlh2lad1iNY_FP8,13772
30
+ tme/preprocessor.py,sha256=ZhlP-8b4NzAzRogWOnk5DLNh_tqCMOpc4msQIJEKBTU,48566
31
+ tme/structure.py,sha256=k5kgFNHkM2mUvXS7VAwaevltLC1mUxEiDhMW0TqTNjo,65834
32
+ tme/types.py,sha256=NAY7C4qxE6yz-DXVtClMvFfoOV-spWGLNfpLATZ1LcU,442
33
+ tme/backends/__init__.py,sha256=4S68W2WJNZ9t33QSrRs6aL3OIyEVFo_zVsqXjS1iWYA,5185
34
+ tme/backends/_jax_utils.py,sha256=YuNJHCYnSqOESMV-9LPr-ZxBg6Zvax2euBjsZM-j-64,5906
35
+ tme/backends/cupy_backend.py,sha256=1nnCJ4nT7tJsXu1mrJGCy7x0Yg1wWVRg4SdzsQ2qiiw,9284
36
+ tme/backends/jax_backend.py,sha256=qRUQ6-j1gSUCeYDgla2qAO3ptnuCUar8uXC68B2lQ7Y,10344
37
+ tme/backends/matching_backend.py,sha256=KfCOKD_rA9el3Y7BeH17KJ1apCUIIhhvn-vmbkb3CB0,33750
38
+ tme/backends/mlx_backend.py,sha256=FJhqmCzgjXAjWGX1HhHFrCy_We4YwQQBkKFNG05ctzM,7788
39
+ tme/backends/npfftw_backend.py,sha256=JDTc_1QcVi9jU3yLQF7jkgwQz_Po60OhkKuV2V3g5v8,16997
40
+ tme/backends/pytorch_backend.py,sha256=61cAu8HBtGPDL8vSJx49f6yBxkLoWiX0X49Dy2tHAuk,15038
41
+ tme/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ tme/data/c48n309.npy,sha256=NwH64mOEbm3tStq5c98o81fY1vMOoq4nvXDAh7Z7iZg,296768
43
+ tme/data/c48n527.npy,sha256=saSUMTa1R0MisPvgFL02a7IHQSwEZ-mJu0v3qJjg5AU,506048
44
+ tme/data/c48n9.npy,sha256=bDVLV6mWjZHSQfeDc-MOCKKarfc1jaNeVvpoe2xMUy4,8768
45
+ tme/data/c48u1.npy,sha256=JeXMFzFITs2ezdc3x5lp3jo1cHHHHVADSA1Tpf77kXs,1088
46
+ tme/data/c48u1153.npy,sha256=ECiEximtYDWtIux3Fwe_EJlyn08gUqP85DN9gjkT9_k,1107008
47
+ tme/data/c48u1201.npy,sha256=aceC_Jeienz_81X4520nPpZcg5tnRhbW795EqbpWkrg,1153088
48
+ tme/data/c48u1641.npy,sha256=p4LwW3LzdTjrUUpA7H53RfNWxYfPX0XjeSwZ39Ac78Q,1575488
49
+ tme/data/c48u181.npy,sha256=mLYXrv1YHLH6DsBp5MkxHkxlxgMnj1mw_KKI0udH-FY,173888
50
+ tme/data/c48u2219.npy,sha256=p8TQeX8YHu4pdxnwJjEAlQWAPa66W7kpK96iZKZr9JE,2130368
51
+ tme/data/c48u27.npy,sha256=k03ZNEsoPwBKCy8IeIa5G0WRZqjGZMtX6Ibu7EpJHvU,26048
52
+ tme/data/c48u2947.npy,sha256=icI97ED6ct66y7FIaJAugmjzrIWk7CINCxtO3wDTnrU,2829248
53
+ tme/data/c48u3733.npy,sha256=tla-__Pf-hpN6h04vtFIfkkFdCLple11VO06kr1dXkM,3583808
54
+ tme/data/c48u4749.npy,sha256=tItOA4oV7SiqCCREwz3fyEpZoxM0lCq_jfEo5_-fp2s,4559168
55
+ tme/data/c48u5879.npy,sha256=bFk89MllIFCX_sLXTYWFquSyN1NuahH4wwnEsPJLxzA,5643968
56
+ tme/data/c48u7111.npy,sha256=CMy9kI2edH-q9eTIVdgUtXurplYNI7Uqp4dXfkkVdf8,6826688
57
+ tme/data/c48u815.npy,sha256=bCuJxLtm0Sjg3GGxtyjGzRYZ1G0Gz79XHI-71GvqQnI,782528
58
+ tme/data/c48u83.npy,sha256=7ODJYnsiuDjGbgd9GFopsyIW2IjrYI0J2X2f-cK868U,79808
59
+ tme/data/c48u8649.npy,sha256=-IPlpR4zrPQZWhhSPu4zEulFdrCEVgTMFffCB5d-huE,8303168
60
+ tme/data/c600v.npy,sha256=JqSu3ALoL1A9iguehc0YGUMFPsh2fprHHp76VXeFXIw,2528
61
+ tme/data/c600vc.npy,sha256=Yht-GFXDSjjGvsjFBvyxxEZAI-ODADPd5gEgFNZQVTA,14528
62
+ tme/data/metadata.yaml,sha256=fAgX-mEzB0QMHTEtYDG4cSMbJhYxBbDJH3sdvJvL7a8,750
63
+ tme/data/quat_to_numpy.py,sha256=-gkDZb10fKBxwfYrSLCUWvMB76TzZWELCeKsYProwws,1333
64
+ tme/preprocessing/__init__.py,sha256=7O3vDzJcIfxovJkf7avWSPtzaIVlTbmsW7egQFukC_s,98
65
+ tme/preprocessing/_utils.py,sha256=hC8Qfh_a_fk4o7v82c4-TZYYRFfo5XttcghvpkzBo0A,6193
66
+ tme/preprocessing/composable_filter.py,sha256=dOE1WDzF3xc-Chv63PWSiW3Gq4wXyAbvNvKuy-lERL8,780
67
+ tme/preprocessing/compose.py,sha256=tilp14UhBh98SJVJhC_STbgi7i8HeU4FB7B7KUOD8gc,1458
68
+ tme/preprocessing/frequency_filters.py,sha256=x900ntX0QI46lDdycPISO-7K0XgM0015ksCOFlm-yM4,12964
69
+ tme/preprocessing/tilt_series.py,sha256=PFzWuXUn-uTqj2gwt_y5IV2Cte1IcL-Ule26LYORe9M,33844
70
+ pytme-0.2.3.dist-info/LICENSE,sha256=K1IUNSVAz8BXbpH5EA8y5FpaHdvFXnAF2zeK95Lr2bY,18467
71
+ pytme-0.2.3.dist-info/METADATA,sha256=C6HajoocJkveHyDTm01adKSZjEIgI6AbYQEMTXuAhfE,5278
72
+ pytme-0.2.3.dist-info/WHEEL,sha256=IHsX_ZtCj0uMf8jtN3eK3zfXfu1SfKRsLbDhsprnhR0,109
73
+ pytme-0.2.3.dist-info/entry_points.txt,sha256=ff3LQL3FCWfCYOwFiP9zatm7laUbnwCkuPELkQVyUO4,241
74
+ pytme-0.2.3.dist-info/top_level.txt,sha256=J8FUkazOb2fZ0n_KexnqCGyNOtie2bwisFSUBiM5-0w,12
75
+ pytme-0.2.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (72.1.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp311-cp311-macosx_14_0_arm64
5
5
 
@@ -1,4 +1,4 @@
1
- #!python
1
+ #!python3
2
2
  """ Apply tme.preprocessor.Preprocessor methods to an input file based
3
3
  on a provided yaml configuration obtaiend from preprocessor_gui.py.
4
4