DICOM Basics using .NET and C# - Radiation Therapy (RT) Objects

Introduction

This is part of my series of articles on the DICOM standard. In this tutorial, we'll explore DICOM Radiation Therapy (RT) objects, which are specialized DICOM Information Object Definitions used in radiation oncology for treatment planning and delivery.

RT objects work together to capture the complete treatment planning workflow, from anatomical contours to beam parameters to calculated dose distributions. Understanding these objects is essential for anyone working with radiation oncology systems.

Prerequisites

Before you begin, ensure you have the following:

  • A .NET development environment (Visual Studio or Visual Studio Code)
  • The Fellow Oak DICOM library (fo-dicom) installed via NuGet
  • Basic understanding of DICOM concepts from previous tutorials
  • You can find all the code demonstrated in this tutorial on GitHub here

“Prevention is better than cure, but precision is essential in cure.” ~ Unknown

RT SOP Classes Overview

DICOM defines several RT-specific SOP Classes:

SOP ClassUIDPurpose
RT Structure Set1.2.840.10008.5.1.4.1.1.481.3Anatomical contours (ROIs)
RT Plan1.2.840.10008.5.1.4.1.1.481.5Treatment beam parameters
RT Dose1.2.840.10008.5.1.4.1.1.481.23D dose distribution
RT Image1.2.840.10008.5.1.4.1.1.481.1Portal images, DRRs
RT Beams Treatment Record1.2.840.10008.5.1.4.1.1.481.4Delivered treatment

RT Structure Set

The RT Structure Set contains anatomical contours (ROIs) defined on CT images:

using System;
using System.Diagnostics;
using FellowOakDicom;

namespace DicomRTObjects
{
    public class Program
    {
        public static void Main(string[] args)
        {
            LogToDebugConsole("=== RT Structure Set ===");
            LogToDebugConsole($"SOP Class: {DicomUID.RTStructureSetStorage.UID}");
            LogToDebugConsole("");

            LogToDebugConsole("Key Sequences:");
            LogToDebugConsole("");

            LogToDebugConsole("Structure Set ROI Sequence (3006,0020):");
            LogToDebugConsole("  Item 1:");
            LogToDebugConsole("    ROI Number: 1");
            LogToDebugConsole("    ROI Name: PTV (Planning Target Volume)");
            LogToDebugConsole("    ROI Generation Algorithm: MANUAL");
            LogToDebugConsole("  Item 2:");
            LogToDebugConsole("    ROI Number: 2");
            LogToDebugConsole("    ROI Name: Spinal Cord");
            LogToDebugConsole("    ROI Generation Algorithm: MANUAL");
            LogToDebugConsole("  Item 3:");
            LogToDebugConsole("    ROI Number: 3");
            LogToDebugConsole("    ROI Name: Left Lung");
            LogToDebugConsole("    ROI Generation Algorithm: AUTOMATIC");
            LogToDebugConsole("");

            LogToDebugConsole("ROI Contour Sequence (3006,0039):");
            LogToDebugConsole("  Item 1 (ROI 1 - PTV):");
            LogToDebugConsole("    Contour Sequence:");
            LogToDebugConsole("      - Slice 1: 45 points, type CLOSED_PLANAR");
            LogToDebugConsole("      - Slice 2: 52 points, type CLOSED_PLANAR");
            LogToDebugConsole("      - (contour for each CT slice)");
            LogToDebugConsole("    ROI Display Color: 255\\0\\0 (Red)");
        }

        private static void LogToDebugConsole(string message)
        {
            Debug.WriteLine(message);
        }
    }
}

Key RT Structure Set Tags

TagNameDescription
(3006,0020)Structure Set ROI SequenceROI definitions
(3006,0022)ROI NumberUnique ROI identifier
(3006,0026)ROI NameHuman-readable name
(3006,0039)ROI Contour SequenceContour data
(3006,0040)Contour SequencePoints per slice
(3006,0080)RT ROI Observations SequenceROI interpretations

RT Plan

