一、流程目的

本流程用于将本地 T1 结构像数据通过 Docker 环境中的 FreeSurfer 6.0 进行结构像预处理,生成后续 braincharts 常模分析所需的皮层厚度和皮层下体积指标。

整体流程如下:

Windows 本地 T1 数据
→ Docker 挂载数据目录
→ FreeSurfer 6.0 recon-all
→ 生成 subjects/sub-xxx 结果目录
→ 提取 aparc.a2009s 皮层厚度
→ 提取 aseg 皮层下体积
→ 整理为后续 braincharts 常模输入表

二、本地文件夹结构

建议在 Windows 本地构建如下目录:

E:\freesurfer\
│
├── license.txt
│
├── data\
│   ├── 001.nii
│   ├── 002.nii
│   ├── 003.nii
│   └── ...
│
├── subjects\
│
└── scripts\

各文件夹含义如下:

license.txt   FreeSurfer 官方授权文件
data          存放原始 T1 NIfTI 文件,建议只读挂载
subjects      存放 FreeSurfer 输出结果,可写挂载
scripts       存放 PowerShell 批处理脚本

建议原始图像文件名尽量简单,避免中文、空格和特殊符号。例如:

001.nii
002.nii
003.nii

也可以使用 BIDS 风格命名:

sub-001_T1w.nii
sub-002_T1w.nii
sub-003_T1w.nii

三、Docker 与 FreeSurfer 镜像准备

1. 检查 Docker 是否可用

在 Windows PowerShell 中运行:

docker --version

测试 Docker:

docker run hello-world

如果能正常输出测试信息,说明 Docker Desktop 可以正常使用。


2. 拉取 FreeSurfer 6.0 镜像

由于 Rutherford et al. 2022 braincharts 常模使用 FreeSurfer 6.0 输出指标,建议使用 FreeSurfer 6.0 镜像:

docker pull freesurfer/freesurfer:6.0

查看镜像是否存在:

docker images

应能看到:

freesurfer/freesurfer    6.0

四、FreeSurfer license 挂载方式

FreeSurfer 6.0 容器中部分程序会默认查找:

/opt/freesurfer/license.txt

因此推荐直接将 Windows 本地 license 挂载到该位置:

-v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro"

不推荐只挂载到:

/license.txt

因为 FreeSurfer 6.0 中的 mri_convert 可能仍然去查找 /opt/freesurfer/license.txt,从而报错:

ERROR: FreeSurfer license file /opt/freesurfer/license.txt not found.

五、进入容器进行环境检查

在 PowerShell 中运行:

docker run --rm -it `
  -v "E:\freesurfer\data:/data:ro" `
  -v "E:\freesurfer\subjects:/subjects" `
  -v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro" `
  -e SUBJECTS_DIR=/subjects `
  freesurfer/freesurfer:6.0 `
  bash

进入容器后,终端提示符类似:

root@xxxx:/#

在容器内检查文件是否挂载成功:

ls /data
ls -l /opt/freesurfer/license.txt
echo $SUBJECTS_DIR
recon-all -version

正常情况下应看到:

/data 中存在 001.nii
/opt/freesurfer/license.txt 存在
SUBJECTS_DIR=/subjects
FreeSurfer 版本为 6.0.0

退出容器:

exit

注意:docker run 命令必须在 Windows PowerShell 中运行,不能在容器内部运行。容器内部只运行 lsrecon-allaparcstats2table 等 Linux/FreeSurfer 命令。


六、单个被试的 FreeSurfer 预处理

1. 直接运行单个被试

假设原始 T1 文件为:

E:\freesurfer\data\001.nii

对应容器内路径为:

/data/001.nii

输出被试名设为:

sub-001

在 PowerShell 中运行:

docker run --rm -it `
  -v "E:\freesurfer\data:/data:ro" `
  -v "E:\freesurfer\subjects:/subjects" `
  -v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro" `
  -e SUBJECTS_DIR=/subjects `
  freesurfer/freesurfer:6.0 `
  recon-all -i /data/001.nii -s sub-001 -all -openmp 4

参数说明:

-v "E:\freesurfer\data:/data:ro"
    将原始 T1 数据目录挂载到容器 /data,并设置为只读。

-v "E:\freesurfer\subjects:/subjects"
    将 FreeSurfer 输出目录挂载到容器 /subjects。

-v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro"
    将 license 文件挂载到 FreeSurfer 默认查找位置。

-e SUBJECTS_DIR=/subjects
    设置 FreeSurfer 输出目录。

