☰
CAT12+SPM实战:VBM灰质体积分析全流程与避坑指南
2026/10/16 6:49:43 网站建设 项目流程

简介:这是基于MATLAB的CAT12工具箱,专为神经影像领域的体素形态计量学(VBM)分析设计,面向需要处理结构磁共振数据、比较不同个体或群体脑灰质/白质形态差异的研究者。压缩包共518个文件,约124MB,以.m源码、mex编译函数、.nii影像数据、.gii表面网格为主,另有Annot分区文件、模板与HTML帮助文档,构成完整的预处理-分割-统计-可视化链路。已有2824人学习下载,既适合刚开始接触VBM的医学影像学生,也适合需要批处理多被试数据的科研人员。包内除核心算法外,还提供示例数据与表面配准模板,可直接跑通从原始sMRI到灰质体积比较的典型流程;同时附带多平台mex版本,便于Windows/Linux/Mac环境复用。该工具箱支持灰质、白质和脑脊液分割,并可结合统计参数映射逐体素检验,能支撑神经退行性疾病、精神疾病等领域的脑结构差异研究。

1. CAT12 到底是什么:先搞清楚它和 SPM 的老关系

上个月有做认知实验的师弟抱着一块硬盘来找我,里面是两百多份 T1 结构像。他导师的要求只有一句:比较两组被试的灰质体积差异。他查了一圈工具,最后停在 CAT12 这个名词上,问我是不是要配合 SPM 用。这个问题问得很典型——CAT12 不是独立软件,它是挂在 SPM 生态里的一个工具箱,专攻基于体素的形态计量学(VBM)分析,输入是 T1 加权结构像,输出是逐体素的灰质体积统计图。它解决的痛点很直接:不同人大脑形状和体积天然不同,脑区之间又没有清晰边界,没法像 ROI 那样直接量体积。这篇笔记就按我实际处理数据的顺序,把 CAT12 从分割参数到统计推断的关键步骤和踩坑记录都过一遍,给想用 VBM 做结构像分析的人一条能直接走通的路。

2. VBM 分析的核心逻辑:分割、配准、调制、平滑四步走

2.1 VBM 为什么能看灰质体积变化:不是在做“形状比较”

VBM 的思路不是把大脑掰成同一个形状再量,而是把所有被试的 T1 像放到同一个标准空间里,逐体素比较灰质“浓度”或“体积”。这个流程可以拆成四个阶段:第一步是把每个被试的 T1 分割成灰质、白质、脑脊液的概率图,每个体素保留一个 0 到 1 之间的概率值;第二步是用 DARTEL 这类非线性配准算法,把所有被试的灰质图对齐到同一模板空间;第三步是调制(modulation),这一步最关键,因为单纯配准会把体积变化“挤压”进体素强度里,如果不做调制,灰质体积差异就会被配准过程吃掉;第四步是用高斯核做平滑,让离散的体素数据变得连续,符合统计检验的随机场假设。

很多人第一次听到“平滑”会不理解:平滑不是把细节抹掉了?对自然图像确实是坏事,但 VBM 里的统计是以体素为单位做的,平滑本质上是在做信号聚合和噪声抑制,同时让结果不依赖于体素网格的精确对齐。平滑核大小一般选 6 到 10 毫米全宽半高(FWHM),选 4 毫米或更小往往会让结果碎片化,选 15 毫米以上又容易把真实差异抹平,这个选择直接影响能不能统计出结果。

2.2 CAT12 与 SPM 的 DARTEL 流程:为什么选 CAT12

传统的 SPM VBM 流程需要手动串好几个模块:先用 New Segment 做分割,再用 DARTEL 建立模板、把灰质图配准到 MNI 空间,最后还要自己写 batch 串联这些步骤。CAT12 把这整条流水线整合成了一个入口,在 SPM 菜单里选择 CAT12,再选 Segment,选好 T1 文件点运行,分割、配准、调制、平滑一次完成。

CAT12 相比 SPM 自带流程还有几个优势:用的是自适应分割算法,对扫描仪品牌和场强差异更鲁棒;内置了质量检查(QA)报告,能直观看到每个被试的图像噪声水平和配准质量;还额外支持皮层厚度计算,不只能做体素水平的灰质体积分析。下表是我从实际使用角度做的对比:

对比维度SPM 经典 DARTEL 流程CAT12 流程
操作步骤分割、建模板、配准、调制、平滑多步串联单入口一键完成
质量检查无内置 QA,需手动查看每一步输出每次运行生成 xml 质量报告
皮层厚度不支持支持
处理速度取决于手动 batch 优化内置并行与优化策略
新手友好度流程复杂,易漏步骤界面和脚本相对清晰

