Skip to content

Quickstart

Zvi Baratz edited this page Oct 29, 2025 · 1 revision

Quickstart: CSA Headers in 5 Minutes

Audience: Beginners

Estimated time: 5 minutes

Prerequisites: Basic Python knowledge, DICOM files from Siemens scanner


What You'll Learn

Extract slice timing information from a Siemens fMRI DICOM file in just a few lines of code. This is essential for fMRI preprocessing with tools like SPM, FSL, or AFNI.


Installation

pip install csa_header pydicom

The Complete Example

import pydicom
from csa_header import CsaHeader

# 1. Load your Siemens DICOM file
dcm = pydicom.dcmread('your_fmri_scan.dcm')

# 2. Extract CSA Series Header Info tag (0x0029, 0x1010)
series_header_bytes = dcm[0x0029, 0x1010].value

# 3. Parse the CSA header binary format
csa = CsaHeader(series_header_bytes)
series_info = csa.read()

# 4. Get slice timing values
slice_times = series_info['MosaicRefAcqTimes']['value']
n_slices = series_info['NumberOfImagesInMosaic']['value']

print(f"Number of slices: {n_slices}")
print(f"Slice timing (ms): {slice_times}")

Expected Output

Number of slices: 36
Slice timing (ms): [0.0, 52.5, 105.0, 157.5, 210.0, 262.5, 315.0, ...]

What Just Happened?

flowchart LR
    A[DICOM File] -->|pydicom.dcmread| B[DICOM Dataset]
    B -->|Extract tag 0x0029,0x1010| C[Raw Bytes]
    C -->|CsaHeader| D[Parser Object]
    D -->|.read| E[Python Dictionary]
    E -->|Access key| F[Slice Times]

    style A fill:#e1f5ff
    style C fill:#fff4e1
    style E fill:#e8f5e9
    style F fill:#c8e6c9
Loading

Step-by-step:

  1. Line 5: Load DICOM file using pydicom
  2. Line 8: Extract raw bytes from CSA tag (0x0029, 0x1010) - this contains series-level metadata
  3. Line 11-12: Parse the binary CSA format into a Python dictionary with tag names as keys
  4. Line 15-16: Access specific parameters by tag name

Try It Yourself

Using the Example DICOM Files

If you don't have a Siemens DICOM file handy, you can test with sample data:

# Download a sample from the repository examples
# Or use your own Siemens fMRI DICOM file

import pydicom
from csa_header import CsaHeader

dcm = pydicom.dcmread('path/to/your/siemens_dicom.dcm')

# Verify it's a Siemens file
if hasattr(dcm, 'Manufacturer'):
    print(f"Manufacturer: {dcm.Manufacturer}")
    if 'SIEMENS' not in dcm.Manufacturer.upper():
        print("⚠️  Warning: Not a Siemens file!")
else:
    print("⚠️  No manufacturer information found")

# Check if CSA headers are present
if (0x0029, 0x1010) in dcm:
    print("✅ CSA Series Header found")
    series_csa = CsaHeader(dcm[0x0029, 0x1010].value).read()
    print(f"Available tags: {len(series_csa)}")
else:
    print("❌ No CSA Series Header found")

if (0x0029, 0x1020) in dcm:
    print("✅ CSA Image Header found")
    image_csa = CsaHeader(dcm[0x0029, 0x1020].value).read()
    print(f"Available tags: {len(image_csa)}")
else:
    print("❌ No CSA Image Header found")

Common Use Cases

Tip

Choose the right CSA header for your needs:

  • Series Header (0x0029, 0x1010): Slice timing, protocol parameters, mosaic info
  • Image Header (0x0029, 0x1020): B-values, gradients, slice position

Extract B-values for Diffusion Imaging

# For DWI/DTI: Use Image Header (0x0029, 0x1020)
image_header_bytes = dcm[0x0029, 0x1020].value
image_info = CsaHeader(image_header_bytes).read()

# Get diffusion parameters
b_value = image_info['B_value']['value']
gradient = image_info['DiffusionGradientDirection']['value']

print(f"B-value: {b_value}")
print(f"Gradient direction: {gradient}")

Expected output:

B-value: 1000
Gradient direction: [0.707, 0.707, 0.0]

Extract TR and TE from Protocol

# Access ASCCONV protocol parameters
series_info = CsaHeader(dcm[0x0029, 0x1010].value).read()

# The protocol is automatically parsed into a nested dictionary
protocol = series_info.get('MrPhoenixProtocol', {}).get('value', {})

if protocol:
    tr = protocol.get('alTR', [None])[0]  # Repetition time in ms
    te = protocol.get('alTE', [None])[0]  # Echo time in ms

    print(f"TR: {tr} ms")
    print(f"TE: {te} ms")

