unveil-python 0.6.0__tar.gz → 0.7.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.6.0
3
+ Version: 0.7.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
@@ -15,6 +15,7 @@ Requires-Dist: dipy
15
15
  Requires-Dist: pyvista
16
16
  Requires-Dist: pyvistaqt
17
17
  Requires-Dist: PyQt6
18
+ Requires-Dist: binama
18
19
  Dynamic: license-file
19
20
 
20
21
  # UNVEIL
@@ -18,7 +18,8 @@ classifiers = ['Intended Audience :: Science/Research',
18
18
  dependencies = ['dipy',
19
19
  'pyvista',
20
20
  'pyvistaqt',
21
- 'PyQt6'
21
+ 'PyQt6',
22
+ 'binama'
22
23
  ]
23
24
  dynamic = ["version"]
24
25
 
@@ -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.6.0"
4
+ __version__ = "0.7.0"
5
5
  __author__ = 'Nicolas Delinte'
@@ -186,6 +186,24 @@ def plot_trk(trk_file, scalar=None, color_map='plasma', opacity: float = 1,
186
186
 
187
187
  p.background_color = background
188
188
 
189
+ def load_gii_data(gii_path):
190
+ """
191
+ Load all data contained in the GifTi file except vertex coordinates and triangles.
192
+ Returns : dictionnary {"Gii table name" : data}
193
+ """
194
+ gii_img = nib.load(gii_path)
195
+ vertex_coords = gii_img.get_arrays_from_intent("NIFTI_INTENT_POINTSET")[0].data
196
+ gii_data = {}
197
+
198
+ # Iterate over all tables of the GIfTI
199
+ for darray in gii_img.darrays:
200
+ # Vertex coords and triangles are stored with None as 'Name'
201
+ if darray.meta.get("Name") is not None:
202
+ # Check that the table contains 1 value per vertex
203
+ if darray.data.shape == vertex_coords[:,0].shape:
204
+ gii_data[darray.meta.get("Name")] = darray.data
205
+
206
+ return gii_data
189
207
 
190
208
  class TrkViewer(QWidget):
191
209
  def __init__(self):
@@ -423,7 +441,7 @@ class TrkViewer(QWidget):
423
441
  self.giiColorModeComboBox = QComboBox()
424
442
  self.giiColorModeComboBox.addItems(['Solid', 'NIfTI values'])
425
443
  self.giiColorModeComboBox.currentIndexChanged.connect(
426
- self.update_gii_viewer)
444
+ lambda index: self.update_gii_viewer(update_color_range=True))
427
445
  color_mode_gii_row.addWidget(self.giiColorModeComboBox)
428
446
  gii_layout.addLayout(color_mode_gii_row)
429
447
 
@@ -500,38 +518,47 @@ class TrkViewer(QWidget):
500
518
  options = QFileDialog.Options()
501
519
  filePath, _ = QFileDialog.getOpenFileName(
502
520
  self, "Open .nii.gz File", "", "Nifti Files (*.nii.gz)", options=options)
503
- if filePath:
504
- print(f"Loaded NIfTI file: {filePath}")
505
- img = nib.load(filePath)
506
- self.nii_affine = img.affine
507
- self.nii_data = img.get_fdata()
521
+
522
+ if not filePath:
523
+ return
524
+
525
+ print(f"Loaded NIfTI file: {filePath}")
526
+ img = nib.load(filePath)
527
+ self.nii_affine = img.affine
528
+ self.nii_data = img.get_fdata()
508
529
 
509
- self.update_volume_color_range_from_nifti()
510
- self.update_surface_color_range_from_nifti()
530
+ self.update_volume_color_range_from_nifti()
511
531
 
512
- grid = pv.ImageData()
513
- grid.dimensions = np.array(self.nii_data.shape) + 1
514
- grid.cell_data['values'] = self.nii_data.flatten(order='F')
515
- self.grid = grid
532
+ grid = pv.ImageData()
533
+ grid.dimensions = np.array(self.nii_data.shape) + 1
534
+ grid.cell_data['values'] = self.nii_data.flatten(order='F')
535
+ self.grid = grid
516
536
 
517
537
  self.XSlider.setMaximum(self.nii_data.shape[0])
518
538
  self.YSlider.setMaximum(self.nii_data.shape[1])
519
539
  self.ZSlider.setMaximum(self.nii_data.shape[2])
520
540
 
521
541
  self.update_nii_viewer(reset_camera=False)
542
+
543
+ # Dilation for surface
544
+ self.nii_data_dilated = dilate_atlas_labels(
545
+ self.nii_data, dilation_width=4)
546
+ self.nii_data_dilated = np.where(self.nii_data_dilated == 0, np.nan,
547
+ self.nii_data_dilated)
548
+
522
549
  if hasattr(self, "gii_mesh"):
550
+ # Sample NIfTI values at each surface vertex
551
+ self.nii_surface_values = self.sample_nifti_on_surface(self.gii_mesh)
523
552
  self.update_gii_viewer(reset_camera=False)
553
+ else:
554
+ self.nii_surface_values = None
524
555
  self.window().refreshActorList()
525
556
 
526
557
  # Update 2D view with current colormap
527
558
  ortho_data = self.nii_data.copy()
528
559
  ortho_data[ortho_data == 0] = None
529
560
 
530
- # Dilation for surface
531
- self.nii_data_dilated = dilate_atlas_labels(
532
- self.nii_data, dilation_width=4)
533
- self.nii_data_dilated = np.where(self.nii_data_dilated == 0, np.nan,
534
- self.nii_data_dilated)
561
+
535
562
 
536
563
  self.window().ortho_viewer.set_volume(ortho_data, self.nii_affine)
537
564
  self.window().ortho_viewer.set_colormap(self.niiColorMapComboBox.currentText())
@@ -577,14 +604,8 @@ class TrkViewer(QWidget):
577
604
 
578
605
  def loadGiftiFile(self):
579
606
  options = QFileDialog.Options()
580
-
581
607
  filePath, _ = QFileDialog.getOpenFileName(
582
- self,
583
- "Open .gii File",
584
- "",
585
- "Gifti Files (*.gii)",
586
- options=options
587
- )
608
+ self, "Open .gii File", "", "Gifti Files (*.gii)", options=options)
588
609
 
