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
drawPenValueis the label to paint. With the defaultdrawcolormap, 1 is red, 2 is green and 3 is blue. A value of 0 erases.drawPenSizeis the brush width in voxels, anddrawPenShapepicksPEN_SHAPE.RECTANGLE(default) orPEN_SHAPE.CIRCLEfor wide brushes.drawPenFilled = truefills the area enclosed by each stroke. Pair it withdrawPenAutoClose = trueso 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
- Drawing: pen, fill, erase, undo, save, load and the magic wand.
- Drawing on 3D blocks: painting directly on the 3D render.
- Annotations: the vector annotation layer.