模型看到的数字究竟是什么
这篇回顾讨论 CT 数据进入视觉模型之前的处理契约,归档月份仅用于整理主题。预处理最危险的问题往往不是公式复杂,而是数字看起来正常,含义却已经改变。
同样是一个浮点数组,它可能保存探测器读数、负对数投影、重建衰减量、经标定的 CT 值,也可能只是显示截图。它们对应不同的物理意义。看到扩展名或文件夹名里有「CT」,不足以直接套用固定 HU 窗口。
把存储转换与显示转换分开
DICOM CT 模块定义了存储值与输出单位之间的线性关系,并对输出为 HU 的条件作出规定;多能量或派生图像需要额外核对类型与单位。DICOM CT Image Module
output_value = rescale_slope * stored_value + rescale_intercept
应确认读取工具是否已经应用转换,再决定是否执行公式。重复乘斜率、加截距会悄悄改变整个分布。增强多帧对象还需要检查每帧的变换信息,不能假定所有帧都共享某个顶层字段。
显示窗决定如何把数值映射到屏幕亮度,不等同于恢复物理量。若输入只有导出的显示图,应保留这个限制,不能假装通过反归一化还原完整 CT 数值。
让归一化成为明确契约
一种可审查的策略是:在确认单位后固定裁剪范围,再线性映射到模型输入区间。范围可以来自任务先验,也可以由训练集统计得到;不能看完测试集后再调整。
import numpy as np
def fixed_scale(image, low, high):
if not np.isfinite([low, high]).all() or high <= low:
raise ValueError("invalid intensity bounds")
x = np.asarray(image, dtype=np.float32)
if not np.isfinite(x).all():
raise ValueError("inspect invalid pixels before scaling")
x = np.clip(x, low, high)
return (x - low) / (high - low)
函数故意不内置 HU 范围,也不自动填补缺失值。填补策略需要结合背景、坏点和任务含义决定。输出配置应保存单位、上下界、转换顺序以及统计所用数据版本。
填充值还应与真实背景区分。为了补齐尺寸而加入的常数,可能在计算均值、分位数和前景范围时占据很大权重。应保留有效区域掩膜,在约定区域内计算统计,并明确填充值进入模型时使用什么数值。
每次转换可同时记录上下界的截断比例。如果某批输入突然出现大量截断,应先核查单位与转换是否一致,再判断是否真的存在分布变化。均值相近不代表整个输入分布相同。
逐图拉伸可能抹去有用差异
逐图最小最大归一化能让每张图填满显示范围,却可能把不同样本的绝对强度差异消掉。一个孤立异常值也可能支配整个比例尺。逐体标准化则要说明统计范围:全体素、前景区域,还是某个稳定参考区域。
这不意味着所有任务都必须保留绝对强度,而是要明确模型应获得什么信息。可在验证集比较固定范围、训练集统计和逐体统计,并同时观察任务结果与异常样本。nnU-Net 强调预处理、训练和推理选择需要共同考虑;这里借鉴的是配置应成体系的思路。nnU-Net,2018
强度正确,还要守住空间关系
预处理记录不能只有均值和标准差。轴顺序、方向、体素间距、裁剪范围以及回填位置,会直接影响标签对齐与结果解释。图像和离散标签可以使用不同插值方法,但必须应用同一空间变换。
常见失败包括:训练使用已缩放数据,推理却重复缩放;重采样后仍沿用旧间距计算边界误差;为去背景而裁掉目标边缘。建议固定几份人工核对样本,保存变换前后的同一解剖位置与数值分位数。发现异常先追转换链,再改网络。
参考
- DICOM CT Image Module:存储值、线性变换和单位的定义。
- nnU-Net: Self-adapting Framework for U-Net-Based Medical Image Segmentation:数据适配与完整分割流程的研究背景。