589
610
  if not filePath:
590
611
  return
@@ -593,8 +614,24 @@ class TrkViewer(QWidget):
593
614
 
594
615
  self.gii_mesh = gifti_to_pyvista(filePath)
595
616
 
617
+ self.gii_data = load_gii_data(filePath)
618
+
619
+ self.giiColorModeComboBox.setCurrentIndex(0)
620
+
621
+ for _ in range(self.giiColorModeComboBox.count()-2):
622
+ self.giiColorModeComboBox.removeItem(2)
623
+
624
+ self.giiColorModeComboBox.addItems(list(self.gii_data.keys()))
625
+
596
626
  self.actor_types["gii_surface"] = "gii"
597
627
 
628
+ if hasattr(self, "nii_data"):
629
+ # Sample NIfTI values at each surface vertex
630
+ self.nii_surface_values = self.sample_nifti_on_surface(self.gii_mesh)
631
+ self.update_gii_viewer(reset_camera=False)
632
+ else:
633
+ self.nii_surface_values = None
634
+
598
635
  self.update_gii_viewer(reset_camera=False)
599
636
  self.window().refreshActorList()
600
637
 
@@ -740,7 +777,7 @@ class TrkViewer(QWidget):
740
777
  reset_camera=reset_camera, clim=[vmin, vmax],
741
778
  user_matrix=self.nii_affine)
742
779
 
743
- def update_gii_viewer(self, reset_camera=False):
780
+ def update_gii_viewer(self, reset_camera=False, update_color_range = False):
744
781
  """Update the GIFTI surface rendering."""
745
782
 
746
783
  if not hasattr(self, "gii_mesh"):
@@ -760,28 +797,29 @@ class TrkViewer(QWidget):
760
797
  reset_camera=reset_camera,
761
798
  point_size=0, render_lines_as_tubes=True)
762
799
 
