3. DICOM协议与元数据工程
在医学影像数据管线中,DICOM 文件既是像素数据的载体,也是临床语义的容器。一个 CT 序列能否被正确重建、一个检查能否被准确归入某位患者、一次科研检索能否命中目标人群,几乎都取决于对 DICOM 信息模型与元数据的理解深度。本节从信息模型出发,逐层展开 SOP 类、传输语法、私有标签、去标识化标准与元数据抽取入库的工程实现。
一、DICOM 信息模型:从患者到像素的四级结构
DICOM 采用面向对象的信息模型,其核心是“实体—关系”结构。最上层是 Patient(患者),向下依次为 Study(检查)、Series(序列)、Instance(实例,即单个 SOP 实例)。四者通过唯一标识符(UID)串联:PatientID 标识患者,StudyInstanceUID 标识一次检查,SeriesInstanceUID 标识一个序列,SOPInstanceUID 标识一个实例。这种层级关系决定了数据入库时的主键设计——若把 SOPInstanceUID 当作唯一主键而忽略 Series 与 Study 的归属,后续按检查或序列聚合时就会出现重复或错配。
每个层级都携带一组属性。例如 Study 层有 StudyDate、StudyDescription、AccessionNumber;Series 层有 Modality、SeriesNumber、BodyPartExamined;Instance 层有 InstanceNumber、SOPClassUID、Rows、Columns。工程上常说的“元数据抽取”,本质就是按这四级结构把属性映射到关系表或宽表中。
二、SOP 类与传输语法:决定“怎么读”
SOP 类(Service-Object Pair Class)定义了“对象是什么”以及“能对它做什么”。例如 CT 图像存储的 SOP Class UID 为 1.2.840.10008.5.1.4.1.1.2,MR 为 1.2.840.10008.5.1.4.1.1.4,而增强型 CT 为 1.2.840.10008.5.1.4.1.1.2.1。解析时若只按 Modality 判断,会把增强型与普通型混为一谈,导致多帧数据被误当作单帧处理。
传输语法(Transfer Syntax)决定字节序、VR 编码方式与压缩算法。常见的有隐式 VR 小端(1.2.840.10008.1.2)、显式 VR 小端(1.2.840.10008.1.2.1)、显式 VR 大端(1.2.840.10008.1.2.2),以及 JPEG Baseline、JPEG Lossless、JPEG 2000 等压缩语法。工程上必须显式读取 (0002,0010) TransferSyntaxUID,再据此选择解码器;若用隐式 VR 解析器去读显式 VR 文件,标签长度会错位,后续所有属性都会偏移。
三、私有标签:厂商扩展的陷阱
私有标签位于奇数 Group 号中,由厂商自行定义,如西门子的 (0019,xx)、GE 的 (0009,xx)、飞利浦的 (2001,xx)。它们常携带重建参数、剂量信息、序列描述等关键字段,但缺乏统一语义。处理原则是:先通过 Private Creator 标签(如 (0019,0010))确认厂商,再按厂商字典解析;对无法识别的私有标签,保留原始字节并记录来源,不要强行猜测含义。入库时建议单独建一张私有标签表,以 SOPInstanceUID + 标签路径为键,避免污染主表结构。
四、去标识化:DICOM PS3.15 的工程落地
PS3.15 定义了去标识化配置文件与保密选项。基础配置文件要求移除或替换患者姓名、出生日期、病历号、机构名称、医生姓名等 30 余类属性,同时保留 StudyInstanceUID 等用于关联的 UID(或按需重新生成)。工程实现通常分三步:第一,按标签清单执行“清除/替换/保留”动作;第二,对像素数据中的烧录注释(Burned-in Annotation)进行检测,必要时用 (0028,0301) 标记;第三,对私有标签中的身份信息做二次扫描。
五、元数据抽取入库:从文件到表
抽取流程可概括为“扫描—解析—映射—写入”。以 Python 为例,使用 pydicom 读取文件后,按四级结构提取字段:
import pydicom
ds = pydicom.dcmread(path, stop_before_pixels=True)
record = {
"patient_id": ds.get("PatientID"),
"study_uid": ds.get("StudyInstanceUID"),
"series_uid": ds.get("SeriesInstanceUID"),
"sop_uid": ds.get("SOPInstanceUID"),
"modality": ds.get("Modality"),
"study_date": ds.get("StudyDate"),
"series_number": ds.get("SeriesNumber"),
"instance_number": ds.get("InstanceNumber"),
"rows": ds.get("Rows"),
"columns": ds.get("Columns"),
"transfer_syntax": ds.file_meta.TransferSyntaxUID,
}
写入时建议以 SOPInstanceUID 为唯一键做 UPSERT,同时建立 study_uid、series_uid、patient_id 的索引。对于多帧增强型对象,还需额外抽取 NumberOfFrames、FrameIncrementPointer 等字段,否则后续按帧检索会缺失粒度。
综上,DICOM 元数据工程的核心不是“读文件”,而是建立一套从信息模型到关系表的稳定映射,并在 SOP 类、传输语法、私有标签与去标识化四个维度上保持一致性。只有这四层都处理正确,后续的影像重建、检索与科研分析才有可靠的数据基础。