pytme 0.2.0b0__cp311-cp311-macosx_14_0_arm64.whl → 0.2.2__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.2.data/scripts/match_template.py +1187 -0
- {pytme-0.2.0b0.data → pytme-0.2.2.data}/scripts/postprocess.py +170 -71
- {pytme-0.2.0b0.data → pytme-0.2.2.data}/scripts/preprocessor_gui.py +179 -86
- pytme-0.2.2.dist-info/METADATA +91 -0
- pytme-0.2.2.dist-info/RECORD +74 -0
- {pytme-0.2.0b0.dist-info → pytme-0.2.2.dist-info}/WHEEL +1 -1
- scripts/extract_candidates.py +126 -87
- scripts/match_template.py +596 -209
- scripts/match_template_filters.py +571 -223
- scripts/postprocess.py +170 -71
- scripts/preprocessor_gui.py +179 -86
- scripts/refine_matches.py +567 -159
- tme/__init__.py +0 -1
- tme/__version__.py +1 -1
- tme/analyzer.py +627 -855
- tme/backends/__init__.py +41 -11
- tme/backends/_jax_utils.py +185 -0
- tme/backends/cupy_backend.py +120 -225
- tme/backends/jax_backend.py +282 -0
- tme/backends/matching_backend.py +464 -388
- tme/backends/mlx_backend.py +45 -68
- tme/backends/npfftw_backend.py +256 -514
- tme/backends/pytorch_backend.py +41 -154
- tme/density.py +312 -421
- tme/extensions.cpython-311-darwin.so +0 -0
- tme/matching_data.py +366 -303
- tme/matching_exhaustive.py +279 -1521
- tme/matching_optimization.py +234 -129
- tme/matching_scores.py +884 -0
- tme/matching_utils.py +281 -387
- tme/memory.py +377 -0
- tme/orientations.py +226 -66
- tme/parser.py +3 -4
- tme/preprocessing/__init__.py +2 -0
- tme/preprocessing/_utils.py +217 -0
- tme/preprocessing/composable_filter.py +31 -0
- tme/preprocessing/compose.py +55 -0
- tme/preprocessing/frequency_filters.py +388 -0
- tme/preprocessing/tilt_series.py +1011 -0
- tme/preprocessor.py +574 -530
- tme/structure.py +495 -189
- tme/types.py +5 -3
- pytme-0.2.0b0.data/scripts/match_template.py +0 -800
- pytme-0.2.0b0.dist-info/METADATA +0 -73
- pytme-0.2.0b0.dist-info/RECORD +0 -66
- tme/helpers.py +0 -881
- tme/matching_constrained.py +0 -195
- {pytme-0.2.0b0.data → pytme-0.2.2.data}/scripts/estimate_ram_usage.py +0 -0
- {pytme-0.2.0b0.data → pytme-0.2.2.data}/scripts/preprocess.py +0 -0
- {pytme-0.2.0b0.dist-info → pytme-0.2.2.dist-info}/LICENSE +0 -0
- {pytme-0.2.0b0.dist-info → pytme-0.2.2.dist-info}/entry_points.txt +0 -0
- {pytme-0.2.0b0.dist-info → pytme-0.2.2.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,5 @@
|
|
1
1
|
#!python
|
2
|
-
"""
|
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
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
lowpass_angstrom: float = 30,
|
40
|
+
highpass_angstrom: float = 140,
|
41
|
+
hard_edges: bool = False,
|
42
|
+
sampling_rate=None,
|
42
43
|
) -> NDArray:
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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,
|
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,
|
50
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(
|
@@ -109,61 +148,6 @@ def mean(
|
|
109
148
|
return preprocessor.mean_filter(template=template, width=width)
|
110
149
|
|
111
150
|
|
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
151
|
def wedge(
|
168
152
|
template: NDArray,
|
169
153
|
tilt_start: float,
|
@@ -274,8 +258,7 @@ WRAPPED_FUNCTIONS = {
|
|
274
258
|
"mean_filter": mean,
|
275
259
|
"wedge_filter": wedge,
|
276
260
|
"power_spectrum": compute_power_spectrum,
|
277
|
-
"
|
278
|
-
"resolution_sphere": resolution_sphere,
|
261
|
+
"ctf": ctf_filter,
|
279
262
|
}
|
280
263
|
|
281
264
|
EXCLUDED_FUNCTIONS = [
|
@@ -421,6 +404,7 @@ def sphere_mask(
|
|
421
404
|
center_y: float,
|
422
405
|
center_z: float,
|
423
406
|
radius: float,
|
407
|
+
sigma_decay: float = 0,
|
424
408
|
**kwargs,
|
425
409
|
) -> NDArray:
|
426
410
|
return create_mask(
|
@@ -428,6 +412,7 @@ def sphere_mask(
|
|
428
412
|
shape=template.shape,
|
429
413
|
center=(center_x, center_y, center_z),
|
430
414
|
radius=radius,
|
415
|
+
sigma_decay=sigma_decay,
|
431
416
|
)
|
432
417
|
|
433
418
|
|
@@ -439,6 +424,7 @@ def ellipsod_mask(
|
|
439
424
|
radius_x: float,
|
440
425
|
radius_y: float,
|
441
426
|
radius_z: float,
|
427
|
+
sigma_decay: float = 0,
|
442
428
|
**kwargs,
|
443
429
|
) -> NDArray:
|
444
430
|
return create_mask(
|
@@ -446,6 +432,7 @@ def ellipsod_mask(
|
|
446
432
|
shape=template.shape,
|
447
433
|
center=(center_x, center_y, center_z),
|
448
434
|
radius=(radius_x, radius_y, radius_z),
|
435
|
+
sigma_decay=sigma_decay,
|
449
436
|
)
|
450
437
|
|
451
438
|
|
@@ -457,6 +444,7 @@ def box_mask(
|
|
457
444
|
height_x: int,
|
458
445
|
height_y: int,
|
459
446
|
height_z: int,
|
447
|
+
sigma_decay: float = 0,
|
460
448
|
**kwargs,
|
461
449
|
) -> NDArray:
|
462
450
|
return create_mask(
|
@@ -464,6 +452,7 @@ def box_mask(
|
|
464
452
|
shape=template.shape,
|
465
453
|
center=(center_x, center_y, center_z),
|
466
454
|
height=(height_x, height_y, height_z),
|
455
|
+
sigma_decay=sigma_decay,
|
467
456
|
)
|
468
457
|
|
469
458
|
|
@@ -476,6 +465,7 @@ def tube_mask(
|
|
476
465
|
inner_radius: float,
|
477
466
|
outer_radius: float,
|
478
467
|
height: int,
|
468
|
+
sigma_decay: float = 0,
|
479
469
|
**kwargs,
|
480
470
|
) -> NDArray:
|
481
471
|
return create_mask(
|
@@ -486,6 +476,7 @@ def tube_mask(
|
|
486
476
|
inner_radius=inner_radius,
|
487
477
|
outer_radius=outer_radius,
|
488
478
|
height=height,
|
479
|
+
sigma_decay=sigma_decay,
|
489
480
|
)
|
490
481
|
|
491
482
|
|
@@ -533,13 +524,23 @@ def wedge_mask(
|
|
533
524
|
|
534
525
|
|
535
526
|
def threshold_mask(
|
536
|
-
template: NDArray,
|
527
|
+
template: NDArray,
|
528
|
+
invert: bool = False,
|
529
|
+
standard_deviation: float = 5.0,
|
530
|
+
sigma: float = 0.0,
|
531
|
+
**kwargs,
|
537
532
|
) -> NDArray:
|
538
533
|
template_mean = template.mean()
|
539
534
|
template_deviation = standard_deviation * template.std()
|
540
535
|
upper = template_mean + template_deviation
|
541
536
|
lower = template_mean - template_deviation
|
542
|
-
mask = np.
|
537
|
+
mask = np.logical_or(template <= lower, template >= upper)
|
538
|
+
|
539
|
+
if sigma != 0:
|
540
|
+
mask_filter = preprocessor.gaussian_filter(template=mask * 1.0, sigma=sigma)
|
541
|
+
mask = np.add(mask, (1 - mask) * mask_filter)
|
542
|
+
mask[mask < np.exp(-np.square(sigma))] = 0
|
543
|
+
|
543
544
|
if invert:
|
544
545
|
np.invert(mask, out=mask)
|
545
546
|
|
@@ -789,7 +790,10 @@ class AlignmentWidget(widgets.Container):
|
|
789
790
|
active_layer = self.viewer.layers.selection.active
|
790
791
|
if active_layer is None:
|
791
792
|
return ()
|
792
|
-
|
793
|
+
try:
|
794
|
+
return [i for i in range(active_layer.data.ndim)]
|
795
|
+
except Exception:
|
796
|
+
return ()
|
793
797
|
|
794
798
|
def _update_align_axis(self, *args):
|
795
799
|
self.align_axis.choices = self._get_active_layer_dims()
|
@@ -887,6 +891,7 @@ class PointCloudWidget(widgets.Container):
|
|
887
891
|
|
888
892
|
self.viewer = viewer
|
889
893
|
self.dataframes = {}
|
894
|
+
self.selected_category = -1
|
890
895
|
|
891
896
|
self.import_button = widgets.PushButton(
|
892
897
|
name="Import", text="Import Point Cloud"
|
@@ -899,10 +904,98 @@ class PointCloudWidget(widgets.Container):
|
|
899
904
|
self.export_button.clicked.connect(self._export_point_cloud)
|
900
905
|
self.export_button.enabled = False
|
901
906
|
|
907
|
+
self.annotation_container = widgets.Container(name="Label", layout="horizontal")
|
908
|
+
self.positive_button = widgets.PushButton(name="Positive", text="Positive")
|
909
|
+
self.negative_button = widgets.PushButton(name="Negative", text="Negative")
|
910
|
+
self.positive_button.clicked.connect(self._set_positive)
|
911
|
+
self.negative_button.clicked.connect(self._set_negative)
|
912
|
+
self.annotation_container.append(self.positive_button)
|
913
|
+
self.annotation_container.append(self.negative_button)
|
914
|
+
|
915
|
+
self.face_color_select = widgets.ComboBox(
|
916
|
+
name="Color", choices=["Label", "Score"], value=None, nullable=True
|
917
|
+
)
|
918
|
+
self.face_color_select.changed.connect(self._update_face_color_mode)
|
919
|
+
|
902
920
|
self.append(self.import_button)
|
903
921
|
self.append(self.export_button)
|
922
|
+
self.append(self.annotation_container)
|
923
|
+
self.append(self.face_color_select)
|
924
|
+
|
904
925
|
self.viewer.layers.selection.events.changed.connect(self._update_buttons)
|
905
926
|
|
927
|
+
self.viewer.layers.events.inserted.connect(self._initialize_points_layer)
|
928
|
+
|
929
|
+
def _update_face_color_mode(self, event: str = None):
|
930
|
+
for layer in self.viewer.layers:
|
931
|
+
if not isinstance(layer, napari.layers.Points):
|
932
|
+
continue
|
933
|
+
|
934
|
+
layer.face_color = "white"
|
935
|
+
if event == "Label":
|
936
|
+
if len(layer.properties.get("detail", ())) == 0:
|
937
|
+
continue
|
938
|
+
layer.face_color = "detail"
|
939
|
+
layer.face_color_cycle = {
|
940
|
+
-1: "grey",
|
941
|
+
0: "red",
|
942
|
+
1: "green",
|
943
|
+
}
|
944
|
+
layer.face_color_mode = "cycle"
|
945
|
+
elif event == "Score":
|
946
|
+
if len(layer.properties.get("score_scaled", ())) == 0:
|
947
|
+
continue
|
948
|
+
layer.face_color = "score_scaled"
|
949
|
+
layer.face_colormap = "turbo"
|
950
|
+
layer.face_color_mode = "colormap"
|
951
|
+
|
952
|
+
layer.refresh_colors()
|
953
|
+
|
954
|
+
return None
|
955
|
+
|
956
|
+
def _set_positive(self, event):
|
957
|
+
self.selected_category = 1 if self.selected_category != 1 else -1
|
958
|
+
self._update_annotation_buttons()
|
959
|
+
|
960
|
+
def _set_negative(self, event):
|
961
|
+
self.selected_category = 0 if self.selected_category != 0 else -1
|
962
|
+
self._update_annotation_buttons()
|
963
|
+
|
964
|
+
def _update_annotation_buttons(self):
|
965
|
+
selected_style = "background-color: darkgrey"
|
966
|
+
default_style = "background-color: none"
|
967
|
+
|
968
|
+
self.positive_button.native.setStyleSheet(
|
969
|
+
selected_style if self.selected_category == 1 else default_style
|
970
|
+
)
|
971
|
+
self.negative_button.native.setStyleSheet(
|
972
|
+
selected_style if self.selected_category == 0 else default_style
|
973
|
+
)
|
974
|
+
|
975
|
+
def _initialize_points_layer(self, event):
|
976
|
+
layer = event.value
|
977
|
+
if not isinstance(layer, napari.layers.Points):
|
978
|
+
return
|
979
|
+
if len(layer.properties) == 0:
|
980
|
+
layer.properties = {"detail": [-1]}
|
981
|
+
|
982
|
+
if "detail" not in layer.properties:
|
983
|
+
layer["detail"] = [-1]
|
984
|
+
|
985
|
+
layer.mouse_drag_callbacks.append(self._on_click)
|
986
|
+
return None
|
987
|
+
|
988
|
+
def _on_click(self, layer, event):
|
989
|
+
if layer.mode == "add":
|
990
|
+
layer.current_properties["detail"][-1] = self.selected_category
|
991
|
+
elif layer.mode == "select":
|
992
|
+
for index in layer.selected_data:
|
993
|
+
layer.properties["detail"][index] = self.selected_category
|
994
|
+
|
995
|
+
# TODO: Check whether current face color is the desired one already
|
996
|
+
self._update_face_color_mode(self.face_color_select.value)
|
997
|
+
layer.refresh_colors()
|
998
|
+
|
906
999
|
def _update_buttons(self, event):
|
907
1000
|
is_pointcloud = isinstance(
|
908
1001
|
self.viewer.layers.selection.active, napari.layers.Points
|
@@ -948,9 +1041,7 @@ class PointCloudWidget(widgets.Container):
|
|
948
1041
|
|
949
1042
|
if "score" in merged_data.columns:
|
950
1043
|
merged_data["score"] = merged_data["score"].fillna(1)
|
951
|
-
|
952
|
-
merged_data["detail"] = merged_data["detail"].fillna(2)
|
953
|
-
|
1044
|
+
merged_data["detail"] = layer.properties["detail"]
|
954
1045
|
merged_data.to_csv(filename, sep="\t", index=False)
|
955
1046
|
|
956
1047
|
def _get_load_path(self, event):
|
@@ -974,7 +1065,7 @@ class PointCloudWidget(widgets.Container):
|
|
974
1065
|
dataframe["score"] = 1
|
975
1066
|
|
976
1067
|
if "detail" not in dataframe.columns:
|
977
|
-
dataframe["detail"] = -
|
1068
|
+
dataframe["detail"] = -1
|
978
1069
|
|
979
1070
|
point_properties = {
|
980
1071
|
"score": np.array(dataframe["score"].values),
|
@@ -988,8 +1079,6 @@ class PointCloudWidget(widgets.Container):
|
|
988
1079
|
points,
|
989
1080
|
size=10,
|
990
1081
|
properties=point_properties,
|
991
|
-
face_color="score_scaled",
|
992
|
-
face_colormap="turbo",
|
993
1082
|
name=layer_name,
|
994
1083
|
)
|
995
1084
|
self.dataframes[layer_name] = dataframe
|
@@ -1022,9 +1111,14 @@ class MatchingWidget(widgets.Container):
|
|
1022
1111
|
def _load_data(self, filename):
|
1023
1112
|
data = load_pickle(filename)
|
1024
1113
|
|
1025
|
-
|
1114
|
+
metadata = {"origin": data[-1][1], "sampling_rate": data[-1][2]}
|
1115
|
+
_ = self.viewer.add_image(
|
1116
|
+
data=data[2], name="Rotations", colormap="orange", metadata=metadata
|
1117
|
+
)
|
1026
1118
|
|
1027
|
-
_ = self.viewer.add_image(
|
1119
|
+
_ = self.viewer.add_image(
|
1120
|
+
data=data[0], name="Scores", colormap="turbo", metadata=metadata
|
1121
|
+
)
|
1028
1122
|
|
1029
1123
|
|
1030
1124
|
def main():
|
@@ -1042,11 +1136,10 @@ def main():
|
|
1042
1136
|
widget=alignment_widget, name="Alignment", area="right"
|
1043
1137
|
)
|
1044
1138
|
viewer.window.add_dock_widget(widget=mask_widget, name="Mask", area="right")
|
1139
|
+
viewer.window.add_dock_widget(widget=export_widget, name="Export", area="right")
|
1045
1140
|
viewer.window.add_dock_widget(widget=point_cloud, name="PointCloud", area="left")
|
1046
1141
|
viewer.window.add_dock_widget(widget=matching_widget, name="Matching", area="left")
|
1047
1142
|
|
1048
|
-
viewer.window.add_dock_widget(widget=export_widget, name="Export", area="right")
|
1049
|
-
|
1050
1143
|
napari.run()
|
1051
1144
|
|
1052
1145
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: pytme
|
3
|
+
Version: 0.2.2
|
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 >=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 ; 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
|
+
|
41
|
+
# Python Template Matching Engine (PyTME)
|
42
|
+
|
43
|
+
[](https://github.com/KosinskiLab/pyTME/actions)
|
44
|
+
[](https://pypi.org/project/pytme/)
|
45
|
+
|
46
|
+
**[Documentation](https://kosinskilab.github.io/pyTME/)** | **[Installation](https://kosinskilab.github.io/pyTME/quickstart/installation.html)** | **[API](https://kosinskilab.github.io/pyTME/reference/index.html)**
|
47
|
+
|
48
|
+
PyTME is a Python library for data-intensive n-dimensional template matching using CPUs and GPUs.
|
49
|
+
|
50
|
+
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
|
51
|
+
|
52
|
+
PyTME includes a [graphical user interface](https://kosinskilab.github.io/pyTME/quickstart/preprocessing.html#practical-example) 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.
|
53
|
+
|
54
|
+
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.
|
55
|
+
|
56
|
+
Running into bugs or missing a feature? Help us improve the project by opening an [issue](https://github.com/KosinskiLab/pyTME/issues).
|
57
|
+
|
58
|
+
## Installation
|
59
|
+
|
60
|
+
We recommend installation using one of the following methods
|
61
|
+
|
62
|
+
| Method | Command |
|
63
|
+
|----------|---------------------------------------------------------|
|
64
|
+
| PyPi | `pip install pytme` |
|
65
|
+
| Source | `pip install git+https://github.com/KosinskiLab/pyTME` |
|
66
|
+
| Docker | `docker build -t pytme -f docker/Dockerfile_GPU .` |
|
67
|
+
|
68
|
+
You can find alternative installation methods in the [documentation](https://kosinskilab.github.io/pyTME/quickstart/installation.html).
|
69
|
+
|
70
|
+
|
71
|
+
## Quickstart
|
72
|
+
|
73
|
+
Learn how to get started with
|
74
|
+
|
75
|
+
- [Preprocessing:](https://kosinskilab.github.io/pyTME/quickstart/preprocessing.html) Picking the right mask and filter for template matching.
|
76
|
+
- [Template matching:](https://kosinskilab.github.io/pyTME/quickstart/match_template.html) Find your template of interest.
|
77
|
+
- [Postprocessing](https://kosinskilab.github.io/pyTME/quickstart/postprocessing.html) Analyze template matching results and downstream integrations.
|
78
|
+
|
79
|
+
## How to Cite
|
80
|
+
|
81
|
+
If PyTME contributed significantly to your research, please cite the corresponding publication on [SoftwareX](https://www.sciencedirect.com/science/article/pii/S2352711024000074).
|
82
|
+
|
83
|
+
```bibtex
|
84
|
+
@article{Maurer:2024aa,
|
85
|
+
author = {Maurer, Valentin J. and Siggel, Marc and Kosinski, Jan},
|
86
|
+
journal = {SoftwareX},
|
87
|
+
pages = {101636},
|
88
|
+
title = {PyTME (Python Template Matching Engine): A fast, flexible, and multi-purpose template matching library for cryogenic electron microscopy data},
|
89
|
+
volume = {25},
|
90
|
+
year = {2024}}
|
91
|
+
```
|
@@ -0,0 +1,74 @@
|
|
1
|
+
pytme-0.2.2.data/scripts/estimate_ram_usage.py,sha256=R1NDpFajcF-MonJ4a43SfDlA-nxBYwK7D2quzCdsVFM,2767
|
2
|
+
pytme-0.2.2.data/scripts/match_template.py,sha256=FXAzPUnKMM2_thOtTZUKtIDV7GCmhFmLXm2yJDCI6G0,41456
|
3
|
+
pytme-0.2.2.data/scripts/postprocess.py,sha256=K3tP8licTsCW1LKy8hQ8rMA7UrJ0AakM3kffx_1d4n4,24780
|
4
|
+
pytme-0.2.2.data/scripts/preprocess.py,sha256=zog-l2Je-GeouJ6SnamOMuHgTn7fFPiGnO5X03y5qSY,2527
|
5
|
+
pytme-0.2.2.data/scripts/preprocessor_gui.py,sha256=3eGC5RqnnZ3aXyfKEVstddLvKQP46kvyclj4-q01vAA,39275
|
6
|
+
scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
scripts/estimate_ram_usage.py,sha256=rN7haobnHg3YcgGJIp81FNiCzy8-saJGeEurQlmQmNQ,2768
|
8
|
+
scripts/extract_candidates.py,sha256=DAfNyuauogWvSdRWIbtH44tsk9buLn13JrL1zJjJGLE,8373
|
9
|
+
scripts/match_template.py,sha256=tEHfYXvfoBZB7bFjaBrHG3qH_e-U96fRfruB4n1uH9U,41457
|
10
|
+
scripts/match_template_filters.py,sha256=Gj4a1b_S5NWp_dfFEPFn0D7jGf-qYgBbnTvZZ4bwqOQ,42036
|
11
|
+
scripts/postprocess.py,sha256=TngTI7R8VaotxyOR9o5Hgmppjn_ILJ6ek1uwyHjkuco,24781
|
12
|
+
scripts/preprocess.py,sha256=ebJVLxbRlB6TI5YHNr0VavZ4lmaRdf8QVafyiDhh_oU,2528
|
13
|
+
scripts/preprocessor_gui.py,sha256=BUm8dsbe5LEo0FyjKrK1KdbeM75aYrS1yiCp2GfI19M,39276
|
14
|
+
scripts/refine_matches.py,sha256=Y17Ku_t0W9vglPNF2oU5EFrqoedJIm3lCGl-hXRHvjc,21920
|
15
|
+
tme/__init__.py,sha256=liARhKZ565F2Y02iWRbFzfrTCkL5You3YaUSiqUkees,202
|
16
|
+
tme/__version__.py,sha256=m6kyaNpwBcP1XYcqrelX2oS3PJuOnElOcRdBa9pEb8c,22
|
17
|
+
tme/analyzer.py,sha256=WfL6RbJDFpjwF1YiIa9NGOxEL8NddlekQIuG1r6B5fA,50325
|
18
|
+
tme/density.py,sha256=JAC2TKzD2OWBa45MCEwT6WbL1XcTdcbiErMylwIwnH0,83837
|
19
|
+
tme/extensions.cpython-311-darwin.so,sha256=e48Rshv8cWPSiBD-xqq5L8ekQJn84MqZse05fndMnkc,411984
|
20
|
+
tme/matching_data.py,sha256=QvPozAnlT9GvEUaXwqmN05j-axQ5EKuoPrNBEELMxvg,25255
|
21
|
+
tme/matching_exhaustive.py,sha256=QlFOORb2a2uDy6-wk4YU3X04y3XW4sfzwk3gC9gL27I,18843
|
22
|
+
tme/matching_memory.py,sha256=bmCAUYyXWEet-1XXhldtc0irio2ytMSsAzWYyFI5LNM,11273
|
23
|
+
tme/matching_optimization.py,sha256=Y8HfecXiOvAHXM1viBaQ_aXljqqTnGwlOlFe0MJpDRQ,45082
|
24
|
+
tme/matching_scores.py,sha256=3XxvwRNztdC-4hKdloyCfwjzt4GYzA0T-WF6YUnOcvs,30867
|
25
|
+
tme/matching_utils.py,sha256=VdQZPad9uVCQ9-32Vulh5r7bhBYylPvqv2X2DQNk91M,39736
|
26
|
+
tme/memory.py,sha256=6xeIMAncQkgYDi6w-PIYgFEWRTUPu0_OTCeRO0p9r9Q,11029
|
27
|
+
tme/orientations.py,sha256=Ul-1g2ci3QdRLfD447ZkcIo6Rv6f7gQH83JZcRkAru0,25384
|
28
|
+
tme/parser.py,sha256=fNiCAdsWI4ql92F1Ob4suiVzpjUOBlh2lad1iNY_FP8,13772
|
29
|
+
tme/preprocessor.py,sha256=ZhlP-8b4NzAzRogWOnk5DLNh_tqCMOpc4msQIJEKBTU,48566
|
30
|
+
tme/structure.py,sha256=k5kgFNHkM2mUvXS7VAwaevltLC1mUxEiDhMW0TqTNjo,65834
|
31
|
+
tme/types.py,sha256=NAY7C4qxE6yz-DXVtClMvFfoOV-spWGLNfpLATZ1LcU,442
|
32
|
+
tme/backends/__init__.py,sha256=Y69k5_Vx7VpcBNV8W0pQrebzo42lp3WBfEGAl1s4Vd4,5187
|
33
|
+
tme/backends/_jax_utils.py,sha256=v9T36Jv4ztyC38IhrbNU5tf80VKAdAXYX5ZxCWe02qU,5757
|
34
|
+
tme/backends/cupy_backend.py,sha256=rK3pjNNB4D46MqzY2DrB_GSObFJlG5GQDOt-eOwhwTg,9429
|
35
|
+
tme/backends/jax_backend.py,sha256=CUEJynVw9jqWid_vARMpNrhORgUR1SlLjz4ZTXkLuvI,9730
|
36
|
+
tme/backends/matching_backend.py,sha256=KfCOKD_rA9el3Y7BeH17KJ1apCUIIhhvn-vmbkb3CB0,33750
|
37
|
+
tme/backends/mlx_backend.py,sha256=FJhqmCzgjXAjWGX1HhHFrCy_We4YwQQBkKFNG05ctzM,7788
|
38
|
+
tme/backends/npfftw_backend.py,sha256=QAQxr3OSq9bBN_Aq0qps-nz5vvUsx0_fFn9TocI_QCw,16988
|
39
|
+
tme/backends/pytorch_backend.py,sha256=fzLucJZH5VYhrUtcX9osirNvLMsyMNPt_JNfS2l-kqg,14940
|
40
|
+
tme/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
41
|
+
tme/data/c48n309.npy,sha256=NwH64mOEbm3tStq5c98o81fY1vMOoq4nvXDAh7Z7iZg,296768
|
42
|
+
tme/data/c48n527.npy,sha256=saSUMTa1R0MisPvgFL02a7IHQSwEZ-mJu0v3qJjg5AU,506048
|
43
|
+
tme/data/c48n9.npy,sha256=bDVLV6mWjZHSQfeDc-MOCKKarfc1jaNeVvpoe2xMUy4,8768
|
44
|
+
tme/data/c48u1.npy,sha256=JeXMFzFITs2ezdc3x5lp3jo1cHHHHVADSA1Tpf77kXs,1088
|
45
|
+
tme/data/c48u1153.npy,sha256=ECiEximtYDWtIux3Fwe_EJlyn08gUqP85DN9gjkT9_k,1107008
|
46
|
+
tme/data/c48u1201.npy,sha256=aceC_Jeienz_81X4520nPpZcg5tnRhbW795EqbpWkrg,1153088
|
47
|
+
tme/data/c48u1641.npy,sha256=p4LwW3LzdTjrUUpA7H53RfNWxYfPX0XjeSwZ39Ac78Q,1575488
|
48
|
+
tme/data/c48u181.npy,sha256=mLYXrv1YHLH6DsBp5MkxHkxlxgMnj1mw_KKI0udH-FY,173888
|
49
|
+
tme/data/c48u2219.npy,sha256=p8TQeX8YHu4pdxnwJjEAlQWAPa66W7kpK96iZKZr9JE,2130368
|
50
|
+
tme/data/c48u27.npy,sha256=k03ZNEsoPwBKCy8IeIa5G0WRZqjGZMtX6Ibu7EpJHvU,26048
|
51
|
+
tme/data/c48u2947.npy,sha256=icI97ED6ct66y7FIaJAugmjzrIWk7CINCxtO3wDTnrU,2829248
|
52
|
+
tme/data/c48u3733.npy,sha256=tla-__Pf-hpN6h04vtFIfkkFdCLple11VO06kr1dXkM,3583808
|
53
|
+
tme/data/c48u4749.npy,sha256=tItOA4oV7SiqCCREwz3fyEpZoxM0lCq_jfEo5_-fp2s,4559168
|
54
|
+
tme/data/c48u5879.npy,sha256=bFk89MllIFCX_sLXTYWFquSyN1NuahH4wwnEsPJLxzA,5643968
|
55
|
+
tme/data/c48u7111.npy,sha256=CMy9kI2edH-q9eTIVdgUtXurplYNI7Uqp4dXfkkVdf8,6826688
|
56
|
+
tme/data/c48u815.npy,sha256=bCuJxLtm0Sjg3GGxtyjGzRYZ1G0Gz79XHI-71GvqQnI,782528
|
57
|
+
tme/data/c48u83.npy,sha256=7ODJYnsiuDjGbgd9GFopsyIW2IjrYI0J2X2f-cK868U,79808
|
58
|
+
tme/data/c48u8649.npy,sha256=-IPlpR4zrPQZWhhSPu4zEulFdrCEVgTMFffCB5d-huE,8303168
|
59
|
+
tme/data/c600v.npy,sha256=JqSu3ALoL1A9iguehc0YGUMFPsh2fprHHp76VXeFXIw,2528
|
60
|
+
tme/data/c600vc.npy,sha256=Yht-GFXDSjjGvsjFBvyxxEZAI-ODADPd5gEgFNZQVTA,14528
|
61
|
+
tme/data/metadata.yaml,sha256=fAgX-mEzB0QMHTEtYDG4cSMbJhYxBbDJH3sdvJvL7a8,750
|
62
|
+
tme/data/quat_to_numpy.py,sha256=-gkDZb10fKBxwfYrSLCUWvMB76TzZWELCeKsYProwws,1333
|
63
|
+
tme/preprocessing/__init__.py,sha256=7O3vDzJcIfxovJkf7avWSPtzaIVlTbmsW7egQFukC_s,98
|
64
|
+
tme/preprocessing/_utils.py,sha256=hC8Qfh_a_fk4o7v82c4-TZYYRFfo5XttcghvpkzBo0A,6193
|
65
|
+
tme/preprocessing/composable_filter.py,sha256=sp3bN8JeFB2r384cEIgvN6yXjC53GCoPhBmGI0S0kbI,781
|
66
|
+
tme/preprocessing/compose.py,sha256=tilp14UhBh98SJVJhC_STbgi7i8HeU4FB7B7KUOD8gc,1458
|
67
|
+
tme/preprocessing/frequency_filters.py,sha256=x900ntX0QI46lDdycPISO-7K0XgM0015ksCOFlm-yM4,12964
|
68
|
+
tme/preprocessing/tilt_series.py,sha256=Yb3dk7TZf7OfPWZPwVuFTirOZK4x6mjLbxYX9KtRBdg,34545
|
69
|
+
pytme-0.2.2.dist-info/LICENSE,sha256=K1IUNSVAz8BXbpH5EA8y5FpaHdvFXnAF2zeK95Lr2bY,18467
|
70
|
+
pytme-0.2.2.dist-info/METADATA,sha256=axenBTwu62Z-5jY-4kScnqv7bbWWwDU7S-VatPzSz0k,5206
|
71
|
+
pytme-0.2.2.dist-info/WHEEL,sha256=IHsX_ZtCj0uMf8jtN3eK3zfXfu1SfKRsLbDhsprnhR0,109
|
72
|
+
pytme-0.2.2.dist-info/entry_points.txt,sha256=ff3LQL3FCWfCYOwFiP9zatm7laUbnwCkuPELkQVyUO4,241
|
73
|
+
pytme-0.2.2.dist-info/top_level.txt,sha256=J8FUkazOb2fZ0n_KexnqCGyNOtie2bwisFSUBiM5-0w,12
|
74
|
+
pytme-0.2.2.dist-info/RECORD,,
|