recon-all -i /data/001.nii -s sub-001 -all -openmp 4
    对 001.nii 运行完整 FreeSurfer 结构像处理流程,输出被试名为 sub-001,使用 4 个线程。

2. 运行时间

单个 T1 的完整 recon-all -all 通常需要较长时间:

高性能服务器:约 3–8 小时
普通笔记本或 Windows Docker/WSL2:约 6–15 小时
硬盘较慢或内存较低:可能超过 15 小时

运行过程中终端会持续输出迭代信息,例如:

rms=...
blurring input image with Gaussian...
setting smoothness coefficient...

这属于正常运行信息,不是报错。


七、FreeSurfer 运行过程中生成的文件

运行开始后,会在 Windows 本地生成:

E:\freesurfer\subjects\sub-001\

典型结构如下:

E:\freesurfer\subjects\sub-001\
│
├── label\
├── mri\
├── scripts\
├── stats\
├── surf\
├── tmp\
├── touch\
└── trash\

其中比较重要的目录包括:

mri      存放体积图像、中间分割结果和归一化结果
surf     存放皮层表面重建结果
stats    存放统计指标,是后续提表最重要的目录
scripts  存放运行日志和命令记录

运行日志文件为:

E:\freesurfer\subjects\sub-001\scripts\recon-all.log

可以在 VS Code 中直接打开查看,也可以另开 PowerShell 实时查看:

Get-Content "E:\freesurfer\subjects\sub-001\scripts\recon-all.log" -Tail 30 -Wait

八、运行完成后的关键文件

recon-all 成功结束后,应出现以下关键文件:

E:\freesurfer\subjects\sub-001\stats\aseg.stats
E:\freesurfer\subjects\sub-001\stats\lh.aparc.a2009s.stats
E:\freesurfer\subjects\sub-001\stats\rh.aparc.a2009s.stats

这些文件分别对应:

aseg.stats
    皮层下结构体积统计,如丘脑、尾状核、壳核、海马、杏仁核、脑室等。

lh.aparc.a2009s.stats
    左半球 Destrieux atlas 皮层分区统计。

rh.aparc.a2009s.stats
    右半球 Destrieux atlas 皮层分区统计。

后续 braincharts 常模所需的主要指标来自这些文件。


九、文件转换说明

1. .nii 与 .nii.gz

原始数据为 .nii 格式可以直接输入 FreeSurfer:

recon-all -i /data/001.nii -s sub-001 -all

不需要强制转换为 .nii.gz

.nii.gz 只是 gzip 压缩后的 NIfTI 文件,图像内容与 .nii 一致。不要直接通过重命名把 .nii 改成 .nii.gz

错误做法:

Rename-Item 001.nii 001.nii.gz

正确压缩方式:

gzip -k 001.nii

或者在 FreeSurfer 容器内使用:

mri_convert /data/001.nii /subjects/001_T1w.nii.gz

但对于当前流程,原始 .nii 可以直接使用,不需要额外转换。


2. FreeSurfer 内部转换

运行 recon-all 后,FreeSurfer 会自动将输入 NIfTI 转换为内部 MGZ 格式,例如:

/subjects/sub-001/mri/orig/001.mgz

对应 Windows 路径为:

E:\freesurfer\subjects\sub-001\mri\orig\001.mgz

这一步由 FreeSurfer 自动完成,不需要手动操作。


十、失败重跑的处理方式

如果第一次运行由于 license、路径或其他错误失败,可能已经生成不完整目录:

E:\freesurfer\subjects\sub-001

此时再次运行:

recon-all -i /data/001.nii -s sub-001 -all

可能报错:

ERROR: You are trying to re-run an existing subject with new input data (-i).

如果确认之前结果是不完整的,应先删除该 subject 目录:

Remove-Item -Recurse -Force "E:\freesurfer\subjects\sub-001"

然后重新运行:

docker run --rm -it `
  -v "E:\freesurfer\data:/data:ro" `
  -v "E:\freesurfer\subjects:/subjects" `
  -v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro" `
  -e SUBJECTS_DIR=/subjects `
  freesurfer/freesurfer:6.0 `
  recon-all -i /data/001.nii -s sub-001 -all -openmp 4

如果只是中途中断,且希望继续同一个 subject,不应再加 -i,可以尝试:

docker run --rm -it `
  -v "E:\freesurfer\data:/data:ro" `
  -v "E:\freesurfer\subjects:/subjects" `
  -v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro" `
  -e SUBJECTS_DIR=/subjects `
  freesurfer/freesurfer:6.0 `
  recon-all -s sub-001 -all -openmp 4

但如果是 license 或路径错误导致早期失败,建议删除后重跑更稳妥。


