13 CDM map study, series, image
13.2 Design
Note
Q: Please help me design how these identifiers should flow into OMOP CDM tables:
- StudyInstanceUID
- SeriesInstanceUID
- SOPInstanceUID
Here’s a comprehensive design for mapping DICOM identifiers into OMOP CDM tables:
13.2.1 Core OMOP Tables Mapping
13.2.1.1 1. PROCEDURE_OCCURRENCE (Study-level)
-- One row per imaging study
-- OMOP auto-generated
procedure_occurrence_id -- Patient identifier
person_id -- Maps to imaging procedure (e.g., "CT Head")
procedure_concept_id -- From StudyDate
procedure_date -- StudyInstanceUID
procedure_source_value -- AccessionNumber modifier_source_value
13.2.1.2 2. MEASUREMENT (Series & Image-level)
-- Multiple rows per study (one per series or per image)
-- OMOP auto-generated
measurement_id -- Patient identifier
person_id -- Maps to measurement type
measurement_concept_id -- From SeriesDate or ContentDate
measurement_date -- Quantitative measurements (if any)
value_as_number -- SeriesInstanceUID or SOPInstanceUID measurement_source_value
13.2.2 Custom Extension Strategy
13.2.2.1 3. DICOM_HIERARCHY (Custom table)
CREATE TABLE dicom_hierarchy (
PRIMARY KEY,
hierarchy_id BIGINT VARCHAR(200) NOT NULL,
study_instance_uid VARCHAR(200),
series_instance_uid VARCHAR(200),
sop_instance_uid -- Links to OMOP
procedure_occurrence_id BIGINT, -- Links to OMOP
measurement_id BIGINT, VARCHAR(10), -- 'STUDY'/'SERIES'/'IMAGE'
hierarchy_level VARCHAR(500),
file_path DATE
created_date );
13.2.3 Mapping Flow Diagram
DICOM Study (CT Brain)
├── StudyInstanceUID: 1.2.3.4.5
│ └── PROCEDURE_OCCURRENCE
│ ├── procedure_source_value: "1.2.3.4.5"
│ └── procedure_concept_id: [CT Head concept]
│
├── SeriesInstanceUID: 1.2.3.4.5.100
│ └── MEASUREMENT (Series-level)
│ ├── measurement_source_value: "1.2.3.4.5.100"
│ └── measurement_concept_id: [Imaging series concept]
│
└── SOPInstanceUID: 1.2.3.4.5.100.001
└── MEASUREMENT (Image-level)
├── measurement_source_value: "1.2.3.4.5.100.001"
└── measurement_concept_id: [CT slice concept]
13.2.4 Python Implementation Example
def map_dicom_to_omop(dicom_metadata):
"""Map DICOM hierarchy to OMOP CDM tables"""
# 1. Create study-level procedure
= {
procedure_data 'person_id': dicom_metadata['patient_id'],
'procedure_concept_id': get_procedure_concept('CT_HEAD'),
'procedure_date': dicom_metadata['study_date'],
'procedure_source_value': dicom_metadata['study_instance_uid']
}= insert_procedure_occurrence(procedure_data)
procedure_id
# 2. Create series-level measurements
for series in dicom_metadata['series']:
= {
series_measurement 'person_id': dicom_metadata['patient_id'],
'measurement_concept_id': get_measurement_concept('IMAGING_SERIES'),
'measurement_date': series['series_date'],
'measurement_source_value': series['series_instance_uid']
}= insert_measurement(series_measurement)
series_measurement_id
# 3. Create image-level measurements (if needed)
for image in series['images']:
= {
image_measurement 'person_id': dicom_metadata['patient_id'],
'measurement_concept_id': get_measurement_concept('CT_SLICE'),
'measurement_date': image['content_date'],
'measurement_source_value': image['sop_instance_uid'],
'value_as_number': image.get('slice_thickness')
}= insert_measurement(image_measurement)
image_measurement_id
# 4. Maintain hierarchy in custom table
insert_dicom_hierarchy({'study_instance_uid': dicom_metadata['study_instance_uid'],
'series_instance_uid': series['series_instance_uid'],
'sop_instance_uid': image['sop_instance_uid'],
'procedure_occurrence_id': procedure_id,
'measurement_id': image_measurement_id,
'hierarchy_level': 'IMAGE'
})
Key Design Decisions
- Study →
PROCEDURE_OCCURRENCE
(represents the imaging exam) - Series →
MEASUREMENT
(represents acquisition parameters) - Images →
MEASUREMENT
(individual slices/observations) - Custom table maintains DICOM hierarchy and file references