mcpField
A
alterlab-pydicom

alterlab-pydicom

github.com/alterlab-ieu数据与数据库7866 次安装5 周前更新
代码仓库

关于 alterlab-pydicom

此 Skill 指导使用 Python 的 pydicom 读取、编辑和创建 DICOM 医学影像文件,涵盖像素数据与元数据,并提供格式转换、元数据提取和匿名化工具。

主要功能

  1. 01读取和写入 DICOM 数据集
  2. 02提取并修改像素数据和元数据
  3. 03处理压缩与解压
  4. 04将 DICOM 转换为常见图像格式
  5. 05使用辅助脚本匿名化文件

适用场景

  1. 01检查 CT、MRI 或 X 射线 DICOM 文件
  2. 02为影像工作流提取元数据
  3. 03将扫描图像转换为 PNG 或 JPEG
  4. 04准备用于研究共享的 DICOM 文件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/alterlab-ieu/alterlab-academic-skills --skill alterlab-pydicom

安装后在 Claude Code 中输入/alterlab-pydicom确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
common_tags.md
transfer_syntaxes.md
scripts/
anonymize_dicom.py
dicom_to_image.py
extract_metadata.py

详细介绍

Pydicom

概述

Pydicom 是一个纯 Python 软件包,用于处理 DICOM 文件。DICOM 是医学影像数据的标准格式。本技能提供读取、写入和操作 DICOM 文件的指导,包括处理像素数据、元数据和各种压缩格式。

何时使用此技能

在以下情况下使用此技能:

  • 处理医学影像文件(CT、MRI、X 射线、超声、PET 等)
  • 提取或修改元数据的 DICOM 数据集
  • 从医学扫描中提取像素数据并进行图像处理
  • 为研究或数据共享对 DICOM 进行匿名化处理
  • 将 DICOM 文件转换为标准图像格式
  • 需要解压缩的压缩 DICOM 数据
  • DICOM 序列和结构化报告
  • 多层切片体积重建
  • PACS(医学影像存档与通信系统)集成
不适用的情况
场景改用
查找并下载公开的癌症影像集合(NCI Imaging Data Commons)alterlab-imaging-data-commons
对全切片病理图像(H&E WSI)进行切片或预处理alterlab-histolab
使用临床影像或 EHR 数据集训练 ML 模型alterlab-pyhealth
处理 ECG/EEG/EDA 生理波形alterlab-neurokit2

安装

适用于 pydicom 3.x(截至 2026-09 的当前版本为 3.0.2;Python >= 3.10)。3.0 重新组织了像素 API,详见下方的版本说明。安装 pydicom 和常用依赖项:

uv pip install "pydicom>=3.0"
uv pip install pillow  # For image format conversion
uv pip install numpy   # For pixel array manipulation
uv pip install matplotlib  # For visualization

处理压缩 DICOM 文件时,可能需要安装其他软件包:

uv pip install pylibjpeg pylibjpeg-libjpeg pylibjpeg-openjpeg  # JPEG / JPEG 2000 decode; JPEG 2000 encode
uv pip install pylibjpeg-rle  # faster RLE encode/decode (pydicom's own RLE codec also works)
uv pip install python-gdcm  # Alternative decoder for most compressed syntaxes
uv pip install pyjpegls     # JPEG-LS encode/decode
pydicom 3.0 API 变更(重要)

3.0 将所有像素处理功能移至 pydicom.pixels 模块,并弃用了一些长期使用的 API(旧名称仍可用,但会发出警告,并将在 4.0 中移除):

  • from pydicom.pixel_data_handlers.util import apply_voi_lut → from pydicom.pixels import apply_voi_lut(convert_color_space 同理)。
  • pydicom.encoders → pydicom.pixels.encoders。
  • ds.save_as(path, write_like_original=False) → ds.save_as(path, enforce_file_format=True)(write_like_original=True 已成为默认行为,无需再显式设置)。
  • 新增辅助方法:ds.set_pixel_data(arr, photometric_interpretation, bits_stored) 会写入 Pixel Data 及对应的 Image Pixel 元素;ds.compress() / ds.decompress() 可在不同传输语法之间转换。

以下示例均使用 3.x API。

核心工作流

读取 DICOM 文件

使用 pydicom.dcmread() 读取 DICOM 文件:

import pydicom

# Read a DICOM file
ds = pydicom.dcmread('path/to/file.dcm')