一句话总结:如果你手上已有 SPM,装一个 CAT12 工具箱就能跑 VBM,不需要另配独立软件;如果你还没装 SPM,就得先把 SPM 装好再挂载 CAT12,两者版本要注意匹配。

2.3 从 T1 到 mwp1:一次预处理到底产出了什么

跑完 Segment 之后,输出目录里会出现一堆带前缀的文件,很多人在这里看懵。以最核心的灰质输出为例,文件名里每个字母都有含义:m 代表调制过(modulated),w 代表已经配准到标准空间(warped),p 代表概率图(probability),后面的 1 代表灰质。也就是说,mwp1 文件就是“调制后、标准化到 MNI 空间、灰质概率图”,这才是你后面统计真正要用的数据。

前缀含义是否用于 VBM 统计
mwp1调制 + 配准 + 灰质概率图是,核心文件
m0wp1未调制的灰质概率图否,体积信息已丢
p1原始空间分割出的灰质仅用于检查
s 前缀分割后各组织视情况用于二次处理
xml质量检查报告用于质控

这里有个常见的理解偏差:很多人以为 p1 是灰质体积,直接拿去做组间比较。但 p1 是“灰质概率”,没有经过调制,不同被试由于全脑体积差异和配准伸缩,体素值并不直接反映体积。我一般会把 mwp1 和 wc1 命名规则打印出来贴在工位上,防止哪天顺手选错文件。

3. 从原始 T1 到预处理完成:一版可直接复现的 CAT12 操作路径

3.1 数据组织:目录结构、格式与命名

跑预处理之前,数据目录一定要整理干净。CAT12 对文件名和路径敏感,路径里有中文或者空格,MATLAB 解析很容易出问题。我一般会建两层目录:第一层按研究分组,第二层按被试编号,T1 文件统一命名为 sub-001_T1.nii 这种风格。头部信息(比如扫描参数)建议用 MI Toolkit 或 SPM 自带的 Display 检查一遍,确认方向信息完整、原点位置合理。

下面是推荐的目录结构:

/data/VBM_raw ├── group1 │ ├── sub-001 │ │ └── sub-001_T1.nii │ ├── sub-002 │ │ └── sub-002_T1.nii │ └── ... └── group2 ├── sub-021 │ └── sub-021_T1.nii └── ...

文件格式优先使用 .nii 或 .nii.gz,尽量避免 .nii 嵌套文件夹(网上下载的原始数据经常是 DICOM,建议先用 dcm2nii 批量转)。转换时注意保留原始分辨率信息,不要顺手把图像重新采样到各向同性,重采样会让后续分割引入不必要的误差。我习惯在转换后用 python 脚本读一遍 nifti header,检查 voxel size 是否在合理范围(常见为 1mm 或 1.2mm 各向同性或接近各向同性)。

3.2 跑通 Segment:GUI 操作与参数含义

数据准备完毕,打开 MATLAB,保证 SPM12 和 CAT12 都在路径里,命令行输入 spm 启动图形界面,依次点:SPM 菜单栏 → CAT12 → Segment。界面会要求选择 T1 图像文件,可以多选,确认后默认参数其实就能跑出结果。但我建议至少调整以下几个选项:

  • Segmentation Estim(估计参数):保持默认即可,新版 CAT12 会自动检测 TPM 模板。
  • Output 里的“GM Output”:开关 Native、Modulated、DARTEL Imported 三项,VBM 统计只保留 DARTEL imported + modulated 的那个文件,其他可以先不生成,能省一大半磁盘空间。
  • Affine Registration 设置:默认 4mm 分辨率,质量差的图像可改成 4mm 精度优先。

如果是批处理多个被试,建议直接用 job 脚本而不是一个个点 GUI。下面这个批量脚本是我日常用的模板:

% CAT12 批量 Segment 脚本,运行前确认 SPM 和 CAT12 已在路径中 spm('defaults', 'FMRI'); spm_jobman('initcfg'); % 路径换成你自己的数据目录 data_dir = '/data/VBM_raw'; files = spm_select('FPListRec', data_dir, '^sub-.*T1.*\.nii$'); matlabbatch{1}.spm.tools.cat.estwrite.data = cellstr(files); matlabbatch{1}.spm.tools.cat.estwrite.output.GM.native = 0; % 不输出原始空间 GM matlabbatch{1}.spm.tools.cat.estwrite.output.GM.mod = 0; % 不输出未配准 GM matlabbatch{1}.spm.tools.cat.estwrite.output.GM.dartel = 1; % 输出配准到模板的 GM matlabbatch{1}.spm.tools.cat.estwrite.output.GM.mwp = 1; % 输出调制后的 mwp1 文件 matlabbatch{1}.spm.tools.cat.estwrite.extopts.expertmode = 1; % 打开专家模式以读取这些选项 spm_jobman('run', matlabbatch);

