Skip to content

About

Convert medical volumes, fuse scans, and extract surface meshes

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

79 Commits

Folders and files

Repository files navigation

medsurface

Install · Quick start · Commands · User guide · Technical reference

Convert medical volumes without changing their image data, extract STL/PLY/OBJ surface meshes, register two segmentations into an editable binary labelmap, or edit a labelmap block by block in Minecraft Java. DICOM series, NIfTI, NRRD, and MetaImage inputs are supported.

Safety: medsurface is not validated for diagnosis, treatment planning, or other clinical decisions. Segmentation, registration, and mesh processing can change or omit anatomy. Review source images and every output independently. See Safety and privacy.

Install

Python 3.11 or newer and uv are required.

uv tool install .

Quick start

First inspect the available volumes:

medsurface list ~/scans/head-ct

Then extract the recommended volume's surface:

medsurface extract ~/scans/head-ct -o skull.stl

The default bone preset extracts stored values at or above 300. It is treated as HU only when DICOM CT metadata supplies sufficient calibration evidence; other inputs receive a warning. Use the unique integer ID printed by list when the catalog has no safe automatic choice:

medsurface extract ~/scans/head-ct --volume 1 -o skull.stl

A direct .nii, .nii.gz, .nrrd, .nhdr, .mha, or .mhd volume needs no selector. To change only its storage format, use strict conversion:

medsurface convert scan.mhd -o scan.nrrd

convert preserves loaded voxel values, scalar pixel type, component count, dimensions, spacing, origin, and direction. It does not threshold, cast, resample, or reorient the image. Source metadata is retained when the output format can represent it; add --strip-metadata to remove it while keeping required format headers.

If another tool already created a segmentation labelmap, start at surface extraction instead of intensity segmentation:

medsurface labelmap extract segmentation.nii.gz -o surface.stl

Commands

command purpose
medsurface list INPUT Show every supported volume and any automatic default
medsurface presets Show the available tissue presets
medsurface convert INPUT -o VOLUME Preserve one image while changing its single-file storage format
medsurface extract INPUT -o MODEL.stl Segment one intensity volume and extract a surface mesh
medsurface fuse FIXED MOVING -o LABELMAP Segment, register, and union two volumes into a binary labelmap
medsurface labelmap extract MASK -o MODEL.stl Extract one surface from all nonzero (or --labels) labels
medsurface labelmap extract MASK --split DIR Extract one validated surface per label
medsurface labelmap fuse FIXED MOVING... -o LABELMAP Register and fuse labelmaps into a binary or --preserve-labels map
medsurface validate MODEL.stl Report mesh quality without changing the file
medsurface repair MODEL.stl -o FIXED.stl Repair an open or non-manifold mesh
medsurface minecraft to-world MASK -o WORLD Build an editable Minecraft Java world, one voxel per block
medsurface minecraft to-labelmap WORLD -o LABELMAP Validate an edited world and export it on the original grid
medsurface minecraft inspect WORLD Show a world's grid, placement, and label palette
medsurface minecraft validate WORLD Check every block in a world's export volume
medsurface minecraft resample MASK -o LABELMAP Write the cubic-block grid to-world --block-mm would build
medsurface minecraft blocks List the 127 supported label blocks with approximate colors

Run medsurface COMMAND --help for every option and medsurface --version for the installed version. Mesh destinations are .stl, .ply, or .obj. Conversion and fusion destinations are atomic single-file .nii, .nii.gz, .nrrd, or .mha volumes. Detached .nhdr and .mhd remain input-only.

Common workflows

Convert a volume without processing it

Convert one selected catalog volume and record the operation:

medsurface convert scans/ --volume 2 -o selected.nii.gz \
  --json selected-conversion.json

.nii is uncompressed, .nii.gz uses gzip, .nrrd uses gzip, and .mha uses zlib. The volume is written to a temporary sibling, read back, and checked for exact voxel data, type, components, dimensions, and physical geometry before it atomically replaces the destination. An unrepresentable core image contract is an error; metadata preservation remains best effort.

Extract an intensity surface

Choose a preset or explicit threshold when producing a mesh:

medsurface extract scans/ --preset teeth -o teeth.stl
medsurface extract scans/ --threshold 250 -o bone-250hu.stl
medsurface extract scan.mha --preset auto -o automatic.stl

