用ENIGMA-toolbox作图

news2024/11/24 3:39:15

89c088b2c3071ac5034c83178cff9caa.jpeg

之前一直使用ggseg呈现结果,最近想试一试其他绘图工具。ENIGMA-toolbox有所了解,绘图功能看起来门槛不高,所以就试着用它呈现一些结果。Matlab版本的ENIGMA-toolbox直接使用就是SurfStat的功能绘图,Python版本的绘图功能应该是根据SurfStat的绘图思路进行改写的。

  1. 下载和安装

参照官网不再赘述。需要注意的是该工具包依赖的numpy和pandas版本较老,若不想降级numpy和pandas的话,可以到工具包安装位置,使用VScode将工具包中以下内容进行修改。

  • numpy已经没有.float和.int的属性了,因此np.float 应该改成float, np.int改成int

  • pandas新版本不再使用error_bad_lines作为输入,因此需要将error_bad_lines=False改为on_bad_lines='skip'

当然另外一种办法就是conda建立虚拟环境,按要求安装依赖也是可行的。

2. 准备Desikan结果-->载入python-->根据标签匹配顺序-->绘图

绘制统计结果,只需要将其按照工具包所需的格式准备即可,具体而言就是脑区的顺序需要和ENIGMA-Toolbox保持一致。这个工具并不像ggseg一样是根据脑区的标签进行数据匹配的,因此使用该工具时需要确保标签和数据的顺序与工具包默认的顺序一致 x 3!

Desikan 68个区域(第一列结束后紧接第二列)

a220d2ef18be5dd312e5d1bbe2ef7032.png

皮层下区域(第一列结束后紧接第二列)

655add4b1c3f37e3b56ff736b3bbb26e.png

快速找到这些区域顺序的方式就是载入ENIGMA-Toolbox自带的一个结果文件

from enigmatoolbox.datasets import load_summary_stats
sum_stats = load_summary_stats('22q')
CT_vector = sum_stats['CortThick_case_vs_controls']['Structure']
SV_vector = sum_stats['SubVol_case_vs_controls']['Structure']

把它做成一个dataFrame用于匹配顺序

CT_order_df = pd.DataFrame(CT_vector.tolist(), columns=['Dependent Variable'])
SV_order_df = pd.DataFrame(SV_vector.tolist(), columns=['Dependent Variable'])

使用Merge的方式将载入的数据集,根据标签的顺序进行匹配。当然这不是必须的,如果将结果载入之前已经确保顺序没问题,就可以直接用数据作图。

CT_df_reordered = pd.merge(CT_order_df, CT_df, on='Dependent Variable', how='left')
SV_df_reordered = pd.merge(SV_order_df, SV_df, on='Dependent Variable', how='left')