注意 output.GM.dartel 和 output.GM.mwp 的区别:前者是没调制的配准后灰质(配准到 DARTEL 模板),后者是调制过的,统计时要用 mwp1,千万别拿 dartel 输出的文件当最终结果。spm_select 用了 FPListRec 递归搜索文件名,如果你的数据不是统一命名,建议先把文件名改好再跑。

运行过程中 SPM 会在命令行滚动日志,如果某个被试中途报错,整个 batch 会中断而不是跳过继续。我的做法是一个组一个组跑,出问题先修掉那一例再继续,避免后面全部白跑。

3.3 QA 报告:不只是“跑完就完”

CAT12 每次 Segment 会在与输入文件同级的目录生成一个 XML 文件,名字类似 cat_sub-001_T1.xml。这个文件是自动生成的质控报告,包含几个核心指标:噪声水平(Noise)、偏置场不均匀度(Bias)、图像质量(Image Quality)、分辨率(Resolution)。每个指标有等级,A/B 代表质量好,C 以下说明图像存在问题。

我用 QA 报告有一条明确标准:任何一个被试出现 Image Quality 等级为 C 或更低,就要拉出来单独看原始 T1 图和分割结果图。不要因为样本量紧张就硬留着质量差的数据,VBM 统计对离群体素极敏感,一个坏数据往往能做出一个假性脑区。如果确实想保留,至少要跑两次统计,一次含该被试、一次不含,看结果稳定性。QA 文件本身也可以用文本编辑器打开,里面是 dat 结构,方便你写批量脚本自动筛查全部被试的质量等级。跑完预处理后,我会先用一个 Python 脚本读取全部 xml,把所有 C 级以下的被试列出来再一一手动检查。

4. 组间比较怎么做:设计矩阵、协变量与统计推断

4.1 两样本 t 检验的完整设置

拿到所有 mwp1 文件,接下来就是标准的 SPM 统计流程。很多人在这里有一个误区:以为分组比较就是简单地把两组的 mwp1 文件路径填进 t 检验界面,其实设计矩阵里还要考虑协变量。VBM 研究中最重要的协变量是脑袋大小(TIV,全脑总体积),不同人全脑体积差异能达到 20% 以上,如果不控制,灰质体积的组间差异会被头大小混杂。CAT12 在预处理时也会在输出目录生成一份 TIV 估计值,统计阶段可以把它作为回归协变量。

在 SPM 里点:Stats → Two-sample t-test,然后将两组的 mwp1 文件分别填入 Group 1 和 Group 2。在 Covariates 栏添加 TIV 协变量,如果你的设计里有性别或者年龄,也一并加进去。下面是直接的脚本写法:

% 两样本 t 检验,带 TIV 协变量 spm('defaults', 'FMRI'); spm_jobman('initcfg'); g1 = cellstr(spm_select('FPList', '/data/VBM_out/group1', '^mwp1.*\.nii$')); g2 = cellstr(spm_select('FPList', '/data/VBM_out/group2', '^mwp1.*\.nii$')); matlabbatch{1}.spm.stats.factorial_design.des.t2.scans1 = g1; matlabbatch{1}.spm.stats.factorial_design.des.t2.scans2 = g2; % TIV 协变量:与被试顺序一一对应 tiv_vals = [1512 1445 1587 ... 1260]; % 改成你的 TIV 数据,长度等于被试总数 matlabbatch{1}.spm.stats.factorial_design.cov(1).c = tiv_vals(:); matlabbatch{1}.spm.stats.factorial_design.cov(1).cname = 'TIV'; matlabbatch{1}.spm.stats.factorial_design.masking.tm.tma.ircat = 1; matlabbatch{1}.spm.stats.factorial_design.masking.tm.tma.atham = 1; matlabbatch{1}.spm.stats.factorial_design.masking.im = 0; % 不用隐式掩膜 spm_jobman('run', matlabbatch);

