Design Choices¶
This page explains the decisions we made, alternatives considered, trade-offs, and future work.
Problem / Goal¶
Catena produces many intermediate artefacts:
- LSDs, affinities, segmentations,
- synapse annotations and model predictions,
- mitochondria masks, neuron IDs, etc.
We need simple, scriptable ways to:
- inspect the EM volumes and labels,
- overlay multiple modalities (raw + seg + synapses),
- debug coordinate issues (offsets, resolutions, ZYX vs XYZ),
- without locking into one viewer or data backend.
The Visualize module is built around:
- Napari for lightweight local exploration,
- Neuroglancer for large-scale 3D browsing and annotation layers.
Alternatives Considered¶
1. Only Napari¶
Napari by itself is excellent for local 2D/3D viewing, but:
- does not natively support web sharing or multi-user access,
- is less suited for working with very large volumes and annotation layers in the browser.
2. Only Neuroglancer¶
Neuroglancer is ideal for huge datasets and networked access, but:
- setup is more involved,
- the workflow is heavier for quick local checks,
- tight integration with Python-based preprocessing (like padding LSD segmentations) is easier to iterate on in Napari.
3. Custom GUI¶
A bespoke GUI would add maintenance burden and duplicate features already well served by Napari/Neuroglancer.
Decision: support both Napari and Neuroglancer through small focused scripts, rather than inventing new viewers.
Decision¶
1. Separate environments per viewer¶
The module ships two environment files:
environment_napari.ymlenvironment_neuroglancer.yml(plus a Mac variant)
Reasons:
- Napari and Neuroglancer have different dependency stacks and expectations.
- Pinning versions in dedicated envs improves reproducibility and avoids conflicts.
- Users can install only what they need (Napari-only, Neuroglancer-only, or both). :contentReference[oaicite:16]{index=16}
2. Generic Zarr/HDF loaders in Napari¶
visualize_napari.py:
- walks the entire Zarr/HDF tree and auto-discovers datasets,
- adds image layers for non-label arrays,
- adds label layers for datasets whose names contain
labelorseg.
This avoids hard-coding dataset keys and makes the script reusable across:
- LSD outputs,
- synapse volumes,
- mitochondria/neuron IDs stored in the same container.
It also supports:
- loading full datasets for small volumes,
- or slicing (
-s,-sf,-st) to avoid blowing up RAM on large volumes.
3. Explicit handling of resolution and offsets¶
Synapse visualisation scripts (Napari + Neuroglancer):
- read offset attributes from datasets, when present,
- convert synapse locations from nm to voxel coordinates using a user-specified resolution (e.g.
40,4,4), - add this offset/scale before plotting pre/post points and connectors.
This makes it possible to:
- overlay raw, labels, and annotations correctly,
- handle CREMI-style volumes and derived cubes consistently.
4. LSD padding as a first-class concern¶
LSD training uses valid convolutions, yielding smaller outputs than the input raw (border pixels are chopped off). The dedicated doc pad_seg_for_napari_viz.md:
- explains why shapes differ,
- shows how to transpose if needed,
- gives a concrete
numpy.padsnippet that pads segmentations symmetrically to match raw.
Instead of hiding this, the design makes padding explicit so users understand and control how segmentations are aligned.
5. Synapse representation: points + connectors¶
Both Napari and Neuroglancer scripts model synapses as:
- pre-synaptic points,
- post-synaptic points,
- line segments connecting them.
In Neuroglancer (visualize_nglancer_synapses_hdf.py, visualize_nglancer_synapses_mito_hdf.py, visualize_synful_inference.py):
- these are turned into
LocalAnnotationLayers with appropriate colours and coordinate spaces, - they can be combined with segmentation layers (neuron IDs, clefts, mito IDs).
In Napari:
visualize_napari_synapses.pyusesPointsandShapeslayers,visualize_napari_synapses_save_load_napari_ids.pyadditionally tracks original annotation IDs, with CSV export/import of Napari index <-> ID mappings.
This representation is:
- simple,
- easy to reason about,
- and natural to extend (e.g. add properties for scores, IDs, neuron labels).
Trade-offs¶
-
Flexibility vs boilerplate
Using small scripts rather than a monolithic "viewer app" means:- some user-side editing (paths, dataset names, resolutions) is required,
- but each script stays short and easy to adapt.
-
Two envs vs one
Separate conda envs add a bit of setup overhead, but materially reduce dependency conflicts between Napari and Neuroglancer stacks. -
Local vs web
Napari is ideal for quick local inspection, while Neuroglancer shines for big volumes and sharing URLs. Supporting both is intentionally redundant but matches real-world workflows.
Implementation Notes¶
-
Napari
visualize_napari.py- generic viewer for any Zarr/HDF file (raw, seg, affinities, etc.).visualize_napari_synapses.py- raw + clefts + pre/post points + connectors.visualize_napari_synapses_save_load_napari_ids.py- as above, plus CSV mapping between Napari indices and original annotation IDs.
-
Neuroglancer
nglancer_pyconnectomics_example.py- template for raw/seg/affinity visualisation from Zarr.visualize_nglancer_synapses_hdf.py- raw + neuron_ids + clefts + synapse annotations (CREMI-style).visualize_nglancer_synapses_mito_hdf.py- above plus mitochondria IDs from separate Zarr.visualize_synful_inference.py- Synful predictions, ground-truth synapses, and post-processed synapse tables as annotation layers.
-
Environments
environment_napari.yml- pins Napari version and related scientific Python stack.environment_neuroglancer.yml/environment_neuroglancer_mac.yml- pin Neuroglancer, Daisy, funlib, and associated tooling.
Operational Guidance¶
-
Start with Napari for:
- quick inspection of a new volume,
- checking that shapes, axes, and labels line up,
- confirming LSD padding is correct.
-
Move to Neuroglancer for:
- exploring bigger volumes,
- sharing a URL with collaborators,
- visualising many synapses, neuron IDs, and mito masks simultaneously.
-
When overlays look wrong:
- print shapes and check ZYX vs XYZ,
- inspect dataset
attrs['offset'], - verify the
--res(voxel size) you passed matches the dataset.
Future Work / Open Questions¶
- More configuration-driven scripts (e.g. small YAML specifying paths, keys, resolution) to avoid editing Python directly.
- Tightening the integration with other Catena modules (e.g. direct handoff from Synful/SimpSyn runs to visualisation scripts).
- Optional hooks to automatically open the correct viewer (Napari vs Neuroglancer) based on dataset size and type.