spectralbrain.io.loaders#

Unified loaders for neuroimaging and geometry formats.

Every loader returns plain NumPy arrays using the canonical type aliases from spectralbrain.runtime. No loader returns library-specific objects (nibabel images, PyVista meshes, etc.) — downstream modules receive only arrays and dicts.

The auto-detection function load() inspects the file extension (and, when ambiguous, magic bytes) to dispatch to the correct format-specific loader.

Dependencies#

  • nibabel — required for FreeSurfer, GIfTI, NIfTI, MGZ. Lazy-imported so import spectralbrain works without it.

  • pyvista — core dependency; reads generic meshes (.ply / .obj / .stl / .vtk / .vtp) natively via VTK.

  • h5py — core dependency, for HDF5 cache files.

Module Attributes

DESIKAN_LOBE_MAP

Map from Desikan-Killiany (aparc) region names to lobe labels.

SCHAEFER_NETWORK_MAP

Map from Schaefer parcel network prefixes to Yeo 7/17 network names.

Functions

aggregate_by_parcellation(data, labels, *[, ...])

Aggregate vertex-wise data per parcellation region.

apply_parcellation(vertices, faces, labels, *)

Split a surface into sub-meshes according to a parcellation.

detect_format(path)

Identify the geometry format of a file.

extract_submesh(vertices, faces, vertex_mask)

Extract the sub-mesh defined by a vertex mask.

labels_to_pointcloud(label_volume, affine, ...)

Extract a point cloud from a volumetric segmentation.

load(path, *[, fmt])

Auto-detect format and load a neuroimaging / geometry file.

load_freesurfer_annot(path)

Load a FreeSurfer annotation (parcellation overlay).

load_freesurfer_morph(path)

Load a FreeSurfer per-vertex scalar overlay.

load_freesurfer_surface(path)

Load a FreeSurfer surface file.

load_gifti_func(path)

Load a GIfTI functional / shape overlay.

load_gifti_label(path)

Load a GIfTI label overlay.

load_gifti_surface(path)

Load a GIfTI surface file.

load_mesh(path)

Load a mesh from a generic format (.ply, .obj, .stl, .vtk, .vtp).

load_nifti(path)

Load a NIfTI or MGZ volume.

remap_parcellation(labels, names, mapping, *)

Remap per-vertex parcellation labels to a coarser grouping.

spectralbrain.io.loaders.aggregate_by_parcellation(data, labels, *, stat='mean', ignore_labels=None, label_names=None)[source]#

Aggregate vertex-wise data per parcellation region.

Compute summary statistics of a vertex-wise array (e.g. thickness, HKS, z-score) within each parcel defined by labels.

Parameters:
  • data (ndarray, shape (V,) or (V, D)) – Vertex-wise data. If 2-D, each column is aggregated independently.

  • labels (ndarray, shape (V,)) – Per-vertex integer labels.

  • stat (str or callable) – Aggregation function. Built-in options: "mean", "median", "std", "min", "max", "sum", "count", "iqr" (interquartile range). Or pass a callable that accepts an array and returns a scalar.

  • ignore_labels (list of int, optional) – Labels to exclude (e.g. [0] for medial wall).

  • label_names (dict of {int: str}, optional) – Mapping from label integer to name. If provided, the returned DataFrame uses region names as the index.

Returns:

pandas.DataFrame – One row per parcel, columns are "label" (or region name) plus one column per data dimension ("d0", "d1", … or "value" for 1-D input).

Return type:

pd.DataFrame

Examples

Mean cortical thickness per Desikan region:

>>> thickness = sb.io.load_freesurfer_morph("lh.thickness")
>>> labels, _, names = sb.io.load_freesurfer_annot("lh.aparc.annot")
>>> df = sb.io.aggregate_by_parcellation(
...     thickness, labels, stat="mean", ignore_labels=[0],
... )
>>> df.head()

Mean HKS per Yeo network (after remapping):

>>> hks = sb.spectral.compute_hks(decomp, n_times=16)
>>> net_labels, net_names = sb.io.remap_parcellation(
...     labels, names, sb.io.SCHAEFER_NETWORK_MAP, match="contains",
... )
>>> df = sb.io.aggregate_by_parcellation(
...     hks, net_labels, stat="mean",
...     ignore_labels=[0], label_names=net_names,
... )
spectralbrain.io.loaders.apply_parcellation(vertices, faces, labels, *, ignore_labels=None)[source]#

