用ENIGMA-toolbox作图

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 绘制统计结果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot页面国际化

GitHub&#xff1a;SpringBootDemo Gitee&#xff1a;SpringBootDemo 微信公众号&#xff1a; 0 开发环境 JDK&#xff1a;1.8Spring Boot&#xff1a;2.7.18 1 检查配置 确保File -> Settings… -> Editor -> File Encodings 配置中编码为UTF-8&#xff0c;否则…

Python | Leetcode Python题解之第2题两数相加

题目&#xff1a; 题解&#xff1a; # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # self.val val # self.next next class Solution:def addTwoNumbers(self, l1: Optional[ListNode], l2: Optiona…

攻防世界-easyphp

题目信息 题目信息如下&#xff1a; 可以看到&#xff0c;key1和key2的值都为1的时候&#xff0c;才能拿到flag。再网上看&#xff0c;发现key1和key2的值取决于a,b和c的值。 <?php highlight_file(__FILE__); $key1 0; $key2 0;$a $_GET[a]; $b $_GET[b];if(isset($a…

Java编程实战:疫情物资分配系统的设计与实现

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

分治——归并排序算法

例题一 解法&#xff08;归并排序&#xff09;&#xff1a; 算法思路&#xff1a; 归并排序的流程充分的体现了「分⽽治之」的思想&#xff0c;⼤体过程分为两步&#xff1a; ◦ 分&#xff1a;将数组⼀分为⼆为两部分&#xff0c;⼀直分解到数组的⻓度为 1 &#xff0c;使…

vulhub打靶记录——driftingbox

文章目录 主机发现端口扫描目录扫描爆破子域名提权总结 主机发现 使用nmap扫描局域网内存活的主机&#xff0c;命令如下&#xff1a; nmap -sP 192.168.56.0/24192.168.56.1&#xff1a;主机IP&#xff1b;192.168.56.100&#xff1a;DHCP服务器IP&#xff1b;192.168.56.101…

回溯算法|39.组合总和

力扣题目链接 class Solution { private:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {if (sum > target) {return;}if (sum target) {result.push_back…

IDEA 详细设置

详细设置 如何打开详细配置界面 1、显示工具栏 2、选择详细配置菜单或按钮 系统设置 默认启动项目配置 启动IDEA时&#xff0c;默认自动打开上次开发的项目&#xff1f;还是自己选择&#xff1f; 如果去掉Reopen projects on startup前面的对勾&#xff0c;每次启动IDEA就会…

【软件安装教程】IDEA

【软件安装教程】IDEA 系统: Windows11 64位版本: ideaIC-2023.3.6官方地址: Jetbrains网盘地址: 百度网盘 下载 处于成本考虑就直接用社区版了&#xff0c;如果专业版配置过程都一样 安装 双击下载的文件 根据电脑存储选择一个合适的地址后点击下一步 选择配置&#…

鸿蒙OS(ArkTS) 案例:【使用http网络请求框架加载验证码】

需求&#xff1a;加载验证码&#xff1b;1.下载验证码图像文件&#xff1b;2.获取header里面验证码ID 踩坑--踩坑--踩坑 根据文档使用 request.downloadFile 请求&#xff0c;官方示例: // pages/xxx.ets // 将网络资源文件下载到应用文件目录并读取一段内容 import common …

Mac使用“Workstation”安装双系统

## 选择虚拟机 Mac推荐使用“VMware” 优点 1.个人版是免费的 2.界面清晰&#xff0c;运行流程 3.使用人群广&#xff0c;遇到问题容易解决 版本比较 VMware Workstation Pro 和 VMware Workstation Player 个人使用推荐 VMware Workstation Player &#xff0c;因为个人的…

二十四种设计模式与六大设计原则(四):【状态模式、原型模式、中介者模式、解释器模式、享元模式、备忘录模式】的定义、举例说明、核心思想、适用场景和优缺点

接上次博客&#xff1a;二十四种设计模式与六大设计原则&#xff08;三&#xff09;&#xff1a;【装饰模式、迭代器模式、组合模式、观察者模式、责任链模式、访问者模式】的定义、举例说明、核心思想、适用场景和优缺点-CSDN博客 目录 状态模式【State Pattern】 定义 举…

Python之numpy:常用运算广播机制

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、numpy运算二、常见运算1.ufunc函数2.复合赋值运算符3.判断符 二、聚合函数三、唯一化、集合四、numpy广播机制 一、numpy运算 numpy有两种基本对象&#xff1a…

【linux】AMD GPU和NVIDIA GPU驱动安装

AMD GPUs - Radeon™ PRO W7900的驱动安装过程 要在Linux系统上安装AMD的Radeon™ PRO W7900显卡驱动程序&#xff0c;通常需要执行以下步骤。以下示例基于Ubuntu系统&#xff1b;其他Linux发行版的具体步骤可能有所不同。 1. 更新系统 打开一个终端窗口&#xff0c;并输入…

redis学习-主从复制和哨兵模式

目录 1. 主从复制&#xff0c;读写分离 1.1 介绍 1.2 使用命令介绍 1.3 实现 1.4全量复制和增量复制 2.哨兵模式 1. 主从复制&#xff0c;读写分离 1.1 介绍 指的是将一台redis服务器中的数据复制到其他redis服务器&#xff0c;前者称为主机&#xff0c;后者称为从机&#xf…

C++刷题篇——04找等值元素

一、题目 二、解题思路 1、分割后放进二维数组 2、使用map&#xff0c;key为数值&#xff0c;value为其坐标 3、遍历二维数组元素&#xff0c;再在map中找该元素对应的value值&#xff08;二维数组形式&#xff09;&#xff0c;倘若value.size为1&#xff0c;那直接返回-1&…

嵌入式第一部分-第一集:ARM那些你得知道的事

ARM&#xff1a;Advanced RISC Machine&#xff0c;先进精简指令集机器 ARM公司只做设计&#xff0c;不生产。 国内IC生产厂商&#xff1a;华为海思、全志、瑞芯微、MTK&#xff08;联发科&#xff09; 扩展&#xff1a;ARM的商业模式了解。 使用三星S5PV210开发板进行视频的讲…

改进的图像LSB加密算法:Matrix encoding embedding

参考文献1 Visually secure image encryption using adaptive-thresholding sparsification and parallel compressive sensing 算法实现 简单说明 算法步骤概述 定义函数f:这个函数用于计算给定码字b的一个特定值,此值将与秘密信息x进行比较。这个计算涉及到将码字b的每一…

基于springboot+vue+Mysql的新生宿舍管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Redis命令-Set命令

基础篇Redis 4.7 Redis命令-Set命令 Redis的Set结构与Java中的HashSet类似&#xff0c;可以看做是一个value为null的HashMap。因为也是一个hash表&#xff0c;因此具备与HashSet类似的特征&#xff1a; 无序元素不可重复查找快支持交集.并集.差集等功能 Set类型的常见命令 …