or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/dcm2niix@1.0.x

docs

examples

edge-cases.mdreal-world-scenarios.md
index.md
tile.json

tessl/pypi-dcm2niix

tessl install tessl/pypi-dcm2niix@1.0.5

Command-line application that converts medical imaging data from DICOM format to NIfTI format with BIDS support

edge-cases.mddocs/examples/

Edge Cases and Troubleshooting

Advanced scenarios, vendor-specific considerations, and solutions to common problems.

Common Issues

No Output Files Generated

Symptoms: dcm2niix runs but no NIfTI files are created.

Diagnosis:

from dcm2niix import main

# Run with verbose output to see what's happening
exit_code = main(["-v", "2", "/input/folder"], capture_output=True, text=True)

if exit_code == 2:
    print("No valid DICOM files found in directory")
elif exit_code == 5:
    print("Input folder is invalid or inaccessible")
elif exit_code == 6:
    print("Output folder cannot be created or is invalid")
elif exit_code == 7:
    print("Output folder is read-only")

Solutions:

from dcm2niix import main
from pathlib import Path

def diagnose_no_output(input_dir, output_dir="/tmp/test_output"):
    """Diagnose why no output is generated."""
    
    input_path = Path(input_dir)
    output_path = Path(output_dir)
    
    # Check 1: Input exists
    if not input_path.exists():
        return f"Input directory does not exist: {input_dir}"
    
    # Check 2: Input contains files
    files = list(input_path.rglob("*"))
    if not files:
        return f"Input directory is empty: {input_dir}"
    
    # Check 3: Output directory writable
    try:
        output_path.mkdir(parents=True, exist_ok=True)
        test_file = output_path / ".test"
        test_file.touch()
        test_file.unlink()
    except PermissionError:
        return f"Output directory not writable: {output_dir}"
    
    # Check 4: Run conversion with verbose output
    exit_code = main(["-v", "2", "-o", str(output_path), str(input_path)])
    
    if exit_code == 2:
        return "No valid DICOM files found (check file extensions and headers)"
    elif exit_code == 0:
        return f"Success! Check output in {output_path}"
    else:
        return f"Conversion failed with exit code {exit_code}"

# Usage
diagnosis = diagnose_no_output("/data/problem/folder")
print(diagnosis)

Incomplete or Corrupted Output

Symptoms: Output files are generated but appear corrupted or incomplete.

Common Causes:

  • Corrupt DICOM files (exit code 4)
  • Incomplete volumes (exit code 10)
  • Interrupted conversion
  • Disk space issues

Diagnosis:

from dcm2niix import main

# Enable maximum verbosity
exit_code = main(["-v", "2", "/input/folder"])

if exit_code == 4:
    print("Corrupt DICOM file detected")
    print("Check verbose output for filename")
elif exit_code == 10:
    print("Incomplete volumes detected (missing slices)")
    print("Some slices may be missing from the series")

Solution - Skip Corrupt Files:

from dcm2niix import main, bin
import subprocess
from pathlib import Path

def find_corrupt_dicoms(dicom_dir):
    """Identify corrupt DICOM files."""
    result = subprocess.run(
        [bin, "-v", "2", dicom_dir],
        capture_output=True,
        text=True
    )
    
    # Parse output for corrupt file mentions
    corrupt_files = []
    for line in result.stderr.split('\n'):
        if 'corrupt' in line.lower() or 'error' in line.lower():
            # Extract filename if present
            corrupt_files.append(line)
    
    return corrupt_files

# Usage
corrupt = find_corrupt_dicoms("/data/problem/folder")
for file_info in corrupt:
    print(f"Corrupt: {file_info}")

Wrong Orientation or Flipped Images

Symptoms: Images appear flipped or rotated incorrectly.

Understanding: dcm2niix automatically converts DICOM LPS (Left-Posterior-Superior) to NIfTI RAS+ (Right-Anterior-Superior) coordinate system.

Verification:

import json
from pathlib import Path