Split a surface into sub-meshes according to a parcellation.

Given a cortical mesh and a per-vertex label array (e.g. from a Schaefer .annot), extracts one sub-mesh per parcel.

Parameters:
  • vertices (ndarray, shape (N, 3)) – Full mesh vertices.

  • faces (ndarray, shape (F, 3)) – Full mesh faces.

  • labels (ndarray, shape (N,)) – Per-vertex parcel labels.

  • ignore_labels (list of int, optional) – Labels to skip (e.g. [0] for the medial wall in Schaefer).

Returns:

dict of {int ((vertices, faces)}) – Mapping from label ID to the corresponding sub-mesh. Each sub-mesh has re-indexed faces starting from 0.

Return type:

dict[int, tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[int64]]]]

Notes

This is the building block for the geometric connectome: apply a Schaefer-200 parcellation, compute spectral descriptors per parcel, and build a 200×200 similarity matrix.

Examples

>>> verts, faces = sb.io.load_freesurfer_surface("lh.white")
>>> labels, _, names = sb.io.load_freesurfer_annot(
...     "lh.Schaefer2018_200Parcels_7Networks_order.annot")
>>> parcels = sb.io.apply_parcellation(verts, faces, labels,
...                                     ignore_labels=[0])
>>> len(parcels)
100  # 100 left-hemisphere parcels
>>> parcels[1][0].shape  # vertices of parcel 1
(823, 3)
spectralbrain.io.loaders.detect_format(path)[source]#

Identify the geometry format of a file.

Parameters:

path (PathLike) – File to inspect.

Returns:

GeometryFormat

Raises:

ValueError – If the format cannot be determined.

Return type:

GeometryFormat

spectralbrain.io.loaders.extract_submesh(vertices, faces, vertex_mask)[source]#

Extract the sub-mesh defined by a vertex mask.

Parameters:
  • vertices (ndarray, shape (N, 3)) – Full mesh vertices.

  • faces (ndarray, shape (F, 3)) – Full mesh faces.

  • vertex_mask (ndarray, shape (N,), bool) – True for vertices to keep.

Returns:

  • sub_vertices (ndarray, shape (M, 3)) – Subset of vertices.

  • sub_faces (ndarray, shape (G, 3)) – Re-indexed faces referencing sub_vertices.

Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[int64]]]

spectralbrain.io.loaders.labels_to_pointcloud(label_volume, affine, label_id, *, jitter=False, jitter_scale=0.25, seed=None)[source]#

Extract a point cloud from a volumetric segmentation.

Given a 3D integer label volume (e.g. FreeSurfer aseg.mgz) and a target label ID, returns the world-space (RAS) coordinates of all voxels with that label.

This is the pathway ③ from the SpectralBrain I/O diagram: volumetric segmentation → point cloud → spectral descriptors.

Parameters:
  • label_volume (ndarray, shape (X, Y, Z)) – Integer label volume.

  • affine (ndarray, shape (4, 4)) – Voxel-to-world affine matrix.

  • label_id (int) – Target label (e.g. 17 for left hippocampus in aseg).

  • jitter (bool) – Add sub-voxel Gaussian jitter to break the grid pattern. Useful for point-cloud Laplacian estimation, where a regular grid causes degenerate eigenvalues.

  • jitter_scale (float) – Standard deviation of the jitter in voxel units (default 0.25 — i.e. ±0.25 voxels).

  • seed (int, optional) – RNG seed for reproducible jitter.

Returns:

points (ndarray, shape (N, 3)) – World-space coordinates of the extracted voxels.

Raises:

ValueError – If label_id is not found in the volume.

Return type:

ndarray[tuple[Any, …], dtype[floating]]

Examples

>>> data, affine = sb.io.load_nifti("aseg.mgz")
>>> hippo_L = sb.io.labels_to_pointcloud(data, affine, label_id=17)
>>> hippo_L.shape
(4231, 3)
spectralbrain.io.loaders.load(path, *, fmt=None)[source]#

Auto-detect format and load a neuroimaging / geometry file.

This is the recommended entry point for users who don’t want to think about file formats. The returned dict always contains a "format" key; other keys depend on the format.

Parameters:
  • path (PathLike) – File to load.

  • fmt (GeometryFormat, optional) – Force a specific format (skip auto-detection).

Returns:

dict – Contents vary by format. Guaranteed keys:

  • "format" : GeometryFormat

