Open In Colab

Installing requirements¶

The cell below installs every Python package needed to run this notebook, at fully pinned versions, using uv for fast resolution. In Colab the cell is collapsed by default — click the ▶ button to run it.

In [1]:
# install cell skipped during CI (deps preinstalled into system Python)

⚠️ Restart runtime after install

The install may upgrade packages already loaded in the kernel. Go to Runtime → Restart session, then Run all cells below (skip this install cell on re-run).

# IBL - Anatomical Localization of Widefield Imaging Data with ndx-anatomical-localization¶

This notebook shows how to stream and visualise the atlas-registration and anatomical-coordinate data from DANDI:001712 using the ndx-anatomical-localization NWB extension.

Extension overview¶

Type Where Description
AtlasRegistration nwbfile.lab_meta_data["atlas_registration"] Images + affine transform + landmarks
Localization nwbfile.lab_meta_data["localization"] Per-pixel coordinates + region masks + coordinate tables
AffineTransformation inside AtlasRegistration 3×3 homogeneous matrix: source px → registered px
Landmarks inside AtlasRegistration Manually placed control-point table
AnatomicalCoordinatesImage localization.anatomical_coordinates_images Per-pixel (x, y, z) + region acronym arrays
AnatomicalCoordinatesTable localization.anatomical_coordinates_tables 3-D coordinates for discrete objects (landmarks)

Contents¶

  1. Setup and Data Access
  2. Atlas Registration
    • Summary images
    • Affine Transformation
    • Landmarks table
  3. Anatomical Coordinates Image
  4. Anatomical Coordinates Tables
  5. Extract Brain Region Activity
    • Build ROI masks in source space
    • Approach A — mask raw OnePhotonSeries directly
    • Approach B — warp frames into registered space

1. Setup and Data Access ¶

In [2]:
# IMPORTANT: import ndx_anatomical_localization BEFORE read_nwb.
# This executes all @register_class decorators so PyNWB maps
# custom types (e.g. BrainRegionMasks) to the correct Python classes.
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import ndx_anatomical_localization  # noqa: F401
import numpy as np
from matplotlib.colors import ListedColormap
from skimage.transform import SimilarityTransform

plt.rcParams["figure.figsize"] = (12, 6)
plt.rcParams["font.size"] = 10
In [3]:
from load_nwb_utils import *

dandiset_id = "001712"
subject_id = "FD-28"  # Example subject
session_id = "81f90b18-e61c-4d32-bbce-3e0c5f33f06c"  # EID for the session

# Choose data source (DANDI streaming or local)
USE_DANDI = True  # Set to False to use local files

if USE_DANDI:
    nwbfile, io = load_nwb_from_dandi(dandiset_id, subject_id, session_id, description="raw")
else:
    # TODO Specify your local directory path
    local_directory = f"E:/IBL-widefield-nwbfiles/full/"
    nwbfile, io = load_nwb_local(local_directory, subject_id, session_id, description="raw")

print("=== SESSION INFORMATION ===")
print(f"Experiment description:\n {nwbfile.experiment_description}")
print(f"Session description:\n {nwbfile.session_description}")
print(f"Session start time:\n {nwbfile.session_start_time}")
=== SESSION INFORMATION ===
Experiment description:
 In dynamic environments, updating beliefs based on past experiences (priors) is essential for optimal decision-making. Prior utilization is often impaired in psychiatric disorders, affecting perception and behavior. We investigate how Neurexin1α (Nrxn1α) loss-of-function disrupts this process, providing insight into circuit deficits underlying sensorimotor dysfunction. While the synaptic role of Nrxn1α is well studied, its impact on network dynamics and decision-making behavior remain unclear. Using widefield calcium imaging, we assess cortex-wide activity in mice performing a two-choice task to probe how priors influence visually-guided decisions. This task requires the mouse to combine sensory evidence with the prior probability over the stimulus side. We find Nrxn1α KO mice underutilized priors and were slower to update choices based on feedback. During decision-making, cortex-wide cortical activity is both elevated and increasingly correlated in Nrxn1α KO mice, independent of task period. Moreover, a larger fraction of cortical variance was explained by movement variables, consistent with stronger coupling of cortical activity to motor signals and a bias toward movement-related dynamics. These findings suggest that core computations underlying decision-making, such as integrating past experience with current evidence, depend on intact synaptic mechanisms shaped by genes like Nrxn1α.

Session description:
 The task protocol(s) performed in this experimental session:
1. Biased choice world — the standard IBL data-collection task for trained mice. A Gabor patch appears at ±35° azimuth and the mouse turns a wheel to bring it to the center. Correct responses earn a water reward (~1.5 µL); incorrect responses trigger white noise and a 2s timeout. Stimulus probability alternates between 80/20 and 20/80 blocks (starting with a 50/50 block), with block lengths drawn from a truncated exponential distribution (min 20, max 100 trials). Full contrast set: [1.0, 0.25, 0.125, 0.0625, 0.0]. 

Session start time:
 2023-11-14 12:10:33.757227-08:00

2. Atlas Registration ¶

AtlasRegistration lives at nwbfile.lab_meta_data["atlas_registration"] and groups all outputs of the atlas-registration pipeline:

Field Type Description
source_image GrayscaleImage Mean FOV in camera space
registered_image GrayscaleImage FOV after affine warp to atlas space
atlas_projection GrayscaleImage 2-D Allen CCF dorsal-cortex reference
affine_transformation AffineTransformation 3×3 homogeneous matrix
landmarks Landmarks Manually placed control-point table
In [4]:
atlas_registration = nwbfile.lab_meta_data["atlas_registration"]
atlas_registration
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[4], line 1
----> 1 atlas_registration = nwbfile.lab_meta_data["atlas_registration"]
      2 atlas_registration

File /opt/hostedtoolcache/Python/3.13.15/x64/lib/python3.13/site-packages/hdmf/utils.py:1069, in LabelledDict.__getitem__(self, args)
   1067         return super().__getitem__(val)
   1068 else:
-> 1069     return super().__getitem__(key)

KeyError: 'atlas_registration'

Summary Images ¶

