Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Visualizing Neuropixels Probe Locations

Open In Colab

It can be handy to know the location and trajectory of the probes that obtain the data. Some NWB files have an electrodes field which stores these locations. To be more precise, they contain arrays of CCF coordinates. CCF is a framework which represents locations in the brain with coordinates that are relative to predefined brain structures in a model brain. This notebook uses CCF coordinate data in an extracellular electrophysiology NWB file to render the locations of the Neuropixels probes that were used.

To be able to render locations in a brain, you don’t need to know how CCF works except that it is a system of coordinates. We render the brain surface and the probe coordinates with Plotly, which produces an interactive HTML/JavaScript 3D scene that renders inline across platforms (VS Code, JupyterLab, Google Colab, and Dandihub). The whole-brain surface mesh comes from the Allen Institute’s CCF structure meshes.

Environment Setup

⚠️Note: If running on a new environment, run this cell once and then restart the kernel⚠️

try:
    from databook_utils.dandi_utils import dandi_download_open
except:
    !git clone --depth 1 https://github.com/AllenInstitute/openscope_databook.git
    %cd openscope_databook
    %pip install -e .
import numpy as np
import plotly.graph_objects as go

Downloading Ecephys File

If you don’t already have a file to analyze, you can use a file from The Allen Institute’s Visual Coding - Neuropixels dataset. If you want to choose your own file to download, set dandiset_id and dandi_filepath accordingly.

dandiset_id = "000253"
dandi_filepath = "sub-637908/sub-637908_ses-1213341633_ogen.nwb"
download_loc = "."
# This can sometimes take a while depending on the size of the file
io = dandi_download_open(dandiset_id, dandi_filepath, download_loc)
nwb = io.read()
File already exists
Opening file

Extracting NWB CCF Coordinates

Here, you can read the NWB file you’re interested in viewing. Specify your file of interest’s relative file path in nwb_filepath. From there, the file will be read and the probe unit coordinates will be extracted and turned into a numpy array.

Note that this will only work with ecephys NWB files which have a valid electrodes field.

### read the x,y,z ccf coordinates and generate points
xs = nwb.electrodes.x
ys = nwb.electrodes.y
zs = nwb.electrodes.z
n = min(len(xs), len(ys), len(zs))
points = np.array([[xs[i], ys[i], zs[i]] for i in range(n)])
# each point has 3 coordinates associated with it
print(points.shape)
(2304, 3)

Rendering

We build an interactive 3D scene with Plotly: the whole-brain CCF surface is drawn as a semi-transparent mesh, and each electrode’s CCF coordinate is plotted as a point inside it. The scene renders inline below and is also saved as a standalone neuropixels_probe_locations.html file you can open in any browser.

import os
import urllib.request

# Whole-brain CCF surface mesh from the Allen Institute (cached locally after first download).
brain_mesh_url = "http://download.alleninstitute.org/informatics-archive/current-release/mouse_ccf/annotation/ccf_2017/structure_meshes/997.obj"
brain_mesh_file = "ccf_root_mesh.obj"

if not os.path.exists(brain_mesh_file):
    request = urllib.request.Request(brain_mesh_url, headers={"User-Agent": "Mozilla/5.0"})
    with urllib.request.urlopen(request, timeout=120) as response, open(brain_mesh_file, "wb") as out_file:
        out_file.write(response.read())

# Parse the OBJ mesh into vertices and triangular faces (OBJ indices are 1-based)
mesh_vertices = []
mesh_faces = []
with open(brain_mesh_file) as mesh_file:
    for line in mesh_file:
        if line.startswith("v "):
            _, vx, vy, vz = line.split()[:4]
            mesh_vertices.append((float(vx), float(vy), float(vz)))
        elif line.startswith("f "):
            face = [int(part.split("/")[0]) - 1 for part in line.split()[1:]]
            mesh_faces.append(face[:3])

mesh_vertices = np.array(mesh_vertices)
mesh_faces = np.array(mesh_faces)

# Keep only electrodes with valid CCF coordinates (finite and inside the brain volume)
valid = np.all(np.isfinite(points), axis=1) & np.all(points > 0, axis=1)
probe_points = points[valid]
print(f"Rendering {len(probe_points)} of {len(points)} electrode locations")
Rendering 2184 of 2304 electrode locations
brain_surface = go.Mesh3d(
    x=mesh_vertices[:, 0], y=mesh_vertices[:, 1], z=mesh_vertices[:, 2],
    i=mesh_faces[:, 0], j=mesh_faces[:, 1], k=mesh_faces[:, 2],
    color="lightgray", opacity=0.15, hoverinfo="skip", name="CCF brain",
)
electrodes = go.Scatter3d(
    x=probe_points[:, 0], y=probe_points[:, 1], z=probe_points[:, 2],
    mode="markers",
    marker=dict(size=2, color=probe_points[:, 1], colorscale="Viridis"),
    name="electrodes",
)

fig = go.Figure(data=[brain_surface, electrodes])
fig.update_layout(
    title="Neuropixels electrode CCF locations",
    scene=dict(
        xaxis_title="anterior → posterior (µm)",
        yaxis_title="dorsal → ventral (µm)",
        zaxis_title="left → right (µm)",
        yaxis=dict(autorange="reversed"),
        aspectmode="data",
    ),
    margin=dict(l=0, r=0, t=30, b=0),
)

# Save a self-contained interactive HTML/JavaScript render that opens in any browser
fig.write_html("neuropixels_probe_locations.html", include_plotlyjs="cdn")
fig
Loading...