Surface files add "vertices" and "faces". Scalar overlays add "scalars". Annotations add "labels", "ctab", "names". Volumes add "data", "affine".

Raises:
Return type:

dict[str, Any]

Examples

>>> result = sb.io.load("lh.white")
>>> verts, faces = result["vertices"], result["faces"]
>>> result = sb.io.load("lh.aparc.annot")
>>> labels, names = result["labels"], result["names"]
spectralbrain.io.loaders.load_freesurfer_annot(path)[source]#

Load a FreeSurfer annotation (parcellation overlay).

Parameters:

path (PathLike) – Path to .annot file (e.g. lh.aparc.annot).

Returns:

  • labels (ndarray, shape (N,)) – Per-vertex parcel index.

  • ctab (ndarray, shape (n_labels, 5)) – Colour table (RGBT + label ID).

  • names (list of str) – Region names, one per row of ctab.

Return type:

tuple[ndarray[tuple[Any, …], dtype[integer]], ndarray, list[str]]

Examples

>>> labels, ctab, names = sb.io.load_freesurfer_annot(
...     "lh.aparc.a2009s.annot")
>>> set(labels)  # unique parcel IDs
spectralbrain.io.loaders.load_freesurfer_morph(path)[source]#

Load a FreeSurfer per-vertex scalar overlay.

Parameters:

path (PathLike) – Path to .thickness, .curv, .sulc, or .area.

Returns:

ndarray, shape (N,) – Per-vertex scalar values.

Return type:

ndarray[tuple[Any, …], dtype[floating]]

spectralbrain.io.loaders.load_freesurfer_surface(path)[source]#

Load a FreeSurfer surface file.

Parameters:

path (PathLike) – Path to a FreeSurfer surface (.white, .pial, .inflated, .sphere, …).

Returns:

  • vertices (ndarray, shape (N, 3)) – Vertex coordinates in TkRAS mm.

  • faces (ndarray, shape (F, 3)) – Triangle indices, 0-indexed.

Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[int64]]]

Examples

>>> verts, faces = sb.io.load_freesurfer_surface("lh.white")
>>> verts.shape
(163842, 3)
spectralbrain.io.loaders.load_gifti_func(path)[source]#

Load a GIfTI functional / shape overlay.

Parameters:

path (PathLike) – Path to .func.gii or .shape.gii.

Returns:

ndarray, shape (N,) or (N, T) – Scalar map (single frame) or descriptor matrix (multi-frame).

Return type:

ndarray[tuple[Any, …], dtype[floating]]

spectralbrain.io.loaders.load_gifti_label(path)[source]#

Load a GIfTI label overlay.

Parameters:

path (PathLike) – Path to .label.gii.

Returns:

  • labels (ndarray, shape (N,))

  • names (list of str)

Return type:

tuple[ndarray[tuple[Any, …], dtype[integer]], list[str]]

spectralbrain.io.loaders.load_gifti_surface(path)[source]#

Load a GIfTI surface file.

Parameters:

path (PathLike) – Path to .surf.gii.

Returns:

  • vertices (ndarray, shape (N, 3))

  • faces (ndarray, shape (F, 3))

Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[int64]]]

spectralbrain.io.loaders.load_mesh(path)[source]#

Load a mesh from a generic format (.ply, .obj, .stl, .vtk, .vtp).

Backed by PyVista, so every listed format works with a default install (no optional dependency required).

Parameters:

path (PathLike) – Mesh file.

Returns:

  • vertices (ndarray, shape (N, 3))

  • faces (ndarray, shape (F, 3))

Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[int64]]]

spectralbrain.io.loaders.load_nifti(path)[source]#

Load a NIfTI or MGZ volume.

Parameters:

path (PathLike) – Path to .nii, .nii.gz, .mgz, or .mgh.

Returns:

  • data (ndarray) – Volume data (3D or 4D).

  • affine (ndarray, shape (4, 4)) – Voxel-to-world affine.

Return type:

tuple[ndarray, ndarray]

spectralbrain.io.loaders.remap_parcellation(labels, names, mapping, *, match='exact', unmapped='unmapped')[source]#

Remap per-vertex parcellation labels to a coarser grouping.

Takes the label array and region names from a FreeSurfer .annot (or any atlas) and remaps each region to a new group according to mapping.