Surface controls such as --mask-smooth-mm, --mesh-smooth-iters, --simplify-error-mm, --post-mesh-smooth-iters, --components, and --destep belong to extract commands only.

Remove broad stair-step ripples

CT slice terraces can survive as shallow ripples on smooth anatomy such as a skull dome. Optional final fairing removes them while leaving frozen regions untouched:

medsurface extract scans/ -o skull.stl --destep auto
medsurface labelmap extract skull.nrrd -o skull.stl \
  --destep band --destep-axis z --destep-full-mm -555 --destep-frozen-mm -600

auto fairs broad surfaces and freezes tightly curved detail such as teeth, together with its surroundings, band ramps from a frozen coordinate to a fully faired one, and all fairs every vertex. Each vertex moves at most --destep-max-mm (1 mm by default). See Reducing stair-step ripples.

Fuse intensity volumes, then extract

Confirm that both scans show the same subject and matching non-deforming anatomy, then create an inspectable binary union before meshing it:

medsurface fuse facial-ct/ sinus-ct/ \
  --fixed-volume 1 --moving-volume 1 -o skull-union.nrrd
medsurface labelmap extract skull-union.nrrd -o skull.stl

fuse independently segments each input, rigidly registers moving foreground to fixed foreground, and publishes scalar uint8 values 0 and 1 on an axis-aligned isotropic grid. It never writes a mesh or applies surface controls. After success it prints a shell-safe medsurface labelmap extract command hint, unless normal output is suppressed with --quiet. Registration-quality checks can refuse unsafe geometry; --force overrides only those gates and can produce a plausible but incorrect labelmap.

Fuse external labelmaps, then extract

TotalSegmentator and similar tools remain separate optional programs. Every nonzero label is treated as one foreground class:

TotalSegmentator -i scan.nii.gz -o selected.nii.gz \
  --ml --roi_subset skull vertebrae_C1
medsurface labelmap extract selected.nii.gz -o selected.stl

To register matching masks from two acquisitions, publish the binary union, optionally edit it, and explicitly extract its surface:

medsurface labelmap fuse fixed-selected.nii.gz moving-selected.nii.gz \
  -o fused.mha --json fused.json
# Edit fused.mha if needed.
medsurface labelmap extract fused.mha -o fused.stl

Different positive source IDs are equivalent and are not preserved. Fusion does not transfer source metadata or intensity-preset surface settings to the result; the later labelmap extract uses its normal labelmap defaults.

Keep structures separate

Select labels, write one mesh per structure, and fuse more than two scans while keeping their label IDs:

medsurface labelmap extract segmentations.nii.gz --labels 25-50,91 \
  --split parts/ -o parts/combined.stl
medsurface labelmap fuse head.nii.gz neck.nii.gz chest.nii.gz \
  --preserve-labels -o body.nii.gz

Per-label files are named from the label table TotalSegmentator embeds in NIfTI files, or from --label-names names.json. Programs can add --progress json to any command for JSON-lines progress on stderr. See Using an external labelmap.

Edit a labelmap in Minecraft

Turn a labelmap into a Minecraft Java 26.2 world, edit it block by block, and export the edited labels on the original grid:

medsurface minecraft to-world segmentations.nii.gz -o ./labels-world
# Copy labels-world into Minecraft's saves directory, edit, and close it.
medsurface minecraft to-labelmap ./labels-world -o edited.nii.gz

One voxel becomes one block and label 0 is air. Add --block-mm auto --orient anatomical for cubic blocks with the anatomy standing upright. See Minecraft worlds.

Validate and repair

Every mesh-producing extraction validates its serialized mesh before atomic publication. Inspect an existing mesh without changing it:

medsurface validate model.stl

Repair validates both its in-memory result and serialized temporary file, then atomically publishes a separate output:

medsurface repair broken.stl -o repaired.stl

Primary outputs and optional JSON reports cannot alias each other or any discovered input, including DICOM instances and detached payloads. Conversion and fusion roll their primary destination back if requested report publication fails. A valid mesh or verified volume can still be anatomically wrong; read the input limitations and validation explanation.

About

Convert medical volumes, fuse scans, and extract surface meshes

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages