Skip to content

Atlases

A brain atlas ties every voxel of a reference volume to a named region. In ConfUSIus an atlas is a plain xarray.Dataset with three data variables on a common (z, y, x) grid (reference, the template volume; annotation, integer region labels; and hemispheres, 1 = left, 2 = right), plus an .atlas accessor that carries all atlas-aware operations. The structure hierarchy rides along in Dataset.attrs["structures"], so a single object fully describes the atlas and its region tree.

BrainGlobe Atlases

ConfUSIus does not ship its own atlases. It builds on the BrainGlobe Atlas API, which packages dozens of community atlases behind one interface, and fetch_brainglobe_atlas fetches any of them by name:

import confusius as cf

atlas = cf.datasets.fetch_brainglobe_atlas("allen_mouse_100um")

The first call downloads the atlas through BrainGlobe and caches it in BrainGlobe's own cache (~/.brainglobe, shared with other BrainGlobe tools); later calls read from the cache. Any name from the BrainGlobe atlas list works, covering mouse, rat, human, zebrafish, and more, at several resolutions.

The returned object is the Dataset described above:

>>> atlas
<xarray.Dataset> Size: 11MB
Dimensions:      (z: 132, y: 80, x: 114)
Coordinates:
  * z            (z) float64 1kB 0.0 0.1 0.2 0.3 0.4 ... 12.8 12.9 13.0 13.1
  * y            (y) float64 640B 0.0 0.1 0.2 0.3 0.4 ... 7.5 7.6 7.7 7.8 7.9
  * x            (x) float64 912B 0.0 0.1 0.2 0.3 0.4 ... 11.0 11.1 11.2 11.3
Data variables:
    reference    (z, y, x) float32 5MB 0.0 0.0 0.0 0.0 0.0 ... 1.0 1.0 1.0 1.0
    annotation   (z, y, x) int32 5MB 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
    hemispheres  (z, y, x) int8 1MB 2 2 2 2 2 2 2 2 2 2 ... 1 1 1 1 1 1 1 1 1 1
