3. 影像组学特征提取基础

影像组学的核心工作,是把医学图像中肉眼难以量化的信息,转换成可计算、可统计、可建模的数值特征。特征提取这一步决定了后续融合建模的“原料质量”。如果特征本身定义混乱、参数不一致或提取流程不可复现,再复杂的模型也难以得到稳定结果。本节围绕四类基础特征——一阶统计、形状、纹理、滤波器与小波特征——说明它们各自描述什么、如何计算,并以 PyRadiomics 为例完成一次可复现的提取实操。

一、四类特征分别描述图像的什么信息

一阶统计特征只关心感兴趣区(ROI)内体素灰度值的分布,不考虑体素之间的空间位置关系。常见指标包括均值、中位数、最小值、最大值、极差、方差、标准差、偏度、峰度、能量、熵和均匀性。例如均值反映整体亮度水平,标准差反映灰度离散程度,偏度描述分布是否偏向一侧,峰度描述分布尖峭或平坦。熵越大,说明灰度分布越杂乱、异质性越强。一阶特征计算简单、解释直观,但对空间结构不敏感,同一组灰度值重新排列后一阶特征不变。

形状特征描述 ROI 的几何形态,与灰度值无关,只依赖分割掩膜。常用指标有体积、表面积、表面积体积比、球形度、紧致度、最大三维直径,以及基于主轴长度的伸长率、扁平率等。球形度越接近 1,说明病灶越接近球体;表面积体积比越大,通常意味着形态越不规则或越呈分叶状。形状特征对分割边界非常敏感,边界轻微变化就可能明显改变表面积和球形度,因此分割质量必须与形状特征一起评估。

纹理特征刻画灰度在空间上的排列模式,弥补一阶特征丢失的空间信息。主流方法包括灰度共生矩阵(GLCM)、灰度游程矩阵(GLRLM)、灰度大小区域矩阵(GLSZM)、邻域灰度差分矩阵(NGTDM)和灰度依赖矩阵(GLDM)。GLCM 统计固定方向和距离上成对灰度出现的频率,可导出对比度、相关性、同质性、能量等;GLRLM 统计同一方向上连续相同灰度的游程长度,反映粗细纹理;GLSZM 统计连通区域内灰度均匀区域的大小;NGTDM 描述某灰度与其邻域平均灰度的差异;GLDM 则统计中心体素与邻域内相似灰度的依赖关系。纹理特征通常需要在多个方向、多个距离上计算,再取平均或合并,以减少方向依赖。

滤波器与小波特征是在原始图像上先做变换,再对变换后的图像提取上述特征。常见滤波器包括高斯拉普拉斯(LoG)、小波、平方、平方根、对数、指数以及梯度等。小波变换把图像分解为不同频率和方向的子带,例如 LL、LH、HL、HH,其中 L 表示低通、H 表示高通,可进一步多级分解。这样可以在不同尺度上捕捉边缘、纹理和粗结构。滤波器特征能显著扩展特征空间,但也带来维度膨胀和冗余,需要配合稳定性筛选和降维。

影像组学特征提取:输入、变换与四类特征输出 原始医学图像 CT / MRI / PET 体素间距、灰度范围 分割掩膜 ROI 勾画边界 / 阈值分割 决定形状与纹理范围 预处理与重采样 各向同性体素间距 灰度离散化 binWidth 特征计算引擎 一阶统计 灰度分布直方图 形状特征 掩膜几何网格 纹理矩阵 GLCM/GLRLM/GLSZM 滤波器 / 小波 LoG、LL/LH/HL/HH 参数一致性控制 特征向量输出(示例字段) original_firstorder_Mean / Entropy / Skewness 灰度分布:均值、熵、偏度、峰度 original_shape_Sphericity / SurfaceArea 几何形态:球形度、表面积、体积 original_glcm_Contrast / Idm 空间纹理:对比度、逆差矩 wavelet-HLL_glcm_Contrast 小波子带上的纹理响应 输出 CSV:每行一个病例,每列一个特征 关键参数与影响: binWidth 控制灰度离散化粒度:值越大,纹理矩阵越稀疏,熵与对比度等指标随之变化; 重采样体素间距统一空间尺度,避免不同设备层厚差异导致形状与纹理不可比; 滤波器与小波会成倍扩展特征维度,需在提取后做稳定性与冗余筛选。

二、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 的冗余特征。完成这些检查后,再进入归一化、降维和融合建模阶段,特征质量才有基本保障。

实操建议:固定一套参数配置并保存为 YAML 文件,所有病例共用同一配置;每次提取后保留 diagnostics_ 信息,便于回溯参数与版本差异。