imaging-data-commons について
idc-indexは、NCI Imaging Data Commonsの公開がん画像データを選択・取得する研究者向けPythonクライアントです。内蔵メタデータ索引をDuckDBでローカルSQL検索でき、画像は公開AWSまたはGoogle Cloud Storageバケットから取得します。
主な機能
- 01内蔵メタデータのローカルSQL検索
- 02コレクション、患者、検査、シリーズの閲覧
- 03DICOM識別子によるダウンロードの絞り込み
- 04取得前のコホート容量確認
- 05OHIFまたはSlimのビューアURL生成
活用シーン
- 01DICOMメタデータによるがん画像コホート選択
- 02再現可能な機械学習用コホートの構築
- 03選択したDICOMシリーズの取得と処理
- 04画像シリーズのブラウザーリンク共有
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill imaging-data-commonsインストール後、Claude Code で/imaging-data-commonsを入力して読み込まれたか確認します。
詳細
NCI Imaging Data Commons (idc-index)
概要
NCI Imaging Data Commons(IDC)は、がん画像データの公開コレクションとして最大規模を誇り、175 件を超える DICOM コレクション(CT、MR、PET、スライド顕微鏡画像、セグメンテーション、構造化レポート)をホストしています。idc-index Python クライアントには、IDC のメタデータカタログ全体が pip ホイールに含まれる parquet ファイルとして同梱されています。IDCClient() はこのファイルを DuckDB に読み込むため、sql_query() はネットワークに一切アクセスせず、ローカルで実行されます。
画像は、同梱の s5cmd 実行ファイルを使って、パブリック AWS S3(デフォルト)または Google Cloud Storage のバケットからストリーミングダウンロードされます。GCP/AWS の認証情報、BigQuery の料金、サービスアカウント JSON は不要です。
使用する場面
- モダリティ、がんの種類、解剖学的部位、DICOM タグを指定して、一般公開されているがん画像データセットを検索する場合
- バージョン管理された IDC リリースから、再現可能な ML コホート(セグメンテーション、分類、マルチモーダル)を構築する場合
- ダウンロードせずに、175 件を超えるすべてのコレクションを対象として SQL で大規模な DICOM メタデータをクエリする場合
- ローカルでの処理やモデルのトレーニング用に、特定の DICOM シリーズをダウンロードする場合
- OHIF/Slim ビューアーの URL を生成して、ブラウザー上でシリーズを共有または対話的に確認する場合
- すでにローカルにある DICOM ファイルの読み取り、編集、匿名化だけが必要な場合は、代わりに pydicom-medical-imaging を使用してください
- ダウンロード後に全スライド病理画像の前処理(タイル分割、染色正規化)を行う場合は、代わりに histolab を使用してください
前提条件
- Python パッケージ:
idc-index(>=0.12)、pandas、pydicom(ダウンロード後の DICOM ファイルの読み取り用) - データ要件:クエリには不要です。ダウンロードする場合は、
series_size_MBに相当する空きディスク容量が必要です - 環境:認証は不要です。すべてのデータに公開アクセスできます。ホイールには parquet インデックスと、高速な S3 転送に使用する
s5cmd実行ファイルの両方が同梱されています - レート制限:ローカル SQL クエリ(ローカルの parquet 上で実行する DuckDB)にはありません。一括ダウンロードは API のクォータではなく、ネットワーク帯域幅によって制限されます
# Skip when already provisioned in a pixi or conda env
pip install idc-index pydicom
クイックスタート
from idc_index import IDCClient
client = IDCClient()
print('IDC version:', client.get_idc_version())
print('Collections:', len(client.get_collections()))
print('First 5:', client.get_collections()[:5])
df = client.sql_query("""
SELECT collection_id, COUNT(DISTINCT SeriesInstanceUID) AS n_series
FROM index
WHERE Modality = 'CT'
GROUP BY collection_id
ORDER BY n_series DESC LIMIT 5
""")
print(df)
コア API
モジュール 1:クライアントの初期化とコレクション
IDCClient() は唯一のエントリーポイントです。parquet インデックスを読み込み、index という名前の DuckDB テーブルとして登録し、同梱の s5cmd を検証します。get_collections() は、nsclc_radiomics、lidc_idri、tcga_gbm などの小文字のコレクション ID を含む、通常の Python リストを返します。
from idc_index import IDCClient
client = IDCClient()
collections = client.get_collections()
print("total:", len(collections), "type:", type(collections).__name__)
print("lung-related:", [c for c in collections if "lung" in c or "nsclc" in c][:5])
モジュール 2:sql_query(ローカルインデックスに対する DuckDB クエリ)
コホート選択には、この API の使用を推奨します。テーブル名は index です。インストール時に追加テーブル(prior_versions_index、任意の sm_index と clinical_index)も自動登録されます。pandas DataFrame を返します。Modality、BodyPartExamined、series_size_MB、または任意の DICOM タグを使ったフィルタリングには、すべてこの API を使用してください。従来の get_series(collection_id=..., modality=...) シグネチャは、idc-index にはもうありません。
# Cohort: small CT series in NSCLC Radiomics, sorted by size for cheap testing
df = client.sql_query("""
SELECT SeriesInstanceUID, StudyInstanceUID, PatientID, Modality, series_size_MB
FROM index
WHERE collection_id = 'nsclc_radiomics'
AND Modality = 'CT'
ORDER BY series_size_MB ASC
LIMIT 5
""")
print(df[["PatientID", "Modality", "series_size_MB"]])
# Cross-collection lung CT count, using DuckDB ILIKE for case-insensitive matching
df = client.sql_query("""
SELECT collection_id, COUNT(DISTINCT SeriesInstanceUID) AS n
FROM index
WHERE Modality = 'CT' AND BodyPartExamined ILIKE '%LUNG%'
GROUP BY collection_id
ORDER BY n DESC LIMIT 5
""")
print(df)
モジュール 3:階層ブラウジング(患者、検査、シリーズ)
DICOM 階層(コレクション -> 患者 -> 検査 -> シリーズ)をたどるには、型指定されたヘルパーを使用してください。各ヘルパーは、dict(デフォルト)、df、または list の outputFormat を受け付けます。注意:get_dicom_series() が受け取るのは studyInstanceUID(collection_id ではありません)です。コレクションまたはモダリティでシリーズをフィルタリングする場合は、sql_query() を使用してください。
patients = client.get_patients('nsclc_radiomics', outputFormat='df')
print("patients:", patients.shape, list(patients.columns)[:5])
# Walk down the hierarchy from one patient
pid = patients["PatientID"].iloc[0]
studies = client.get_dicom_studies(pid, outputFormat='df')
print("studies for", pid, ":", studies.shape)
study_uid = studies["StudyInstanceUID"].iloc[0]
series = client.get_dicom_series(study_uid, outputFormat='df')
print("series in study:", series.shape, "modalities:", series["Modality"].unique().tolist())
モジュール 4:download_from_selection(最新のダウンロード方法)
推奨されるダウンロード方法です。collection_id、patientId、studyInstanceUID、seriesInstanceUID、sopInstanceUID、crdc_series_uuid を任意に組み合わせて受け付けます(それぞれ文字列またはリストを指定できます)。フィルターは順番に適用されます。バイトデータを取得する前にコホートのサイズを確認するには、dry_run=True を使用してください。ファイルは dirTemplate(デフォルト:%collection_id/%PatientID/%StudyInstanceUID/%Modality_%SeriesInstanceUID)の下に保存されます。
import tempfile, glob, os
# Pick the smallest CT series for a fast smoke test (about 40 MB)
df = client.sql_query("""
SELECT SeriesInstanceUID FROM index
WHERE collection_id = 'nsclc_radiomics' AND Modality = 'CT'
ORDER BY series_size_MB ASC LIMIT 1
""")
uid = df["SeriesInstanceUID"].iloc[0]
out = tempfile.mkdtemp(prefix='idc_dl_')
client.download_from_selection(
downloadDir=out,
seriesInstanceUID=[uid],
quiet=True,
show_progress_bar=False,
source_bucket_location='aws', # 'aws' (default) or 'gcs'
)
files = glob.glob(os.path.join(out, "**/*.dcm"), recursive=True)
print(f"downloaded {len(files)} DICOM files to {out}")
# Size a cohort before downloading
client.download_from_selection(
downloadDir='./preview',
collection_id='nsclc_radiomics',
dry_run=True,
)
モジュール 5:download_dicom_series(便利なラッパー)
単一シリーズのダウンロードに使用します。内部では download_from_selection(seriesInstanceUID=...) を呼び出します。UID の文字列またはリストを受け付けます。
import tempfile, glob, os
out = tempfile.mkdtemp(prefix='idc_dl_')
client.download_dicom_series(
seriesInstanceUID=uid, # string or list
downloadDir=out,
quiet=True,
show_progress_bar=False,
)
print("files:", len(glob.glob(os.path.join(out, "**/*.dcm"), recursive=True)))
Module 6: get_viewer_URL(ブラウザーでの可視化)
IDC OHIF(放射線科)または Slim(スライド顕微鏡)のビューアーを開く、共有可能な URL を返します。viewer_selector が省略された場合、モダリティに基づいてビューアーを自動選択します。
url = client.get_viewer_URL(seriesInstanceUID=uid)
print("Open in browser:", url)
# Force a specific viewer
url_v2 = client.get_viewer_URL(seriesInstanceUID=uid, viewer_selector='ohif_v2')
print(url_v2)
Module 7: pydicom によるダウンロード済み DICOM の検査
ダウンロード後、ファイルは dirTemplate の下に整理されます。ヘッダーとピクセルの検査には pydicom を使用します。
import pydicom, glob, os
dcm_files = sorted(glob.glob(os.path.join(out, "**/*.dcm"), recursive=True))
ds = pydicom.dcmread(dcm_files[0])
print("PatientID:", ds.PatientID)
print("Modality :", ds.Modality)
print("Rows x Cols:", ds.Rows, "x", ds.Columns)
print("Pixel array:", ds.pixel_array.shape)
重要な概念
ローカル優先、認証不要のアーキテクチャ
IDC クライアントは特殊な設計になっており、client = IDCClient() はクエリに完全オフラインで対応します。parquet インデックス(数百 MB。idc-index-data に同梱)はメモリに読み込まれ、index という名前の DuckDB テーブルとして登録されます。すべての sql_query()、get_collections()、get_patients() などは、ローカルでの pandas/DuckDB 操作です。ネットワーク通信が発生するのは、download_from_selection() / download_dicom_series() の実行時だけです。これらは同梱の s5cmd 実行可能ファイルを使って、公開バケット(デフォルトでは s3://idc-open-data/...。source_bucket_location=gcs に設定すると gcs に切り替わります)からコピーします。そのため、gcloud 認証、GCP プロジェクト ID、BigQuery の課金、API キーは不要です。
DICOM の階層と識別子
IDC は標準の DICOM モデルに従います。Collection(collection_id。nsclc_radiomics のように小文字とアンダースコアを使用)-> Patient(PatientID)-> Study(StudyInstanceUID)-> Series(SeriesInstanceUID)-> Instance(SOPInstanceUID)。デフォルトでは、ダウンロードはシリーズ単位で行われます。crdc_series_uuid は、長期的な参照に適した、変更されない別の識別子です。
バージョン管理
インデックスのバージョンは、インストールされている idc-index-data パッケージのバージョン(client.get_idc_version()、例:v24)に固定されます。再現性を確保するには、環境内で idc-index と idc-index-data の両方のバージョンを固定してください。過去のリリースには、sql_query() 内の prior_versions_index テーブルから引き続きアクセスできます。
よくあるワークフロー
ワークフロー 1:コホート選択 -> マニフェスト -> ダウンロード
目標:ML のプロトタイピング用に小規模な CT シリーズの CSV マニフェストを作成し、ダウンロードします。
from idc_index import IDCClient
import pandas as pd, tempfile, os, glob
client = IDCClient()
# Step 1: SQL cohort: small CTs across three lung collections
cohort = client.sql_query("""
SELECT SeriesInstanceUID, PatientID, collection_id, Modality, series_size_MB
FROM index
WHERE collection_id IN ('nsclc_radiomics', 'tcga_luad', 'tcga_lusc')
AND Modality = 'CT'
AND series_size_MB < 60
ORDER BY series_size_MB ASC
""")
print(f"Cohort: {len(cohort)} series, total {cohort['series_size_MB'].sum():.1f} MB")
# Step 2: Save manifest
cohort.to_csv('ct_lung_manifest.csv', index=False)
# Step 3: Download a small subset
out = tempfile.mkdtemp(prefix='idc_cohort_')
client.download_from_selection(
downloadDir=out,
seriesInstanceUID=cohort["SeriesInstanceUID"].head(2).tolist(),
quiet=True,
show_progress_bar=False,
)
print("downloaded", len(glob.glob(os.path.join(out, "**/*.dcm"), recursive=True)), "files")
ワークフロー 2:1 人の患者を階層的に検査する
目標:SQL を記述せずに、コレクション -> 患者 -> 検査 -> シリーズ -> DICOM ヘッダーの順に掘り下げて確認します。
from idc_index import IDCClient
import tempfile, glob, os, pydicom
client = IDCClient()
# Pick first patient in the collection
patients = client.get_patients('nsclc_radiomics', outputFormat='df')
pid = patients["PatientID"].iloc[0]
studies = client.get_dicom_studies(pid, outputFormat='df')
series_df = client.get_dicom_series(studies["StudyInstanceUID"].iloc[0], outputFormat='df')
print(f"Patient {pid}: {len(studies)} studies, {len(series_df)} series in first study")
print("modalities:", series_df["Modality"].unique().tolist())
# Pick the smallest image series (CT/MR, not SR/SEG) and download
imaging = series_df[series_df["Modality"].isin(["CT", "MR", "PT"])]
target_uid = imaging.sort_values("series_size_MB").iloc[0]["SeriesInstanceUID"]
out = tempfile.mkdtemp(prefix='idc_one_')
client.download_dicom_series(seriesInstanceUID=target_uid, downloadDir=out,
quiet=True, show_progress_bar=False)
ds = pydicom.dcmread(sorted(glob.glob(os.path.join(out, "**/*.dcm"), recursive=True))[0])
print("first slice:", ds.Modality, ds.pixel_array.shape)
print("viewer:", client.get_viewer_URL(seriesInstanceUID=target_uid))
主要なパラメーター
| パラメーター | モジュール / 関数 | デフォルト | 範囲 / オプション | 効果 |
|---|---|---|---|---|
| outputFormat | get_patients / get_dicom_studies / get_dicom_series | dict | dict, df, list | アクセサーメソッドの戻り値の型 |
| seriesInstanceUID | download_from_selection / download_dicom_series | None | str or list[str] | DICOM SeriesInstanceUID でフィルター |
| collection_id | download_from_selection / get_patients | None / required | nsclc_radiomics のような小文字の文字列 | コレクションでフィルター(小文字、アンダースコア区切り) |
| source_bucket_location | download_from_selection / download_dicom_series | aws | aws, gcs | データ取得元の公開バケット |
| dirTemplate | download_from_selection | %collection_id/%PatientID/%StudyInstanceUID/%Modality_%SeriesInstanceUID | %-プレフィックス付き DICOM タグを含むテンプレート文字列。フラットなレイアウトにする場合は None | ディスク上のフォルダー階層 |
| dry_run | download_from_selection / download_dicom_series | False | True/False | ダウンロードせずにコホートのサイズを計算 |
| use_s5cmd_sync | download_from_selection / download_dicom_series | False | True/False | cp の代わりに s5cmd sync を使用(部分的なダウンロードを再開) |
| viewer_selector | get_viewer_URL | None (auto) | ohif_v2, ohif_v3, slim | 特定のブラウザービューアーを指定 |
ベストプラクティス
-
単一の階層ステップを超えるフィルタには sql_query() を使用します:DataFrame を返し、DuckDB を介して約 1,000 万行のインデックス全体を即座に処理でき、任意の DICOM タグにも対応しています。get_patients / get_dicom_studies / get_dicom_series は、親 UID がすでにわかっている場合にのみ使用してください。
-
大規模なコホートでは必ずドライランを実行します:download_from_selection(..., dry_run=True) を呼び出すと、データの転送前に合計サイズが表示されます。lidc_idri のような小規模なコレクションでも約 120 GB あり、nlst は 10 TB を超えます。
-
idc-index と idc-index-data の両方のバージョンを固定します:インデックスのバージョンが再現性を左右します。client.get_idc_version() の値(例:v24)を、データセットカードまたは論文の方法セクションに記録してください。
-
ほとんどのダウンロードでは AWS を優先します:source_bucket_location=aws がデフォルトであり、ほとんどの学術ネットワークで最速です。GCP の計算環境がバケットと同じ場所にある場合にのみ、gcs に切り替えてください。
-
従来の BigQuery の手順をデフォルトとして使わないでください:bigquery-public-data.idc_current.dicom_all は現在も機能しますが、GCP の認証と課金設定が必要で、推奨される方法ではなくなっています。任意のコード例については Recipes を参照してください。
よくあるレシピ
レシピ:異なるモダリティの患者コホート(同一患者の CT と MR)
使用する場面:すべての患者が両方のモダリティを持つマルチモーダル研究を構築する場合。
df = client.sql_query("""
SELECT PatientID
FROM index
WHERE collection_id = 'tcga_gbm'
GROUP BY PatientID
HAVING SUM(CASE WHEN Modality = 'CT' THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN Modality = 'MR' THEN 1 ELSE 0 END) > 0
""")
print(f"Patients with both CT and MR in TCGA-GBM: {len(df)}")
レシピ:QA 用のビューアー URL を一括生成する
使用する場面:目視で確認するシリーズの候補一覧を共同研究者に送る場合。
sample = client.sql_query("""
SELECT SeriesInstanceUID, Modality
FROM index WHERE collection_id = 'lidc_idri' AND Modality = 'CT'
LIMIT 5
""")
for _, row in sample.iterrows():
print(row.Modality, client.get_viewer_URL(seriesInstanceUID=row.SeriesInstanceUID))
レシピ:任意の応用(データセット間の結合に BigQuery を使用)
使用する場面:IDC のメタデータを別の BigQuery 公開データセット(例:TCGA の臨床データ)と結合する必要があり、課金設定済みの GCP プロジェクトがある場合。その他の用途では、sql_query() を優先してください。
# Requires: a google-cloud-bigquery install and gcloud application-default credentials.
from google.cloud import bigquery
bq = bigquery.Client(project='your-gcp-project-id')
df = bq.query("""
SELECT collection_id, Modality, COUNT(DISTINCT SeriesInstanceUID) AS n
FROM `bigquery-public-data.idc_current.dicom_all`
WHERE Modality IN ('CT', 'MR', 'PET')
GROUP BY collection_id, Modality
ORDER BY n DESC LIMIT 20
""").to_dataframe()
print(df)
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
ImportError: No module named idc_index | パッケージがインストールされていない | pip install idc-index を実行します(認証の設定は不要です) |
| IDCClient() の初回呼び出しが遅い | parquet インデックスをメモリに読み込んでいる(1~3 秒、プロセスごとに一度のみ) | クエリ間でクライアントを再利用し、呼び出しごとに再インスタンス化しないでください |
| sql_query: Catalog Error: Table idc_index does not exist | テーブル名が間違っている | DuckDB のテーブル名は index です。idc_index や dicom_all ではありません |
| get_dicom_series(collection_id=..., modality=...) が TypeError を発生させる | API が古く、そのシグネチャは削除されています | collection_id と Modality のフィルタを WHERE 句に指定して sql_query を使用してください |
| download_from_selection が s5cmd: command not found で失敗する | 同梱の s5cmd が PATH にありません | idc-index を再インストールしてください。wheel に s5cmd が同梱されています。NixOS または glibc を使用しないシステムでは、IDC_INDEX_S5CMD_EXE にシステムの s5cmd を設定してください |
| ダウンロードが停止する、または非常に遅い | 認証ではなくネットワークのボトルネック | GCP 上では source_bucket_location=gcs を試してください。それ以外の場合は use_s5cmd_sync=True を使用して再開してください |
| ValueError: collection_id ... does not exist | コレクション ID は大文字と小文字を区別し、アンダースコアを使用します | 小文字とアンダースコアを使用してください(LIDC-IDRI ではなく lidc_idri)。client.get_collections() で確認してください |
| pydicom がピクセルデータを読み取れない | 圧縮転送構文 | pylibjpeg、pylibjpeg-libjpeg、gdcm をインストールしてから、ds.pixel_array を再試行してください |
関連スキル
- pydicom-medical-imaging - IDC からダウンロードした DICOM ファイルの読み取り、編集、匿名化
- histolab-wsi-processing - 全スライド画像のタイル化と染色の正規化(IDC SM モダリティ)
- pathml - IDC のスライドコレクションを使用する、計算病理学向けエンドツーエンド ML パイプライン
参考資料
- idc-index GitHub - ソース、README、課題トラッカー(正式な API)
- idc-index on PyPI - 最新リリース
- learn.canceridc.dev - IDC の公式チュートリアル、ノートブック、概念ガイド
- IDC portal - コレクションやビューアー URL を探索するためのブラウザー UI
- IDC BigQuery dataset - 高度な分析や複数データセットをまたぐ SQL(任意、GCP の課金設定が必要)