# Access metadata
print(f"Patient Name: {ds.PatientName}")
print(f"Study Date: {ds.StudyDate}")
print(f"Modality: {ds.Modality}")

# Display all elements
print(ds)

要点:

  • dcmread() 返回一个 Dataset 对象
  • 使用属性表示法(例如 ds.PatientName)或标签表示法(例如 ds[0x0010, 0x0010])访问数据元素
  • 使用 ds.file_meta 访问文件元数据,例如 Transfer Syntax UID
  • 使用 getattr(ds, 'AttributeName', default_value) 或 hasattr(ds, 'AttributeName') 处理缺失的属性
处理像素数据

从 DICOM 文件中提取并操作图像数据:

import pydicom
import numpy as np
import matplotlib.pyplot as plt

# Read DICOM file
ds = pydicom.dcmread('image.dcm')

# Get pixel array (requires numpy)
pixel_array = ds.pixel_array

# Image information
print(f"Shape: {pixel_array.shape}")
print(f"Data type: {pixel_array.dtype}")
print(f"Rows: {ds.Rows}, Columns: {ds.Columns}")

# Apply windowing for display (CT/MRI)
if hasattr(ds, 'WindowCenter') and hasattr(ds, 'WindowWidth'):
    from pydicom.pixels import apply_voi_lut  # pydicom 3.x location
    windowed_image = apply_voi_lut(pixel_array, ds)
else:
    windowed_image = pixel_array

# Display image
plt.imshow(windowed_image, cmap='gray')
plt.title(f"{ds.Modality} - {ds.StudyDescription}")
plt.axis('off')
plt.show()

处理彩色图像:

# RGB images have shape (rows, columns, 3)
if ds.PhotometricInterpretation == 'RGB':
    rgb_image = ds.pixel_array
    plt.imshow(rgb_image)
elif ds.PhotometricInterpretation == 'YBR_FULL':
    from pydicom.pixels import convert_color_space  # pydicom 3.x location
    rgb_image = convert_color_space(ds.pixel_array, 'YBR_FULL', 'RGB')
    plt.imshow(rgb_image)

多帧图像(视频/序列):

# For multi-frame DICOM files
if hasattr(ds, 'NumberOfFrames') and ds.NumberOfFrames > 1:
    frames = ds.pixel_array  # Shape: (num_frames, rows, columns)
    print(f"Number of frames: {frames.shape[0]}")

    # Display specific frame
    plt.imshow(frames[0], cmap='gray')
将 DICOM 转换为图像格式

使用提供的 dicom_to_image.py 脚本,也可以手动转换:

from PIL import Image
import pydicom
import numpy as np

ds = pydicom.dcmread('input.dcm')
pixel_array = ds.pixel_array

# Normalize to 0-255 range
if pixel_array.dtype != np.uint8:
    pixel_array = ((pixel_array - pixel_array.min()) /
                   (pixel_array.max() - pixel_array.min()) * 255).astype(np.uint8)

# Save as PNG
image = Image.fromarray(pixel_array)
image.save('output.png')

使用脚本:python scripts/dicom_to_image.py input.dcm output.png

修改元数据

修改 DICOM 数据元素:

import pydicom
from datetime import datetime

ds = pydicom.dcmread('input.dcm')

# Modify existing elements
ds.PatientName = "Doe^John"
ds.StudyDate = datetime.now().strftime('%Y%m%d')
ds.StudyDescription = "Modified Study"

# Add new elements
ds.SeriesNumber = 1
ds.SeriesDescription = "New Series"

# Remove elements
if hasattr(ds, 'PatientComments'):
    delattr(ds, 'PatientComments')
# Or using del
if 'PatientComments' in ds:
    del ds.PatientComments

# Save modified file
ds.save_as('modified.dcm')
匿名化 DICOM 文件

移除或替换患者身份信息:

import pydicom
from datetime import datetime

ds = pydicom.dcmread('input.dcm')

# Tags commonly containing PHI (Protected Health Information)
tags_to_anonymize = [
    'PatientName', 'PatientID', 'PatientBirthDate',
    'PatientSex', 'PatientAge', 'PatientAddress',
    'InstitutionName', 'InstitutionAddress',
    'ReferringPhysicianName', 'PerformingPhysicianName',
    'OperatorsName', 'StudyDescription', 'SeriesDescription',
]

# Remove or replace sensitive data
for tag in tags_to_anonymize:
    if hasattr(ds, tag):
        if tag in ['PatientName', 'PatientID']:
            setattr(ds, tag, 'ANONYMOUS')
        elif tag == 'PatientBirthDate':
            setattr(ds, tag, '19000101')
        else:
            delattr(ds, tag)

