Understanding these fundamental concepts is essential for working effectively with DICOM data.
Attribute Types
DICOM defines three attribute types that indicate requirements for data elements. Understanding these types is critical when creating or validating DICOM files, as violations can cause interoperability failures or rejected storage requests. Type designations determine whether attributes must be present and whether they must contain values.
Type 1 (Required and Must Have Value)
Type 1 attributes are mandatory and must contain meaningful data. Omitting these attributes or leaving them empty will cause validation failures and rejection by most DICOM systems. Common Type 1 attributes include UIDs and modality identifiers.
// Must be present, must have a valid value
// Cannot be empty - will cause validation errors
dicomImage.Attributes["0020000D"].Value = DicomUID.Generate(); // Study Instance UID
dicomImage.Attributes["00080060"].Value = "CT"; // Modality
// Wrong: Empty Type 1 attribute
// dicomImage.Attributes["0020000D"].Value = ""; // ERROR!
Type 2 (Required But May Be Empty)
Type 2 attributes must be present in the dataset, but their values may be empty when the information is unknown or unavailable. This allows systems to acknowledge that a field exists while indicating that no data is available. Use empty strings rather than omitting these attributes entirely.
// Must be present, but value can be empty if unknown
// Use empty string when information is unavailable
dicomImage.Attributes["00100010"].Value = ""; // Patient Name - can be empty
dicomImage.Attributes["00080020"].Value = ""; // Study Date - can be empty
// The attribute MUST exist, but value is optional
Type 3 (Optional)
Type 3 attributes are completely optional and may be omitted from the dataset entirely. Only include these attributes when you have valid information to store. Adding Type 3 attributes with empty values provides no benefit and unnecessarily increases file size.
// May or may not be present
// Only add if you have the information
if (!string.IsNullOrEmpty(patientBirthDate))
{
dicomImage.Attributes["00100030"].Value = patientBirthDate; // Patient Birth Date
}
// No error if attribute is completely absent
Type Summary
| Type | Present | Value | Example Tags |
|---|---|---|---|
| Type 1 | Required | Required | Study Instance UID, Modality |
| Type 2 | Required | May be empty | Patient Name, Study Date |
| Type 3 | Optional | Optional | Patient Birth Date, Study Description |
Information Object Definitions (IODs)
IODs define the structure and content of DICOM objects. They specify what information must or may be present.
IOD Module Structure
SOP Classes (Service-Object Pair Classes)
SOP Classes combine an IOD with a set of DIMSE services. They define what you can DO with an object. When establishing DICOM network connections, you must negotiate which SOP Classes both parties support. Knowing the correct SOP Class UID for each modality type is essential for storage, query, and retrieval operations.
Common SOP Classes
The following constants define frequently used SOP Class UIDs. Store these in a centralized location in your codebase to ensure consistency and ease maintenance when adding support for new modalities.
public static class SOPClasses
{
// Storage SOP Classes
public const string CTImageStorage = "1.2.840.10008.5.1.4.1.1.2";
public const string MRImageStorage = "1.2.840.10008.5.1.4.1.1.4";
public const string SecondaryCaptureStorage = "1.2.840.10008.5.1.4.1.1.7";
public const string UltrasoundImageStorage = "1.2.840.10008.5.1.4.1.1.6.1";
// Query/Retrieve SOP Classes
public const string StudyRootQueryRetrieve = "1.2.840.10008.5.1.4.1.2.2.1";
public const string PatientRootQueryRetrieve = "1.2.840.10008.5.1.4.1.2.1.1";
// Worklist SOP Class
public const string ModalityWorklistFind = "1.2.840.10008.5.1.4.31";
// Verification SOP Class
public const string Verification = "1.2.840.10008.1.1";
}
SOP Class and Services
// Setting SOP Class in code
dicomImage.Attributes["00080016"].Value = "1.2.840.10008.5.1.4.1.1.2"; // SOP Class UID
dicomImage.Attributes["00080018"].Value = DicomUID.Generate(); // SOP Instance UID
Transfer Syntaxes
Transfer Syntaxes define how data is encoded at the binary level. They specify byte ordering, whether Value Representations are explicitly stated, and what compression algorithm (if any) is applied to pixel data. Choosing the right transfer syntax affects file size, interoperability, and processing performance.
Components of Transfer Syntax
Transfer syntax selection involves three independent choices that combine to determine the final encoding format. Understanding these components helps you choose appropriate syntaxes for different use cases.
Common Transfer Syntaxes
Define transfer syntax UIDs as constants for use in association negotiation and file writing operations. The most widely supported syntax is Explicit VR Little Endian, which should be your default choice for maximum interoperability.
public static class TransferSyntax
{
// Uncompressed
public const string ImplicitVRLittleEndian = "1.2.840.10008.1.2"; // Default
public const string ExplicitVRLittleEndian = "1.2.840.10008.1.2.1"; // Recommended
public const string ExplicitVRBigEndian = "1.2.840.10008.1.2.2"; // Legacy
// Lossless Compression
public const string JPEGLossless = "1.2.840.10008.1.2.4.70";
public const string JPEG2000Lossless = "1.2.840.10008.1.2.4.90";
public const string RLELossless = "1.2.840.10008.1.2.5";
// Lossy Compression
public const string JPEGBaseline = "1.2.840.10008.1.2.4.50";
public const string JPEG2000Lossy = "1.2.840.10008.1.2.4.91";
}
Choosing Transfer Syntax
| Use Case | Recommended Transfer Syntax | Reason |
|---|---|---|
| Archival | JPEG 2000 Lossless | Good compression, diagnostic quality |
| Diagnostic reading | Explicit VR Little Endian | Uncompressed, fastest access |
| Web delivery | JPEG 2000 Lossy | High compression for bandwidth |
| Interoperability | Explicit VR Little Endian | Most compatible |
UIDs (Unique Identifiers)
UIDs uniquely identify DICOM entities following ISO 8824 standard. Every study, series, and instance must have globally unique identifiers to prevent data collisions when images from multiple sources are archived together. UIDs are also used to identify SOP Classes, Transfer Syntaxes, and other DICOM-defined entities.
UID Structure
UIDs follow a hierarchical numeric format where organizations register root prefixes to ensure global uniqueness. Understanding the structure helps you generate valid UIDs and recognize well-known DICOM-defined identifiers.
UID Format: <root>.<organization>.<application>.<instance>
============================================================
Example: 1.2.840.113619.2.55.1.1762295408.1084.1234567890.1
Breakdown:
1.2.840 = ISO registered (USA)
113619 = GE Medical Systems
2.55.1 = Application identifier
1762295408 = Process/timestamp
1084 = Counter
1234567890 = Instance specific
.1 = Sub-instance
Generating UIDs
Generate UIDs using the SDK’s built-in methods to ensure proper formatting and uniqueness. For production systems, consider registering your organization’s own UID root prefix to maintain namespace isolation and traceability.
// Using DICOMObjects UID generation
string studyUID = DicomUID.Generate();
string seriesUID = DicomUID.Generate();
string instanceUID = DicomUID.Generate();
// Custom UID with organization root
const string MyOrgRoot = "1.2.840.99999"; // Must be registered
string customUID = $"{MyOrgRoot}.{DateTime.Now.Ticks}";
// UID validation
public bool IsValidUID(string uid)
{
if (string.IsNullOrEmpty(uid)) return false;
if (uid.Length > 64) return false; // Max 64 characters
return System.Text.RegularExpressions.Regex.IsMatch(uid, @"^[\d.]+$");
}
Important UID Uses
Different UIDs serve specific purposes within the DICOM hierarchy. Study UIDs are shared across all images from a single examination, while Series UIDs group related images within a study. Understanding these relationships ensures proper data organization in PACS systems.
// Study Instance UID - identifies entire study
// Shared across all series in the study
dicomImage.Attributes["0020000D"].Value = studyUID;
// Series Instance UID - identifies a series
// Unique within the study
dicomImage.Attributes["0020000E"].Value = seriesUID;
// SOP Instance UID - identifies single DICOM object
// Globally unique
dicomImage.Attributes["00080018"].Value = instanceUID;
// Frame of Reference UID - links images spatially
// Shared by images in same coordinate system
dicomImage.Attributes["00200052"].Value = frameOfReferenceUID;
Value Representations (VR)
VR defines the data type of an attribute’s value, similar to data types in programming languages. Each DICOM attribute has a defined VR that specifies how its value should be encoded and interpreted. Understanding VRs is essential for correctly parsing and creating DICOM data, especially when working with Implicit VR transfer syntaxes that require VR lookup from dictionaries.
Common VRs
The following VRs are frequently encountered when working with DICOM data. Each has specific formatting rules and length limits that must be followed for valid DICOM encoding.
// AE - Application Entity (16 bytes max)
// Example: "PACS_SERVER", "CT_SCANNER"
// AS - Age String (4 bytes, format: nnnD/W/M/Y)
// Example: "025Y" for 25 years
// CS - Code String (16 bytes max, uppercase)
// Example: "CT", "MR", "ORIGINAL"
// DA - Date (8 bytes, format: YYYYMMDD)
// Example: "20240115"
// DS - Decimal String (16 bytes max)
// Example: "1.5", "0.25"
// DT - DateTime (26 bytes max)
// Example: "20240115093045.123456"
// IS - Integer String (12 bytes max)
// Example: "512", "256"
// LO - Long String (64 chars max)
// PN - Person Name (64 chars per component)
// Format: LastName^FirstName^MiddleName^Prefix^Suffix
// Example: "Doe^John^A^Dr^Jr"
// SH - Short String (16 chars max)
// SQ - Sequence of Items
// TM - Time (16 bytes, format: HHMMSS.FFFFFF)
// Example: "093045" or "093045.123"
// UI - Unique Identifier (64 bytes max)
// UL - Unsigned Long (4 bytes)
// US - Unsigned Short (2 bytes)
Working with VRs in Code
When processing DICOM data programmatically, you often need to check the VR of attributes to determine proper parsing or formatting. The SDK provides methods to inspect VR information and perform type-appropriate conversions. Multi-valued attributes use backslash delimiters.
// Check VR of an attribute
DicomAttribute attr = dicomImage.Attributes["00100010"];
DicomVR vr = attr.VR;
Console.WriteLine($"VR: {vr}"); // Output: VR: PN
// Convert values based on VR
if (attr.VR == DicomVR.VRDate)
{
string dateString = attr.Value.ToString();
DateTime date = DateTime.ParseExact(dateString, "yyyyMMdd", null);
}
// Handle multi-valued attributes (backslash separator)
dicomImage.Attributes["00200037"].Value = "1\\0\\0\\0\\1\\0"; // Image Orientation
string[] orientations = dicomImage.Attributes["00200037"].Value.ToString().Split('\\');
VR Summary Table
| VR | Name | Example | Max Length |
|---|---|---|---|
| AE | Application Entity | ”PACS_SERVER” | 16 |
| DA | Date | ”20240115” | 8 |
| DS | Decimal String | ”1.5” | 16 |
| IS | Integer String | ”512” | 12 |
| LO | Long String | Description text | 64 |
| PN | Person Name | ”Doe^John” | 64 per component |
| SH | Short String | ”ACC123” | 16 |
| TM | Time | ”143000” | 16 |
| UI | Unique Identifier | ”1.2.840…“ | 64 |
| US | Unsigned Short | 512 | 2 bytes |
| SQ | Sequence | Nested items | N/A |
Understanding these core concepts provides the foundation for all DICOM development work.