driftlessmap 1.1.0__py3-none-any.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 (210) hide show
  1. driftlessmap/__init__.py +16 -0
  2. driftlessmap/__main__.py +5 -0
  3. driftlessmap/about.py +24 -0
  4. driftlessmap/allen_downloader.py +908 -0
  5. driftlessmap/app.py +8085 -0
  6. driftlessmap/atlas_downloader.py +370 -0
  7. driftlessmap/atlas_loader.py +526 -0
  8. driftlessmap/atlas_processor.py +784 -0
  9. driftlessmap/atlas_transform.py +111 -0
  10. driftlessmap/atlas_view.py +1435 -0
  11. driftlessmap/cell_detection.py +27 -0
  12. driftlessmap/coordinate_validation.py +19 -0
  13. driftlessmap/czi_reader.py +236 -0
  14. driftlessmap/data/WHS_atlas_labels.pkl +0 -0
  15. driftlessmap/data/allen_mice_atlas_labels.pkl +0 -0
  16. driftlessmap/data/atlas_labels.pkl +0 -0
  17. driftlessmap/data/query.csv +1328 -0
  18. driftlessmap/download_utils.py +85 -0
  19. driftlessmap/herbs.png +0 -0
  20. driftlessmap/icons/backward.svg +15 -0
  21. driftlessmap/icons/dot.svg +11 -0
  22. driftlessmap/icons/down-arrow.svg +10 -0
  23. driftlessmap/icons/fast_backward.svg +14 -0
  24. driftlessmap/icons/fast_forward.svg +14 -0
  25. driftlessmap/icons/forward.svg +14 -0
  26. driftlessmap/icons/layers/add.png +0 -0
  27. driftlessmap/icons/layers/ai.png +0 -0
  28. driftlessmap/icons/layers/eye_off.png +0 -0
  29. driftlessmap/icons/layers/eye_on.png +0 -0
  30. driftlessmap/icons/layers/eye_white.png +0 -0
  31. driftlessmap/icons/layers/hi.png +0 -0
  32. driftlessmap/icons/layers/trash.png +0 -0
  33. driftlessmap/icons/object.svg +22 -0
  34. driftlessmap/icons/sidebar/add.png +0 -0
  35. driftlessmap/icons/sidebar/atlascontrol.png +0 -0
  36. driftlessmap/icons/sidebar/atlascontrol.svg +32 -0
  37. driftlessmap/icons/sidebar/bnd.svg +14 -0
  38. driftlessmap/icons/sidebar/c_section.png +0 -0
  39. driftlessmap/icons/sidebar/c_section2.png +0 -0
  40. driftlessmap/icons/sidebar/cell.svg +24 -0
  41. driftlessmap/icons/sidebar/check.svg +9 -0
  42. driftlessmap/icons/sidebar/compare.svg +18 -0
  43. driftlessmap/icons/sidebar/contour.svg +14 -0
  44. driftlessmap/icons/sidebar/h_section.png +0 -0
  45. driftlessmap/icons/sidebar/info.svg +14 -0
  46. driftlessmap/icons/sidebar/layers.png +0 -0
  47. driftlessmap/icons/sidebar/layers.svg +23 -0
  48. driftlessmap/icons/sidebar/line.svg +10 -0
  49. driftlessmap/icons/sidebar/link.svg +18 -0
  50. driftlessmap/icons/sidebar/link_off.svg +18 -0
  51. driftlessmap/icons/sidebar/merge.svg +11 -0
  52. driftlessmap/icons/sidebar/object.svg +13 -0
  53. driftlessmap/icons/sidebar/probe.png +0 -0
  54. driftlessmap/icons/sidebar/probe.svg +23 -0
  55. driftlessmap/icons/sidebar/rotation_horizontal.svg +14 -0
  56. driftlessmap/icons/sidebar/rotation_vertical.svg +14 -0
  57. driftlessmap/icons/sidebar/s_section.png +0 -0
  58. driftlessmap/icons/sidebar/tool.png +0 -0
  59. driftlessmap/icons/sidebar/tool.svg +19 -0
  60. driftlessmap/icons/sidebar/trash.png +0 -0
  61. driftlessmap/icons/sidebar/tree_checked.svg +10 -0
  62. driftlessmap/icons/sidebar/treeview.png +0 -0
  63. driftlessmap/icons/sidebar/treeview.svg +10 -0
  64. driftlessmap/icons/sidebar/treeview2.png +0 -0
  65. driftlessmap/icons/sidebar/virus.svg +24 -0
  66. driftlessmap/icons/tdown.svg +16 -0
  67. driftlessmap/icons/toolbar/accept.svg +18 -0
  68. driftlessmap/icons/toolbar/accept2.svg +24 -0
  69. driftlessmap/icons/toolbar/aim.svg +44 -0
  70. driftlessmap/icons/toolbar/aim_not.svg +28 -0
  71. driftlessmap/icons/toolbar/anchor.svg +14 -0
  72. driftlessmap/icons/toolbar/anticlockwise_rotation.svg +12 -0
  73. driftlessmap/icons/toolbar/atlas_icon.png +0 -0
  74. driftlessmap/icons/toolbar/boundary_register.svg +18 -0
  75. driftlessmap/icons/toolbar/cancel.svg +32 -0
  76. driftlessmap/icons/toolbar/cell_select.svg +22 -0
  77. driftlessmap/icons/toolbar/cell_select_not.svg +12 -0
  78. driftlessmap/icons/toolbar/check.svg +9 -0
  79. driftlessmap/icons/toolbar/clockwise_rotation.svg +11 -0
  80. driftlessmap/icons/toolbar/closed_path.svg +16 -0
  81. driftlessmap/icons/toolbar/closed_path2 copy.svg +19 -0
  82. driftlessmap/icons/toolbar/closed_path2.svg +20 -0
  83. driftlessmap/icons/toolbar/eraser.png +0 -0
  84. driftlessmap/icons/toolbar/eraser.svg +19 -0
  85. driftlessmap/icons/toolbar/eye.svg +17 -0
  86. driftlessmap/icons/toolbar/eye_closed.svg +19 -0
  87. driftlessmap/icons/toolbar/fill.svg +12 -0
  88. driftlessmap/icons/toolbar/gps.svg +15 -0
  89. driftlessmap/icons/toolbar/h_flip.png +0 -0
  90. driftlessmap/icons/toolbar/handle.png +0 -0
  91. driftlessmap/icons/toolbar/image_icon.png +0 -0
  92. driftlessmap/icons/toolbar/image_icon.svg +25 -0
  93. driftlessmap/icons/toolbar/info.svg +14 -0
  94. driftlessmap/icons/toolbar/inpart.png +0 -0
  95. driftlessmap/icons/toolbar/inpart.svg +17 -0
  96. driftlessmap/icons/toolbar/lasso.png +0 -0
  97. driftlessmap/icons/toolbar/lasso.svg +14 -0
  98. driftlessmap/icons/toolbar/left90.png +0 -0
  99. driftlessmap/icons/toolbar/line.svg +10 -0
  100. driftlessmap/icons/toolbar/line_sites.svg +11 -0
  101. driftlessmap/icons/toolbar/linear_silicon.png +0 -0
  102. driftlessmap/icons/toolbar/linear_silicon.svg +88 -0
  103. driftlessmap/icons/toolbar/list.svg +14 -0
  104. driftlessmap/icons/toolbar/location.svg +14 -0
  105. driftlessmap/icons/toolbar/magic-wand.svg +16 -0
  106. driftlessmap/icons/toolbar/magic_white.png +0 -0
  107. driftlessmap/icons/toolbar/mask.svg +10 -0
  108. driftlessmap/icons/toolbar/match.svg +20 -0
  109. driftlessmap/icons/toolbar/matchbnd.svg +18 -0
  110. driftlessmap/icons/toolbar/matching.svg +36 -0
  111. driftlessmap/icons/toolbar/merge.svg +11 -0
  112. driftlessmap/icons/toolbar/move_down.png +0 -0
  113. driftlessmap/icons/toolbar/move_left.png +0 -0
  114. driftlessmap/icons/toolbar/move_right.png +0 -0
  115. driftlessmap/icons/toolbar/move_up.png +0 -0
  116. driftlessmap/icons/toolbar/moving.png +0 -0
  117. driftlessmap/icons/toolbar/multi-probe.svg +119 -0
  118. driftlessmap/icons/toolbar/multi_pencil.svg +23 -0
  119. driftlessmap/icons/toolbar/multi_probe.png +0 -0
  120. driftlessmap/icons/toolbar/object.svg +13 -0
  121. driftlessmap/icons/toolbar/open_path.svg +22 -0
  122. driftlessmap/icons/toolbar/outpart.png +0 -0
  123. driftlessmap/icons/toolbar/outpart.svg +26 -0
  124. driftlessmap/icons/toolbar/pencil.png +0 -0
  125. driftlessmap/icons/toolbar/pencil.svg +19 -0
  126. driftlessmap/icons/toolbar/plasso.png +0 -0
  127. driftlessmap/icons/toolbar/probe.svg +19 -0
  128. driftlessmap/icons/toolbar/radar.svg +32 -0
  129. driftlessmap/icons/toolbar/rotation.svg +45 -0
  130. driftlessmap/icons/toolbar/rotation_clockwise.svg +18 -0
  131. driftlessmap/icons/toolbar/rotation_counter_clockwise.svg +18 -0
  132. driftlessmap/icons/toolbar/ruler.svg +27 -0
  133. driftlessmap/icons/toolbar/separate_sites.svg +13 -0
  134. driftlessmap/icons/toolbar/single_pencil.svg +16 -0
  135. driftlessmap/icons/toolbar/toa.svg +11 -0
  136. driftlessmap/icons/toolbar/toa_delete.svg +36 -0
  137. driftlessmap/icons/toolbar/toaa.svg +11 -0
  138. driftlessmap/icons/toolbar/toh.svg +11 -0
  139. driftlessmap/icons/toolbar/toh_delete.svg +36 -0
  140. driftlessmap/icons/toolbar/tohh.svg +11 -0
  141. driftlessmap/icons/toolbar/trans.png +0 -0
  142. driftlessmap/icons/toolbar/trans.svg +16 -0
  143. driftlessmap/icons/toolbar/triangulation.svg +10 -0
  144. driftlessmap/icons/toolbar/two_window.png +0 -0
  145. driftlessmap/icons/toolbar/unmerge.svg +16 -0
  146. driftlessmap/icons/toolbar/v_flip.png +0 -0
  147. driftlessmap/icons/toolbar/virus.svg +24 -0
  148. driftlessmap/icons/toolbar/virus_register.svg +31 -0
  149. driftlessmap/icons/toolbar/vis2d.svg +29 -0
  150. driftlessmap/icons/toolbar/window2.png +0 -0
  151. driftlessmap/icons/toolbar/window3.png +0 -0
  152. driftlessmap/icons/toolbar/window4.png +0 -0
  153. driftlessmap/icons/tree_close.svg +14 -0
  154. driftlessmap/icons/tree_open.svg +14 -0
  155. driftlessmap/icons/up-arrow.svg +10 -0
  156. driftlessmap/image_curves.py +579 -0
  157. driftlessmap/image_reader.py +186 -0
  158. driftlessmap/image_stacks.py +359 -0
  159. driftlessmap/image_view.py +646 -0
  160. driftlessmap/images_reader.py +5 -0
  161. driftlessmap/label_tree.py +278 -0
  162. driftlessmap/layer_validation.py +25 -0
  163. driftlessmap/layers_control.py +554 -0
  164. driftlessmap/main_window.ui +883 -0
  165. driftlessmap/movable_points.py +254 -0
  166. driftlessmap/obj_items.py +122 -0
  167. driftlessmap/object_control.py +1762 -0
  168. driftlessmap/persistence.py +376 -0
  169. driftlessmap/popup_message.py +16 -0
  170. driftlessmap/probe_csv.py +319 -0
  171. driftlessmap/probe_reconstruction.py +409 -0
  172. driftlessmap/probe_utiles.py +1442 -0
  173. driftlessmap/qss/atlas_view_group_box.qss +15 -0
  174. driftlessmap/qss/box_label.qss +7 -0
  175. driftlessmap/qss/channel_selector.qss +19 -0
  176. driftlessmap/qss/color_combo.qss +121 -0
  177. driftlessmap/qss/decor_label.qss +9 -0
  178. driftlessmap/qss/dialogs.qss +11 -0
  179. driftlessmap/qss/hidden_line_edit.qss +5 -0
  180. driftlessmap/qss/label_tree.qss +123 -0
  181. driftlessmap/qss/main_window.qss +243 -0
  182. driftlessmap/qss/menu_bar.qss +42 -0
  183. driftlessmap/qss/multi_handle_slider.qss +14 -0
  184. driftlessmap/qss/obj_ctrl_bottom_button.qss +29 -0
  185. driftlessmap/qss/object_text_button.qss +19 -0
  186. driftlessmap/qss/page_control.qss +36 -0
  187. driftlessmap/qss/rotation_button.qss +9 -0
  188. driftlessmap/qss/side_bar.qss +23 -0
  189. driftlessmap/qss/spinbox.qss +106 -0
  190. driftlessmap/qss/tabs.qss +211 -0
  191. driftlessmap/qss/tool_bar.qss +77 -0
  192. driftlessmap/resources.py +34 -0
  193. driftlessmap/roi_analysis.py +375 -0
  194. driftlessmap/run_driftlessmap.py +4 -0
  195. driftlessmap/slice_stacks.py +268 -0
  196. driftlessmap/slice_validation.py +20 -0
  197. driftlessmap/toolbox.py +569 -0
  198. driftlessmap/triangulation.py +517 -0
  199. driftlessmap/triangulation_points.py +108 -0
  200. driftlessmap/user_settings.py +92 -0
  201. driftlessmap/uuuuuu.py +1168 -0
  202. driftlessmap/version.py +3 -0
  203. driftlessmap/widgets_utils.py +140 -0
  204. driftlessmap/wtiles.py +715 -0
  205. driftlessmap-1.1.0.dist-info/METADATA +186 -0
  206. driftlessmap-1.1.0.dist-info/RECORD +210 -0
  207. driftlessmap-1.1.0.dist-info/WHEEL +5 -0
  208. driftlessmap-1.1.0.dist-info/entry_points.txt +2 -0
  209. driftlessmap-1.1.0.dist-info/licenses/LICENSE.txt +23 -0
  210. driftlessmap-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1435 @@
