4. WSI元数据提取与标准化
WSI(Whole Slide Image)文件本身是一个多层、多分辨率的金字塔容器,除了像素数据之外,还携带了大量描述“这张切片是怎么来的、怎么扫的、扫成了什么”的元数据。这些元数据决定了后续所有处理步骤的合法性:如果不知道扫描倍率,就无法判断某个放大层级对应多少微米;如果不知道MPP,就无法把像素坐标换算成物理尺寸;如果不知道组织区域在哪里,就会把大量白色背景送进模型白白浪费算力。因此,元数据提取与标准化是WSI数据工程的第一道地基。
一、需要提取哪些元数据
从工程视角看,WSI元数据可以分为四类:扫描参数(扫描倍率、MPP、层数、每层尺寸)、空间信息(组织区域轮廓、有效区域边界框、缩略图)、染色信息(染色类型、染色方案、颜色分布特征)以及文件级信息(格式、厂商、压缩方式、文件大小)。这四类信息分别服务于不同的下游任务:扫描参数用于坐标换算和尺度对齐,空间信息用于裁剪和采样,染色信息用于颜色归一化和质控,文件级信息用于存储管理和格式转换。
不同厂商的WSI格式(如Aperio SVS、Hamamatsu NDPI、Leica SCN、Philips TIFF)在元数据存储方式上差异很大。SVS通常把关键信息写在TIFF标签和ImageDescription的XML中,NDPI使用私有标签,部分格式甚至把MPP藏在厂商自定义字段里。因此,实际工程中往往需要借助OpenSlide、tiffslide、pyvips等库统一读取,再针对缺失字段做推断或回填。
二、扫描倍率与MPP的关系
扫描倍率(如20×、40×)描述的是物镜放大能力,而MPP(Microns Per Pixel,微米/像素)描述的是单个像素在物理切片上对应的实际尺寸。两者通过光学系统关联,但并非严格一一对应:同样是20×扫描,不同扫描仪的MPP可能略有差异。工程上更可靠的做法是以MPP为准,倍率仅作为辅助标签。
MPP通常以二元组形式给出,例如mpp = (0.25, 0.25)表示X、Y方向均为0.25 μm/pixel。如果元数据中只给出倍率而缺失MPP,可以用经验值回填:40×约0.25 μm/pixel,20×约0.5 μm/pixel,10×约1.0 μm/pixel。但必须明确标注这是推断值,不能当作实测参数使用。
MPP的核心用途是坐标换算。假设某组织区域在level 0下的像素宽度为W0,则其物理宽度为W0 × mpp_x微米。若要在level 2下裁剪同样物理范围,需要先读取该层的下采样因子downsample,再计算像素坐标。这个换算关系是所有ROI裁剪、多尺度融合和跨切片对齐的基础。
三、组织区域提取
组织区域(Tissue Region)指的是切片上真正含有组织的区域,通常通过缩略图上的颜色阈值分割得到。最常用的方法是Otsu阈值法:将缩略图转为灰度,计算全局阈值,把低于阈值的像素判为组织,高于阈值的判为背景。对于H&E染色切片,也可以利用饱和度通道,因为组织区域饱和度明显高于白色背景。
得到二值掩膜后,通常还需要做形态学开闭运算去除噪点、填补空洞,再提取最大连通域或所有连通域的外接矩形。最终输出两类结果:一是组织轮廓多边形,用于精确裁剪;二是有效区域边界框,用于快速定位。边界框通常以level 0坐标记录,格式为(x, y, width, height)。
四、染色信息与颜色特征
染色信息包括染色类型(H&E、IHC、特殊染色等)、染色方案名称以及颜色分布特征。对于H&E切片,最常用的颜色特征是RGB均值、HSV均值以及颜色反卷积后的苏木素和伊红通道强度。这些特征可以用于:判断切片是否褪色、检测染色批次差异、驱动颜色归一化算法(如Macenko、Reinhard)。
实际提取时,通常从组织区域内随机采样若干小块(如20个256×256 patch),计算每个patch的RGB均值和标准差,再取平均作为整张切片的颜色特征。这样比直接对整张缩略图求均值更稳定,因为缩略图包含大量背景像素会稀释组织颜色。
五、结构化存储设计
提取出的元数据必须结构化存储,才能被下游程序稳定读取。推荐使用“一文件一记录”的方式,每条记录包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| slide_id | string | 唯一标识,建议用文件名去扩展名 |
| file_path | string | 原始WSI路径 |
| format | string | svs / ndpi / tiff 等 |
| magnification | float | 扫描倍率,如20.0 |
| mpp_x, mpp_y | float | 微米/像素,缺失时标注为推断值 |
| level_count | int | 金字塔层数 |
| level_dimensions | list | 每层宽高 |
| level_downsamples | list | 每层下采样因子 |
| tissue_bbox | list | level 0下的组织边界框 |
| tissue_area_ratio | float | 组织面积占比 |
| stain_type | string | H&E / IHC 等 |
| color_mean_rgb | list | 组织区域RGB均值 |
| color_std_rgb | list | 组织区域RGB标准差 |
| extract_time | string | 提取时间戳 |
| extract_version | string | 提取脚本版本号 |
存储格式推荐JSON Lines(每行一个JSON对象)或Parquet。JSON Lines便于逐条追加和人工查看,Parquet适合大规模批量分析和列式查询。无论哪种格式,都应保留extract_version字段,以便在提取逻辑更新后能够识别哪些记录需要重新生成。
六、提取流程与质量检查
完整的元数据提取流程可以概括为:读取WSI → 解析厂商元数据 → 提取扫描参数 → 生成缩略图 → 分割组织区域 → 计算颜色特征 → 组装结构化记录 → 写入存储。每一步都可能出现异常,因此需要配套的质量检查。
质量检查至少应覆盖以下几点:MPP是否缺失或异常(如小于0.1或大于2.0);组织面积占比是否过低(如低于5%,可能切片过小或分割失败)或过高(如高于95%,可能阈值过松);颜色均值是否偏离同批次其他切片过大;各层尺寸是否与下采样因子一致。对于异常记录,应标记状态并记录原因,而不是直接丢弃。
七、标准化与版本管理
标准化意味着不同来源、不同格式的WSI,经过提取后输出统一的字段结构和单位。例如MPP统一用微米/像素,坐标统一用level 0像素坐标,颜色统一用0-255整数。标准化后的元数据表可以直接用于数据集划分、批次效应分析和训练采样策略。
最后,元数据提取脚本本身需要版本管理。每次修改提取逻辑(如更换组织分割阈值、增加新字段),都应递增extract_version,并在元数据表中保留旧版本记录。这样当发现某批数据质量问题时,可以快速定位是数据本身的问题还是提取逻辑变更引入的问题。