dicom-processing について
DICOM と NIfTI の医用画像を変換、読み取り、匿名化するワークフロー用スキルです。シリーズ単位の一括処理や BIDS ディレクトリ構成の手順も扱います。
主な機能
- 01BIDS サイドカー付き DICOM から NIfTI への変換
- 02DICOM ヘッダーとピクセルデータの読み取り
- 03PHI とプライベートタグの除去
- 04NIfTI ボリュームとアフィン行列の検査
- 05SeriesInstanceUID 単位の一括処理
活用シーン
- 01DICOM シリーズを圧縮 NIfTI に変換
- 02医用画像のヘッダーやピクセルを確認
- 03個別検査や大規模コホートの匿名化
- 04BIDS 形式で画像データセットを整理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ awslabs/ hcls-agent-skills --skill dicom-processingインストール後、Claude Code で/dicom-processingを入力して読み込まれたか確認します。
詳細
DICOM 処理
概要
DICOM および NIfTI 形式の医用画像を変換、読み込み、匿名化するためのパイプラインスキルです。以下の決定論的なコマンドとコードをまとめています。
dcm2niixを使用した DICOM → NIfTI 変換(BIDS サイドカーを含む)pydicomを使用した DICOM ヘッダーとピクセルデータの読み込みpydicomまたは CTP DicomAnonymizer を使用した PHI の削除(匿名化)nibabelを使用した NIfTI の検査SeriesInstanceUIDごとにグループ化したバッチ処理- BIDS 準拠のディレクトリ構成
ユーザーが .dcm、.nii、.nii.gz、DICOM ディレクトリ、または匿名化ワークフローを扱う場合は、このスキルを使用してください。
使用方法
1. DICOM シリーズを NIfTI に変換する(dcm2niix)
# Standard conversion: gzip output, BIDS sidecar, pattern-named files
dcm2niix -z y -b y -f %p_%s -o output_dir input_dir
フラグ(覚えておいてください):
| フラグ | 意味 |
|---|---|
-z y | gzip 形式で出力する(.nii.gz) |
-b y | BIDS JSON サイドカーを出力する |
-f %p_%s | ファイル名のパターン:%p = プロトコル、%s = シリーズ番号 |
-m n | シリーズをまたいで 2D スライスをマージしない(シリーズ間の混在を防止) |
-o | 出力ディレクトリ |
よく使う命名トークン:%p プロトコル、%s シリーズ、%t 時刻、%i 患者 ID、%n 患者名、%d 説明。
2. pydicom で DICOM ファイルを読み込む
import pydicom
ds = pydicom.dcmread('file.dcm')
print(ds.PatientName, ds.StudyDate, ds.Modality)
print(ds.pixel_array.shape) # numpy array of pixel data
3. DICOM ファイルを匿名化する(pydicom)
import pydicom
from pydicom.uid import generate_uid
ds = pydicom.dcmread('file.dcm')
tags_to_remove = [
'PatientName', 'PatientID', 'PatientBirthDate',
'InstitutionName', 'ReferringPhysicianName', 'StudyDate',
]
for tag in tags_to_remove:
if tag in ds:
del ds[tag]
ds.PatientID = 'ANON_001'
# Remove private (vendor) tags — they frequently contain PHI
ds.remove_private_tags()
# Replace UIDs so the study/series/instance cannot be linked back
ds.StudyInstanceUID = generate_uid()
ds.SeriesInstanceUID = generate_uid()
ds.SOPInstanceUID = generate_uid()
ds.save_as('anon.dcm')
4. CTP DicomAnonymizer でバッチ匿名化する
# RSNA CTP / DicomBrowser DAP (DICOM Anonymizer Program)
java -jar DAP.jar -p profile.script -i input/ -o output/
大規模なコホートや、検証済みの DICOM Supplement 142 プロファイルが必要な場合に使用してください。
5. nibabel で NIfTI ボリュームを読み込む
import nibabel as nib
img = nib.load('file.nii.gz')
data = img.get_fdata() # numpy ndarray
affine = img.affine # 4x4 voxel-to-world transform
header = img.header
6. バッチ処理のパターン:SeriesInstanceUID ごとにグループ化し、シリーズ単位で変換する
from pathlib import Path
from collections import defaultdict
import pydicom, subprocess
def group_by_series(input_dir):
series = defaultdict(list)
for p in Path(input_dir).rglob('*.dcm'):
ds = pydicom.dcmread(p, stop_before_pixels=True)
series[ds.SeriesInstanceUID].append(p)
return series
def convert_all(input_dir, output_dir):
Path(output_dir).mkdir(parents=True, exist_ok=True)
# dcm2niix handles series grouping internally when given a directory
subprocess.run(
['dcm2niix', '-z', 'y', '-b', 'y', '-f', '%p_%s',
'-o', output_dir, input_dir],
check=True,
)
7. BIDS の構成
dataset/
├── dataset_description.json
├── participants.tsv
└── sub-01/
└── ses-01/
├── anat/ sub-01_ses-01_T1w.nii.gz
├── func/ sub-01_ses-01_task-rest_bold.nii.gz
└── dwi/ sub-01_ses-01_dwi.nii.gz
ルール:sub-<label>/ses-<label>/<modality>/<sub>_<ses>_<suffix>.nii.gz。対応する .json サイドカーも必要です。
応答形式
- 必要なコマンドまたはコードを最初に示し、その後に説明する
- 次の構成にする:入力の確認 → 動作するコード → 主要パラメーターの説明 → 注意点
- タスクごとに、完全に動作する例を 1 つ示す。代替案をすべて示す必要はない
- コードコメントは最小限かつ実用的にする(存在理由ではなく、何をするかを記述する)
- 目安:コード 50~100 行と、簡潔な説明
基本概念
形式と匿名化の判断ツリー
What is the target format?
├─ NIfTI (analysis) → dcm2niix -z y -b y
│ ├─ Need BIDS layout? → add -f sub-%i_ses-%t_%p, reorganize post-hoc
│ └─ Single series only? → point dcm2niix at series directory directly
└─ DICOM (archive/PACS) → pydicom read/write
De-identification method:
├─ Small cohort (< 100 studies) → pydicom script (remove tags + private + replace UIDs)
├─ Large cohort or regulatory → CTP DicomAnonymizer with Supplement 142 profile
└─ Burned-in pixel PHI risk?
├─ US / Secondary Capture / Screenshots → YES: add OCR + pixel masking step
└─ CT / MR / PT → Usually NO: tag-level de-ID sufficient
DICOM の階層。 Patient → Study → Series → Instance。「シリーズ」は変換の基本単位であり、通常、1 つの NIfTI ボリュームは 1 つの SeriesInstanceUID に対応します。
UID は識別子であり、秘密情報ではありませんが、相互に関連付けることができます。 StudyInstanceUID を共有する 2 つのファイルは、同じ検査に由来します。匿名化では UID を置き換え(generate_uid())、リリース間の関連付けを断つ必要があります。
プライベートタグには PHI が含まれることがあります。 ベンダー固有のタグ(グループ番号が奇数のもの)には、担当者のメモ、患者 ID、焼き込み済みのメタデータが含まれていることがよくあります。必ず ds.remove_private_tags() を呼び出してください。
ピクセルデータにも PHI が含まれることがあります。 超音波フレーム、セカンダリーキャプチャ、スクリーンショットでは、患者名や MRN がピクセルに焼き込まれていることがよくあります。タグを削除してもピクセルは消去されません。このリスクがある場合は、別途 OCR とマスキングを実行してください。
ジオメトリとモダリティに関する主要な DICOM タグ:
| タグ | 名前 | 用途 |
|---|---|---|
(0008,0060) | Modality | CT / MR / CR / US / PT ... |
(0018,0050) | SliceThickness | mm |
(0028,0030) | PixelSpacing | 面内間隔 [row, col] mm |
(0020,0032) | ImagePositionPatient | 患者座標におけるスライスの原点 |
(0020,0037) | ImageOrientationPatient | 行/列の方向余弦 |
これら 5 つのタグで、NIfTI の affine 行列を再構築できます。
NIfTI と DICOM の違い。 NIfTI は 1 つのアフィン変換を持つ単一の 3D/4D ボリュームを保存します。DICOM はスライス単位のメタデータを保存します。変換ツール(dcm2niix)はシリーズ内のスライスを集約し、上記のタグからアフィン変換を計算します。
クイックリファレンス
# Convert
dcm2niix -z y -b y -f %p_%s -o out/ in/
# Skip merging across series (safer)
dcm2niix -z y -b y -m n -f %p_%s -o out/ in/
# Batch anonymization (CTP)
java -jar DAP.jar -p profile.script -i input/ -o output/
# Read DICOM
ds = pydicom.dcmread('file.dcm')
ds.PatientName, ds.Modality, ds.pixel_array.shape
# Header-only (fast, no pixels)
ds = pydicom.dcmread('file.dcm', stop_before_pixels=True)
# Anonymize core fields
for t in ['PatientName','PatientID','PatientBirthDate',
'InstitutionName','ReferringPhysicianName','StudyDate']:
if t in ds: del ds[t]
ds.remove_private_tags()
ds.StudyInstanceUID = generate_uid()
ds.SeriesInstanceUID = generate_uid()
ds.SOPInstanceUID = generate_uid()
# Load NIfTI
img = nib.load('file.nii.gz'); data = img.get_fdata(); aff = img.affine
よくある間違い
-
誤り: DICOM の非識別化でプライベートタグを削除しない 正しい方法: 非識別化パイプラインの一環として、必ず
ds.remove_private_tags()を呼び出します 理由: ベンダーのプライベートタグ(奇数のグループ番号)には、標準的なタグレベルの削除後も残る PHI が日常的に含まれています -
誤り: 匿名化後も元の UID を保持する 正しい方法: 非識別化するファイルごとに、
StudyInstanceUID、SeriesInstanceUID、SOPInstanceUIDをgenerate_uid()で置き換えます 理由: 元の UID は元の PACS までたどることができます。2 つのファイルで同じ StudyInstanceUID が使われていると、同じ検査に由来することがわかってしまいます -
誤り: 確認せずに dcm2niix のデフォルトの結合動作を使う 正しい方法:
-m nを使って結合を無効にするか、出力ファイル数を想定されるシリーズのリストと照合します 理由: デフォルトの結合では、別々の撮像が 1 つの NIfTI ボリュームにまとめられ、データが破損する場合があります -
誤り: 非識別化中にピクセルデータへ焼き込まれた注釈を無視する 正しい方法: 超音波、セカンダリーキャプチャ、スクリーンショットのモダリティでは、OCR による検出とピクセルのマスキングを追加します 理由: タグレベルの非識別化ではピクセルは処理されません。画像に焼き込まれた患者名や MRN は、タグの削除後も残ります
-
誤り: dcm2niix から非圧縮の
.niiファイルを出力する 正しい方法: 後続のツールで非圧縮ファイルが明示的に必要な場合を除き、dcm2niix に常に-z yを渡して、gzip 圧縮された.nii.gzを出力します 理由: 非圧縮の NIfTI ファイルは 3~5 倍のサイズになり、多くのパイプラインでは利点がないままストレージを浪費し、転送を遅くします -
誤り: DICOM ヘッダーだけが必要なのに、ピクセルデータ全体を読み込む 正しい方法: メタデータのみをスキャンする場合は
pydicom.dcmread(path, stop_before_pixels=True)を使います 理由: Modality、SeriesInstanceUID、SliceThickness などのタグだけが必要な場合、大規模なコホートのピクセル配列を読み込むと、メモリと時間を浪費します -
誤り: DICOMDIR をファイルディレクトリと見なして、そのエントリーを反復処理する 正しい方法: 代わりに
Path(input_dir).rglob('*.dcm')を使って、実際の.dcmファイルを反復処理します 理由: DICOMDIR は独自の構造を持つインデックスファイルです。不完全または古い場合や、存在しない場合もあります。ファイルを直接反復処理するほうが確実です
参考資料
- dcm2niix:Li et al. J Neurosci Methods 2016、https://doi.org/10.1016/j.jneumeth.2016.03.001
- pydicom:https://pydicom.github.io/pydicom/stable/
- BIDS:Gorgolewski et al. Sci Data 2016、https://doi.org/10.1038/sdata.2016.44
- DICOM の非識別化:DICOM PS3.15 Annex E、https://dicom.nema.org/medical/dicom/current/output/chtml/part15/chapter_E.html