unveil-python 0.7.0__tar.gz → 0.7.1__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.7.0
3
+ Version: 0.7.1
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,7 +15,6 @@ Requires-Dist: dipy
15
15
  Requires-Dist: pyvista
16
16
  Requires-Dist: pyvistaqt
17
17
  Requires-Dist: PyQt6
18
- Requires-Dist: binama
19
18
  Dynamic: license-file
20
19
 
21
20
  # UNVEIL
@@ -19,7 +19,6 @@ dependencies = ['dipy',
19
19
  'pyvista',
20
20
  'pyvistaqt',
21
21
  'PyQt6',
22
- 'binama'
23
22
  ]
24
23
  dynamic = ["version"]
25
24
 
@@ -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.7.0"
4
+ __version__ = "0.7.1"
5
5
  __author__ = 'Nicolas Delinte'
@@ -12,7 +12,7 @@ from pyvistaqt import QtInteractor
12
12
  from PyQt6.QtCore import Qt
13
13
  from PyQt6.QtWidgets import (QApplication, QWidget, QHBoxLayout, QVBoxLayout,
14
14
  QPushButton, QFileDialog, QCheckBox, QSlider,
15
- QLabel, QComboBox, QMainWindow, QLineEdit,
15
+ QLabel, QComboBox, QMainWindow,
16
16
  QDockWidget, QTreeWidget, QTreeWidgetItem,
17
17
  QColorDialog, QTabWidget, QDoubleSpinBox,
18
18
  QGroupBox)
@@ -23,7 +23,7 @@ from matplotlib.colors import LinearSegmentedColormap
23
23
  from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
24
24
  from matplotlib.figure import Figure
25
25
  # from unravel.utils import get_streamline_density
26
- from binama.utils import dilate_atlas_labels
26
+ from unveil.utils import dilate_atlas_labels
27
27
 
28
28
 
29
29
  def gifti_to_pyvista(gii_path):
@@ -186,13 +186,15 @@ def plot_trk(trk_file, scalar=None, color_map='plasma', opacity: float = 1,
186
186
 
187
187
  p.background_color = background
188
188
 
189
+
189
190
  def load_gii_data(gii_path):
190
191
  """
191
192
  Load all data contained in the GifTi file except vertex coordinates and triangles.
192
193
  Returns : dictionnary {"Gii table name" : data}
193
194
  """
194
195
  gii_img = nib.load(gii_path)
195
- vertex_coords = gii_img.get_arrays_from_intent("NIFTI_INTENT_POINTSET")[0].data
196
+ vertex_coords = gii_img.get_arrays_from_intent(
197
+ "NIFTI_INTENT_POINTSET")[0].data
196
198
  gii_data = {}
197
199
 
198
200
  # Iterate over all tables of the GIfTI
@@ -200,11 +202,12 @@ def load_gii_data(gii_path):
200
202
  # Vertex coords and triangles are stored with None as 'Name'
201
203
  if darray.meta.get("Name") is not None:
202
204
  # 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
+ if darray.data.shape == vertex_coords[:, 0].shape:
206
+ gii_data[darray.meta.get("Name")] = darray.data
205
207
 
206
208
  return gii_data
207
209
 
210
+
208
211
  class TrkViewer(QWidget):
209
212
  def __init__(self):
210
213
  super().__init__()
@@ -518,10 +521,10 @@ class TrkViewer(QWidget):
518
521
  options = QFileDialog.Options()
519
522
  filePath, _ = QFileDialog.getOpenFileName(
520
523
  self, "Open .nii.gz File", "", "Nifti Files (*.nii.gz)", options=options)
521
-
524
+
522
525
  if not filePath:
523
526
  return
524
-
527
+
525
528
  print(f"Loaded NIfTI file: {filePath}")
526
529
  img = nib.load(filePath)
527
530
  self.nii_affine = img.affine
@@ -544,11 +547,12 @@ class TrkViewer(QWidget):
544
547
  self.nii_data_dilated = dilate_atlas_labels(
545
548
  self.nii_data, dilation_width=4)
546
549
  self.nii_data_dilated = np.where(self.nii_data_dilated == 0, np.nan,
547
- self.nii_data_dilated)
548
-
550
+ self.nii_data_dilated)
551
+
549
552
  if hasattr(self, "gii_mesh"):
