【医学影像数据处理】nii 数据格式文件操作汇总

大部分医学领域数据存储的都是dicom格式,但是对于CT等一类的序号图像,就需要多个dicom文件独立存储,最终构成一个序列series,这样存储就太过于复杂了。

nifti(Neuroimaging Informatics Technology Initiative,神经影像信息技术倡议)格式,是一种用于神经影像学数据存储和交换的标准格式。它的设计旨在简化神经影像学数据的处理、分析和共享,并且广泛应用于医学影像、脑成像和神经科学研究领域。

NIfTI数据格式基于扩展名为“.nii”“.nii.gz”的文件。可以直接在itk-snap软件中打开,直接拖动就可以了。这块我也在我博客的其他文章进行了展示,感兴趣的可以直接去我的主页查看。

这里我们就简单介绍下,nii格式文件的读取和保存,目前发现有很多方法,反倒是有些乱了。整理汇总下,方便有这部分需求的伙伴。

注意:如果图像是用作AI训练,普通的png图像和原始的nii存储的3维转2维数据都是可以使用的。一般都会在前处理阶段对数据做归一化操作,所以,我们验证的结果来看,影响不大。

一、文件读取与存储

1.1、读取nii

1.1.1、nibabel库读取

NiBabel提供对一些常见医学和神经影像文件格式的读/写访问,包括ANALYZE(plain,SPM99,SPM2及更高版本),GIFTI,NIfTI1,NIfTI2,CIFTI-2,MINC1,MINC2,AFNI BRIK/HEAD,MGH和ECAT以及Philips PAR/REC。该库可以完全或选择性地访问各种图像格式的元数据,可以通过 NumPy 数组访问图像数据,对DICOM 的支持非常有限,也是PyNIfTI第三方库的继任者。

nibabel图像由三个方面组成

  1. hdr: 描述图像的图像元数据(关于数据的数据),通常以图像头部的形式, header;
  2. ext: 自己可以随意定义数据的部分。dicom2nii后的文件,存储的是一个告知图像数组数据在引用空间中的位置的仿射数组, affine;
  3. img: 存储 3D 或 4D图像数据数组, get_fdata

用下面代码,将nii的三个部分打印出来查看,如下:

import nibabel as nibimg = nib.load(r"./data/DET0000101.nii.gz")# Convert them to numpy format,
data = img.get_fdata()
affine = img.affine
head = img.headerprint("数组大小为\n{}".format(data.shape))
print("仿射变换矩阵为\n{}".format(affine))
print("数组头为\n{}".format(head))

打印内容如下:

数组大小为
(128, 128, 96)
仿射变换矩阵为
[[-1.  0.  0. -0.][ 0. -1.  0. -0.][ 0.  0.  1.  0.][ 0.  0.  0.  1.]]
数组头为
<class 'nibabel.nifti1.Nifti1Header'> object, endian='<'
sizeof_hdr      : 348
data_type       : b''
db_name         : b''
extents         : 0
session_error   : 0
regular         : b'r'
dim_info        : 0
dim             : [  3 128 128  96   1   1   1   1]
intent_p1       : 0.0
intent_p2       : 0.0
intent_p3       : 0.0
intent_code     : none
datatype        : int32
bitpix          : 32
slice_start     : 0
pixdim          : [1. 1. 1. 1. 0. 0. 0. 0.]
vox_offset      : 0.0
scl_slope       : nan
scl_inter       : nan
slice_end       : 0
slice_code      : unknown
xyzt_units      : 2
cal_max         : 0.0
cal_min         : 0.0
slice_duration  : 0.0
toffset         : 0.0
glmax           : 0
glmin           : 0
descrip         : b''
aux_file        : b''
qform_code      : scanner
sform_code      : scanner
quatern_b       : 0.0
quatern_c       : 0.0
quatern_d       : 1.0
qoffset_x       : -0.0
qoffset_y       : -0.0
qoffset_z       : 0.0
srow_x          : [-1.  0.  0. -0.]
srow_y          : [ 0. -1.  0. -0.]
srow_z          : [0. 0. 1. 0.]
intent_name     : b''
magic           : b'n+1'

