DICOM Data Model | Medplum

DICOM Modeling

DICOM organizes imaging into a three-level hierarchy — study, series, instance — and Medplum models that hierarchy directly with three resource types rather than flattening it into FHIR ImagingStudy. The DICOM information model and the FHIR one disagree in enough places that a lossy translation at ingest time would throw away exactly the attributes a viewer needs to render the study. Storing the DICOM shape natively means a DICOMweb response can be reconstructed faithfully, while the resources remain searchable and access-controlled like anything else in the project.

Resource types

DicomStudy

One DicomStudy per DICOM Study Instance UID. Created conditionally on studyInstanceUid, so instances arriving over separate uploads collect under one study.

Field DICOM tag Notes
studyInstanceUid (0020,000D) Required. The conditional-create key.
studyId (0020,0010)
studyDate (0008,0020) Converted YYYYMMDDYYYY-MM-DD
studyTime (0008,0030) Converted HHMMSSHH:MM:SS
accessionNumber (0008,0050)
instanceAvailability (0008,0056)
modalitiesInStudy (0008,0061)
referringPhysiciansName (0008,0090)
timezoneOffsetFromUtc (0008,0201)
patientName (0010,0010) The Alphabetic component of the DICOM person name
patientId (0010,0020) A DICOM string, not a reference to a FHIR Patient
patientBirthDate (0010,0030) Converted YYYYMMDDYYYY-MM-DD
patientSex (0010,0040)
numberOfStudyRelatedSeries (0020,1206)
numberOfStudyRelatedInstances (0020,1208)

DicomSeries

One DicomSeries per Series Instance UID, created conditionally on seriesInstanceUid.

Field DICOM tag
study Reference to the parent DicomStudy
seriesInstanceUid (0020,000E)
seriesNumber (0020,0011)
modality (0008,0060)
seriesDescription (0008,103E)
timezoneOffsetFromUtc (0008,0201)
numberOfSeriesRelatedInstances (0020,1209)
performedProcedureStepStartDate (0040,0244)
performedProcedureStepStartTime (0040,0245)

DicomInstance

One DicomInstance per stored SOP instance. Unlike study and series, instances are not created conditionally.

Field DICOM tag Notes
study, series References to the parent resources
sopClassUid (0008,0016)
sopInstanceUid (0008,0018)
instanceAvailability (0008,0056)
timezoneOffsetFromUtc (0008,0201)
instanceNumber (0020,0013) Defaults to "1" when the source file omits it
rows, columns (0028,0010), (0028,0011)
bitsAllocated (0028,0100)
numberOfFrames (0028,0008)
metadata The instance's full DICOM JSON dataset, serialized as a JSON string
raw Reference to the Binary holding the original .dcm file
pixelData References to per-frame pixel Binary resources, filled in asynchronously

What lands in metadata

DicomInstance.metadata is the instance's DICOM JSON dataset — the same representation a WADO-RS metadata request returns — stored as a string. It is cleaned on the way in:

Pixel data extraction

Storing an instance does not extract its pixels inline — that would make every C-STORE wait on decoding. Instead, the create is dispatched to a background worker:

  1. A DicomInstance is created, or its raw reference changes.
  2. The dicom worker enqueues a job on the DicomQueue BullMQ queue (three attempts, exponential backoff starting at one second).
  3. The worker reads the raw Binary, parses the full file, and splits PixelData into frames.
  4. Each frame is written as its own Binary, with securityContext set to the DicomInstance so it inherits the instance's access control.
  5. DicomInstance.pixelData is patched with references to those binaries, in frame order.

The Binary.contentType of each frame is derived from the file's Transfer Syntax UID:

Transfer Syntax UID Content type
1.2.840.10008.1.2.4.50, .57, .70 image/jpeg
1.2.840.10008.1.2.4.90, .91 image/jp2
1.2.840.10008.1.2.4.201, .202 image/jxl
Anything else, including uncompressed application/octet-stream

Search parameters

The DICOM resources are searchable through the normal FHIR search API.

Resource Search parameters
DicomStudy study-instance-uid, study-id, study-date, study-time, accession-number, modalities, referring-physicians-name, patient-name, patient-id
DicomSeries study, series-instance-uid, series-number, performed-procedure-step-start-date, performed-procedure-step-start-time, scheduled-procedure-step-id, requested-procedure-id
DicomInstance study, series, sop-class-uid, sop-instance-uid, instance-number
// Every CT and MR study for an accession number

const studies = await medplum.searchResources('DicomStudy', {

'accession-number': 'A12345',

});

// Every series in a study

const series = await medplum.searchResources('DicomSeries', {

study: `DicomStudy/${studies[0].id}`,

});

Relationship to FHIR ImagingStudy

Medplum does not currently create an ImagingStudy alongside a DicomStudy, and DicomStudy.patientId holds the DICOM Patient ID string rather than a reference to a Medplum Patient. Linking imaging into the chart is on the roadmap.

In the meantime, a Bot subscribed to DicomStudy creation can do the reconciliation your workflow needs — matching patientId against your MRN identifier system, creating an ImagingStudy that references both the Patient and the study's UIDs, and flagging studies whose patient could not be resolved for manual review. Doing it in a Bot rather than at ingest keeps the matching logic — which is highly site-specific — under your control.