What Is DICOM?
DICOM (Digital Imaging and Communications in Medicine) is the international standard for medical images and related information. Developed by the American College of Radiology (ACR) and the National Electrical Manufacturers Association (NEMA) since 1985 (initially as ACR-NEMA), DICOM PS3.x defines everything about how medical images are stored, transmitted, and displayed β from CT scans and MRIs to ultrasounds, X-rays, mammograms, and endoscopy images.
A DICOM file is not just an image β it is an information object combining pixel data with rich metadata: patient identity, acquisition parameters, institution information, equipment calibration data, and clinical context. This comprehensive metadata is what distinguishes DICOM from consumer image formats like JPEG or PNG, and what makes it medically and legally essential.
DICOM is ubiquitous in healthcare: every hospital PACS (Picture Archiving and Communication System), every radiology workstation, every radiotherapy planning system, and every modality (CT, MRI, PET, ultrasound) produces and consumes DICOM. The standard is maintained by the DICOM Standards Committee and updated continuously.
DICOM File Structure
File Meta Information (Preamble + DICOM Header)
Every DICOM file starts with:
Bytes 0β127: 128-byte preamble (zeros by convention, sometimes ASCII text)
Bytes 128β131: "DICM" magic (0x44 0x49 0x43 0x4D)
Bytes 132+: File Meta Information dataset (Group 0002 attributes)
The File Meta Information contains:
- (0002,0001) File Meta Information Version: 2 bytes, always
0x00 0x01 - (0002,0002) Media Storage SOP Class UID: What type of image (CT Image, MR Image, etc.)
- (0002,0003) Media Storage SOP Instance UID: Globally unique ID for this specific image
- (0002,0010) Transfer Syntax UID: Encoding rules (byte order, compression)
Data Elements
After the File Meta Information, the DICOM dataset consists of data elements in Tag-Length-Value (TLV) format:
|-- 4 bytes --|-- 2 bytes --|-- 2 or 4 bytes --|-- N bytes --|
| Tag (GG,EE)| VR | Length | Value |
- Tag: Two 16-bit unsigned integers: Group (GG) and Element (EE). Tags are written as
(GGGG,EEEE). - VR (Value Representation): 2-byte ASCII code describing data type
- Length: Size of value in bytes
- Value: The actual data
Example tags:
(0008,0060) CS Modality = "CT"
(0008,0020) DA Study Date = "20240315"
(0010,0010) PN Patient Name = "Doe^John^A"
(0010,0020) LO Patient ID = "PAT-123456"
(0010,0030) DA Patient Birth Date = "19750822"
(0010,0040) CS Patient Sex = "M"
(0020,000D) UI Study Instance UID = "1.2.840.10008.5.1.4.1.1..."
(0028,0010) US Rows = 512
(0028,0011) US Columns = 512
(0028,0030) DS Pixel Spacing = "0.703125\0.703125"
(0028,0100) US Bits Allocated = 16
(0028,0103) US Pixel Representation = 0 (unsigned)
(7FE0,0010) OW Pixel Data = [binary pixel data]
Value Representations (VR)
| VR | Name | Description |
|---|---|---|
CS |
Code String | Short uppercase string (max 16 chars): "CT", "MR", "M", "F" |
DA |
Date | 8-char date: "YYYYMMDD" |
DS |
Decimal String | Floating-point as string: "0.703125" |
DT |
DateTime | 14-26 char datetime string |
IS |
Integer String | Integer as string: "256" |
LO |
Long String | 64-char string (patient ID, series description) |
PN |
Person Name | "Family^Given^Middle^Prefix^Suffix" |
SQ |
Sequence | Nested dataset (for complex structures) |
UI |
Unique Identifier | UID string: "1.2.840.10008.1.2.1" |
US |
Unsigned Short | 2-byte unsigned int (0β65535) |
SS |
Signed Short | 2-byte signed int (β32768β32767) |
UL |
Unsigned Long | 4-byte unsigned int |
OW |
Other Word | Binary pixel data |
OB |
Other Byte | Binary data |
Transfer Syntaxes
The Transfer Syntax UID (0002,0010) determines byte order and compression:
| UID | Name | Byte Order | Compression |
|---|---|---|---|
| 1.2.840.10008.1.2 | Implicit VR Little Endian | Little | None (legacy) |
| 1.2.840.10008.1.2.1 | Explicit VR Little Endian | Little | None (default) |
| 1.2.840.10008.1.2.2 | Explicit VR Big Endian | Big | None (retired) |
| 1.2.840.10008.1.2.4.50 | JPEG Baseline (8-bit) | Little | JPEG lossy |
| 1.2.840.10008.1.2.4.70 | JPEG Lossless | Little | JPEG lossless |
| 1.2.840.10008.1.2.4.90 | JPEG 2000 Lossless | Little | JPEG 2000 |
| 1.2.840.10008.1.2.4.91 | JPEG 2000 Lossy | Little | JPEG 2000 |
| 1.2.840.10008.1.2.5 | RLE Lossless | Little | RLE |
Explicit VR Little Endian (1.2.840.10008.1.2.1) is the default for modern DICOM files. Implicit VR (where VR must be inferred from the data dictionary) is a legacy format still found in older archives.
CT and MRI Pixel Data
CT Hounsfield Units (HU)
CT images store pixel values in Hounsfield Units, a standardized radiodensity scale:
| Tissue | HU Range |
|---|---|
| Air | β1000 |
| Lung | β600 to β400 |
| Fat | β120 to β90 |
| Water | 0 |
| Soft tissue | +20 to +80 |
| Bone | +400 to +1000 |
| Dense bone/metal | +1000 to +3000 |
Pixel values are stored as 12 or 16-bit integers. The DICOM tags (0028,1052) Rescale Intercept and (0028,1053) Rescale Slope convert stored values to HU:
HU = stored_pixel_value Γ RescaleSlope + RescaleIntercept
Typical: slope=1, intercept=β1024 β stored value 1024 = 0 HU (water).
Window Center and Width
Displaying HU-range data on 8-bit screens requires windowing:
(0028,1050) Window Center: The HU value mapped to 50% brightness(0028,1051) Window Width: The HU range mapped to 0β100% brightness
Standard window presets:
- Abdomen: W=350, C=40
- Lung: W=1500, C=β600
- Brain: W=80, C=40
- Bone: W=2000, C=300
DICOM Networking (DIMSE Services)
DICOM is also a network protocol. The DIMSE (DICOM Message Service Element) defines services:
- C-STORE: Send an image from one system to another
- C-FIND: Query for studies/series/instances (like a database query)
- C-MOVE: Request a remote system to send images to a third system
- C-GET: Request images directly (pull from server)
- C-ECHO: Ping (verify connection)
Each DICOM network entity is an AE (Application Entity) identified by its AE Title (up to 16 characters). DICOM runs over TCP/IP, typically port 104 (or 11112 for non-root).
WADO-RS (RESTful DICOM)
The modern API for DICOM retrieval is WADO-RS (Web Access to DICOM Objects, RESTful):
# Retrieve a study
GET /wado/rs/studies/{StudyInstanceUID}
# Retrieve a series
GET /wado/rs/studies/{StudyUID}/series/{SeriesUID}
# Retrieve a specific instance
GET /wado/rs/studies/{StudyUID}/series/{SeriesUID}/instances/{SOPInstanceUID}
# Retrieve rendered JPEG (thumbnail)
GET /wado/rs/studies/{StudyUID}/series/{SeriesUID}/instances/{SOPInstanceUID}/rendered?accept=image/jpeg
Working with DICOM Files
pydicom (Python)
import pydicom
import numpy as np
import matplotlib.pyplot as plt
# Load a DICOM file
ds = pydicom.dcmread('CT_slice.dcm')
# Access metadata
print(ds.PatientName) # "Doe^John"
print(ds.Modality) # "CT"
print(ds.StudyDate) # "20240315"
print(ds.Rows, ds.Columns) # 512, 512
print(ds.PixelSpacing) # [0.703125, 0.703125]
# Get pixel array (applies RescaleSlope/Intercept)
pixel_array = ds.pixel_array # raw stored values
hu_array = pixel_array * ds.RescaleSlope + ds.RescaleIntercept # HU
# Apply windowing and display
window_center = 40 # abdomen
window_width = 350
img_min = window_center - window_width / 2
img_max = window_center + window_width / 2
windowed = np.clip(hu_array, img_min, img_max)
windowed = (windowed - img_min) / (img_max - img_min) * 255
plt.imshow(windowed, cmap='gray')
plt.axis('off')
plt.savefig('ct_slice.png', bbox_inches='tight', pad_inches=0)
Converting DICOM to PNG/JPEG
import pydicom
from PIL import Image
import numpy as np
def dicom_to_png(dcm_path, png_path, window_center=40, window_width=400):
ds = pydicom.dcmread(dcm_path)
arr = ds.pixel_array.astype(float)
# Apply rescale
slope = getattr(ds, 'RescaleSlope', 1)
intercept = getattr(ds, 'RescaleIntercept', 0)
arr = arr * slope + intercept
# Window
img_min = window_center - window_width / 2
img_max = window_center + window_width / 2
arr = np.clip(arr, img_min, img_max)
arr = ((arr - img_min) / (img_max - img_min) * 255).astype(np.uint8)
Image.fromarray(arr).save(png_path)
dicom_to_png('CT_slice.dcm', 'output.png')
DCMTK Command-Line Tools
# Display DICOM tags
dcmdump CT_slice.dcm | head -50
# Convert DICOM to PNG (with auto windowing)
dcm2pnm --write-png CT_slice.dcm output.png
# Anonymize DICOM (remove patient data)
dcmodify -ie "(0010,0010)" -ie "(0010,0020)" -ie "(0010,0030)" CT_slice.dcm
# Change transfer syntax (compress with JPEG lossless)
dcmcjpeg CT_slice.dcm CT_compressed.dcm
Converting to NIfTI (Neuroimaging)
# dcm2niix β converts DICOM series to NIfTI for neuroimaging analysis
dcm2niix -z y -f "scan_%p_%t" -o ./output ./dicom_series/
Privacy and Anonymization
DICOM files contain extensive PHI (Protected Health Information). Before sharing DICOM files for research, education, or development, always anonymize:
Required removals (HIPAA Safe Harbor):
- Patient name, ID, birth date, address
- Study date (replace with days-since-epoch or offset)
- Institution name, physician name
- Device serial numbers
- UIDs (replace with new generated UIDs)
Tools: DICOM Confidentiality Option (PS3.15), pydicom deidentify(), DCMTK dcmodify, Horos/OsiriX DICOM anonymizer, CTP (Clinical Trials Processor).
DICOM Viewers
| Tool | Platform | Use Case |
|---|---|---|
| Horos | macOS | Clinical review, 3D reconstruction |
| OsiriX | macOS | Same (Horos fork) |
| 3D Slicer | Cross-platform | Research, segmentation, 3D visualization |
| Weasis | Cross-platform (Java) | Web/desktop PACS viewer |
| dwv | Browser | JavaScript DICOM viewer |
| MicroDicom | Windows | Simple viewer |
| RadiAnt | Windows | Fast clinical viewer |
DICOM's four-decade legacy as the universal medical imaging language β combined with active standardization β makes it essential knowledge for anyone working in health informatics, medical AI, or imaging system integration.