Atlases and labels
A label volume (an atlas) stores an integer region code in each voxel instead of an intensity. To display it, you give the volume a label colormap: a color and a name for each code. NiiVue then draws each region in its own color and reports the region name under the crosshair.
A volume has one colormap. It is a label colormap when its definition has a
labels array, and a continuous ramp otherwise; setVolume(i, { colormap })
selects either kind.
The demo loads the AAL atlas over the MNI152 template. Click anywhere to see the region name:
import NiiVue, { SHOW_RENDER } from '@niivue/niivue'
const demo = async (canvas, { ui, asset }) => {
const nv = new NiiVue({
showRender: SHOW_RENDER.ALWAYS,
backgroundColor: [0.3, 0.3, 0.5, 1],
})
await nv.attachToCanvas(canvas)
// A colormap with a `labels` array turns the integer volume into an atlas
await nv.addColormapFromUrl(asset('volumes/aal.json'), 'aal')
await nv.loadVolumes([
{ url: asset('volumes/mni152.nii.gz'), calMin: 30, calMax: 80 },
{ url: asset('volumes/aal.nii.gz'), opacity: 0.5, colormap: 'aal' },
])
const show = ui.status('Click a region')
nv.addEventListener('locationChange', (e) => {
show(e.detail.values[1]?.label ?? 'no region')
})
ui.slider(
'Opacity',
0,
1,
0.5,
async (v) => {
await nv.setVolume(1, { opacity: v })
},
0.05,
)
ui.slider('Outline', 0, 3, 0, (v) => {
nv.setAtlasOutline(v, 1)
})
ui.checkbox('Hide dark voxels', false, (on) => {
nv.volumeIsAlphaClipDark = on
})
return nv
}
export default demo
Label colormaps
addColormapFromUrl fetches a JSON file with R, G and B arrays plus
labels (and optionally I, the code of each entry) and registers it under
its file name. Select it like any colormap:
await nv.addColormapFromUrl('/volumes/aal.json') // registered as 'aal'
await nv.loadVolumes([
{ url: '/volumes/mni152.nii.gz', calMin: 30, calMax: 80 },
{ url: '/volumes/aal.nii.gz', opacity: 0.5, colormap: 'aal' },
])
The URL can also be a File, and addColormap(name, { R, G, B, labels })
registers a table you build in code. labels may be empty: it still marks
the colormap as a label table. Built-in label colormaps include
freesurfer, itksnap, slicer3d and Random (generated per volume);
nv.colormapNames('label') lists them all:
await nv.setVolume(1, { colormap: 'freesurfer' })
Picking a continuous colormap such as 'gray' turns the labels off again.
A volume whose NIfTI header marks it as labels (NIFTI_INTENT_LABEL) and is
loaded without a colormap is drawn with Random, a generated palette that
gives every region code in the volume its own color. The same code gets the
same color in every volume, so a segmentation looks right with no setup;
pick an atlas table for its own colors and names. The palette covers at
least codes 0 to 255 and grows to every code the volume holds, in all of
its 4D frames and when updateVolumeData or an edit brings higher codes, so
a live segmentation keeps distinct colors.
Label volumes are sampled with nearest-neighbor interpolation on 2D slices,
so region borders stay sharp. A volume's isNearestInterpolation option
overrides that choice.
Reading labels
With a label colormap set, the locationChange event includes the region
name for each volume in detail.values[i].label:
nv.addEventListener('locationChange', (e) => {
console.log(e.detail.values[1]?.label)
})
To measure the background image inside one region, pass that region's code
as a mask to getDescriptives:
const stats = nv.getDescriptives({ volumeIndex: 0, masks: [{ index: 1, values: [17] }] })
Outlines and dark voxels
nv.setAtlasOutline(width, volumeIndex) outlines the region borders. The
width is the distance, in the atlas's own voxels, that NiiVue probes for a
neighboring label, and 0 turns it off. You can also set atlasOutline in the load options. Outlines
are not drawn on streamed (chunked) volumes.
nv.volumeIsAlphaClipDark = true makes voxels below calMin transparent,
which hides the dark air around a template.
Labels on a drawing
You can also show labels in the drawing layer. Register a colormap with
labels, make it the drawing colormap, and load the label image as a
drawing:
nv.addColormap('torso', {
R: [0, 0, 185],
G: [0, 20, 102],
B: [0, 152, 83],
A: [0, 255, 255],
I: [0, 1, 2],
labels: ['background', 'spleen', 'kidneyR'],
})
nv.drawColormap = 'torso'
await nv.loadDrawing('/volumes/torsoLabel.nii.gz')
A drawing holds codes 0 to 255, so drawColormap accepts any label
colormap whose codes fit and refuses the rest. See
Drawing and segmentation for editing the drawing.
Probabilistic and mesh atlases
PAQD files store a probabilistic atlas (several labels and probabilities per
voxel). nv.volumePaqdUniforms tunes how they render; see the
PAQD atlas example.
Meshes can carry atlas labels too. MZ3 atlas meshes load with loadMeshes,
and list their labels in the legend, as a label volume does;
setMesh(0, { isLegendVisible: false }) or setVolume(i, { isLegendVisible: false })
takes one out, and nv.isLegendVisible = false hides the legend. Readers for
FreeSurfer annot files and GIfTI label layers have not been ported yet, so
other mesh atlas formats do not load.
NiiVue 0.x had addLabel() for text labels in the 3D view. This version
does not. The @niivue/uikit package's UIKitPointLabelOverlay draws a text
label with a leader line to a point instead.