digital-pathology について
digital-pathology は、病理画像解析やモデル展開に使う全スライド画像(WSI)処理コードを生成するためのスキルです。TIAToolbox の処理手順、特徴抽出、CAMELYON データセット、SageMaker への展開方法を扱います。
主な機能
- 01WSI の検査とタイル分割
- 02組織マスクと染色正規化
- 03TIAToolbox による特徴抽出
- 04H-optimus-0 の利用ガイダンス
- 05CAMELYON データセット処理
- 06SageMaker への展開パターン
活用シーン
- 01WSI のタイル分割・特徴抽出パイプライン構築
- 02CAMELYON データによるスライド分類モデルの学習
- 03組織領域の処理と染色の正規化
- 04WSI モデルのバッチスコアリングへの展開
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ awslabs/ hcls-agent-skills --skill digital-pathologyインストール後、Claude Code で/digital-pathologyを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.gitkeephoptimus0-endpoint/build_and_push.shDockerfileinference.py詳細
デジタル病理
概要
このスキルでは、全スライド画像(WSI)パイプライン向けの、正しく実行可能なコードをエージェントが生成できるようにします。TIAToolbox CLI と Python API、基盤モデル H-optimus-0、標準的な組織→タイル→特徴量→集約→予測のパターン、CAMELYON データセット、規制対象ワークロード向けの SageMaker デプロイパターンを扱います。
スライド I/O を再実装するのではなく、TIAToolbox のプリミティブ(WSIReader、PatchPredictor、DeepFeatureExtractor)をデフォルトで使用します。タスクで別途指定されていない限り、デフォルトでは 0.5 mpp/224×224 の H-optimus-0 を使用します。
使用方法
ユーザーから次の依頼があった場合に、このスキルを呼び出します。
- WSI(SVS、NDPI、MRXS、ピラミッド TIFF)を検査、タイル化、または染色正規化する。
- 病理学基盤モデルで特徴量を抽出する。
- CAMELYON または同様のコホートで、スライドレベルの分類器を構築する。
- WSI モデルを AWS SageMaker にデプロイする。
解像度、モデル、または出力成果物が曖昧な場合にのみ、詳細を確認してください。それ以外の場合は、「解像度のルール」セクションのデフォルトを適用します。
応答形式
- 必要なコマンドまたはコードを最初に示し、その後に説明を加える
- 次の順に構成する:入力の確認 → 動作するコード → 主要なパラメーターの説明 → 注意点
- タスクごとに、完全に動作する例を 1 つ示す。すべての代替案は示さない
- コードコメントは最小限にし、実用的な内容(存在理由ではなく、何をするか)にする
- 目安:コード 50~100 行と、簡潔な説明
基本概念
- ピラミッド型 WSI:複数解像度の画像(レベル)。
baseline= レベル 0(ネイティブ、最高解像度)。指定にはresolutionとunits ∈ {mpp, power, level, baseline}を使用します。 - mpp:1 ピクセルあたりのマイクロメートル数。
0.5 mpp ≈ 20×、0.25 mpp ≈ 40×。ダウンサンプル率 =baseline_mpp / target_mpp。 - 組織マスク:低解像度(例:1.25×または 4 mpp)で計算する二値前景マスク。背景タイルを除外するために使用します。
- パッチ/タイル:ターゲット mpp で読み込む固定サイズの切り出し画像(例:224×224)。モデル入力として使用します。
- 基盤モデルの埋め込み:事前学習済み ViT によるタイルごとの特徴ベクトル(H-optimus-0 → 1536 次元)。
- 集約:タイル特徴量からスライドレベルの予測を行います(ABMIL、CLAM、TransMIL、SlideGraph)。
クイックリファレンス — 標準パイプライン
WSI 分類タスクには、このエンドツーエンドのパターンをコピーして調整してください。
import torch
from tiatoolbox.wsicore.wsireader import WSIReader
from tiatoolbox.models.architecture.vanilla import TimmBackbone
from tiatoolbox.models.engine.semantic_segmentor import IOSegmentorConfig
from tiatoolbox.models.engine.patch_predictor import IOPatchPredictorConfig
from tiatoolbox.models import DeepFeatureExtractor
SLIDE = "slide.svs"
OUT = "./out"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# 1. Inspect
reader = WSIReader.open(SLIDE)
info = reader.info.as_dict()
print(info["mpp"], info["level_dimensions"])
# 2. Tissue mask (Otsu at low res — do NOT pass as external mask)
mask_reader = reader.tissue_mask(method="otsu", resolution=4, units="mpp")
# 3. Tile + extract H-optimus-0 features @ 0.5 mpp, 224x224
ioconfig = IOPatchPredictorConfig(
input_resolutions=[{"units": "mpp", "resolution": 0.5}],
patch_input_shape=[224, 224],
stride_shape=[224, 224],
)
extractor = DeepFeatureExtractor(
model=TimmBackbone("H-optimus-0", pretrained=True),
batch_size=16,
num_loader_workers=4,
)
output = extractor.run(
images=[SLIDE],
masks=[mask_reader],
patch_mode=False,
ioconfig=ioconfig,
save_dir=OUT,
device=DEVICE,
)
# output[0] -> (positions.npy, features.npy)
次に、features.npy に対して集約(ABMIL/SlideGraph/平均プーリング)を行い、スライドレベルの予測をします。
TIAToolbox CLI
すべての CLI コマンドで、--img-input(ファイルまたはディレクトリ)、--output-path、--resolution、--units、--batch-size、--device cuda を指定できます。
# Slide metadata
tiatoolbox slide-info --img-input slide.svs --mode show
# Tissue mask (binary PNG)
tiatoolbox tissue-mask --img-input slide.svs --output-path ./mask --method otsu \
--resolution 1.25 --units power
# Tile classification with built-in ResNet18 on Kather100k
tiatoolbox patch-predictor --img-input slide.svs \
--pretrained-model resnet18-kather100k \
--output-path ./pred --batch-size 32 --device cuda \
--resolution 0.5 --units mpp
# Stain normalization (Reinhard / Macenko / Vahadane)
tiatoolbox stain-norm --img-input tiles/ --method reinhard --output-path ./norm
入力があらかじめ抽出された PNG タイルの場合は、--units baseline を使用して「unknown scale」警告を回避します。
TIAToolbox Python API
WSIReader
from tiatoolbox.wsicore.wsireader import WSIReader
reader = WSIReader.open("slide.svs")
reader.info.as_dict() # mpp, levels, vendor
reader.slide_dimensions(resolution=0.5, units="mpp")
tile = reader.read_rect(
location=(10000, 10000), # baseline coords
size=(224, 224),
resolution=0.5, units="mpp",
)
region = reader.read_bounds(
bounds=(0, 0, 4096, 4096),
resolution=2.0, units="mpp",
)
mask = reader.tissue_mask(method="otsu", resolution=4, units="mpp")
PatchPredictor
from tiatoolbox.models import PatchPredictor
from tiatoolbox.models.engine.patch_predictor import IOPatchPredictorConfig
ioconfig = IOPatchPredictorConfig(
input_resolutions=[{"units": "mpp", "resolution": 0.5}],
patch_input_shape=[224, 224],
stride_shape=[224, 224],
)
predictor = PatchPredictor(pretrained_model="resnet18-kather100k", batch_size=32)
out = predictor.run(
images=["slide.svs"], masks=[mask],
patch_mode=False, ioconfig=ioconfig,
save_dir="./pred", device="cuda",
)
DeepFeatureExtractor + TimmBackbone
from tiatoolbox.models import DeepFeatureExtractor
from tiatoolbox.models.architecture.vanilla import TimmBackbone
extractor = DeepFeatureExtractor(
model=TimmBackbone("H-optimus-0", pretrained=True),
batch_size=16, num_loader_workers=4,
)
SlideGraph の構築
import numpy as np
from tiatoolbox.tools.graph import SlideGraphConstructor
positions = np.load("out/0.position.npy") # (N, 4) x1,y1,x2,y2
features = np.load("out/0.features.npy") # (N, D)
graph = SlideGraphConstructor.build(
positions=positions[:, :2], features=features,
)
# graph -> {"x": features, "edge_index": (2,E), "coords": (N,2)}
染色正規化
from tiatoolbox.tools.stainnorm import get_normalizer
norm = get_normalizer("reinhard") # or "macenko", "vahadane"
norm.fit(target_tile) # RGB ndarray HxWx3
normed = norm.transform(source_tile)
H-optimus-0 基盤モデル
11 億パラメーターの ViT-G/14 です。入力は 0.5 mpp の 224×224 RGB、出力は 1536 次元の埋め込みです。ライセンスにはアクセス制限があります。Hugging Face で規約に同意し、huggingface-cli login を実行してください。
timm の直接使用
import timm, torch
from torchvision import transforms
model = timm.create_model(
"hf-hub:bioptimus/H-optimus-0",
pretrained=True,
init_values=1e-5,
dynamic_img_size=False,
).eval().to("cuda")
preprocess = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(
mean=(0.707223, 0.578729, 0.703617),
std=(0.211883, 0.230117, 0.177517),
),
])
with torch.inference_mode(), torch.autocast("cuda", dtype=torch.float16):
feats = model(preprocess(tile).unsqueeze(0).to("cuda")) # (1, 1536)
fp16 自動混合精度を使用する 24 GB GPU では、batch_size=16 を使用します。TIAToolbox が正しい前処理を自動的に適用するため、DeepFeatureExtractor 内では TimmBackbone("H-optimus-0", pretrained=True) を優先してください。
解像度のルール
| モデル | mpp | パッチサイズ |
|---|---|---|
| H-optimus-0 / UNI / Prov-GigaPath | 0.5 | 224×224 |
| HoVer-Net (PanNuke) | 0.25 | 256×256 |
| SlideGraph CNN features | 0.25 | 512×512 |
| 組織マスク(Otsu) | 4 mpp または 1.25× | — |
ダウンサンプル率 = baseline_mpp / target_mpp。0.25 mpp のスライドを 0.5 mpp で処理する場合、ダウンサンプル率は 2 です。
CAMELYON データセット
- 形式:ピラミッド型
.tif。OpenSlide 互換で、ベースラインのサイズは約 100 000 × 100 000 px です。 - アノテーション:ASAP XML。ポリゴン座標はベースライン(レベル 0)ピクセル単位です。
- CAMELYON16:トレーニング用スライド 270 枚(
normal/+tumor/)とテスト用スライド 130 枚があり、ラベルはreference.csvにあります。タスク:スライド単位の腫瘍分類と腫瘍セグメンテーション。 - CAMELYON17:100 人の患者にわたる 1000 枚のスライド(患者 1 人あたり 5 枚)。患者単位の pN ステージは
stages.csvにあります。公式の 5 分割は患者単位です。スライド単位で分割しないでください。
import xml.etree.ElementTree as ET
tree = ET.parse("slide.xml") # ASAP format
for ann in tree.iter("Annotation"):
pts = [(float(c.get("X")), float(c.get("Y")))
for c in ann.iter("Coordinate")] # baseline coords
SageMaker のデプロイ
WSI はギガピクセル規模です。呼び出し経路を通じて生バイト列を送信しないでください。リクエストには S3 URI を渡し、コンテナが s3fs/boto3 経由で読み込むようにします。
- エンドポイントの種類:非同期推論(長時間実行、リクエスト単位)またはバッチ変換(コホートのスコアリング)。
- ベースコンテナ:
763104351884.dkr.ecr.<region>.amazonaws.com/pytorch-inference:2.x-gpu-py310を拡張します。 - Apt パッケージ:
openslide-tools libpixman-1-dev libvips。 - Pip:
tiatoolbox timm huggingface_hub s3fs。 - インスタンス:H-optimus-0 fp16(24 GB A10G)には、最低でも
ml.g5.2xlargeを使用します。バッチにはml.g5.12xlargeを使用します。 - 非同期設定:
invocations_timeout=3600、max_concurrent_invocations_per_instance=2。
from sagemaker.async_inference import AsyncInferenceConfig
async_config = AsyncInferenceConfig(
output_path="s3://bucket/async-out/",
max_concurrent_invocations_per_instance=2,
)
predictor = model.deploy(
instance_type="ml.g5.2xlarge", initial_instance_count=1,
async_inference_config=async_config,
)
predictor.predict_async(input_path="s3://bucket/slides/slide.svs")
Dockerfile の概要:
FROM 763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-inference:2.3.0-gpu-py310
RUN apt-get update && apt-get install -y openslide-tools libpixman-1-dev libvips \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir tiatoolbox timm huggingface_hub s3fs
よくある間違い
-
誤り: OpenSlide で古い pixman ライブラリ(<0.40)を使用する
正しい方法:apt-get install libpixman-1-devを実行して再ビルドするか、libvips ≥ 8.12 を使用します
理由: レンダリングのバグにより、read_rectからすべて黒いタイルが返されます -
誤り: スライドのネイティブ mpp より細かい解像度を指定する
正しい方法: より粗いresolutionを使用するか、先にreader.info.mppを確認します
理由:UserWarning: Scale > 1が発生し、アップサンプリング(補間)されたデータが生成されます -
誤り: ゲート付きモデルの利用規約に同意せずに H-optimus-0 を読み込もうとする
正しい方法: Hugging Face のモデルページで利用規約に同意してから、huggingface-cli loginを実行します
理由:OSError: 401認証エラーが発生します -
誤り: DeepFeatureExtractor で GPU メモリに対して大きすぎるバッチサイズを使用する
正しい方法:batch_sizeを小さくし(16→8→4)、fp16 autocast が有効になっていることを確認します
理由: CUDA のメモリ不足エラーが発生します -
誤り: ピラミッド型でない TIFF を OpenSlide に渡す
正しい方法: まずvips tiffsave in.tif out.tif --tile --pyramid --compression jpegで変換します
理由: OpenSlide はピラミッド型の形式を必要とするため、OpenSlideUnsupportedFormatErrorが発生します -
誤り: WSI のピラミッドと形状が一致しない NumPy 配列を外部マスクとして渡す
正しい方法:reader.tissue_mask(...)を直接使用し、返された reader オブジェクトを渡します
理由: 解像度/形状が一致せず、DimensionMismatchErrorが発生します -
誤り: 解像度の単位を指定せずに PNG タイルを処理する
正しい方法:units="baseline"と明示的なresolution=1.0を渡します
理由: タイルには mpp メタデータがなく、「unknown scale」警告が発生します -
誤り: CAMELYON17 のデータを患者単位ではなくスライド単位で分割する
正しい方法: 公式プロトコルに従い、patient_id単位で分割します(5 分割)
理由: 各患者には 5 枚のスライドがあるため、患者データのリークが発生します -
誤り: 特徴抽出時に H-optimus-0 の正規化(平均/標準偏差)を省略する
正しい方法: 正規化が自動で適用されるTimmBackboneを使用するか、平均/標準偏差を明示的に適用します
理由: 適切な入力正規化を行わないと、特徴がランダムに見え、AUC が低くなります -
誤り: アノテーション座標に誤ったピラミッドレベルを使用する
正しい方法: ASAP XML の座標は常にベースラインレベルです。低解像度で読み込む場合は座標をスケーリングします
理由: レベルの不一致を考慮しないと、座標が 2 倍または 4 倍ずれます
最小限の依存関係
tiatoolbox>=1.5
timm>=1.0
torch>=2.1
openslide-python
huggingface_hub
システム:openslide-tools、libpixman-1-dev、libvips(ピラミッド型でない TIFF の変換用)。
参考文献
- TIAToolbox:Pocock et al. Commun Med 2022, https://doi.org/10.1038/s43856-022-00186-5
- H-optimus-0:Bioptimus, https://huggingface.co/bioptimus/H-optimus-0
- CAMELYON16:Bejnordi et al. JAMA 2017, https://doi.org/10.1001/jama.2017.14585
- CAMELYON17:Bandi et al. IEEE TMI 2019, https://doi.org/10.1109/TMI.2018.2867350