先说个真实感受:fNIRS数据处理里,做光极可视化和通道坐标计算,看起来只是“画个图”的辅助工作,很多人一上来就急着跑GLM、算HbO浓度变化,结果到了写论文要报告通道坐标、要画大脑皮层光极分布图的时候,才发现之前根本没把这一步走顺,回头补数据、重跑流程,非常折腾。我当年刚接触fNIRS时也是这样,对着NIRS-SPM的英文文档和界面发懵,光是搞懂怎么让光极位置落到标准脑空间、怎么把通道坐标导出来,就反复折腾了好几天。这篇内容就是把我自己的实操过程完整梳理一遍,从软件安装、数据目录整理开始讲起,重点拆解NIRS-SPM里光极可视化与通道坐标计算的具体操作,并且把每一步为什么要这么做、底层在算什么讲清楚。无论你是刚开始做fNIRS数据的新手,还是已经跑过一些流程、但一直没把光极坐标这块弄明白的研究生,这篇文章都直接给你可以照着做的路径。
1. 为什么要用NIRS-SPM做光极可视化与坐标计算
NIRS-SPM(Near-Infrared Spectroscopy Statistical Parametric Mapping)是韩国KAIST团队开发的fNIRS数据分析工具包,运行在MATLAB环境下。它最大的特点是把fNIRS数据处理方式向fMRI的SPM框架靠拢,支持从预处理一直到统计推断的完整流程。而在它整个流程里,光极可视化和通道坐标计算处于一个承上启下的位置——上游承接光极位置的物理测量,下游直接影响空间配准、皮层映射和结果报告。
实际应用场景非常集中:你需要回答“我的50个通道分别覆盖了大脑的哪些脑区”,或者要在论文里画出“通道在标准脑模板上的分布图”,又或者要结合解剖标签(比如BA分区、AAL模板)来分析通道-脑区对应关系。这些需求都绕不开一个前提:把每个通道的光极坐标和中心坐标算出来,并且投影到一个标准空间中。
那么为什么推荐NIRS-SPM而不是其他工具?两个原因最实际。第一,它高度集成,从光极可视化、通道坐标计算到后续的GLM统计,全都在一个环境里完成,不用在多个工具之间来回导数据。第二,它兼容性好,既可以从旧版NIRS数据文件(如.txt或.mat格式)读取信息,也支持新版SNIRF格式,加上MATLAB生态本身在科研场景的普及度,遇到问题查资料、问师兄师姐都方便。
2. 动手之前:数据准备与NIRS-SPM能识别什么
2.1 软件环境与版本选择
NIRS-SPM的核心运行环境是MATLAB,推荐使用R2018b到R2021a这些相对稳定的版本。太老的MATLAB版本在跑新版NIRS-SPM时容易出现脚本不兼容,太新的版本又可能因为MATLAB自身组件更新,导致个别GUI控件显示异常。我自己的经验是,MATLAB R2020a配合NIRS-SPM的13.x版本,整体运行非常稳。
安装步骤很简单,三步就够:
- 从NIRS-SPM官网(nitrc.org/projects/nirs_spm)下载最新工具包压缩文件。
- 解压到本地任意路径,路径中不要有中文和空格,比如放在
D:\Tools\NIRS_SPM。 - 打开MATLAB,点击“设置路径”按钮,选择“添加并包含子文件夹”,把NIRS_SPM整个文件夹加进去。
注意:每次新开MATLAB后,如果提示找不到nirs_spm函数,先检查路径是否被重置了。建议在MATLAB启动脚本(startup.m)里直接加入
addpath(genpath('D:\Tools\NIRS_SPM')),省得每次都手动加。
2.2 数据目录结构:NIRS-SPM严格到什么程度
这是NIRS-SPM最容易被吐槽、但也是最重要的一个点:它对数据目录结构有强制要求,不符合就是报错。很多新手在刚开始玩的时候,把所有文件一股脑放在同一个文件夹里,结果运行到一半就报“file not found”或者“wrong data format”。
NIRS-SPM处理单次实验数据时,目录结构应该这样组织:
- 项目根目录
- Subj001(被试文件夹)
- 条件1文件夹(比如Task1)
- 条件2文件夹(比如Task2)
- Subj001(被试文件夹)
具体来说,NIRS-SPM要求每个被试的数据放在一个独立文件夹下,文件夹名称不能包含空格和特殊符号。在同一个被试文件夹里,不同实验条件的子文件夹区分放好,每个子文件夹里放该条件对应的数据文件,文件名推荐使用英文和数字组合,如fNIRS_data.nirs或data.txt。
这个目录结构之所以严格,是因为NIRS-SPM在处理多被试、多条件数据时,会按照固定路径去批量读取数据。如果路径不规范,它的批处理功能就会挂掉。哪怕你只是想做一个被试的可视化,我也建议从一开始就按照规范的目录结构来整理数据,这样后面要做组分析时,你不用把数据再翻出来重排一遍。
2.3 数据文件格式说明
NIRS-SPM支持的数据文件格式主要有两个阶段:老版本常用的是.nirs文件,这是一个基于MATLAB的二进制或文本格式文件;新版本支持.snirf格式,这是fNIRS领域的标准格式,设计目的是解决不同厂商设备数据格式不互通的问题。
.nirs文件本质上是MATLAB的.mat结构体,里面通常包含这些关键字段:
d:原始光强数据矩阵,维度是[时间点 × 通道数×波长数]SD:光源-探测器布局结构体,包含光极坐标、通道定义、波长等信息t:时间向量aux:辅助通道数据(如呼吸、心率等)
如果是从TBSI、Hitachi、NIRx等不同厂家的设备导出数据,建议先用设备自带软件把数据转化成SNIRF格式,或者手动整理成NIRS-SPM兼容的结构。这一步虽然繁琐,但一次做好能省掉后面大量麻烦。
3. 核心实操:NIRS-SPM界面光极可视化全流程
3.1 启动NIRS-SPM与第一个关键选择
一切准备就绪后,在MATLAB命令行输入:
nirs_spm回车后,会弹出NIRS-SPM的主界面,左侧是一排功能按钮,从上到下依次是“Setup”、“GLM”、“Visualization”、“NIRS-SPM Results”等。光极可视化和通道坐标计算主要涉及“Setup”和“Visualization”两个功能模块。
(这里由于无法直接贴图,我会把界面布局和每个按钮的定位描述清楚。你打开软件后会看到主界面标题栏显示NIRS-SPM版本号,下方正中间主操作区域,左侧按钮就是导航菜单。)
进入Setup界面后,系统会先让你选择数据文件夹。这时要特别留意,NIRS-SPM会检查你选中的文件夹路径中是否包含被试和条件层级。正确做法是:直接选中被试文件夹(如Subj001这一级)作为当前处理路径,而不是选到最外层项目根目录,也不是选到具体条件子文件夹里。选错层级会导致后面设定条件名称时找不到数据。
3.2 Step by Step:定义被试与条件
假设你的实验有两个条件:TaskA和TaskB。
在Setup界面中,参照下方步骤操作:
- 在“Subject ID”输入框中填写被试编号,如
001或S01。 - 点击“Add”按钮,将被试信息加入列表。
- 在“Condition”区域,手动输入条件名称,如
TaskA,点击“Add”。 - 重复上述步骤添加
TaskB。 - 点击“Load”按钮,系统会按你设定的被试文件夹和条件名自动检索对应数据文件。
我实际操作时发现,很多新手在这里会因为条件名和数据文件名不一致而找不到数据。比如条件名填的是TaskA,但数据文件叫block1.nirs,这时NIRS-SPM会提示找不到匹配文件。解决办法有两个:一是直接把数据文件重命名为条件名对应的名称,二是在文件中检查数据是否包含多阶段数据,如果是单次数据,可以调整条件名称与文件名保持一致。
3.3 光极位置设置:从个体空间到标准空间的关键一步
光极可视化最核心的是配准。NIRS-SPM提供了两种配准方式:手动配准和自动配准。
手动配准是传统做法,流程大概是:
- 在Setup界面,点击“Set Channel Positions”或类似选项(不同版本位置略有差异,一般在“Optodes”相关菜单下)。
- 系统会打开一个3D可视化窗口,显示一个默认的头部模型。
- 在右侧面板中找到三个关键解剖标志点(fiducials):鼻根点(Nasion)、左耳前点(Left Ear)、右耳前点(Right Ear)。
- 按照你自己的实验光极放置图,手动调整光极点的位置,让它们和头部模型匹配。
- 保存配准结果,此时光极位置已经映射到了头部模型所在的坐标空间。
自动配准则是新版本NIRS-SPM的亮点,它利用概率配准算法,将光极位置模板自动对准到标准脑空间(MNI空间)。这需要在界面里选择对应的10-20系统模板(比如国际10-20系统的探头排布模板),然后一键完成配准。
我自己更推荐新用户优先尝试自动配准,理由很直接:手动配准受主观影响大,同一个光极图不同人来放可能差出好几毫米;自动配准用的模板经过多被试数据验证,可重复性和标准性更强。但前提是你的实验光极排布必须严格按照某个标准模板摆放,如果自己随便定的位置,自动配准反而会闹笑话,这时候手动配准是唯一选择。
3.4 通道中心坐标计算逻辑
配准完成后,NIRS-SPM会根据你设定的光源-探测器对(也就是通道定义),自动计算每个通道的中心坐标。计算原理很直接:通道中心坐标等于该通道光源坐标和探测器坐标的中点。
假设第1通道连接光源S1和探测器D3,S1的坐标为(x1, y1, z1),D3的坐标为(x2, y2, z2),则通道中心坐标C1为:
C1 = ((x1+x2)/2, (y1+y2)/2, (z1+z2)/2)这是最简单的裸中点算法。不过NIRS-SPM在可视化时,会进一步基于头部皮层表面模型做投影,使得通道中心坐标最终落在大脑皮层表面上,而不是停留在头皮表面。这一点非常重要,因为后续做通道与脑区对应关系分析时,我们要的是皮层表面的坐标,而不是头皮上的坐标。
3.5 执行可视化与结果输出
一切设置完成后,回到NIRS-SPM主界面,点击“Visualization”按钮。在弹出的可视化界面中,你会看到:
- 灰色的半透明头部三维模型
- 红色的点表示光源(Source)
- 蓝色的点表示探测器(Detector)
- 黄色或绿色的线段连接对应通道
- 通道中心可能用小球或其他标记表示,并标注通道编号
这个界面支持拖动旋转、缩放,方便你从各个角度检查光极是否落在预期脑区上。
你可以通过菜单栏的“Save as Image”保存当前视角的截图,用于论文或组会汇报。更关键的是,这里生成的通道坐标信息可以导出为文本文件或MATLAB变量,在后续分析中直接复用到通道-脑区对应分析或结果报告中。
3.6 把结果导出成你想要的形式
在Visualization界面中,找到“Export”或“Save”菜单。NIRS-SPM通常允许导出:
- 通道坐标文本文件(每行格式:通道号、x、y、z)
- 光极坐标文本文件(每行格式:光极类型、编号、x、y、z)
- 可视化图像(多种格式可选)
导出的坐标默认是MNI空间坐标(如果使用了自动配准),这意味着你后续可以和在MNI空间做统计的fMRI数据一起做联合分析,也可以直接在标准脑图谱上查看通道覆盖区域。
我在实际项目中通常这样使用这个功能:导出所有通道坐标后,直接用MATLAB写个小脚本,把通道坐标映射到AAL2模板的各个脑区上,然后统计每个通道到底落在哪个脑区、概率是多少。这个过程就完全建立在NIRS-SPM导出的坐标基础上。
4. NIRS-SPM通道坐标计算原理与结果解读
4.1 MNI坐标到底是怎么来的
很多初学者会有疑问:我的光极位置明明是在个体头皮的物理空间测的,经过NIRS-SPM处理后就给出了一组MNI坐标,这中间经历了什么?
简单来说,配准算法做的事是:把你个体空间中的三个解剖标志点(鼻根、左耳前点、右耳前点),对齐到标准头部模型中的同名标志点上。标准头部模型本身已经用MNI坐标系统标定好了,所以对齐完成后,你的每个光极点就从“个体空间坐标”被映射成了“标准空间MNI坐标”。
这里有个重要概念:坐标转换是刚性的还是非刚性的?NIRS-SPM自动配准通常采用刚性变换加少量非线性调整,实际精度取决于你手动标注解剖点的准确度以及头部模型和真实头型的差异。所以,在做这个步骤前,务必要核对你的三维定位仪数据是否正确,三个标志点有没有标反,否则后续所有坐标都会出错。
4.2 通道编号和坐标一一对应
当NIRS-SPM输出通道坐标时,它同时会给每个通道分配一个内部的通道编号。这个编号的顺序通常遵循你在SD结构体中的通道定义顺序。如果想知道“通道1到底连接了哪个光源和哪个探测器”,需要回到SD结构中查找(SD.SrcPos、SD.DetPos以及SD.MeasList)。
我建议在导出坐标后,顺手把通道对应关系单独保存一份,格式类似:
| 通道编号 | 光源编号 | 探测器编号 | 中心坐标(x, y, z) |
|---|---|---|---|
| 1 | S1 | D3 | (-38.2, 62.4, 8.5) |
| 2 | S1 | D4 | (-42.1, 51.3, 12.8) |
| ... | ... | ... | ... |
这样在写方法部分时,你可以准确写明每个通道对应的测量位置。
4.3 结果解读:哪些脑区被覆盖了
拿到通道坐标后,有一种常见做法是用NIRS-SPM自带的Anatomical Labeling功能,直接查看每个通道落在AAL模板的哪个脑区。这个功能通常在可视化界面里也有入口。
操作上,点击相关按钮后,NIRS-SPM会读取MNI空间中每个通道坐标所在的脑区标签,然后在界面中列出通道号和对应脑区名称。输出的信息格式大致像“Channel 5: Left Precentral Gyrus (BA4)”。
如果你需要更高精度的解剖对应关系,建议把坐标导入到外部软件中,比如xf(IBASPM)或MRIcron,基于更细的图谱模板(如Brodmann分区、LONI概率图谱)做后处理。这也解释了为什么坐标导出功能如此重要——它是连接NIRS-SPM和外部图谱分析工具的桥梁。
5. 踩坑实录:常见问题、排查方法与避坑建议
这一部分是我自己反复折腾后的真实记录。每一位刚开始用NIRS-SPM做光极可视化和通道坐标计算的人,几乎都会遇到下面几类问题,我把常见报错和解决方案整理成速查表:
| 问题表现 | 可能原因 | 解决方案 |
|---|---|---|
| 加载数据时提示“no data found” | 条件名与文件名不匹配,或目录结构不对 | 检查文件夹层级,确认条件名与数据文件名一致;将数据统一放到条件子文件夹下 |
| 可视化窗口空白,无头部模型 | 头部模型文件缺失或路径被改动 | 检查NIRS-SPM安装目录下的template文件夹是否存在;重新解压工具包并设置路径 |
| 自动配准按钮灰色不可用 | 当前版本不支持该功能,或未选择标准模板 | 升级NIRS-SPM到最新版本;手动配准作为备选方案 |
| 坐标导出后,部分通道坐标为NaN | 配准失败或光极未全部对齐 | 检查手动配准时是否遗漏部分光极点;重新执行配准流程 |
| 通道坐标明显偏离脑区(如落在后脑勺) | 解剖标志点输入错误,或配准模板选择错误 | 重新检查Nasion和左右耳前点的坐标;确认图片中的前、后、左、右方向是否颠倒 |
5.1 我踩过最深的坑:目录结构不规范导致批处理崩溃
我自己早期做一批共20个被试数据时,因为原始数据是从不同电脑拷过来的,文件夹命名里有的带下划线、有的带空格,还有被试文件夹下面条件文件夹直接用中文命名。结果NIRS-SPM在处理到第7个被试时直接报错退出,前面跑完的能耗和数据全部白费。
那次教训让我总结出两条铁律:
- 原始数据拷进分析环境后,第一时间统一重命名所有文件夹和文件,用英文字母+数字,不要用任何特殊符号和中文。
- 每个被试文件夹下,各个条件的子文件夹名字用同一个标准(如
Task1、Task2),并且确认每个条件下都有且仅有一份数据文件。一个条件多分数据文件也会导致加载异常。
5.2 自动配准模板选错了会怎样
还有一次,我用一套按国际10-20系统扩展摆放的光极数据去做自动配准,但模板选成了标准10-20(较少通道数版本),结果输出坐标整体偏移,特别是额叶和颞叶区域的通道坐标偏差明显,肉眼都能看出通道连线落在了不匹配的位置。
正确做法是选与实验通道数匹配的模板。如果你不确定自己的排布属于哪种模板,宁可用手动配准,手动导入三个标志点之后再微调每个光极位置,虽然费时但可靠性更高。自动配准确实方便,但是模板选错会带来系统性错误,这种错误往往不会在报错信息里直接被发现。
5.3 坐标导出后与旧数据不匹配的问题
如果你是接着之前已经处理过的数据继续分析,一定要搞清楚旧数据里的坐标是哪个空间的。NIRS-SPM不同版本或者不同设置导出的坐标可能不在同一个空间体系里,比如有的版本导出的是Talairach坐标,有的导出的是MNI坐标。两者只能通过转换公式近似互换,不适合直接混用。
最常见的安全做法是:统一使用MNI空间坐标,并在论文中明确写明“All channel coordinates were reported in MNI space after automatic anatomical registration by NIRS-SPM”。
6. 操作过程中我总结的几个实用技巧
这一节是额外补充的内容,针对性解决实操中一些“文档没有细说但特别管用”的点。不需要全部照搬,但有几条确实能帮你节省大量时间。
关于检查配准质量:自动配准完成后,不要急着看通道坐标,先看可视化图里的三个标志点是否与模板的标准位置吻合。如果Nasion点明显偏了,或者左右耳前点位置不对称,十有八九是配准过程引入了偏差。我每次跑完都会把图旋转一圈,从正面、侧面、顶面各检查一次,再决定是否接受这个配准结果。
关于批量处理多被试:如果项目里有几十个被试,不要每一个都手动打开界面操作。NIRS-SPM支持脚本批量化,核心思路是循环调用配准和数据读取函数。实现也不复杂,可以在MATLAB命令行用循环依次切换被试文件夹,然后重复执行加载和可视化相关命令。虽然最初的代码搭建需要点时间,但跑完后能省下大量重复劳动时间。
关于验证一条通道位置是否合理:可以找一个通道,把它在MNI空间的坐标放到在线MNI坐标查询工具里对比一下,看落到哪个脑区,再和自己实验设计的预期对比。比如你设计时想覆盖背外侧前额叶(DLPFC),查出来的坐标却落在了颞叶,那说明光极位置或者配准出了问题。用坐标交叉验证是最高效的自检手段,希望你能养成这个习惯。
7. 从可视化到后续分析的扩展思路
光极可视化和通道坐标计算只是fNIRS数据分析的起点,但这一步做好了,后面的路会顺很多。沿着这条线继续往下走,有两条常见的扩展方向:
第一,通道-脑区对应表生成。把NIRS-SPM导出的通道坐标放到AAL2、Brodmann或Talairach图谱中,生成一张完整的通道-脑区映射表。有了这张表,论文方法部分可以非常清楚地说明每个通道测量的解剖区域,审稿人一眼就能看懂你的测量覆盖范围是否合理。
第二,与fMRI联合分析。如果是fNIRS-fMRI同步采集的数据,MNI空间的通道坐标可以直接和fMRI的ROI统计结果空间对齐,进一步做有效连接或皮层活动一致性分析。整个联合分析的逻辑起点就是这个坐标转换的准确性。
我自己使用NIRS-SPM一年多来,最大的体验是:这个工具其实做得挺全面,但它的文档和新手引导确实做得不够友好,很多关键细节都是要在报错和排查里一点点学会的。正因如此,把整个流程完整写下来,希望后来的研究者不用重复踩同样的坑。如果在实际使用中遇到其他奇怪的问题,也欢迎多交流,毕竟数据分析这种技术活,经验都是靠一个个真实问题攒下来的。