教程 | 近红外数据的预处理和平均(上)

news2024/11/17 23:36:29

前言

近红外光谱(NIRS)是一种测量流经传感器所在组织的血液中氧合水平的方法。它基于这样一个事实,即含氧血红蛋白和脱氧血红蛋白具有不同的吸收光谱,因此你会看到它有不同的颜色。大多数近红外系统在每个光源光电二极管发射2个波长的光,通常约700和850nm,有些系统则使用3个波长。

光电探测器被放置在离源光极几厘米远的地方。光源和探测器的光电二极管共同组成一个通道,但系统对于一个通道总是有两个空间位置(大多数人认为记录的活动主要来自光源和探测器的中间位置)和两个波长。光散射穿过组织,一小部分光将通过光电探测器在组织的表面被探测到。在头皮上检测到的光强随着位于源和探测器光电二极管之间的组织中含氧和脱氧血红蛋白浓度的变化而有系统地波动。由于(或多或少的随机)散射,大多数到达探测器的光只穿过了组织表面,只有一小部分穿过了表面下更深一点的区域。通常情况下,在头皮上检测到的光穿过了一个组织体积,从侧面看是一个香蕉形状。如果光源和探测器被放置得更远,那么被探测到的光可能在组织中传播得更深。但实际上,这是有限的,因为到达探测器的那部分光会随着光源-探测器距离的增加而急剧下降。当测量大脑中血红蛋白的变化时,颅骨作为一个重要的屏障,吸收了很多光。因此,对大脑深处进行扫描只有在头骨较薄的婴儿中才可行。

氧和脱氧血红蛋白的浓度不能以绝对条件评估,这意味着所有的测量都将反映的是相对变化。因此,像EEG和MEG一样,需要一个基线期。在血流动力学响应函数(HRF)上,记录和后期处理的fNIRS信号与fMRI类似,但具有更高的时间分辨率。因此,至少在理论上,可以测量曲线的初始倾角。与fMRI相比,空间分辨率要差很多,通常我们主要从大脑表面进行记录。因此,绘图通常类似于EEG。EEG的时间分辨率更高,但fNIRS的一个优势是,测量的信号只来自光源和探测器之间的区域。fNIRS是一种非常有前景的方法,可以测量无法通过fMRI扫描仪测试的人群的大脑活动,并且允许被试做出无法在扫描仪中完成的更多动作。但是血压会随着你所做的任务而变化,从而也会影响测量到的信号。

单通道近红外数据的预处理和平均

本文将演示如何分析单个通道的功能性近红外光谱(fNIRS)数据。主要使用FieldTrip对Artinis近红外光谱数据进行基本分析。由于本文分析的是单通道数据,因而没有展示如何处理坏通道,关于如何删除坏通道和分析多通道的信息详见近红外数据的预处理和平均(下),关注茗创科技,不迷路哦~

本教程所使用的数据集

本文将使用到Artinis Medical Systems的Oxymon MK III系统采集运动皮层信息的近红外数据集。光电极放置在运动皮层上,随后要求被试进行手指敲击。除了来自大脑的记录,还记录了一个带有标注的事件(event)通道,在事件通道中,参与者开始敲手指时的记录为“A”,停止敲手指的记录为“B”。这个运动任务一共重复了12次,所有的数据,包括事件通道,都保存在一个.oxy3文件中。本文中使用的数据可以从https://download.fieldtriptoolbox.org/tutorial/nirs_multichannel/上获得。

分析步骤

根据数据和实验设计的不同,分析可以有许多不同的方式和顺序。这里将介绍分析步骤的标准顺序,随后小伙伴们可以逐步测试这些步骤。以下步骤为分析fNIRS数据提供了一个很好的标准方法,如图1所示:

1、读取连续数据;

2、(优先地)裁剪记录开始和结尾的数据,特别是当这些需要被剪切的数据是噪声或不感兴趣的片段时;

3、删除通道中的坏段(例如,运动伪影);

4、将光密度转化为含氧血红蛋白(oxyHb)和脱氧血红蛋白(deoxyHb)浓度的变化;

5、将功能响应与系统反应分开,可以使用以下方法其中之一,或者组合使用;

①滤波

②减去参考通道

③每个通道都有反相关的oxyHb/deoxyHb

6、在实验任务中,定义与试次相对应的段;

7、将试次数据平均并可视化。

图1.标准fNIRS分析流程概览。