更多了解的,可以查询下Analyze格式和NIFTI格式,以及dicom数据和nii格式定义的方向,是不同的。这部分感兴趣的可以自己查询,可能在你的项目中会遇到这个情况,造成困惑。

1.1.2、itk库读取

ITK(The Insight Toolkit)是一个开源的跨平台图像处理库,提供了强大了图像处理和分析能力。其主要用于医学图像处理和计算机辅助诊断领域。但是也可以应用于洽谈领域的图像处理任务。

ITK是由美国国家生物医学图像库和美国国家癌症研究所公共开发的开源软件。采用C++编写,提供python接口,具有丰富的图像处理算法和工具,包括图像滤波,分割,配准和重建。

(原本以为只能打开nii的文件呢,原来他的功能这么强大,NB了)

读取nii文件:

import itknii_path = os.path.join(dir, 'sample.nii')
imgs = itk.array_from_image(itk.imread(nii_path))

实现中值滤波:

image = itk.imread(input_filename)
median = itk.MedianImageFilter.New(image, Radius = 2)
itk.imwrite(median, output_filename)
  • 官方文档地址:itk documentation
  • 更多关于itk库的图像操作,可以去官网学习,点击直达:Quick_start_guide

1.1.3、SimpleITK库读取

SimpleITK 是专门处理医学影像的软件,是 ITK 的简化接口,使用起来非常便捷。SimpleITK 支持 8 种编程语言,包括c++、Python、R、Java、c#、Lua、Ruby 和 TCL

读取nii文件:

nii_path = os.path.join(path, filename)
image = sitk.ReadImage(nii_path)
print('image size:', image.GetSize())

更多关于simple itk库的图像操作,可以去官网学习,点击直达:Quick_start_guide

1.2、存储 nii

存储nii文件就比较的直接,将数组直接存储为nii格式的文件,或者nii.gz的文件。但是呢,如果header和ext的信息,记得一并写入进去,否则在一些数据处理的情况下,会存在不一致的问题。

1.2.1、nibabel 库存储

# 创建一个新的Nifti对象,使用之前的header和修改后的数组
new_image_obj = nib.Nifti1Image(image, image_obj.affine, header=image_obj.header)
# 保存为nii.gz文件
nib.save(new_image_obj, os.path.join(save_dir, filename))

1.2.2、itk 库存储

ct = itk.image_from_array(array)
itk.imwrite(ct, os.path.join(save_path, e+'.nii'))  # 保存nii文件

1.2.3、SimpleITK 库存储

sitk.WriteImage(image_arr, os.path.join(save_dir, filename))

1.2.4、保存nii文件过程中,header问题

上述这些方法都是可以对数组,存储为niinii.gz文件,但是,对于nii文件内的header,可能会被修改,与之前的header信息不一致。这部分信息是非常重要的,所以,希望希望对数组修改后存储的文件,能够沿用之前的header

这是原始的volumenii文件内,header记录的信息,如下:

sizeof_hdr 348
data_type b''
db_name b''
extents 0
session_error 0
regular b'r'
dim_info 0
dim [  3 512 512 333   1   1   1   1]
intent_p1 0.0
intent_p2 0.0
intent_p3 0.0
intent_code 0
datatype 8
bitpix 32
slice_start 0
pixdim [1.       0.826172 0.826172 1.25     0.       0.       0.       0.      ]
vox_offset 0.0
scl_slope nan
scl_inter nan
slice_end 0
slice_code 0
xyzt_units 2
cal_max 0.0
cal_min 0.0
slice_duration 0.0
toffset 0.0
glmax 0
glmin 0
descrip b''
aux_file b''
qform_code 1
sform_code 0
quatern_b 0.0
quatern_c 0.0
quatern_d 1.0
qoffset_x 204.014
qoffset_y 211.5
qoffset_z -431.974
srow_x [0. 0. 0. 0.]
srow_y [0. 0. 0. 0.]
srow_z [0. 0. 0. 0.]
intent_name b''
magic b'n+1'