这里 cov(1).c 的每一行对应一个被试,顺序必须跟 g1 和 g2 拼接起来的顺序完全一致。一个稳妥做法是把两组路径按名字排序后输出成一个被试顺序文件,再用程序读取 TIV 列表,避免手工排列错位。设计矩阵生成后,点 Estimate 估算参数,再点 Results 设置对比。两样本 t 检验的对比向量一般是 [-1 1],正方向表示第二组大于第一组;TIV 协变量不进入对比向量,因为它的目的是把全脑体积差异的方差解释掉。

4.2 平滑核、显式掩膜和绝对阈值:影响结果是否显著的三个隐藏开关

统计结果能不能出来,往往不取决于被试数量,而是取决于这三个设置。第一是平滑核,CAT12 默认生成的数据已经做过 8mm 平滑,但如果你自己改了预处理参数,统计时就要确认平滑程度是否匹配。一般推荐 8mm,这个数值不是拍脑袋定的:体素尺度普遍在 1.5mm,配准后的残余误差在 2-4mm,8mm 平滑既能压制噪声又不会过度模糊解剖边界。第二是显式掩膜,可以在统计时指定一个灰质模板作为兴趣范围,把脑外组织和脑白质排除在外,抑制假阳性。第三是绝对阈值掩膜(absolute threshold masking),这个参数在实际使用中比显式掩膜更容易被忽略,SPM 默认值是 0.05,VBM 分析我一般调到 0.1 或 0.2。原因很简单:低于这个阈值的体素,灰质概率太低,大概率是脑脊液或边界区域,把这些体素纳入统计只会增加多重比较的负担。

有人说:“我不设阈值,把所有体素都跑一遍不更客观?”这样做的结果是统计校正范围太大,很多真实但较小的效应过不了 FWE 校正。设置合理阈值不是操控结果,而是缩小搜索空间到解剖学上有意义的灰质区域,前提是你把阈值设置写进方法部分。

4.3 结果怎么看:xjView、坐标与校正方式

统计跑完,SPM Results 界面会显示对比后的显著性图。先看顶部面板里的峰值体素表和簇表,SPM 默认报告的是未校正或 FWE 校正的体素水平 p 值。VBM 分析常见的报告方式是簇水平 FWE 校正,簇大小和峰值坐标都要报告。如果只写了峰值坐标和 t 值,审稿人一般会追问校正方式和簇大小。

我习惯用 xjView 打开统计图,它能直接读取 spmT 图像并显示脑区标签,还可以导出多层横断面图。坐标要注意:CAT12 预处理输出的空间是 MNI 空间,报告坐标要注明是 MNI,如果你想对照 Talairach 坐标做脑区定位,需要用转换工具,不要直接混用。另外不要在 FWE 不显著时悄悄换成 uncorrected p < 0.001 再报告“显著”,要么明说这是探索性分析,要么把未校正的结果严格当假设生成而不下结论。

5. 避坑:从预处理到统计最容易翻车的五个细节

5.1 分割进度卡住或报错 “No inlier”

现象:某被试跑到 Segmentation 阶段直接报错,或者卡在 99% 不动,日志里出现“No inlier”或“Error using … estimate”的提示。

原因:最常见的是图像原点偏离标准位置太多,DARTEL 初始化搜索不到合适的配准起点;其次是被试图像的方向信息缺失或错误,nifti header 的 qform/sform 不一致。

解决:先用 SPM Display 打开该 T1,检查原点位置是否在大脑中心附近。如果偏离明显,用任何支持重置 origin 的工具(比如 ITK-SNAP)把原点重置到前联合附近,再重新跑。如果图像方向信息损坏,用 FSL 的 fslreorient2std 修正后再转 nii。我还会在预处理前加一步校验,判断所有文件的 qform 矩阵是否接近单位阵,提前拦截这批问题数据。

5.2 加 TIV 协变量后结果两级反转

现象:第一次跑不加 TIV,结果显著;加上 TIV 之后不显著了,或者反过来不加不显著、加了变显著。

原因:TIV 与组别存在强相关。比如老年组和青年组比较,本来就存在整体脑体积差异,TIV 解释了相当一部分组间方差;要是把这种与分组高度相关的协变量和分组同时放入模型,会造成多重共线性,结果解读就变得很微妙。

解决:先做一次 TIV 的独立样本 t 检验,报告两组差异。如果差异较大,加协变量就要谨慎,可以在模型里只把 TIV 作为相关协变量,但结果中要明确说明“结果在控制 TIV 后获得”,避免被质疑为统计操纵。我一般会把加不加 TIV 的两套结果都跑出来,对比稳定性,报告中选主分析结果,补充材料里放敏感性分析。

5.3 平滑核从一个数换到另一个数,结果时有时无

