Source code for PyNutil.io.atlas_loader
from brainglobe_atlasapi import BrainGlobeAtlas
import pandas as pd
import numpy as np
import nrrd
from functools import lru_cache
from ..results import AtlasData
def load_atlas_labels(atlas=None, atlas_name=None):
if atlas_name:
atlas = BrainGlobeAtlas(atlas_name=atlas_name)
if not atlas_name and not atlas:
raise Exception("Either atlas or atlas name must be specified")
atlas_structures = {
"idx": [],
"name": [],
"r": [],
"g": [],
"b": [],
}
for structure in atlas.structures_list:
atlas_structures["idx"].append(structure["id"])
atlas_structures["name"].append(structure["name"])
rgb = structure["rgb_triplet"]
atlas_structures["r"].append(rgb[0])
atlas_structures["g"].append(rgb[1])
atlas_structures["b"].append(rgb[2])
atlas_structures["idx"].insert(0, 0)
atlas_structures["name"].insert(0, "Clear Label")
atlas_structures["r"].insert(0, 0)
atlas_structures["g"].insert(0, 0)
atlas_structures["b"].insert(0, 0)
atlas_labels = pd.DataFrame(atlas_structures)
return atlas_labels
def resolve_atlas(atlas):
"""Convert an atlas argument to AtlasData.
Accepts an ``AtlasData`` instance (returned as-is) or a
``BrainGlobeAtlas``-like object (converted via volume processing and
label loading).
"""
if isinstance(atlas, AtlasData):
return atlas
# Assume BrainGlobeAtlas-like object
volume = process_atlas_volume(atlas.annotation)
hemi_map = process_atlas_volume(atlas.hemispheres)
labels = load_atlas_labels(atlas)
resolution = getattr(atlas, "resolution", None)
voxel_size_um = float(resolution[0]) if resolution is not None else None
return AtlasData(volume=volume, hemi_map=hemi_map, labels=labels, voxel_size_um=voxel_size_um)
def resolve_atlas_labels(atlas_labels):
"""Resolve atlas labels input into a DataFrame.
Accepts a raw labels DataFrame, AtlasData-like objects exposing ``labels``,
or BrainGlobeAtlas-like objects exposing ``structures_list``.
"""
if isinstance(atlas_labels, pd.DataFrame):
return atlas_labels
if hasattr(atlas_labels, "labels"):
return atlas_labels.labels
if hasattr(atlas_labels, "structures_list"):
return load_atlas_labels(atlas_labels)
raise TypeError(
"atlas_labels must be a pandas DataFrame, AtlasData-like (.labels), "
"or BrainGlobeAtlas-like (.structures_list)."
)
def process_atlas_volume(vol):
"""
Processes the atlas volume by transposing and reversing axes.
Parameters
----------
vol : numpy.ndarray
The atlas volume to process.
Returns
-------
numpy.ndarray
The processed atlas volume.
"""
return np.transpose(vol, [2, 0, 1])[::-1, ::-1, ::-1]
[docs]
@lru_cache(maxsize=8)
def load_custom_atlas(atlas_path, hemi_path, label_path):
"""
Loads a custom atlas from provided file paths.
Returns
-------
AtlasData
Bundle containing atlas volume, hemisphere map, and labels.
"""
atlas_volume, _ = nrrd.read(atlas_path)
if hemi_path:
hemi_volume, _ = nrrd.read(hemi_path)
else:
hemi_volume = None
atlas_labels = pd.read_csv(label_path)
return AtlasData(volume=atlas_volume, hemi_map=hemi_volume, labels=atlas_labels)