本文分析的数据集名为“motor_cortex.oxy3”。首先,需要将FieldTrip指向这个文件。注意,FieldTrip没有图形用户界面(GUI),而是通过脚本来实现最终目标。需要将FieldTrip文件夹添加到MATLAB路径中,然后执行ft_defaults函数,输入:

ft_defaults

通常,FieldTrip会将函数的所有输入参数存储在一个名为“configuration”或“cfg”的本地变量中。虽然MATLAB变量可以取任何名称,但为了方便起见,这里将继续使用名称“cfg”。分析流程中的一个(关键)步骤是读入数据并对数据进行预处理。为此,这里将使用FieldTrip函数ft_preprocessing。因为FieldTrip是一个开源工具箱,可通过输入以下代码来查看代码细节:

edit ft_preprocessing

读取和裁剪数据

在变量cfg.dataset中输入数据的文件名。

cfg = [];cfg.dataset = 'motor_cortex.oxy3';

通常需要指定完整路径,包括驱动器号。这可以确保代码将独立于当前的MATLAB位置执行。将原始数据、处理过的数据和脚本保存在三个不同的位置是一个很棒的做法(推荐)。在本例中,将使用当前工作目录中的数据。

现在,尝试执行ft_preprocessing:

[data] = ft_preprocessing(cfg);