The RT Plan contains treatment beam parameters:

private static void DemonstrateRTPlan()
{
    LogToDebugConsole("=== RT Plan ===");
    LogToDebugConsole($"SOP Class: {DicomUID.RTPlanStorage.UID}");
    LogToDebugConsole("");

    LogToDebugConsole("Plan Information:");
    LogToDebugConsole("  RT Plan Label: LUNG_SBRT_5FX");
    LogToDebugConsole("  RT Plan Date: 20240115");
    LogToDebugConsole("  Plan Intent: CURATIVE");
    LogToDebugConsole("");

    LogToDebugConsole("Fraction Group Sequence (300A,0070):");
    LogToDebugConsole("  Fraction Group Number: 1");
    LogToDebugConsole("  Number of Fractions Planned: 5");
    LogToDebugConsole("  Number of Beams: 7");
    LogToDebugConsole("");

    LogToDebugConsole("Beam Sequence (300A,00B0):");
    LogToDebugConsole("  Beam 1:");
    LogToDebugConsole("    Beam Number: 1");
    LogToDebugConsole("    Beam Name: AP");
    LogToDebugConsole("    Beam Type: STATIC");
    LogToDebugConsole("    Radiation Type: PHOTON");
    LogToDebugConsole("    Nominal Beam Energy: 6 MV");
    LogToDebugConsole("    Gantry Angle: 0.0");
    LogToDebugConsole("    Collimator Angle: 0.0");
    LogToDebugConsole("    Couch Angle: 0.0");
    LogToDebugConsole("  Beam 2:");
    LogToDebugConsole("    Beam Number: 2");
    LogToDebugConsole("    Beam Name: LAO_45");
    LogToDebugConsole("    Gantry Angle: 45.0");
}

Key RT Plan Tags

TagNameDescription
(300A,0002)RT Plan LabelPlan identifier
(300A,000A)Plan IntentCURATIVE, PALLIATIVE, etc.
(300A,0070)Fraction Group SequenceFractionation scheme
(300A,0078)Number of Fractions PlannedTotal fractions
(300A,00B0)Beam SequenceBeam definitions
(300C,0060)Referenced Structure Set SequenceLink to RT SS

RT Dose

The RT Dose contains the 3D dose distribution:

private static void DemonstrateRTDose()
{
    LogToDebugConsole("=== RT Dose ===");
    LogToDebugConsole($"SOP Class: {DicomUID.RTDoseStorage.UID}");
    LogToDebugConsole("");

    LogToDebugConsole("Dose Information:");
    LogToDebugConsole("  Dose Units: GY");
    LogToDebugConsole("  Dose Type: PHYSICAL");
    LogToDebugConsole("  Dose Summation Type: PLAN");
    LogToDebugConsole("  Dose Grid Scaling: 0.0001");
    LogToDebugConsole("");

    LogToDebugConsole("Dose Grid:");
    LogToDebugConsole("  Rows: 256");
    LogToDebugConsole("  Columns: 256");
    LogToDebugConsole("  Number of Frames: 80");
    LogToDebugConsole("  Pixel Spacing: 2.0\\2.0 mm");
    LogToDebugConsole("");

    LogToDebugConsole("DVH Sequence (3004,0050):");
    LogToDebugConsole("  DVH 1 (PTV):");
    LogToDebugConsole("    DVH Type: CUMULATIVE");
    LogToDebugConsole("    Dose Units: GY");
    LogToDebugConsole("    DVH Volume Units: CM3");
    LogToDebugConsole("  DVH 2 (Spinal Cord):");
    LogToDebugConsole("    DVH Max Dose: 8.5 Gy");
    LogToDebugConsole("    DVH Mean Dose: 2.3 Gy");
}

Key RT Dose Tags

TagNameDescription
(3004,0002)Dose UnitsGY (Gray) or RELATIVE
(3004,0004)Dose TypePHYSICAL, EFFECTIVE, ERROR
(3004,000A)Dose Summation TypePLAN, BEAM, FRACTION
(3004,000E)Dose Grid ScalingScale factor for pixel values
(3004,0050)DVH SequenceDose-Volume Histogram data