550
553
  # Sample NIfTI values at each surface vertex
551
- self.nii_surface_values = self.sample_nifti_on_surface(self.gii_mesh)
554
+ self.nii_surface_values = self.sample_nifti_on_surface(
555
+ self.gii_mesh)
552
556
  self.update_gii_viewer(reset_camera=False)
553
557
  else:
554
558
  self.nii_surface_values = None
@@ -558,8 +562,6 @@ class TrkViewer(QWidget):
558
562
  ortho_data = self.nii_data.copy()
559
563
  ortho_data[ortho_data == 0] = None
560
564
 
561
-
562
-
563
565
  self.window().ortho_viewer.set_volume(ortho_data, self.nii_affine)
564
566
  self.window().ortho_viewer.set_colormap(self.niiColorMapComboBox.currentText())
565
567
 
@@ -615,19 +617,20 @@ class TrkViewer(QWidget):
615
617
  self.gii_mesh = gifti_to_pyvista(filePath)
616
618
 
617
619
  self.gii_data = load_gii_data(filePath)
618
-
620
+
619
621
  self.giiColorModeComboBox.setCurrentIndex(0)
620
-
622
+
621
623
  for _ in range(self.giiColorModeComboBox.count()-2):
622
624
  self.giiColorModeComboBox.removeItem(2)
623
-
625
+
624
626
  self.giiColorModeComboBox.addItems(list(self.gii_data.keys()))
625
-
627
+
626
628
  self.actor_types["gii_surface"] = "gii"
627
629
 
628
630
  if hasattr(self, "nii_data"):
629
631
  # Sample NIfTI values at each surface vertex
630
- self.nii_surface_values = self.sample_nifti_on_surface(self.gii_mesh)
632
+ self.nii_surface_values = self.sample_nifti_on_surface(
633
+ self.gii_mesh)
631
634
  self.update_gii_viewer(reset_camera=False)
632
635
  else:
633
636
  self.nii_surface_values = None
@@ -777,7 +780,7 @@ class TrkViewer(QWidget):
777
780
  reset_camera=reset_camera, clim=[vmin, vmax],
778
781
  user_matrix=self.nii_affine)
779
782
 
780
- def update_gii_viewer(self, reset_camera=False, update_color_range = False):
783
+ def update_gii_viewer(self, reset_camera=False, update_color_range=False):
781
784
  """Update the GIFTI surface rendering."""
782
785
 
783
786
  if not hasattr(self, "gii_mesh"):
@@ -799,16 +802,16 @@ class TrkViewer(QWidget):
799
802
 
800
803
  else:
801
804
  if color_mode == "NIfTI values":
802
- scalars = self.nii_surface_values
805
+ scalars = self.nii_surface_values
803
806
  else:
804
807
  scalars = self.gii_data[color_mode]
805
808
 
806
809
  if scalars is None:
807
810
  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)
811
+ culling="back", smooth_shading=True,
812
+ opacity=opacity, name="gii_surface",
813
+ reset_camera=reset_camera,
814
+ point_size=0, render_lines_as_tubes=True)
812
815
  return
813
816
 
814
817
  # Copy the mesh so that the original GIFTI mesh is not modified
@@ -828,12 +831,12 @@ class TrkViewer(QWidget):
828
831
  vmax = vmin + 1e-6
829
832
 
830
833
  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)
