nifti2dicom
Convert NIfTI images and masks into DICOM with a reference scan.
- Scalar 2D, 3D and 4D images → classic CT, MR or PET series.
- Binary and multilabel masks → one multi-segment DICOM SEG.
- 2D/3D RGB images → Secondary Capture.
- Physical-coordinate geometry, quantitative pixel encoding, structured errors, and output validation before publication.
Quick start
pip install nifti2dicom
nifti2dicom scan.nii.gz --reference ./dicom
Output goes to scan_dicom/ beside the input. It contains DICOM files and a
conversion.json report with warnings, geometry and label mapping. Supply
-o ./converted to choose another location.
The NIfTI's dimensions, orientation and spacing are read automatically. The reference provides patient/study and modality metadata; it does not need the same dimensions or number of slices for CT/MR. PET requires matching frames to preserve acquisition timing and correction metadata. A reference may be a file or a directory containing a single series, including nested folders.
# Inspect inferred meaning and reference geometry without creating output
nifti2dicom inspect scan.nii.gz --reference ./dicom
# Explicitly resample into the reference's physical grid
nifti2dicom scan.nii.gz --reference ./dicom --geometry reference
# Select a series when a folder contains several
nifti2dicom scan.nii.gz --reference ./dicom --series-uid 1.2.3.4
# Machine-readable result or error; no progress text on stdout
nifti2dicom scan.nii.gz --reference ./dicom --json
Segmentations
DICOM BINARY describes each segment's pixels, not the number of segments.
Both a binary mask and a multilabel mask can become a BINARY SEG containing
multiple segments. The encoder uses highdicom and verifies the written masks
against their source DICOM frames.
# A manually drawn binary mask
nifti2dicom mask.nii.gz --reference ./dicom --kind seg --algorithm-type manual
# Multilabel output from a model
nifti2dicom organs.nii.gz --reference ./dicom --labels labels.json \
--algorithm-type automatic --algorithm-name MOOSE --algorithm-version 3.0
Values must be nonnegative integers: 0 is background. Values such as 5 and 300 are supported without an 8-bit cast. The conversion report records the mapping from input label values to consecutive DICOM segment numbers.
A simple label mapping:
{
"5": "Lesion",
"300": "Liver"
}
For semantic interoperability, include coded category and type descriptions. Names alone cannot establish anatomy; missing codes use an explicitly unspecified local code and produce a warning. Supply real codes from your segmentation schema rather than inferring them from pixel values.
MOOSE's nested organ_indices format and its SNOMED.ID/SNOMED.name entries
are accepted. A richer file can also carry algorithm provenance:
{
"algorithm": {"type": "automatic", "name": "MyModel", "version": "1.0"},
"labels": {
"300": {
"name": "Liver",
"category": {
"value": "123037004", "scheme": "SCT", "meaning": "Anatomical Structure"
},
"type": {"value": "10200004", "scheme": "SCT", "meaning": "Liver"}
}
}
}
SEG always aligns to the reference grid using nearest-neighbor interpolation. Foreground outside that grid, disappearance of a segment during resampling, and an entirely empty mask are reported clearly. Masks are never silently thresholded. Automatic and semiautomatic masks require their creator's name and version; conversion software is not treated as the segmentation algorithm.
Separate overlapping masks, probability/FRACTIONAL output, newer LABELMAP SEG, and time-varying SEG are not yet supported. Do not collapse overlapping masks into one integer map: that would discard overlap.
See highdicom's SEG guide and the DICOM segmentation module.
RGB
nifti2dicom color.nii.gz --reference ./dicom --kind rgb
Explicit RGB supports arrays shaped (X,Y,3) or (X,Y,Z,3). Channels must be
8-bit integer values from 0 to 255. NIfTI's structured RGB datatype is inferred
automatically. An ordinary fourth axis of length three is treated as three
scalar volumes unless RGB is explicitly selected.
Python API
from nifti2dicom import convert, inspect, ConversionError
info = inspect("scan.nii.gz", "./dicom")
try:
result = convert("scan.nii.gz", "./dicom", "./converted")
except ConversionError as error:
print(error.message)
print(error.hint)
print(error.code) # Stable identifier for automation
else:
print(result.files) # Only the DICOM files
print(result.warnings) # Assumptions and conversion notices
Python calls print nothing. Pass on_progress=callback to receive typed progress
events. result.to_dict() is JSON-ready.
Automatic decisions and limits
- Native geometry is preserved by default; resampling is explicit.
- Reference stacks tolerate scanner coordinate rounding within at most 0.005 mm of the grid spanning their first and last planes, with a warning above 0.001 mm. Every plane is checked; uneven spacing and accumulated drift still fail.
- Frame matching separately allows bounded NIfTI affine roundoff: exact index correspondence and all eight physical corners must agree, with a 0.001 mm absolute cap. A genuinely shifted or stretched PET grid remains an error.
- Spatial units are converted to millimeters. Unknown units assume millimeters and are recorded as a warning.
- Label descriptions or NIfTI label intent select SEG. Integer values alone do not.
- Multiple reference series require
--series-uid; patient/study mismatches fail. - 4D images retain timepoint identities. Timing comes from explicit NIfTI time units or matching reference frames, never from a made-up acquisition duration. Input time order is preserved; a NIfTI without per-frame provenance cannot reveal whether an upstream tool already scrambled its volumes.
- A complete, consistently reversed native PET slice index can be recovered with an explicit warning when independent frame timing proves the ordering. Mixed/missing indices and contradictory timing still fail; source files are never rewritten. MR exact reorientation preserves volume-uniform acquisition times even when the output slice planes change. Conflicting plane facts are omitted with warnings, not copied onto unrelated planes.
- Reference CT/MR/PT acquisition fields must be available where required by the selected output profile. PET values must already use the reference's units: SUV and activity concentration cannot be distinguished from a NIfTI array alone.
- Classic MR uses a standard-extended rescale mapping; 4D CT uses temporal extensions. The receiving viewer must support these attributes. See the DICOM output profiles and validation evidence.
- Sheared/singular geometry, complex/vector/tensor data, enhanced/multiframe references, temporal RGB, and gated/reprojection PET fail explicitly.
- Scalar encoding uses 16-bit integers plus rescale parameters. Integer values
fitting the selected signed/unsigned representation are exact; other values
are quantized. Maximum reconstruction error is recorded in DICOM
DerivationDescription. PET scales each image independently to preserve low-signal slices/timepoints; consumers must apply each image's own rescale tags. CT/MR retain a shared volume-wide scale. PET signedness stays constant across the series; any negative value selects signed storage, so positive values above 32767 can require quantization. - Volumes are loaded into memory. Very large 4D datasets need adequate RAM.
- SEG omits incomplete optional study codes; PET omits incomplete optional tracer codes within copied isotope metadata. Both use a shared completeness check and record a warning. Complete codes, other isotope fields and source files are preserved; missing codes are never invented.
Validation includes serialized pixel decoding and SEG label/source-frame round trips. This is not a blanket DICOM conformance or viewer-compatibility claim: external IOD validators and target viewers remain part of release acceptance.
See the real-data validation report for fresh LION/MOOSE inference, independent SEG decoding, and known limitations. The public PET audit checks genuine 32- and 45-timepoint acquisitions, spatial/time reversal detection, and all output voxels. Its optional tracer-code defect is now fixed: all 3,126 regenerated files passed current-standard validation. The follow-up report records that evidence and PET precision concerns from volume-wide 16-bit scaling. The precision follow-up compares scaling policies and validates the per-image PET encoding introduced to address them. The baseline Slicer audit exposed receiver defects in default dynamic PET import. The optional Slicer PET integration fixes those paths without changing converter output. The repair acceptance report passes normal installed import for all 51.2 million public PET voxels, both dynamic representations, and binary/multilabel SEG. Stock Slicer without the integration still has the baseline defects; compatibility is limited to the tested installation and profiles. The expanded IDC repair acceptance adds Philips/Siemens static and 45-frame PET, sagittal 25-frame MR, and ten separate CT phases: 291 million source voxels, all 10,910 output files checked with dciodvfy, and strict Slicer checks of both dynamic representations. It records the PET ordering/MR metadata fixes and remaining profile limitations.
Errors and output safety
Expected failures explain what happened and how to resolve it. Use --debug
for tracebacks on unexpected failures, --quiet to suppress normal output, or
--json for structured automation.
An existing output is an error. With --overwrite, old output remains in place
until the new conversion passes validation. A failed restore preserves a backup
and reports its path. Input/output overlap is rejected. Interruptions clean up
staging files; a hard process kill may leave a named lock directory.
Upgrading from earlier versions
Existing convert, segment, rgb and resample subcommands still work.
Python conversion functions and PUMA wrappers delegate to the same pipeline.
Intentional changes:
- Different image/reference dimensions are supported.
- Existing output errors instead of silently skipping.
- Vendor-specific flips are gone; the deprecated vendor argument is ignored.
- SEG requires truthful algorithm provenance, including via the labels JSON.
- Pixel overflow errors or quantitative rescaling replace silent clipping.
- Generated filenames are stable
IM_000001.dcmsequences, not template filenames. - Use
result.filesor*.dcm; the output also includes a JSON report.
Development
pip install -e '.[dev]'
pytest
ruff check nifti2dicom tests integrations/slicer validation
ruff format --check nifti2dicom tests integrations/slicer validation
python -m mypy nifti2dicom --ignore-missing-imports
python -m build
python -m validation.check_distribution dist/*.tar.gz dist/*.whl
Start with architecture and AGENTS.md. Tests use asymmetric synthetic data to verify physical coordinates, quantitative values, temporal frames, segmentation references and failure recovery. Optional reproducible public-data checks are described in validation/README.md; downloads and viewer tests are not dependencies of ordinary unit tests or package installation.
MIT licensed. Authors: Lalith Kumar Shiyam Sundar, Aaron Selfridge and Siqi Li.
Metadata
Release files for nifti2dicom 2.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nifti2dicom-2.1.0.tar.gz | 882.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nifti2dicom-2.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 945.2 kB
Release files / nifti2dicom-2.1.0.tar.gz
| Download URL | nifti2dicom-2.1.0.tar.gz |
|---|---|
| Size | 882.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
43c8414a6982a4f923be1d9be641f3e50dcfe7bce06afcac11f2de2c1570566b
|
|
BLAKE2b-256 checksum How to use checksums |
319f93b3f7114b8b191eb209a1d3edc82e42d8c0ccbaf451d8b7eb03ae9f3d8e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / nifti2dicom-2.1.0-py3-none-any.whl
| Download URL | nifti2dicom-2.1.0-py3-none-any.whl |
|---|---|
| Size | 62.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f896f0b111d26bf6c0195c4e3624666170843821c0490e3550f09873ba1271c2
|
|
BLAKE2b-256 checksum How to use checksums |
e327e70ebb35137f6dd46badbb4d07cb3e7d7f9b95aacccc12eb84fe4e624f2c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|