RT Workflow

The typical radiation therapy workflow:

1. Imaging (CT Simulation)
   - Patient positioned and CT scan acquired
   - CT images sent to Treatment Planning System (TPS)
   - Output: CT Image Series

2. Contouring
   - Physician/dosimetrist draws ROIs
   - Targets: GTV, CTV, PTV
   - OARs: Spinal cord, lungs, heart
   - Output: RT Structure Set

3. Treatment Planning
   - Dosimetrist designs beam arrangement
   - Optimizer calculates MLC positions
   - Dose calculated on CT grid
   - Output: RT Plan + RT Dose

4. Plan Approval
   - Physician reviews plan and DVH
   - Plan approved for treatment

5. Treatment Delivery
   - Plan sent to treatment machine
   - Treatment delivered per fraction
   - Output: RT Beams Treatment Record

Reading RT Objects

Here's how to read key information from RT objects:

public static void ReadRTStructureSet(string filePath)
{
    var file = DicomFile.Open(filePath);
    var dataset = file.Dataset;

    // Verify SOP Class
    var sopClass = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, "");
    if (sopClass != DicomUID.RTStructureSetStorage.UID)
    {
        LogToDebugConsole("Not an RT Structure Set");
        return;
    }

    // Read Structure Set ROI Sequence
    var roiSeq = dataset.GetSequence(DicomTag.StructureSetROISequence);
    if (roiSeq != null)
    {
        foreach (var item in roiSeq.Items)
        {
            var roiNumber = item.GetSingleValueOrDefault(DicomTag.ROINumber, "");
            var roiName = item.GetSingleValueOrDefault(DicomTag.ROIName, "");
            LogToDebugConsole($"ROI {roiNumber}: {roiName}");
        }
    }
}

public static void ReadRTPlan(string filePath)
{
    var file = DicomFile.Open(filePath);
    var dataset = file.Dataset;

    var planLabel = dataset.GetSingleValueOrDefault(DicomTag.RTPlanLabel, "");
    LogToDebugConsole($"Plan Label: {planLabel}");

    // Read Beam Sequence
    var beamSeq = dataset.GetSequence(DicomTag.BeamSequence);
    if (beamSeq != null)
    {
        foreach (var beam in beamSeq.Items)
        {
            var beamNumber = beam.GetSingleValueOrDefault(DicomTag.BeamNumber, "");
            var beamName = beam.GetSingleValueOrDefault(DicomTag.BeamName, "");
            LogToDebugConsole($"Beam {beamNumber}: {beamName}");
        }
    }
}

Common ROI Types in RT Structure Set

ROI TypeDescription
GTVGross Tumor Volume - visible tumor
CTVClinical Target Volume - microscopic spread
PTVPlanning Target Volume - setup margin
OAROrgan At Risk - normal tissue to protect
EXTERNALPatient external contour
SUPPORTPatient positioning devices

Best Practices

  • Maintain references: RT Plan must reference the correct Structure Set
  • Verify UIDs: Always check Referenced SOP Instance UIDs match
  • Handle coordinate systems: Understand patient coordinate systems
  • Validate contours: Check contour data is on valid CT slices
  • Test interoperability: Verify data transfers between TPS and linac

Conclusion

DICOM RT objects form the backbone of modern radiation therapy workflows. Understanding the relationships between RT Structure Set, RT Plan, and RT Dose is essential for anyone working with radiation oncology systems.

These objects are typically created by Treatment Planning Systems and consumed by treatment delivery systems. While the structures are complex, fo-dicom provides the tools to read and navigate RT DICOM data for integration and analysis purposes.

This concludes the DICOM .NET programming series. I hope these tutorials have given you a comprehensive understanding of working with DICOM in .NET applications. From basic file parsing to advanced networking operations, you now have the foundation to build robust medical imaging applications. For more information on DICOM, please refer to my series of articles on the DICOM standard.