In [5]:
source_img     = atlas_registration.source_image.data[:]     # raw FOV (camera space)
registered_img = atlas_registration.registered_image.data[:] # affine-warped to atlas space
atlas_proj     = atlas_registration.atlas_projection.data[:] # Allen CCF dorsal-cortex reference

print(f"source_image shape     : {source_img.shape}")
print(f"registered_image shape : {registered_img.shape}")
print(f"atlas_projection shape : {atlas_proj.shape}")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 source_img     = atlas_registration.source_image.data[:]     # raw FOV (camera space)
      2 registered_img = atlas_registration.registered_image.data[:] # affine-warped to atlas space
      3 atlas_proj     = atlas_registration.atlas_projection.data[:] # Allen CCF dorsal-cortex reference
      4 

NameError: name 'atlas_registration' is not defined

Affine Transformation ¶

AffineTransformation stores a 3×3 matrix in homogeneous coordinates that maps pixel positions from the source (camera) image to the registered (atlas-aligned) image. The inverse maps registered pixels back to source space.

In [6]:
affine_transform = atlas_registration.affine_transformation
M = SimilarityTransform(affine_transform.affine_matrix)

print("Affine matrix (3×3 homogeneous — source px → registered px):")
for row in affine_transform.affine_matrix:
    print(f"  [{row[0]: .5f}  {row[1]: .5f}  {row[2]: .5f}]")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 affine_transform = atlas_registration.affine_transformation
      2 M = SimilarityTransform(affine_transform.affine_matrix)
      3 
      4 print("Affine matrix (3×3 homogeneous — source px → registered px):")

NameError: name 'atlas_registration' is not defined

Landmarks Table ¶

Landmarks is a DynamicTable of manually identified control points used to estimate the affine transform. Each row is one anatomical landmark.

Column Description
source_x, source_y Pixel coords in the raw (source) FOV
registered_x, registered_y Pixel coords in the registered (atlas-aligned) FOV
reference_x, reference_y Pixel coords in the Allen dorsal-cortex atlas projection
landmark_labels Anatomical label string (e.g. "OB_center", "RSP_base")
bregma_offset_x, bregma_offset_y Bregma position in atlas projection pixel coords
resolution µm per pixel of the atlas reference projection
In [7]:
landmarks = atlas_registration.landmarks
print(landmarks.description)
landmarks_df = landmarks[:]
landmarks_df
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 landmarks = atlas_registration.landmarks
      2 print(landmarks.description)
      3 landmarks_df = landmarks[:]
      4 landmarks_df

NameError: name 'atlas_registration' is not defined
In [8]:
fig, axes = plt.subplots(1, 3, figsize=(21, 6), dpi=100)

# --- Source image ---
axes[0].imshow(source_img, cmap="gray")
axes[0].plot(
    landmarks_df["source_x"], landmarks_df["source_y"],
    "g+", ms=16, mew=2, label="Landmarks (source)",
)
for _, row in landmarks_df.iterrows():
    axes[0].text(
        row["source_x"], row["source_y"], row["landmark_labels"],
        color="w", fontsize=8, va="bottom", ha="center",
    )
axes[0].set_title("Source image (raw FOV, camera space)")
axes[0].axis("off")
axes[0].legend(loc="lower right", fontsize=9, frameon=True, facecolor="black", framealpha=0.5)

# --- Atlas projection ---
proj_masked = np.ma.masked_equal(atlas_proj, 0)
cmap_jet = plt.get_cmap("jet").copy()
cmap_jet.set_bad(alpha=0)
axes[1].imshow(proj_masked, cmap=cmap_jet)
axes[1].plot(
    landmarks_df["reference_x"], landmarks_df["reference_y"],
    "w+", ms=16, mew=2, label="Landmarks (reference)",
)
for _, row in landmarks_df.iterrows():
    axes[1].text(
        row["reference_x"], row["reference_y"], row["landmark_labels"],
        color="w", fontsize=8, va="bottom", ha="center",
    )
axes[1].set_title("Allen CCF — dorsal cortex projection")
axes[1].axis("off")
axes[1].legend(loc="lower right", fontsize=9, frameon=True, facecolor="black", framealpha=0.5)

# --- Registered image ---
axes[2].imshow(registered_img, cmap="gray")
axes[2].plot(
    landmarks_df["registered_x"], landmarks_df["registered_y"],
    "rx", ms=16, mew=2, label="Landmarks (registered)",
)
for _, row in landmarks_df.iterrows():
    axes[2].text(
        row["registered_x"], row["registered_y"], row["landmark_labels"],
        color="w", fontsize=8, va="bottom", ha="center",
    )
axes[2].set_title("Registered image (atlas space)")
axes[2].axis("off")
axes[2].legend(loc="lower right", fontsize=9, frameon=True, facecolor="black", framealpha=0.5)

plt.suptitle("AtlasRegistration", fontsize=13)
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 4
      1 fig, axes = plt.subplots(1, 3, figsize=(21, 6), dpi=100)
      2 
      3 # --- Source image ---
