unveil-python 0.3.0__tar.gz → 0.4.0__tar.gz
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.
- {unveil_python-0.3.0/src/unveil_python.egg-info → unveil_python-0.4.0}/PKG-INFO +2 -2
- {unveil_python-0.3.0 → unveil_python-0.4.0}/README.md +1 -1
- {unveil_python-0.3.0 → unveil_python-0.4.0}/src/unveil/__init__.py +1 -1
- {unveil_python-0.3.0 → unveil_python-0.4.0}/src/unveil/core.py +54 -34
- {unveil_python-0.3.0 → unveil_python-0.4.0/src/unveil_python.egg-info}/PKG-INFO +2 -2
- {unveil_python-0.3.0 → unveil_python-0.4.0}/LICENSE +0 -0
- {unveil_python-0.3.0 → unveil_python-0.4.0}/pyproject.toml +0 -0
- {unveil_python-0.3.0 → unveil_python-0.4.0}/setup.cfg +0 -0
- {unveil_python-0.3.0 → unveil_python-0.4.0}/src/unveil/__main__.py +0 -0
- {unveil_python-0.3.0 → unveil_python-0.4.0}/src/unveil_python.egg-info/SOURCES.txt +0 -0
- {unveil_python-0.3.0 → unveil_python-0.4.0}/src/unveil_python.egg-info/dependency_links.txt +0 -0
- {unveil_python-0.3.0 → unveil_python-0.4.0}/src/unveil_python.egg-info/requires.txt +0 -0
- {unveil_python-0.3.0 → unveil_python-0.4.0}/src/unveil_python.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: unveil-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Implementation of UNVEIL
|
|
5
5
|
Author-email: Nicolas Delinte <nicolas.delinte@uclouvain.be>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -25,7 +25,7 @@ Dynamic: license-file
|
|
|
25
25
|
|
|
26
26
|
A simple tractography viewer implemented in python.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
<img width="1297" height="757" alt="image" src="https://github.com/user-attachments/assets/28807e18-17fb-485b-a8ce-2179cdef796b" />
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
The GUI is launched with the following python command in a python console:
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
A simple tractography viewer implemented in python.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<img width="1297" height="757" alt="image" src="https://github.com/user-attachments/assets/28807e18-17fb-485b-a8ce-2179cdef796b" />
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
The GUI is launched with the following python command in a python console:
|
|
@@ -19,7 +19,6 @@ from dipy.io.streamline import load_tractogram
|
|
|
19
19
|
from matplotlib import pyplot as plt
|
|
20
20
|
from matplotlib.colors import LinearSegmentedColormap
|
|
21
21
|
# from unravel.utils import get_streamline_density
|
|
22
|
-
# from unravel.viz import plot_trk # We'll use our local version below
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
def gifti_to_pyvista(gii_path):
|
|
@@ -51,9 +50,28 @@ def gifti_to_pyvista(gii_path):
|
|
|
51
50
|
return mesh
|
|
52
51
|
|
|
53
52
|
|
|
53
|
+
def convert_rgb_array_to_new_basis(rgb_array):
|
|
54
|
+
"""
|
|
55
|
+
Converts an array of RGB colors shape [n, 3] into a new basis.
|
|
56
|
+
|
|
57
|
+
rgb_array: numpy array of shape (n, 3) where each row is [R, G, B]
|
|
58
|
+
Returns: numpy array of shape (n, 3) where each row is [newR, newG, newB]
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
# Based on the Color-Blind Safe IBM Design Library Color Palette
|
|
62
|
+
C1 = np.array([220, 38, 127])
|
|
63
|
+
C2 = np.array([255, 176, 0])
|
|
64
|
+
C3 = np.array([120, 94, 240])
|
|
65
|
+
|
|
66
|
+
B = np.column_stack([C1, C2, C3])
|
|
67
|
+
new_rgb = rgb_array.dot(B.T)
|
|
68
|
+
|
|
69
|
+
return new_rgb
|
|
70
|
+
|
|
71
|
+
|
|
54
72
|
def plot_trk(trk_file, scalar=None, color_map='plasma', opacity: float = 1,
|
|
55
73
|
show_points: bool = False, background: str = 'black', plotter=None,
|
|
56
|
-
name=None, reset_camera=False):
|
|
74
|
+
name=None, reset_camera=False, color_blind: bool = False):
|
|
57
75
|
'''
|
|
58
76
|
3D render for .trk files.
|
|
59
77
|
|
|
@@ -115,10 +133,12 @@ def plot_trk(trk_file, scalar=None, color_map='plasma', opacity: float = 1,
|
|
|
115
133
|
point = streamlines._data
|
|
116
134
|
next_point = np.roll(point, -1, axis=0)
|
|
117
135
|
vs = next_point-point
|
|
136
|
+
ends = (streamlines._offsets+streamlines._lengths-1)
|
|
137
|
+
if color_blind:
|
|
138
|
+
vs = convert_rgb_array_to_new_basis(np.abs(vs))
|
|
118
139
|
norm = np.linalg.norm(vs, axis=1)
|
|
119
140
|
norm = np.stack((norm,)*3, axis=1, dtype=np.float32)
|
|
120
141
|
norm = np.divide(vs, norm, dtype=np.float64)
|
|
121
|
-
ends = (streamlines._offsets+streamlines._lengths-1)
|
|
122
142
|
norm[ends, :] = norm[ends-1, :]
|
|
123
143
|
scalars = np.abs(norm)
|
|
124
144
|
rgb = True
|
|
@@ -144,26 +164,22 @@ def plot_trk(trk_file, scalar=None, color_map='plasma', opacity: float = 1,
|
|
|
144
164
|
interpolate_before_map=False, render_lines_as_tubes=True,
|
|
145
165
|
line_width=2, point_size=point_size, rgb=rgb,
|
|
146
166
|
cmap=color_map, clim=color_lim, scalars=scalars, name=name,
|
|
147
|
-
reset_camera=reset_camera,
|
|
148
|
-
user_matrix=trk.affine)
|
|
167
|
+
reset_camera=reset_camera, user_matrix=trk.affine)
|
|
149
168
|
|
|
150
169
|
elif color_map == 'flesh':
|
|
151
170
|
p.add_mesh(mesh, opacity=opacity, diffuse=0.4, ambient=ambient,
|
|
152
171
|
interpolate_before_map=False, render_lines_as_tubes=True,
|
|
153
172
|
line_width=2, point_size=point_size, rgb=rgb,
|
|
154
173
|
color=[250, 225, 210], name=name,
|
|
155
|
-
reset_camera=reset_camera,
|
|
156
|
-
user_matrix=trk.affine)
|
|
174
|
+
reset_camera=reset_camera, user_matrix=trk.affine)
|
|
157
175
|
else:
|
|
158
176
|
p.add_mesh(mesh, opacity=opacity, diffuse=diffuse, ambient=ambient,
|
|
159
177
|
interpolate_before_map=False, render_lines_as_tubes=True,
|
|
160
178
|
line_width=2, point_size=point_size, rgb=rgb,
|
|
161
179
|
cmap=color_map, scalars=scalars, name=name,
|
|
162
|
-
reset_camera=reset_camera,
|
|
163
|
-
user_matrix=trk.affine)
|
|
180
|
+
reset_camera=reset_camera, user_matrix=trk.affine)
|
|
164
181
|
|
|
165
182
|
p.background_color = background
|
|
166
|
-
# Do not call p.show() here when using an embedded interactor
|
|
167
183
|
|
|
168
184
|
|
|
169
185
|
class TrkViewer(QWidget):
|
|
@@ -179,6 +195,8 @@ class TrkViewer(QWidget):
|
|
|
179
195
|
self.z = 0
|
|
180
196
|
self.initUI()
|
|
181
197
|
self.background = 'white'
|
|
198
|
+
self.color_blind = False
|
|
199
|
+
self.flicker = False
|
|
182
200
|
|
|
183
201
|
def initUI(self):
|
|
184
202
|
# Create the main layout
|
|
@@ -357,7 +375,6 @@ class TrkViewer(QWidget):
|
|
|
357
375
|
for roi_path in filePaths:
|
|
358
376
|
img = nib.load(roi_path)
|
|
359
377
|
roi = img.get_fdata()
|
|
360
|
-
affine = img.affine
|
|
361
378
|
|
|
362
379
|
datapv = pv.wrap(roi)
|
|
363
380
|
datapv.cell_data['labels'] = roi[:-1, :-1, :-1].flatten(order='F')
|
|
@@ -368,16 +385,10 @@ class TrkViewer(QWidget):
|
|
|
368
385
|
|
|
369
386
|
actor_name = f"roi_{roi_path.split('/')[-1]}"
|
|
370
387
|
|
|
371
|
-
self.plotter.add_mesh(
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
color="white",
|
|
376
|
-
user_matrix=affine,
|
|
377
|
-
reset_camera=False,
|
|
378
|
-
point_size=0,
|
|
379
|
-
render_lines_as_tubes=True,
|
|
380
|
-
)
|
|
388
|
+
self.plotter.add_mesh(smooth, name=actor_name, opacity=1,
|
|
389
|
+
color="white", user_matrix=img.affine,
|
|
390
|
+
reset_camera=False, point_size=0,
|
|
391
|
+
render_lines_as_tubes=True)
|
|
381
392
|
|
|
382
393
|
self.parent().refreshActorList()
|
|
383
394
|
|
|
@@ -402,25 +413,30 @@ class TrkViewer(QWidget):
|
|
|
402
413
|
|
|
403
414
|
self.plotter.background_color = self.background
|
|
404
415
|
|
|
416
|
+
def toggleColorBlind(self):
|
|
417
|
+
|
|
418
|
+
self.color_blind = not self.color_blind
|
|
419
|
+
self.update_trk_viewer()
|
|
420
|
+
|
|
405
421
|
def view_isometric(self):
|
|
406
|
-
# equivalent to BackgroundPlotter 'iso' camera position
|
|
407
422
|
self.plotter.view_isometric()
|
|
408
|
-
self.plotter.
|
|
423
|
+
self.plotter.camera.azimuth += self.flicker*180
|
|
424
|
+
self.flicker = not self.flicker
|
|
409
425
|
|
|
410
426
|
def view_xy(self):
|
|
411
|
-
|
|
412
|
-
self.plotter.
|
|
413
|
-
self.
|
|
427
|
+
self.plotter.view_xy()
|
|
428
|
+
self.plotter.camera.azimuth += self.flicker*180
|
|
429
|
+
self.flicker = not self.flicker
|
|
414
430
|
|
|
415
431
|
def view_xz(self):
|
|
416
|
-
# view from +Y axis, XZ plane (sagittal)
|
|
417
432
|
self.plotter.view_xz()
|
|
418
|
-
self.plotter.
|
|
433
|
+
self.plotter.camera.azimuth += self.flicker*180
|
|
434
|
+
self.flicker = not self.flicker
|
|
419
435
|
|
|
420
436
|
def view_yz(self):
|
|
421
|
-
# view from +X axis, YZ plane (coronal)
|
|
422
437
|
self.plotter.view_yz()
|
|
423
|
-
self.plotter.
|
|
438
|
+
self.plotter.camera.azimuth += self.flicker*180
|
|
439
|
+
self.flicker = not self.flicker
|
|
424
440
|
|
|
425
441
|
def take_screenshot(self):
|
|
426
442
|
"""Take a screenshot of the current 3D view."""
|
|
@@ -451,7 +467,7 @@ class TrkViewer(QWidget):
|
|
|
451
467
|
|
|
452
468
|
# Copy all meshes and volumes from the main plotter to the offscreen one
|
|
453
469
|
for actor_name, actor in self.plotter.actors.items():
|
|
454
|
-
offscreen.add_actor(actor
|
|
470
|
+
offscreen.add_actor(actor)
|
|
455
471
|
|
|
456
472
|
# Match background color and camera
|
|
457
473
|
offscreen.background_color = self.plotter.background_color
|
|
@@ -492,7 +508,7 @@ class TrkViewer(QWidget):
|
|
|
492
508
|
plot_trk(file, opacity=opacity, plotter=self.plotter, scalar=scalar,
|
|
493
509
|
show_points=show_points, color_map=color_map,
|
|
494
510
|
name=file, background=self.background,
|
|
495
|
-
reset_camera=reset_camera)
|
|
511
|
+
reset_camera=reset_camera, color_blind=self.color_blind)
|
|
496
512
|
|
|
497
513
|
def _update_nii_x(self):
|
|
498
514
|
|
|
@@ -609,10 +625,14 @@ class MainWindow(QMainWindow):
|
|
|
609
625
|
setBackColorAction = QAction('Background Color', self)
|
|
610
626
|
setBackColorAction.triggered.connect(self.viewer.set_background_color)
|
|
611
627
|
viewMenu.addAction(setBackColorAction)
|
|
612
|
-
toggleActorListAction = QAction(
|
|
613
|
-
|
|
628
|
+
toggleActorListAction = QAction("Toggle Actor List", self,
|
|
629
|
+
checkable=True)
|
|
614
630
|
toggleActorListAction.triggered.connect(self.toggleActorDock)
|
|
615
631
|
viewMenu.addAction(toggleActorListAction)
|
|
632
|
+
toggleColorBlindAction = QAction("Use color-blind colors", self,
|
|
633
|
+
checkable=True)
|
|
634
|
+
toggleColorBlindAction.triggered.connect(self.viewer.toggleColorBlind)
|
|
635
|
+
viewMenu.addAction(toggleColorBlindAction)
|
|
616
636
|
|
|
617
637
|
def initActorDock(self):
|
|
618
638
|
self.actorDock = QDockWidget("Scene Actors", self)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: unveil-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Implementation of UNVEIL
|
|
5
5
|
Author-email: Nicolas Delinte <nicolas.delinte@uclouvain.be>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -25,7 +25,7 @@ Dynamic: license-file
|
|
|
25
25
|
|
|
26
26
|
A simple tractography viewer implemented in python.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
<img width="1297" height="757" alt="image" src="https://github.com/user-attachments/assets/28807e18-17fb-485b-a8ce-2179cdef796b" />
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
The GUI is launched with the following python command in a python console:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|