# Vendor private tags frequently carry identifiers
ds.remove_private_tags()

# Replace dates consistently (e.g. one per-patient offset) to keep intervals meaningful
if hasattr(ds, 'StudyDate'):
    ds.StudyDate = '20000101'

ds.PatientIdentityRemoved = 'YES'

# Keep pixel data intact
ds.save_as('anonymized.dcm')

使用提供的脚本:python scripts/anonymize_dicom.py input.dcm output.dcm

这样的标签列表只是起点,并不能完成彻底去标识化。其他日期/时间元素、UID、登记号、嵌套序列、自由文本字段以及烧录到像素中的文本,也可能包含身份标识信息(请检查 BurnedInAnnotation,并检查超声图像和二次采集图像)。如果要将数据带离所在机构,请采用基于 DICOM PS3.15 附录 E(基本应用级保密配置文件)的配置文件,并使用 deid(pydicom 项目)或 dicognito 等专用工具处理;发布前还应审核输出内容——这是 IRB 或数据使用协议要求的同等标准。

编写 DICOM 文件

从头创建 DICOM 文件:

import pydicom
from pydicom.dataset import Dataset, FileDataset, FileMetaDataset
from datetime import datetime
import numpy as np

# Create file meta information (use FileMetaDataset, not a bare Dataset)
file_meta = FileMetaDataset()
file_meta.MediaStorageSOPClassUID = pydicom.uid.CTImageStorage
file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()
file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian

# Create the FileDataset instance
ds = FileDataset('new_dicom.dcm', {}, file_meta=file_meta, preamble=b"\0" * 128)

# Add required DICOM elements
ds.PatientName = "Test^Patient"
ds.PatientID = "123456"
ds.Modality = "CT"
ds.StudyDate = datetime.now().strftime('%Y%m%d')
ds.StudyTime = datetime.now().strftime('%H%M%S')
ds.ContentDate = ds.StudyDate
ds.ContentTime = ds.StudyTime

# Add image-specific elements
ds.SamplesPerPixel = 1
ds.PhotometricInterpretation = "MONOCHROME2"
ds.Rows = 512
ds.Columns = 512
ds.BitsAllocated = 16
ds.BitsStored = 16
ds.HighBit = 15
ds.PixelRepresentation = 0

# Create pixel data (pydicom >= 3.0 alternative for the block above and this one:
# ds.set_pixel_data(pixel_array, photometric_interpretation="MONOCHROME2", bits_stored=12))
pixel_array = np.random.randint(0, 4096, (512, 512), dtype=np.uint16)
ds.PixelData = pixel_array.tobytes()

# Add required UIDs
ds.SOPClassUID = pydicom.uid.CTImageStorage
ds.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID
ds.SeriesInstanceUID = pydicom.uid.generate_uid()
ds.StudyInstanceUID = pydicom.uid.generate_uid()

# Save the file
ds.save_as('new_dicom.dcm')
压缩与解压缩

处理压缩的 DICOM 文件:

import pydicom

# Read compressed DICOM file
ds = pydicom.dcmread('compressed.dcm')

# Check transfer syntax
print(f"Transfer Syntax: {ds.file_meta.TransferSyntaxUID}")
print(f"Transfer Syntax Name: {ds.file_meta.TransferSyntaxUID.name}")

# Decompress and save as uncompressed (Transfer Syntax becomes Explicit VR LE)
ds.decompress()
ds.save_as('uncompressed.dcm', enforce_file_format=True)

# Or compress (RLE Lossless works with numpy alone)
ds_uncompressed = pydicom.dcmread('uncompressed.dcm')
ds_uncompressed.compress(pydicom.uid.RLELossless)
ds_uncompressed.save_as('compressed_rle.dcm')

pydicom 3.0 只能编码 RLE 无损(原生、pylibjpeg-rle 或 gdcm)、JPEG-LS 无损/近无损(pyjpegls)以及 JPEG 2000 无损/有损(pylibjpeg-openjpeg)。JPEG Baseline 和 JPEG Lossless 仅支持解码——对它们调用 compress() 会引发 NotImplementedError。

常见传输语法:

  • ExplicitVRLittleEndian - 未压缩,最常见
  • JPEGBaseline8Bit - JPEG 有损压缩
  • JPEGLossless - JPEG 无损压缩
  • JPEG2000Lossless - JPEG 2000 无损压缩
  • RLELossless - 无损游程编码