834
+ cmap=cmap_name, clim=[vmin, vmax],
835
+ culling="back", smooth_shading=True,
836
+ opacity=opacity, name="gii_surface",
837
+ reset_camera=reset_camera, point_size=0,
838
+ render_lines_as_tubes=True,
839
+ show_scalar_bar=False)
837
840
 
838
841
  self.plotter.render()
839
842
 
@@ -867,7 +870,7 @@ class TrkViewer(QWidget):
867
870
  self.niiMinSpinBox.blockSignals(False)
868
871
  self.niiMaxSpinBox.blockSignals(False)
869
872
 
870
- def update_surface_color_range(self,data):
873
+ def update_surface_color_range(self, data):
871
874
 
872
875
  if data is None:
873
876
  return
@@ -0,0 +1,58 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Mon Oct 26 10:03:52 2026
4
+
5
+ @author: N. Delinte
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.ndimage import distance_transform_edt
10
+
11
+
12
+ def dilate_atlas_labels(atlas, brain_mask=None, dilation_width: float = 1,
13
+ min_label: int = 0):
14
+ """
15
+ Dilates cortical atlas labels to include nearby unlabeled voxels (0),
16
+ constrained by a brain mask.
17
+ Copied from 'pilab-binama' package.
18
+
19
+ Parameters
20
+ ----------
21
+ atlas : np.ndarray (3D, int)
22
+ Cortical atlas with integer labels (0 = unlabeled).
23
+ brain_mask : np.ndarray (3D, bool or int), optional
24
+ Binary mask of the brain (same shape as atlas). The default is one
25
+ dilation_width : float, optional
26
+ Maximum dilation distance in voxels. The default is None.
27
+ min_label : int, optional
28
+ Labels below this values are not dilated
29
+
30
+ Returns
31
+ -------
32
+ dilated_atlas : np.ndarray (3D, int)
33
+ Atlas with dilated labels.
34
+ """
35
+
36
+ # Mask unlabeled voxels inside the brain
37
+ unlabeled = np.where(atlas <= min_label, 1, 0)
38
+ if brain_mask is not None:
39
+ unlabeled *= brain_mask.astype('int32')
40
+
41
+ # Compute distance transform from labeled voxels
42
+ # Also retrieve indices of nearest labeled voxel for each position
43
+ distances, nearest_idx = distance_transform_edt(unlabeled,
44
+ return_indices=True)
45
+
46
+ # Copy atlas to output
47
+ dilated_atlas = atlas.copy()
48
+
49
+ # For unlabeled voxels within dilation_width, assign nearest label
50
+ within_dilation = unlabeled & (distances <= dilation_width)
51
+
52
+ # Map nearest labeled voxel indices back to atlas labels
53
+ coords = np.argwhere(within_dilation)
54
+ for x, y, z in coords:
55
+ nx, ny, nz = nearest_idx[:, x, y, z]
56
+ dilated_atlas[x, y, z] = atlas[nx, ny, nz]
57
+
58
+ return dilated_atlas
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unveil-python
3
- Version: 0.7.0
3
+ Version: 0.7.1
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,7 +15,6 @@ Requires-Dist: dipy
15
15
  Requires-Dist: pyvista
16
16
  Requires-Dist: pyvistaqt
17
17
  Requires-Dist: PyQt6
18
- Requires-Dist: binama
19
18
  Dynamic: license-file
20
19
 
21
20
  # UNVEIL
@@ -4,6 +4,7 @@ pyproject.toml
4
4
  src/unveil/__init__.py
5
5
  src/unveil/__main__.py
6
6
  src/unveil/core.py
7
+ src/unveil/utils.py
7
8
  src/unveil_python.egg-info/PKG-INFO
8
9
  src/unveil_python.egg-info/SOURCES.txt
9
10
  src/unveil_python.egg-info/dependency_links.txt
@@ -2,4 +2,3 @@ dipy
2
2
  pyvista
3
3
  pyvistaqt
4
4
  PyQt6
5
- binama
File without changes
File without changes
File without changes