pydicom 是做什么的?
使用 pydicom 进行 DICOM 数据集 I/O 和像素处理。版本 3.0.2 是
当前审查的稳定版本。它修复了 CVE-2026-32711,一个精心构造的
DICOMDIR 路径遍历问题。pydicom 3.0.2 声明 Python >=3.10;其
捆绑的 DICOM 字典是 2024c 版本,而在线 DICOM 标准可能更新。
强制性安全边界
- 仅处理用户已获授权访问的本地数据。
- DICOM 元数据、文件名、私有元素、覆盖图、结构化内容 和像素数据可能包含受保护健康信息 (PHI)。
- 默认情况下,切勿打印
Dataset、导出完整元数据/JSON 或记录元素值。 使用已记录的允许列表和聚合输出。 - pydicom 是一个通用的 DICOM 框架,而非诊断查看器。像素输出、 验证、转换和插件可用性均非诊断用途。
- 去标识化依赖于配置文件、目的、接收者、管辖区和 威胁上下文。需要隐私/DICOM 专家验证。
- 切勿声称标签移除脚本符合 DICOM PS3.15、HIPAA、GDPR 或其他 合规性。保留原始文件并审计派生输出。
- 将确定性假名化密钥和 UID 映射视为重新标识秘密: 使用最小权限和加密/托管秘密存储,切勿提交、同步、记录或 与派生数据共享,并定义备份、轮换、撤销和销毁程序。泄漏的密钥会 破坏预期的分离;轮换也会改变确定性映射。
- 在解析不受信任或异常大的数据集之前,设置明确的输入文件、文件计数、 帧计数、已解码字节和输出限制。
安装
创建或激活一个隔离的环境,然后安装已审查的确切版本:
uv pip install "pydicom==3.0.2"
未压缩像素数组和图像渲染:
uv pip install "pydicom==3.0.2" "numpy==2.5.1" "Pillow==12.3.0"
仅安装部署所需的传输语法插件:
# 通过 pylibjpeg 进行 JPEG/JPEG-LS、JPEG 2000/HTJ2K 和更快的 RLE
uv pip install "numpy==2.5.1" "pylibjpeg==2.1.0" \
"pylibjpeg-libjpeg==2.4.0" "pylibjpeg-openjpeg==2.5.0" \
"pylibjpeg-rle==2.2.0"
# JPEG-LS 编码器/解码器
uv pip install "numpy==2.5.1" "pyjpegls==1.5.1"
# 具有平台特定 wheel 的替代解码器
uv pip install "python-gdcm==3.2.6"
插件的许可证和 wheel 因包/平台而异;在部署前请检查它们。 Pillow 存在已知的解码限制,并且 pydicom 警告插件输出必须独立检查。
本地编解码器 wheel 扩大了供应链和内存安全边界。对于 受控部署,请在受信任的构建主机上解析这些确切的固定版本,锁定 并验证 wheel 哈希/来源,在内部镜像批准的工件,扫描 它们,并使用哈希强制安装,而不是在运行时从公共索引解析。
选择工作流
- 需要聚合概览:运行
scripts/extract_metadata.py。 - 需要有限的技术检查:运行
scripts/dicom_inventory.py。 - 需要编解码器部署预检:运行
scripts/transfer_syntax_inspector.py。 - 需要帧/内存规划:运行
scripts/pixel_frame_planner.py。 - 需要一个非诊断渲染帧:运行
scripts/dicom_to_image.py。 - 需要一个假名化派生数据:阅读去标识化部分,创建
一个经站点审查的操作配置文件,然后运行
scripts/anonymize_dicom.py和scripts/deidentification_audit.py。 - 需要检查敏感的 UID 映射:运行
scripts/uid_mapping_validator.py。
安全读取数据集
dcmread() 返回一个 FileDataset,它是 Dataset 的子类,包含文件格式
状态,例如 file_meta、前导码和原始编码。
from pathlib import Path
import pydicom
path = Path("authorized/input.dcm")
ds = pydicom.dcmread(
path,
stop_before_pixels=True,
specific_tags=[
"SOPClassUID",
"Modality",
"Rows",
"Columns",
"NumberOfFrames",
],
)
technical = {
"sop_class": ds.get("SOPClassUID"),
"modality": ds.get("Modality"),
"rows": ds.get("Rows"),
"columns": ds.get("Columns"),
}
使用:
- 对于仅元数据的工作,使用
stop_before_pixels=True。 - 对于最小允许列表,使用
specific_tags=[...]。 - 当后续写入必须保留大值时,使用
defer_size="1 MiB"。 - 使用
force=False(默认)。force=True仅绕过文件格式头 检查;它不能证明字节是有效的 DICOM。
不要在临床数据上调用 print(ds)、repr(ds) 或将值迭代记录到日志中。
Dataset、DataElement 和序列
通过关键字访问标准元素并检查是否存在:
modality = ds.get("Modality", "UNSPECIFIED")
if "ReferencedImageSequence" in ds:
for item in ds.ReferencedImageSequence:
referenced_class = item.get("ReferencedSOPClassUID")
标签访问,例如 ds[0x0010, 0x0010],返回一个 DataElement;其 .value
是分开的。Sequence 类似于嵌套 Dataset 项目的列表。隐私操作
必须递归遍历每个序列项,而不仅仅是顶层。
创建文件时,对组 0002 使用 FileMetaDataset,保持数据集和
文件元 SOP UID 一致,设置传输语法 UID,并在强制文件格式下写入:
from pydicom import dcmwrite
from pydicom.dataset import FileDataset, FileMetaDataset
from pydicom.uid import CTImageStorage, ExplicitVRLittleEndian, generate_uid
meta = FileMetaDataset()
meta.MediaStorageSOPClassUID = CTImageStorage
meta.MediaStorageSOPInstanceUID = generate_uid()
meta.TransferSyntaxUID = ExplicitVRLittleEndian
ds = FileDataset(None, {}, file_meta=meta, preamble=b"\0" * 128)
ds.SOPClassUID = meta.MediaStorageSOPClassUID
ds.SOPInstanceUID = meta.MediaStorageSOPInstanceUID
# 在写入之前添加所选 IOD 要求的所有属性。
dcmwrite("new.dcm", ds, enforce_file_format=True, overwrite=False)
write_like_original 在 pydicom 3.0 中已弃用;请使用
enforce_file_format。成功的写入不代表完整的 PS3.3 IOD 一致性。
UID 和传输语法
文件元信息传输语法 UID 控制数据集编码和 像素压缩:
ts = ds.file_meta.TransferSyntaxUID
summary = {
"uid": str(ts),
"name": ts.name,
"compressed": ts.is_compressed,
"implicit_vr": ts.is_implicit_VR,
"little_endian": ts.is_little_endian,
}
pydicom 3.0 在旧的 dataset 标志之前从传输语法 UID 选择写入编码。 在假名化期间请勿替换结构性 UID(传输语法、SOP 类或 编码方案 UID)。实例/引用 UID 替换 必须在完整声明的范围内是一对一且一致的。
在压缩、解压缩或封装之前,请阅读 references/transfer_syntaxes.md。
像素数据和帧
稳定的 pydicom.pixels API 支持基于路径的、帧特定的解码:
from pydicom.pixels import pixel_array
# 仅在源允许的情况下读取所选帧。
frame = pixel_array("authorized/image.dcm", index=0, raw=False)
形状语义:
- 灰度单帧:
(rows, columns) - 灰度多帧:
(frames, rows, columns) - 彩色单帧:
(rows, columns, samples) - 彩色多帧:
(frames, rows, columns, samples)
raw=False 在可能的情况下将 YCbCr 像素数据转换为 RGB;raw=True 保留
在强制性最小处理后的解码色彩空间。使用
iter_pixels(path, indices=[...]) 进行有限的多帧迭代。
对于灰度显示,按此顺序应用变换:
from pydicom.pixels import apply_modality_lut, apply_voi_lut
modality_values = apply_modality_lut(frame, ds)
display_values = apply_voi_lut(modality_values, ds, index=0)
模态 LUT/重缩放和 VOI/窗宽窗位改变显示/值语义。
MONOCHROME1 可能需要显示反转。调色板颜色需要
apply_color_lut()。呈现状态和 ICC 行为可能需要一个经过验证的
查看器。切勿对定量分析使用逐帧最小/最大归一化。
压缩、解压缩和封装
- 访问
pixel_array会按需解码,但不会更改数据集。 Dataset.decompress()原地更改像素数据,设置 Explicit VR Little Endian,更新图像元数据,并默认生成一个新的 SOP Instance UID。Dataset.compress(uid)原地更改像素数据和传输语法,并默认 生成一个新的 SOP Instance UID。- pydicom 3.0 内置/找到的编码器覆盖 RLE Lossless、JPEG-LS 和 JPEG 2000 组合,这些组合在稳定的插件矩阵中有文档说明。
- 每个压缩帧被分别编码,然后封装。使用
encapsulate()或encapsulate_extended()处理外部编码的帧。 - 使用当前的
pydicom.encaps.generate_frames()或get_frame()读取帧; 旧式封装生成器名称在 pydicom 4 中已弃用。
始终先检查能力,限制解码字节/帧,并独立验证像素 正确性。有损压缩的可接受性不在 pydicom 和 DICOM 编码规范的范围内。
DICOM JSON 和私有元素
Dataset.to_json()、to_json_dict() 和 Dataset.from_json() 实现了
DICOM JSON 模型,但 pydicom 将 JSON 支持记录为 beta。完整的 JSON
可能内联二进制数据并暴露每个标识符和像素负载。请勿将
其作为元数据报告发出。BulkDataURI 处理程序引入了单独的存储、授权
和检索义务。
私有元素未经标准化,可能包含 PHI:
# 递归删除,但本身不足以进行去标识化。
ds.remove_private_tags()
仅在有明确审查的安全私有策略下保留私有元素。 阅读 references/common_tags.md 了解标签访问、 隐私类别和标准指针。
去标识化工作流
DICOM PS3.15 附录 E 明确指出,保密配置文件不保证 删除所有标识信息,并且不能替代完整的去标识化过程。
- 定义目的、接收者、链接需求、法规、威胁模型和 可接受的重新标识风险。
- 选择基本应用级别保密配置文件和所需选项(像素、可识别视觉特征、 图形、结构化内容、描述符、时间信息、患者特征、设备、 机构、UID 和安全私有数据)。
- 在受控存储中保持不变地保留源对象。
- 递归应用每个操作,包括嵌套序列。
- 在完整范围内一致地替换实例/引用 UID; 保留结构性 UID。
- 明确决定日期/时间处理。固定偏移可以保留间隔, 但部分日期、时区、独立时间、闰日、纵向链接 和外部事件需要经过审查的策略。
- 检查像素、覆盖图、图形、结构化内容和可识别视觉特征。
请勿从未缺失的元数据推断干净的像素,或在未经验证的情况下设置
BurnedInAnnotation=NO。 - 重建文件元信息和前导码以防止泄漏。
- 运行技术验证和去标识化审计,然后执行专家 验证和记录的风险审查。
捆绑脚本有意将 PatientIdentityRemoved 设置为 NO,因为它
无法建立成功的去标识化。
帮助器 CLI
所有 --help 路径均无依赖。这些工具不执行任何网络访问,并且
除了狭窄的技术允许列表外,不发出任何 DICOM 值。
捆绑内容包含两个链接的参考资料、文档化的帮助器脚本 和合成测试。pydicom 运行时依赖从固定的 PyPI 版本安装。
# 编辑过的聚合元数据
python scripts/extract_metadata.py authorized/ --recursive
# 仅元数据的技术清单
python scripts/dicom_inventory.py authorized/ --recursive
# 已安装编解码器/插件能力
python scripts/transfer_syntax_inspector.py --input authorized/image.dcm
# 帧形状、字节和变换计划
python scripts/pixel_frame_planner.py authorized/image.dcm --frames 0,2-4
# 一个非诊断帧
python scripts/dicom_to_image.py authorized/image.dcm frame.png \
--acknowledge-pixel-phi
# 创建一个密钥,然后创建限定了作用域的假名化派生数据及审计
python scripts/anonymize_dicom.py --generate-uid-key project.key
python scripts/anonymize_dicom.py authorized/in.dcm derived/out.dcm \
--uid-key-file project.key --uid-scope export-v1 \
--audit-report derived/out.audit.json
# 审计候选元数据;不解码像素
python scripts/deidentification_audit.py derived/out.dcm
# 验证显式请求的敏感 UID 映射
python scripts/uid_mapping_validator.py derived/uid-map.json \
--uid-key-file project.key --uid-scope export-v1
生成的原始密钥文件是一个受控的本地便利设施,并以 所有者仅权限创建。对于生产环境,请将密钥字节从批准的 密钥管理器物化到锁定的临时文件中,限制对去标识化服务的访问,并 随后安全地移除该文件。将任何可选的 UID 映射与 派生数据分开存储;它直接链接原始和替换标识符。
pydicom 3.0 迁移说明
read_file()和write_file()已被移除;请使用dcmread()和dcmwrite()。write_like_original已弃用;请使用enforce_file_format。- 计划在 v4 中移除
pydicom.pixel_data_handlers;请使用pydicom.pixels。 Dataset.pixel_array默认使用新的像素后端,并在 可能的情况下将 YCbCr 转换为 RGB。JPEGLossless现在表示 UID1.2.840.10008.1.2.4.57;JPEGLosslessSV1是.70。Dataset.is_little_endian和is_implicit_VR计划在 v4 中弃用。
来源(已验证于 2026-07-23)
- PyPI 上的 pydicom 3.0.2 — 发布于 2026-08
- pydicom 官方文档 — 参考文档
- DICOM 标准 PS3.15 — 保密配置文件
- pylibjpeg 文档 — 插件使用
许可信息
pydicom 本身根据 MIT 许可证发布。第三方插件可能具有不同的 许可证;请在使用前单独审查它们。
安全注意事项
- 始终在受控、隔离的环境中运行 pydicom,尤其是处理不受信任的 DICOM 文件时。
- 对输入数据进行大小和来源验证,以防止资源耗尽攻击。
- pydicom 3.0.2 修复了 CVE-2026-32711,但任何处理 DICOM 的代码都应定期更新以获取安全修复。
- 请勿在生产医疗或诊断环境中使用 pydicom 代替经过认证的医学图像查看器。