Skip to main content

Drawing and segmentation

NiiVue can paint a label volume on top of the background image. The drawing has the same grid as the background volume (volume 0), each voxel holds a label number, and you can save it as NIfTI. Try drawing on the slice below, then switch on Filled and draw a closed loop.

import NiiVue, { SLICE_TYPE } from '@niivue/niivue'

const demo = async (canvas, { ui, asset }) => {
const nv = new NiiVue({ sliceType: SLICE_TYPE.AXIAL, drawPenSize: 3 })
await nv.attachToCanvas(canvas)
await nv.loadVolumes([{ url: asset('volumes/mni152.nii.gz') }])
nv.createEmptyDrawing()

ui.checkbox('Draw', true, (on) => {
nv.drawIsEnabled = on
})
ui.select(
'Pen',
[
['Red', '1'],
['Green', '2'],
['Blue', '3'],
['Erase', '0'],
],
'1',
(v) => {
nv.drawPenValue = Number(v)
},
)
ui.checkbox('Filled', false, (on) => {
nv.drawPenFilled = on
nv.drawPenAutoClose = on
})
ui.button('Undo', () => nv.drawUndo())
return nv
}

export default demo

Start a drawing​

Load a background volume first, then call createEmptyDrawing(). It makes a blank drawing, clears the undo history and turns drawing on. Toggle the pen with drawIsEnabled: while it is on, left-dragging on a slice paints instead of moving the crosshair.

await nv.loadVolumes([{ url: '/volumes/mni152.nii.gz' }])
nv.createEmptyDrawing()
nv.drawIsEnabled = false // look around without painting

loadDrawing(url or File) opens an existing label image as the drawing instead, and closeDrawing() discards the drawing and turns drawing off.

The pen​

  • drawPenValue is the label to paint. With the default draw colormap, 1 is red, 2 is green and 3 is blue. A value of 0 erases.
  • drawPenSize is the brush width in voxels, and drawPenShape picks PEN_SHAPE.RECTANGLE (default) or PEN_SHAPE.CIRCLE for wide brushes.
  • drawPenFilled = true fills the area enclosed by each stroke. Pair it with drawPenAutoClose = true so a stroke that does not quite meet its start is closed for you.
  • drawIsFillOverwriting (on by default) lets a fill paint over existing labels. Turn it off to fill only empty voxels.
nv.drawPenValue = 2
nv.drawPenSize = 3
nv.drawPenFilled = true
nv.drawPenAutoClose = true

Appearance​

drawOpacity (0 to 1, default 0.8) sets how strongly the drawing covers the image. drawRimOpacity gives the edge of each region its own opacity, so a low drawOpacity with a rim of 1 shows outlined regions; the default of -1 turns the rim off. drawColormap picks the label colors: any label colormap whose codes fit in 0 to 255. nv.drawingColormaps lists the names you can use.

Undo​

Every stroke, fill and magic-wand click saves an undo step. drawUndo() steps back one. If you change the drawing yourself (for example from an extension), call drawAddUndoBitmap() first so that change can be undone too.

Magic wand​

Set drawIsClickToSegment = true and a click grows a region of similar intensity from the clicked voxel, painting it with the current pen value. drawClickToSegmentTolerance sets how similar the voxels must be, and drawClickToSegmentIs2D (on by default) keeps the region on the clicked slice. A clickToSegment event reports the voxel count and volume of each fill.

Saving​

saveDrawing(filename) writes the drawing as NIfTI and downloads it. getDescriptives({ volumeIndex: 0, isDrawingMask: true }) measures the background image inside the drawn region.

await nv.saveDrawing('lesion.nii.gz')

Events​

The viewer fires drawingChanged (with an action such as 'stroke', 'undo', 'create' or 'load'), drawingEnabled when drawIsEnabled changes, and penValueChanged when drawPenValue changes. See Mouse, touch and events.

Vector annotations​

Separate from the voxel drawing, the annotation layer draws vector shapes (freehand, ellipse, rectangle, line, arrow and others) in slice coordinates, and annotationsToSVG() exports them. Turn it on with annotationIsEnabled and pick a shape with annotationTool. Use one layer at a time: while drawIsEnabled is on, the voxel pen takes the pointer and annotation editing does nothing. Spline and livewire contours are placed one click at a time and closed with a double-click on a 2D slice. While a contour is open, a double-click on the 3D render is ignored and one on a graph keeps its graph action. A bidirectional measurement takes two drags on the same slice; a second drag on another slice starts it over. Switching to a different tool or turning annotations off drops a half-drawn shape; setting annotationTool to the tool already selected keeps it. loadDocument drops a half-drawn shape and also clears the annotation selection and the annotationUndo() history, which belong to the old scene.

Beyond the core​

The @niivue/nv-ext-drawing package adds helpers that run on the drawing, such as interpolating a mask between drawn slices; see Extensions. Grow-cut segmentation (drawGrowCut) and drawingBinaryDilationWithSeed from the old package are not ported yet.

See also​