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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unveil-python
3
- Version: 0.3.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
- ![image](https://github.com/user-attachments/assets/4fbefa7a-3fc2-446a-8835-2844266b14fa)
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
- ![image](https://github.com/user-attachments/assets/4fbefa7a-3fc2-446a-8835-2844266b14fa)
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:
@@ -1,5 +1,5 @@
1
1
  """
2
2
  This package contains an UNVEIL, a lightweight tractography viewer implemented in python."""
3
3
 
4
- __version__ = "0.3.0"
4
+ __version__ = "0.4.0"
5
5
  __author__ = 'Nicolas Delinte'
@@ -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
- smooth,
373
- name=actor_name,
374
- opacity=1,
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.render()
423
+ self.plotter.camera.azimuth += self.flicker*180
424
+ self.flicker = not self.flicker
409
425
 
410
426
  def view_xy(self):
411
- # view from +Z axis, XY plane (axial)
412
- self.plotter.view_xy() # pyvista method
413
- self.plotter.render()
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.render()
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.render()
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.copy())
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
- "Toggle Actor List", self, checkable=True)
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.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
- ![image](https://github.com/user-attachments/assets/4fbefa7a-3fc2-446a-8835-2844266b14fa)
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