关于 digital-pathology
digital-pathology 是指导 Agent 编写全切片图像(WSI)处理代码的技能,面向病理图像分析与模型部署任务。它覆盖 TIAToolbox 工作流、特征提取、CAMELYON 数据集及 SageMaker 部署模式。
主要功能
- 01WSI 检查与切片处理
- 02组织掩膜与染色标准化
- 03TIAToolbox 特征提取流程
- 04H-optimus-0 使用指导
- 05CAMELYON 数据集处理
- 06SageMaker 部署模式
适用场景
- 01构建 WSI 切片与特征提取流程
- 02训练 CAMELYON 切片分类模型
- 03规范处理组织区域与染色
- 04部署 WSI 模型进行批量评分
安装
复制下面的命令,在终端中运行。
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详细介绍
数字病理学
概述
此技能旨在教会 Agent 为全切片图像(WSI)工作流生成正确、可运行的代码。内容涵盖 TIAToolbox CLI 和 Python API、H-optimus-0 基础模型、标准的 tissue→tiles→features→aggregate→predict 模式、CAMELYON 数据集,以及适用于受监管工作负载的 SageMaker 部署模式。
优先使用 TIAToolbox 基础组件(WSIReader、PatchPredictor、DeepFeatureExtractor),而不是重新实现切片 I/O。除非任务另有指定,默认使用 H-optimus-0,分辨率为 0.5 mpp / 224×224。
用法
在用户要求以下操作时调用此技能:
- 检查 WSI、将其切块或进行染色归一化(SVS、NDPI、MRXS、金字塔 TIFF)。
- 使用病理基础模型提取特征。
- 在 CAMELYON 或类似队列上构建切片级分类器。
- 将 WSI 模型部署到 AWS SageMaker。
只有在分辨率、模型或输出产物不明确时才要求澄清。否则,按“分辨率规则”一节中的默认值处理。
响应格式
- 先给出用户需要的命令或代码,再作说明
- 结构为:确认输入 → 可运行代码 → 解释关键参数 → 注意事项
- 每项任务提供一个完整可运行示例;不要展示所有备选方案
- 代码注释保持精简且实用(说明用途,而非存在原因)
- 目标:50-100 行代码,并附简短说明
核心概念
- 金字塔式 WSI:多分辨率图像(多个层级)。
baseline= level 0(原始分辨率,最精细)。寻址方式使用resolution+units ∈ {mpp, power, level, baseline}。 - mpp:每像素微米数。
0.5 mpp ≈ 20×,0.25 mpp ≈ 40×。下采样倍数 =baseline_mpp / target_mpp。 - 组织掩码:在低分辨率下计算的二值前景掩码(例如在 1.25× 或 4 mpp 下使用 Otsu 算法),用于跳过背景图块。
- Patch / tile:以目标 mpp 读取的固定尺寸裁剪区域(例如 224×224),用作模型输入。
- 基础模型嵌入:预训练 ViT 为每个图块生成的特征向量(H-optimus-0 → 1536-d)。
- 聚合:根据图块特征进行切片级预测(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 / mean-pool),以进行切片级预测。
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,输入为 224×224 RGB,0.5 mpp,输出为 1536-d 嵌入。许可证需通过审核——在 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)
在 24 GB GPU 上使用 batch_size=16 和 fp16 自动混合精度。优先在 DeepFeatureExtractor 中使用 TimmBackbone("H-optimus-0", pretrained=True)——TIAToolbox 会自动应用正确的预处理。
分辨率规则
| 模型 | 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,基准层(level-0)像素中的多边形坐标。
- CAMELYON16:270 张训练切片(
normal/+tumor/)+ 130 张测试切片,标签位于reference.csv。任务:切片级肿瘤分类 + 肿瘤分割。 - CAMELYON17:100 位患者的 1000 张切片(每位患者 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 至少需要
ml.g5.2xlarge(24 GB A10G)。批量处理请使用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
常见错误
-
**错误:**将旧版 pixman 库(<0.40)与 OpenSlide 搭配使用 **正确:**运行
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转换 **原因:**引发OpenSlideUnsupportedFormatError,因为 OpenSlide 要求使用金字塔格式 -
**错误:**传递与 WSI 金字塔形状不匹配的 numpy 数组作为外部掩码 **正确:**直接使用
reader.tissue_mask(...),并传递返回的 reader 对象 **原因:**由于分辨率/形状不匹配,导致DimensionMismatchError -
**错误:**处理 PNG 图块时未指定分辨率单位 **正确:**传入
units="baseline"和明确的resolution=1.0**原因:**图块缺少 mpp 元数据,触发“unknown scale”警告 -
**错误:**按切片而非患者划分 CAMELYON17 数据 **正确:**按照官方协议按
patient_id划分(5 折) **原因:**导致患者数据泄漏,因为每位患者有 5 张切片 -
**错误:**提取特征时跳过 H-optimus-0 归一化(mean/std) **正确:**使用
TimmBackbone(自动应用归一化),或明确应用 mean/std **原因:**未正确归一化输入时,特征看起来像随机数据,且 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 等,《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 等,《JAMA》,2017 年,https://doi.org/10.1001/jama.2017.14585
- CAMELYON17:Bandi 等,《IEEE TMI》,2019 年,https://doi.org/10.1109/TMI.2018.2867350