现象:同一批数据用 6mm 平滑有结果,换成 10mm 平滑就没结果,或者反过来。

原因:平滑核过小,噪声主导了估计;平滑核过大,真实效应被抹平。另外配准质量也会和平滑核相互作用:配准质量差,需要更大平滑来掩盖,但代价是丢失细节。

解决:不要在结果出来后反复试平滑核挑一个“好看”的结果,这是典型的多重比较 p-hacking。应该在分析前就根据体素尺寸和文献确定平滑核,通常 8mm FWHM。如果真想验证稳健性,把 6mm/8mm/10mm 跑完统一报告,而不是挑一个。

5.4 把未调制的 wc1 当体积文件用

现象:统计时选了一批 wc1 或 dartel 输出的灰质文件,结果发现两组差异巨大,但查看原始图发现只是配准不够精细,差异是形状差异不是体积差异。

原因:未调制文件保留的是配准过程中“挤压”后的强度,不能直接反映灰质体积,只有在比较相对浓度时才使用。

解决:认准 mwp1 前缀。调制过程会把 Jacobian 行列式乘回到体素强度,这样体素值和灰质体积对应。拿到输出目录后用文件名筛选工具只保留 mwp1,避免后面误选。

5.5 QA 报告一团黄红,仍然硬用

现象:某个被试图像质量评级 D,但统计出来的显著簇恰好包含这个被试,结果不稳定,删掉它结果就消失。

原因:噪声大或存在运动伪影的被试会在配准时产生偏倚,尤其在皮层边界区域造成假阳性。小样本研究中,一个坏被试的影响会被放大。

解决:把 QA 报告和统计影响一并做敏感性分析。先看 QA 等级排除 D 级,再跑一次删掉该被试的统计验证稳定性。切记在论文方法部分写明 QA 筛选标准,这不是为了让结果好看,而是给读者完整的可复现信息。

6. 批处理脚本与结果验证:把整套流程收进一份脚本里

6.1 从 T1 到平滑一步到位

上面拆开讲了 Segment 和统计,实际项目里我更推荐把预处理全部整理成脚本一次跑完。CAT12 支持在同一个 matlabbatch 里串联 Segment 和平滑,代码如下:

% 一步完成分割 + 平移到统计所需的 mwp1 平滑文件 matlabbatch{1}.spm.tools.cat.estwrite.data = cellstr(spm_select('FPListRec', ... '/data/VBM_raw', '^sub-.*\.nii$')); matlabbatch{1}.spm.tools.cat.estwrite.output.GM.dartel = 1; matlabbatch{1}.spm.tools.cat.estwrite.output.GM.mwp = 1; matlabbatch{1}.spm.tools.cat.estwrite.extopts.expertmode = 1; spm_jobman('run', matlabbatch); % 批量平滑 mwp1 文件 files = spm_select('FPList', '/data/VBM_out', '^mwp1.*\.nii$'); matlabbatch{1}.spm.spatial.smooth.data = cellstr(files); matlabbatch{1}.spm.spatial.smooth.fwhm = [8 8 8]; matlabbatch{1}.spm.spatial.smooth.dtype = 0; spm_jobman('run', matlabbatch);

这里的 FWHM 选择了 8 毫米,如果你前面的体素尺寸不是 1.5mm 等常见值,平滑核可以按体素大小换算后微调,但尽量保持与文献一致。需要注意的是这两个步骤放在一个 matlabbatch 里跑时,第二步的文件选择依赖第一步完成,如果数据量大建议拆成两个脚本,在命令行分步执行,方便排查问题。

6.2 跑完一批数据后必须做的三件事

第一件:逐个打开 QA xml 报告,把所有 C 级以下的被试列出来手动复核。第二件:做组内或全样本平均模板,用 Display 检查归一化后的平均灰质图是否清晰平滑,边界有没有明显变形。第三件:把每个被试的 mwp1 文件值做一次分布统计,看看有没有极端离群值。这三件事做下来,预处理阶段的问题基本都能暴露,别急着跑统计。

之前有一批 60 例的数据,我没做 QA 直接跑统计,结果出来一个非常漂亮的脑区,兴奋之余多看了一眼,发现是某个被试头皮未完全去除留下的残余信号,一个体素的强度是其他所有被试的三倍。从那以后我每次处理一批结构像,都会强制先做 QA 筛查和分布检查,哪怕多花十分钟也值得。CAT12 的批处理确实省力,但那份 xml 报告和后续验证步骤,才是真正决定组间结果可信度的关键。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询