mcpField
P
pathml

pathml

github.com/jaechang-hits数据与数据库38468 次安装1 周前更新
代码仓库

关于 pathml

pathml 是面向计算病理学的 Python 工具包,用于处理全视野病理图像并构建机器学习流程。它将切片读取、切块、预处理和特征提取纳入统一的 SlideData 与 Pipeline 抽象。

主要功能

  1. 01读取多种全视野图像格式
  2. 02切片切块与组织检测
  3. 03染色归一化与核分割
  4. 04支持多重免疫荧光图像处理
  5. 05提取图像块特征并导出数据

适用场景

  1. 01构建病理图像分类训练集
  2. 02处理 H&E 切片并统一染色
  3. 03分割病理图像中的细胞核
  4. 04批量处理多个全视野切片

安装

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

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill pathml

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

详细介绍

pathml

概述

PathML 是一个 Python 工具包,专为全视野图像(WSI)的计算病理工作流设计。它提供从原始切片文件(SVS、NDPI、MRXS、TIFF)到图块提取、预处理(染色归一化、细胞核分割、组织检测)、特征提取和机器学习的统一流程。PathML 集成了常用的 Python 机器学习和图像处理库,并通过 SlideData 和 Pipeline 抽象简化 WSI 处理的复杂性。

适用场景

  • 处理全视野 H&E 图像:将大型 WSI 切分为图块,并对来自不同扫描仪或批次的切片进行染色差异归一化。
  • 病理切片上的细胞核分割:使用内置分割流程,在 H&E 或 DAPI 染色的 WSI 中检测并分割细胞核。
  • 从 WSI 构建机器学习训练数据集:提取带有关联标签的图块,用于训练组织分类器、肿瘤检测器或生存预测模型。
  • 多重免疫荧光(mIF)图像分析:处理多通道免疫荧光切片,并进行针对各通道的预处理和特征提取。
  • 跨队列染色归一化:应用 Macenko 或 Vahadane 染色归一化,协调来自多个机构的 H&E 切片。
  • 为下游机器学习提取特征:从图块中提取手工设计或深度学习特征,用于患者级预测任务。
  • 对于标准 2D 显微镜图像(非 WSI),可直接使用 scikit-image 或 cellpose,无需承担 PathML 的额外开销。

前置条件

  • Python 软件包:pathml、torch、torchvision、numpy、scikit-image、openslide-python
  • 系统:OpenSlide C 库(读取 WSI 所必需)
  • 数据要求:SVS、NDPI、MRXS 或 TIFF 格式的 WSI 文件;建议使用 GPU 进行分割
  • 环境:Python 3.8+;用于深度学习预处理的 CUDA 兼容 GPU
# Install system dependency first
conda install -c conda-forge openslide

# Install PathML
pip install pathml

# For GPU support
pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu118

快速入门

from pathml.core import SlideData
from pathml.preprocessing import Pipeline
from pathml.preprocessing.transforms import BoxBlur, TissueDetectionHE

# Load → build pipeline → tile → preprocess
slide = SlideData("tumor.svs", name="demo")
pipeline = Pipeline([BoxBlur(kernel_size=3), TissueDetectionHE(mask_name="tissue")])
slide.run(pipeline, tile_size=256, tile_stride=256)

# Inspect tiles
from pathml.core import Tile
tiles = [t for t in slide.tiles if t.masks["tissue"].any()]
print(f"Tissue tiles: {len(tiles)} of {len(slide.tiles)}")

工作流

步骤 1:加载全视野图像
from pathml.core import SlideData

# Load an H&E whole-slide image
slide = SlideData("path/to/slide.svs", name="tumor_slide_001")
print(f"Slide name: {slide.name}")
print(f"Slide shape: {slide.slide.shape}")
print(f"Slide properties: {slide.slide.properties}")
步骤 2:定义预处理流程
from pathml.preprocessing import Pipeline
from pathml.preprocessing.transforms import (
    BoxBlur,
    TissueDetectionHE,
    HEStainNormalization,
)

# Build a preprocessing pipeline for H&E slides
pipeline = Pipeline([
    BoxBlur(kernel_size=5),                       # smooth image
    TissueDetectionHE(mask_name="tissue"),         # detect tissue regions
    HEStainNormalization(target="normalize"),       # normalize H&E staining
])
print(f"Pipeline steps: {len(pipeline.transforms)}")
步骤 3:创建 TileDataset
from pathml.core import TileDataset

# Tile the slide into 256x256 patches at 20x magnification
slide.generate_tiles(
    shape=(256, 256),
    stride=(256, 256),
    pad=False,
    level=0,           # pyramid level 0 = highest resolution
    coords_format="fractional",
)
print(f"Total tiles generated: {len(slide.tiles)}")
步骤 4:运行预处理流程
# Apply preprocessing pipeline to all tiles
slide.run(pipeline, distributed=False, tile_pad=False)
print("Pipeline complete — tiles preprocessed")

# Inspect a single tile
tile = slide.tiles[0]
print(f"Tile shape: {tile.image.shape}")      # (256, 256, 3)
print(f"Tile masks: {list(tile.masks.keys())}")
步骤 5:细胞核分割
from pathml.preprocessing.transforms import NuclearSegmentation

# Run Hematoxylin-channel nuclear segmentation
seg_pipeline = Pipeline([
    TissueDetectionHE(mask_name="tissue"),
    NuclearSegmentation(mask_name="nuclei"),
])

slide.run(seg_pipeline, distributed=False)

# Count nuclei per tile
for tile in list(slide.tiles)[:5]:
    n_nuclei = tile.masks["nuclei"].max()
    print(f"Tile {tile.coords}: {n_nuclei} nuclei detected")