获取需要呈现的统计值(这里是Cohen's d),然后使用parcel_to_surface函数将各个区域的数据映射到surface的vertex上面,就可以绘图了。

from enigmatoolbox.utils.parcellation import parcel_to_surface
from enigmatoolbox.plotting import plot_cortical, plot_subcortical
CT_d = CT_df_reordered['Cohen\'s d']
CT_d_fsa5 = parcel_to_surface(CT_d, 'aparc_fsa5')
# Project the results on the surface brain
plot_cortical(array_name=CT_d_fsa5,
                 surface_name="fsa5",
                 size=(1200, 600),
                 cmap='RdBu_r',
                 color_bar=False,
                 color_range=(-1.8, 1.8),
                 screenshot=True,
                 filename=os.path.join(plot_path,'CT_d_all.png'),
                 background=(1, 1, 1),
                 transparent_bg=False,
                 scale= (3,3))

4a55b3aa020d5737b63779183d0bb650.png

对于皮层下区域,由于区域比较少,不需要单独做parcel_to_surface,直接绘图即可。

SV_d = SV_df_reordered['Cohen\'s d']
plot_subcortical(array_name=SV_d, 
                 size=(1200, 600),
                 cmap='RdBu_r', 
                 color_bar=False, 
                 color_range=(-1, 1),
                 screenshot=True,
                 filename=os.path.join(plot_path,'SV_d_all.png'),
                 background=(1, 1, 1),
                 transparent_bg=False,
                 scale= (1,1))

869552fd62b9a191964862a7ba94165b.png

parcellated<->vertexwise的转换比想象中的简单,surface_to_parcel或者parcel_to_surface函数使用了一个vertex到ROI的映射表进行相互转换。

这是该工具包提供的映射表,有desikan, glasser360, schaefer100, 200,300,400, economo_koskinas,都是可以用该方式绘制和保存的,没有的模板也可以自己制作映射表绘制。

495870000b494acd918316d878c9e556.png

另外可以试用matlab的这个函数查看annot文件相关信息,判断映射表中区域的信息是否准确。

read_annotation('fsa5_lh_aparc.annot')

3. 保存

无论是plot_cortical还是plot_subcortical都默认使用了VTK生成图形,它们可以缩放和旋转,要保存图片需要将画图函数中的screenshot设置为True,并设置filename为保存文件的位置和名称。此外,由于后续有拼图的需要,建议将colorbar关闭,拼图完成后手动添加一个colorbar。

4. 拼图

Python拼图的工具好像不多(?!),此外plot_cortical和plot_subcortical函数都是基于VTK作图,保存是截图的方式,并不返回plt内容,大部分的拼图工具可能也不好使。所以建立一个简单工作流完成拼图:

  1. 读取图片

  2. subplot展示图片

  3. 裁剪(删除全白色的行和列)

    裁剪时如果想留白某一位置,可以将该位置加入非白元素。

  4. 保存

将上述步骤封装为函数方便多次调用。

def merge_images(images_dict, output_file, trim_edges=False):
    """
    Merges a list of images into a single figure and saves it, based on provided image information.
    
    Parameters:
    - images_dict: List of dictionaries, each containing 'file_path' and 'title' for each image.
    - output_file: Path where the merged image should be saved.
    - trim_edges: Whether to trim white edges from the merged image before saving.


    # Example usage:
        images_dict = [
            {'file_path': os.path.join(plot_path, 'CT_d_all.png'), 'title': 'Cortical'},
            {'file_path': os.path.join(plot_path, 'SV_d_all.png'), 'title': 'Subcortical'}]
        output_file = os.path.join(plot_path, 'CT_SV_d_all.png')
        merge_images(images_dict, output_file, trim_edges=True)
    """
    n_images = len(images_dict)
    fig, axs = plt.subplots(n_images, 1, figsize=(12, 7 * n_images))
    
    for i, img_info in enumerate(images_dict):
        img = mpimg.imread(img_info['file_path'])
        axs[i].imshow(img)
        axs[i].axis('off')
        title_text = img_info.get('title', '')
        axs[i].set_title(title_text, y=0.7, fontdict={'family': 'sans-serif', 'fontsize': 18},)
        # Adding a subtle marker (barely visible)
        axs[i].text(0.5, 0.65, '|', fontsize=12, color=(0.995, 0.995, 0.995), ha='center', transform=axs[i].transAxes)
        axs[i].text(0.5, 0.75, '|', fontsize=22, color=(0.995, 0.995, 0.995), ha='center', transform=axs[i].transAxes)


    plt.tight_layout()
    plt.savefig(output_file, bbox_inches='tight',dpi=300)
    
    if trim_edges:
        # Load the saved image, trim edges, and save again
        merged_img = mpimg.imread(output_file)
        trimmed_img = trim_white_edges(merged_img)
        plt.imsave(output_file, np.ascontiguousarray(trimmed_img),dpi=300)
 
def trim_white_edges(img_array):
    """
    Trim white edges from an image with pixel values normalized to the 0 to 1 range.
    
    Parameters:
    - img_array: Image array with pixel values in the range [0, 1].
    
    Returns:
    - Cropped image array with white edges removed.
    """
    # Detect pixels that are not completely white (assuming white is [1, 1, 1])
    mask = np.any(img_array < 1, axis=-1)
    
    # Find the rows and columns that contain non-white pixels
    rows = np.any(mask, axis=1)
    cols = np.any(mask, axis=0)
    
    # Crop the image to these rows and columns
    cropped_img = img_array[rows][:, cols]
    
    return cropped_img

调用函数即可拼图

images_dict = [
    {'file_path': os.path.join(plot_path, 'CT_d_all.png'), 'title': 'Cortical'},
    {'file_path': os.path.join(plot_path, 'SV_d_all.png'), 'title': 'Subcortical'}
]
output_file = os.path.join(plot_path, 'CT_SV_d_all.png')
merge_images(images_dict, output_file, trim_edges=True)

图中在subplot标题下加入了非白元素(肉眼难以识别),从而增加title和图片的距离;另外可以看到由于subplot标题的存在,左右半球之间的空白区域并未被移除,正好使得布局看起来更美观。美中不足的是皮层下区域看起来略大,所以在手动加colorbar的时候可以调一下大小。

43c930bdaf4782ff6d226682bd71dae1.png

该函数可以一次拼接多个png图片,比如以下病例-对照结果:

images_dict = [
    {'file_path': os.path.join(plot_path, 'CT_d_{}.png'.format(condition_name)), 'title': condition_name} for condition_name, _ in conditions]
output_file = os.path.join(plot_path, 'all_CT_d_maps.png')
merge_images(images_dict, output_file, trim_edges=True)

f7c78bf57f5c35b8d55e7a4f10a49a1f.png

往期推荐

ENIGMA-Toolbox

R|ggsegExtra绘制Desterieux统计结果

R| ggseg 绘制统计结果

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

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

相关文章

详解k8s集群内外的访问方式

文章目录 1、集群内访问2、集群外访问2.1、Ingress转发外网请求2.2、LoadBanlancer接入外网请求2.3、NodePort接入外网请求 3、总结和对比3.1、Ingress、NodePort和LoadBalancer总结3.2、Ingress和网关的区别 1、集群内访问 在k8s中创建的微服务&#xff0c;大部分都是在集群内…

手搓 Docker Image Creator(DIC)工具(02):预备知识

此节主要简单介绍一下 Docker、Dockerfile 的基本概念&#xff0c;Dockerfile 对的基本语法&#xff0c;Windows 和 macOS 下 Docker 桌面的安装&#xff0c;Docker 镜像的创建和运行测试等。 1 关于 Docker Docker 是一个开源的应用容器引擎&#xff0c;它允许开发者打包应用…

非关系型数据库-----------Redis的主从复制、哨兵模式

目录 一、redis群集有三种模式 1.1主从复制、哨兵、集群的区别 1.1.1主从复制 1.1.2哨兵 1.1.3集群 二、主从复制 2.1主从复制概述 2.2主从复制的作用 ①数据冗余 ②故障恢复 ③负载均衡 ④高可用基石 2.3主从复制流程 2.4搭建redis主从复制 2.4.1环境准备 2.4…

hadoop在linux上启动成功了,但是浏览器访问不了

根据网上的资料进行安装hadoop的伪集群 都安装成功&#xff0c;并且启动也成功了&#xff0c;如下图所示&#xff1a; 2、但是在浏览器上确是怎么也访问不了&#xff0c; 解决思路&#xff0c; 2.1、根据网上的一些文章处理解决是关闭防火墙&#xff0c; 2.1.1、我根据操作步骤…

unity工程输出的log在哪里?

在编辑器里进行活动输出的log位置&#xff1a; C:\Users\username\AppData\Local\Unity\Editor\Editor.log ------------------------------------ 已经打包完成&#xff0c;形成的exe运行后的log位置&#xff1a; C:\Users\xxx用户\AppData\LocalLow\xx公司\xx项目

manga-ocr漫画日文ocr

github 下载 解压 anaconda新建环境 conda create -n manga_ocr python3.8 激活环境 conda activate manga_ocr cd到解压目录 cd /d manga-ocr-master 安装依赖包 pip install -r requirements.txt pip3 install manga-ocr 下载离线model huggingface 123云盘 解压到一个目录…

Python爬虫详解:原理、常用库与实战案例

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言引言&#xff1a;一、爬虫原理1. HTTP请求与响应过程2. 常用爬虫技术 二、P…

回溯算法 DFS

目录 回溯算法和dfs的区别回溯算法基本框架例题&#xff1a;【1,2&#xff0c;3】的全排列代码详解完整代码 DFS 本文思路、代码均参考于&#xff1a;https://labuladong.online/algo/essential-technique/backtrack-framework-2/#%E4%B8%80%E3%80%81%E5%85%A8%E6%8E%92%E5%88%…

搭建 Qt 开发环境

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、QT SDK 的下载和安装 1.QT SDK 的下载 二、QT SDK的安装 1、找到下载的文件并双击 2、双击之…

【项目实战经验】DataKit迁移MySQL到openGauss(上)

前言 本文将分享DataKit迁移MySQL到openGauss的项目实战&#xff0c;供广大openGauss爱好者参考。 1. 下载操作系统 https://www.openeuler.org/zh/download https://support.huawei.com/enterprise/zh/doc/EDOC1100332931/1a643956 https://support.huawei.com/enterprise…

VMware虚拟机三种网络模式配置

vmware有三种网络工作模式&#xff1a;Bridged&#xff08;桥接模式&#xff09;、NAT&#xff08;网络地址转换模式&#xff09;、Host-Only&#xff08;仅主机模式&#xff09;。 1. 打开网络编辑器&#xff08;编辑 --> 虚拟网络编辑器&#xff09; 在主机上有VMware Ne…

LeetCode-19. 删除链表的倒数第 N 个结点【链表 双指针】

LeetCode-19. 删除链表的倒数第 N 个结点【链表 双指针】 题目描述&#xff1a;解题思路一&#xff1a;双指针解题思路二&#xff1a;优化解题思路三&#xff1a;0 题目描述&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。…

hexo博客7:构建简单的多层安全防御体系

【hexo博客7】构建简单的多层安全防御体系 写在最前面理解全面安全策略的重要性防御常见的网络攻击1. SQL注入攻击2. 文件上传漏洞3. 跨站脚本攻击&#xff08;XSS&#xff09;4. 跨站请求伪造&#xff08;CSRF&#xff09;5. 目录遍历/本地文件包含&#xff08;LFI/RFI&#x…

CRMEB 标准版 v5.3公测版发布,快来体验

演示站 后台&#xff1a;http://v5.crmeb.net/admin 账号&#xff1a;demo 密码&#xff1a;crmeb.com H5端&#xff1a;http://v5.crmeb.net 新增功能 后台支持所有功能设置搜索 事业部&#xff1a;想在事业部添加代理商&#xff0c;可以在后台选择添加员工&#xff0c;设…

【c++】STl-list使用list模拟实现

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;c_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1. list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 …

算法学习 | day34/60 不同路径/不同路径II

一、题目打卡 1.1 不同路径 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 拿到手&#xff0c;首先见到答案需要求的是种类的个数&#xff0c;并且看题目&#xff0c;每次移动的时候只有两个方向&#xff0c;这也就说明&#xff0c;对于某一个位置来说&#x…

[已解决]Vue3+Element-plus使用el-dialog对话框无法显示

文章目录 问题发现原因分析解决方法 问题发现 点击按钮&#xff0c;没有想要的弹框 代码如下 修改 el-dialog到body中&#xff0c;还是不能显示 原因分析 使用devtool中vue工具进行查看组件结构 原因在于&#xff0c;在一个局部组件(Detail->ElTabPane->…)中使用…

C++初阶:list类及模拟实现

list的介绍及使用 list的介绍 list 1. list 是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list 的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向…

代码随想录算法训练营第二十二天| 235.二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点

系列文章目录 目录 系列文章目录235. 二叉搜索树的最近公共祖先①递归法自己写的简洁版 ②迭代法不能这样写&#xff01;正确写法 701.二叉搜索树中的插入操作①递归法②迭代法 450.删除二叉搜索树中的节点递归法 235. 二叉搜索树的最近公共祖先 ①递归法 自己写的 class So…

【C+ +】第一个C+ + 项目的创建及namespace命名空间解释C++中的输入输出

目录 1.创建第一个c项目 1.1项目创建 1.2 .cpp源文件建立 1.3 第一个c程序hello world对比c语言hello world 2.命名空间 2.1 C关键字 2.2 命名空间---解决c语言中的命名冲突 2.2.1 namespace命名空间用法 2.2.2 &#xff1a;&#xff1a; 预作用限定符 2.2.3 命名空间的嵌套…