mcpField
D
digital-pathology

digital-pathology

github.com/awslabs数据与数据库6880 次安装4 周前更新
代码仓库

关于 digital-pathology

digital-pathology 是指导 Agent 编写全切片图像(WSI)处理代码的技能,面向病理图像分析与模型部署任务。它覆盖 TIAToolbox 工作流、特征提取、CAMELYON 数据集及 SageMaker 部署模式。

主要功能

  1. 01WSI 检查与切片处理
  2. 02组织掩膜与染色标准化
  3. 03TIAToolbox 特征提取流程
  4. 04H-optimus-0 使用指导
  5. 05CAMELYON 数据集处理
  6. 06SageMaker 部署模式

适用场景

  1. 01构建 WSI 切片与特征提取流程
  2. 02训练 CAMELYON 切片分类模型
  3. 03规范处理组织区域与染色
  4. 04部署 WSI 模型进行批量评分

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/awslabs/hcls-agent-skills --skill digital-pathology

安装后在 Claude Code 中输入/digital-pathology确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.gitkeep
hoptimus0-endpoint/
build_and_push.sh
Dockerfile
inference.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-GigaPath0.5224×224
HoVer-Net (PanNuke)0.25256×256
SlideGraph CNN features0.25512×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)。

参考文献

评论

支持 Markdown