LocalNeuroglancer — Python API¶
LocalNeuroglancer runs a local Neuroglancer instance that can load
.zarrvectors stores directly from disk or remote object stores. It
manages both a local file server (for serving local data) and a
Neuroglancer viewer tab in your browser. This tutorial covers the Python
API for loading stores, configuring layer rendering, and managing the
viewer state programmatically.
For the interactive shell console, see Shell console.
Launching the viewer¶
from ngtools.local.viewer import LocalNeuroglancer
viewer = LocalNeuroglancer()
# Output:
# fileserver: http://127.0.0.1:9123/
# neuroglancer: http://127.0.0.1:9321/v/1/
A Neuroglancer tab opens in your default browser automatically. If it does not open (e.g. on a remote server), navigate to the printed URL manually.
The viewer runs in the background as a Tornado server. Your Python session remains interactive.
Controlling the port¶
viewer = LocalNeuroglancer(
fileserver_port=9123, # local file server port
neuroglancer_port=9321, # Neuroglancer viewer port
open_browser=True, # set False to suppress auto-open
)
Using as a context manager¶
with LocalNeuroglancer() as viewer:
viewer.add("scan.zarrvectors")
input("Press Enter to close…")
# Viewer and server are shut down on exit
Loading a ZVF store¶
From local disk¶
# Infer type from .zarrvectors extension
viewer.add("scan.zarrvectors")
# Explicit layer name
viewer.add("scan.zarrvectors", name="synchrotron_scan")
# Explicit scheme
viewer.add("zarr_vectors:///path/to/scan.zarrvectors")
add() reads the root .zattrs of the store, determines the geometry
type, and registers the appropriate Neuroglancer layer type. The store is
not read in full at this point — data is fetched on demand as the viewer
pans and zooms.
From S3¶
viewer.add("zarr_vectors://s3://open-neuro/datasets/scan.zarrvectors")
For private S3 buckets, credentials must be available via the standard
AWS credential chain (~/.aws/credentials, environment variables, or IAM
role). zv-ngtools uses s3fs under the hood.
From GCS¶
viewer.add("zarr_vectors://gs://my-bucket/tracts.zarrvectors")
Loading multiple layers¶
# Load a registered OME-Zarr image volume (upstream ngtools feature)
viewer.add("zarr:///path/to/em_volume.zarr", name="EM")
# Overlay ZVF layers on the same coordinate space
viewer.add("neurons.zarrvectors", name="neurons")
viewer.add("tracts.zarrvectors", name="tracts")
viewer.add("vessels.zarrvectors", name="vessels")
All layers must share the same physical coordinate system. If your ZVF store is in voxel space and the image is in RAS mm, supply a transform (see Coordinate transforms below).
Layer listing¶
layers = viewer.list_layers()
print(layers)
# ['EM', 'neurons', 'tracts', 'vessels']
Configuring layer rendering¶
Point cloud rendering¶
viewer.add("scan.zarrvectors", name="scan")
# Point size in screen pixels
viewer.set_point_size("scan", size=3)
# Colour by attribute value (maps float32 to a colour ramp)
viewer.set_shader("scan", """
void main() {
float val = prop_intensity();
emitRGB(vec3(val, 0.2, 1.0 - val));
}
""")
# Fixed colour
viewer.set_shader("scan", color="#ff2d9a")
Streamline rendering¶
viewer.add("tracts.zarrvectors", name="tracts")
# Line width in screen pixels
viewer.set_line_width("tracts", width=1.5)
# Colour by per-vertex FA (custom GLSL shader)
viewer.set_shader("tracts", """
void main() {
float fa = prop_fa();
emitRGB(colormapJet(fa));
}
""")
# Solid colour
viewer.set_shader("tracts", color="#4da6ff")
# Opacity
viewer.set_opacity("tracts", 0.7)
Skeleton rendering¶
viewer.add("neurons.zarrvectors", name="neurons")
# Colour axons and dendrites differently
viewer.set_shader("neurons", """
void main() {
int t = prop_swc_type();
if (t == 2) emitRGB(vec3(0.2, 0.8, 1.0)); // axon: blue
else if (t == 3) emitRGB(vec3(1.0, 0.5, 0.2)); // basal: orange
else if (t == 4) emitRGB(vec3(0.2, 1.0, 0.5)); // apical: green
else emitRGB(vec3(0.8, 0.8, 0.8)); // other: grey
}
""")
Mesh rendering¶
viewer.add("brain.zarrvectors", name="brain_surface")
# Transparency
viewer.set_opacity("brain_surface", 0.4)
# Colour by attribute (e.g. cortical thickness)
viewer.set_shader("brain_surface", """
void main() {
float t = prop_thickness();
emitRGB(colormapViridis(clamp((t - 1.5) / 3.0, 0.0, 1.0)));
}
""")
LOD and resolution level control¶
Automatic LOD (default)¶
By default, zv-ngtools selects the resolution level based on the
Neuroglancer viewport’s screen resolution at the current zoom. As the
user zooms out, coarser levels are served automatically.
The LOD decision uses:
# Pseudocode inside the zv-ngtools request handler
def select_level(store, requested_resolution_um):
for level in reversed(store.levels): # coarsest first
if store.bin_shape_at(level).max() <= requested_resolution_um:
return level
return 0 # finest level if nothing coarser qualifies
Force a specific level¶
To lock the viewer to a specific resolution level (useful for debugging or when the automatic LOD selection is not appropriate):
viewer.set_level("tracts", level=2) # always serve level 2
viewer.set_level("tracts", level=None) # restore automatic LOD
Coordinate transforms¶
If a ZVF store and an image volume are in different coordinate spaces, apply a transform when loading:
import numpy as np
# 4×4 affine (homogeneous): scales from voxel to µm and offsets origin
voxel_to_um = np.array([
[4.0, 0, 0, -500.0], # x: 4 µm/vx, offset -500 µm
[0, 4.0, 0, -500.0], # y
[0, 0, 25.0, -250.0], # z: 25 µm/vx
[0, 0, 0, 1.0 ],
])
viewer.add(
"scan.zarrvectors",
name="scan",
transform=voxel_to_um,
)
The transform is stored in the Neuroglancer layer state and applied when rendering. It does not modify the ZVF store.
Hiding and removing layers¶
# Hide a layer (keeps it loaded, stops rendering)
viewer.set_visible("tracts", visible=False)
viewer.set_visible("tracts", visible=True)
# Remove a layer entirely
viewer.remove("tracts")
Getting and setting viewer state¶
The Neuroglancer state is a JSON object that encodes all layers, positions, zoom, and rendering settings. It can be serialised and shared:
# Get current state as a Python dict
state = viewer.get_state()
# Save to file
import json
with open("session_state.json", "w") as f:
json.dump(state, f, indent=2)
# Restore from file
with open("session_state.json") as f:
saved_state = json.load(f)
viewer.set_state(saved_state)
Programmatic screenshot¶
# Save the current viewport as a PNG
viewer.screenshot("screenshot.png")
# Specify resolution
viewer.screenshot("hires.png", size=(3840, 2160))
Common patterns¶
Quick data inspection¶
from ngtools.local.viewer import LocalNeuroglancer
viewer = LocalNeuroglancer()
viewer.add("scan.zarrvectors")
# Browse the data manually, then close the terminal to stop the server
Scripted figure generation¶
from ngtools.local.viewer import LocalNeuroglancer
import time
viewer = LocalNeuroglancer(open_browser=False)
viewer.add("em_volume.zarr", name="EM")
viewer.add("neurons.zarrvectors", name="neurons")
viewer.set_shader("neurons", color="#22d97a")
viewer.set_layout("3d")
viewer.move_to([1000., 800., 400.])
viewer.zoom(2.0)
time.sleep(1) # allow rendering to complete
viewer.screenshot("figure_3a.png", size=(1920, 1080))
viewer.close()