763
- elif color_mode == "NIfTI values":
764
-
765
- if self.nii_data is None:
766
- self.plotter.add_mesh(self.gii_mesh, color="ghostwhite",
767
- culling="back", smooth_shading=True,
768
- opacity=opacity, name="gii_surface",
769
- reset_camera=reset_camera,
770
- point_size=0, render_lines_as_tubes=True)
771
- return
772
-
773
- # Sample NIfTI values at each surface vertex
774
- scalars = self.sample_nifti_on_surface(self.gii_mesh)
800
+ else:
801
+ if color_mode == "NIfTI values":
802
+ scalars = self.nii_surface_values
803
+ else:
804
+ scalars = self.gii_data[color_mode]
775
805
 
776
806
  if scalars is None:
807
+ self.plotter.add_mesh(self.gii_mesh, color="ghostwhite",
808
+ culling="back", smooth_shading=True,
809
+ opacity=opacity, name="gii_surface",
810
+ reset_camera=reset_camera,
811
+ point_size=0, render_lines_as_tubes=True)
777
812
  return
778
813
 
779
814
  # Copy the mesh so that the original GIFTI mesh is not modified
780
815
  surface_mesh = self.gii_mesh.copy(deep=True)
781
- surface_mesh.point_data["NIfTI values"] = scalars
816
+ surface_mesh.point_data[color_mode] = scalars
782
817
 
783
818
  cmap_name = self.giiColorMapComboBox.currentText()
784
819
 
820
+ if update_color_range:
821
+ self.update_surface_color_range(scalars)
822
+
785
823
  vmin = self.giiMinSpinBox.value()
786
824
  vmax = self.giiMaxSpinBox.value()
787
825
 
@@ -789,13 +827,13 @@ class TrkViewer(QWidget):
789
827
  if vmax <= vmin:
790
828
  vmax = vmin + 1e-6
791
829
 
792
- self.plotter.add_mesh(surface_mesh, scalars="NIfTI values",
793
- cmap=cmap_name, clim=[vmin, vmax],
794
- culling="back", smooth_shading=True,
795
- opacity=opacity, name="gii_surface",
796
- reset_camera=reset_camera, point_size=0,
797
- render_lines_as_tubes=True,
798
- show_scalar_bar=False)
830
+ self.plotter.add_mesh(surface_mesh, scalars=color_mode,
831
+ cmap=cmap_name, clim=[vmin, vmax],
832
+ culling="back", smooth_shading=True,
833
+ opacity=opacity, name="gii_surface",
834
+ reset_camera=reset_camera, point_size=0,
835
+ render_lines_as_tubes=True,
836
+ show_scalar_bar=False)
799
837
 
800
838
  self.plotter.render()
801
839
 
@@ -829,12 +867,12 @@ class TrkViewer(QWidget):
829
867
  self.niiMinSpinBox.blockSignals(False)
830
868
  self.niiMaxSpinBox.blockSignals(False)
831
869
 
832
- def update_surface_color_range_from_nifti(self):
870
+ def update_surface_color_range(self,data):
833
871
 
834
- if self.nii_data is None:
872
+ if data is None:
835
873
  return
836
874
 
837
- data = np.asarray(self.nii_data, dtype=float)
875
+ data = np.asarray(data, dtype=float)
838
876
  finite = np.isfinite(data)
839
877
 
840
878
  if not np.any(finite):
@@ -997,7 +1035,7 @@ class TrkViewer(QWidget):
997
1035
  """Enable surface scalar controls only when using NIfTI values."""
998
1036
 
999
1037
  use_nifti = (self.giiColorModeComboBox.currentText()
1000
- == "NIfTI values")
1038
+ != "Solid")
1001
1039
 
1002
1040
  self.giiColorMapLabel.setEnabled(use_nifti)
1003
1041
  self.giiColorMapComboBox.setEnabled(use_nifti)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unveil-python
3
- Version: 0.6.0
3
+ Version: 0.7.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
@@ -15,6 +15,7 @@ Requires-Dist: dipy
15
15
  Requires-Dist: pyvista
16
16
  Requires-Dist: pyvistaqt
17
17
  Requires-Dist: PyQt6
18
+ Requires-Dist: binama
18
19
  Dynamic: license-file
19
20
 
20
21
  # UNVEIL
@@ -2,3 +2,4 @@ dipy
2
2
  pyvista
3
3
  pyvistaqt
4
4
  PyQt6
5
+ binama
File without changes
File without changes
File without changes