Expected output:

TR: 2000 ms
TE: 30 ms

Common Questions

Q: What if I get KeyError: 'MosaicRefAcqTimes'?

A: Not all sequences include slice timing. This is normal for:

  • Single-slice acquisitions
  • Non-EPI sequences
  • Some older scanner software versions

See Troubleshooting#missing-slice-timing for solutions.

Q: Can I use this with non-Siemens data?

A: No. CSA headers are Siemens-specific proprietary structures. Other manufacturers use different formats:

  • GE: Use pydicom to access standard DICOM tags
  • Philips: Look for private tags in (2001, xxxx) range
  • Canon/Toshiba: Limited private tag documentation

Q: Do I need NiBabel?

A: No, but it's helpful! csa_header only requires pydicom and numpy. NiBabel is useful for:

  • Loading DICOM images as numpy arrays
  • Converting to NIfTI format
  • Advanced neuroimaging workflows

See Integration with NiBabel for combined workflows.

Q: What's the difference between the two CSA headers?

A: Siemens DICOM files contain two CSA headers:

Header Tag Contains Use For
Series Header (0x0029, 0x1010) Series-level info Slice timing, protocol, mosaic info
Image Header (0x0029, 0x1020) Image-level info B-values, gradients, slice position

Q: How do I know which CSA format version I have?

A: Don't worry! The CsaHeader class automatically detects the format:

  • CSA2 (modern): Starts with 'SV10' identifier
  • CSA1 (legacy): No identifier

Both are parsed identically - you don't need to do anything different.


What's Next?

Now that you've extracted basic CSA information, explore more advanced topics:

📚 Learn More

🔧 Practical Guides

❓ Need Help?


Code Template

Here's a complete template you can copy and adapt:

#!/usr/bin/env python3
"""
Extract CSA header information from Siemens DICOM files.
"""

import sys
import pydicom
from csa_header import CsaHeader, CsaReadError


def extract_csa_info(dicom_path: str) -> dict:
    """
    Extract CSA header information from a Siemens DICOM file.

    Parameters
    ----------
    dicom_path : str
        Path to Siemens DICOM file

    Returns
    -------
    dict
        Dictionary containing extracted CSA information
    """
    # Load DICOM file
    dcm = pydicom.dcmread(dicom_path)

    results = {}

    # Check manufacturer
    if not hasattr(dcm, 'Manufacturer'):
        raise ValueError("No manufacturer information found")

    if 'SIEMENS' not in dcm.Manufacturer.upper():
        raise ValueError(f"Not a Siemens file (manufacturer: {dcm.Manufacturer})")

    # Extract Series Header (0x0029, 0x1010)
    if (0x0029, 0x1010) in dcm:
        try:
            series_data = dcm[0x0029, 0x1010].value
            series_info = CsaHeader(series_data).read()

            # Extract slice timing if available
            if 'MosaicRefAcqTimes' in series_info:
                results['slice_times'] = series_info['MosaicRefAcqTimes']['value']

            if 'NumberOfImagesInMosaic' in series_info:
                results['n_slices'] = series_info['NumberOfImagesInMosaic']['value']

            # Extract protocol parameters
            if 'MrPhoenixProtocol' in series_info:
                protocol = series_info['MrPhoenixProtocol']['value']
                if isinstance(protocol, dict):
                    results['TR'] = protocol.get('alTR', [None])[0]
                    results['TE'] = protocol.get('alTE', [None])[0]

        except CsaReadError as e:
            print(f"Error parsing Series Header: {e}", file=sys.stderr)

    # Extract Image Header (0x0029, 0x1020)
    if (0x0029, 0x1020) in dcm:
        try:
            image_data = dcm[0x0029, 0x1020].value
            image_info = CsaHeader(image_data).read()

            # Extract diffusion parameters if available
            if 'B_value' in image_info:
                results['b_value'] = image_info['B_value']['value']

            if 'DiffusionGradientDirection' in image_info:
                results['gradient'] = image_info['DiffusionGradientDirection']['value']

        except CsaReadError as e:
            print(f"Error parsing Image Header: {e}", file=sys.stderr)

    return results


if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: python script.py <path_to_dicom>")
        sys.exit(1)

    try:
        info = extract_csa_info(sys.argv[1])

        print("\n=== CSA Header Information ===")
        for key, value in info.items():
            if isinstance(value, list) and len(value) > 5:
                print(f"{key}: [{value[0]}, {value[1]}, ..., {value[-1]}] ({len(value)} values)")
            else:
                print(f"{key}: {value}")

    except Exception as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)

Save this as extract_csa.py and run:

python extract_csa.py your_dicom_file.dcm

Ready for more? Continue to the User Guide for comprehensive examples and workflows.

Clone this wiki locally