def check_orientation(nifti_file):
    """Check orientation from BIDS JSON sidecar."""
    json_file = Path(nifti_file).with_suffix('.json')
    
    if not json_file.exists():
        return "No JSON sidecar found (run with -b y)"
    
    with open(json_file, 'r') as f:
        metadata = json.load(f)
    
    orientation = metadata.get('ImageOrientationPatientDICOM', 'Not found')
    print(f"Image Orientation: {orientation}")
    
    # Check if non-standard orientation
    if orientation != [1, 0, 0, 0, 1, 0]:
        print("Warning: Non-standard orientation detected")
    
    return orientation

# Usage
check_orientation("/output/T1_MPRAGE.nii.gz")

Solution: Orientation is correct by default. If issues persist, check source DICOM headers.

Multiple Unwanted Output Files

Symptoms: Too many output files, including scouts, localizers, and derived images.

Solution - Filter Output:

from dcm2niix import main

# Ignore derived, localizer, and 2D images
exit_code = main([
    "-i", "y",              # Ignore derived/localizer
    "-z", "y",              # Compress
    "/input/folder"
])

Solution - Select Specific Series:

from dcm2niix import main, bin
import subprocess
import re

def convert_only_primary_sequences(dicom_dir, output_dir):
    """Convert only T1, T2, and FLAIR sequences."""
    
    # Step 1: Get series list
    result = subprocess.run(
        [bin, "-q", "l", dicom_dir],
        capture_output=True,
        text=True
    )
    
    # Step 2: Filter for desired sequences
    desired_crcs = []
    for line in result.stdout.split('\n'):
        match = re.search(r'(.+?) \[(\d+)\]', line)
        if match:
            name = match.group(1)
            crc = match.group(2)
            
            # Check if it's a sequence we want
            if any(seq in name.upper() for seq in ['T1', 'T2', 'FLAIR']):
                desired_crcs.append(crc)
                print(f"Selected: {name} (CRC: {crc})")
    
    # Step 3: Convert selected series
    if desired_crcs:
        args = ["-z", "y", "-b", "y", "-o", output_dir]
        for crc in desired_crcs:
            args.extend(["-n", crc])
        args.append(dicom_dir)
        
        exit_code = main(args)
        return exit_code in [0, 8]
    else:
        print("No matching sequences found")
        return False

# Usage
success = convert_only_primary_sequences("/data/exam", "/data/nifti")

Vendor-Specific Considerations

Siemens

Mosaic Format:

Siemens EPI data is often stored in "mosaic" format (multi-slice image stored as single 2D image). dcm2niix automatically detects and converts this.

from dcm2niix import main

# Mosaic format handled automatically
exit_code = main(["-z", "y", "/siemens/epi/folder"])

CSA Header Information:

Siemens private tags (CSA headers) are automatically extracted to BIDS JSON:

import json

# Check CSA-derived fields in JSON
with open("output.json", 'r') as f:
    metadata = json.load(f)
    
# Siemens-specific fields
coil_name = metadata.get('CoilString', 'Not found')
sequence_name = metadata.get('SequenceName', 'Not found')
print(f"Coil: {coil_name}, Sequence: {sequence_name}")

Diffusion Directions:

# Diffusion gradient directions extracted from CSA header
# Available in BIDS JSON as DiffusionGradientDirection

Philips

Critical: Precise Float Scaling

Philips DICOM includes two scaling factors:

  • Display scaling: For visual appearance
  • Precise scaling (RWV): True quantitative values

Always use -p y for quantitative analysis:

from dcm2niix import main

# REQUIRED for Philips quantitative imaging
exit_code = main([
    "-p", "y",              # Precise float scaling (RWV)
    "-l", "o",              # Preserve original values
    "-z", "y",
    "/philips/dwi/folder"
])

Verification:

import json

def verify_philips_scaling(json_file):
    """Verify Philips scaling parameters."""
    with open(json_file, 'r') as f:
        metadata = json.load(f)
    
    # Check for Philips-specific fields
    rwv_slope = metadata.get('PhilipsRWVSlope')
    rwv_intercept = metadata.get('PhilipsRWVIntercept')
    
    if rwv_slope is not None:
        print(f"Philips RWV Slope: {rwv_slope}")
        print(f"Philips RWV Intercept: {rwv_intercept}")
        print("✓ Precise scaling applied")
    else:
        print("⚠ Philips RWV parameters not found")