如果采用nibabel进行保存,则需要改写为下面这种形式,如下:

nii_path = os.path.join(data_dir, filename)
image_obj = nib.load(nii_path)
image = image_obj.get_fdata()
header = image_obj.header
# print(np.max(image))
# print(image.shape)
# print(header, type(header))
dim = header['dim']pixdim = header['pixdim']shape = dim * pixdim
shape_z = shape[3]if shape_z <200:new_z = round(320.0/shape_z)print(filename, shape_z, new_z)print(dim)pixdim[3] = new_zprint(pixdim)n+=1image_obj.header['pixdim'] = pixdim# 创建一个新的Nifti对象,使用之前的header和修改后的数组new_image_obj = nib.Nifti1Image(image, image_obj.affine, header=image_obj.header)# 保存为nii.gz文件nib.save(new_image_obj, os.path.join(save_dir, filename))

1.3、小汇总

import itk
import SimpleITK as sitk
import nibabel as nibdef read_nii_itk(nii_path):image = itk.array_from_image(itk.imread(nii_path))return imagedef read_nii_nib(nii_path):I = nib.load(nii_path)I_affine = I.affineI = I.get_fdata()return I, I_affinedef save_nii_itk(array_data, save_path):image_data = itk.image_from_array(array_data)itk.imwrite(image_data, save_path)  # 保存nii文件print('save end')def save_nii_nib(array_data, I_affine, save_path):nib.Nifti1Image(array_data, I_affine).to_filename(save_path)print('save ends')

二、SimpleITK 和 itk 库数组的差异

在日常操作中,发现上述两个库都是可以对nii文件进行操作的,但是两个库读取后的数组,存在差异,具体我们可以通过下面的介绍,进行理解。

先对两个库读取同一个nii文件如下:

import os
import SimpleITK as sitk
import itknii_path = os.path.join(r'./images', 'brain.nii.gz')image = sitk.ReadImage(nii_path)
print('image size:', image.GetSize())   # depth、height、widthimgs = itk.array_from_image(itk.imread(nii_path))
print('img shape:', imgs.shape) # width、height、depth

打印结果:

image size: (224, 256, 192)
img shape: (192, 256, 224)

为什么两个库读取的结果,会有差异呢?

  1. nii文件中,像素的排列顺序是从头到尾按顺序排列的,即最外层是z轴方向,然后是y轴方向,最内层是x轴方向。
  2. 但是在读取nii文件时,有些库会将这个排列顺序调整为从内到外的顺序,即最内层是z轴方向,然后是y轴方向,最外层是x轴方向。
  3. 这是因为不同的库对于数组的存储方式不同,一些库使用C语言的方式进行存储,而C语言中数组的存储方式是从内到外的。

itkSimpleITK读取时候存在差异吗?

  • itk中,采用的是C语言的方式进行存储,因此读取出来的数组顺序是从外到内的。
  • 而在SimpleITK中,采用的是C++的方式进行存储,因此读取出来的数组顺序是从内到外的。

这个顺序的不同可能会导致一些问题,因此在使用不同的库读取nii文件时,需要注意数据的存储顺序。

三、nii 文件软件查看

nii文件,可以采用itk-snap直接打开查看。如下展示的就是原始图像的nii文件,和标注后的mask文件,同时在itk-snap打开后的样子。

在这里插入图片描述

四、总结

nii文件格式,在对医疗数据处理的过程中,无处不在,是一个最最经常会遇到的。了解和操作nii 文件,对其中可能遇到的问题有一些基础的了解,至关重要。但是,现在我更喜欢nrrd文件了,不知道你是否知道nrrd文件。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