----> 4 axes[0].imshow(source_img, cmap="gray")
      5 axes[0].plot(
      6     landmarks_df["source_x"], landmarks_df["source_y"],
      7     "g+", ms=16, mew=2, label="Landmarks (source)",

NameError: name 'source_img' is not defined
No description has been provided for this image

3. Anatomical Coordinates Image ¶

AnatomicalCoordinatesImage assigns a physical 3-D coordinate and a brain-region label to every pixel in an image. It lives under nwbfile.lab_meta_data["localization"].anatomical_coordinates_images.

IBL Bregma (RAS)

Name Linked to Description
AnatomicalCoordinatesImageIBLBregma GrayscaleImage (RegisteredImage) Registered-space per-pixel IBL bregma coordinates

Coordinate system:

Array Shape Description
x (H, W) Mediolateral (positive = Right)
y (H, W) Anteroposterior (positive = Anterior)
z (H, W) Dorsoventral (positive = Superior; ≈ 0 for 2-D FOV)
brain_region (H, W) Allen acronym string ("out-of-atlas" outside atlas)
In [9]:
localization = nwbfile.lab_meta_data["localization"]
aci = localization.anatomical_coordinates_images["AnatomicalCoordinatesImageIBLBregma"]

space        = aci.space
x_coords     = aci.x[:]               # ML in µm
y_coords     = aci.y[:]               # AP in µm
region_names = np.asarray(aci.brain_region[:])
in_atlas     = region_names != "out-of-atlas"

print(f"Name          : {aci.name}")
print(f"Description   : {aci.description}")
print(f"Space         : {space.space_name}")
print(f"  Origin      : {space.origin}")
print(f"  Units       : {space.units}")
print(f"  Orientation : {space.orientation}  (x={space.orientation[0]}, y={space.orientation[1]}, z={space.orientation[2]})")
print(f"Image shape   : {aci.image.data[:].shape}")
print(f"ML (x) range  : [{x_coords[in_atlas].min():.0f}, {x_coords[in_atlas].max():.0f}] µm")
print(f"AP (y) range  : [{y_coords[in_atlas].min():.0f}, {y_coords[in_atlas].max():.0f}] µm")
print(f"Brain regions : {len(np.unique(region_names[in_atlas]))} unique Allen acronyms in atlas")
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[9], line 1
----> 1 localization = nwbfile.lab_meta_data["localization"]
      2 aci = localization.anatomical_coordinates_images["AnatomicalCoordinatesImageIBLBregma"]
      3 
      4 space        = aci.space

File /opt/hostedtoolcache/Python/3.13.15/x64/lib/python3.13/site-packages/hdmf/utils.py:1069, in LabelledDict.__getitem__(self, args)
   1067         return super().__getitem__(val)
   1068 else:
-> 1069     return super().__getitem__(key)

KeyError: 'localization'
In [10]:
# Brain region overlay on the registered image
background   = "out-of-atlas"
names        = np.unique(region_names)
names        = names[names != background]
n            = len(names)
name_to_idx  = {name: i + 1 for i, name in enumerate(names)}

idx_img = np.zeros(region_names.shape, dtype=np.int16)
for name, i in name_to_idx.items():
    idx_img[region_names == name] = i
idx_masked = np.ma.masked_where(idx_img == 0, idx_img)

base_cmap   = plt.get_cmap("tab20b", max(n, 1))
colors      = np.vstack([[0, 0, 0, 0], base_cmap(np.arange(n))])
region_cmap = ListedColormap(colors)
region_cmap.set_bad(alpha=0)

fig, ax = plt.subplots(figsize=(9, 8), dpi=100)
ax.imshow(registered_img, cmap="gray")
ax.imshow(idx_masked, cmap=region_cmap, alpha=0.35, interpolation="nearest")
ax.plot(
    landmarks_df["registered_x"], landmarks_df["registered_y"],
    "w+", ms=14, mew=2, label="Landmarks",
)
ax.set_title(
    f"AnatomicalCoordinatesImage — brain region overlay\n({n} regions, IBL Bregma, registered space)",
    fontsize=11,
)
ax.axis("off")

handles = [
    mpatches.Patch(color=region_cmap(name_to_idx[name]), label=name)
    for name in names[::2]  # every other region to keep legend readable
]
ax.legend(
    handles=handles,
    loc="center left", bbox_to_anchor=(1.02, 0.5),
    frameon=True, title="Regions (every other)",
    fontsize=7, title_fontsize=8,
)
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[10], line 3
      1 # Brain region overlay on the registered image
      2 background   = "out-of-atlas"
----> 3 names        = np.unique(region_names)
      4 names        = names[names != background]
      5 n            = len(names)
      6 name_to_idx  = {name: i + 1 for i, name in enumerate(names)}

NameError: name 'region_names' is not defined

Allen CCF (PIR)

Name Linked to Description
AnatomicalCoordinatesImageCCFv3 GrayscaleImage (RegisteredImage) Registered-space per-pixel Allen CCF coordinates
AnatomicalCoordinatesCCFv3MappedOnSourceImage GrayscaleImage (SourceImage) Source-space per-pixel Allen CCF coordinates

Coordinate system:

Array Shape Description
x (H, W) Anteroposterior (positive = Anterior)
y (H, W) Dorsoventral (positive = Superior)
z (H, W) Mediolateral (positive = Right)
brain_region (H, W) Allen acronym string ("out-of-atlas" outside atlas)
In [11]:
localization = nwbfile.lab_meta_data["localization"]
aci = localization.anatomical_coordinates_images["AnatomicalCoordinatesImageCCFv3"]

space        = aci.space
x_coords     = aci.x[:]               # ML in µm
y_coords     = aci.y[:]               # AP in µm
region_names = np.asarray(aci.brain_region[:])
in_atlas     = region_names != "out-of-atlas"

print(f"Name          : {aci.name}")
print(f"Description   : {aci.description}")
print(f"Space         : {space.space_name}")
print(f"  Origin      : {space.origin}")
print(f"  Units       : {space.units}")
print(f"  Orientation : {space.orientation}  (x={space.orientation[0]}, y={space.orientation[1]}, z={space.orientation[2]})")
print(f"Image shape   : {aci.image.data[:].shape}")
print(f"ML (x) range  : [{x_coords[in_atlas].min():.0f}, {x_coords[in_atlas].max():.0f}] µm")
print(f"AP (y) range  : [{y_coords[in_atlas].min():.0f}, {y_coords[in_atlas].max():.0f}] µm")
print(f"Brain regions : {len(np.unique(region_names[in_atlas]))} unique Allen acronyms in atlas")
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[11], line 1
----> 1 localization = nwbfile.lab_meta_data["localization"]
      2 aci = localization.anatomical_coordinates_images["AnatomicalCoordinatesImageCCFv3"]
      3 
      4 space        = aci.space

File /opt/hostedtoolcache/Python/3.13.15/x64/lib/python3.13/site-packages/hdmf/utils.py:1069, in LabelledDict.__getitem__(self, args)
   1067         return super().__getitem__(val)
   1068 else:
-> 1069     return super().__getitem__(key)

KeyError: 'localization'
In [12]:
# Brain region overlay on the registered image
background   = "out-of-atlas"
names        = np.unique(region_names)
names        = names[names != background]
n            = len(names)
name_to_idx  = {name: i + 1 for i, name in enumerate(names)}

idx_img = np.zeros(region_names.shape, dtype=np.int16)
for name, i in name_to_idx.items():
    idx_img[region_names == name] = i
idx_masked = np.ma.masked_where(idx_img == 0, idx_img)

base_cmap   = plt.get_cmap("tab20b", max(n, 1))
colors      = np.vstack([[0, 0, 0, 0], base_cmap(np.arange(n))])
region_cmap = ListedColormap(colors)
region_cmap.set_bad(alpha=0)

fig, ax = plt.subplots(figsize=(9, 8), dpi=100)
ax.imshow(registered_img, cmap="gray")
ax.imshow(idx_masked, cmap=region_cmap, alpha=0.35, interpolation="nearest")
ax.plot(
    landmarks_df["registered_x"], landmarks_df["registered_y"],
    "w+", ms=14, mew=2, label="Landmarks",
)
ax.set_title(
    f"AnatomicalCoordinatesImage — brain region overlay\n({n} regions, Allen CCF, registered space)",
    fontsize=11,
)
ax.axis("off")

handles = [
    mpatches.Patch(color=region_cmap(name_to_idx[name]), label=name)
    for name in names[::2]  # every other region to keep legend readable
]
ax.legend(
    handles=handles,
    loc="center left", bbox_to_anchor=(1.02, 0.5),
    frameon=True, title="Regions (every other)",
    fontsize=7, title_fontsize=8,
)
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[12], line 3
      1 # Brain region overlay on the registered image
      2 background   = "out-of-atlas"
----> 3 names        = np.unique(region_names)
      4 names        = names[names != background]
      5 n            = len(names)
      6 name_to_idx  = {name: i + 1 for i, name in enumerate(names)}

NameError: name 'region_names' is not defined
In [13]:
localization = nwbfile.lab_meta_data["localization"]
aci = localization.anatomical_coordinates_images["AnatomicalCoordinatesCCFv3MappedOnSourceImage"]

space = aci.space
x_coords = aci.x[:]  # ML in µm
y_coords = aci.y[:]  # AP in µm
region_names = np.asarray(aci.brain_region[:])
in_atlas = region_names != "out-of-atlas"

print(f"Name          : {aci.name}")
print(f"Description   : {aci.description}")
print(f"Space         : {space.space_name}")
print(f"  Origin      : {space.origin}")
print(f"  Units       : {space.units}")
print(
    f"  Orientation : {space.orientation}  (x={space.orientation[0]}, y={space.orientation[1]}, z={space.orientation[2]})"
)
print(f"Image shape   : {aci.image.data[:].shape}")
print(f"ML (x) range  : [{x_coords[in_atlas].min():.0f}, {x_coords[in_atlas].max():.0f}] µm")
print(f"AP (y) range  : [{y_coords[in_atlas].min():.0f}, {y_coords[in_atlas].max():.0f}] µm")
print(f"Brain regions : {len(np.unique(region_names[in_atlas]))} unique Allen acronyms in atlas")
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[13], line 1
----> 1 localization = nwbfile.lab_meta_data["localization"]
      2 aci = localization.anatomical_coordinates_images["AnatomicalCoordinatesCCFv3MappedOnSourceImage"]
      3 
      4 space = aci.space

File /opt/hostedtoolcache/Python/3.13.15/x64/lib/python3.13/site-packages/hdmf/utils.py:1069, in LabelledDict.__getitem__(self, args)
   1067         return super().__getitem__(val)
   1068 else:
-> 1069     return super().__getitem__(key)

KeyError: 'localization'
In [14]:
# Brain region overlay on the source image
background = "out-of-atlas"
names = np.unique(region_names)
names = names[names != background]
n = len(names)
name_to_idx = {name: i + 1 for i, name in enumerate(names)}

idx_img = np.zeros(region_names.shape, dtype=np.int16)
for name, i in name_to_idx.items():
    idx_img[region_names == name] = i
idx_masked = np.ma.masked_where(idx_img == 0, idx_img)

base_cmap = plt.get_cmap("tab20b", max(n, 1))
colors = np.vstack([[0, 0, 0, 0], base_cmap(np.arange(n))])
region_cmap = ListedColormap(colors)
region_cmap.set_bad(alpha=0)

fig, ax = plt.subplots(figsize=(9, 8), dpi=100)
ax.imshow(source_img, cmap="gray")
ax.imshow(idx_masked, cmap=region_cmap, alpha=0.35, interpolation="nearest")
ax.plot(
    landmarks_df["source_x"],
    landmarks_df["source_y"],
    "w+",
    ms=14,
    mew=2,
    label="Landmarks",
)
ax.set_title(
    f"AnatomicalCoordinatesImage — brain region overlay\n({n} regions, Allen CCF, source space)",
    fontsize=11,
)
ax.axis("off")

handles = [
    mpatches.Patch(color=region_cmap(name_to_idx[name]), label=name)
    for name in names[::2]  # every other region to keep legend readable
]
ax.legend(
    handles=handles,
    loc="center left",
    bbox_to_anchor=(1.02, 0.5),
    frameon=True,
    title="Regions (every other)",
    fontsize=7,
    title_fontsize=8,
)
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[14], line 3
      1 # Brain region overlay on the source image
      2 background = "out-of-atlas"
----> 3 names = np.unique(region_names)
      4 names = names[names != background]
      5 n = len(names)
      6 name_to_idx = {name: i + 1 for i, name in enumerate(names)}

NameError: name 'region_names' is not defined

CCFv3 coordinate sanity check ¶

The three cells below visualise the per-pixel CCFv3 coordinate fields and perform a quick sanity check on the landmark positions.

What to look for

Map Expected pattern
AP heatmap Smooth gradient — more-anterior (smaller x) at the top of the image, more-posterior (larger x) at the bottom
DV heatmap Nearly uniform — all dorsal-cortex pixels sit at roughly the same depth (~332 µm from the superior face)
ML heatmap Smooth symmetric gradient — left hemisphere (smaller z) on one side, right hemisphere (larger z) on the other

Landmark scatter (AP vs ML plane)

Landmark Expected position
OB_left, OB_right Symmetric about the midline (~5 700 µm ML), at the anterior pole (large x)
OB_center On the midline, same AP as OB_left / OB_right
RSP_base On the midline, at the posterior pole (small x, since CCF PIR x increases posterior)
In [15]:
# CCFv3 PIR orientation:
#   x = Posterior (AP axis, increases anterior→posterior from ASL corner)
#   y = Inferior  (DV axis, increases superior→inferior)
#   z = Right     (ML axis, increases left→right)
#
# Sanity checks:
#   AP map  → should show a smooth anterior-to-posterior gradient top→bottom
#   DV map  → should be nearly uniform (~332 µm for dorsal-cortex surface)
#   ML map  → should show a symmetric left-to-right gradient

ccf_aci = localization.anatomical_coordinates_images["AnatomicalCoordinatesImageCCFv3"]
ap_img = ccf_aci.x[:]  # PIR x = AP (Posterior direction)
dv_img = ccf_aci.y[:]  # PIR y = DV (Inferior direction)
ml_img = ccf_aci.z[:]  # PIR z = ML (Right direction)
region_names_ccf = np.asarray(ccf_aci.brain_region[:])
in_atlas_ccf = region_names_ccf != "out-of-atlas"

ap_masked = np.where(in_atlas_ccf, ap_img, np.nan)
dv_masked = np.where(in_atlas_ccf, dv_img, np.nan)
ml_masked = np.where(in_atlas_ccf, ml_img, np.nan)

lm_df = landmarks[:]  # already loaded in the Atlas-Registration section


def _add_landmarks(ax, df):
    ax.plot(df["registered_x"], df["registered_y"], "w+", ms=14, mew=2, zorder=5)
    for _, row in df.iterrows():
        ax.text(
            row["registered_x"],
            row["registered_y"],
            row["landmark_labels"],
            color="white",
            fontsize=8,
            va="bottom",
            ha="center",
            zorder=6,
        )


fig, axes = plt.subplots(1, 3, figsize=(22, 7))

# --- AP (x in PIR) ---
im0 = axes[0].imshow(ap_masked, cmap="coolwarm", interpolation="nearest")
_add_landmarks(axes[0], lm_df)
axes[0].set_title(
    f"AP  (x in CCFv3 PIR)\nAnterior ← small | Posterior → large\n"
    f"range [{np.nanmin(ap_masked):.0f}, {np.nanmax(ap_masked):.0f}] µm",
    fontsize=9,
)
axes[0].axis("off")
plt.colorbar(im0, ax=axes[0], label="µm from ASL corner", shrink=0.75)

# --- DV (y in PIR) — expect near-constant for a dorsal projection ---
im1 = axes[1].imshow(dv_masked, cmap="viridis", interpolation="nearest")
_add_landmarks(axes[1], lm_df)
axes[1].set_title(
    f"DV  (y in CCFv3 PIR)\nSuperior ← small | Inferior → large\n"
    f"range [{np.nanmin(dv_masked):.0f}, {np.nanmax(dv_masked):.0f}] µm",
    fontsize=9,
)
axes[1].axis("off")
plt.colorbar(im1, ax=axes[1], label="µm from ASL corner", shrink=0.75)

# --- ML (z in PIR) ---
im2 = axes[2].imshow(ml_masked, cmap="RdYlGn", interpolation="nearest")
_add_landmarks(axes[2], lm_df)
axes[2].set_title(
    f"ML  (z in CCFv3 PIR)\nLeft ← small | Right → large\n"
    f"range [{np.nanmin(ml_masked):.0f}, {np.nanmax(ml_masked):.0f}] µm",
    fontsize=9,
)
axes[2].axis("off")
plt.colorbar(im2, ax=axes[2], label="µm from ASL corner", shrink=0.75)

plt.suptitle(
    "AnatomicalCoordinatesImageCCFv3 — per-pixel CCF coordinate fields\n"
    "(out-of-atlas pixels shown as white / NaN; landmark crosses from registered image space)",
    fontsize=12,
)
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[15], line 11
      7 #   AP map  → should show a smooth anterior-to-posterior gradient top→bottom
      8 #   DV map  → should be nearly uniform (~332 µm for dorsal-cortex surface)
      9 #   ML map  → should show a symmetric left-to-right gradient
     10 
---> 11 ccf_aci = localization.anatomical_coordinates_images["AnatomicalCoordinatesImageCCFv3"]
     12 ap_img = ccf_aci.x[:]  # PIR x = AP (Posterior direction)
     13 dv_img = ccf_aci.y[:]  # PIR y = DV (Inferior direction)
     14 ml_img = ccf_aci.z[:]  # PIR z = ML (Right direction)

NameError: name 'localization' is not defined
In [16]:
# Landmark positions in the CCF AP–ML plane
# Expected anatomy:
#   - OB (olfactory bulb) is at the anterior pole of the dorsal cortex
#   - RSP (retrosplenial cortex) is near the posterior pole
#   - OB_left and OB_right should be symmetric about the midline
#   - OB_center and RSP_base should lie on the midline (ML ≈ 5700 µm in CCFv3)
#
# In CCFv3 PIR the AP axis runs: 0 µm (anterior face) → ~13 200 µm (posterior face)
# so SMALLER x values are more ANTERIOR.

ccf_lm = localization.anatomical_coordinates_tables["AnatomicalCoordinatesCCFv3"][:]

print("CCF landmark coordinates (PIR orientation, µm from ASL corner):")
print(f"  {'Landmark':15s} {'AP (x) µm':>12s}  {'DV (y) µm':>12s}  {'ML (z) µm':>12s}")
print("  " + "-" * 53)
for _, row in ccf_lm.iterrows():
    print(f"  {row['brain_region']:15s} {row['x']:12.1f}  {row['y']:12.1f}  {row['z']:12.1f}")

# Derived sanity metrics
ob_left_ml = ccf_lm.loc[ccf_lm["brain_region"] == "OB_left", "z"].values[0]
ob_right_ml = ccf_lm.loc[ccf_lm["brain_region"] == "OB_right", "z"].values[0]
ob_center_ml = ccf_lm.loc[ccf_lm["brain_region"] == "OB_center", "z"].values[0]
ob_center_ap = ccf_lm.loc[ccf_lm["brain_region"] == "OB_center", "x"].values[0]
rsp_ml = ccf_lm.loc[ccf_lm["brain_region"] == "RSP_base", "z"].values[0]
rsp_ap = ccf_lm.loc[ccf_lm["brain_region"] == "RSP_base", "x"].values[0]

midline_est = (ob_left_ml + ob_right_ml) / 2
CCF_MIDLINE_UM = 5700  # approximate midline in Allen CCFv3
CCF_BREGMA_AP_UM = 5400  # approximate bregma AP position in CCFv3

print()
print("AP ordering (smaller x = more anterior in CCF PIR):")
ob_is_anterior = ob_center_ap > rsp_ap  # in CCF, larger x = more posterior
print(f"  OB_center AP = {ob_center_ap:.0f} µm,  RSP_base AP = {rsp_ap:.0f} µm")

# ── Build per-region colormap ─────────────────────────────────────────────────
unique_regions_ccf = np.unique(region_names_ccf[in_atlas_ccf])
n_regions = len(unique_regions_ccf)
region_palette = plt.get_cmap("tab20b")(np.linspace(0, 1, n_regions))
region_color_map = {r: region_palette[i] for i, r in enumerate(unique_regions_ccf)}

# ── Scatter plot: AP vs ML ────────────────────────────────────────────────────
fig, ax = plt.subplots(figsize=(8, 6))

# One scatter call per region so each gets a legend entry
for region in unique_regions_ccf:
    mask = region_names_ccf == region
    ax.scatter(
        ml_img[mask],
        ap_img[mask],
        s=5,
        color=region_color_map[region],
        alpha=0.4,
        label=region,
        rasterized=True,
    )

# Landmark markers on top
colors = plt.get_cmap("tab10")(range(len(ccf_lm)))
for i, (_, row) in enumerate(ccf_lm.iterrows()):
    ax.scatter(
        row["z"],
        row["x"],
        s=160,
        color=colors[i],
        zorder=4,
        edgecolors="white",
        linewidths=0.8,
        label=f"{row['brain_region']} (landmark)",
    )
    ax.annotate(
        row["brain_region"],
        xy=(row["z"], row["x"]),
        xytext=(80, 0),
        textcoords="offset points",
        fontsize=9,
        va="center",
        arrowprops=dict(arrowstyle="-", color="gray", lw=0.8),
    )

ax.axvline(CCF_MIDLINE_UM, color="gray", ls="--", lw=1.5, label=f"Midline ≈ {CCF_MIDLINE_UM} µm")
ax.axhline(CCF_BREGMA_AP_UM, color="tab:blue", ls=":", lw=1.5, label=f"Bregma AP ≈ {CCF_BREGMA_AP_UM} µm")

ax.set_xlabel("ML  (z in CCFv3 PIR) — Left → Right  [µm]")
ax.set_ylabel("AP  (x in CCFv3 PIR) — Anterior → Posterior  [µm]")
ax.set_title("Landmark positions in CCFv3 AP–ML plane\n")
ax.invert_yaxis()  # small AP (anterior) at top

# Split legend: region pixels on the left panel, landmarks/lines on the right
region_handles = [mpatches.Patch(color=region_color_map[r], label=r) for r in unique_regions_ccf]
landmark_handles, landmark_labels = [], []
for h, l in zip(*ax.get_legend_handles_labels()):
    if l not in unique_regions_ccf:
        landmark_handles.append(h)
        landmark_labels.append(l)

legend1 = ax.legend(
    handles=region_handles,
    loc="center left",
    bbox_to_anchor=(1.02, 0.5),
    fontsize=7,
    title="Brain regions",
    title_fontsize=8,
    frameon=True,
)
ax.add_artist(legend1)
ax.legend(
    landmark_handles,
    landmark_labels,
    loc="upper right",
    fontsize=9,
)

plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[16], line 11
      7 #
      8 # In CCFv3 PIR the AP axis runs: 0 µm (anterior face) → ~13 200 µm (posterior face)
      9 # so SMALLER x values are more ANTERIOR.
     10 
---> 11 ccf_lm = localization.anatomical_coordinates_tables["AnatomicalCoordinatesCCFv3"][:]
     12 
     13 print("CCF landmark coordinates (PIR orientation, µm from ASL corner):")
     14 print(f"  {'Landmark':15s} {'AP (x) µm':>12s}  {'DV (y) µm':>12s}  {'ML (z) µm':>12s}")

NameError: name 'localization' is not defined

4. Anatomical Coordinates Tables ¶

AnatomicalCoordinatesTable stores 3-D anatomical coordinates for discrete objects (the registration landmarks here). Each row gives (x, y, z) in a named Space and a back-reference (localized_entity) to the Landmarks table.

Two tables are stored per session under localization.anatomical_coordinates_tables:

Name Space Orientation Description
AnatomicalCoordinatesIBLBregma IBL Bregma RAS x=ML, y=AP, z=DV; bregma=origin; µm
AnatomicalCoordinatesCCFv3 Allen CCFv3 PIR x=AP, y=DV, z=ML; ASL corner=origin; µm
In [17]:
ibl_table = localization.anatomical_coordinates_tables["AnatomicalCoordinatesIBLBregma"]

print(f"Space      : {ibl_table.space.space_name}")
print(f"  Origin   : {ibl_table.space.origin}")
print(f"  Orient.  : {ibl_table.space.orientation}")
print(f"  Units    : {ibl_table.space.units}")
print(f"Method     : {ibl_table.method}")
print()
ibl_table[:]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[17], line 1
----> 1 ibl_table = localization.anatomical_coordinates_tables["AnatomicalCoordinatesIBLBregma"]
      2 
      3 print(f"Space      : {ibl_table.space.space_name}")
      4 print(f"  Origin   : {ibl_table.space.origin}")

NameError: name 'localization' is not defined
In [18]:
ccf_table = localization.anatomical_coordinates_tables["AnatomicalCoordinatesCCFv3"]

print(f"Space      : {ccf_table.space.space_name}")
print(f"  Origin   : {ccf_table.space.origin}")
print(f"  Orient.  : {ccf_table.space.orientation}")
print(f"  Units    : {ccf_table.space.units}")
print(f"Method     : {ccf_table.method}")
print()
ccf_table[:]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[18], line 1
----> 1 ccf_table = localization.anatomical_coordinates_tables["AnatomicalCoordinatesCCFv3"]
      2 
      3 print(f"Space      : {ccf_table.space.space_name}")
      4 print(f"  Origin   : {ccf_table.space.origin}")

NameError: name 'localization' is not defined

5. Extract Brain Region Activity ¶

The per-pixel atlas data lets us select an anatomically-defined sub-region of the field of view and extract its mean activity directly from the raw OnePhotonSeries. We demonstrate two equivalent approaches:

Approach Mask space Frames used
A — mask stays in camera space Source space (AnatomicalCoordinatesCCFv3MappedOnSourceImage) Raw OnePhotonSeriesCalcium (no warping)
B — frames are warped into atlas space Registered space (AnatomicalCoordinatesImageCCFv3) Each frame warped via AffineTransformation

Both are applied to two kinds of ROI:

  • A named Allen region (e.g. "VISp") from the per-pixel brain_region array.
  • A CCF bounding box built directly from the per-pixel (x, y, z) arrays.

Note: for physiologically-meaningful activity, one typically uses the haemodynamic-corrected SVD traces in the desc-processed file (see processed_widefield.ipynb). The cells below showcase the mechanics of spatially-selective extraction on the raw fluorescence.

5.1 Build ROI masks in source space ¶

AnatomicalCoordinatesCCFv3MappedOnSourceImage has the same (H, W) shape as a single raw frame, so any mask built from it can be applied directly to OnePhotonSeriesCalcium.data[t] — no warping required.

We build two boolean masks:

  1. Named region — pixels whose Allen acronym equals "VISp" (right hemisphere, selected by keeping pixels with z > CCF_MIDLINE_UM).
  2. CCF bounding box — pixels whose (x, z) = (AP, ML) fall inside a chosen rectangle in Allen CCF space.
In [19]:
# --- Source-space coordinate arrays (same shape as a raw frame) ---
aci_src      = localization.anatomical_coordinates_images["AnatomicalCoordinatesCCFv3MappedOnSourceImage"]
region_src   = np.asarray(aci_src.brain_region[:])  # Allen acronym per source pixel
ap_src       = aci_src.x[:]                         # CCFv3 PIR: x = AP  (µm)
ml_src       = aci_src.z[:]                         # CCFv3 PIR: z = ML  (µm)
H, W         = region_src.shape

CCF_MIDLINE_UM = 5700  # approximate midline in Allen CCFv3 (z-axis)

# --- ROI 1: named Allen region (right hemisphere) ---
region_name = "VISp"
region_mask = (region_src == region_name) & (ml_src > CCF_MIDLINE_UM)
print(f"Region '{region_name}' (right hemisphere): {region_mask.sum()} pixels")

# --- ROI 2: CCF bounding box (AP × ML rectangle, right hemisphere) ---
ap_lo, ap_hi = 3000, 4500   # µm; smaller AP = more anterior in CCFv3 PIR
ml_lo, ml_hi = 5800, 7500   # µm; > midline = right hemisphere
bbox_mask = (
    (ap_src >= ap_lo) & (ap_src <= ap_hi)
    & (ml_src >= ml_lo) & (ml_src <= ml_hi)
    & (region_src != "out-of-atlas")
)
print(f"CCF bbox AP∈[{ap_lo},{ap_hi}] µm, ML∈[{ml_lo},{ml_hi}] µm: {bbox_mask.sum()} pixels")

# --- Visualise both masks on the source image ---
fig, axes = plt.subplots(1, 2, figsize=(14, 6), dpi=100)

axes[0].imshow(source_img, cmap="gray")
axes[0].imshow(
    np.ma.masked_where(~region_mask, region_mask.astype(float)),
    cmap="autumn", alpha=0.5, interpolation="nearest",
)
axes[0].set_title(f"Named region mask — '{region_name}' (right hemi)\n{region_mask.sum()} pixels")
axes[0].axis("off")

axes[1].imshow(source_img, cmap="gray")
axes[1].imshow(
    np.ma.masked_where(~bbox_mask, bbox_mask.astype(float)),
    cmap="cool", alpha=0.5, interpolation="nearest",
)
axes[1].set_title(
    f"CCF bounding-box mask\nAP [{ap_lo},{ap_hi}] µm × ML [{ml_lo},{ml_hi}] µm "
    f"({bbox_mask.sum()} pixels)"
)
axes[1].axis("off")

plt.suptitle("ROI masks built in source (camera) space", fontsize=12)
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[19], line 2
      1 # --- Source-space coordinate arrays (same shape as a raw frame) ---
----> 2 aci_src      = localization.anatomical_coordinates_images["AnatomicalCoordinatesCCFv3MappedOnSourceImage"]
      3 region_src   = np.asarray(aci_src.brain_region[:])  # Allen acronym per source pixel
      4 ap_src       = aci_src.x[:]                         # CCFv3 PIR: x = AP  (µm)
      5 ml_src       = aci_src.z[:]                         # CCFv3 PIR: z = ML  (µm)

NameError: name 'localization' is not defined

5.2 Approach A — mask raw OnePhotonSeries directly ¶

Because the masks above live in source (camera) space, we can index the raw calcium frames directly. We read a short time window to keep streaming fast, then take the mean over the mask on every frame.

In [20]:
# --- Read a short window of the raw calcium series ---
ops = nwbfile.acquisition["OnePhotonSeriesCalcium"]
fps = nwbfile.imaging_planes["ImagingPlaneCalcium"].imaging_rate  # 15 Hz

window_seconds = 30
n_frames       = int(window_seconds * fps)
frames         = ops.data[:n_frames]         # (n_frames, H, W), uint8
timestamps     = ops.timestamps[:n_frames]
print(f"Read {n_frames} frames × {H}×{W} = {frames.nbytes / 1e6:.1f} MB")

# --- Mean intensity inside each mask, per frame (vectorised) ---
region_trace_src = frames[:, region_mask].mean(axis=1)
bbox_trace_src   = frames[:, bbox_mask].mean(axis=1)

# --- Plot ---
fig, ax = plt.subplots(figsize=(12, 4), dpi=120)
ax.plot(timestamps, region_trace_src, color="tab:orange", lw=1.2, label=f"{region_name} (right hemi)")
ax.plot(timestamps, bbox_trace_src,   color="tab:cyan",   lw=1.2,
        label=f"CCF bbox AP[{ap_lo},{ap_hi}] × ML[{ml_lo},{ml_hi}] µm")
ax.set_xlabel("Time (s)")
ax.set_ylabel("Mean raw fluorescence (a.u., uint8)")
ax.set_title(f"Approach A — source-space masks on raw OnePhotonSeriesCalcium (first {window_seconds}s)")
ax.legend(loc="upper right", fontsize=9)
ax.set_frame_on(False)
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[20], line 9
      5 window_seconds = 30
      6 n_frames       = int(window_seconds * fps)
      7 frames         = ops.data[:n_frames]         # (n_frames, H, W), uint8
      8 timestamps     = ops.timestamps[:n_frames]
----> 9 print(f"Read {n_frames} frames × {H}×{W} = {frames.nbytes / 1e6:.1f} MB")
     10 
     11 # --- Mean intensity inside each mask, per frame (vectorised) ---
     12 region_trace_src = frames[:, region_mask].mean(axis=1)

NameError: name 'H' is not defined

5.3 Approach B — warp frames into registered space ¶

Equivalently, we can apply the AffineTransformation to each raw frame and build the masks in registered (atlas) space using AnatomicalCoordinatesImageCCFv3. skimage.transform.warp wants the inverse map (output → input), so we pass M.inverse.

We process the window frame-by-frame to keep memory bounded (no need to materialise the full warped stack just to take a mean).

In [21]:
from skimage.transform import warp

# --- Build the SAME masks in registered space ---
aci_reg    = localization.anatomical_coordinates_images["AnatomicalCoordinatesImageCCFv3"]
region_reg = np.asarray(aci_reg.brain_region[:])
ap_reg     = aci_reg.x[:]
ml_reg     = aci_reg.z[:]

region_mask_reg = (region_reg == region_name) & (ml_reg > CCF_MIDLINE_UM)
bbox_mask_reg = (
    (ap_reg >= ap_lo) & (ap_reg <= ap_hi)
    & (ml_reg >= ml_lo) & (ml_reg <= ml_hi)
    & (region_reg != "out-of-atlas")
)

# --- Warp each frame into registered space and take the mean over the mask ---
# M was defined in section 2 as SimilarityTransform(affine_matrix)
region_trace_reg = np.empty(n_frames, dtype=np.float32)
bbox_trace_reg   = np.empty(n_frames, dtype=np.float32)
for i in range(n_frames):
    warped_i = warp(
        frames[i],
        inverse_map=M,
        output_shape=(H, W),
        preserve_range=True,
        order=1,
    )
    region_trace_reg[i] = warped_i[region_mask_reg].mean()
    bbox_trace_reg[i]   = warped_i[bbox_mask_reg].mean()

# --- Show one warped frame with the registered-space masks on top ---
warped_example = warp(
    frames[0], inverse_map=M, output_shape=(H, W),
    preserve_range=True, order=1,
)
fig, axes = plt.subplots(1, 2, figsize=(14, 6), dpi=100)
axes[0].imshow(warped_example, cmap="gray")
axes[0].imshow(
    np.ma.masked_where(~region_mask_reg, region_mask_reg.astype(float)),
    cmap="autumn", alpha=0.5, interpolation="nearest",
)
axes[0].set_title(f"Warped frame 0 — '{region_name}' mask (registered space)")
axes[0].axis("off")

axes[1].imshow(warped_example, cmap="gray")
axes[1].imshow(
    np.ma.masked_where(~bbox_mask_reg, bbox_mask_reg.astype(float)),
    cmap="cool", alpha=0.5, interpolation="nearest",
)
axes[1].set_title("Warped frame 0 — CCF bbox mask (registered space)")
axes[1].axis("off")
plt.tight_layout()
plt.show()

# --- Compare traces from the two approaches ---
fig, axes = plt.subplots(2, 1, figsize=(12, 7), dpi=120, sharex=True)

axes[0].plot(timestamps, region_trace_src, color="tab:orange", lw=1.2, label="A — source-space mask")
axes[0].plot(timestamps, region_trace_reg, color="tab:purple", lw=1.2, ls="--", label="B — warped frames")
axes[0].set_ylabel("Mean fluorescence (a.u.)")
axes[0].set_title(f"{region_name} (right hemi) — Approach A vs B")
axes[0].legend(loc="upper right", fontsize=9)
axes[0].set_frame_on(False)

axes[1].plot(timestamps, bbox_trace_src, color="tab:cyan",  lw=1.2, label="A — source-space mask")
axes[1].plot(timestamps, bbox_trace_reg, color="tab:green", lw=1.2, ls="--", label="B — warped frames")
axes[1].set_xlabel("Time (s)")
axes[1].set_ylabel("Mean fluorescence (a.u.)")
axes[1].set_title(f"CCF bbox AP[{ap_lo},{ap_hi}] × ML[{ml_lo},{ml_hi}] µm — Approach A vs B")
axes[1].legend(loc="upper right", fontsize=9)
axes[1].set_frame_on(False)

plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[21], line 4
      1 from skimage.transform import warp
      2 
      3 # --- Build the SAME masks in registered space ---
----> 4 aci_reg    = localization.anatomical_coordinates_images["AnatomicalCoordinatesImageCCFv3"]
      5 region_reg = np.asarray(aci_reg.brain_region[:])
      6 ap_reg     = aci_reg.x[:]
      7 ml_reg     = aci_reg.z[:]

NameError: name 'localization' is not defined