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
scripts/preprocessor_gui.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#!python3
|
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,
|
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
|
-
"
|
278
|
-
"
|
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,
|
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.
|
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
|
-
|
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"] = -
|
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
|
-
|
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(
|
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
|
|