步骤 6:特征提取
import numpy as np
from pathml.core import SlideDataset

features = []
for tile in slide.tiles:
    if "tissue" in tile.masks and tile.masks["tissue"].any():
        img = tile.image
        feat = {
            "mean_r":    img[:, :, 0].mean(),
            "mean_g":    img[:, :, 1].mean(),
            "mean_b":    img[:, :, 2].mean(),
            "std_r":     img[:, :, 0].std(),
            "n_nuclei":  int(tile.masks["nuclei"].max()) if "nuclei" in tile.masks else 0,
            "tile_x":    tile.coords[0],
            "tile_y":    tile.coords[1],
        }
        features.append(feat)

import pandas as pd
df = pd.DataFrame(features)
df.to_csv("slide_features.csv", index=False)
print(f"Extracted features from {len(df)} tissue tiles -> slide_features.csv")
步骤 7:保存并导出处理后的切片
import h5py

# Save slide data (tiles + masks) to HDF5
slide.write("processed_slide.h5")
print("Slide saved to processed_slide.h5")

# Reload for downstream use
from pathml.core import SlideData
slide_loaded = SlideData.read("processed_slide.h5")
print(f"Reloaded: {len(slide_loaded.tiles)} tiles")

关键参数

参数默认值范围 / 选项作用
shape(256, 256)(64,64) – (1024,1024)图块的像素尺寸
stride等于 shape任意 ≤ shape 的元组图块中心之间的步长;stride < shape 时图块会重叠
level00 – 最大金字塔层级金字塔分辨率层级(0 = 全分辨率)
kernel_size5奇数 3–21BoxBlur 中的平滑核尺寸
mask_name必需任意字符串存储在 tile.masks 中的输出掩码名称
distributedFalseTrue、False为大型切片启用 Dask 分布式处理
padFalseTrue、False将边缘图块填充至完整的 shape 尺寸

常用方案

方案:仅保留组织图块

适用场景:排除背景图块,以减少后续步骤中的内存和计算开销。

# Filter tiles to only tissue regions after running tissue detection pipeline
tissue_tiles = [t for t in slide.tiles if "tissue" in t.masks and t.masks["tissue"].mean() > 0.5]
print(f"Tissue tiles: {len(tissue_tiles)} / {len(slide.tiles)} total")
方案:将图块导出为 PNG 文件

适用场景:创建带标签的图块数据集,用于在 PyTorch 中训练自定义分类器。

from PIL import Image
import numpy as np
from pathlib import Path

output_dir = Path("tiles_png")
output_dir.mkdir(exist_ok=True)

for i, tile in enumerate(slide.tiles):
    if "tissue" in tile.masks and tile.masks["tissue"].mean() > 0.5:
        img = Image.fromarray(tile.image.astype(np.uint8))
        img.save(output_dir / f"tile_{i:05d}_x{tile.coords[0]}_y{tile.coords[1]}.png")

print(f"Saved {i+1} tiles to {output_dir}/")
方案:批量处理多个切片

适用场景:对某个目录中的 WSI 文件运行相同的预处理流程。

from pathlib import Path
from pathml.core import SlideData
from pathml.preprocessing import Pipeline
from pathml.preprocessing.transforms import TissueDetectionHE, HEStainNormalization

pipeline = Pipeline([
    TissueDetectionHE(mask_name="tissue"),
    HEStainNormalization(target="normalize"),
])

wsi_dir = Path("slides/")
for wsi_path in sorted(wsi_dir.glob("*.svs")):
    slide = SlideData(str(wsi_path), name=wsi_path.stem)
    slide.generate_tiles(shape=(256, 256), stride=(256, 256), level=0)
    slide.run(pipeline, distributed=False)
    slide.write(f"processed/{wsi_path.stem}.h5")
    print(f"Processed {wsi_path.name}: {len(slide.tiles)} tiles")

预期输出

  • slide.tiles — 可迭代对象,包含 Tile 对象;每个对象都有 .image(numpy 数组)和 .masks(numpy 数组字典)
  • slide_features.csv — 按图块整理的表格特征(颜色统计、细胞核计数、坐标)
  • processed_slide.h5 — HDF5 文件,包含图块、掩码和供下游使用的元数据
  • PNG 图块文件(可选)— 可直接加载为 PyTorch ImageFolder 数据集

故障排查

问题原因解决方案
openslide.lowlevel.OpenSlideUnsupportedFormatError未安装 OpenSlide C 库,或不支持 WSI 格式conda install -c conda-forge openslide;检查格式兼容性
分割过程中出现 CUDA out of memory图块尺寸对 GPU 来说过大将图块 shape 缩小至 (128, 128),或在 CPU 上使用 distributed=False 运行
generate_tiles 后 slide.tiles 为空层级索引超出范围,或所有图块都被筛除使用 level=0;通过 slide.slide.level_count 检查切片金字塔
染色标准化生成黑色图块源切片对比度过低,或组织检测失败在标准化前应用 TissueDetectionHE;检查组织掩码的覆盖范围
在 tile.masks 中出现 KeyError: 'nuclei'尚未运行分割流程访问掩码前,先使用 slide.run() 运行 NuclearSegmentation 流程
图块生成速度很慢大型 SVS 使用了高分辨率的 0 级使用较低的金字塔层级(level=1 或 level=2)加快原型验证
AttributeError: SlideData has no attribute 'write'PathML 版本较旧使用 pip install --upgrade pathml 获取 HDF5 保存/加载支持

参考资料

评论

支持 Markdown