# Usage
verify_philips_scaling("/output/DWI.json")

Enhanced DICOM:

Philips enhanced DICOM (multi-frame) is automatically detected and converted.

GE

Diffusion Gradient Cycling:

GE diffusion sequences may use gradient cycling modes. dcm2niix auto-detects, but can be overridden:

# Auto-detect (default)
dcm2niix /ge/dwi/folder

# Override if needed
dcm2niix --diffCyclingModeGE 0 /ge/dwi/folder
from dcm2niix import main

# Override gradient cycling mode
exit_code = main([
    "--diffCyclingModeGE", "0",
    "/ge/dwi/folder"
])

Internal Pulse Sequence Names:

import json

# GE internal sequence names in JSON
with open("output.json", 'r') as f:
    metadata = json.load(f)
    
internal_name = metadata.get('InternalPulseSequenceName', 'Not found')
print(f"GE Internal Sequence: {internal_name}")

Timeout and Resource Management

Long-Running Conversions

from dcm2niix import main
import subprocess

def convert_with_timeout(input_dir, output_dir, timeout_seconds=600):
    """Convert with timeout for large datasets."""
    
    try:
        exit_code = main(
            ["-z", "y", "-o", output_dir, input_dir],
            timeout=timeout_seconds
        )
        return True, exit_code
        
    except subprocess.TimeoutExpired:
        print(f"Conversion timed out after {timeout_seconds} seconds")
        return False, -1

# Usage for very large dataset
success, code = convert_with_timeout("/large/dataset", "/output", timeout_seconds=1800)

Memory Management

from dcm2niix import main
import psutil
import os

def convert_with_memory_monitoring(input_dir, output_dir):
    """Monitor memory usage during conversion."""
    
    process = psutil.Process(os.getpid())
    initial_memory = process.memory_info().rss / 1024 / 1024  # MB
    
    print(f"Initial memory: {initial_memory:.1f} MB")
    
    exit_code = main(["-z", "y", "-o", output_dir, input_dir])
    
    final_memory = process.memory_info().rss / 1024 / 1024  # MB
    memory_used = final_memory - initial_memory
    
    print(f"Final memory: {final_memory:.1f} MB")
    print(f"Memory used: {memory_used:.1f} MB")
    
    return exit_code

# Usage
exit_code = convert_with_memory_monitoring("/data/dicom", "/data/nifti")

Complex Image Types

Multi-Echo Sequences

# Multi-echo automatically separated
# Output: scan_e1.nii, scan_e2.nii, scan_e3.nii

Verification:

# Check for echo-separated files
ls /output/*_e*.nii.gz

Complex-Valued Images

# Complex images automatically separated
# Output: scan_real.nii, scan_imaginary.nii, scan_ph.nii

Multi-Coil Data

# Uncombined coil data automatically separated
# Output: scan_c1.nii, scan_c2.nii, ..., scan_c32.nii

BIDS Compliance Issues

Anonymization Verification

import json

def verify_anonymization(json_file):
    """Verify patient info was anonymized."""
    with open(json_file, 'r') as f:
        metadata = json.load(f)
    
    # Check for PHI fields
    phi_fields = [
        'PatientName',
        'PatientID',
        'PatientBirthDate',
        'StudyInstanceUID',
        'SeriesInstanceUID'
    ]
    
    found_phi = []
    for field in phi_fields:
        if field in metadata:
            found_phi.append(field)
    
    if found_phi:
        print(f"⚠ PHI fields found: {', '.join(found_phi)}")
        print("Use -ba y to anonymize")
        return False
    else:
        print("✓ No PHI fields found")
        return True

# Usage
verify_anonymization("/output/scan.json")

HIPAA Compliance

from dcm2niix import main

# HIPAA Safe Harbor method requires removing 18 identifiers
# dcm2niix -ba y removes direct identifiers but may not be sufficient
# for full HIPAA compliance

def hipaa_compliant_conversion(input_dir, output_dir):
    """
    Convert with maximum anonymization.
    
    Note: This removes direct identifiers but does not guarantee
    full HIPAA compliance. Consult with compliance officer.
    """
    exit_code = main([
        "-b", "y",              # Generate JSON
        "-ba", "y",             # Anonymize
        "-c", "",               # Clear comment field
        "-f", "scan_%s",        # Generic filename (no patient info)
        "-o", output_dir,
        input_dir
    ])
    
    return exit_code in [0, 8]

# Usage
success = hipaa_compliant_conversion("/phi/data", "/deidentified/data")

Compression Issues

pigz Not Found

Symptom: -z y falls back to internal compression or fails.

Solution:

# Install pigz
# macOS
brew install pigz

# Ubuntu/Debian
sudo apt-get install pigz

# CentOS/RHEL
sudo yum install pigz

Verify:

which pigz
pigz --version

Fallback:

from dcm2niix import main
import shutil

def convert_with_compression_fallback(input_dir, output_dir):
    """Use pigz if available, otherwise internal compression."""
    
    if shutil.which("pigz"):
        print("Using pigz for compression")
        compression = "y"
    else:
        print("pigz not found, using internal compression")
        compression = "i"
    
    exit_code = main([
        "-z", compression,
        "-o", output_dir,
        input_dir
    ])
    
    return exit_code in [0, 8]

# Usage
success = convert_with_compression_fallback("/data/dicom", "/data/nifti")

Transfer Syntax Issues

Unsupported Compression

Symptom: Error about unsupported transfer syntax.

Diagnosis:

# Check dcm2niix capabilities
dcm2niix --version
# Look for: JPEGLS, JP2:OpenJPEG, etc.

Common unsupported formats:

  • JPEG-LS (requires CharLS library)
  • JPEG2000 (requires OpenJPEG or Jasper)

Solution: Request uncompressed or RLE transfer syntax from PACS, or build dcm2niix from source with optional libraries.

Debugging Workflow

Complete Diagnostic Script

from dcm2niix import main, bin, __version__
from pathlib import Path
import subprocess
import json

def full_diagnostic(input_dir, output_dir="/tmp/dcm2niix_test"):
    """Complete diagnostic for troubleshooting."""
    
    print("=== dcm2niix Diagnostic ===\n")
    
    # 1. Version info
    print(f"1. Version: {__version__}")
    result = subprocess.run([bin, "--version"], capture_output=True, text=True)
    print(f"   Binary: {result.stdout.strip()}\n")
    
    # 2. Input validation
    input_path = Path(input_dir)
    print(f"2. Input: {input_dir}")
    print(f"   Exists: {input_path.exists()}")
    if input_path.exists():
        files = list(input_path.rglob("*"))
        print(f"   Files: {len(files)}\n")
    else:
        print("   ERROR: Input does not exist\n")
        return
    
    # 3. Output validation
    output_path = Path(output_dir)
    print(f"3. Output: {output_dir}")
    try:
        output_path.mkdir(parents=True, exist_ok=True)
        print(f"   Writable: True\n")
    except Exception as e:
        print(f"   ERROR: {e}\n")
        return
    
    # 4. Test conversion
    print("4. Test conversion (verbose):")
    exit_code = main([
        "-v", "2",
        "-z", "y",
        "-b", "y",
        "-o", str(output_path),
        str(input_path)
    ])
    print(f"   Exit code: {exit_code}")
    
    # 5. Check output
    output_files = list(output_path.glob("*"))
    print(f"   Output files: {len(output_files)}")
    for f in output_files[:5]:  # Show first 5
        print(f"     - {f.name}")
    
    # 6. Interpretation
    print("\n5. Interpretation:")
    if exit_code == 0:
        print("   ✓ SUCCESS")
    elif exit_code == 2:
        print("   ✗ No valid DICOM files found")
    elif exit_code == 4:
        print("   ✗ Corrupt DICOM file detected")
    elif exit_code == 8:
        print("   ⚠ Partial success")
    else:
        print(f"   ✗ Failed with exit code {exit_code}")

# Usage
full_diagnostic("/data/problem/folder")

Next Steps

  • Real-World Scenarios
  • Python API Reference
  • CLI Options Reference
  • Supported Formats Reference