mcpField
A
alterlab-pydicom

alterlab-pydicom

github.com/alterlab-ieuデータ・データベース7866 回インストール5 週間前に更新
リポジトリ

alterlab-pydicom について

Python の pydicom を使った DICOM 医用画像ファイルの読み込み、編集、作成を案内するスキルです。ピクセルデータとメタデータの処理に加え、形式変換、メタデータ抽出、匿名化のユーティリティも扱います。

主な機能

  1. 01DICOM データセットの読み書き
  2. 02ピクセルデータとメタデータの抽出・編集
  3. 03圧縮と解凍への対応
  4. 04DICOM を一般的な画像形式に変換
  5. 05補助スクリプトによるファイル匿名化

活用シーン

  1. 01CT、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 は、医用画像データの標準形式である DICOM ファイルを扱うための純粋な Python パッケージです。このスキルでは、ピクセルデータ、メタデータ、さまざまな圧縮形式の扱いを含む、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 を確認し、超音波画像やセカンダリキャプチャ画像を調べてください)。施設外にデータを出す場合は、deid(pydicom プロジェクト)や dicognito などの専用ツールを使い、DICOM PS3.15 Annex E(基本アプリケーションレベル機密性プロファイル)に基づくプロファイルを適用してください。また、公開前に出力をレビューしてください。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 Lossless(ネイティブ、pylibjpeg-rle、または gdcm)、JPEG-LS Lossless/Near-Lossless(pyjpegls)、JPEG 2000 Lossless/Lossy(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 スイートの一部です。

コメント