Attributes:
    name:              allen_mouse
    citation:          Wang et al 2020, https://doi.org/10.1016/j.cell.2020.0...
    species:           Mus musculus
    orientation:       asr
    structures:        root (997) ...
    physical_to_base:  [[1. 0. 0. 0.] ...

Coordinates are in millimeters, so an atlas plots and registers against fUSI recordings directly.

Building your own atlas

Because ConfUSIus leans on the BrainGlobe atlas format, a custom atlas (for example a coarse parcellation for functional connectivity) is best authored as a BrainGlobe atlas and then loaded with the same fetch_brainglobe_atlas call. BrainGlobe's Adding a new atlas guide and the atlas_scripts in the brainglobe-atlasapi repository walk through packaging a reference stack, an annotation stack, and a structure hierarchy into a distributable atlas.

Exploring the Structure Hierarchy

Regions form a tree: root contains gray and white matter, which contain areas, which contain layers. show_tree prints the whole hierarchy, and its nid argument restricts the printout to one branch, which is handy since the full Allen tree has well over a thousand nodes:

# Print just the hippocampus (HIP, id 1080) subtree.
atlas.atlas.show_tree(nid=1080)
HIP (1080)
├── CA (375)
│   ├── CA1 (382)
│   ├── CA2 (423)
│   └── CA3 (463)
├── DG (726)
│   ├── DG-mo (10703)
│   ├── DG-po (10704)
│   └── DG-sg (632)
├── FC (982)
└── IG (19)

Indexing structures by acronym (or integer id) returns the BrainGlobe record for a region, including its full path from the root:

>>> atlas.structures["HIP"]
{'acronym': 'HIP',
 'id': 1080,
 'name': 'Hippocampal region',
 'structure_id_path': [997, 8, 567, 688, 695, 1089, 1080],
 'rgb_triplet': [126, 208, 75],
 'mesh_filename': PosixPath('.../allen_mouse_100um_v1.2/meshes/1080.obj')}

ancestors returns the same path as tree nodes, from the root down to (but excluding) the region:

>>> [node.tag for node in atlas.atlas.ancestors("HIP")]
['root (997)', 'grey (8)', 'CH (567)', 'CTX (688)', 'CTXpl (695)', 'HPF (1089)']

Looking Up and Searching Regions

lookup flattens the hierarchy into a pandas.DataFrame indexed by region id, with the acronym, full name, and RGB color of every structure:

>>> atlas.atlas.lookup.head()
       acronym                           name      rgb_triplet
id
997       root                           root  [255, 255, 255]
8         grey  Basic cell groups and regions  [191, 218, 227]
567         CH                       Cerebrum  [176, 240, 255]
688        CTX                Cerebral cortex  [176, 255, 184]
695      CTXpl                 Cortical plate  [112, 255, 112]

Region acronyms are terse, so search finds structures by substring or regex. By default it matches both the acronym and the name (case-insensitive):

>>> atlas.atlas.search("visual").head()
       acronym                                  name    rgb_triplet
id
669        VIS                          Visual areas  [8, 133, 140]
402      VISal             Anterolateral visual area  [8, 133, 140]
1074    VISal1    Anterolateral visual area, layer 1  [8, 133, 140]
905   VISal2/3  Anterolateral visual area, layer 2/3  [8, 133, 140]
1114    VISal4    Anterolateral visual area, layer 4  [8, 133, 140]

Pass field="acronym" (or "name") to match one column exactly with a regex, useful for pinning down a single area without its layer subdivisions:

>>> atlas.atlas.search("VISp", field="acronym")
    acronym                 name    rgb_triplet
id
385    VISp  Primary visual area  [8, 133, 140]

search, get_masks, and get_mesh are also exposed as free functions in confusius.atlas that take the Dataset as their first argument, so cf.atlas.search_atlas(atlas, "visual") is equivalent to atlas.atlas.search("visual"). Both forms validate the Dataset as an atlas before running.

Plotting Annotations over the Reference

The reference and annotation volumes plot with the ordinary plot_volume tools. Overlaying the region boundaries on a reference slice with add_contours gives the familiar atlas view; contour colors come from each region's atlas color automatically:

plotter = cf.plotting.plot_volume(
    atlas.atlas.reference.sel(z=slice(6, 6)), show_colorbar=False
)
plotter.add_contours(atlas.atlas.annotation.sel(z=slice(6, 6)))

Coronal reference slice with Allen region annotation contours Coronal reference slice with Allen region annotation contours

Region Surface Meshes

Many BrainGlobe atlases bundle a triangular surface mesh per region. get_mesh returns the mesh as a (vertices, faces) pair in the atlas's physical space (millimeters), ready to hand to any 3D viewer. napari's add_surface takes exactly that pair:

import napari

surface_data = atlas.atlas.get_mesh("root")
napari.Viewer(ndisplay=3).add_surface(surface_data)

Whole-brain surface mesh of the Allen mouse atlas in napari

Pass side="left" or side="right" to clip the mesh to one hemisphere. Because meshes come back in the atlas's current physical space, they stay aligned with the volumes after a resample (see below).

Masks for Regional Analysis

get_masks turns regions into integer voxel masks stacked along a mask dimension, automatically including every descendant in the hierarchy. Pass one region or many, and optionally restrict each to a hemisphere:

>>> atlas.atlas.get_masks(["VISp", "AUDp", "MOp"])
<xarray.DataArray (mask: 3, z: 132, y: 80, x: 114)> Size: 14MB
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Coordinates:
  * mask     (mask) <U4 48B 'VISp' 'AUDp' 'MOp'
  * z        (z) float64 1kB 0.0 0.1 0.2 0.3 0.4 ... 12.7 12.8 12.9 13.0 13.1
  * y        (y) float64 640B 0.0 0.1 0.2 0.3 0.4 0.5 ... 7.5 7.6 7.7 7.8 7.9
  * x        (x) float64 912B 0.0 0.1 0.2 0.3 0.4 ... 10.9 11.0 11.1 11.2 11.3
Attributes:
    rgb_lookup:  {997: [255, 255, 255], 8: [191, 218, 227], 567: [176, 240, 2...
    roi_labels:  {997: 'root (root)', 8: 'Basic cell groups and regions (grey...
    cmap:        <matplotlib.colors.ListedColormap object at 0x71ba5c798050>
    norm:        <matplotlib.colors.BoundaryNorm object at 0x71ba5cb3f230>

>>> left_hip = atlas.atlas.get_masks("HIP", sides="left")
>>> left_hip.mask
<xarray.DataArray 'mask' (mask: 1)> Size: 20B
'HIP_L'
Coordinates:
  * mask     (mask) <U5 20B 'HIP_L'

These masks feed directly into signal extraction and connectivity; see the examples Atlas-based region correlation matrix and Atlas-based seed connectivity maps for more.

Aligning an Atlas to a Recording

An atlas is only useful once it shares a grid with your data. After registering a recording to the atlas template (see Registration), resample_like warps the reference, annotation, hemisphere map, and region meshes onto the recording's grid in one call, using an affine, B-spline, or displacement-field transform from register_volume:

resampled = atlas.atlas.resample_like(recording, transform)

Resampling can be costly, so it is worth doing once and caching the result. save_atlas / load_atlas write the whole atlas (arrays, structure hierarchy, and region meshes) to a Zarr store and read it back ready to use. The Register a recording to an Allen fUSI template example walks through the full register → resample → save → reload workflow.

API Reference

For full parameter documentation, see the atlas API reference and the I/O API reference for save_atlas/load_atlas.