完整列表请参阅 references/transfer_syntaxes.md。

处理 DICOM 序列

处理嵌套数据结构:

import pydicom

ds = pydicom.dcmread('file.dcm')

# Access sequences
if 'ReferencedStudySequence' in ds:
    for item in ds.ReferencedStudySequence:
        print(f"Referenced SOP Instance UID: {item.ReferencedSOPInstanceUID}")

# Create a sequence
from pydicom.sequence import Sequence

sequence_item = Dataset()
sequence_item.ReferencedSOPClassUID = pydicom.uid.CTImageStorage
sequence_item.ReferencedSOPInstanceUID = pydicom.uid.generate_uid()

ds.ReferencedImageSequence = Sequence([sequence_item])
处理 DICOM 序列图像

处理多个相关的 DICOM 文件:

import pydicom
import numpy as np
from pathlib import Path

# Read all DICOM files in a directory
dicom_dir = Path('dicom_series/')
slices = []

for file_path in dicom_dir.glob('*.dcm'):
    ds = pydicom.dcmread(file_path)
    slices.append(ds)

# Sort by slice location or instance number
slices.sort(key=lambda x: float(x.ImagePositionPatient[2]))
# Or: slices.sort(key=lambda x: int(x.InstanceNumber))

# Create 3D volume
volume = np.stack([s.pixel_array for s in slices])
print(f"Volume shape: {volume.shape}")  # (num_slices, rows, columns)

# Get spacing information for proper scaling
pixel_spacing = slices[0].PixelSpacing  # [row_spacing, col_spacing]
slice_thickness = slices[0].SliceThickness
print(f"Voxel size: {pixel_spacing[0]}x{pixel_spacing[1]}x{slice_thickness} mm")

辅助脚本

此技能在 scripts/ 目录中包含实用脚本:

anonymize_dicom.py

通过移除或替换受保护健康信息(PHI)来匿名化 DICOM 文件。

python scripts/anonymize_dicom.py input.dcm output.dcm
dicom_to_image.py

将 DICOM 文件转换为常见图像格式(PNG、JPEG、TIFF)。

python scripts/dicom_to_image.py input.dcm output.png
python scripts/dicom_to_image.py input.dcm output.jpg --format JPEG
extract_metadata.py

以易读的格式提取并显示 DICOM 元数据。

python scripts/extract_metadata.py file.dcm
python scripts/extract_metadata.py file.dcm --output metadata.txt

参考资料

详细参考信息见 references/ 目录:

  • common_tags.md:按类别(患者、检查、序列、图像等)整理的常用 DICOM 标签详尽列表
  • transfer_syntaxes.md:DICOM 传输语法和压缩格式的完整参考

常见问题与解决方案

问题:“Unable to decode pixel data”

  • 解决方案:安装其他压缩处理程序:uv pip install pylibjpeg pylibjpeg-libjpeg python-gdcm

问题:访问标签时出现“AttributeError”

  • 解决方案:使用 hasattr(ds, 'AttributeName') 检查属性是否存在,或使用 ds.get('AttributeName', default)

问题:图像显示不正确(过暗/过亮)

  • 解决方案:应用 VOI LUT 窗宽调节:apply_voi_lut(pixel_array, ds),或使用 WindowCenter 和 WindowWidth 手动调整

问题:大型序列出现内存问题

  • 解决方案:逐个处理文件、使用内存映射数组,或对图像进行降采样

最佳实践

  1. 访问必需属性前务必检查其是否存在,使用 hasattr() 或 get()
  2. 修改文件时保留原始编码:直接使用 ds.save_as(path) 会保留源布局(pydicom 3.x 默认采用此行为);仅在需要完全符合标准的 File Format 输出时才传入 enforce_file_format=True
  3. 处理像素数据前,使用传输语法 UID 了解压缩格式
  4. 读取不可信来源的文件时处理异常
  5. 进行医学图像可视化时应用适当的窗宽调节(VOI LUT)
  6. 处理 3D 体数据时保留空间信息(像素间距、层厚)
  7. 共享医学数据前彻底验证匿名化结果(包括私有标签、烧录文本、UID 和日期)
  8. 正确使用 UID——创建新实例时生成新的 UID,修改实例时保留原 UID

文档

pydicom 官方文档(stable = 当前 3.x 版本):https://pydicom.github.io/pydicom/stable/

AlterLab Academic Skills 套件的一部分。

评论

支持 Markdown