1
+ import os
2
+ import sys
3
+ import numpy as np
4
+ import pyqtgraph as pg
5
+ # from pyqtgraph.Qt import QtGui, QtCore
6
+ import pyqtgraph.functions as fn
7
+ import pyqtgraph.opengl as gl
8
+ from PyQt6.QtWidgets import *
9
+ from PyQt6.QtGui import *
10
+ from PyQt6.QtCore import *
11
+
12
+ from .image_stacks import SliceStack
13
+ from .slice_stacks import SliceStacks
14
+ from .label_tree import LabelTree
15
+ from .uuuuuu import read_qss_file, get_corner_line_from_rect, get_slice_atlas_coord, make_contour_img, \
16
+ rotate_base_points, rotation_x, rotation_y, rotation_z
17
+ from .probe_utiles import get_tilt_sign#, get_direction_rotation
18
+ from .slice_validation import slice_info_is_ready
19
+ from .resources import resource_path
20
+
21
+
22
+ class PageController(QWidget):
23
+ DRAG_UPDATE_DELAY_MS = 75
24
+
25
+ class SignalProxy(QObject):
26
+ sigPageChanged = pyqtSignal(object)
27
+
28
+ def __init__(self):
29
+ self._sigprox = PageController.SignalProxy()
30
+ self.sig_page_changed = self._sigprox.sigPageChanged
31
+
32
+ QWidget.__init__(self)
33
+ page_control_style = read_qss_file('qss/page_control.qss')
34
+ self.setStyleSheet(page_control_style)
35
+
36
+ self.max_val = None
37
+ self._pending_page = None
38
+ self._last_emitted_page = None
39
+ self._drag_update_timer = QTimer(self)
40
+ self._drag_update_timer.setSingleShot(True)
41
+ self._drag_update_timer.setInterval(self.DRAG_UPDATE_DELAY_MS)
42
+ self._drag_update_timer.timeout.connect(self._flush_pending_page)
43
+
44
+ self.page_slider = QSlider(Qt.Orientation.Horizontal)
45
+ self.page_slider.setMinimum(0)
46
+ self.page_slider.valueChanged.connect(self.slider_value_changed)
47
+ self.page_slider.sliderReleased.connect(self._flush_pending_page)
48
+
49
+ self.page_label = QLabel()
50
+ self.page_label.setFixedSize(50, 20)
51
+
52
+ self.page_left_btn = QPushButton()
53
+ self.page_left_btn.setIcon(QIcon(resource_path("icons/backward.svg")))
54
+ self.page_left_btn.setIconSize(QSize(16, 16))
55
+ self.page_left_btn.clicked.connect(self.left_btn_clicked)
56
+
57
+ self.page_right_btn = QPushButton()
58
+ self.page_right_btn.setIcon(QIcon(resource_path("icons/forward.svg")))
59
+ self.page_right_btn.setIconSize(QSize(16, 16))
60
+ self.page_right_btn.clicked.connect(self.right_btn_clicked)
61
+
62
+ self.page_fast_left_btn = QPushButton()
63
+ self.page_fast_left_btn.setIcon(QIcon(resource_path("icons/fast_backward.svg")))
64
+ self.page_fast_left_btn.setIconSize(QSize(16, 16))
65
+ self.page_fast_left_btn.clicked.connect(self.fast_left_btn_clicked)
66
+
67
+ self.page_fast_right_btn = QPushButton()
68
+ self.page_fast_right_btn.setIcon(QIcon(resource_path("icons/fast_forward.svg")))
69
+ self.page_fast_right_btn.setIconSize(QSize(16, 16))
70
+ self.page_fast_right_btn.clicked.connect(self.fast_right_btn_clicked)
71
+
72
+ page_ctrl_layout = QHBoxLayout()
73
+ page_ctrl_layout.setSpacing(0)
74
+ page_ctrl_layout.setContentsMargins(10, 5, 10, 5)
75
+ page_ctrl_layout.addWidget(self.page_left_btn)
76
+ page_ctrl_layout.addSpacing(10)
77
+ page_ctrl_layout.addWidget(self.page_fast_left_btn)
78
+ page_ctrl_layout.addSpacing(10)
79
+ page_ctrl_layout.addWidget(self.page_slider)
80
+ page_ctrl_layout.addSpacing(5)
81
+ page_ctrl_layout.addWidget(self.page_label)
82
+ page_ctrl_layout.addWidget(self.page_fast_right_btn)
83
+ page_ctrl_layout.addSpacing(10)
84
+ page_ctrl_layout.addWidget(self.page_right_btn)
85
+
86
+ self.setLayout(page_ctrl_layout)
87
+
88
+ def set_val(self, val):
89
+ self.page_slider.setValue(val)
90
+
91
+ def set_max(self, val):
92
+ self.max_val = val
93
+ self.page_slider.setMaximum(val)
94
+
95
+ def slider_value_changed(self):
96
+ val = self.page_slider.value()
97
+ self.page_label.setText(str(val))
98
+ if self.page_slider.isSliderDown():
99
+ self._pending_page = val
100
+ self._drag_update_timer.start()
101
+ return
102
+ self._drag_update_timer.stop()
103
+ self._pending_page = None
104
+ self._emit_page(val)
105
+
106
+ def _emit_page(self, val):
107
+ if val == self._last_emitted_page:
108
+ return
109
+ self._last_emitted_page = val
110
+ self.sig_page_changed.emit(val)
111
+
112
+ def _flush_pending_page(self):
113
+ self._drag_update_timer.stop()
114
+ val = self._pending_page
115
+ self._pending_page = None
116
+ if val is not None:
117
+ self._emit_page(val)
118
+
119
+ def left_btn_clicked(self):
120
+ self.step_by(-1)
121
+
122
+ def right_btn_clicked(self):
123
+ self.step_by(1)
124
+
125
+ def step_by(self, amount):
126
+ if self.max_val is None:
127
+ return
128
+ val = min(
129
+ self.max_val,
130
+ max(0, self.page_slider.value() + int(amount)),
131
+ )
132
+ self.set_val(val)
133
+
134
+ def fast_left_btn_clicked(self):
135
+ self.step_by(-10)
136
+
137
+ def fast_right_btn_clicked(self):
138
+ self.step_by(10)
139
+
140
+
141
+ class SliceRotation(QWidget):
142
+
143
+ class SignalProxy(QObject):
144
+ sigSliceRotated = pyqtSignal(object) # id
145
+
146
+ def __init__(self):
147
+ self._sigprox = SliceRotation.SignalProxy()
148
+ self.sig_slice_rotated = self._sigprox.sigSliceRotated
149
+
150
+ QWidget.__init__(self)
151
+
152
+ spinbox_line_edit_style = read_qss_file('qss/hidden_line_edit.qss')
153
+ self.prec = 0.01
154
+ self.rot_range = 30
155
+ self.n_steps = int(self.rot_range / self.prec)
156
+
157
+ self.h_slider = QSlider(Qt.Orientation.Horizontal)
158
+ self.h_slider.valueChanged.connect(self.h_slider_changed)
159
+ self.h_slider.setValue(0)
160
+ self.h_slider.setRange(-self.n_steps, self.n_steps)
161
+
162
+ self.h_spinbox = QDoubleSpinBox()
163
+ self.h_spinbox.lineEdit().setStyleSheet(spinbox_line_edit_style)
164
+ self.h_spinbox.valueChanged.connect(self.h_spinbox_changed)
165
+ self.h_spinbox.setDecimals(2)
166
+ self.h_spinbox.setValue(0)
167
+ self.h_spinbox.setRange(-self.rot_range, self.rot_range)
168
+ self.h_spinbox.setSingleStep(self.prec)
169
+ self.h_spinbox.setMinimumSize(50, 20)
170
+
171
+ self.v_slider = QSlider(Qt.Orientation.Horizontal)
172
+ self.v_slider.valueChanged.connect(self.v_slider_changed)
173
+ self.v_slider.setValue(0)
174
+ self.v_slider.setRange(-self.n_steps, self.n_steps)
175
+
176
+ self.v_spinbox = QDoubleSpinBox()
177
+ self.v_spinbox.lineEdit().setStyleSheet(spinbox_line_edit_style)
178
+ self.v_spinbox.valueChanged.connect(self.v_spinbox_changed)
179
+ self.v_spinbox.setDecimals(2)
180
+ self.v_spinbox.setValue(0)
181
+ self.v_spinbox.setRange(-self.rot_range, self.rot_range)
182
+ self.v_spinbox.setSingleStep(self.prec)
183
+ self.v_spinbox.setMinimumSize(50, 20)
184
+
185
+ def set_prec(self, prec):
186
+ self.prec = prec
187
+
188
+ def h_slider_changed(self):
189
+ val = self.h_slider.value() * self.prec
190
+ self.h_spinbox.setValue(val)
191
+
192
+ def h_spinbox_changed(self):
193
+ val = int(self.h_spinbox.value() / self.prec)
194
+ if self.h_slider.value() != val:
195
+ self.h_slider.setValue(val)
196
+ self.sig_slice_rotated.emit(np.deg2rad([self.h_spinbox.value(), self.v_spinbox.value()]))
197
+
198
+ def v_slider_changed(self):
199
+ val = self.v_slider.value() * self.prec
200
+ self.v_spinbox.setValue(val)
201
+
202
+ def v_spinbox_changed(self):
203
+ val = int(self.v_spinbox.value() / self.prec)
204
+ if self.v_slider.value() != val:
205
+ self.v_slider.setValue(val)
206
+ self.sig_slice_rotated.emit(np.deg2rad([self.h_spinbox.value(), self.v_spinbox.value()]))
207
+
208
+
209
+ class ImageLabel(QWidget):
210
+ def __init__(self, img, title):
211
+ super(QWidget, self).__init__()
212
+ layout = QHBoxLayout(self)
213
+ self.label3 = QLabel(self)
214
+ self.title = QLabel(title)
215
+ self.pixmap = QPixmap(img)
216
+ self.pixmap = self.pixmap.scaled(QSize(20, 10))
217
+ self.label3.setPixmap(self.pixmap)
218
+ self.label3.setAlignment(Qt.AlignmentFlag.AlignCenter)
219
+ self.title.setMinimumHeight(self.pixmap.height())
220
+ self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
221
+ layout.addWidget(self.label3)
222
+ layout.addWidget(self.title)
223
+ self.label3.setStyleSheet('background-color: transparent;')
224
+ self.title.setStyleSheet("""
225
+ background-color: transparent;
226
+ color: black;
227
+ padding: 0px 3px 0px 0px;
228
+ """)
229
+ layout.setSpacing(0)
230
+ layout.addStretch()
231
+
232
+
233
+ class RotationBtn(QPushButton):
234
+ def __init__(self, icon_path):
235
+ super(QPushButton, self).__init__()
236
+ btn_style = read_qss_file('qss/rotation_button.qss')
237
+ self.setStyleSheet(btn_style)
238
+ self.setFixedSize(15, 15)
239
+ self.setIcon(QIcon(resource_path(icon_path)))
240
+ self.setIconSize(QSize(15, 15))
241
+
242
+
243
+ class AtlasView(QObject):
244
+ """
245
+ A collection of user interface elements bound together:
246
+
247
+
248
+ """
249
+ def __init__(self):
250
+ super(AtlasView, self).__init__()
251
+ QObject.__init__(self)
252
+
253
+ self.atlas_data = None
254
+ self.atlas_label = None
255
+ self.label_info = None
256
+ self.atlas_boundary = None
257
+ self.slice_size = None
258
+ self.slice_tb_size_base = 80
259
+ self.coronal_tb_size = None
260
+ self.sagital_tb_size = None
261
+ self.horizontal_tb_size = None
262
+ self.slice_tb_size = None
263
+
264
+ self.slice_image_data = None
265
+ self.processing_slice = None
266
+ self.slice_cut = 'Coronal'
267
+ self.slice_width = 0
268
+ self.slice_height = 0
269
+ self.slice_distance = 0
270
+ self.slice_bregma = []
271
+ self.slice_boundary_points = []
272
+ self.slice_info_ready = False
273
+
274
+ self.remain_angle = False
275
+ self.coronal_rotated = False
276
+ self.sagittal_rotated = False
277
+ self.horizontal_rotated = False
278
+ self.coronal_rads = (0, 0)
279
+ self.sagittal_rads = (0, 0)
280
+ self.horizontal_rads = (0, 0)
281
+
282
+ self.atlas_size = None
283
+ self.anterior_info = None
284
+ self.dorsal_info = None
285
+ self.right_info = None
286
+ self.vox_size_um = None
287
+ self.Bregma = None
288
+ self.rotate_origin_3d = None
289
+ self.origin_3d = None
290
+
291
+ self.c_size = None
292
+ self.s_size = None
293
+ self.h_size = None
294
+
295
+ self.c_rotm_2d = None
296
+ self.s_rotm_2d = None
297
+ self.h_rotm_2d = None
298
+
299
+ self.c_rotm_3d = None
300
+ self.s_rotm_3d = None
301
+ self.h_rotm_3d = None
302
+
303
+ self.side_lines = None
304
+ self.corner_points = None
305
+
306
+ self.current_coronal_index = None
307
+ self.current_sagital_index = None
308
+ self.current_horizontal_index = None
309
+
310
+ self.anchor_coronal_index = None
311
+ self.anchor_sagital_index = None
312
+ self.anchor_horizontal_index = None
313
+
314
+ self.has_display_objects = False
315
+
316
+ rotation_gb_style = read_qss_file('qss/atlas_view_group_box.qss')
317
+ button_style = read_qss_file('qss/side_bar.qss')
318
+ spinbox_line_edit_style = read_qss_file('qss/hidden_line_edit.qss')
319
+
320
+ # self.display_atlas = None
321
+ # self.display_label = None
322
+ # self.scale = None
323
+ # self.interpolate = True
324
+ # self.oy_vector = np.array([0, 1, 0])
325
+ # self.oz_vector = np.array([0, 0, 1])
326
+ # self.ox_vector = np.array([1, 0, 0])
327
+
328
+ self.axis = gl.GLAxisItem()
329
+ self.axis.setSize(250, 700, 250)
330
+
331
+ self.grid = gl.GLGridItem()
332
+ self.grid.scale(2, 2, 1)
333
+
334
+ self.mesh = gl.GLMeshItem(smooth=True, color=[0.5, 0.5, 0.5, 0.2], shader='balloon')
335
+ self.mesh.setGLOptions('additive')
336
+
337
+ # 3d things
338
+ plate_color = [0.5, 0.5, 0.5, 0.2]
339
+ self.ap_plate_verts = np.array([[-1, 0, -1], [-1, 0, 1], [1, 0, 1], [1, 0, -1]])
340
+ self.ap_plate_faces = np.array([[0, 1, 2], [0, 2, 3]])
341
+ ap_plate_md = gl.MeshData(vertexes=self.ap_plate_verts, faces=self.ap_plate_faces)
342
+ self.ap_plate_mesh = gl.GLMeshItem(meshdata=ap_plate_md, smooth=False, color=plate_color)
343
+ self.ap_plate_mesh.setGLOptions('additive')
344
+
345
+ self.dv_plate_verts = np.array([[-1, -1, 0], [-1, 1, 0], [1, 1, 0], [1, -1, 0]])
346
+ self.dv_plate_faces = np.array([[0, 1, 2], [0, 2, 3]])
347
+ dv_plate_md = gl.MeshData(vertexes=self.dv_plate_verts, faces=self.dv_plate_faces)
348
+ self.dv_plate_mesh = gl.GLMeshItem(meshdata=dv_plate_md, smooth=False, color=plate_color)
349
+ self.dv_plate_mesh.setGLOptions('additive')
350
+
351
+ self.ml_plate_verts = np.array([[0, -1, -1], [0, 1, -1], [0, 1, 1], [0, -1, 1]])
352
+ self.ml_plate_faces = np.array([[0, 1, 2], [0, 2, 3]])
353
+ ml_plate_md = gl.MeshData(vertexes=self.ml_plate_verts, faces=self.ml_plate_faces)
354
+ self.ml_plate_mesh = gl.GLMeshItem(meshdata=ml_plate_md, smooth=False, color=plate_color)
355
+ self.ml_plate_mesh.setGLOptions('additive')
356
+
357
+ # ap - coronal
358
+ self.cimg = SliceStacks()
359
+ self.cpage_ctrl = PageController()
360
+ self.cpage_ctrl.sig_page_changed.connect(self.coronal_slice_page_changed)
361
+ self.cimg.sig_page_step_requested.connect(self.cpage_ctrl.step_by)
362
+ self.clut = pg.HistogramLUTWidget()
363
+ self.clut.setImageItem(self.cimg.img)
364
+
365
+ self.simg = SliceStacks()
366
+ self.spage_ctrl = PageController()
367
+ self.spage_ctrl.sig_page_changed.connect(self.sagital_slice_page_changed)
368
+ self.simg.sig_page_step_requested.connect(self.spage_ctrl.step_by)
369
+ self.slut = pg.HistogramLUTWidget()
370
+ self.slut.setImageItem(self.simg.img)
371
+
372
+ self.himg = SliceStacks()
373
+ self.hpage_ctrl = PageController()
374
+ self.hpage_ctrl.sig_page_changed.connect(self.horizontal_slice_page_changed)
375
+ self.himg.sig_page_step_requested.connect(self.hpage_ctrl.step_by)
376
+ self.hlut = pg.HistogramLUTWidget()
377
+ self.hlut.setImageItem(self.himg.img)
378
+
379
+ self.label_tree = LabelTree()
380
+
381
+ self.working_atlas = self.cimg
382
+ self.working_page_control = self.cpage_ctrl
383
+
384
+ self.slice_stack = SliceStack()
385
+
386
+
387
+ # radio buttons
388
+ self.radio_group = QFrame()
389
+ self.radio_group.setFixedHeight(50)
390
+ self.radio_group.setStyleSheet('QFrame{border: 1px solid gray; border-radius: 3px}')
391
+ radio_group_layout = QHBoxLayout(self.radio_group)
392
+ radio_group_layout.setContentsMargins(5, 0, 5, 0)
393
+ radio_group_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
394
+ self.section_rabnt1 = QRadioButton('Coronal')
395
+ self.section_rabnt1.setChecked(True)
396
+ self.section_rabnt2 = QRadioButton('Sagittal')
397
+ self.section_rabnt3 = QRadioButton('Horizontal')
398
+ self.section_rabnt1.setStyleSheet('color: white')
399
+ self.section_rabnt2.setStyleSheet('color: white')
400
+ self.section_rabnt3.setStyleSheet('color: white')
401
+ radio_group_layout.addWidget(self.section_rabnt1)
402
+ radio_group_layout.addWidget(self.section_rabnt2)
403
+ radio_group_layout.addWidget(self.section_rabnt3)
404
+
405
+ # angles
406
+ self.crotation_ctrl = SliceRotation()
407
+ self.crotation_ctrl.sig_slice_rotated.connect(self.coronal_slice_rotated)
408
+ self.srotation_ctrl = SliceRotation()
409
+ self.srotation_ctrl.sig_slice_rotated.connect(self.sagital_slice_rotated)
410
+ self.hrotation_ctrl = SliceRotation()
411
+ self.hrotation_ctrl.sig_slice_rotated.connect(self.horizontal_slice_rotated)
412
+
413
+
414
+ # opacity
415
+ atlas_op_label = QLabel('Opacity: ')
416
+ self.atlas_op_slider = QSlider(Qt.Orientation.Horizontal)
417
+ self.atlas_op_slider.setValue(100)
418
+ self.atlas_op_slider.setMinimum(0)
419
+ self.atlas_op_slider.setMaximum(100)
420
+ self.atlas_op_slider.valueChanged.connect(self.change_opacity_spinbox_value)
421
+ # self.atlas_op_slider.valueChanged.connect(self.sig_rescale_slider)
422
+
423
+ self.atlas_op_spinbox = QDoubleSpinBox()
424
+ self.atlas_op_spinbox.lineEdit().setStyleSheet(spinbox_line_edit_style)
425
+ self.atlas_op_spinbox.setValue(1)
426
+ self.atlas_op_spinbox.setRange(0, 1)
427
+ self.atlas_op_spinbox.setSingleStep(0.05)
428
+ self.atlas_op_spinbox.setMinimumSize(50, 20)
429
+ self.atlas_op_spinbox.valueChanged.connect(self.opacity_changed)
430
+
431
+ opacity_wrap = QFrame()
432
+ opacity_layout = QHBoxLayout(opacity_wrap)
433
+ opacity_layout.setSpacing(2)
434
+ opacity_layout.setContentsMargins(0, 0, 0, 0)
435
+ opacity_layout.addWidget(atlas_op_label)
436
+ opacity_layout.addWidget(self.atlas_op_slider)
437
+ opacity_layout.addWidget(self.atlas_op_spinbox)
438
+
439
+ # show brain region boundary of a volume atlas slice
440
+ self.show_boundary_btn = QPushButton('Show Boundary')
441
+ self.show_boundary_btn.setStyleSheet(button_style)
442
+ self.show_boundary_btn.setCheckable(True)
443
+
444
+ # turn on/off navigation
445
+ self.navigation_btn = QPushButton('Navigation')
446
+ self.navigation_btn.setStyleSheet(button_style)
447
+ self.navigation_btn.setCheckable(True)
448
+ self.navigation_btn.clicked.connect(self.navigation_btn_clicked)
449
+
450
+ # turn on/off rotation remain
451
+ self.rotation_remain_btn = QPushButton('Keep Slice Angles')
452
+ self.rotation_remain_btn.setStyleSheet(button_style)
453
+ self.rotation_remain_btn.setCheckable(True)
454
+ self.rotation_remain_btn.clicked.connect(self.rotation_remain_btn_clicked)
455
+
456
+ # coronal section control
457
+ coronal_rotation_wrap = QGroupBox('Coronal Section')
458
+ coronal_rotation_wrap.setStyleSheet(rotation_gb_style)
459
+ coronal_wrap_layout = QGridLayout(coronal_rotation_wrap)
460
+ coronal_wrap_layout.setContentsMargins(0, 0, 0, 0)
461
+ coronal_wrap_layout.setSpacing(10)
462
+
463
+ c_section_label_img = QPixmap(resource_path('icons/sidebar/c_section.png'))
464
+ c_section_label_img = c_section_label_img.scaled(QSize(40, 40))
465
+ c_section_label = QLabel('Tilt Z: ')
466
+ c_section_label.setPixmap(c_section_label_img)
467
+
468
+ c_lr_btn = RotationBtn("icons/sidebar/rotation_horizontal.svg")
469
+ c_ud_btn = RotationBtn("icons/sidebar/rotation_vertical.svg")
470
+
471
+ coronal_wrap_layout.addWidget(c_section_label, 0, 0, 2, 1)
472
+ coronal_wrap_layout.addWidget(c_lr_btn, 0, 1, 1, 1)
473
+ coronal_wrap_layout.addWidget(self.crotation_ctrl.h_slider, 0, 2, 1, 1)
474
+ coronal_wrap_layout.addWidget(self.crotation_ctrl.h_spinbox, 0, 3, 1, 1)
475
+ coronal_wrap_layout.addWidget(c_ud_btn, 1, 1, 1, 1)
476
+ coronal_wrap_layout.addWidget(self.crotation_ctrl.v_slider, 1, 2, 1, 1)
477
+ coronal_wrap_layout.addWidget(self.crotation_ctrl.v_spinbox, 1, 3, 1, 1)
478
+
479
+ # sagital section control
480
+ sagital_rotation_wrap = QGroupBox('Sagittal Section')
481
+ sagital_rotation_wrap.setStyleSheet(rotation_gb_style)
482
+ sagital_wrap_layout = QGridLayout(sagital_rotation_wrap)
483
+ sagital_wrap_layout.setContentsMargins(0, 0, 0, 0)
484
+ sagital_wrap_layout.setSpacing(10)
485
+
486
+ s_section_label_img = QPixmap(resource_path('icons/sidebar/s_section.png'))
487
+ s_section_label_img = s_section_label_img.scaled(QSize(40, 40))
488
+ s_section_label = QLabel()
489
+ s_section_label.setPixmap(s_section_label_img)
490
+
491
+ s_lr_btn = RotationBtn("icons/sidebar/rotation_horizontal.svg")
492
+ s_ud_btn = RotationBtn("icons/sidebar/rotation_vertical.svg")
493
+
494
+ sagital_wrap_layout.addWidget(s_section_label, 0, 0, 2, 1)
495
+ sagital_wrap_layout.addWidget(s_lr_btn, 0, 1, 1, 1)
496
+ sagital_wrap_layout.addWidget(self.srotation_ctrl.h_slider, 0, 2, 1, 1)
497
+ sagital_wrap_layout.addWidget(self.srotation_ctrl.h_spinbox, 0, 3, 1, 1)
498
+ sagital_wrap_layout.addWidget(s_ud_btn, 1, 1, 1, 1)
499
+ sagital_wrap_layout.addWidget(self.srotation_ctrl.v_slider, 1, 2, 1, 1)
500
+ sagital_wrap_layout.addWidget(self.srotation_ctrl.v_spinbox, 1, 3, 1, 1)
501
+
502
+ # horizontal section control
503
+ horizontal_rotation_wrap = QGroupBox('Horizontal Section')
504
+ horizontal_rotation_wrap.setStyleSheet(rotation_gb_style)
505
+ horizontal_wrap_layout = QGridLayout(horizontal_rotation_wrap)
506
+ horizontal_wrap_layout.setContentsMargins(0, 0, 0, 0)
507
+ horizontal_wrap_layout.setSpacing(10)
508
+
509
+ h_section_label_img = QPixmap(resource_path('icons/sidebar/h_section.png'))
510
+ h_section_label_img = h_section_label_img.scaled(QSize(40, 40))
511
+ h_section_label = QLabel()
512
+ h_section_label.setPixmap(h_section_label_img)
513
+
514
+ h_lr_btn = RotationBtn("icons/sidebar/rotation_horizontal.svg")
515
+ h_ud_btn = RotationBtn("icons/sidebar/rotation_vertical.svg")
516
+
517
+ horizontal_wrap_layout.addWidget(h_section_label, 0, 0, 2, 1)
518
+ horizontal_wrap_layout.addWidget(h_lr_btn, 0, 1, 1, 1)
519
+ horizontal_wrap_layout.addWidget(self.hrotation_ctrl.h_slider, 0, 2, 1, 1)
520
+ horizontal_wrap_layout.addWidget(self.hrotation_ctrl.h_spinbox, 0, 3, 1, 1)
521
+ horizontal_wrap_layout.addWidget(h_ud_btn, 1, 1, 1, 1)
522
+ horizontal_wrap_layout.addWidget(self.hrotation_ctrl.v_slider, 1, 2, 1, 1)
523
+ horizontal_wrap_layout.addWidget(self.hrotation_ctrl.v_spinbox, 1, 3, 1, 1)
524
+
525
+ self.sidebar_wrap = QFrame()
526
+ sidebar_wrap_layout = QVBoxLayout(self.sidebar_wrap)
527
+ sidebar_wrap_layout.setSpacing(0)
528
+ sidebar_wrap_layout.addWidget(self.radio_group)
529
+ sidebar_wrap_layout.addSpacing(10)
530
+ sidebar_wrap_layout.addWidget(opacity_wrap)
531
+ sidebar_wrap_layout.addSpacing(10)
532
+ sidebar_wrap_layout.addWidget(self.show_boundary_btn)
533
+ sidebar_wrap_layout.addSpacing(10)
534
+ sidebar_wrap_layout.addWidget(coronal_rotation_wrap)
535
+ sidebar_wrap_layout.addSpacing(10)
536
+ sidebar_wrap_layout.addWidget(sagital_rotation_wrap)
537
+ sidebar_wrap_layout.addSpacing(10)
538
+ sidebar_wrap_layout.addWidget(horizontal_rotation_wrap)
539
+ sidebar_wrap_layout.addSpacing(5)
540
+ sidebar_wrap_layout.addWidget(self.rotation_remain_btn)
541
+ sidebar_wrap_layout.addStretch(1)
542
+ sidebar_wrap_layout.addWidget(self.navigation_btn)
543
+
544
+ # self.lut.sigLookupTableChanged.connect(self.histlut_changed)
545
+ # self.lut.sigLevelsChanged.connect(self.histlut_changed)
546
+
547
+ def reset_3d_plates(self):
548
+ self.ap_plate_verts = np.array([[-1, 0, -1], [-1, 0, 1], [1, 0, 1], [1, 0, -1]])
549
+ self.ap_plate_faces = np.array([[0, 1, 2], [0, 2, 3]])
550
+
551
+ self.dv_plate_verts = np.array([[-1, -1, 0], [-1, 1, 0], [1, 1, 0], [1, -1, 0]])
552
+ self.dv_plate_faces = np.array([[0, 1, 2], [0, 2, 3]])
553
+
554
+ self.ml_plate_verts = np.array([[0, -1, -1], [0, 1, -1], [0, 1, 1], [0, -1, 1]])
555
+ self.ml_plate_faces = np.array([[0, 1, 2], [0, 2, 3]])
556
+
557
+ def set_slice_data(self, slice_data):
558
+ self.slice_image_data = slice_data.copy()
559
+ self.slice_stack.set_data(slice_data)
560
+ self.slice_size = np.ravel(self.slice_stack.img_layer.image.shape[:2])
561
+ slice_scale = np.max(self.slice_size / self.slice_tb_size_base)
562
+ self.slice_tb_size = self.slice_size / slice_scale
563
+ self.slice_tb_size = self.slice_tb_size[::-1].astype(int)
564
+
565
+ self.working_atlas = self.slice_stack
566
+
567
+ rect = (0, 0, int(self.slice_size[1]), int(self.slice_size[0]))
568
+ self.corner_points, self.side_lines = get_corner_line_from_rect(rect)
569
+ self.working_atlas.image_dict['tri_pnts'].set_range(x_range=self.slice_size[1] - 1,
570
+ y_range=self.slice_size[0] - 1)
571
+
572
+ def set_slice_info(self, slice_info):
573
+ self.slice_cut = slice_info.cut
574
+ self.slice_width = slice_info.width
575
+ self.slice_height = slice_info.height
576
+ self.slice_distance = slice_info.distance
577
+ self.slice_info_ready = slice_info_is_ready(
578
+ self.slice_width, self.slice_height, self.slice_distance, self.slice_bregma
579
+ )
580
+
581
+ def save_slice_data_and_info(self):
582
+ data = {'data': self.slice_image_data,
583
+ 'cut': self.slice_cut,
584
+ 'width': self.slice_width,
585
+ 'height': self.slice_height,
586
+ 'distance': self.slice_distance,
587
+ 'Bregma': self.slice_bregma,
588
+ 'ready': self.slice_info_ready}
589
+ return data
590
+
591
+ def set_slice_data_and_info(self, slice_data):
592
+ img_data = slice_data['data'].copy()
593
+ self.slice_cut = slice_data['cut']
594
+ self.slice_width = slice_data['width']
595
+ self.slice_height = slice_data['height']
596
+ self.slice_distance = slice_data['distance']
597
+ self.slice_bregma = slice_data['Bregma']
598
+ self.slice_info_ready = slice_info_is_ready(
599
+ self.slice_width, self.slice_height, self.slice_distance, self.slice_bregma
600
+ )
601
+ self.set_slice_data(img_data)
602
+
603
+ def get_slice_coords(self, ruler_data):
604
+ x, y, z = get_slice_atlas_coord(ruler_data, self.slice_cut, self.slice_size,
605
+ self.slice_width, self.slice_height, self.slice_distance, self.slice_bregma)
606
+ return x, y, z
607
+
608
+ def check_info_ready(self):
609
+ self.slice_info_ready = slice_info_is_ready(
610
+ self.slice_width, self.slice_height, self.slice_distance, self.slice_bregma
611
+ )
612
+
613
+ def clear_slice_info(self):
614
+ if self.slice_image_data is not None:
615
+ self.slice_cut = 'Coronal'
616
+ self.slice_width = 0
617
+ self.slice_height = 0
618
+ self.slice_distance = 0
619
+ self.slice_bregma = []
620
+ self.slice_info_ready = False
621
+ else:
622
+ return
623
+
624
+ def set_data(self, atlas_data, atlas_label, atlas_info, label_info, boundaries):
625
+ if self.atlas_data is not None:
626
+ self.reset_3d_plates()
627
+ self.clear_all_display_obj()
628
+
629
+ self.atlas_data = atlas_data
630
+ self.atlas_label = atlas_label
631
+ self.label_info = label_info
632
+ self.atlas_boundary = boundaries
633
+ self.label_tree.set_labels(label_info)
634
+
635
+ self.atlas_size = self.atlas_data.shape
636
+ self.anterior_info = atlas_info[0]
637
+ self.dorsal_info = atlas_info[1]
638
+ self.right_info = atlas_info[2]
639
+ self.vox_size_um = atlas_info[3]['vxsize']
640
+
641
+ if np.all(np.ravel(atlas_info[3]['Bregma']) == 0):
642
+ da_bregma = (int(self.atlas_size[1] / 2), int(self.atlas_size[2] / 2), int(self.atlas_size[0] / 2))
643
+ else:
644
+ da_bregma = atlas_info[3]['Bregma']
645
+ inverse_b2 = self.atlas_size[0] - 1 - da_bregma[2]
646
+ self.Bregma = (inverse_b2, da_bregma[0], da_bregma[1])
647
+ self.rotate_origin_3d = np.array(da_bregma)
648
+ self.origin_3d = np.array(da_bregma)
649
+
650
+ self.current_coronal_index = self.origin_3d[1]
651
+ self.current_sagital_index = self.origin_3d[0]
652
+ self.current_horizontal_index = self.origin_3d[2]
653
+
654
+ self.cpage_ctrl.set_max(self.atlas_size[2] - 1)
655
+ self.spage_ctrl.set_max(self.atlas_size[1] - 1)
656
+ self.hpage_ctrl.set_max(self.atlas_size[0] - 1)
657
+
658
+ if self.current_coronal_index == 0:
659
+ self.coronal_slice_page_changed(self.current_coronal_index)
660
+ self.cpage_ctrl.page_label.setText('0')
661
+ else:
662
+ self.cpage_ctrl.set_val(self.current_coronal_index)
663
+
664
+ if self.current_sagital_index == 0:
665
+ self.sagital_slice_page_changed(self.current_sagital_index)
666
+ self.spage_ctrl.page_label.setText('0')
667
+ else:
668
+ self.spage_ctrl.set_val(self.current_sagital_index)
669
+
670
+ if self.current_horizontal_index == 0:
671
+ self.horizontal_slice_page_changed(self.current_horizontal_index)
672
+ self.hpage_ctrl.page_label.setText('0')
673
+ else:
674
+ self.hpage_ctrl.set_val(self.current_horizontal_index)
675
+
676
+ self.cimg.label_img.setLevels(levels=[0, self.label_tree.label_level])
677
+ self.simg.label_img.setLevels(levels=[0, self.label_tree.label_level])
678
+ self.himg.label_img.setLevels(levels=[0, self.label_tree.label_level])
679
+
680
+ lut = self.label_tree.lookup_table()
681
+ self.cimg.label_img.setLookupTable(lut=lut)
682
+ self.simg.label_img.setLookupTable(lut=lut)
683
+ self.himg.label_img.setLookupTable(lut=lut)
684
+
685
+ self.c_size = np.array([self.atlas_size[0], self.atlas_size[1]])
686
+ coronal_scale = np.max(self.c_size / self.slice_tb_size_base)
687
+ self.coronal_tb_size = self.c_size[::-1] / coronal_scale
688
+ self.coronal_tb_size = self.coronal_tb_size.astype(int)
689
+
690
+ self.s_size = np.array([self.atlas_size[0], self.atlas_size[2]])
691
+ sagital_scale = np.max(self.s_size / self.slice_tb_size_base)
692
+ self.sagital_tb_size = self.s_size[::-1] / sagital_scale
693
+ self.sagital_tb_size = self.sagital_tb_size.astype(int)
694
+
695
+ self.h_size = np.array([self.atlas_size[1], self.atlas_size[2]])
696
+ horizontal_scale = np.max(self.h_size / self.slice_tb_size_base)
697
+ self.horizontal_tb_size = self.h_size[::-1] / horizontal_scale
698
+ self.horizontal_tb_size = self.horizontal_tb_size.astype(int)
699
+
700
+ self.update_3d_plate_component()
701
+
702
+ def update_3d_plate_component(self):
703
+ self.ap_plate_verts[:, 0] = self.ap_plate_verts[:, 0] * self.atlas_size[1] * 0.5
704
+ self.ap_plate_verts[:, 2] = self.ap_plate_verts[:, 2] * self.atlas_size[0] * 0.5
705
+
706
+ self.dv_plate_verts[:, 0] = self.dv_plate_verts[:, 0] * self.atlas_size[1] * 0.5
707
+ self.dv_plate_verts[:, 1] = self.dv_plate_verts[:, 1] * self.atlas_size[2] * 0.5
708
+
709
+ self.ml_plate_verts[:, 1] = self.ml_plate_verts[:, 1] * self.atlas_size[2] * 0.5
710
+ self.ml_plate_verts[:, 2] = self.ml_plate_verts[:, 2] * self.atlas_size[0] * 0.5
711
+
712
+ z_slide_dist = (self.atlas_size[0] * 0.5 - self.origin_3d[2])
713
+ y_slide_dist = (self.atlas_size[2] * 0.5 - self.origin_3d[1])
714
+ x_slide_dist = (self.atlas_size[1] * 0.5 - self.origin_3d[0])
715
+
716
+ self.ap_plate_verts = self.ap_plate_verts + np.array([x_slide_dist, 0, z_slide_dist])
717
+ self.dv_plate_verts = self.dv_plate_verts + np.array([x_slide_dist, y_slide_dist, 0])
718
+ self.ml_plate_verts = self.ml_plate_verts + np.array([0, y_slide_dist, z_slide_dist])
719
+
720
+ ap_plate_md = gl.MeshData(vertexes=self.ap_plate_verts, faces=self.ap_plate_faces)
721
+ self.ap_plate_mesh.setMeshData(meshdata=ap_plate_md)
722
+
723
+ dv_plate_md = gl.MeshData(vertexes=self.dv_plate_verts, faces=self.dv_plate_faces)
724
+ self.dv_plate_mesh.setMeshData(meshdata=dv_plate_md)
725
+
726
+ ml_plate_md = gl.MeshData(vertexes=self.ml_plate_verts, faces=self.ml_plate_faces)
727
+ self.ml_plate_mesh.setMeshData(meshdata=ml_plate_md)
728
+
729
+ def working_cut_changed(self, atlas_display):
730
+ self.pre_trajectory_changed()
731
+ if atlas_display == "coronal":
732
+ self.working_atlas = self.cimg
733
+ self.working_page_control = self.cpage_ctrl
734
+ self.slice_tb_size = self.coronal_tb_size
735
+ self.slice_size = self.c_size
736
+ elif atlas_display == "sagittal":
737
+ self.working_atlas = self.simg
738
+ self.working_page_control = self.spage_ctrl
739
+ self.slice_tb_size = self.sagital_tb_size
740
+ self.slice_size = self.s_size
741
+ else:
742
+ self.working_atlas = self.himg
743
+ self.working_page_control = self.hpage_ctrl
744
+ self.slice_tb_size = self.horizontal_tb_size
745
+ self.slice_size = self.h_size
746
+ rect = (0, 0, int(self.slice_size[1]), int(self.slice_size[0]))
747
+ self.corner_points, self.side_lines = get_corner_line_from_rect(rect)
748
+ self.working_atlas.image_dict['tri_pnts'].set_range(x_range=self.slice_size[1] - 1,
749
+ y_range=self.slice_size[0] - 1)
750
+
751
+ def navigation_btn_clicked(self):
752
+ if self.navigation_btn.isChecked():
753
+ # self.
754
+ self.cimg.v_line.setVisible(True)
755
+ self.cimg.h_line.setVisible(True)
756
+ self.himg.v_line.setVisible(True)
757
+ self.himg.h_line.setVisible(True)
758
+ self.simg.v_line.setVisible(True)
759
+ self.simg.h_line.setVisible(True)
760
+ else:
761
+ self.cimg.v_line.setVisible(False)
762
+ self.cimg.h_line.setVisible(False)
763
+ self.himg.v_line.setVisible(False)
764
+ self.himg.h_line.setVisible(False)
765
+ self.simg.v_line.setVisible(False)
766
+ self.simg.h_line.setVisible(False)
767
+
768
+ def rotation_remain_btn_clicked(self):
769
+ if self.rotation_remain_btn.isChecked():
770
+ self.remain_angle = True
771
+ else:
772
+ self.remain_angle = False
773
+
774
+ def change_opacity_spinbox_value(self):
775
+ val = self.atlas_op_slider.value()
776
+ self.atlas_op_spinbox.setValue(val / 100)
777
+
778
+ def opacity_changed(self):
779
+ val = self.atlas_op_spinbox.value()
780
+ if self.atlas_op_slider.value() != int(val * 100):
781
+ self.atlas_op_slider.setValue(int(val * 100))
782
+ self.cimg.label_img.setOpts(opacity=val)
783
+ self.simg.label_img.setOpts(opacity=val)
784
+ self.himg.label_img.setOpts(opacity=val)
785
+
786
+ # coronal page number changed and slice rotated
787
+ def set_perfect_coronal_slice(self):
788
+ da_atlas_slice = self.atlas_data[:, :, self.current_coronal_index]
789
+ da_atlas_label = self.atlas_label[:, :, self.current_coronal_index]
790
+ da_atlas_contour = None
791
+ if self.cimg.boundary.isVisible():
792
+ if self.atlas_boundary is None:
793
+ da_atlas_contour = make_contour_img(da_atlas_label)
794
+ else:
795
+ da_atlas_contour = self.atlas_boundary['c_contour'][
796
+ :, :, self.current_coronal_index
797
+ ]
798
+ display_label = self.label_tree.map_labels(da_atlas_label)
799
+ self.cimg.set_data(
800
+ da_atlas_slice,
801
+ da_atlas_label,
802
+ da_atlas_contour,
803
+ scale=None,
804
+ display_label=display_label,
805
+ )
806
+
807
+ slide_dist = (self.current_coronal_index - self.origin_3d[1])
808
+ ap_plate_verts = self.ap_plate_verts + np.array([0, slide_dist, 0])
809
+ ap_plate_md = gl.MeshData(vertexes=ap_plate_verts, faces=self.ap_plate_faces)
810
+ self.ap_plate_mesh.setMeshData(meshdata=ap_plate_md)
811
+ self.get_3d_origin()
812
+
813
+ def rotate_coronal_current_slice(self):
814
+ # calculate for 2d rotation
815
+ c_id = self.current_coronal_index
816
+ s_id = self.current_sagital_index
817
+ h_id = self.atlas_size[0] - 1 - self.current_horizontal_index
818
+
819
+ z_angle = self.coronal_rads[0]
820
+ x_angle = self.coronal_rads[1]
821
+ self.c_rotm_2d = np.dot(rotation_x(z_angle), rotation_y(x_angle))
822
+
823
+ o_val = np.array([0, 0, c_id])
824
+ o_rot = np.array([h_id, s_id, c_id])
825
+
826
+ oz_vector = np.dot(self.c_rotm_2d, np.array([1, 0, 0]))
827
+ ox_vector = np.dot(self.c_rotm_2d, np.array([0, 1, 0]))
828
+
829
+ oval_new = o_rot + np.dot(self.c_rotm_2d, o_val - o_rot)
830
+
831
+ ox_length = self.atlas_size[1]
832
+ oz_length = self.atlas_size[0]
833
+
834
+ atlas = fn.affineSlice(self.atlas_data, shape=(oz_length, ox_length), vectors=[oz_vector, ox_vector],
835
+ origin=(oval_new[0], oval_new[1], oval_new[2]), axes=(0, 1, 2), order=1)
836
+ label = fn.affineSlice(self.atlas_label, shape=(oz_length, ox_length), vectors=[oz_vector, ox_vector],
837
+ origin=(oval_new[0], oval_new[1], oval_new[2]), axes=(0, 1, 2), order=0)
838
+ da_contour = make_contour_img(label) if self.cimg.boundary.isVisible() else None
839
+ display_label = self.label_tree.map_labels(label)
840
+ self.cimg.set_data(
841
+ atlas, label, da_contour, scale=None, display_label=display_label
842
+ )
843
+
844
+ self.c_rotm_3d = np.dot(rotation_z(z_angle), rotation_x(-x_angle))
845
+
846
+ slide_dist = self.current_coronal_index - self.origin_3d[1]
847
+ ap_plate_verts = self.ap_plate_verts + np.array([0, slide_dist, 0])
848
+ ap_plate_verts = np.dot(self.c_rotm_3d, (ap_plate_verts - self.rotate_origin_3d).T).T + self.rotate_origin_3d
849
+ ap_plate_md = gl.MeshData(vertexes=ap_plate_verts, faces=self.ap_plate_faces)
850
+ self.ap_plate_mesh.setMeshData(meshdata=ap_plate_md)
851
+
852
+ def coronal_slice_page_changed(self, page_number):
853
+ self.clear_all_display_obj()
854
+ if self.atlas_data is None or self.atlas_label is None:
855
+ return
856
+ self.current_coronal_index = page_number
857
+ self.set_perfect_coronal_slice()
858
+ if self.remain_angle:
859
+ if self.coronal_rotated:
860
+ self.rotate_coronal_current_slice()
861
+ else:
862
+ if self.coronal_rotated:
863
+ self.crotation_ctrl.h_spinbox.blockSignals(True)
864
+ self.crotation_ctrl.v_spinbox.blockSignals(True)
865
+ self.crotation_ctrl.h_slider.setValue(0)
866
+ self.crotation_ctrl.v_slider.setValue(0)
867
+ self.crotation_ctrl.h_spinbox.blockSignals(False)
868
+ self.crotation_ctrl.v_spinbox.blockSignals(False)
869
+ self.coronal_rotated = False
870
+
871
+ def coronal_slice_rotated(self, rads):
872
+ self.clear_all_display_obj()
873
+ if self.atlas_data is None or self.atlas_label is None:
874
+ return
875
+ self.coronal_rads = rads
876
+ if np.all(np.ravel(rads) == 0):
877
+ if self.coronal_rotated:
878
+ self.set_perfect_coronal_slice()
879
+ self.coronal_rotated = False
880
+ else:
881
+ self.rotate_coronal_current_slice()
882
+ self.coronal_rotated = True
883
+
884
+ # sagittal page number changed and slice rotated
885
+ def set_perfect_sagittal_slice(self):
886
+ da_atlas_slice = self.atlas_data[:, self.current_sagital_index, :]
887
+ da_atlas_label = self.atlas_label[:, self.current_sagital_index, :]
888
+ da_atlas_contour = None
889
+ if self.simg.boundary.isVisible():
890
+ if self.atlas_boundary is None:
891
+ da_atlas_contour = make_contour_img(da_atlas_label)
892
+ else:
893
+ da_atlas_contour = self.atlas_boundary['s_contour'][
894
+ :, self.current_sagital_index, :
895
+ ]
896
+ display_label = self.label_tree.map_labels(da_atlas_label)
897
+ self.simg.set_data(
898
+ da_atlas_slice,
899
+ da_atlas_label,
900
+ da_atlas_contour,
901
+ scale=None,
902
+ display_label=display_label,
903
+ )
904
+
905
+ slide_dist = (self.current_sagital_index - self.origin_3d[0])
906
+ ml_plate_verts = self.ml_plate_verts + np.array([slide_dist, 0, 0])
907
+ ml_plate_md = gl.MeshData(vertexes=ml_plate_verts, faces=self.ml_plate_faces)
908
+ self.ml_plate_mesh.setMeshData(meshdata=ml_plate_md)
909
+ self.get_3d_origin()
910
+
911
+ def rotate_sagittal_current_slice(self):
912
+ # calculate for 2d rotation
913
+ c_id = self.current_coronal_index
914
+ s_id = self.current_sagital_index
915
+ h_id = self.atlas_size[0] - 1 - self.current_horizontal_index
916
+
917
+ z_angle = self.sagittal_rads[0]
918
+ y_angle = self.sagittal_rads[1]
919
+ self.s_rotm_2d = np.dot(rotation_x(z_angle), rotation_z(y_angle))
920
+
921
+ o_val = np.array([0, s_id, 0])
922
+ o_rot = np.array([h_id, s_id, c_id])
923
+
924
+ oz_vector = np.dot(self.s_rotm_2d, np.array([1, 0, 0]))
925
+ oy_vector = np.dot(self.s_rotm_2d, np.array([0, 0, 1]))
926
+
927
+ oval_new = o_rot + np.dot(self.s_rotm_2d, o_val - o_rot)
928
+
929
+ oy_length = self.atlas_size[2]
930
+ oz_length = self.atlas_size[0]
931
+
932
+ atlas = fn.affineSlice(self.atlas_data, shape=(oz_length, oy_length), vectors=[oz_vector, oy_vector],
933
+ origin=(oval_new[0], oval_new[1], oval_new[2]), axes=(0, 1, 2), order=1)
934
+ label = fn.affineSlice(self.atlas_label, shape=(oz_length, oy_length), vectors=[oz_vector, oy_vector],
935
+ origin=(oval_new[0], oval_new[1], oval_new[2]), axes=(0, 1, 2), order=0)
936
+ da_contour = make_contour_img(label) if self.simg.boundary.isVisible() else None
937
+ display_label = self.label_tree.map_labels(label)
938
+ self.simg.set_data(
939
+ atlas, label, da_contour, scale=None, display_label=display_label
940
+ )
941
+
942
+ self.s_rotm_3d = np.dot(rotation_z(z_angle), rotation_y(-y_angle))
943
+
944
+ slide_dist = (self.current_sagital_index - self.Bregma[1])
945
+ ml_plate_verts = self.ml_plate_verts + np.array([slide_dist, 0, 0])
946
+ ml_plate_verts = np.dot(self.s_rotm_3d, (ml_plate_verts - self.rotate_origin_3d).T).T + self.rotate_origin_3d
947
+ ml_plate_md = gl.MeshData(vertexes=ml_plate_verts, faces=self.ml_plate_faces)
948
+ self.ml_plate_mesh.setMeshData(meshdata=ml_plate_md)
949
+
950
+ def sagital_slice_page_changed(self, page_number):
951
+ self.clear_all_display_obj()
952
+ if self.atlas_data is None or self.atlas_label is None:
953
+ return
954
+ self.current_sagital_index = page_number
955
+ self.set_perfect_sagittal_slice()
956
+ if self.remain_angle:
957
+ if self.sagittal_rotated:
958
+ self.rotate_sagittal_current_slice()
959
+ else:
960
+ if self.sagittal_rotated:
961
+ self.srotation_ctrl.h_spinbox.blockSignals(True)
962
+ self.srotation_ctrl.v_spinbox.blockSignals(True)
963
+ self.srotation_ctrl.h_slider.setValue(0)
964
+ self.srotation_ctrl.v_slider.setValue(0)
965
+ self.srotation_ctrl.h_spinbox.blockSignals(False)
966
+ self.srotation_ctrl.v_spinbox.blockSignals(False)
967
+ self.sagittal_rotated = False
968
+
969
+ def sagital_slice_rotated(self, rads):
970
+ self.clear_all_display_obj()
971
+ if self.atlas_data is None or self.atlas_label is None:
972
+ return
973
+ self.sagittal_rads = rads
974
+ if np.all(np.ravel(rads) == 0):
975
+ if self.sagittal_rotated:
976
+ self.set_perfect_sagittal_slice()
977
+ self.sagittal_rotated = False
978
+ else:
979
+ self.rotate_sagittal_current_slice()
980
+ self.sagittal_rotated = True
981
+
982
+ # horizontal page number changed and slice rotated
983
+ def set_perfect_horizontal_slice(self):
984
+ slice_number = self.atlas_size[0] - 1 - self.current_horizontal_index
985
+ da_atlas_slice = self.atlas_data[slice_number, :, :]
986
+ da_atlas_label = self.atlas_label[slice_number, :, :]
987
+ da_atlas_contour = None
988
+ if self.himg.boundary.isVisible():
989
+ if self.atlas_boundary is None:
990
+ da_atlas_contour = make_contour_img(da_atlas_label)
991
+ else:
992
+ da_atlas_contour = self.atlas_boundary['h_contour'][
993
+ slice_number, :, :
994
+ ]
995
+ display_label = self.label_tree.map_labels(da_atlas_label)
996
+ self.himg.set_data(
997
+ da_atlas_slice,
998
+ da_atlas_label,
999
+ da_atlas_contour,
1000
+ scale=None,
1001
+ display_label=display_label,
1002
+ )
1003
+
1004
+ slide_dist = (self.current_horizontal_index - self.origin_3d[2])
1005
+ dv_plate_verts = self.dv_plate_verts + np.array([0, 0, slide_dist])
1006
+ dv_plate_md = gl.MeshData(vertexes=dv_plate_verts, faces=self.dv_plate_faces)
1007
+ self.dv_plate_mesh.setMeshData(meshdata=dv_plate_md)
1008
+ self.get_3d_origin()
1009
+
1010
+ def rotate_horizontal_current_slice(self):
1011
+ c_id = self.current_coronal_index
1012
+ s_id = self.current_sagital_index
1013
+ h_id = self.atlas_size[0] - 1 - self.current_horizontal_index
1014
+
1015
+ x_angle = self.horizontal_rads[0]
1016
+ y_angle = self.horizontal_rads[1]
1017
+ self.h_rotm_2d = np.dot(rotation_z(y_angle), rotation_y(x_angle))
1018
+
1019
+ o_val = np.array([h_id, 0, 0])
1020
+ o_rot = np.array([h_id, s_id, c_id])
1021
+
1022
+ ox_vector = np.dot(self.h_rotm_2d, np.array([0, 1, 0]))
1023
+ oy_vector = np.dot(self.h_rotm_2d, np.array([0, 0, 1]))
1024
+
1025
+ oval_new = o_rot + np.dot(self.h_rotm_2d, o_val - o_rot)
1026
+
1027
+ oy_length = self.atlas_size[2]
1028
+ ox_length = self.atlas_size[1]
1029
+
1030
+ atlas = fn.affineSlice(self.atlas_data, shape=(ox_length, oy_length), vectors=[ox_vector, oy_vector],
1031
+ origin=(oval_new[0], oval_new[1], oval_new[2]), axes=(0, 1, 2), order=1)
1032
+ label = fn.affineSlice(self.atlas_label, shape=(ox_length, oy_length), vectors=[ox_vector, oy_vector],
1033
+ origin=(oval_new[0], oval_new[1], oval_new[2]), axes=(0, 1, 2), order=0)
1034
+ da_contour = make_contour_img(label) if self.himg.boundary.isVisible() else None
1035
+ display_label = self.label_tree.map_labels(label)
1036
+ self.himg.set_data(
1037
+ atlas, label, da_contour, scale=None, display_label=display_label
1038
+ )
1039
+
1040
+ self.h_rotm_3d = np.dot(rotation_y(-y_angle), rotation_x(-x_angle))
1041
+
1042
+ slide_dist = (self.Bregma[0] - h_id)
1043
+ dv_plate_verts = self.dv_plate_verts + np.array([0, 0, slide_dist])
1044
+ dv_plate_verts = np.dot(self.h_rotm_3d, (dv_plate_verts - self.rotate_origin_3d).T).T + self.rotate_origin_3d
1045
+ dv_plate_md = gl.MeshData(vertexes=dv_plate_verts, faces=self.dv_plate_faces)
1046
+ self.dv_plate_mesh.setMeshData(meshdata=dv_plate_md)
1047
+
1048
+ def horizontal_slice_page_changed(self, page_number):
1049
+ self.clear_all_display_obj()
1050
+ if self.atlas_data is None or self.atlas_label is None:
1051
+ return
1052
+ self.current_horizontal_index = page_number
1053
+
1054
+ self.set_perfect_horizontal_slice()
1055
+ if self.remain_angle:
1056
+ if self.horizontal_rotated:
1057
+ self.rotate_horizontal_current_slice()
1058
+ else:
1059
+ if self.horizontal_rotated:
1060
+ self.hrotation_ctrl.h_spinbox.blockSignals(True)
1061
+ self.hrotation_ctrl.v_spinbox.blockSignals(True)
1062
+ self.hrotation_ctrl.h_slider.setValue(0)
1063
+ self.hrotation_ctrl.v_slider.setValue(0)
1064
+ self.hrotation_ctrl.h_spinbox.blockSignals(False)
1065
+ self.hrotation_ctrl.v_spinbox.blockSignals(False)
1066
+ self.horizontal_rotated = False
1067
+
1068
+ def horizontal_slice_rotated(self, rads):
1069
+ self.clear_all_display_obj()
1070
+ if self.atlas_data is None or self.atlas_label is None:
1071
+ return
1072
+ self.horizontal_rads = rads
1073
+ if np.all(np.ravel(rads) == 0):
1074
+ if self.horizontal_rotated:
1075
+ self.set_perfect_horizontal_slice()
1076
+ self.horizontal_rotated = False
1077
+ else:
1078
+ self.rotate_horizontal_current_slice()
1079
+ self.horizontal_rotated = True
1080
+
1081
+ def set_boundary_visible(self, visible):
1082
+ stacks = (self.cimg, self.simg, self.himg)
1083
+ if visible:
1084
+ for stack in stacks:
1085
+ if stack.label_data is not None:
1086
+ stack.set_boundary_data(make_contour_img(stack.label_data))
1087
+ for stack in stacks:
1088
+ stack.boundary.setVisible(visible)
1089
+
1090
+ # get 3d rotation origin
1091
+ def get_3d_origin(self):
1092
+ c_id = self.current_coronal_index
1093
+ s_id = self.current_sagital_index
1094
+ h_id = self.current_horizontal_index
1095
+ o_rot = np.array([s_id, c_id, h_id])
1096
+ self.rotate_origin_3d = o_rot - self.origin_3d
1097
+
1098
+ #
1099
+ def rotate_cs_plane_after_merging_probe(self, display_data):
1100
+ # display data has to be the merged probe data
1101
+ self.cpage_ctrl.set_val(display_data['insertion_vox'][1])
1102
+ self.spage_ctrl.set_val(display_data['insertion_vox'][0])
1103
+ self.hpage_ctrl.set_val(display_data['insertion_vox'][2])
1104
+
1105
+ ap_sign, ml_sign = get_tilt_sign(display_data['insertion_coords'], display_data['terminus_coords'])
1106
+
1107
+ ap_angle = -1 * ap_sign * display_data['ap_angle']
1108
+ ml_angle = ml_sign * display_data['ml_angle']
1109
+
1110
+ start_pnt = display_data['insertion_coords_3d'].copy()
1111
+ end_pnt = display_data['terminus_coords_3d'].copy()
1112
+ # if ap_angle <= 30 and ap_angle >= -30:
1113
+ if ap_angle <= -1e-6 or ap_angle >= 1e-6:
1114
+ self.coronal_slice_rotated((0, np.radians(ap_angle)))
1115
+ rotated_back_p1 = np.dot(self.c_rotm_3d.T, start_pnt - self.rotate_origin_3d)
1116
+ rotated_back_p2 = np.dot(self.c_rotm_3d.T, end_pnt - self.rotate_origin_3d)
1117
+
1118
+ rotated_back_p1 = rotated_back_p1 + self.origin_3d + self.rotate_origin_3d
1119
+ rotated_back_p2 = rotated_back_p2 + self.origin_3d + self.rotate_origin_3d
1120
+
1121
+ c_start_pnt = [rotated_back_p1[0], self.atlas_size[0] - rotated_back_p1[2]]
1122
+ c_end_pnt = [rotated_back_p2[0], self.atlas_size[0] - rotated_back_p2[2]]
1123
+ else:
1124
+ c_start_pnt_3d = start_pnt + self.origin_3d
1125
+ c_end_pnt_3d = end_pnt + self.origin_3d
1126
+
1127
+ c_start_pnt = [c_start_pnt_3d[0], self.atlas_size[0] - c_start_pnt_3d[2]]
1128
+ c_end_pnt = [c_end_pnt_3d[0], self.atlas_size[0] - c_end_pnt_3d[2]]
1129
+
1130
+ if ml_angle <= -1e-6 or ml_angle >= 1e-6:
1131
+ self.sagital_slice_rotated((0, np.radians(ml_angle)))
1132
+ rotated_back_p1 = np.dot(self.s_rotm_3d.T, start_pnt - self.rotate_origin_3d)
1133
+ rotated_back_p2 = np.dot(self.s_rotm_3d.T, end_pnt - self.rotate_origin_3d)
1134
+
1135
+ rotated_back_p1 = rotated_back_p1 + self.origin_3d + self.rotate_origin_3d
1136
+ rotated_back_p2 = rotated_back_p2 + self.origin_3d + self.rotate_origin_3d
1137
+
1138
+ s_start_pnt = [rotated_back_p1[1], self.atlas_size[0] - rotated_back_p1[2]]
1139
+ s_end_pnt = [rotated_back_p2[1], self.atlas_size[0] - rotated_back_p2[2]]
1140
+ else:
1141
+ s_start_pnt_3d = start_pnt + self.origin_3d
1142
+ s_end_pnt_3d = end_pnt + self.origin_3d
1143
+
1144
+ s_start_pnt = [s_start_pnt_3d[0], self.atlas_size[0] - s_start_pnt_3d[2]]
1145
+ s_end_pnt = [s_end_pnt_3d[0], self.atlas_size[0] - s_end_pnt_3d[2]]
1146
+
1147
+ c_pos = np.stack([c_start_pnt, c_end_pnt], axis=0)
1148
+ s_pos = np.stack([s_start_pnt, s_end_pnt], axis=0)
1149
+
1150
+ self.cimg.add_display_obj_to_view(c_pos, display_data['vis_color'], 'probe')
1151
+ self.simg.add_display_obj_to_view(s_pos, display_data['vis_color'], 'probe')
1152
+ self.has_display_objects = True
1153
+
1154
+
1155
+ def clear_all_display_obj(self):
1156
+ if not self.has_display_objects:
1157
+ return
1158
+ c_n_obj = len(self.cimg.display_objects)
1159
+ c_obj_indexes = np.arange(c_n_obj)[::-1]
1160
+ for i in c_obj_indexes:
1161
+ self.cimg.remove_display_obj_from_view(i)
1162
+
1163
+ s_n_obj = len(self.simg.display_objects)
1164
+ s_obj_indexes = np.arange(s_n_obj)[::-1]
1165
+ for j in s_obj_indexes:
1166
+ self.simg.remove_display_obj_from_view(j)
1167
+
1168
+ self.cimg.display_objects.clear()
1169
+ self.simg.display_objects.clear()
1170
+
1171
+ self.has_display_objects = False
1172
+
1173
+
1174
+
1175
+
1176
+ def get_atlas_angles(self):
1177
+ c_ang = (self.crotation_ctrl.h_spinbox.value(), self.crotation_ctrl.v_spinbox.value())
1178
+ s_ang = (self.srotation_ctrl.h_spinbox.value(), self.srotation_ctrl.v_spinbox.value())
1179
+ h_ang = (self.hrotation_ctrl.h_spinbox.value(), self.hrotation_ctrl.v_spinbox.value())
1180
+ return [c_ang, s_ang, h_ang]
1181
+
1182
+ def set_volume_atlas_ctrl_data(self, atlas_ctrl_data):
1183
+ self.cpage_ctrl.page_slider.setValue(atlas_ctrl_data['coronal_index'])
1184
+ self.spage_ctrl.page_slider.setValue(atlas_ctrl_data['sagittal_index'])
1185
+ self.hpage_ctrl.page_slider.setValue(atlas_ctrl_data['horizontal_index'])
1186
+ atlas_rotation = atlas_ctrl_data['slice_rotation']
1187
+ self.crotation_ctrl.h_spinbox.setValue(atlas_rotation[0][0])
1188
+ self.crotation_ctrl.v_spinbox.setValue(atlas_rotation[0][1])
1189
+ self.srotation_ctrl.h_spinbox.setValue(atlas_rotation[1][0])
1190
+ self.srotation_ctrl.v_spinbox.setValue(atlas_rotation[1][1])
1191
+ self.hrotation_ctrl.h_spinbox.setValue(atlas_rotation[2][0])
1192
+ self.hrotation_ctrl.v_spinbox.setValue(atlas_rotation[2][1])
1193
+
1194
+ def get_coronal_3d(self, points2, coronal_index=None):
1195
+ if coronal_index is None:
1196
+ da_y = np.ones(len(points2)) * self.current_coronal_index
1197
+ else:
1198
+ da_y = coronal_index
1199
+ points3 = np.vstack([points2[:, 0], da_y, self.atlas_size[0] - points2[:, 1]]).T
1200
+ points3 = points3 - self.origin_3d
1201
+ if self.coronal_rotated:
1202
+ rot_mat = self.c_rotm_3d
1203
+ rotation_origin = self.rotate_origin_3d
1204
+ points3 = np.dot(rot_mat, (points3 - rotation_origin).T).T + rotation_origin
1205
+ return points3
1206
+
1207
+ def get_sagittal_3d(self, points2, sagital_index=None):
1208
+ if sagital_index is None:
1209
+ da_x = np.ones(len(points2)) * self.current_sagital_index
1210
+ else:
1211
+ da_x = sagital_index
1212
+ points3 = np.vstack([da_x, points2[:, 0], self.atlas_size[0] - points2[:, 1]]).T
1213
+ points3 = points3 - self.origin_3d
1214
+ if self.sagittal_rotated:
1215
+ rot_mat = self.s_rotm_3d
1216
+ rotation_origin = self.rotate_origin_3d
1217
+ points3 = np.dot(rot_mat, (points3 - rotation_origin).T).T + rotation_origin
1218
+ return points3
1219
+
1220
+ def get_horizontal_3d(self, points2, horizontal_index=None):
1221
+ if horizontal_index is None:
1222
+ da_z = np.ones(len(points2)) * self.current_horizontal_index
1223
+ else:
1224
+ da_z = horizontal_index
1225
+ points3 = np.vstack([points2[:, 1], points2[:, 0], da_z]).T
1226
+ points3 = points3 - self.origin_3d
1227
+ if self.horizontal_rotated:
1228
+ rot_mat = self.h_rotm_3d
1229
+ rotation_origin = self.rotate_origin_3d
1230
+ points3 = np.dot(rot_mat, (points3 - rotation_origin).T).T + rotation_origin
1231
+ return points3
1232
+
1233
+ def get_3d_data_from_2d_view(self, processing_data, atlas_display):
1234
+ if atlas_display == 'coronal':
1235
+ data = self.get_coronal_3d(processing_data)
1236
+ elif atlas_display == 'sagittal':
1237
+ data = self.get_sagittal_3d(processing_data)
1238
+ else:
1239
+ data = self.get_horizontal_3d(processing_data)
1240
+ return data
1241
+
1242
+ def pre_trajectory_changed(self):
1243
+ self.working_atlas.remove_pre_trajectories_vis_lines()
1244
+
1245
+ def get_pre_vis_data_for_volume_atlas(self, data, base_loc):
1246
+ base_loc = np.ravel(base_loc) / self.vox_size_um
1247
+ start_pnt, end_pnt = rotate_base_points(data, base_loc)
1248
+ return start_pnt, end_pnt
1249
+
1250
+ def get_pre_vis_data_for_slice_atlas(self, data, base_loc):
1251
+ start_pnt, end_pnt = rotate_base_points(data, base_loc)
1252
+ return start_pnt, end_pnt
1253
+
1254
+ def set_pre_vis_data(self, start_pnt, end_pnt):
1255
+ line_data = []
1256
+ for i in range(len(start_pnt)):
1257
+ da_pnts = np.asarray([start_pnt[i], end_pnt[i]])
1258
+ line_data.append(da_pnts)
1259
+ self.working_atlas.set_pre_design_vis_data(line_data)
1260
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=np.asarray(start_pnt))
1261
+
1262
+ def draw_pre_2d_vis_data_for_volume_atlas(self, data, base_loc):
1263
+ if len(data) == 1:
1264
+ base_loc = np.ravel(base_loc) / self.vox_size_um
1265
+ temp = np.zeros((len(base_loc), 2))
1266
+ temp[:, 0] = base_loc + data[0][0]
1267
+ temp[:, 1] = data[0][1]
1268
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=np.asarray(temp))
1269
+ elif len(data) == 2:
1270
+ start_pnt, end_pnt = self.get_pre_vis_data_for_volume_atlas(data, base_loc)
1271
+ temp = np.vstack([start_pnt, end_pnt])
1272
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=temp)
1273
+ else:
1274
+ self.working_atlas.image_dict['atlas-probe'].clear()
1275
+ self.working_atlas.remove_pre_trajectories_vis_lines()
1276
+
1277
+ def draw_pre_2d_vis_data_for_slice_atlas(self, data, base_loc):
1278
+ if self.slice_info_ready:
1279
+ base_loc = np.ravel(base_loc) / 1000 * self.slice_size[1] / self.slice_width
1280
+ if len(data) == 1:
1281
+ temp = np.zeros((len(base_loc), 2))
1282
+ temp[:, 0] = base_loc + data[0][0]
1283
+ temp[:, 1] = data[0][1]
1284
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=np.asarray(temp))
1285
+ elif len(data) == 2:
1286
+ start_pnt, end_pnt = self.get_pre_vis_data_for_slice_atlas(data, base_loc)
1287
+ temp = np.vstack([start_pnt, end_pnt])
1288
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=temp)
1289
+ else:
1290
+ self.working_atlas.image_dict['atlas-probe'].clear()
1291
+ self.working_atlas.remove_pre_trajectories_vis_lines()
1292
+ else:
1293
+ if len(data) == 1:
1294
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=np.asarray(data))
1295
+ elif len(data) == 2:
1296
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=np.asarray(data))
1297
+ self.working_atlas.set_pre_design_vis_data([np.asarray(data)])
1298
+ else:
1299
+ self.working_atlas.image_dict['atlas-probe'].clear()
1300
+ self.working_atlas.remove_pre_trajectories_vis_lines()
1301
+
1302
+
1303
+ # def get_pre_multi_probe_data(self, data, atlas_display, multi_settings):
1304
+ # x_vals = multi_settings['x_vals']
1305
+ # y_vals = multi_settings['y_vals']
1306
+ # n_probes = len(x_vals)
1307
+ #
1308
+ # if atlas_display == 'coronal':
1309
+ # data3 = self.get_coronal_3d(data)
1310
+ # elif atlas_display == 'sagittal':
1311
+ # data3 = self.get_sagittal_3d(data)
1312
+ # else:
1313
+ # data3 = self.get_horizontal_3d(data)
1314
+ #
1315
+ # pdata = np.asarray(data3)
1316
+ # p_order = np.argsort(pdata[:, 1])
1317
+ # pdata = pdata[p_order, :]
1318
+ #
1319
+ # direction = pdata[0] - pdata[1]
1320
+ # direction = direction / np.linalg.norm(direction)
1321
+ #
1322
+ # rot_m = get_direction_rotation(direction)
1323
+ # base_end = np.dot(rot_m.T, pdata[1] - pdata[0])
1324
+ #
1325
+ # base_start_3d = np.zeros((n_probes, 3))
1326
+ # base_start_3d[:, 1] = x_vals.copy()
1327
+ # base_start_3d[:, 2] = y_vals.copy()
1328
+ #
1329
+ # base_end_3d = base_start_3d.copy()
1330
+ # base_end_3d[:, 0] = base_end
1331
+ #
1332
+ # points3_list = []
1333
+ # for i in range(n_probes):
1334
+ # tp3 = np.vstack([base_start_3d[i], base_end_3d[i]])
1335
+ # points3 = np.dot(rot_m, tp3.T).T
1336
+ # points3_list.append(points3)
1337
+ #
1338
+ # return points3_list
1339
+
1340
+ def get_multi_probe_2d_vis_data(self, data, multi_settings):
1341
+ x_vals = multi_settings['x_vals']
1342
+ y_vals = multi_settings['y_vals']
1343
+ n_probes = len(x_vals)
1344
+
1345
+ vind = np.where(y_vals == 0)[0]
1346
+ if len(vind) == 0:
1347
+ vis_data = [data]
1348
+ else:
1349
+ vis_data = []
1350
+ base_loc = np.ravel(x_vals)[vind]
1351
+
1352
+ def get_plane_norm_vector(self, atlas_display):
1353
+ if atlas_display == 'coronal':
1354
+ if self.coronal_rotated:
1355
+ n_vec = np.dot(self.c_rotm_3d, np.array([0, -1, 0]))
1356
+ else:
1357
+ n_vec = np.array([0, -1, 0])
1358
+ elif atlas_display == 'sagittal':
1359
+ if self.sagittal_rotated:
1360
+ n_vec = np.dot(self.s_rotm_3d, np.array([1, 0, 0]))
1361
+ else:
1362
+ n_vec = np.array([1, 0, 0])
1363
+ else:
1364
+ if self.horizontal_rotated:
1365
+ n_vec = np.dot(self.h_rotm_3d, np.array([0, 0, 1]))
1366
+ else:
1367
+ n_vec = np.array([0, 0, 1])
1368
+ return n_vec
1369
+
1370
+
1371
+ #
1372
+ def draw_volume_pre_trajectory(self, data, n_pre_trajectory):
1373
+ if len(data) == 1:
1374
+ if n_pre_trajectory == 1:
1375
+
1376
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=np.asarray(data))
1377
+ else:
1378
+ base_loc = np.array([-375, -125, 125, 375]) / self.vox_size_um
1379
+ temp = np.stack([data[0][0] + base_loc, np.repeat(data[0][1], 4)], axis=1)
1380
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=np.asarray(temp))
1381
+ elif len(data) == 2:
1382
+ if n_pre_trajectory == 1:
1383
+ print('data', data)
1384
+ start_pnt, end_pnt = rotate_base_points(np.asarray(data), np.array([0]))
1385
+ print(start_pnt, end_pnt)
1386
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=np.asarray(data))
1387
+ else:
1388
+ base_loc = np.array([-375, -125, 125, 375]) / self.vox_size_um
1389
+ start_pnt, end_pnt = rotate_base_points(np.asarray(data), base_loc)
1390
+ temp = np.vstack([start_pnt, end_pnt])
1391
+ self.working_atlas.image_dict['atlas-probe'].setData(pos=temp)
1392
+ else:
1393
+ self.working_atlas.image_dict['atlas-probe'].clear()
1394
+
1395
+ def vis_3d_planes(self, vis):
1396
+ self.ap_plate_mesh.setVisible(vis)
1397
+ self.dv_plate_mesh.setVisible(vis)
1398
+ self.ml_plate_mesh.setVisible(vis)
1399
+
1400
+ def vis_3d_axes(self, vis):
1401
+ self.axis.setVisible(vis)
1402
+
1403
+ # ----------- clear atlas
1404
+ def clear_atlas_stacks(self):
1405
+ valid_keys = ['atlas-overlay', 'atlas-contour', 'atlas-mask', 'circle_follow', 'lasso_path', 'atlas-virus',
1406
+ 'atlas-probe', 'atlas-cells', 'atlas-drawing']
1407
+ for da_key in valid_keys:
1408
+ self.working_atlas.image_dict[da_key].clear()
1409
+
1410
+ def clear_volume_atlas(self):
1411
+ if self.atlas_label is None or self.atlas_data is None:
1412
+ return
1413
+ valid_keys = ['atlas-overlay', 'atlas-contour', 'atlas-mask', 'circle_follow', 'lasso_path', 'atlas-virus',
1414
+ 'atlas-probe', 'atlas-cells', 'atlas-drawing']
1415
+ for da_key in valid_keys:
1416
+ self.cimg.image_dict[da_key].clear()
1417
+ self.simg.image_dict[da_key].clear()
1418
+ self.himg.image_dict[da_key].clear()
1419
+ self.cimg.img.clear()
1420
+ self.simg.img.clear()
1421
+ self.himg.img.clear()
1422
+
1423
+
1424
+ def clear_slice_atlas(self):
1425
+ pass
1426
+
1427
+ def clear_atlas(self):
1428
+ self.clear_volume_atlas()
1429
+ self.clear_slice_atlas()
1430
+
1431
+
1432
+
1433
+
1434
+
1435
+