返回系列

DICOM 基础:从一个 .dcm 文件理解医学影像

解释 DICOM 的层级、Tag、PixelData、窗宽窗位、HU,以及 CT、PET、超声在数据形态上的差异。

问题引入

第一次接触 DICOM 时,很容易把 .dcm 当作一种医学图片格式。这个理解只对了一小半。DICOM 文件当然可以携带像素,但它更重要的是携带医学语义:这是哪位患者、哪次检查、哪组序列、哪张图像、来自什么设备、像素间距是多少、切片位置在哪里、默认窗宽窗位是什么。一个 Viewer 如果只把 PixelData 当图片显示,它很快会在序列组织、测量、MPR、PACS、导出上遇到问题。

DicomVision 的很多实现都围绕这个事实展开:先尊重 DICOM 的层级和元数据,再处理像素。因为 Viewer 打开的通常不是一张图,而是一组可以构成空间关系的实例。CT 的三维重建、MPR 的正交切面、测量中的毫米单位,全都依赖这些元数据。

背景概念

DICOM 最常见的层级是 Patient、Study、Series、Instance。Patient 是患者,Study 是一次检查,Series 是检查中的一组影像,Instance 是一张图像或一个标准对象。Viewer 通常按 Series 打开,是因为同一个 Series 内的图像通常共享 modality、扫描参数和几何关系,可以被当成一个连续序列处理。

常见字段可以分成几类:

类别典型 Tag工程意义
身份与归档PatientID、StudyInstanceUID、SeriesInstanceUID、SOPInstanceUID分组、检索、导出、引用原始对象
图像尺寸Rows、Columns、BitsStored、PixelRepresentation决定像素数组 shape 和 dtype 解释
空间几何PixelSpacing、SliceThickness、ImagePositionPatient、ImageOrientationPatient距离测量、MPR、3D 重建
显示建议WindowCenter、WindowWidth、PhotometricInterpretation默认窗宽窗位、灰度方向
像素数据PixelData、TransferSyntaxUID解码实际影像像素
设备与类型Modality、Manufacturer、SeriesDescription区分 CT、MR、PT、US、SR、PR 等对象

CT 图像的像素通常要通过 RescaleSlope 和 RescaleIntercept 转成 HU,也就是 Hounsfield Unit。空气接近 -1000 HU,水接近 0 HU,骨骼可以到几百甚至更高。窗宽窗位不是简单调亮度,而是选择一段 HU 范围映射到 0 到 255 灰度。肺窗、骨窗、纵隔窗本质上是在看不同组织的 HU 区间。

项目实现

DicomVision 加载文件时不会立刻把所有像素读进内存。它先读取 header,判断是否是可显示的普通像素影像,再根据 SeriesInstanceUID、目录分区和 4D phase 信息分组。这样做的原因是很现实的:一个 CT 序列可能有几百张图,如果扫描阶段就解码每张 PixelData,会让打开目录的等待时间和内存占用都显著上升。

当用户真正打开某个序列并需要渲染时,后端才通过 dataset.pixel_array 解码像素。CT 像素会转成 float32,再应用 slope 和 intercept。对于 PhotometricInterpretation == "MONOCHROME1" 的图像,项目会反相,保证后续渲染路径仍然遵循“数值越大显示越亮”的模型。

CT、PET、超声在 Viewer 中的困难不同。CT 切片更规则,适合堆成 (z, y, x) 体数据。PET 通常涉及 SUV、衰减校正、彩色 LUT 和 PET/CT 融合。超声常见多帧、彩色、多普勒、私有标签和非规则采集,不能简单套用 CT 的 MPR 思路。DicomVision 当前最成熟的主线是 CT 类体数据,其他 modality 可以显示一部分,但需要按数据特点逐步增强。

关键函数表

概念相关字段或函数在 Viewer 中的作用
序列归属SeriesInstanceUID把多个 Instance 归入同一可打开序列
唯一实例SOPInstanceUID缓存、导出和标准对象引用
像素解码Dataset.pixel_array从 DICOM 编码中得到 NumPy 数组
HU 转换RescaleSlope、RescaleIntercept把原始像素值转为物理/近物理值
灰度方向PhotometricInterpretation处理 MONOCHROME1 的反相
物理距离PixelSpacing把像素距离转换为毫米
切片姿态ImagePositionPatient、ImageOrientationPatient建立体数据几何和 MPR 坐标

自绘图表

Patient
<rect x="390" y="130" width="200" height="60" rx="14" fill="var(--surface)" stroke="var(--line)" />
<text x="490" y="166" text-anchor="middle" fill="currentColor" font-weight="800">Study</text>

<rect x="170" y="250" width="180" height="60" rx="14" fill="var(--surface)" stroke="var(--line)" />
<text x="260" y="286" text-anchor="middle" fill="currentColor" font-weight="800">Series A</text>
<rect x="400" y="250" width="180" height="60" rx="14" fill="var(--surface)" stroke="var(--line)" />
<text x="490" y="286" text-anchor="middle" fill="currentColor" font-weight="800">Series B</text>
<rect x="630" y="250" width="180" height="60" rx="14" fill="var(--surface)" stroke="var(--line)" />
<text x="720" y="286" text-anchor="middle" fill="currentColor" font-weight="800">Series C</text>

<text x="260" y="350" text-anchor="middle" fill="var(--muted)" font-size="14">Instance 1..N</text>
<text x="490" y="350" text-anchor="middle" fill="var(--muted)" font-size="14">Instance 1..N</text>
<text x="720" y="350" text-anchor="middle" fill="var(--muted)" font-size="14">Instance 1..N</text>

<line x1="490" y1="90" x2="490" y2="130" stroke="var(--accent)" stroke-width="3" marker-end="url(#arrow-dicom-tree)" />
<line x1="490" y1="190" x2="260" y2="250" stroke="var(--accent)" stroke-width="3" marker-end="url(#arrow-dicom-tree)" />
<line x1="490" y1="190" x2="490" y2="250" stroke="var(--accent)" stroke-width="3" marker-end="url(#arrow-dicom-tree)" />
<line x1="490" y1="190" x2="720" y2="250" stroke="var(--accent)" stroke-width="3" marker-end="url(#arrow-dicom-tree)" />
<text x="490" y="222" text-anchor="middle" fill="var(--muted)" font-size="14">Viewer 通常按 Series 打开</text>
DICOM 的可显示影像通常以 Series 为工作单元,一组 Instance 才能构成连续切片。
低 HU 高 HU Window Center 把选中的 HU 区间线性映射到灰度 center - width/2 center + width/2 白 黑
窗宽窗位不是修改原始像素,而是定义显示时的 HU 到灰度映射。
Modality数据特点Viewer 中的典型难点
CT切片规则,HU 语义明确,适合 MPR 和 3D几何排序、spacing、窗宽窗位、体数据内存
PET常见 SUV、彩色 LUT、与 CT 融合标准化摄取值、配准、双模态融合显示
US多帧、彩色、多普勒、私有标签较多多帧播放、彩色解释、设备私有差异

小结

理解 DICOM 的重点不是背 Tag,而是知道哪些 Tag 会改变工程实现。SeriesInstanceUID 决定分组,PixelSpacing 决定测量,ImagePositionPatient 和 ImageOrientationPatient 决定 MPR,WindowCenter 和 WindowWidth 决定默认显示,PixelData 决定真正的内存压力。后面的 pydicom、NumPy、MPR、VTK 都是在这些 DICOM 事实上继续往下走。