在处理.oxy3文件时,需要加载包含光极布局的光极模板。如果MATLAB无法在你的路径上找到模板文件,它将会弹出一个图形用户界面对话框,要求你定位XML文件。默认情况下,该文件位于C:\Program Files(x86)\Artinis Medical Systems BV\Oxysoft 3.0.103。需要选择的文件是optodetemplates.xml。鉴于FieldTrip经常会需要搜索此函数,最好将此函数复制到存放MATLAB分析脚本或fNIRS数据的文件夹中。关于如何选择最佳模板的信息,请参阅Artinis网页上的这篇博客文章(网址:https://www.artinis.com/blogpost-all/2017/6/27/how-do-i-choose-the-correct-fibers-and-template-for-my-oxymon)。

当你在屏幕上看到类似这样的内容时,FieldTrip就完成了。

>> the call to "ft_preprocessing" took 9 seconds

仔细观察workspace,就会注意到添加了一个名为“data”的新变量。这个名称与ft_preprocessing前面输入的名称相同。如果写入[something],那么变量'something'就会被添加到workspace。

如果按照以上描述进行预处理,数据将具有以下字段:​​​​​​​

data =        hdr: [1x1 struct]      label: {48x1 cell}       time: {[1x4462 double]}      trial: {[48x4462 double]}    fsample: 5 sampleinfo: [1 4462]       opto: [1x1 struct]        cfg: [1x1 struct]

“hdr”字段包含关于数据的所有最高级信息,例如原始采样率、通道数量等。字段“label”列出了你决定读入的所有通道的名称。字段“time”,表示数据集的时间轴。“trial”字段包含所有通道的数据,它被称为“试次”,因为通常情况下,数据被分割成与实验中的不同试次相对应的片段。字段“fsample”描述了“trial”字段中数据的采样率。“sampleinfo”字段描述了相对于磁盘上原始测量值的每个试次的样本编号。“opto”包含了有关通道和光电极组成的信息,例如使用了什么波长,光点极位置等等。最后,“cfg”字段与刚才使用的cfg相同,只是通过一些默认值进行了扩展。

为了快速查看数据,可以使用ft_databrowser函数。databrowser不仅仅是一个简单的“数据浏览器”,不过现在将利用这个功能来实现查看数据的目的。在配置中添加'ylim ='maxmin'来调整y轴,使图中的最小值决定y轴上的最低点,图中的最大值决定y轴上的最高点。​​​​​​​

cfg = [];cfg.ylim = 'maxabs';ft_databrowser(cfg, data);

图2.在databrowser中显示原始数据。

使用ft_databrowser,还可以删除不需要的数据片段。例如,如果你在放置光电极的同时开始记录,可能会在记录的开始有一大段不需要分析的数据,这些数据包含与大脑无关的值,并且快速波动。把这些片段剪掉更有利于后续的分析。本示例数据不需要裁剪。

此外,这里先只选择一对通道,以减少分析过程的复杂性。​​​​​​​

cfg = [];cfg.ylim = 'maxmin';cfg.channel = {'Rx4b-Tx5 [860nm]', 'Rx4b-Tx5 [764nm]'}; % you can also use wildcards like 'Rx4b-Tx5*'ft_databrowser(cfg, data);

图3.在databrowser中显示一对通道(两个波长)。

去除伪影

近红外光谱数据不仅包含了氧和脱氧血红蛋白浓度的变化,而且还包含了其他生理信号、随机噪声和来自测量环境的变化。因此,可能存在较为明显的运动伪影,它是由光电二极管和头皮之间接触的临时变化产生的,通常是由头部运动引起。这些运动伪影可能存在于所有通道中,但也可能只有一个或几个通道受到影响。比如,数据中短的、意想不到的峰值被认为由运动导致,可以通过ft_artifact_zvalue检测和删除这些伪影。

指定一些预处理选项来检测伪影,如下所示:​​​​​​​

cfg = [];cfg.artfctdef.zvalue.channel = {'Rx4b-Tx5 [860nm]', 'Rx4b-Tx5 [764nm]'};cfg.artfctdef.zvalue.cutoff = 5;cfg.artfctdef.zvalue.hpfilter = 'yes';cfg.artfctdef.zvalue.hpfreq = 0.1;cfg.artfctdef.zvalue.rectify = 'yes';cfg.artfctdef.zvalue.artpadding = 2;% cfg.artfctdef.zvalue.interactive = 'yes'; % the interactive display makes more sense after segmentating data in trials[cfg, artifact] = ft_artifact_zvalue(cfg, data);

运行上述代码可以发现,FieldTrip在此过程中识别了8个伪影。请注意,只是标识出来了这些伪影,还没有被删除。将其缓存,并在数据滤波分段之后调用ft_rejectartifact。

转化为oxyHB/deoxyHB浓度的变化

此时的数据是光密度(OD)值,而不是含氧和脱氧血红蛋白浓度。可以使用ft_nirs_transform_ODs将数据转换为浓度变化。在使用ft_nirs_transform_ODs时,要做出的一项选择是差分路径长度因子(DPF),它取决于参与者的年龄和组织类型(例如,当分析的是肌肉组织而不是大脑的血氧变化时)。​​​​​​​

cfg = [];cfg.dpf = 5.9;cfg.channel = {'Rx4b-Tx5 [860nm]', 'Rx4b-Tx5 [764nm]'};data_conc = ft_nirs_transform_ODs(cfg, data);

将功能响应与系统反应分开

除了出现在数据中的运动伪影,fNIRS测量还包含系统反应,这是研究中不感兴趣的,并希望从数据中能够过滤掉这些反应。有多种方法可以提取功能响应(大脑的血流动力学响应),这里将采用一种简单但相对有效的方法,即应用带通滤波器来消除频带以外的信号值,该频带捕获了大脑血流动力学响应中的动态。由于实验任务和血流动力学响应函数,这里预计活动低于0.1Hz(对应~10s的波动)。此外,将较低的范围设置为0.01Hz(波动~100s),以消除缓慢漂移。​​​​​​​

cfg = [];cfg.bpfilter = 'yes';cfg.bpfreq = [0.01 0.1];data_filtered = ft_preprocessing(cfg, data_conc);

定义兴趣段

如果想知道大脑是否会对实验中发生的事件做出特定的反应,所以需要把分析的重点放在事件发生的时间窗上(这些时间窗通常被称为epoch或trials)。

此前,通过调用ft_preprocessing时不指定cfg.trl,读取了所有epoch。在帮助文档中,cfg.trl指向ft_definetrial,现在将使用它来定义试次。接下来先看一下函数:

help ft_definetrial

假设我们对数据中的triggers没有任何线索。因此,现在将利用隐藏的功能来检索这些信息。​​​​​​​

cfg = [];cfg.dataset = 'motor_cortex.oxy3';cfg.trialdef.eventtype = '?';

问号表示不确定事件triggers,因此函数ft_trialfun_general将输出在数据集中找到的所有事件。接下来,调用ft_definetrial

ft_definetrial(cfg);

在命令窗口中,将看到发现了24个事件,并且使用了两种类型的事件,即事件'A'和事件'B'。此外,需要注意的是,现在没有定义输出变量'cfg',因此变量'cfg'将保持不变。如果我们想看看从刺激前10s(事件'A'之前)开始到'A'之后35s(刺激后)结束的epoch/trials。可以定义试次,然后使用它来调用ft_preprocessing:​​​​​​​

cfg.trialdef.eventtype = 'event';cfg.trialdef.eventvalue = 'A';cfg.trialdef.prestim = 10;cfg.trialdef.poststim = 35;cfg = ft_definetrial(cfg);cfg.channel = {'Rx4b-Tx5 [860nm]', 'Rx4b-Tx5 [764nm]'};data_epoch = ft_redefinetrial(cfg, data_filtered);

现在已经选择了一对通道,并在12个试次中剪切了数据。使用databrowser进行检查,使用以下这些设置,可以使绘制图形看起来更整洁,同时也可视化之前被识别出的伪影:​​​​​​​

cfg = [];cfg.ylim = [-1 1];cfg.viewmode = 'vertical';cfg.artfctdef.zvalue.artifact = artifact;ft_databrowser(cfg, data_epoch);

图4.Databrowser显示了(12个试次之一的)滤波后的数据。

现在可以删除确定为伪影的试次。​​​​​​​

cfg = [];cfg.artfctdef.zvalue.artifact = artifact;cfg.artfctdef.reject = 'complete';data_epoch = ft_rejectartifact(cfg, data_epoch);

锁时分析

使用ft_timelockanalysis计算试次的平均值。随后,可以使用FieldTrip绘图函数来查看数据,也可以简单地使用MATLAB绘图函数进行可视化。​​​​​​​

cfg = [];data_timelock = ft_timelockanalysis(cfg, data_epoch);

输出是带有以下字段的data_timelock数据结构​​​​​​​

data_timelock =      avg: [2x225 double]      var: [2x225 double]     time: [1x225 double]      dof: [2x225 double]    label: {2x1 cell}   dimord: 'chan_time'     opto: [1x1 struct]      cfg: [1x1 struct]

接下来,绘制A-10s到A+35s的平均O2Hb和HHb轨迹。根据fNIRS惯例,O2Hb为红色,HHb为蓝色。​​​​​​​

time = data_timelock.time;O2Hb = data_timelock.avg(1,:);HHb = data_timelock.avg(2,:);figure;plot(time,O2Hb,'r'); hold on;plot(time,HHb,'b');legend('O2Hb','HHb'); ylabel('\DeltaHb (\muM)'); xlabel('time (s)');

图5.平均O2Hb和HHb轨迹。

参考来源:

Preprocessing and averaging of single-channel NIRS data.

https://www.fieldtriptoolbox.org/tutorial/nirs_singlechannel/

https://www.artinis.com/blogpost-all/2017/6/27/how-do-i-choose-the-correct-fibers-and-template-for-my-oxymon

https://download.fieldtriptoolbox.org/tutorial/nirs_multichannel/

小伙伴们点星标关注茗创科技,将第一时间收到精彩内容推送哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【记录】ORB_SLAM2 例程(README文件里的单双目RDB-D、ROS、AR、外接摄像头、点云)

第一次跑 ORB_SLAM2,记录一下一、普通环境0.build.sh 遇到的问题(1)usleep(2)AlignedBit1.单目、TUM数据集2.双目、KITTI数据集3.RGB-D、TUM数据集二、ROS0.build_ros.sh 遇到的问题(1)rospack …

【C++】结构体应用案例 1

目录 1、缘起 2、案例描述 3、案例分析 4、代码清单 1 5、代码清单 2 6、总结 1、缘起 最近学习完了 C 语言的结构体相关知识点,如 结构体数组,结构体指针,结构体嵌套结构体 和 结构体做函数参数。本篇博客围绕着这些知识点&#xff0c…

【华为机试真题详解JAVA实现】—Sudoku

目录 一、题目描述 二、解题代码 一、题目描述 问题描述:数独(Sudoku)是一款大众喜爱的数字逻辑游戏。玩家需要根据9X9盘面上的已知数字,推算出所有剩余空格的数字,并且满足每一行、每一列、每一个3X3粗线宫内的数字均含1-9,并且不重复。 例如: 输入 输出

SpringCloud学习-实用篇02

以下内容的代码可见:SpringCloud_learn/day02 1.Nacos配置管理 之前提到的Nacos是作为注册中心,除此之外它还有配置管理功能 统一配置管理 假设有多个微服务之间有关联,此时修改了某个微服务的配置后其他相关的微服务也需要重启,十…

Javaweb | 过滤器、配置、过滤器链、优先级

💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! 过滤器 概念 过滤器(Filter)是处于客户端与服务器目标资源之间的一道过滤技术 用户的请求和响应都需要经过过滤器 过滤器作用 执行地位在Servl…

C++初阶 -1- C++入门part2

文章目录6.引用什么是引用?引用的使用引用的应用传值、传引用效率比较权限引用和指针的区别⭐7.内联函数8.auto关键字9.基于范围的for循环10.指针空值——nullptr6.引用 什么是引用? “别名” int a 0; int& b 0;👆即 地址为0x00000…

【linux】:模拟文件基本操作以及文件在磁盘中如何存储的学习

文章目录 前言一、模拟C库文件操作二、磁盘文件总结前言 经过我们上一篇对linux系统文件操作的学习想必我们已经会使用系统文件接口了,今天我们就用系统文件接口来封装一个像C语言库那样的文件操作函数的函数来加深我们对文件操作的学习。 一、模拟C库文件操作 首…

通过Milo实现的OPC UA客户端连接并订阅Prosys OPC UA Simulation Server模拟服务器

背景 前面我们搭建了一个本地的 PLC 仿真环境,并通过 KEPServerEX6 读取 PLC 上的数据,最后还使用 UAExpert 作为 OPC 客户端完成从 KEPServerEX6 这个OPC服务器的数据读取与订阅功能:SpringBoot集成Milo库实现OPC UA客户端:连接…

新一代信息技术赋能,安科瑞搭建智慧水务体系的新思路

随着新时期治水方针的逐步落实,水利现代化、智能化建设已开启,物联网、图像识别、数字孪生等新技术的成熟,也为智慧水务体系的搭建提供了技术保障,新时代治水新思路正逐步得到落实。本文对智慧水务的总体架构与包含的建设内容进行…

Qt第六十二章:图标库QtAwesome的使用

目录 一、安装依赖 二、主页 三、文档 四、案例 1、图标 2、样式 3、alpha 通道 4、 多图标堆叠 5、动画 6、字体 五、系列 1、msc系列 2、fa5系列(选择free栏) 3、fa5s系列(选择free栏) 4、fa5b系列(选…

由libunifex来看Executor的任务构建

前言 之前的一篇文章讲述了future的优缺点,以及future的组合性,其中也讲述了构建任务DAG一些问题,同时给出了比较好的方案则是Executor。 Executor还未进入标准(C23),Executor拥有惰性构建及良好的抽象模型…

尚硅谷大数据技术Zookeeper教程-笔记03【源码解析-算法基础】

视频地址:【尚硅谷】大数据技术之Zookeeper 3.5.7版本教程_哔哩哔哩_bilibili 尚硅谷大数据技术Zookeeper教程-笔记01【Zookeeper(入门、本地安装、集群操作)】尚硅谷大数据技术Zookeeper教程-笔记02【服务器动态上下线监听案例、ZooKeeper分布式锁案例、企业面试真…

多模态大模型的发展、挑战与应用

多模态大模型的发展、挑战与应用 2023/04/15 研究进展 随着 AlexNet [1] 的出现,过去十年里深度学习得到了快速的发展,而卷积神经网络也从 AlexNet 逐步发展到了 VGG [2]、ResNet [3]、DenseNet [4]、HRNet [5] 等更深的网络结构。研究者们发现&#…

用vscode运行Java程序初体验

最近开始学习Java编程了,以前学习过C、C 、Python,主要用微软的visual studio code来运行python程序,于是就尝试了用vscode来运行java代码,记录一下使用的经验,帮助大家少走弯路。 安装了Java的集成编辑器IDE "Ec…

c++STL之关联式容器

目录 set容器 set的默认构造 set的插入与迭代器 set集合的元素排序 set集合的初始化及遍历 从小到大(默认情况下) 从大到小 仿函数 set的查找 pair的使用 multiset容器 map和multimap容器 map的插入与迭代器 map的大小 map的删除 map的查找 关联式容器&#…

【LeetCode: 337. 打家劫舍 III | 暴力递归=>记忆化搜索=>动态规划 | 树形dp】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

整数二分从入门到精通

前言: 开个玩笑,我们写算法可不能这样哈~ 好了,正片开始: 你是否曾经也有过整数二分因为一直死循环而苦恼,你是否因为搞不清楚整数二分的边界处理而焦躁,明明很简单的一道二分,但是最后就是搞…

Python入门教程+项目实战-9.1节: 字符串的定义与编码

目录 9.1.1 理解字符串 9.1.2 字符串的类型名 9.1.3 字符的数字编码 9.1.4 常用的字符编码 9.1.5 字符串的默认编码 9.1.6 字符串的编码与解码 9.1.7 转义字符详解 9.1.8 对字符串进行遍历 9.1.9 知识要点 9.1.10 系统学习python 9.1.1 理解字符串 理解字符串&#…

005:Mapbox GL添加全屏显示功能

第005个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中添加全屏显示功能 。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共60行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https://…

还在因为写项目函数太多而烦恼?C++模板一文带你解决难题

📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段>——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程&…