十一、单被试运行脚本

建议在:

E:\freesurfer\scripts\

中新建脚本:

run_sub001.ps1

内容如下:

docker run --rm -it `
  -v "E:\freesurfer\data:/data:ro" `
  -v "E:\freesurfer\subjects:/subjects" `
  -v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro" `
  -e SUBJECTS_DIR=/subjects `
  freesurfer/freesurfer:6.0 `
  recon-all -i /data/001.nii -s sub-001 -all -openmp 4

运行方式:

cd E:\freesurfer\scripts
.\run_sub001.ps1

如果 PowerShell 不允许运行脚本,可以使用:

powershell -ExecutionPolicy Bypass -File .\run_sub001.ps1

十二、批量处理脚本

1. 输入文件命名规则

假设 E:\freesurfer\data 中有:

001.nii
002.nii
003.nii
004.nii

希望输出为:

sub-001
sub-002
sub-003
sub-004

2. 批量运行 recon-all 脚本

在:

E:\freesurfer\scripts\

中新建:

run_batch.ps1

写入以下内容:

$DataDir = "E:\freesurfer\data"
$SubjectsDir = "E:\freesurfer\subjects"
$License = "E:\freesurfer\license.txt"
$Image = "freesurfer/freesurfer:6.0"
$Threads = 4

$files = Get-ChildItem $DataDir -Filter "*.nii"

foreach ($file in $files) {

    $base = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
    $subject = "sub-$base"

    $outDir = Join-Path $SubjectsDir $subject
    $doneFile = Join-Path $outDir "scripts\recon-all.done"

    Write-Host "=========================================="
    Write-Host "Processing file: $($file.Name)"
    Write-Host "Subject ID: $subject"
    Write-Host "Output dir: $outDir"
    Write-Host "=========================================="

    if (Test-Path $doneFile) {
        Write-Host "Skip $subject : recon-all.done already exists."
        continue
    }

    docker run --rm -it `
      -v "${DataDir}:/data:ro" `
      -v "${SubjectsDir}:/subjects" `
      -v "${License}:/opt/freesurfer/license.txt:ro" `
      -e SUBJECTS_DIR=/subjects `
      $Image `
      recon-all -i "/data/$($file.Name)" -s $subject -all -openmp $Threads
}

运行方式:

cd E:\freesurfer\scripts
.\run_batch.ps1

如果执行策略受限:

powershell -ExecutionPolicy Bypass -File .\run_batch.ps1

3. 批处理逻辑说明

该脚本会自动:

1. 扫描 E:\freesurfer\data 下所有 .nii 文件
2. 对每个文件生成 subject ID
   例如 001.nii → sub-001
3. 检查是否已经存在 recon-all.done
4. 如果已经完成,则跳过
5. 如果未完成,则启动 FreeSurfer 6.0 容器运行 recon-all
6. 输出结果写入 E:\freesurfer\subjects

十三、提取 FreeSurfer 统计表格

当所有被试都完成 recon-all 后,需要从 FreeSurfer 输出目录中提取表格。

1. 进入 FreeSurfer 容器

在 PowerShell 中运行:

docker run --rm -it `
  -v "E:\freesurfer\subjects:/subjects" `
  -v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro" `
  -e SUBJECTS_DIR=/subjects `
  freesurfer/freesurfer:6.0 `
  bash

进入容器后:

cd /subjects
ls -d sub-* > subjects.txt

2. 提取左半球 Destrieux 皮层厚度

aparcstats2table \
  --subjectsfile subjects.txt \
  --hemi lh \
  --parc aparc.a2009s \
  --meas thickness \
  --tablefile lh.aparc.a2009s.thickness.tsv

3. 提取右半球 Destrieux 皮层厚度

aparcstats2table \
  --subjectsfile subjects.txt \
  --hemi rh \
  --parc aparc.a2009s \
  --meas thickness \
  --tablefile rh.aparc.a2009s.thickness.tsv

4. 提取皮层下体积

asegstats2table \
  --subjectsfile subjects.txt \
  --meas volume \
  --tablefile aseg.volume.tsv

5. 输出文件位置

提取完成后,Windows 本地会生成:

E:\freesurfer\subjects\subjects.txt
E:\freesurfer\subjects\lh.aparc.a2009s.thickness.tsv
E:\freesurfer\subjects\rh.aparc.a2009s.thickness.tsv
E:\freesurfer\subjects\aseg.volume.tsv

这些表格后续可用于整理成 braincharts 常模输入格式。


十四、自动提取统计表格脚本

可以在:

E:\freesurfer\scripts\

