Skip to content

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.yml
  • environment_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 label or seg.

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.pad snippet 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.py uses Points and Shapes layers,
  • visualize_napari_synapses_save_load_napari_ids.py additionally 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.