0
# Sequences and Collections
1
2
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.
3
4
## Capabilities
5
6
### Sequence Class
7
8
List-like container for datasets in DICOM sequences with specialized functionality for nested data structures.
9
10
```python { .api }
11
class Sequence(list):
12
"""
13
DICOM Sequence container for nested datasets.
14
15
List-like container that holds Dataset objects in DICOM sequences,
16
providing standard list operations plus DICOM-specific functionality
17
for handling nested medical imaging data structures.
18
"""
19
20
def __init__(self, iterable=None):
21
"""
22
Initialize sequence.
23
24
Parameters:
25
- iterable: list or None - Initial sequence items (Dataset objects)
26
"""
27
28
def append(self, item):
29
"""
30
Add dataset to sequence.
31
32
Parameters:
33
- item: Dataset - Dataset to add to sequence
34
35
Raises:
36
TypeError - If item is not a Dataset
37
"""
38
39
def insert(self, index, item):
40
"""
41
Insert dataset at specific position.
42
43
Parameters:
44
- index: int - Position to insert at
45
- item: Dataset - Dataset to insert
46
47
Raises:
48
TypeError - If item is not a Dataset
49
"""
50
51
def extend(self, items):
52
"""
53
Extend sequence with multiple datasets.
54
55
Parameters:
56
- items: iterable - Datasets to add
57
58
Raises:
59
TypeError - If any item is not a Dataset
60
"""
61
62
def __setitem__(self, index, value):
63
"""
64
Set dataset at index.
65
66
Parameters:
67
- index: int - Index to set
68
- value: Dataset - Dataset to set
69
70
Raises:
71
TypeError - If value is not a Dataset
72
"""
73
74
def __str__(self):
75
"""Return string representation of sequence."""
76
77
def __repr__(self):
78
"""Return detailed representation of sequence."""
79
```
80
81
### MultiValue Class
82
83
Container for multiple values in single data element with type constraints and validation.
84
85
```python { .api }
86
class MultiValue(list):
87
"""
88
Container for multiple values in DICOM data elements.
89
90
Type-constrained list that ensures all values are of the same type,
91
providing validation and conversion for multi-value DICOM elements.
92
"""
93
94
def __init__(self, type_constructor, iterable=None):
95
"""
96
Initialize multi-value container.
97
98
Parameters:
99
- type_constructor: callable - Function to construct/validate element type
100
- iterable: list or None - Initial values
101
102
Raises:
103
TypeError - If any initial value doesn't match type constructor
104
"""
105
106
def append(self, value):
107
"""
108
Add value to multi-value container.
109
110
Parameters:
111
- value: Any - Value to add (will be validated/converted)
112
113
Raises:
114
TypeError - If value cannot be converted to required type
115
"""
116
117
def insert(self, index, value):
118
"""
119
Insert value at specific position.
120
121
Parameters:
122
- index: int - Position to insert at
123
- value: Any - Value to insert (will be validated/converted)
124
125
Raises:
126
TypeError - If value cannot be converted to required type
127
"""
128
129
def extend(self, values):
130
"""
131
Extend with multiple values.
132
133
Parameters:
134
- values: iterable - Values to add (all will be validated/converted)
135
136
Raises:
137
TypeError - If any value cannot be converted to required type
138
"""
139
140
def __setitem__(self, index, value):
141
"""
142
Set value at index.
143
144
Parameters:
145
- index: int - Index to set
146
- value: Any - Value to set (will be validated/converted)
147
148
Raises:
149
TypeError - If value cannot be converted to required type
150
"""
151
152
@property
153
def type_constructor(self):
154
"""callable: Type constructor function for validation."""
155
156
def __str__(self):
157
"""Return string representation suitable for DICOM."""
158
159
def __repr__(self):
160
"""Return detailed representation."""
161
```
162
163
### ConstrainedList Base Class
164
165
Base class for type-constrained lists providing validation framework.
166
167
```python { .api }
168
class ConstrainedList(list):
169
"""
170
Base class for type-constrained list implementations.
171
172
Provides framework for creating lists that enforce type constraints
173
on their elements, used as foundation for MultiValue and similar classes.
174
"""
175
176
def __init__(self, type_constructor, iterable=None):
177
"""
178
Initialize constrained list.
179
180
Parameters:
181
- type_constructor: callable - Type validation/conversion function
182
- iterable: list or None - Initial items
183
"""
184
185
def _validate_item(self, value):
186
"""
187
Validate and convert item using type constructor.
188
189
Parameters:
190
- value: Any - Value to validate/convert
191
192
Returns:
193
Any - Validated/converted value
194
195
Raises:
196
TypeError - If value cannot be converted
197
"""
198
199
def _validate_iterable(self, iterable):
200
"""
201
Validate all items in iterable.
202
203
Parameters:
204
- iterable: list - Items to validate
205
206
Returns:
207
list - Validated items
208
209
Raises:
210
TypeError - If any item cannot be converted
211
"""
212
```
213
214
### Sequence Utility Functions
215
216
Functions for working with sequences and validating sequence structures.
217
218
```python { .api }
219
def is_sequence(value):
220
"""
221
Check if value is a DICOM sequence.
222
223
Parameters:
224
- value: Any - Value to check
225
226
Returns:
227
bool - True if value is Sequence instance
228
"""
229
230
def validate_sequence(sequence):
231
"""
232
Validate sequence structure and contents.
233
234
Parameters:
235
- sequence: Sequence - Sequence to validate
236
237
Returns:
238
list - Validation errors/warnings
239
"""
240
241
def sequence_delimiter():
242
"""
243
Get sequence delimiter data element.
244
245
Returns:
246
DataElement - Sequence delimiter element
247
"""
248
249
def item_delimiter():
250
"""
251
Get item delimiter data element.
252
253
Returns:
254
DataElement - Item delimiter element
255
"""
256
```
257
258
### MultiValue Creation Functions
259
260
Factory functions for creating multi-value containers with appropriate type constructors.
261
262
```python { .api }
263
def create_multivalue(VR, values):
264
"""
265
Create MultiValue container for VR type.
266
267
Parameters:
268
- VR: str - Value Representation
269
- values: list - Initial values
270
271
Returns:
272
MultiValue - Container with appropriate type constructor
273
"""
274
275
def multivalue_from_string(VR, value_string, separator="\\"):
276
"""
277
Create MultiValue from delimited string.
278
279
Parameters:
280
- VR: str - Value Representation
281
- value_string: str - Delimited string of values
282
- separator: str - Value separator (usually backslash)
283
284
Returns:
285
MultiValue - Container with parsed values
286
"""
287
288
def string_from_multivalue(multivalue, separator="\\"):
289
"""
290
Convert MultiValue to delimited string.
291
292
Parameters:
293
- multivalue: MultiValue - Container to convert
294
- separator: str - Value separator
295
296
Returns:
297
str - Delimited string representation
298
"""
299
```
300
301
### Sequence Navigation Functions
302
303
Functions for navigating and manipulating nested sequence structures.
304
305
```python { .api }
306
def find_datasets_in_sequence(sequence, condition):
307
"""
308
Find datasets in sequence matching condition.
309
310
Parameters:
311
- sequence: Sequence - Sequence to search
312
- condition: callable - Function that returns True for matching datasets
313
314
Returns:
315
list - Matching datasets
316
"""
317
318
def walk_sequence_tree(sequence, visit_func):
319
"""
320
Walk through nested sequence tree structure.
321
322
Parameters:
323
- sequence: Sequence - Root sequence to walk
324
- visit_func: callable - Function called for each dataset
325
"""
326
327
def flatten_sequence(sequence, max_depth=None):
328
"""
329
Flatten nested sequences into single list.
330
331
Parameters:
332
- sequence: Sequence - Sequence to flatten
333
- max_depth: int - Maximum nesting depth to flatten
334
335
Returns:
336
list - Flattened list of datasets
337
"""
338
```
339
340
### Collection Validation Functions
341
342
Functions for validating collection contents and structure.
343
344
```python { .api }
345
def validate_multivalue(multivalue):
346
"""
347
Validate MultiValue container contents.
348
349
Parameters:
350
- multivalue: MultiValue - Container to validate
351
352
Returns:
353
list - Validation errors/warnings
354
"""
355
356
def check_collection_consistency(collection):
357
"""
358
Check collection for internal consistency.
359
360
Parameters:
361
- collection: Sequence or MultiValue - Collection to check
362
363
Returns:
364
bool - True if collection is consistent
365
"""
366
367
def collection_statistics(collection):
368
"""
369
Get statistics about collection contents.
370
371
Parameters:
372
- collection: Sequence or MultiValue - Collection to analyze
373
374
Returns:
375
dict - Statistics (count, types, sizes, etc.)
376
"""
377
```
378
379
## Usage Examples
380
381
### Creating and Using Sequences
382
383
```python
384
from pydicom import Dataset, Sequence
385
from pydicom.sequence import Sequence
386
387
# Create empty sequence
388
referenced_images = Sequence()
389
390
# Add datasets to sequence
391
for i in range(3):
392
item = Dataset()
393
item.ReferencedSOPClassUID = "1.2.840.10008.5.1.4.1.1.2" # CT Image Storage
394
item.ReferencedSOPInstanceUID = f"1.2.3.4.5.{i}"
395
referenced_images.append(item)
396
397
# Create dataset with sequence
398
dataset = Dataset()
399
dataset.PatientName = "Test Patient"
400
dataset.ReferencedImageSequence = referenced_images
401
402
# Access sequence items
403
print(f"Number of referenced images: {len(dataset.ReferencedImageSequence)}")
404
405
for i, item in enumerate(dataset.ReferencedImageSequence):
406
print(f"Image {i}: {item.ReferencedSOPInstanceUID}")
407
```
408
409
### Working with Nested Sequences
410
411
```python
412
from pydicom import Dataset, Sequence
413
414
# Create nested sequence structure
415
main_dataset = Dataset()
416
417
# Create sequence of study records
418
study_sequence = Sequence()
419
420
for study_num in range(2):
421
study_item = Dataset()
422
study_item.StudyInstanceUID = f"1.2.3.{study_num}"
423
study_item.StudyDescription = f"Study {study_num + 1}"
424
425
# Create nested sequence of series within each study
426
series_sequence = Sequence()
427
428
for series_num in range(3):
429
series_item = Dataset()
430
series_item.SeriesInstanceUID = f"1.2.3.{study_num}.{series_num}"
431
series_item.SeriesDescription = f"Series {series_num + 1}"
432
series_item.Modality = "CT"
433
434
series_sequence.append(series_item)
435
436
study_item.SeriesSequence = series_sequence
437
study_sequence.append(study_item)
438
439
main_dataset.StudySequence = study_sequence
440
441
# Navigate nested structure
442
for study in main_dataset.StudySequence:
443
print(f"Study: {study.StudyDescription}")
444
for series in study.SeriesSequence:
445
print(f" Series: {series.SeriesDescription} ({series.Modality})")
446
```
447
448
### MultiValue Elements
449
450
```python
451
from pydicom.multival import MultiValue
452
from pydicom.valuerep import DS, IS
453
454
# Create multi-value numeric elements
455
# Window Centers - multiple DS values
456
window_centers = MultiValue(DS, ["200", "400", "600"])
457
print(f"Window Centers: {window_centers}")
458
print(f"First center: {window_centers[0]}")
459
print(f"All centers: {list(window_centers)}")
460
461
# Image Position Patient - three DS values for x, y, z
462
image_position = MultiValue(DS, ["-125.0", "-125.0", "100.0"])
463
print(f"Image Position: {image_position}")
464
465
# Instance Numbers - multiple IS values
466
instance_numbers = MultiValue(IS, ["1", "2", "3", "4", "5"])
467
print(f"Instance Numbers: {instance_numbers}")
468
469
# Add values to existing MultiValue
470
window_centers.append("800")
471
print(f"Updated centers: {window_centers}")
472
473
# Slice through MultiValue
474
first_three = window_centers[:3]
475
print(f"First three centers: {first_three}")
476
```
477
478
### String-based MultiValue Creation
479
480
```python
481
from pydicom.multival import multivalue_from_string, string_from_multivalue
482
from pydicom.valuerep import DS
483
484
# Create from delimited string (standard DICOM format)
485
pixel_spacing_str = "0.625\\0.625" # Backslash-delimited
486
pixel_spacing = multivalue_from_string("DS", pixel_spacing_str)
487
print(f"Pixel Spacing: {pixel_spacing}")
488
print(f"X spacing: {pixel_spacing[0]}")
489
print(f"Y spacing: {pixel_spacing[1]}")
490
491
# Convert back to string
492
spacing_str = string_from_multivalue(pixel_spacing)
493
print(f"String representation: '{spacing_str}'")
494
495
# Multiple window values
496
window_values_str = "200\\400\\600\\800"
497
window_values = multivalue_from_string("DS", window_values_str)
498
print(f"Window values: {window_values}")
499
```
500
501
### Sequence Manipulation
502
503
```python
504
from pydicom import Dataset, Sequence
505
506
# Create sequence
507
procedure_sequence = Sequence()
508
509
# Add multiple items
510
procedures = [
511
{"CodeValue": "P001", "CodeMeaning": "CT Head"},
512
{"CodeValue": "P002", "CodeMeaning": "CT Chest"},
513
{"CodeValue": "P003", "CodeMeaning": "CT Abdomen"}
514
]
515
516
for proc in procedures:
517
item = Dataset()
518
item.CodeValue = proc["CodeValue"]
519
item.CodingSchemeDesignator = "LOCAL"
520
item.CodeMeaning = proc["CodeMeaning"]
521
procedure_sequence.append(item)
522
523
# Insert item at specific position
524
new_proc = Dataset()
525
new_proc.CodeValue = "P001.5"
526
new_proc.CodingSchemeDesignator = "LOCAL"
527
new_proc.CodeMeaning = "CT Neck"
528
procedure_sequence.insert(1, new_proc)
529
530
# Remove item
531
del procedure_sequence[0]
532
533
# Iterate and modify
534
for i, proc in enumerate(procedure_sequence):
535
proc.SequenceNumber = i + 1
536
print(f"{proc.SequenceNumber}: {proc.CodeMeaning}")
537
```
538
539
### Complex Sequence Structures
540
541
```python
542
from pydicom import Dataset, Sequence
543
544
# Create structured report-style sequence
545
measurement_sequence = Sequence()
546
547
# Measurement 1: Length
548
length_item = Dataset()
549
length_item.ConceptNameCodeSequence = Sequence()
550
concept = Dataset()
551
concept.CodeValue = "410668003"
552
concept.CodingSchemeDesignator = "SCT"
553
concept.CodeMeaning = "Length"
554
length_item.ConceptNameCodeSequence.append(concept)
555
556
# Add measured value
557
length_item.MeasuredValueSequence = Sequence()
558
value_item = Dataset()
559
value_item.NumericValue = "25.4"
560
value_item.MeasurementUnitsCodeSequence = Sequence()
561
unit = Dataset()
562
unit.CodeValue = "mm"
563
unit.CodingSchemeDesignator = "UCUM"
564
unit.CodeMeaning = "millimeter"
565
value_item.MeasurementUnitsCodeSequence.append(unit)
566
length_item.MeasuredValueSequence.append(value_item)
567
568
measurement_sequence.append(length_item)
569
570
# Measurement 2: Area
571
area_item = Dataset()
572
area_item.ConceptNameCodeSequence = Sequence()
573
concept2 = Dataset()
574
concept2.CodeValue = "42798000"
575
concept2.CodingSchemeDesignator = "SCT"
576
concept2.CodeMeaning = "Area"
577
area_item.ConceptNameCodeSequence.append(concept2)
578
579
area_item.MeasuredValueSequence = Sequence()
580
value_item2 = Dataset()
581
value_item2.NumericValue = "15.8"
582
value_item2.MeasurementUnitsCodeSequence = Sequence()
583
unit2 = Dataset()
584
unit2.CodeValue = "mm2"
585
unit2.CodingSchemeDesignator = "UCUM"
586
unit2.CodeMeaning = "square millimeter"
587
value_item2.MeasurementUnitsCodeSequence.append(unit2)
588
area_item.MeasuredValueSequence.append(value_item2)
589
590
measurement_sequence.append(area_item)
591
592
print(f"Number of measurements: {len(measurement_sequence)}")
593
for i, measurement in enumerate(measurement_sequence):
594
concept_name = measurement.ConceptNameCodeSequence[0].CodeMeaning
595
value = measurement.MeasuredValueSequence[0].NumericValue
596
unit = measurement.MeasuredValueSequence[0].MeasurementUnitsCodeSequence[0].CodeMeaning
597
print(f"Measurement {i+1}: {concept_name} = {value} {unit}")
598
```
599
600
### Sequence Validation and Search
601
602
```python
603
from pydicom import Dataset, Sequence
604
605
# Create sequence with validation
606
def validate_image_reference(dataset):
607
"""Validate that dataset has required image reference elements."""
608
required = ['ReferencedSOPClassUID', 'ReferencedSOPInstanceUID']
609
return all(hasattr(dataset, attr) for attr in required)
610
611
# Create sequence
612
ref_sequence = Sequence()
613
614
# Add valid items
615
valid_item = Dataset()
616
valid_item.ReferencedSOPClassUID = "1.2.840.10008.5.1.4.1.1.2"
617
valid_item.ReferencedSOPInstanceUID = "1.2.3.4.5"
618
ref_sequence.append(valid_item)
619
620
# Add another valid item
621
valid_item2 = Dataset()
622
valid_item2.ReferencedSOPClassUID = "1.2.840.10008.5.1.4.1.1.1"
623
valid_item2.ReferencedSOPInstanceUID = "1.2.3.4.6"
624
ref_sequence.append(valid_item2)
625
626
# Validate all items
627
valid_items = [validate_image_reference(item) for item in ref_sequence]
628
print(f"All items valid: {all(valid_items)}")
629
630
# Search for specific items
631
def find_ct_images(dataset):
632
"""Find CT image references."""
633
ct_class_uid = "1.2.840.10008.5.1.4.1.1.2"
634
return dataset.ReferencedSOPClassUID == ct_class_uid
635
636
ct_references = [item for item in ref_sequence if find_ct_images(item)]
637
print(f"Found {len(ct_references)} CT image references")
638
```
639
640
### MultiValue Type Safety
641
642
```python
643
from pydicom.multival import MultiValue
644
from pydicom.valuerep import DS, IS
645
646
# Type-safe multi-value container
647
try:
648
# Create DS MultiValue
649
measurements = MultiValue(DS, ["1.5", "2.3", "4.7"])
650
print(f"Measurements: {measurements}")
651
652
# Add valid value
653
measurements.append("3.8") # String that converts to DS
654
measurements.append(5.2) # Float that converts to DS
655
print(f"After additions: {measurements}")
656
657
# Try to add invalid value
658
measurements.append("invalid") # This will raise an error
659
660
except (ValueError, TypeError) as e:
661
print(f"Type validation error: {e}")
662
663
# Ensure consistent types
664
window_levels = MultiValue(IS, [100, 200, 300])
665
print(f"Window levels: {window_levels}")
666
print(f"Type of first element: {type(window_levels[0])}")
667
```