Atlas-based seed connectivity maps¶
This example computes voxel-wise seed-based functional connectivity maps: register a
single-slice fUSI recording to an Allen-space template, bring an
Allen Mouse Brain Atlas into the recording's native space,
pick four atlas regions of interest as seeds, and correlate each seed's signal
against every voxel with SeedBasedMaps. Each
resulting map is displayed with
plot_stat_map, using the resampled Allen
reference volume as background.
We reuse the same recording, template, and registration workflow as the Atlas-based region correlation matrix example—see it for a detailed walkthrough of the registration steps condensed here.
Fetch the recording and register to the Allen atlas¶
from pathlib import Path
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
import confusius as cf
# Adapt background color to the current Matplotlib style.
bg_color = mpl.colors.to_hex(mpl.rcParams["figure.facecolor"])
xr.set_options(display_expand_data=False)
bids_root = cf.datasets.fetch_nunez_elizalde_2022(
subjects="CR022",
sessions="20201007",
tasks="spontaneous",
acqs="slice02",
)
data_path = (
Path(bids_root)
/ "sub-CR022"
/ "ses-20201007"
/ "fusi"
/ "sub-CR022_ses-20201007_task-spontaneous_acq-slice02_pwd.nii.gz"
)
# The recording's timepoints are not perfectly uniformly spaced, so we resample to a
# uniform grid before any time-domain processing.
data = cf.timing.resample_to_uniform_time(cf.load(data_path))
moving = data.mean(dim="time").fusi.scale.db().compute()
template = cf.datasets.fetch_template_pepe_mariani_2026().compute()
/tmp/ipykernel_44778/2112388043.py:31: UserWarning: Time coordinate is non-uniform; using the median step to build a uniform target grid.
data = cf.timing.resample_to_uniform_time(cf.load(data_path))
As in the correlation-matrix example, we initialize registration with an affine measured once in Napari's registration widget, inverted so it maps template physical coordinates to recording physical coordinates, and crop the template to a thin band around the recording's expected location.
napari_affine = np.array(
[
[1.0, 0.0, 0.0, 5.594638656430411],
[0.0, 1.0, 0.0, -2.50293925701927],
[0.0, 0.0, 1.0, 5.6650243788545875],
[0.0, 0.0, 0.0, 1.0],
]
)
initialization = np.linalg.inv(napari_affine)
target_z = napari_affine[0, 3] + float(moving.z.values[0])
fixed = template.sel(z=slice(target_z - 1.0, target_z + 1.0))
registered, affine, diagnostics = cf.registration.register_volume(
moving=moving,
fixed=fixed,
transform_type="affine",
metric="correlation",
convergence_window_size=100,
number_of_iterations=500,
learning_rate=1,
initialization=initialization,
show_progress=True,
)
print(f"Initial metric: {diagnostics.metric_values[0]:.4f}")
print(f"Final metric: {diagnostics.final_metric_value:.4f}")
Resample the Allen atlas onto the recording's native grid¶
Composing the template's physical_to_sform affine with the inverse of the estimated
registration affine gives a single transform from the recording's native coordinates
directly to Allen atlas coordinates.
Atlas.resample_like resamples the atlas'
reference volume, annotations, and hemisphere map onto that grid in one call, so
atlas_native.reference can be used directly as the anatomical background for our
stat maps below.
physical_to_sform = template.attrs["affines"]["physical_to_sform"]
subject_to_atlas = physical_to_sform @ np.linalg.inv(affine)
atlas = cf.atlas.Atlas.from_brainglobe("allen_mouse_100um", check_latest=False)
atlas_native = atlas.resample_like(moving, subject_to_atlas)
Choose seed regions¶
We pick one seed from each of four functional systems, to contrast their
connectivity patterns: the primary somatosensory barrel field cortex ("SSp-bfd"), a
classic resting-state seed with a strong, well-documented bilateral
(interhemispheric) correlation signature; the retrosplenial cortex ("RSP"), a
default-mode-like hub expected to correlate broadly across much of cortex; the
hippocampus ("HIP"), which should instead correlate with a more localized,
hippocampal-formation-restricted network; and the ventral posteromedial thalamic
nucleus ("VPM"), a somatosensory relay nucleus expected to correlate with
"SSp-bfd" through the thalamocortical pathway. All seeds are taken from the left
hemisphere only, so that any right-hemisphere correlation in the resulting maps
reflects genuine interhemispheric connectivity rather than the seed leaking into its
own mask.
Atlas.get_masks returns a stacked
(mask, z, y, x) integer DataArray — one layer per requested region — which
SeedBasedMaps accepts directly as
seed_masks.
seed_regions = ["SSp-bfd", "RSP", "HIP", "VPM"]
seed_masks = atlas_native.get_masks(seed_regions, sides="left")
Smooth and compute nuisance regressors¶
We lightly smooth the recording spatially with
smooth_volume (0.1 mm FWHM) to improve the
voxel-wise SNR before extracting confound signals and fitting the seed-based maps.
As in the correlation-matrix example, we regress out an
aCompCor component extracted from
white-matter voxels (the Allen ontology's "fiber tracts" division) together with a
low_cutoff high-pass filter for slow drift. Both are passed to SeedBasedMaps via
clean_kwargs, which cleans the full voxel-wise recording before extracting the
seed signals, so seeds and voxels are preprocessed consistently.
data = cf.spatial.smooth_volume(data, fwhm=0.1)
white_matter = atlas_native.get_masks("fiber tracts").isel(mask=0)
acompcor = cf.signal.compute_compcor_confounds(
data, noise_mask=white_matter, n_components=1, variance_threshold=0.95
)
Compute the seed-based correlation maps¶
SeedBasedMaps extracts each seed's average
signal, correlates it against every voxel in the recording, and returns one Pearson r
map per seed, stacked along a region dimension.
mapper = cf.connectivity.SeedBasedMaps(
seed_masks=seed_masks, clean_kwargs={"low_cutoff": 0.01, "confounds": acompcor}
)
mapper.fit(data)
mapper.maps_
- region: 4
- z: 1
- y: 125
- x: 80
- dask.array<chunksize=(4, 1, 125, 80), meta=np.ndarray>
Array Chunk Bytes 312.50 kiB 312.50 kiB Shape (4, 1, 125, 80) (4, 1, 125, 80) Dask graph 1 chunks in 55 graph layers Data type float64 numpy.ndarray - region(region)<U9'SSp-bfd_L' 'RSP_L' 'HIP_L' 'VPM_L'
array(['SSp-bfd_L', 'RSP_L', 'HIP_L', 'VPM_L'], dtype='<U9')
- z(z)float640.4
array([0.4])
- y(y)float642.996 3.044 3.092 ... 8.939 8.988
array([2.99584, 3.04416, 3.09248, 3.1408 , 3.18912, 3.23744, 3.28576, 3.33408, 3.3824 , 3.43072, 3.47904, 3.52736, 3.57568, 3.624 , 3.67232, 3.72064, 3.76896, 3.81728, 3.8656 , 3.91392, 3.96224, 4.01056, 4.05888, 4.1072 , 4.15552, 4.20384, 4.25216, 4.30048, 4.3488 , 4.39712, 4.44544, 4.49376, 4.54208, 4.5904 , 4.63872, 4.68704, 4.73536, 4.78368, 4.832 , 4.88032, 4.92864, 4.97696, 5.02528, 5.0736 , 5.12192, 5.17024, 5.21856, 5.26688, 5.3152 , 5.36352, 5.41184, 5.46016, 5.50848, 5.5568 , 5.60512, 5.65344, 5.70176, 5.75008, 5.7984 , 5.84672, 5.89504, 5.94336, 5.99168, 6.04 , 6.08832, 6.13664, 6.18496, 6.23328, 6.2816 , 6.32992, 6.37824, 6.42656, 6.47488, 6.5232 , 6.57152, 6.61984, 6.66816, 6.71648, 6.7648 , 6.81312, 6.86144, 6.90976, 6.95808, 7.0064 , 7.05472, 7.10304, 7.15136, 7.19968, 7.248 , 7.29632, 7.34464, 7.39296, 7.44128, 7.4896 , 7.53792, 7.58624, 7.63456, 7.68288, 7.7312 , 7.77952, 7.82784, 7.87616, 7.92448, 7.9728 , 8.02112, 8.06944, 8.11776, 8.16608, 8.2144 , 8.26272, 8.31104, 8.35936, 8.40768, 8.456 , 8.50432, 8.55264, 8.60096, 8.64928, 8.6976 , 8.74592, 8.79424, 8.84256, 8.89088, 8.9392 , 8.98752]) - x(x)float64-3.95 -3.85 -3.75 ... 3.85 3.95
array([-3.95, -3.85, -3.75, -3.65, -3.55, -3.45, -3.35, -3.25, -3.15, -3.05, -2.95, -2.85, -2.75, -2.65, -2.55, -2.45, -2.35, -2.25, -2.15, -2.05, -1.95, -1.85, -1.75, -1.65, -1.55, -1.45, -1.35, -1.25, -1.15, -1.05, -0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95, 1.05, 1.15, 1.25, 1.35, 1.45, 1.55, 1.65, 1.75, 1.85, 1.95, 2.05, 2.15, 2.25, 2.35, 2.45, 2.55, 2.65, 2.75, 2.85, 2.95, 3.05, 3.15, 3.25, 3.35, 3.45, 3.55, 3.65, 3.75, 3.85, 3.95])
- long_name :
- Pearson r
- cmap :
- coolwarm
- norm :
- <matplotlib.colors.Normalize object at 0x7fcf6af59a50>
Plot the seed maps¶
Each seed's map is plotted with plot_stat_map
over the resampled Allen reference volume, with vmax=0.8 fixing the colormap to a
shared range so the four seeds are directly comparable. mapper.maps_ stacks all
four seed maps along a region dimension, so a single plot_stat_map call with
slice_mode="region" plots one panel per seed as long as the background is broadcast
to the same region dimension first. We outline each seed's own ROI with
VolumePlotter.add_contours, leaving
colors unset so each region is drawn in its canonical Allen color (read from the
atlas mask's attrs["cmap"]/attrs["norm"], the same convention used by
Atlas.get_masks elsewhere).
# coolwarm's white midpoint reads as a washed-out hole on a dark background, so switch
# to berlin (Crameri's perceptually uniform diverging colormap, black midpoint) when the
# current Matplotlib style is dark.
is_dark_theme = sum(mpl.colors.to_rgb(bg_color)) / 3 < 0.5
cmap = "berlin" if is_dark_theme else None
# Broadcast the shared background and brain outline across a "region" dimension
# matching mapper.maps_, so plot_stat_map can slice both by region in one call.
bg_by_region = atlas_native.reference.expand_dims(region=mapper.maps_.region)
fig, axes = plt.subplots(2, 2, figsize=(8, 6), constrained_layout=True)
fig.patch.set_facecolor(bg_color)
plotter = cf.plotting.plot_stat_map(
mapper.maps_,
bg_volume=bg_by_region,
slice_mode="region",
cmap=cmap,
vmax=0.8,
threshold=0.20,
cbar_label="Pearson correlation",
show_titles=False,
show_axes=False,
figure=fig,
axes=axes,
bg_color=bg_color,
)
plotter.add_contours(seed_masks.rename(mask="region"), linewidths=1.5)
for ax, region in zip(axes.ravel(), seed_regions):
ax.set_title(region)
_ = fig.suptitle("Seed-based connectivity maps", fontsize=16)
Total running time: 244.0 s



