A pure Python package for reading and writing DICOM data
—
Management of DICOM sequences and multi-value elements providing comprehensive support for nested datasets, proper validation, type-constrained collections, and hierarchical DICOM data structures used in medical imaging workflows.
List-like container for datasets in DICOM sequences with specialized functionality for nested data structures.
class Sequence(list):
"""
DICOM Sequence container for nested datasets.
List-like container that holds Dataset objects in DICOM sequences,
providing standard list operations plus DICOM-specific functionality
for handling nested medical imaging data structures.
"""
def __init__(self, iterable=None):
"""
Initialize sequence.
Parameters:
- iterable: list or None - Initial sequence items (Dataset objects)
"""
def append(self, item):
"""
Add dataset to sequence.
Parameters:
- item: Dataset - Dataset to add to sequence
Raises:
TypeError - If item is not a Dataset
"""
def insert(self, index, item):
"""
Insert dataset at specific position.
Parameters:
- index: int - Position to insert at
- item: Dataset - Dataset to insert
Raises:
TypeError - If item is not a Dataset
"""
def extend(self, items):
"""
Extend sequence with multiple datasets.
Parameters:
- items: iterable - Datasets to add
Raises:
TypeError - If any item is not a Dataset
"""
def __setitem__(self, index, value):
"""
Set dataset at index.
Parameters:
- index: int - Index to set
- value: Dataset - Dataset to set
Raises:
TypeError - If value is not a Dataset
"""
def __str__(self):
"""Return string representation of sequence."""
def __repr__(self):
"""Return detailed representation of sequence."""Container for multiple values in single data element with type constraints and validation.
class MultiValue(list):
"""
Container for multiple values in DICOM data elements.
Type-constrained list that ensures all values are of the same type,
providing validation and conversion for multi-value DICOM elements.
"""
def __init__(self, type_constructor, iterable=None):
"""
Initialize multi-value container.
Parameters:
- type_constructor: callable - Function to construct/validate element type
- iterable: list or None - Initial values
Raises:
TypeError - If any initial value doesn't match type constructor
"""
def append(self, value):
"""
Add value to multi-value container.
Parameters:
- value: Any - Value to add (will be validated/converted)
Raises:
TypeError - If value cannot be converted to required type
"""
def insert(self, index, value):
"""
Insert value at specific position.
Parameters:
- index: int - Position to insert at
- value: Any - Value to insert (will be validated/converted)
Raises:
TypeError - If value cannot be converted to required type
"""
def extend(self, values):
"""
Extend with multiple values.
Parameters:
- values: iterable - Values to add (all will be validated/converted)
Raises:
TypeError - If any value cannot be converted to required type
"""
def __setitem__(self, index, value):
"""
Set value at index.
Parameters:
- index: int - Index to set
- value: Any - Value to set (will be validated/converted)
Raises:
TypeError - If value cannot be converted to required type
"""
@property
def type_constructor(self):
"""callable: Type constructor function for validation."""
def __str__(self):
"""Return string representation suitable for DICOM."""
def __repr__(self):
"""Return detailed representation."""Base class for type-constrained lists providing validation framework.
class ConstrainedList(list):
"""
Base class for type-constrained list implementations.
Provides framework for creating lists that enforce type constraints
on their elements, used as foundation for MultiValue and similar classes.
"""
def __init__(self, type_constructor, iterable=None):
"""
Initialize constrained list.
Parameters:
- type_constructor: callable - Type validation/conversion function
- iterable: list or None - Initial items
"""
def _validate_item(self, value):
"""
Validate and convert item using type constructor.
Parameters:
- value: Any - Value to validate/convert
Returns:
Any - Validated/converted value
Raises:
TypeError - If value cannot be converted
"""
def _validate_iterable(self, iterable):
"""
Validate all items in iterable.
Parameters:
- iterable: list - Items to validate
Returns:
list - Validated items
Raises:
TypeError - If any item cannot be converted
"""Functions for working with sequences and validating sequence structures.
def is_sequence(value):
"""
Check if value is a DICOM sequence.
Parameters:
- value: Any - Value to check
Returns:
bool - True if value is Sequence instance
"""
def validate_sequence(sequence):
"""
Validate sequence structure and contents.
Parameters:
- sequence: Sequence - Sequence to validate
Returns:
list - Validation errors/warnings
"""
def sequence_delimiter():
"""
Get sequence delimiter data element.
Returns:
DataElement - Sequence delimiter element
"""
def item_delimiter():
"""
Get item delimiter data element.
Returns:
DataElement - Item delimiter element
"""Factory functions for creating multi-value containers with appropriate type constructors.
def create_multivalue(VR, values):
"""
Create MultiValue container for VR type.
Parameters:
- VR: str - Value Representation
- values: list - Initial values
Returns:
MultiValue - Container with appropriate type constructor
"""
def multivalue_from_string(VR, value_string, separator="\\"):
"""
Create MultiValue from delimited string.
Parameters:
- VR: str - Value Representation
- value_string: str - Delimited string of values
- separator: str - Value separator (usually backslash)
Returns:
MultiValue - Container with parsed values
"""
def string_from_multivalue(multivalue, separator="\\"):
"""
Convert MultiValue to delimited string.
Parameters:
- multivalue: MultiValue - Container to convert
- separator: str - Value separator
Returns:
str - Delimited string representation
"""Functions for navigating and manipulating nested sequence structures.
def find_datasets_in_sequence(sequence, condition):
"""
Find datasets in sequence matching condition.
Parameters:
- sequence: Sequence - Sequence to search
- condition: callable - Function that returns True for matching datasets
Returns:
list - Matching datasets
"""
def walk_sequence_tree(sequence, visit_func):
"""
Walk through nested sequence tree structure.
Parameters:
- sequence: Sequence - Root sequence to walk
- visit_func: callable - Function called for each dataset
"""
def flatten_sequence(sequence, max_depth=None):
"""
Flatten nested sequences into single list.
Parameters:
- sequence: Sequence - Sequence to flatten
- max_depth: int - Maximum nesting depth to flatten
Returns:
list - Flattened list of datasets
"""Functions for validating collection contents and structure.
def validate_multivalue(multivalue):
"""
Validate MultiValue container contents.
Parameters:
- multivalue: MultiValue - Container to validate
Returns:
list - Validation errors/warnings
"""
def check_collection_consistency(collection):
"""
Check collection for internal consistency.
Parameters:
- collection: Sequence or MultiValue - Collection to check
Returns:
bool - True if collection is consistent
"""
def collection_statistics(collection):
"""
Get statistics about collection contents.
Parameters:
- collection: Sequence or MultiValue - Collection to analyze
Returns:
dict - Statistics (count, types, sizes, etc.)
"""from pydicom import Dataset, Sequence
from pydicom.sequence import Sequence
# Create empty sequence
referenced_images = Sequence()
# Add datasets to sequence
for i in range(3):
item = Dataset()
item.ReferencedSOPClassUID = "1.2.840.10008.5.1.4.1.1.2" # CT Image Storage
item.ReferencedSOPInstanceUID = f"1.2.3.4.5.{i}"
referenced_images.append(item)
# Create dataset with sequence
dataset = Dataset()
dataset.PatientName = "Test Patient"
dataset.ReferencedImageSequence = referenced_images
# Access sequence items
print(f"Number of referenced images: {len(dataset.ReferencedImageSequence)}")
for i, item in enumerate(dataset.ReferencedImageSequence):
print(f"Image {i}: {item.ReferencedSOPInstanceUID}")from pydicom import Dataset, Sequence
# Create nested sequence structure
main_dataset = Dataset()
# Create sequence of study records
study_sequence = Sequence()
for study_num in range(2):
study_item = Dataset()
study_item.StudyInstanceUID = f"1.2.3.{study_num}"
study_item.StudyDescription = f"Study {study_num + 1}"
# Create nested sequence of series within each study
series_sequence = Sequence()
for series_num in range(3):
series_item = Dataset()
series_item.SeriesInstanceUID = f"1.2.3.{study_num}.{series_num}"
series_item.SeriesDescription = f"Series {series_num + 1}"
series_item.Modality = "CT"
series_sequence.append(series_item)
study_item.SeriesSequence = series_sequence
study_sequence.append(study_item)
main_dataset.StudySequence = study_sequence
# Navigate nested structure
for study in main_dataset.StudySequence:
print(f"Study: {study.StudyDescription}")
for series in study.SeriesSequence:
print(f" Series: {series.SeriesDescription} ({series.Modality})")from pydicom.multival import MultiValue
from pydicom.valuerep import DS, IS
# Create multi-value numeric elements
# Window Centers - multiple DS values
window_centers = MultiValue(DS, ["200", "400", "600"])
print(f"Window Centers: {window_centers}")
print(f"First center: {window_centers[0]}")
print(f"All centers: {list(window_centers)}")
# Image Position Patient - three DS values for x, y, z
image_position = MultiValue(DS, ["-125.0", "-125.0", "100.0"])
print(f"Image Position: {image_position}")
# Instance Numbers - multiple IS values
instance_numbers = MultiValue(IS, ["1", "2", "3", "4", "5"])
print(f"Instance Numbers: {instance_numbers}")
# Add values to existing MultiValue
window_centers.append("800")
print(f"Updated centers: {window_centers}")
# Slice through MultiValue
first_three = window_centers[:3]
print(f"First three centers: {first_three}")from pydicom.multival import multivalue_from_string, string_from_multivalue
from pydicom.valuerep import DS
# Create from delimited string (standard DICOM format)
pixel_spacing_str = "0.625\\0.625" # Backslash-delimited
pixel_spacing = multivalue_from_string("DS", pixel_spacing_str)
print(f"Pixel Spacing: {pixel_spacing}")
print(f"X spacing: {pixel_spacing[0]}")
print(f"Y spacing: {pixel_spacing[1]}")
# Convert back to string
spacing_str = string_from_multivalue(pixel_spacing)
print(f"String representation: '{spacing_str}'")
# Multiple window values
window_values_str = "200\\400\\600\\800"
window_values = multivalue_from_string("DS", window_values_str)
print(f"Window values: {window_values}")from pydicom import Dataset, Sequence
# Create sequence
procedure_sequence = Sequence()
# Add multiple items
procedures = [
{"CodeValue": "P001", "CodeMeaning": "CT Head"},
{"CodeValue": "P002", "CodeMeaning": "CT Chest"},
{"CodeValue": "P003", "CodeMeaning": "CT Abdomen"}
]
for proc in procedures:
item = Dataset()
item.CodeValue = proc["CodeValue"]
item.CodingSchemeDesignator = "LOCAL"
item.CodeMeaning = proc["CodeMeaning"]
procedure_sequence.append(item)
# Insert item at specific position
new_proc = Dataset()
new_proc.CodeValue = "P001.5"
new_proc.CodingSchemeDesignator = "LOCAL"
new_proc.CodeMeaning = "CT Neck"
procedure_sequence.insert(1, new_proc)
# Remove item
del procedure_sequence[0]
# Iterate and modify
for i, proc in enumerate(procedure_sequence):
proc.SequenceNumber = i + 1
print(f"{proc.SequenceNumber}: {proc.CodeMeaning}")from pydicom import Dataset, Sequence
# Create structured report-style sequence
measurement_sequence = Sequence()
# Measurement 1: Length
length_item = Dataset()
length_item.ConceptNameCodeSequence = Sequence()
concept = Dataset()
concept.CodeValue = "410668003"
concept.CodingSchemeDesignator = "SCT"
concept.CodeMeaning = "Length"
length_item.ConceptNameCodeSequence.append(concept)
# Add measured value
length_item.MeasuredValueSequence = Sequence()
value_item = Dataset()
value_item.NumericValue = "25.4"
value_item.MeasurementUnitsCodeSequence = Sequence()
unit = Dataset()
unit.CodeValue = "mm"
unit.CodingSchemeDesignator = "UCUM"
unit.CodeMeaning = "millimeter"
value_item.MeasurementUnitsCodeSequence.append(unit)
length_item.MeasuredValueSequence.append(value_item)
measurement_sequence.append(length_item)
# Measurement 2: Area
area_item = Dataset()
area_item.ConceptNameCodeSequence = Sequence()
concept2 = Dataset()
concept2.CodeValue = "42798000"
concept2.CodingSchemeDesignator = "SCT"
concept2.CodeMeaning = "Area"
area_item.ConceptNameCodeSequence.append(concept2)
area_item.MeasuredValueSequence = Sequence()
value_item2 = Dataset()
value_item2.NumericValue = "15.8"
value_item2.MeasurementUnitsCodeSequence = Sequence()
unit2 = Dataset()
unit2.CodeValue = "mm2"
unit2.CodingSchemeDesignator = "UCUM"
unit2.CodeMeaning = "square millimeter"
value_item2.MeasurementUnitsCodeSequence.append(unit2)
area_item.MeasuredValueSequence.append(value_item2)
measurement_sequence.append(area_item)
print(f"Number of measurements: {len(measurement_sequence)}")
for i, measurement in enumerate(measurement_sequence):
concept_name = measurement.ConceptNameCodeSequence[0].CodeMeaning
value = measurement.MeasuredValueSequence[0].NumericValue
unit = measurement.MeasuredValueSequence[0].MeasurementUnitsCodeSequence[0].CodeMeaning
print(f"Measurement {i+1}: {concept_name} = {value} {unit}")from pydicom import Dataset, Sequence
# Create sequence with validation
def validate_image_reference(dataset):
"""Validate that dataset has required image reference elements."""
required = ['ReferencedSOPClassUID', 'ReferencedSOPInstanceUID']
return all(hasattr(dataset, attr) for attr in required)
# Create sequence
ref_sequence = Sequence()
# Add valid items
valid_item = Dataset()
valid_item.ReferencedSOPClassUID = "1.2.840.10008.5.1.4.1.1.2"
valid_item.ReferencedSOPInstanceUID = "1.2.3.4.5"
ref_sequence.append(valid_item)
# Add another valid item
valid_item2 = Dataset()
valid_item2.ReferencedSOPClassUID = "1.2.840.10008.5.1.4.1.1.1"
valid_item2.ReferencedSOPInstanceUID = "1.2.3.4.6"
ref_sequence.append(valid_item2)
# Validate all items
valid_items = [validate_image_reference(item) for item in ref_sequence]
print(f"All items valid: {all(valid_items)}")
# Search for specific items
def find_ct_images(dataset):
"""Find CT image references."""
ct_class_uid = "1.2.840.10008.5.1.4.1.1.2"
return dataset.ReferencedSOPClassUID == ct_class_uid
ct_references = [item for item in ref_sequence if find_ct_images(item)]
print(f"Found {len(ct_references)} CT image references")from pydicom.multival import MultiValue
from pydicom.valuerep import DS, IS
# Type-safe multi-value container
try:
# Create DS MultiValue
measurements = MultiValue(DS, ["1.5", "2.3", "4.7"])
print(f"Measurements: {measurements}")
# Add valid value
measurements.append("3.8") # String that converts to DS
measurements.append(5.2) # Float that converts to DS
print(f"After additions: {measurements}")
# Try to add invalid value
measurements.append("invalid") # This will raise an error
except (ValueError, TypeError) as e:
print(f"Type validation error: {e}")
# Ensure consistent types
window_levels = MultiValue(IS, [100, 200, 300])
print(f"Window levels: {window_levels}")
print(f"Type of first element: {type(window_levels[0])}")Install with Tessl CLI
npx tessl i tessl/pypi-pydicom