本工具提供了完整的端到端(E2E)WSI(全切片图像)处理流程,从原始slide文件到最终的推理结果。
本工具实现了完整的WSI处理流水线:
pip install -r requirements.txt
主要依赖包括:
根据您使用的模型,需要准备相应的权重文件:
.pt或.pth格式)权重文件可以通过以下方式获取:
python run_slide_encoder_inference.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path /path/to/uni2_weights.pt \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path /path/to/slide_encoder_weights.pt
在推理时同时保存patch级别的attention权重,用于后续可视化:
python run_slide_encoder_inference.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path /path/to/uni2_weights.pt \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path /path/to/slide_encoder_weights.pt \
--save_attention_map \
--attention_map_type both
输出结果会包含:
attention_maps/{slide_name}_attention.npz: Attention权重和坐标数据attention_maps/{slide_name}_attention_meta.json: 统计信息attention_maps/{slide_name}_heatmap.png: 热力图可视化(当type包含heatmap)attention_maps/{slide_name}_spatial.png: 空间分布图(当type包含spatial)如果已有保存的attention map文件,可以直接生成heatmap可视化,无需重新计算:
python run_heatmap_visualization.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--attention_from_file \
--attention_map_path /path/to/output/attention_maps/slide_name_attention.npz \
--heatmap_save_dir /path/to/heatmap_output
使用 --extract_features_only 参数可以跳过slide encoder推理,仅提取并保存patch特征:
python run_slide_encoder_inference.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path /path/to/uni2_weights.pt \
--extract_features_only
此模式下不需要指定 --slide_encoder 和 --slide_encoder_weights_path 参数。
工具支持多种WSI格式:
.svs (Aperio).tiff / .tiffrom trident import load_wsi
slide = load_wsi(
slide_path=args.slide_path,
lazy_init=False,
custom_mpp_keys=args.custom_mpp_keys
)
识别并提取WSI中的组织区域,可选的分割器:
hest:默认分割器grandqc:高质量分割器分割结果保存为GeoJSON格式,如果已存在分割结果,会自动跳过此步骤。
# 使用自定义分割权重
--seg_weights_path /path/to/seg_weights.ckpt \
--segmenter hest \
--seg_conf_thresh 0.5
从分割的组织区域中提取patch坐标:
--mag: 提取的放大倍数(5, 10, 20, 40)--patch_size: Patch大小(默认256像素)--overlap: Patch之间的重叠像素数(默认0)坐标信息保存为H5格式,包含每个patch的坐标位置。
使用指定的patch encoder提取每个patch的特征表示。
支持的Patch Encoder包括:
uni_v2: UNI2(推荐用于医学图像)uni_v1: UNI v1conch_v1, conch_v15: CONCH系列ctranspath: CTransPathphikon, phikon_v2: Phikon系列virchow, virchow2: Virchow系列gigapath: GigaPath特征提取结果保存为H5格式,包含:
coords: Patch坐标数组features: Patch特征矩阵 (N × feature_dim)coords_attrs: 坐标相关属性(如patch_size, mag等)如果未使用 --extract_features_only 参数,将使用slide encoder对patch特征进行聚合,得到slide级别的表示,并进行下游任务推理。
支持的Slide Encoder包括:
gfy_abmil: ABMIL-based slide encoderthreads: THREADS模型titan: Titan模型prism: PRISM模型chief: CHIEF模型gigapath: GigaPath slide encodermean-*: 简单的平均池化推理结果以概率分布的形式输出。
注意:如果使用 --extract_features_only,将跳过此步骤,仅保存patch特征文件。
使用 --extract_features_only 参数,仅提取并保存patch特征,不执行slide encoder推理:
python run_slide_encoder_inference.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path /path/to/uni2_weights.pt \
--extract_features_only \
--batch_size 32
输出结果:
{job_dir}/{mag}x_{patch_size}px_{overlap}px_overlap/features_uni_v2/{slide_name}.h5coords: (N, 2) 坐标数组features: (N, 1536) UNI2特征矩阵(1536维)coords_attrs: 元数据import h5py
import numpy as np
# 加载特征文件
with h5py.File('path/to/slide.h5', 'r') as f:
coords = f['coords'][:] # (N, 2)
features = f['features'][:] # (N, 1536)
attrs = dict(f['coords'].attrs)
print(f"提取了 {len(coords)} 个patches")
print(f"特征维度: {features.shape[1]}")
UNI2 (UNI v2) 是一个大型视觉Transformer模型,专门用于医学图像分析:
权重文件可以从以下来源获取:
MahmoodLab/UNI2-hAttention map提供了模型决策过程的可视化,帮助理解:
在run_slide_encoder_inference.py中添加--save_attention_map参数:
python run_slide_encoder_inference.py \
--slide_path slide.svs \
--job_dir output/ \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path encoder.pt \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path model.pt \
--save_attention_map \
--attention_map_type both
参数说明:
| 参数 | 可选值 | 说明 |
|---|---|---|
--save_attention_map | - | 启用attention保存(布尔标志) |
--attention_map_type | heatmap, spatial, both | 输出类型(默认heatmap) |
输出文件:
attention_maps/{slide}_attention.npz: 原始权重数据(coords和weights)attention_maps/{slide}_attention_meta.json: 统计信息(min, max, mean, std等)attention_maps/{slide}_heatmap.png: 热力图可视化attention_maps/{slide}_spatial.png: 空间分布图方式A: 从保存的attention map生成(推荐)
python run_heatmap_visualization.py \
--slide_path slide.svs \
--job_dir output/ \
--mag 20 \
--patch_size 256 \
--attention_from_file \
--attention_map_path output/attention_maps/slide_name_attention.npz \
--heatmap_save_dir output/heatmaps
优势:快速、高效,无需重新计算attention
方式B: 直接从slide encoder计算(原始方式)
python run_heatmap_visualization.py \
--slide_path slide.svs \
--job_dir output/ \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path encoder.pt \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path model.pt \
--heatmap_save_dir output/heatmaps
python run_heatmap_visualization.py \
--slide_path slide.svs \
--job_dir output/ \
--attention_from_file \
--attention_map_path output/attention_maps/slide_attention.npz \
--vis_level 4 \ # 可视化倍数
--cmap coolwarm \ # 颜色map (jet, viridis等)
--alpha 0.5 \ # 透明度 (0-1)
--blank_canvas \ # 使用空白背景而非原始图像
--blur \ # 应用高斯模糊
--convert_to_percentiles \ # 转换为百分位数
--binarize \ # 二值化
--binary_thresh 0.5 # 二值化阈值
加载和分析attention map数据:
import numpy as np
import json
# 加载attention map
data = np.load('attention_maps/slide_attention.npz')
attention_weights = data['attention_weights'] # (N,)
coords = data['coords'] # (N, 2)
# 加载元数据
with open('attention_maps/slide_attention_meta.json', 'r') as f:
meta = json.load(f)
# 获取top patches
top_k = 20
top_indices = np.argsort(attention_weights)[-top_k:][::-1]
top_coords = coords[top_indices]
top_weights = attention_weights[top_indices]
print(f"Total patches: {len(attention_weights)}")
print(f"Attention range: {attention_weights.min():.4f} - {attention_weights.max():.4f}")
print(f"Mean attention: {attention_weights.mean():.4f}")
print(f"\nTop {top_k} patches:")
for i, (coord, weight) in enumerate(zip(top_coords, top_weights), 1):
print(f" {i}. Coord: {coord}, Weight: {weight:.6f}")
所有支持的patch encoder都可以用于attention map提取:
完全支持 attention权重返回:
gfy_abmil (GFY-ABMIL) ✅ 推荐abmil (Attention-based MIL) ✅其他模型 可能需要验证支持情况。
# 推理并保存attention
python run_slide_encoder_inference.py \
--slide_path tumor_slide.svs \
--job_dir results/ \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path tumor_model.pt \
--save_attention_map \
--attention_map_type both
# 生成高质量heatmap用于报告
python run_heatmap_visualization.py \
--slide_path tumor_slide.svs \
--job_dir results/ \
--attention_from_file \
--attention_map_path results/attention_maps/tumor_slide_attention.npz \
--vis_level 4 \
--cmap coolwarm \
--alpha 0.6
分析:
import os
import subprocess
slides = ['slide1.svs', 'slide2.svs', 'slide3.svs']
for slide in slides:
slide_name = os.path.splitext(slide)[0]
# 推理并保存attention
subprocess.run([
'python', 'run_slide_encoder_inference.py',
'--slide_path', f'slides/{slide}',
'--job_dir', 'batch_output/',
'--slide_encoder', 'gfy_abmil',
'--slide_encoder_weights_path', 'model.pt',
'--save_attention_map'
])
# 生成heatmap
subprocess.run([
'python', 'run_heatmap_visualization.py',
'--slide_path', f'slides/{slide}',
'--job_dir', 'batch_output/',
'--attention_from_file',
'--attention_map_path', f'batch_output/attention_maps/{slide_name}_attention.npz',
'--heatmap_save_dir', 'heatmaps/'
])
import numpy as np
import json
# 比较两个模型的attention差异
model1_data = np.load('attention_maps/model1_attention.npz')
model2_data = np.load('attention_maps/model2_attention.npz')
attn1 = model1_data['attention_weights']
attn2 = model2_data['attention_weights']
# 计算相关性
correlation = np.corrcoef(attn1, attn2)[0, 1]
print(f"Attention correlation between models: {correlation:.4f}")
# 找到最不同的patches
diff = np.abs(attn1 - attn2)
different_indices = np.argsort(diff)[-20:]
print(f"Top 20 most different patches: {different_indices}")
output/
├── attention_maps/ # Attention map结果
│ ├── slide1_attention.npz # 原始权重数据
│ ├── slide1_attention_meta.json # 统计信息
│ ├── slide1_heatmap.png # 热力图
│ └── slide1_spatial.png # 空间分布图
│
└── heatmaps/ # Heatmap可视化结果
├── slide1_heatmap.png # TRIDENT生成的高分辨率heatmap
└── ...
Q: Attention权重的范围是多少?
A: 通常在0-1之间(经过softmax归一化)。在可视化时会进一步扩展到0-255用于图像显示。
Q: 为什么某些slides的attention分布很不均匀?
A: 这通常表明模型对特定区域有强烈的聚焦。可能原因:
Q: 能否用attention权重进行区域级别的决策?
A: 可以。高attention权重的patches对应诊断相关区域,可用于:
Q: 如何选择合适的可视化参数?
A:
vis_level: 根据slide大小,建议4-5alpha: 0.4-0.6用于overlay背景,1.0用于纯热力图cmap: coolwarm/jet用于突出对比,viridis用于单调性blur: 启用以平滑细节| 参数 | 说明 |
|---|---|
--slide_path | WSI文件路径 |
--job_dir | 输出目录 |
--slide_encoder | Slide encoder类型(未使用--extract_features_only时必需) |
--slide_encoder_weights_path | Slide encoder权重路径(未使用--extract_features_only时必需) |
| 参数 | 默认值 | 说明 |
|---|---|---|
--mag | 20 | 提取放大倍数 (5/10/20/40) |
--patch_size | 256 | Patch大小(像素) |
--overlap | 0 | Patch重叠像素数 |
--custom_mpp_keys | None | 自定义MPP键列表 |
| 参数 | 默认值 | 说明 |
|---|---|---|
--segmenter | hest | 分割器类型 (hest/grandqc) |
--seg_weights_path | None | 分割模型权重路径 |
--seg_conf_thresh | 0.5 | 分割置信度阈值 |
--remove_holes | False | 是否移除孔洞 |
--remove_artifacts | False | 是否移除伪影 |
--remove_penmarks | False | 是否移除笔迹标记 |
| 参数 | 默认值 | 说明 |
|---|---|---|
--patch_encoder | uni_v2 | Patch encoder类型 |
--patch_encoder_weights_path | None | Patch encoder权重路径 |
--batch_size | 32 | 特征提取的batch size |
| 参数 | 默认值 | 说明 |
|---|---|---|
--slide_encoder | gfy_abmil | Slide encoder类型(未使用--extract_features_only时必需) |
--slide_encoder_weights_path | None | Slide encoder权重路径(未使用--extract_features_only时必需) |
| 参数 | 默认值 | 说明 |
|---|---|---|
--extract_features_only | False | 仅提取patch特征,跳过slide encoder推理 |
| 参数 | 默认值 | 说明 |
|---|---|---|
--save_attention_map | False | 是否在推理时保存patch级别的attention map |
--attention_map_type | heatmap | 输出类型: heatmap/spatial/both |
| 参数 | 默认值 | 说明 |
|---|---|---|
--attention_from_file | False | 从保存的NPZ文件加载attention map(推荐) |
--attention_map_path | None | 保存的attention map NPZ文件路径 |
--vis_level | 4 | Heatmap可视化层级 |
--cmap | coolwarm | Colormap类型(jet, viridis, cool warm等) |
--alpha | 0.5 | Heatmap透明度 (0-1) |
--blank_canvas | False | 使用纯白背景而非原始WSI |
--blur | True | 应用高斯模糊 |
--convert_to_percentiles | True | 转换为百分位数 |
--binarize | False | 二值化输出 |
--binary_thresh | 0.5 | 二值化阈值 (0-1) |
运行完成后,输出目录结构如下:
job_dir/
├── contours_geojson/ # 组织分割结果(GeoJSON格式)
│ └── {slide_name}.geojson
│
├── {mag}x_{patch_size}px_{overlap}px_overlap/
│ ├── patches/ # Patch坐标文件
│ │ └── {slide_name}_patches.h5
│ │
│ └── features_{patch_encoder}/ # Patch特征文件
│ └── {slide_name}.h5
│
├── attention_maps/ # Attention map结果(当--save_attention_map时)
│ ├── {slide_name}_attention.npz # 原始权重和坐标数据
│ ├── {slide_name}_attention_meta.json # 统计信息
│ ├── {slide_name}_heatmap.png # 热力图可视化
│ └── {slide_name}_spatial.png # 空间分布图(可选)
│
└── heatmaps/ # Heatmap可视化结果(run_heatmap_visualization.py输出)
└── {slide_name}_heatmap.png
Patch坐标文件 (*_patches.h5):
coords: (N, 2) numpy数组,包含每个patch的(x, y)坐标coords_attrs: 属性字典(mag, patch_size等)Patch特征文件 (features_*/{slide_name}.h5):
coords: (N, 2) 坐标数组features: (N, feature_dim) 特征矩阵coords_attrs: 坐标属性NPZ文件 ({slide_name}_attention.npz):
attention_weights: (N,) 权重数组,值在0-1之间(softmax后)coords: (N, 2) patch坐标数组JSON元数据 ({slide_name}_attention_meta.json):
{
"slide_name": "slide_name",
"n_patches": 12345,
"attention_stats": {
"min": 0.0001,
"max": 0.0432,
"mean": 0.000812,
"std": 0.001234
},
"patch_size": 256
}
A: 可以从以下途径获取:
MahmoodLab/UNI2-hUNI_CKPT_PATH环境变量,工具会自动使用该路径--patch_encoder_weights_path显式指定A:
A:
--batch_size--patch_size--mag(如10x而非20x)A: 支持OpenSlide支持的所有格式,包括:
.svs (Aperio).ndpi (Hamamatsu).vms, .vmu, .ndpi (Hamamatsu).scn (Leica).mrxs (3DHistech).tiff, .tifA: 分为两步:
第一步: 推理时保存attention map
python run_slide_encoder_inference.py \
--slide_path slide.svs \
--job_dir output/ \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path model.pt \
--save_attention_map
第二步: 生成heatmap可视化
python run_heatmap_visualization.py \
--slide_path slide.svs \
--job_dir output/ \
--attention_from_file \
--attention_map_path output/attention_maps/slide_attention.npz
A: 主要用途包括:
A: 从文件加载的优势:
推荐流程:
推理(一次) → 保存attention → 可视化(多次,不同参数)
A: 使用Python API对比:
import numpy as np
# 加载两个模型的attention map
data1 = np.load('model1_attention.npz')
data2 = np.load('model2_attention.npz')
attn1 = data1['attention_weights']
attn2 = data2['attention_weights']
# 计算相关性
corr = np.corrcoef(attn1, attn2)[0, 1]
print(f"模型间Attention相关性: {corr:.4f}")
# 找出差异最大的patches
diff = np.abs(attn1 - attn2)
different_indices = np.argsort(diff)[-20:] # Top 20
本工具基于TRIDENT框架构建,主要模块:
trident.wsi_objects: WSI加载和处理trident.segmentation_models: 组织分割模型trident.patch_encoder_models: Patch特征编码器trident.slide_encoder_models: Slide级别编码器更多技术细节请参考代码注释。
Python
100.0%
本工具提供了完整的端到端(E2E)WSI(全切片图像)处理流程,从原始slide文件到最终的推理结果。
本工具实现了完整的WSI处理流水线:
pip install -r requirements.txt
主要依赖包括:
根据您使用的模型,需要准备相应的权重文件:
.pt或.pth格式)权重文件可以通过以下方式获取:
python run_slide_encoder_inference.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path /path/to/uni2_weights.pt \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path /path/to/slide_encoder_weights.pt
在推理时同时保存patch级别的attention权重,用于后续可视化:
python run_slide_encoder_inference.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path /path/to/uni2_weights.pt \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path /path/to/slide_encoder_weights.pt \
--save_attention_map \
--attention_map_type both
输出结果会包含:
attention_maps/{slide_name}_attention.npz: Attention权重和坐标数据attention_maps/{slide_name}_attention_meta.json: 统计信息attention_maps/{slide_name}_heatmap.png: 热力图可视化(当type包含heatmap)attention_maps/{slide_name}_spatial.png: 空间分布图(当type包含spatial)如果已有保存的attention map文件,可以直接生成heatmap可视化,无需重新计算:
python run_heatmap_visualization.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--attention_from_file \
--attention_map_path /path/to/output/attention_maps/slide_name_attention.npz \
--heatmap_save_dir /path/to/heatmap_output
使用 --extract_features_only 参数可以跳过slide encoder推理,仅提取并保存patch特征:
python run_slide_encoder_inference.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path /path/to/uni2_weights.pt \
--extract_features_only
此模式下不需要指定 --slide_encoder 和 --slide_encoder_weights_path 参数。
工具支持多种WSI格式:
.svs (Aperio).tiff / .tiffrom trident import load_wsi
slide = load_wsi(
slide_path=args.slide_path,
lazy_init=False,
custom_mpp_keys=args.custom_mpp_keys
)
识别并提取WSI中的组织区域,可选的分割器:
hest:默认分割器grandqc:高质量分割器分割结果保存为GeoJSON格式,如果已存在分割结果,会自动跳过此步骤。
# 使用自定义分割权重
--seg_weights_path /path/to/seg_weights.ckpt \
--segmenter hest \
--seg_conf_thresh 0.5
从分割的组织区域中提取patch坐标:
--mag: 提取的放大倍数(5, 10, 20, 40)--patch_size: Patch大小(默认256像素)--overlap: Patch之间的重叠像素数(默认0)坐标信息保存为H5格式,包含每个patch的坐标位置。
使用指定的patch encoder提取每个patch的特征表示。
支持的Patch Encoder包括:
uni_v2: UNI2(推荐用于医学图像)uni_v1: UNI v1conch_v1, conch_v15: CONCH系列ctranspath: CTransPathphikon, phikon_v2: Phikon系列virchow, virchow2: Virchow系列gigapath: GigaPath特征提取结果保存为H5格式,包含:
coords: Patch坐标数组features: Patch特征矩阵 (N × feature_dim)coords_attrs: 坐标相关属性(如patch_size, mag等)如果未使用 --extract_features_only 参数,将使用slide encoder对patch特征进行聚合,得到slide级别的表示,并进行下游任务推理。
支持的Slide Encoder包括:
gfy_abmil: ABMIL-based slide encoderthreads: THREADS模型titan: Titan模型prism: PRISM模型chief: CHIEF模型gigapath: GigaPath slide encodermean-*: 简单的平均池化推理结果以概率分布的形式输出。
注意:如果使用 --extract_features_only,将跳过此步骤,仅保存patch特征文件。
使用 --extract_features_only 参数,仅提取并保存patch特征,不执行slide encoder推理:
python run_slide_encoder_inference.py \
--slide_path /path/to/slide.svs \
--job_dir /path/to/output \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path /path/to/uni2_weights.pt \
--extract_features_only \
--batch_size 32
输出结果:
{job_dir}/{mag}x_{patch_size}px_{overlap}px_overlap/features_uni_v2/{slide_name}.h5coords: (N, 2) 坐标数组features: (N, 1536) UNI2特征矩阵(1536维)coords_attrs: 元数据import h5py
import numpy as np
# 加载特征文件
with h5py.File('path/to/slide.h5', 'r') as f:
coords = f['coords'][:] # (N, 2)
features = f['features'][:] # (N, 1536)
attrs = dict(f['coords'].attrs)
print(f"提取了 {len(coords)} 个patches")
print(f"特征维度: {features.shape[1]}")
UNI2 (UNI v2) 是一个大型视觉Transformer模型,专门用于医学图像分析:
权重文件可以从以下来源获取:
MahmoodLab/UNI2-hAttention map提供了模型决策过程的可视化,帮助理解:
在run_slide_encoder_inference.py中添加--save_attention_map参数:
python run_slide_encoder_inference.py \
--slide_path slide.svs \
--job_dir output/ \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path encoder.pt \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path model.pt \
--save_attention_map \
--attention_map_type both
参数说明:
| 参数 | 可选值 | 说明 |
|---|---|---|
--save_attention_map | - | 启用attention保存(布尔标志) |
--attention_map_type | heatmap, spatial, both | 输出类型(默认heatmap) |
输出文件:
attention_maps/{slide}_attention.npz: 原始权重数据(coords和weights)attention_maps/{slide}_attention_meta.json: 统计信息(min, max, mean, std等)attention_maps/{slide}_heatmap.png: 热力图可视化attention_maps/{slide}_spatial.png: 空间分布图方式A: 从保存的attention map生成(推荐)
python run_heatmap_visualization.py \
--slide_path slide.svs \
--job_dir output/ \
--mag 20 \
--patch_size 256 \
--attention_from_file \
--attention_map_path output/attention_maps/slide_name_attention.npz \
--heatmap_save_dir output/heatmaps
优势:快速、高效,无需重新计算attention
方式B: 直接从slide encoder计算(原始方式)
python run_heatmap_visualization.py \
--slide_path slide.svs \
--job_dir output/ \
--mag 20 \
--patch_size 256 \
--patch_encoder uni_v2 \
--patch_encoder_weights_path encoder.pt \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path model.pt \
--heatmap_save_dir output/heatmaps
python run_heatmap_visualization.py \
--slide_path slide.svs \
--job_dir output/ \
--attention_from_file \
--attention_map_path output/attention_maps/slide_attention.npz \
--vis_level 4 \ # 可视化倍数
--cmap coolwarm \ # 颜色map (jet, viridis等)
--alpha 0.5 \ # 透明度 (0-1)
--blank_canvas \ # 使用空白背景而非原始图像
--blur \ # 应用高斯模糊
--convert_to_percentiles \ # 转换为百分位数
--binarize \ # 二值化
--binary_thresh 0.5 # 二值化阈值
加载和分析attention map数据:
import numpy as np
import json
# 加载attention map
data = np.load('attention_maps/slide_attention.npz')
attention_weights = data['attention_weights'] # (N,)
coords = data['coords'] # (N, 2)
# 加载元数据
with open('attention_maps/slide_attention_meta.json', 'r') as f:
meta = json.load(f)
# 获取top patches
top_k = 20
top_indices = np.argsort(attention_weights)[-top_k:][::-1]
top_coords = coords[top_indices]
top_weights = attention_weights[top_indices]
print(f"Total patches: {len(attention_weights)}")
print(f"Attention range: {attention_weights.min():.4f} - {attention_weights.max():.4f}")
print(f"Mean attention: {attention_weights.mean():.4f}")
print(f"\nTop {top_k} patches:")
for i, (coord, weight) in enumerate(zip(top_coords, top_weights), 1):
print(f" {i}. Coord: {coord}, Weight: {weight:.6f}")
所有支持的patch encoder都可以用于attention map提取:
完全支持 attention权重返回:
gfy_abmil (GFY-ABMIL) ✅ 推荐abmil (Attention-based MIL) ✅其他模型 可能需要验证支持情况。
# 推理并保存attention
python run_slide_encoder_inference.py \
--slide_path tumor_slide.svs \
--job_dir results/ \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path tumor_model.pt \
--save_attention_map \
--attention_map_type both
# 生成高质量heatmap用于报告
python run_heatmap_visualization.py \
--slide_path tumor_slide.svs \
--job_dir results/ \
--attention_from_file \
--attention_map_path results/attention_maps/tumor_slide_attention.npz \
--vis_level 4 \
--cmap coolwarm \
--alpha 0.6
分析:
import os
import subprocess
slides = ['slide1.svs', 'slide2.svs', 'slide3.svs']
for slide in slides:
slide_name = os.path.splitext(slide)[0]
# 推理并保存attention
subprocess.run([
'python', 'run_slide_encoder_inference.py',
'--slide_path', f'slides/{slide}',
'--job_dir', 'batch_output/',
'--slide_encoder', 'gfy_abmil',
'--slide_encoder_weights_path', 'model.pt',
'--save_attention_map'
])
# 生成heatmap
subprocess.run([
'python', 'run_heatmap_visualization.py',
'--slide_path', f'slides/{slide}',
'--job_dir', 'batch_output/',
'--attention_from_file',
'--attention_map_path', f'batch_output/attention_maps/{slide_name}_attention.npz',
'--heatmap_save_dir', 'heatmaps/'
])
import numpy as np
import json
# 比较两个模型的attention差异
model1_data = np.load('attention_maps/model1_attention.npz')
model2_data = np.load('attention_maps/model2_attention.npz')
attn1 = model1_data['attention_weights']
attn2 = model2_data['attention_weights']
# 计算相关性
correlation = np.corrcoef(attn1, attn2)[0, 1]
print(f"Attention correlation between models: {correlation:.4f}")
# 找到最不同的patches
diff = np.abs(attn1 - attn2)
different_indices = np.argsort(diff)[-20:]
print(f"Top 20 most different patches: {different_indices}")
output/
├── attention_maps/ # Attention map结果
│ ├── slide1_attention.npz # 原始权重数据
│ ├── slide1_attention_meta.json # 统计信息
│ ├── slide1_heatmap.png # 热力图
│ └── slide1_spatial.png # 空间分布图
│
└── heatmaps/ # Heatmap可视化结果
├── slide1_heatmap.png # TRIDENT生成的高分辨率heatmap
└── ...
Q: Attention权重的范围是多少?
A: 通常在0-1之间(经过softmax归一化)。在可视化时会进一步扩展到0-255用于图像显示。
Q: 为什么某些slides的attention分布很不均匀?
A: 这通常表明模型对特定区域有强烈的聚焦。可能原因:
Q: 能否用attention权重进行区域级别的决策?
A: 可以。高attention权重的patches对应诊断相关区域,可用于:
Q: 如何选择合适的可视化参数?
A:
vis_level: 根据slide大小,建议4-5alpha: 0.4-0.6用于overlay背景,1.0用于纯热力图cmap: coolwarm/jet用于突出对比,viridis用于单调性blur: 启用以平滑细节| 参数 | 说明 |
|---|---|
--slide_path | WSI文件路径 |
--job_dir | 输出目录 |
--slide_encoder | Slide encoder类型(未使用--extract_features_only时必需) |
--slide_encoder_weights_path | Slide encoder权重路径(未使用--extract_features_only时必需) |
| 参数 | 默认值 | 说明 |
|---|---|---|
--mag | 20 | 提取放大倍数 (5/10/20/40) |
--patch_size | 256 | Patch大小(像素) |
--overlap | 0 | Patch重叠像素数 |
--custom_mpp_keys | None | 自定义MPP键列表 |
| 参数 | 默认值 | 说明 |
|---|---|---|
--segmenter | hest | 分割器类型 (hest/grandqc) |
--seg_weights_path | None | 分割模型权重路径 |
--seg_conf_thresh | 0.5 | 分割置信度阈值 |
--remove_holes | False | 是否移除孔洞 |
--remove_artifacts | False | 是否移除伪影 |
--remove_penmarks | False | 是否移除笔迹标记 |
| 参数 | 默认值 | 说明 |
|---|---|---|
--patch_encoder | uni_v2 | Patch encoder类型 |
--patch_encoder_weights_path | None | Patch encoder权重路径 |
--batch_size | 32 | 特征提取的batch size |
| 参数 | 默认值 | 说明 |
|---|---|---|
--slide_encoder | gfy_abmil | Slide encoder类型(未使用--extract_features_only时必需) |
--slide_encoder_weights_path | None | Slide encoder权重路径(未使用--extract_features_only时必需) |
| 参数 | 默认值 | 说明 |
|---|---|---|
--extract_features_only | False | 仅提取patch特征,跳过slide encoder推理 |
| 参数 | 默认值 | 说明 |
|---|---|---|
--save_attention_map | False | 是否在推理时保存patch级别的attention map |
--attention_map_type | heatmap | 输出类型: heatmap/spatial/both |
| 参数 | 默认值 | 说明 |
|---|---|---|
--attention_from_file | False | 从保存的NPZ文件加载attention map(推荐) |
--attention_map_path | None | 保存的attention map NPZ文件路径 |
--vis_level | 4 | Heatmap可视化层级 |
--cmap | coolwarm | Colormap类型(jet, viridis, cool warm等) |
--alpha | 0.5 | Heatmap透明度 (0-1) |
--blank_canvas | False | 使用纯白背景而非原始WSI |
--blur | True | 应用高斯模糊 |
--convert_to_percentiles | True | 转换为百分位数 |
--binarize | False | 二值化输出 |
--binary_thresh | 0.5 | 二值化阈值 (0-1) |
运行完成后,输出目录结构如下:
job_dir/
├── contours_geojson/ # 组织分割结果(GeoJSON格式)
│ └── {slide_name}.geojson
│
├── {mag}x_{patch_size}px_{overlap}px_overlap/
│ ├── patches/ # Patch坐标文件
│ │ └── {slide_name}_patches.h5
│ │
│ └── features_{patch_encoder}/ # Patch特征文件
│ └── {slide_name}.h5
│
├── attention_maps/ # Attention map结果(当--save_attention_map时)
│ ├── {slide_name}_attention.npz # 原始权重和坐标数据
│ ├── {slide_name}_attention_meta.json # 统计信息
│ ├── {slide_name}_heatmap.png # 热力图可视化
│ └── {slide_name}_spatial.png # 空间分布图(可选)
│
└── heatmaps/ # Heatmap可视化结果(run_heatmap_visualization.py输出)
└── {slide_name}_heatmap.png
Patch坐标文件 (*_patches.h5):
coords: (N, 2) numpy数组,包含每个patch的(x, y)坐标coords_attrs: 属性字典(mag, patch_size等)Patch特征文件 (features_*/{slide_name}.h5):
coords: (N, 2) 坐标数组features: (N, feature_dim) 特征矩阵coords_attrs: 坐标属性NPZ文件 ({slide_name}_attention.npz):
attention_weights: (N,) 权重数组,值在0-1之间(softmax后)coords: (N, 2) patch坐标数组JSON元数据 ({slide_name}_attention_meta.json):
{
"slide_name": "slide_name",
"n_patches": 12345,
"attention_stats": {
"min": 0.0001,
"max": 0.0432,
"mean": 0.000812,
"std": 0.001234
},
"patch_size": 256
}
A: 可以从以下途径获取:
MahmoodLab/UNI2-hUNI_CKPT_PATH环境变量,工具会自动使用该路径--patch_encoder_weights_path显式指定A:
A:
--batch_size--patch_size--mag(如10x而非20x)A: 支持OpenSlide支持的所有格式,包括:
.svs (Aperio).ndpi (Hamamatsu).vms, .vmu, .ndpi (Hamamatsu).scn (Leica).mrxs (3DHistech).tiff, .tifA: 分为两步:
第一步: 推理时保存attention map
python run_slide_encoder_inference.py \
--slide_path slide.svs \
--job_dir output/ \
--slide_encoder gfy_abmil \
--slide_encoder_weights_path model.pt \
--save_attention_map
第二步: 生成heatmap可视化
python run_heatmap_visualization.py \
--slide_path slide.svs \
--job_dir output/ \
--attention_from_file \
--attention_map_path output/attention_maps/slide_attention.npz
A: 主要用途包括:
A: 从文件加载的优势:
推荐流程:
推理(一次) → 保存attention → 可视化(多次,不同参数)
A: 使用Python API对比:
import numpy as np
# 加载两个模型的attention map
data1 = np.load('model1_attention.npz')
data2 = np.load('model2_attention.npz')
attn1 = data1['attention_weights']
attn2 = data2['attention_weights']
# 计算相关性
corr = np.corrcoef(attn1, attn2)[0, 1]
print(f"模型间Attention相关性: {corr:.4f}")
# 找出差异最大的patches
diff = np.abs(attn1 - attn2)
different_indices = np.argsort(diff)[-20:] # Top 20
本工具基于TRIDENT框架构建,主要模块:
trident.wsi_objects: WSI加载和处理trident.segmentation_models: 组织分割模型trident.patch_encoder_models: Patch特征编码器trident.slide_encoder_models: Slide级别编码器更多技术细节请参考代码注释。
Python
100.0%