Parameters:
  • labels (ndarray, shape (V,)) – Per-vertex integer labels (as returned by load_freesurfer_annot()).

  • names (sequence of str or bytes) – Region names, one per unique label in the annotation colour table. Index i corresponds to label integer i.

  • mapping (dict of {str: str}) – Source region name → target group name.

  • match ("exact" or "contains") –

    How to match region names to mapping keys.

    • "exact" — the region name (lowered, stripped) must equal a mapping key.

    • "contains" — the region name is assigned to the first mapping key that appears as a substring. Useful for Schaefer parcels whose names embed the network prefix (e.g. "7Networks_LH_Vis_1" matches key "Vis").

  • unmapped (str) – Group name for regions that do not match any mapping key.

Returns:

  • new_labels (ndarray, shape (V,)) – Remapped per-vertex labels (contiguous integers starting at 0 for unmapped, 1 for the first group, etc.).

  • new_names (dict of {int: str}) – Mapping from new integer label to group name.

Return type:

tuple[ndarray, dict[int, str]]

Examples

Group Desikan parcels into lobes:

>>> labels, ctab, names = sb.io.load_freesurfer_annot("lh.aparc.annot")
>>> lobe_labels, lobe_names = sb.io.remap_parcellation(
...     labels, names, sb.io.DESIKAN_LOBE_MAP,
... )
>>> set(lobe_names.values())
{'frontal', 'parietal', 'temporal', 'occipital', 'insular', 'unmapped'}

Group Schaefer parcels into Yeo networks:

>>> labels, _, names = sb.io.load_freesurfer_annot(
...     "lh.Schaefer2018_400Parcels_7Networks_order.annot"
... )
>>> net_labels, net_names = sb.io.remap_parcellation(
...     labels, names, sb.io.SCHAEFER_NETWORK_MAP, match="contains",
... )
spectralbrain.io.loaders.DESIKAN_LOBE_MAP: dict[str, str] = {'bankssts': 'temporal', 'caudalanteriorcingulate': 'frontal', 'caudalmiddlefrontal': 'frontal', 'cuneus': 'occipital', 'entorhinal': 'temporal', 'frontalpole': 'frontal', 'fusiform': 'temporal', 'inferiorparietal': 'parietal', 'inferiortemporal': 'temporal', 'insula': 'insular', 'isthmuscingulate': 'parietal', 'lateraloccipital': 'occipital', 'lateralorbitofrontal': 'frontal', 'lingual': 'occipital', 'medialorbitofrontal': 'frontal', 'middletemporal': 'temporal', 'paracentral': 'frontal', 'parahippocampal': 'temporal', 'parsopercularis': 'frontal', 'parsorbitalis': 'frontal', 'parstriangularis': 'frontal', 'pericalcarine': 'occipital', 'postcentral': 'parietal', 'posteriorcingulate': 'parietal', 'precentral': 'frontal', 'precuneus': 'parietal', 'rostralanteriorcingulate': 'frontal', 'rostralmiddlefrontal': 'frontal', 'superiorfrontal': 'frontal', 'superiorparietal': 'parietal', 'superiortemporal': 'temporal', 'supramarginal': 'parietal', 'temporalpole': 'temporal', 'transversetemporal': 'temporal'}#

Map from Desikan-Killiany (aparc) region names to lobe labels.

Works with both aparc.annot and aparc.DKTatlas.annot (Desikan-Killiany-Tourville). The mapping follows standard neuroanatomical conventions.

Examples

>>> labels, ctab, names = sb.io.load_freesurfer_annot("lh.aparc.annot")
>>> lobe_labels, lobe_names = sb.io.remap_parcellation(
...     labels, names, DESIKAN_LOBE_MAP,
... )
spectralbrain.io.loaders.SCHAEFER_NETWORK_MAP: dict[str, str] = {'Cont': 'Control', 'Default': 'Default', 'DorsAttn': 'DorsalAttention', 'Limbic': 'Limbic', 'SalVentAttn': 'SalVentAttn', 'SomMot': 'Somatomotor', 'TempPar': 'TempPar', 'Vis': 'Visual'}#

Map from Schaefer parcel network prefixes to Yeo 7/17 network names.

Used with remap_parcellation() when match="contains" to group Schaefer-200/400/600/800/1000 parcels by their parent network.

Examples

>>> labels, ctab, names = sb.io.load_freesurfer_annot(
...     "lh.Schaefer2018_200Parcels_7Networks_order.annot"
... )
>>> net_labels, net_names = sb.io.remap_parcellation(
...     labels, names, SCHAEFER_NETWORK_MAP, match="contains",
... )