DICOM → NIfTI, BIDS & DWI¶
Turn a DICOM series into the files a neuroimaging or ML pipeline expects: a NIfTI volume, a BIDS JSON sidecar, and — for diffusion — an FSL gradient table.
A series → NIfTI¶
import pydcm
vol = pydcm.load_series("ct_series/")
vol.pixels # [depth, rows, cols] float32 HU, spatially sorted
vol.spacing # (z, y, x) mm
vol.affine # 4×4 voxel→world
path = vol.to_nifti("ct.nii.gz")
The affine is validated including the gantry-tilt case (the column-2 increment follows the true Image-Position-Patient step, so tilted CT does not drift). Non-tilted series are bit-identical to the straightforward construction.
BIDS sidecar¶
meta = pydcm.bids_sidecar("ep2d_diff/0001.dcm") # dict from one instance, standard BIDS fields
meta["PhaseEncodingDirection"] # e.g. "j-"
meta["SliceTiming"] # per-slice acquisition times
meta["EffectiveEchoSpacing"], meta["TotalReadoutTime"]
Write it next to the NIfTI yourself:
Diffusion (DWI) → FSL .bval / .bvec¶
import glob
files = glob.glob("ep2d_diff/*.dcm")
bvals, bvecs = pydcm.diffusion_table(files, output_prefix="dwi") # writes dwi.bval / dwi.bvec
rotate=True(default) puts each gradient against the image axes, using the same native conversionload_dwiandsave_dwiuse. It is not a blanket rotation: vendors that store against the patient frame get projected ontoImageOrientationPatient, while UIH already stores against the image axes and is left alone. Passrotate=Falsefor the vector exactly as stored.- The bvecs pair with pixel data in DICOM row order — what
load_dwireturns and whatto_nifti/save_dwiwrite, since the NIfTI writer copies rows verbatim and expresses LPS→RAS in the affine alone. Pairing them with a NIfTI written rows-bottom-up (what common DICOM→NIfTI converters produce) mirrors every tensor about the row axis while leaving FA, MD and every other invariant identical, so it will not announce itself. For that case takegradient_fslfrompydcm._core.read_diffusioninstead. - Vendor coverage is built in: Siemens CSA + mosaic, enhanced multi-frame, and the GE / Philips / UIH private encodings.
To get the 4-D diffusion volume and the table together — load_dwi returns a
4-tuple:
Or write the NIfTI and the FSL table in one call (returns the three paths):
The output feeds FSL, MRtrix or dipy directly — pydcm produces the gradient table; the downstream analysis stays in those tools.
The streamlines those tools reconstruct come back too: write_mktract writes
FSL / MRtrix / dipy tractography into a DICOM Tractography Results object, and
read_tract reads it back to NumPy — see surface & tractography
export, and diffusion tensors (DTI) for the
maps that seed the tracking.