关于 histolab-wsi-processing
histolab-wsi-processing 是面向计算病理研究者的 Python 全切片图像处理库,支持组织检测、切片提取与预处理,并可按质量对图块评分。
主要功能
- 01组织区域检测与掩膜
- 02随机、网格和评分式切片
- 03可组合的图像与形态学滤镜
- 04按细胞核密度或细胞性评分
- 05切片、掩膜和图块可视化
适用场景
- 01为模型训练准备图块数据集
- 02抽取组织区域供切片检查
- 03去除切片背景和伪影
- 04比较多个切片集合的图块质量
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill histolab-wsi-processing安装后在 Claude Code 中输入/histolab-wsi-processing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/filters_preprocessing.mdtile_extraction.mdvisualization_slides.md详细介绍
Histolab WSI 处理
概述
Histolab 是一个用于数字病理学全切片图像(WSI)处理的 Python 库。它可自动检测组织,使用多种策略从十亿像素图像中提取有信息量的图块,并提供可组合的预处理过滤器流水线。该库通过 OpenSlide 处理 SVS、TIFF、NDPI 等 WSI 格式。
适用场景
- 从全切片图像中提取图块,用于深度学习模型训练
- 检测组织区域,并过滤组织病理切片中的背景和伪影
- 为 H&E 或 IHC 染色组织切片构建预处理流水线
- 创建按细胞核密度或细胞密度排序、基于质量的图块数据集
- 使用一致的参数,跨多个切片批量提取图块
- 在计算病理学工作流开始前评估切片质量和组织覆盖率
- 如果只需访问原始切片而不提取图块,请直接使用
openslide-python - 对于复杂的多重成像或空间蛋白质组学流水线,请改用
pathml
前置条件
- Python 包:
histolab(包含 OpenSlide Python 绑定) - 系统依赖项:必须单独安装 OpenSlide C 库
- 支持的格式:SVS、TIFF、NDPI、VMS、SCN、MRXS(通过 OpenSlide)
# macOS
brew install openslide
pip install histolab
# Ubuntu/Debian
sudo apt-get install openslide-tools
pip install histolab
快速入门
from histolab.slide import Slide
from histolab.tiler import RandomTiler
# Load slide
slide = Slide("slide.svs", processed_path="output/")
print(f"Dimensions: {slide.dimensions}, Levels: {slide.levels}")
# Configure tiler
tiler = RandomTiler(
tile_size=(512, 512), n_tiles=100, level=0, seed=42,
check_tissue=True, tissue_percent=80.0
)
# Preview and extract
tiler.locate_tiles(slide, n_tiles=20)
tiler.extract(slide)
核心 API
模块 1:切片管理
Slide 类是加载和检查 WSI 文件的主要接口。
from histolab.slide import Slide
from histolab.data import prostate_tissue
# Load from built-in sample data (prostate, ovarian, breast, heart, kidney)
prostate_svs, prostate_path = prostate_tissue()
slide = Slide(prostate_path, processed_path="output/")
# Inspect properties
print(f"Dimensions: {slide.dimensions}") # (width, height) at level 0
print(f"Levels: {slide.levels}") # Number of pyramid levels
print(f"Level dims: {slide.level_dimensions}") # Dimensions per level
print(f"Magnification: {slide.properties.get('openslide.objective-power', 'N/A')}")
print(f"MPP-X: {slide.properties.get('openslide.mpp-x', 'N/A')}")
# Thumbnail and scaled image
slide.save_thumbnail() # Saves to processed_path
scaled = slide.scaled_image(scale_factor=32)
# Extract region at specific coordinates
region = slide.extract_region(location=(1000, 2000), size=(512, 512), level=0)
模块 2:组织检测
掩码类用于识别组织区域,并在提取图块时过滤背景。
from histolab.masks import TissueMask, BiggestTissueBoxMask, BinaryMask
import numpy as np
# TissueMask: segments ALL tissue regions (multiple sections)
tissue_mask = TissueMask()
mask_array = tissue_mask(slide) # Binary NumPy array: True=tissue, False=background
print(f"Tissue coverage: {mask_array.sum() / mask_array.size * 100:.1f}%")
# BiggestTissueBoxMask: bounding box of largest tissue region (default)
biggest_mask = BiggestTissueBoxMask()
# Visualize mask on slide thumbnail
slide.locate_mask(tissue_mask)
# Custom mask via BinaryMask subclass
class RectangularROI(BinaryMask):
def __init__(self, x, y, w, h):
self.x, self.y, self.w, self.h = x, y, w, h
def _mask(self, slide):
thumb = slide.thumbnail
mask = np.zeros(thumb.shape[:2], dtype=bool)
mask[self.y:self.y+self.h, self.x:self.x+self.w] = True
return mask
模块 3:图块提取
提供三种图块提取策略:随机采样、网格覆盖和基于评分的选择。
from histolab.tiler import RandomTiler, GridTiler, ScoreTiler
from histolab.scorer import NucleiScorer
from histolab.masks import TissueMask
# RandomTiler: fixed number of randomly positioned tiles
random_tiler = RandomTiler(
tile_size=(512, 512), n_tiles=100, level=0,
seed=42, check_tissue=True, tissue_percent=80.0
)
random_tiler.locate_tiles(slide, n_tiles=20) # Preview first
random_tiler.extract(slide)
# GridTiler: systematic grid coverage
grid_tiler = GridTiler(
tile_size=(512, 512), level=0,
pixel_overlap=0, check_tissue=True, tissue_percent=70.0
)
grid_tiler.extract(slide, extraction_mask=TissueMask())
# ScoreTiler: top-ranked tiles by scoring function
score_tiler = ScoreTiler(
tile_size=(512, 512), n_tiles=50, level=0,
scorer=NucleiScorer(), check_tissue=True
)
score_tiler.extract(slide, report_path="tiles_report.csv")
# Report CSV: tile_name, x_coord, y_coord, level, score, tissue_percent
模块 4:过滤器和预处理
用于组织检测和预处理的可组合图像过滤器与形态学过滤器。
from histolab.filters.image_filters import (
RgbToGrayscale, RgbToHsv, RgbToHed,
OtsuThreshold, AdaptiveThreshold,
StretchContrast, HistogramEqualization, Invert
)
from histolab.filters.morphological_filters import (
BinaryDilation, BinaryErosion, BinaryOpening, BinaryClosing,
RemoveSmallObjects, RemoveSmallHoles
)
from histolab.filters.compositions import Compose
# Standard tissue detection pipeline
tissue_pipeline = Compose([
RgbToGrayscale(),
OtsuThreshold(),
BinaryDilation(disk_size=5),
RemoveSmallHoles(area_threshold=1000),
RemoveSmallObjects(area_threshold=500)
])
# Use custom pipeline with TissueMask
from histolab.masks import TissueMask
custom_mask = TissueMask(filters=tissue_pipeline)
# Stain deconvolution (H&E)
hed_filter = RgbToHed() # Hematoxylin-Eosin-DAB separation
# Apply filters to individual tiles
from histolab.tile import Tile
filter_chain = Compose([RgbToGrayscale(), StretchContrast()])
filtered_tile = tile.apply_filters(filter_chain)
# Lambda for custom inline filters
from histolab.filters.image_filters import Lambda
import numpy as np
brightness = Lambda(lambda img: np.clip(img * 1.2, 0, 255).astype(np.uint8))
red_channel = Lambda(lambda img: img[:, :, 0])
模块 5:评分
评分器根据图块的组织内容质量对其排序,以供 ScoreTiler 使用。
from histolab.scorer import NucleiScorer, CellularityScorer, Scorer
import numpy as np
# Built-in scorers
nuclei = NucleiScorer() # Scores by nuclei density (grayscale threshold + count)
cellularity = CellularityScorer() # Scores by overall cellular content
# Custom scorer
class ColorVarianceScorer(Scorer):
def __call__(self, tile):
"""Score tiles by color variance (higher = more informative)."""
tile_array = np.array(tile.image)
return np.var(tile_array, axis=(0, 1)).sum()
score_tiler = ScoreTiler(
tile_size=(512, 512), n_tiles=30,
scorer=ColorVarianceScorer()
)
模块 6:可视化
用于检查切片、掩码和图块的内置方法及 matplotlib 用法示例。
import matplotlib.pyplot as plt
from histolab.masks import TissueMask
# Built-in: mask overlay on slide thumbnail
slide.locate_mask(TissueMask())
# Built-in: tile location preview
tiler.locate_tiles(slide, n_tiles=20)
# Manual side-by-side: slide vs mask
mask = TissueMask()
mask_array = mask(slide)
fig, axes = plt.subplots(1, 2, figsize=(15, 7))
axes[0].imshow(slide.thumbnail); axes[0].set_title("Slide"); axes[0].axis('off')
axes[1].imshow(mask_array, cmap='gray'); axes[1].set_title("Mask"); axes[1].axis('off')
plt.tight_layout()
plt.show()
# Display extracted tiles in grid
from pathlib import Path
from PIL import Image
tile_paths = list(Path("output/tiles/").glob("*.png"))[:16]
fig, axes = plt.subplots(4, 4, figsize=(12, 12))
for idx, tp in enumerate(axes.ravel()):
if idx < len(tile_paths):
tp.imshow(Image.open(tile_paths[idx]))
tp.set_title(tile_paths[idx].stem, fontsize=8)
tp.axis('off')
plt.tight_layout()
plt.show()
核心概念
WSI 金字塔层级
全切片图像采用包含多个分辨率层级的金字塔结构。第 0 层分辨率最高(原生扫描分辨率)。更高层级的分辨率逐级降低,以便更快地访问图像。
for level in range(slide.levels):
dims = slide.level_dimensions[level]
downsample = slide.level_downsamples[level]
print(f"Level {level}: {dims}, downsample: {downsample:.0f}x")
# Level 0: (98304, 221184), downsample: 1x
# Level 1: (24576, 55296), downsample: 4x
过滤器组合模式
过滤器旨在通过 Compose 链式组合。一个过滤器的输出将成为下一个过滤器的输入。图像过滤器处理 RGB/灰度数组;形态学过滤器处理二值数组。顺序很重要:在应用后续过滤器之前,务必先转换为预期的输入类型。
掩码与图块提取器的集成
所有图块提取器都接受 extraction_mask 参数。默认值为 BiggestTissueBoxMask()。对于包含多个组织切片的切片,可将其替换为 TissueMask();对于特定 ROI 的提取,可使用自定义的 BinaryMask 子类。
常见工作流
工作流 1:探索性切片分析
目标:快速检查切片、检测组织,并采样多个不同区域以供审查。
from histolab.slide import Slide
from histolab.tiler import RandomTiler
from histolab.masks import TissueMask
import matplotlib.pyplot as plt
import logging
logging.basicConfig(level=logging.INFO)
slide = Slide("slide.svs", processed_path="output/exploratory/")
print(f"Dimensions: {slide.dimensions}, Levels: {slide.levels}")
slide.save_thumbnail()
# Visualize tissue detection
tissue_mask = TissueMask()
slide.locate_mask(tissue_mask)
mask_arr = tissue_mask(slide)
print(f"Tissue coverage: {mask_arr.sum() / mask_arr.size * 100:.1f}%")
# Sample tiles
tiler = RandomTiler(
tile_size=(512, 512), n_tiles=50, level=0,
seed=42, check_tissue=True, tissue_percent=80.0
)
tiler.locate_tiles(slide, n_tiles=20)
tiler.extract(slide)
工作流 2:深度学习数据集准备
目标:从多个切片构建经过质量控制的图块数据集,用于模型训练。
from pathlib import Path
from histolab.slide import Slide
from histolab.tiler import ScoreTiler
from histolab.scorer import NucleiScorer
import pandas as pd
import logging
logging.basicConfig(level=logging.INFO)
slide_dir = Path("slides/")
output_base = Path("output/dataset/")
all_reports = []
tiler = ScoreTiler(
tile_size=(512, 512), n_tiles=100, level=0,
scorer=NucleiScorer(), check_tissue=True, tissue_percent=80.0
)
for slide_path in sorted(slide_dir.glob("*.svs")):
out_dir = output_base / slide_path.stem
out_dir.mkdir(parents=True, exist_ok=True)
slide = Slide(str(slide_path), processed_path=str(out_dir))
slide.save_thumbnail()
report_path = str(out_dir / "report.csv")
tiler.extract(slide, report_path=report_path)
df = pd.read_csv(report_path)
df["slide"] = slide_path.stem
all_reports.append(df)
print(f"{slide_path.stem}: {len(df)} tiles, mean score {df['score'].mean():.3f}")
combined = pd.concat(all_reports, ignore_index=True)
combined.to_csv(output_base / "dataset_manifest.csv", index=False)
print(f"Total: {len(combined)} tiles from {len(all_reports)} slides")
工作流 3:自定义组织检测与伪影去除
目标:使用自定义过滤器流水线处理带有笔迹标注或染色异常的切片。
from histolab.slide import Slide
from histolab.masks import TissueMask
from histolab.tiler import GridTiler
from histolab.filters.compositions import Compose
from histolab.filters.image_filters import RgbToGrayscale, OtsuThreshold
from histolab.filters.morphological_filters import (
BinaryDilation, RemoveSmallObjects, RemoveSmallHoles
)
# Aggressive artifact removal pipeline
aggressive_filters = Compose([
RgbToGrayscale(),
OtsuThreshold(),
BinaryDilation(disk_size=10),
RemoveSmallHoles(area_threshold=5000),
RemoveSmallObjects(area_threshold=3000)
])
custom_mask = TissueMask(filters=aggressive_filters)
slide = Slide("artifact_slide.svs", processed_path="output/clean/")
# Compare default vs custom mask
slide.locate_mask(TissueMask()) # Default
slide.locate_mask(custom_mask) # Custom (tighter)
# Extract with custom mask
grid_tiler = GridTiler(
tile_size=(512, 512), level=1,
check_tissue=True, tissue_percent=70.0
)
grid_tiler.extract(slide, extraction_mask=custom_mask)
关键参数
| 参数 | 模块 | 默认值 | 范围 / 选项 | 效果 |
|---|---|---|---|---|
tile_size | 所有 Tiler | (512, 512) | 任意 (w, h) 元组 | 切片的像素尺寸 |
level | 所有 Tiler | 0 | 0 到 slide.levels-1 | 金字塔层级(0 = 最高分辨率) |
check_tissue | 所有 Tiler | True | True/False | 根据组织内容筛选切片 |
tissue_percent | 所有 Tiler | 80.0 | 0.0-100.0 | 最低组织覆盖率阈值 |
n_tiles | Random/ScoreTiler | 不定 | 任意正整数 | 要提取的切片数量 |
seed | RandomTiler | None | 任意整数 | 用于确保可复现性的随机种子 |
pixel_overlap | GridTiler | 0 | 0+ | 相邻切片之间的像素重叠量 |
scorer | ScoreTiler | 必填 | NucleiScorer()、CellularityScorer()、自定义 | 用于切片排序的评分函数 |
extraction_mask | 所有 Tiler | BiggestTissueBoxMask() | 任意 BinaryMask | 定义有效提取区域的掩码 |
disk_size | 形态学滤镜 | 5 | 1-20 | 结构元素尺寸 |
area_threshold | RemoveSmall* | 500/1000 | 0+ | 对象/孔洞的最小像素面积 |
最佳实践
-
提取前务必预览:调用
locate_tiles()和locate_mask(),在开始完整提取前验证设置。这能为大型切片集合节省数小时。 -
根据分析分辨率选择层级:层级 0 可提供最高细节,但速度较慢;层级 1-2 通常足以进行初步分析。只有需要细胞级细节的任务才使用层级 0。
-
选择合适的切片器策略:
RandomTiler用于探索性分析和多样化采样GridTiler用于完整覆盖和空间分析ScoreTiler用于以质量为导向的数据集整理
-
使用种子确保可复现性:始终为
RandomTiler设置seed,以确保每次运行选择的切片一致。 -
针对特定染色自定义掩码:H&E 和 IHC 染色具有不同的颜色特征。调整滤镜参数,或为非标准染色构建自定义
Compose管道。 -
反模式——
BiggestTissueBoxMask已足够时不要使用TissueMask:TissueMask的计算开销更大。只有在切片包含多个组织区域时才使用它。 -
批处理时启用日志记录:
logging.basicConfig(level=logging.INFO)可在提取过程中跟踪进度。 -
使用 ScoreTiler 生成 CSV 报告:在
extract()中使用report_path,为下游 ML 管道创建元数据清单。 -
反模式——初步探索时不要在层级 0 提取:使用层级 1 或 2 快速迭代,然后切换到层级 0 生成最终数据集。
常用示例
示例:细胞核增强管道
适用场景:从 H&E 染色切片中分离并增强细胞核信号,以供分析。
from histolab.filters.image_filters import RgbToHed, HistogramEqualization, Lambda
from histolab.filters.compositions import Compose
nuclei_pipeline = Compose([
RgbToHed(),
Lambda(lambda hed: hed[:, :, 0]), # Extract hematoxylin channel
HistogramEqualization()
])
# Apply to slide thumbnail for visualization
enhanced = nuclei_pipeline(slide.thumbnail)
示例:评分分布分析
适用场景:评估切片质量分布,并确定最佳评分阈值。
import pandas as pd
import matplotlib.pyplot as plt
report = pd.read_csv("tiles_report.csv")
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[0].hist(report['score'], bins=30, edgecolor='black', alpha=0.7)
axes[0].set_xlabel('Tile Score'); axes[0].set_ylabel('Frequency')
axes[0].set_title('Score Distribution')
axes[1].scatter(report['tissue_percent'], report['score'], alpha=0.5)
axes[1].set_xlabel('Tissue %'); axes[1].set_ylabel('Score')
axes[1].set_title('Score vs Tissue Coverage')
plt.tight_layout()
plt.savefig("quality_analysis.png", dpi=150, bbox_inches='tight')
plt.show()
示例:多层级分层提取
适用场景:从相同位置提取多个放大层级的切片,以进行多尺度分析。
from histolab.tiler import RandomTiler
for level in [0, 1, 2]:
tiler = RandomTiler(
tile_size=(512, 512), n_tiles=50,
level=level, seed=42, # Same seed = same locations
prefix=f"level{level}_"
)
tiler.extract(slide)
print(f"Extracted level {level} tiles")
故障排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
OpenSlideError: Unsupported format | 未安装 OpenSlide C 库,或不支持该切片格式 | 安装 OpenSlide 系统库;使用 openslide-show-properties 验证格式 |
| 未提取到图块 | tissue_percent 过高,或掩膜未覆盖组织 | 降低 tissue_percent(尝试 60-70%);使用 locate_mask() 预览掩膜 |
| 背景图块过多 | check_tissue=False 或掩膜效果不佳 | 启用 check_tissue=True;提高 tissue_percent;使用 TissueMask() |
| 提取速度非常慢 | 对大型切片使用第 0 级,或在多个区域上使用 TissueMask | 在第 1-2 级提取;使用 BiggestTissueBoxMask;减少 n_tiles |
| 图块包含笔迹伪影 | 默认掩膜包含笔迹 | 使用基于 HSV 的笔迹检测构建自定义筛选器;提高 RemoveSmallObjects 阈值 |
提取期间出现 MemoryError | 对非常大的 WSI 进行第 0 级图块访问 | 在较低级别提取;减少每批处理的图块数 |
| 多次运行的结果不一致 | RandomTiler 中缺少 seed | 始终设置 seed 参数 |
| 图块对模型来说过小或过大 | tile_size 与模型输入尺寸不匹配 | 调整 tile_size 以符合模型要求(通常为 224、256、512) |
附带资源
references/filters_preprocessing.md
全面的筛选器参考,涵盖所有图像筛选器(RgbToGrayscale、RgbToHsv、RgbToHed、OtsuThreshold、AdaptiveThreshold、Invert、StretchContrast、HistogramEqualization、Lambda)和形态学筛选器(BinaryDilation、BinaryErosion、BinaryOpening、BinaryClosing、RemoveSmallObjects、RemoveSmallHoles),包含各筛选器的代码示例、用例说明和常见预处理流程(组织检测、笔迹去除、细胞核增强、染色归一化)。
- 涵盖内容:所有筛选器类型及各自的代码块、
Compose链式调用、质量控制筛选器、自定义掩膜集成 - 已移至行内:标准组织检测流程和 Lambda 筛选器基础知识已移至 Core API Module 4
- 省略内容:筛选器效果的分步可视化(见 references/visualization_slides.md);最佳实践列表已部分整合到主最佳实践部分
references/tile_extraction.md
详细的图块提取参考,涵盖全部三种图块提取策略(RandomTiler、GridTiler、ScoreTiler)的完整参数文档、所有内置评分器(NucleiScorer、CellularityScorer)、自定义评分器创建、带日志记录和 CSV 报告的提取工作流,以及高级模式(多级、分层、提取后模糊筛选)。
- 涵盖内容:各图块提取器的参数和用例、评分器 API、图块预览、带报告的提取、高级模式、性能优化
- 已移至行内:基础图块提取器用法和评分器创建已移至 Core API Modules 3 和 5;常用参数表已移至关键参数部分
- 省略内容:ASCII 网格模式示意图(简单的视觉辅助)
references/visualization_slides.md
整合了可视化、幻灯片管理和组织掩膜参考内容。涵盖幻灯片检查工作流、掩膜对比可视化、切片网格显示、质量评估(评分分布、最佳/最差切片对比)、多幻灯片集合缩略图、组织覆盖率条形图、筛选效果可视化流程、PDF 报告生成,以及交互式 Jupyter 控件。
- 来源:整合了 visualization.md(547 行)+ slide_management.md(172 行)+ tissue_masks.md(251 行)
- 涵盖内容:原有的所有可视化模式、幻灯片检查工作流、示例数据集、金字塔层级枚举、掩膜类别及自定义、标注排除模式
- 已内联迁移:核心幻灯片属性和掩膜基础知识已移至 Core API Modules 1-2;缩略图显示及基础的 locate_mask/locate_tiles 已移至 Module 6
- 已省略:幻灯片名称/路径等简单属性(2 行);通过手动计算坐标实现的自定义切片位置可视化(仅为概念性内容,不具实用性)
原始参考文件处置情况(5 个文件):
- slide_management.md(172 行)—— (b) 已整合:核心内容已移至 Core API Module 1(Slide 类、属性、示例数据、缩略图、区域、金字塔层级);高级幻灯片检查和多幻灯片工作流已移至 references/visualization_slides.md
- tissue_masks.md(251 行)—— (b) 已整合:核心掩膜类及用法已移至 Core API Module 2;自定义掩膜(RectangularMask、AnnotationExclusionMask)及掩膜对比已移至 references/visualization_slides.md
- tile_extraction.md(421 行)—— (a) 已迁移至 references/tile_extraction.md,并进行了精简
- filters_preprocessing.md(514 行)—— (a) 已迁移至 references/filters_preprocessing.md,并进行了精简
- visualization.md(547 行)—— (a) 已迁移至 references/visualization_slides.md(与 slide_management.md 和 tissue_masks.md 的内容合并)
相关技能
- pathml-spatial-omics (planned) —— 高级多重成像和空间蛋白质组学
- matplotlib-scientific-plotting —— 基于 histolab 输出生成符合出版标准的图表
参考资料
- Histolab 文档 —— 官方 API 参考和教程
- Histolab GitHub 仓库 —— 源代码和示例
- OpenSlide —— 用于支持 WSI 格式的底层 C 库
- TCGA 示例数据 —— 内置示例数据集的来源