2. DICOM影像数据解析与预处理
在PACS影像AI在线推理链路中,DICOM解析与预处理是决定模型输入质量的第一道关口。模型最终看到的并不是PACS里那个.dcm文件,而是一个归一化后的像素张量。如果解析阶段读错了像素、窗宽窗位映射不一致、多帧序列顺序错乱,后续推理结果再优化也无法挽回。本节围绕DICOM文件结构、元数据读取、像素解码、窗宽窗位调整、多帧多序列处理以及常见伪影与预处理策略展开。
一、DICOM文件结构:从文件头到像素数据
一个标准DICOM文件由文件前言、数据集和像素数据三部分组成。文件前言共128字节,通常为全零,其后紧跟4字节的“DICM”标识,用于快速判断是否为DICOM文件。数据集由一系列数据元素(Data Element)构成,每个元素包含标签(Tag)、值表示(VR)、值长度(Value Length)和值域(Value Field)。标签由组号(Group)和元素号(Element)组成,例如(0028,0010)表示图像行数Rows,(0028,0011)表示列数Columns,(7FE0,0010)则是像素数据Pixel Data。
传输语法(Transfer Syntax UID,标签(0002,0010))决定了像素数据是否压缩、字节序如何排列。常见的有隐式VR小端(1.2.840.10008.1.2)、显式VR小端(1.2.840.10008.1.2.1)、JPEG Baseline、JPEG Lossless、JPEG 2000等。解析器必须先读取传输语法,才能正确解码后续数据。若忽略这一点,把压缩像素当作原始像素读取,会得到完全错误的图像。
二、元数据读取与像素数据解码
元数据读取的核心是正确解析数据元素。以Python为例,常用pydicom库:
import pydicom
import numpy as np
ds = pydicom.dcmread("CT_001.dcm")
print(ds.TransferSyntaxUID) # 传输语法
print(ds.Rows, ds.Columns) # 图像尺寸
print(ds.BitsAllocated, ds.BitsStored, ds.HighBit)
print(ds.PixelRepresentation) # 0=无符号, 1=有符号
print(ds.RescaleSlope, ds.RescaleIntercept)
print(ds.WindowCenter, ds.WindowWidth)
# 解码像素数据为numpy数组
pixel_array = ds.pixel_array.astype(np.float32)
# 应用Rescale Slope/Intercept,转为物理量(如CT的HU值)
hu = pixel_array * float(ds.RescaleSlope) + float(ds.RescaleIntercept)
这里有一个极易出错的点:BitsStored与HighBit决定了有效位的位置。例如BitsAllocated=16、BitsStored=12、HighBit=11时,有效数据位于低12位,高4位可能是填充位。若直接按16位读取而不做位移或掩码,会引入错误的高位噪声。此外,PixelRepresentation=1表示有符号像素,解码后需按有符号整数解释,否则负值会被误读为大正数。
三、窗宽窗位调整
医学影像的原始像素动态范围通常为12~16位,而显示器和多数模型输入为8位。窗宽(Window Width, WW)和窗位(Window Center, WC)定义了将原始值映射到显示灰度的区间。映射公式为:
下限 = WC − WW/2,上限 = WC + WW/2
将原始值线性截断并缩放到0~255:
def apply_window(hu, wc, ww):
lower = wc - ww / 2
upper = wc + ww / 2
windowed = np.clip(hu, lower, upper)
windowed = (windowed - lower) / (upper - lower) * 255.0
return windowed.astype(np.uint8)
以CT为例,肺窗常用WC=−600、WW=1500,用于观察肺实质;纵隔窗常用WC=40、WW=400,用于观察软组织与血管;骨窗常用WC=400、WW=1800。同一份CT数据,选择不同窗宽窗位,模型看到的输入分布差异巨大。如果AI模型训练时使用的是肺窗,推理时却用了纵隔窗,性能会显著下降。因此,预处理阶段必须与模型训练时的窗宽窗位策略严格对齐,或采用多窗融合、自适应窗等策略。
四、多帧与多序列处理
多帧图像(如超声、X射线透视、部分MR序列)在单个DICOM文件中包含NumberOfFrames帧,像素数据按帧连续排列。解码后数组形状为(frames, rows, columns)或(frames, rows, columns, samples)。处理时需注意帧与帧之间的时间或空间顺序,不能简单打乱。
多序列则涉及同一检查下的多个Series,例如MR的T1、T2、DWI、ADC等。每个Series有独立的SeriesInstanceUID,但共享StudyInstanceUID。AI推理前需要根据SeriesDescription、ProtocolName或ImageType筛选目标序列。常见做法是:先按StudyInstanceUID分组,再按SeriesNumber排序,最后根据序列描述匹配模型所需的输入通道。例如一个脑肿瘤分割模型可能需要T1、T1ce、T2、FLAIR四个序列,预处理时必须确保四个序列的空间对齐(同一ImagePositionPatient网格)和像素间距一致。
五、常见伪影与预处理策略
DICOM数据在采集和传输过程中可能引入多种伪影,预处理阶段需要识别并处理:
- 金属伪影:CT中金属植入物导致条状伪影,HU值异常。可在预处理时对高密度区域做阈值截断,或使用金属伪影校正算法。
- 运动伪影:患者移动导致图像模糊或重影。可通过图像质量评估指标(如清晰度、信噪比)筛选,低质量帧不送入模型。
- 截断伪影:FOV外组织被截断,重建时产生亮暗条纹。可对边缘区域做加窗或填充处理。
- 灰度不均匀:MR表面线圈导致信号强度不均。可应用N4偏置场校正或简单的高斯滤波。
- 像素填充值:部分设备用
PixelPaddingValue标记无效区域,预处理时应将其掩码掉,避免参与归一化统计。
综上,DICOM解析与预处理是一个需要精细控制的工程环节。从传输语法识别、元数据校验、像素解码、Rescale映射、窗宽窗位调整,到多帧多序列的组织与伪影处理,每一步都直接影响模型输入的可靠性。在实际集成中,建议将这一环节封装为独立的预处理服务,输入DICOM文件路径或PACS查询结果,输出标准化的NIfTI或NumPy张量,并记录关键参数日志,便于问题追溯与版本对比。