中新建:

extract_stats.ps1

内容如下:

$SubjectsDir = "E:\freesurfer\subjects"
$License = "E:\freesurfer\license.txt"
$Image = "freesurfer/freesurfer:6.0"

docker run --rm -it `
  -v "${SubjectsDir}:/subjects" `
  -v "${License}:/opt/freesurfer/license.txt:ro" `
  -e SUBJECTS_DIR=/subjects `
  $Image `
  bash -c "cd /subjects && \
           ls -d sub-* > subjects.txt && \
           aparcstats2table --subjectsfile subjects.txt --hemi lh --parc aparc.a2009s --meas thickness --tablefile lh.aparc.a2009s.thickness.tsv && \
           aparcstats2table --subjectsfile subjects.txt --hemi rh --parc aparc.a2009s --meas thickness --tablefile rh.aparc.a2009s.thickness.tsv && \
           asegstats2table --subjectsfile subjects.txt --meas volume --tablefile aseg.volume.tsv"

运行:

cd E:\freesurfer\scripts
.\extract_stats.ps1

或:

powershell -ExecutionPolicy Bypass -File .\extract_stats.ps1

十五、质量控制建议

FreeSurfer 结果不能只看是否生成文件,还需要做质量控制。

建议至少检查:

1. 原始 T1 是否存在明显运动伪影、截断、强度不均匀。
2. FreeSurfer brainmask 是否正确。
3. white surface 是否沿白质边界。
4. pial surface 是否贴合脑表面,是否跑到脑外。
5. 颞叶、眶额叶、脑底部是否有明显错误。
6. stats 文件是否完整生成。

主要日志文件:

E:\freesurfer\subjects\sub-001\scripts\recon-all.log

成功完成标志通常类似:

recon-all -s sub-001 finished without error

十六、常见错误与处理

1. 在容器内部运行 docker run

错误提示:

bash: docker: command not found
bash: -v: command not found

原因:

当前已经在容器内部,不能再运行 docker run。

解决:

exit

回到 Windows PowerShell 后再运行 docker run


2. license 找不到

错误提示:

ERROR: FreeSurfer license file /opt/freesurfer/license.txt not found.

解决:

使用以下挂载方式:

-v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro"

3. 重复运行已有 subject

错误提示:

ERROR: You are trying to re-run an existing subject with new input data (-i).

如果之前是失败的半成品,删除后重跑:

Remove-Item -Recurse -Force "E:\freesurfer\subjects\sub-001"

如果只是继续中断任务,不要加 -i

docker run --rm -it `
  -v "E:\freesurfer\data:/data:ro" `
  -v "E:\freesurfer\subjects:/subjects" `
  -v "E:\freesurfer\license.txt:/opt/freesurfer/license.txt:ro" `
  -e SUBJECTS_DIR=/subjects `
  freesurfer/freesurfer:6.0 `
  recon-all -s sub-001 -all -openmp 4

4. VS Code 无法远程进入 FreeSurfer 容器

错误提示:

Missing GLIBCXX >= 3.4.25
Missing GLIBC >= 2.28

原因:

FreeSurfer 6.0 容器基于较老的 CentOS6 环境,不能满足 VS Code Server 要求。

处理方式:

不要使用 VS Code Remote Attach Container。
只用 VS Code 打开 Windows 本地目录 E:\freesurfer,
然后在 VS Code 终端中运行 docker run 命令。

十七、后续 braincharts 常模分析前需要准备的文件

完成 FreeSurfer 后,至少应准备:

1. lh.aparc.a2009s.thickness.tsv
2. rh.aparc.a2009s.thickness.tsv
3. aseg.volume.tsv
4. 人口学表:subject_id, age, sex, site

人口学表示例:

subject_id,age,sex,site
sub-001,23.5,1,site01
sub-002,31.2,0,site01
sub-003,45.8,1,site01

后续需将 FreeSurfer 提取的结构指标与人口学表合并,并根据 braincharts 官方模型要求调整 ROI 列名,最终计算每个被试、每个脑区的 deviation Z-score。


十八、推荐执行顺序

完整执行顺序如下:

1. 建立 E:\freesurfer 文件夹结构
2. 放入 license.txt
3. 将 T1 .nii 文件放入 data
4. 拉取 freesurfer/freesurfer:6.0 镜像
5. 运行单个被试测试
6. 检查 stats 和 recon-all.log
7. 单例成功后运行 run_batch.ps1
8. 所有被试完成后运行 extract_stats.ps1
9. 检查输出 TSV 表格
10. 整理人口学表
11. 进入 braincharts 常模分析
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