专有钉钉微应用埋点以及本地调试埋点总结

最近在对接浙政钉&#xff0c;稳定性监控、通用采集 SDK、基础埋点、基础埋点&#xff0c;每次发布上去&#xff0c;工作人员那边反馈抓取不到信息 稳定性监控代码、通用采集 SDK index.html <!-- 流量稳定监控 S 关于埋点上线打开--><script src"https://wpk-…

通讯录的实现(顺序表应用)

上一篇文章我们讲过顺序表的实现&#xff0c;通讯录与顺序表唯一的区别就是修改了变量&#xff0c;将原来的int类型数组修改为结构体数组&#xff0c;再加写扫雷时用到的方法&#xff0c;我们就可以创建这样一个通讯录。 我们还是按照函数声明&#xff0c;函数实现与编写本体的…

基于隐私保护的可追踪可撤销密文策略属性加密方案论文阅读

论文是2022年发表的A Traceable and Revocable Ciphertext-Policy Attribute-based Encryption Scheme Based on Privacy Protection 摘要 本篇论文提出了一种具有用户撤销、白盒追踪、策略策略隐藏功能的CP-ABE方案。在该方案中密文被分为两个部分&#xff1a;第一个部分是和…

Whisper对于中文语音识别与转写中文文本优化的实践(Python3.10)

原文&#xff1a;Whisper对于中文语音识别与转写中文文本优化的实践(Python3.10) - 知乎 阿里的FunAsr对Whisper中文领域的转写能力造成了一定的挑战&#xff0c;但实际上&#xff0c;Whisper的使用者完全可以针对中文的语音做一些优化的措施&#xff0c;换句话说&#xff0c;…

股票价格预测 | Python使用BP神经网络和LSTM神经网络预测股票价格

文章目录 效果一览文章概述代码设计BP神经网络LSTM神经网络效果一览 文章概述 BP神经网络使用

算法沉淀——动态规划篇(子数组系列问题(下))

算法沉淀——动态规划篇&#xff08;子数组系列问题&#xff08;下&#xff09;&#xff09; 前言一、等差数列划分二、最长湍流子数组三、单词拆分四、环绕字符串中唯一的子字符串 前言 几乎所有的动态规划问题大致可分为以下5个步骤&#xff0c;后续所有问题分析都将基于此 …

Jenkins首次安装选择推荐插件时出现”No such plugin cloudbees-folder”解决方案

安装Jenkins成功之后&#xff0c;首次启动Jenkins后台管理&#xff0c;进入到安装插件的步骤&#xff0c;选择"推荐安装"&#xff0c;继续下一步的时候出现错误提示&#xff1a; 出现一个错误 安装过程中出现一个错误&#xff1a;No such plugin&#xff1a;cloudb…

Linux的开发工具(二):编译器gcc/g++与Linux项目自动化构建工具-Makefile

目录 Linux的编译器-gcc/g 问题一&#xff1a;gcc有时候为什么不能编译带有for循环的c语言源文件&#xff1f; 问题二&#xff1a;gcc中c源文件的后缀是什么&#xff1f; 问题三&#xff1a;gcc能编译c的源文件文件吗&#xff1f; 问题四&#xff1a;如何查看自己是否拥有…

jmeter链路压测

比如登录后返回token&#xff0c;业务打印上传的操作需要用到token 线程组中添加登录请求&#xff0c;并执行 1、添加登录并执行&#xff0c;查看结果 2、结果树中下拉选择正则表达式&#xff0c;将token参数和值复制粘贴到下方&#xff0c;将token值改为(.*?)&#xff0…

关于第十二届蓝桥杯时间显示题中包和模块的使用解释

题目信息&#xff1a; 解题代码&#xff1a; from datetime import datetime, timedelta # 定义起始时间&#xff0c;即 Unix 时间戳的零点&#xff08;1970年1月1日&#xff09; start datetime(year1970, month1, day1) # 定义时间间隔&#xff0c;这里以毫秒为单位 dela …

