3. 影像组学特征提取基础
影像组学的核心工作,是把医学图像中肉眼难以量化的信息,转换成可计算、可统计、可建模的数值特征。特征提取这一步决定了后续融合建模的“原料质量”。如果特征本身定义混乱、参数不一致或提取流程不可复现,再复杂的模型也难以得到稳定结果。本节围绕四类基础特征——一阶统计、形状、纹理、滤波器与小波特征——说明它们各自描述什么、如何计算,并以 PyRadiomics 为例完成一次可复现的提取实操。
一、四类特征分别描述图像的什么信息
一阶统计特征只关心感兴趣区(ROI)内体素灰度值的分布,不考虑体素之间的空间位置关系。常见指标包括均值、中位数、最小值、最大值、极差、方差、标准差、偏度、峰度、能量、熵和均匀性。例如均值反映整体亮度水平,标准差反映灰度离散程度,偏度描述分布是否偏向一侧,峰度描述分布尖峭或平坦。熵越大,说明灰度分布越杂乱、异质性越强。一阶特征计算简单、解释直观,但对空间结构不敏感,同一组灰度值重新排列后一阶特征不变。
形状特征描述 ROI 的几何形态,与灰度值无关,只依赖分割掩膜。常用指标有体积、表面积、表面积体积比、球形度、紧致度、最大三维直径,以及基于主轴长度的伸长率、扁平率等。球形度越接近 1,说明病灶越接近球体;表面积体积比越大,通常意味着形态越不规则或越呈分叶状。形状特征对分割边界非常敏感,边界轻微变化就可能明显改变表面积和球形度,因此分割质量必须与形状特征一起评估。
纹理特征刻画灰度在空间上的排列模式,弥补一阶特征丢失的空间信息。主流方法包括灰度共生矩阵(GLCM)、灰度游程矩阵(GLRLM)、灰度大小区域矩阵(GLSZM)、邻域灰度差分矩阵(NGTDM)和灰度依赖矩阵(GLDM)。GLCM 统计固定方向和距离上成对灰度出现的频率,可导出对比度、相关性、同质性、能量等;GLRLM 统计同一方向上连续相同灰度的游程长度,反映粗细纹理;GLSZM 统计连通区域内灰度均匀区域的大小;NGTDM 描述某灰度与其邻域平均灰度的差异;GLDM 则统计中心体素与邻域内相似灰度的依赖关系。纹理特征通常需要在多个方向、多个距离上计算,再取平均或合并,以减少方向依赖。
滤波器与小波特征是在原始图像上先做变换,再对变换后的图像提取上述特征。常见滤波器包括高斯拉普拉斯(LoG)、小波、平方、平方根、对数、指数以及梯度等。小波变换把图像分解为不同频率和方向的子带,例如 LL、LH、HL、HH,其中 L 表示低通、H 表示高通,可进一步多级分解。这样可以在不同尺度上捕捉边缘、纹理和粗结构。滤波器特征能显著扩展特征空间,但也带来维度膨胀和冗余,需要配合稳定性筛选和降维。
二、PyRadiomics 实操:从图像与掩膜到特征表
PyRadiomics 是当前最常用的开源影像组学特征提取工具,支持一阶、形状、纹理以及滤波器和小波特征,并可通过配置文件统一控制参数。下面以一份示例数据为例,说明完整流程。示例中的路径、参数和输出均为教学假设,实际使用时需替换为自己的数据。
首先安装并导入:
pip install pyradiomics SimpleITK
import SimpleITK as sitk
from radiomics import featureextractor
读取图像和掩膜。图像与掩膜必须空间对齐,且掩膜为二值标签,通常用 1 表示 ROI:
image = sitk.ReadImage("case_001_image.nrrd")
mask = sitk.ReadImage("case_001_mask.nrrd")
接着配置提取器。关键参数包括:binWidth 控制灰度离散化,示例取 25;resampledPixelSpacing 统一体素间距,示例取 [1,1,1];interpolator 选择插值方式;geometryTolerance 用于检查图像与掩膜几何一致性。是否启用形状特征、哪些纹理矩阵、是否启用小波,都在这里决定:
params = {
"binWidth": 25,
"resampledPixelSpacing": [1, 1, 1],
"interpolator": "sitkBSpline",
"geometryTolerance": 1e-4,
"force2D": False,
"featureClass": {
"firstorder": [],
"shape": [],
"glcm": [],
"glrlm": [],
"glszm": [],
"ngtdm": [],
"gldm": []
},
"imageType": {
"Original": {},
"Wavelet": {},
"LoG": {"sigma": [1.0, 3.0]}
}
}
extractor = featureextractor.RadiomicsFeatureExtractor(**params)
执行提取并查看结果。返回的 result 是一个字典,键为特征名,值为特征值;以 diagnostics_ 开头的键记录版本、参数和配置信息,便于复现:
result = extractor.execute(image, mask)
for key, value in result.items():
if key.startswith("original_firstorder"):
print(key, value)
典型输出会包含类似 original_firstorder_Mean、original_firstorder_Entropy、original_shape_Sphericity、original_glcm_Contrast、wavelet-HLL_glcm_Contrast 等字段。批量处理时,把每个病例的结果整理成一行,所有特征名作为列,即可得到建模用的特征表:
import pandas as pd
rows = []
for case_id in case_list:
img = sitk.ReadImage(f"{case_id}_image.nrrd")
msk = sitk.ReadImage(f"{case_id}_mask.nrrd")
res = extractor.execute(img, msk)
row = {"case_id": case_id}
row.update({k: v for k, v in res.items() if not k.startswith("diagnostics_")})
rows.append(row)
df = pd.DataFrame(rows)
df.to_csv("radiomics_features.csv", index=False)
三、提取结果如何判断是否可用
特征表生成后,不能直接送入模型。先做三项检查。第一,检查缺失值和异常值,例如某病例形状特征为空,往往说明掩膜为空或几何不匹配。第二,检查特征稳定性,可对同一病例做轻微分割扰动或重采样,观察特征变化幅度;变化过大的特征在后续建模中容易引入噪声。第三,检查冗余,一阶与纹理特征之间、不同小波子带之间常高度相关,可计算相关系数矩阵,剔除相关系数绝对值高于 0.9 的冗余特征。完成这些检查后,再进入归一化、降维和融合建模阶段,特征质量才有基本保障。
diagnostics_ 信息,便于回溯参数与版本差异。