rt-convert-js
v0.1.1
Published
Convert between DICOM RT Structure Sets and DICOM Segmentation, with every lossy step recorded in provenance. Built on rt-geometry-js, rtstruct-js, and dicom-seg-js.
Maintainers
Readme
rt-convert-js
Convert between DICOM RT Structure Sets and DICOM Segmentation, built on
rt-geometry-js,
rtstruct-js, and
dicom-seg-js. Part of the
DICOM imaging toolkit.
Status: published — rt-convert-js
0.1.0 on npm. Both directions are implemented and
round-trip-validated on real TCIA data — rtstructToSeg (RTSTRUCT ROI → BINARY SEG,
voxel-for-voxel identical to the loaded ROI mask) and segToRtstruct (BINARY SEG straight
through, FRACTIONAL SEG cut at a required threshold; the mask → contour step's fidelity
is measured per conversion). See
VALIDATION.md.
The two directions are not symmetric, and neither is lossless in general:
- RTSTRUCT → SEG is a voxel copy.
RTStruct.loadalready rasterized the contours onto a grid; this writes exactly those voxels. No information is lost here (it was lost, if at all, at rasterization time). - SEG → RTSTRUCT traces a mask to polygon contours (a vectorization), and — for a
FRACTIONALSEG — first cuts the probability/occupancy field to a binary mask at a threshold the caller must choose. RTSTRUCT cannot store a per-voxel value.
Every conversion returns { bytes, provenance }. provenance.lossySteps lists each step
that does not round-trip, with the numbers to audit it. See
docs/CONVERSION.md.
Install
npm install rt-convert-js rt-geometry-js rtstruct-js dicom-seg-jsAll three toolkit packages are peer dependencies.
Use
import { RTStruct } from "rtstruct-js";
import { rtstructToSeg } from "rt-convert-js";
const rt = await RTStruct.load({ rtstruct: rtssBytes, geometry: ctGeometry });
const { bytes, provenance } = rtstructToSeg(rt, "Kidney", {
category: { value: "T-D0050", scheme: "SRT", meaning: "Anatomical Structure" },
propertyType: { value: "T-71000", scheme: "SRT", meaning: "Kidney" },
});
provenance.lossySteps; // [] — this direction is a voxel copyimport { readSeg } from "dicom-seg-js";
import { segToRtstruct } from "rt-convert-js";
const seg = readSeg(segBytes);
const { bytes, provenance } = await segToRtstruct(seg, 1, { interpretedType: "GTV" });
provenance.lossySteps[0];
// { kind: "mask-vectorization", voxelsBefore, voxelsAfter, voxelDisagreement, dice, detail }
// — segToRtstruct re-rasterizes what it wrote and reports the fidelity of *this* structureFor a FRACTIONAL SEG, pass threshold (a voxel is kept when its value is >= it);
MissingThresholdError if you don't. thresholdScale is "unit" (default, against the
[0,1] field) or "raw" (against the stored integers). The result then carries two
lossy steps — fractional-threshold then mask-vectorization:
const { provenance } = await segToRtstruct(seg, 1, { threshold: 0.5 });
provenance.lossySteps[0];
// { kind: "fractional-threshold", threshold: 0.5, thresholdScale: "unit",
// fractionalType: "PROBABILITY", maximumFractionalValue: 255, voxelsBefore, voxelsAfter, detail }rt-convert-js does not re-export rtstruct-js or dicom-seg-js (they each
re-export all of rt-geometry-js, so re-exporting both would collide). Import RTStruct,
readSeg, and the geometry types from those packages directly.