js类型转换

类型转换只有这四种&#xff0c;例如如果要对象转数字&#xff0c;那么就需要先把对象转成原始类型&#xff0c;再从原始类型转到数字。 空数组转原始类型是一个空字符串。空对象转原始类型是[object Object]。 let a {} console.log(a);// NaN //等价于 a->原始 然后原始…

适用于 Linux 的 Windows 子系统安装初体验

1、简述 Windows Subsystem for Linux (WSL) 是 Windows 的一项功能&#xff0c;允许您在 Windows 计算机上运行 Linux 环境&#xff0c;而无需单独的虚拟机或双重启动。 WSL 旨在为想要同时使用 Windows 和 Linux 的开发人员提供无缝且高效的体验。 使用 WSL 安装和运行各种 L…

InternLM2-lesson2作业

书生浦语大模型趣味 Demo 视频连接&#xff1a;https://www.bilibili.com/video/BV1AH4y1H78d/?vd_source902e3124d4683c41b103f1d1322401fa 目录 书生浦语大模型趣味 Demo一、基础作业二、进阶作业 一、基础作业 第一次执行&#xff1a; 第二次执行&#xff1a; 第一次执…

四核8g服务器价格多少钱?

2024年腾讯云4核8G服务器租用优惠价格&#xff1a;轻量应用服务器4核8G12M带宽646元15个月&#xff0c;CVM云服务器S5实例优惠价格1437.24元买一年送3个月&#xff0c;腾讯云4核8G服务器活动页面 txybk.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云4核8G服务器优惠价格 轻…

Doris实践——同程数科实时数仓建设

目录 前言 一、早期架构演进 二、Doris和Clickhouse选型对比 三、新一代统一实时数据仓库 四、基于Doris的一站式数据平台 4.1 一键生成任务脚本提升任务开发效率 4.2 自动调度监控保障任务正常运行 4.3 安全便捷的可视化查询分析 4.4 完备智能的集群监控 五、收益与…

基于单片机的无线红外报警系统

**单片机设计介绍&#xff0c;基于单片机的无线红外报警系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的无线红外报警系统是一种结合了单片机控制技术和无线红外传感技术的安防系统。该系统通过无线红外传感器实…

Excel、PowerQuery 和 ChatGPT 终极手册(下)

原文&#xff1a;Ultimate ChatGPT Handbook for Enterprises 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 使用 SUMIFS、SUMPRODUCT、AGGREGATE 和 MAX 函数查找数值数据 其中之一鲜为人知的事实是&#xff0c;当查找单个数值时&#xff0c;匹配和三角函数可能比查…

哈佛大学商业评论 -- 第二篇:增强现实是如何工作的?

AR将全面融入公司发展战略&#xff01; AR将成为人类和机器之间的新接口&#xff01; AR将成为人类的关键技术之一&#xff01; 请将此文转发给您的老板&#xff01; --- 本文作者&#xff1a;Michael E.Porter和James E.Heppelmann 虽然物理世界是三维的&#xff0c;但大…

视觉大模型--deter的深入理解

但对于transformer用于目标检测领域的开创性模型&#xff0c;该模型言简意赅&#xff0c;但是但从论文理解&#xff0c;有很多细节都不清楚&#xff0c;尤其是解码器的query和二分图匹配(Bipartite Matching)和匈牙利算法(Hungarian Algorithm)相关&#xff0c;本文将根据代码详…

Android自定义view;实现掌阅打开书籍动画效果

这里利用自定义view的方式来处理&#xff0c;初始化数据&#xff0c;camera通过setLocation调整相机的位置&#xff0c;但是Camera 的位置单位是英寸&#xff0c;英寸和像素的换算单位在 Skia 中被写成了72 像素&#xff0c;8 x 72 576&#xff0c;所以它的默认位置是 (0, 0, …