--- name: dicom-processing description: DICOM and NIfTI medical image processing pipeline. Triggers on DICOM, NIfTI, dcm2niix, de-identification, pydicom, DICOM header, conversion, anonymization, BIDS, DICOM tags, medical image format conversion, "DICOM to NIfTI", "burned-in PHI", "SeriesInstanceUID", "nibabel", "DICOM anonymization". usage: Invoke when converting, parsing, or de-identifying DICOM/NIfTI medical images, or organizing data into BIDS format. version: 1.0.0 validated_against: date: 2025-01-15 packages: {pydicom: "2.4", dcm2niix: "1.0.20240202", nibabel: "5.2"} tags: [skill, category:pipeline, medical-imaging, dicom, nifti, de-identification, hcls] --- # DICOM Processing ## Overview Pipeline skill for converting, reading, and de-identifying medical images in DICOM and NIfTI formats. Encodes deterministic commands and code for: - DICOM → NIfTI conversion with `dcm2niix` (including BIDS sidecars) - Reading DICOM headers and pixel data with `pydicom` - PHI removal (de-identification) with `pydicom` or CTP DicomAnonymizer - NIfTI inspection with `nibabel` - Batch processing grouped by `SeriesInstanceUID` - BIDS-compliant directory organization Use this skill whenever the user works with `.dcm`, `.nii`, `.nii.gz`, DICOM directories, or anonymization workflows. ## Usage ### 1. Convert a DICOM series to NIfTI (dcm2niix) ```bash # Standard conversion: gzip output, BIDS sidecar, pattern-named files dcm2niix -z y -b y -f %p_%s -o output_dir input_dir ``` Flags (memorize): | Flag | Meaning | | --- | --- | | `-z y` | gzip output (`.nii.gz`) | | `-b y` | emit BIDS JSON sidecar | | `-f %p_%s` | filename pattern: `%p` = protocol, `%s` = series number | | `-m n` | do NOT merge 2D slices across series (prevents cross-series mixing) | | `-o` | output directory | Common naming tokens: `%p` protocol, `%s` series, `%t` time, `%i` patient ID, `%n` patient name, `%d` description. ### 2. Read a DICOM file with pydicom ```python import pydicom ds = pydicom.dcmread('file.dcm') print(ds.PatientName, ds.StudyDate, ds.Modality) print(ds.pixel_array.shape) # numpy array of pixel data ``` ### 3. De-identify a DICOM file (pydicom) ```python import pydicom from pydicom.uid import generate_uid ds = pydicom.dcmread('file.dcm') tags_to_remove = [ 'PatientName', 'PatientID', 'PatientBirthDate', 'InstitutionName', 'ReferringPhysicianName', 'StudyDate', ] for tag in tags_to_remove: if tag in ds: del ds[tag] ds.PatientID = 'ANON_001' # Remove private (vendor) tags — they frequently contain PHI ds.remove_private_tags() # Replace UIDs so the study/series/instance cannot be linked back ds.StudyInstanceUID = generate_uid() ds.SeriesInstanceUID = generate_uid() ds.SOPInstanceUID = generate_uid() ds.save_as('anon.dcm') ``` ### 4. Batch de-identification with CTP DicomAnonymizer ```bash # RSNA CTP / DicomBrowser DAP (DICOM Anonymizer Program) java -jar DAP.jar -p profile.script -i input/ -o output/ ``` Use this for large cohorts or when a validated DICOM Supplement 142 profile is required. ### 5. Load a NIfTI volume with nibabel ```python import nibabel as nib img = nib.load('file.nii.gz') data = img.get_fdata() # numpy ndarray affine = img.affine # 4x4 voxel-to-world transform header = img.header ``` ### 6. Batch pattern: group by SeriesInstanceUID, convert per series ```python from pathlib import Path from collections import defaultdict import pydicom, subprocess def group_by_series(input_dir): series = defaultdict(list) for p in Path(input_dir).rglob('*.dcm'): ds = pydicom.dcmread(p, stop_before_pixels=True) series[ds.SeriesInstanceUID].append(p) return series def convert_all(input_dir, output_dir): Path(output_dir).mkdir(parents=True, exist_ok=True) # dcm2niix handles series grouping internally when given a directory subprocess.run( ['dcm2niix', '-z', 'y', '-b', 'y', '-f', '%p_%s', '-o', output_dir, input_dir], check=True, ) ``` ### 7. BIDS organization ``` dataset/ ├── dataset_description.json ├── participants.tsv └── sub-01/ └── ses-01/ ├── anat/ sub-01_ses-01_T1w.nii.gz ├── func/ sub-01_ses-01_task-rest_bold.nii.gz └── dwi/ sub-01_ses-01_dwi.nii.gz ``` Rule: `sub-