【医学影像数据处理】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.mzph.cn/news/793130.shtml

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

相关文章

GT收发器64B66B协议(2)自定义PHY设计

文章目录 前言一、设计框图二、GT_module三、PHY_module3.1、PHY_tx模块3.2、PHY_rx_bitsync模块3.3、PHY_rx模块 四、上板测试 前言 有了对64B66B协议的认识以及我们之前设计8B10B自定义PHY的经验&#xff0c;本文开始对64B66B自定义PHY的设计 一、设计框图 二、GT_module …

蓝桥杯单片机速成8-NE555频率测量

一、原理图 NOTE&#xff1a;使用NE555测量频率之前&#xff0c;需要将J3-15(SIGNAL)与J3-16(P34短接) 在使用矩阵键盘的时候也记得把跳冒拔下&#xff0c;因为有公共引脚P34 又是因为他的输出引脚是P34&#xff0c;所以只能用定时器0来作为计数器进行频率测量了 二、代码实现 …

CSS设置网页背景

目录 概述&#xff1a; 1.background-color: 2.background-image&#xff1a; 3.background-repeat&#xff1a; 4.background-position&#xff1a; 5.background-attachment&#xff1a; 6.background-size&#xff1a; 7.background-origin&#xff1a; 8.background-…

Linux初学(十四)LampLnmp

一、简介 LAMP和LNMP是两种常见的web服务器组合。具体如下&#xff1a; LAMP&#xff1a;LAMP代表的是Linux&#xff08;操作系统&#xff09; Apache&#xff08;HTTP服务器&#xff09; MySQL&#xff08;数据库&#xff09; PHP&#xff08;编程语言&#xff09;。这个组合被…

C++利用键值对计算某一个数对应的最值及其索引位置

目录 一、算法概述二、代码实现1、计算最值2、计算最值及其索引 三、结果展示 本文由CSDN点云侠原创&#xff0c;原文链接。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫与GPT。 一、算法概述 类似下图所示&#xff0c;计算第一列中1或2对应的最…

线段树练习

1.单点修改区间查询 P3374 【模板】树状数组 1 题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行下面两种操作&#xff1a; 将某一个数加上 x 求出某区间每一个数的和 输入格式 第一行包含两个正整数 n,m&#xff0c;分别表示该数列数字的个数和操作的总个…

ChatGPT 与 OpenAI 的现代生成式 AI(下)

原文&#xff1a;Modern Generative AI with ChatGPT and OpenAI Models 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 七、通过 ChatGPT 掌握营销技巧 在本章中&#xff0c;我们将重点介绍营销人员如何利用 ChatGPT&#xff0c;在这一领域中查看 ChatGPT 的主要用例…

Delphi编写的图片查看器

UNIT Unit17;INTERFACEUSESWinapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,Vcl.StdCtrls, Vcl.ExtDlgs, Vcl.ExtCtrls, Vcl.Imaging.jpeg; //注意&#xff1a;要加入jpej 否侧浏览图…

ids工业相机与电控位移台同步控制及数据采集

通过VS2017和OpenCV,实现ids工业相机与电控位移台同步控制及数据采集 目录项目环境配置代码流程及思路项目架构项目开发运行效果开发关键ids相机配置位移台环境配置相机头文件相机参数设置保存图像函数设置电控位移台头文件电控位移台设置参数最后就是通过main函数进行调用和控…

《QT实用小工具·十一》Echart图表JS交互之仪表盘

1、概述 源码放在文章末尾 该项目为Echart图表JS交互之炫酷的仪表盘&#xff0c;可以用鼠标实时改变仪表盘的读数。 下面为demo演示&#xff1a; 该项目部分代码如下&#xff1a; #include "widget.h" #include "ui_widget.h" #include "qurl.h&q…

PWM技术的应用

目录 PWM技术简介 PWM重要参数 PWM实现呼吸灯 脉宽调制波形 PWM案例 电路图 keil文件 直流电机 直流电机的控制 直流电机的驱动芯片L293D L293D引脚图 L293D功能表 直流电机案例 电路图 keil文件 步进电机 步进电机特点 步进电机驱动芯片L298 L298引脚图 L…

vue快速入门(一)vue的导入方法

注释很详细&#xff0c;直接上代码 新增内容 下载js代码导入实例数据绑定显示 源码 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-widt…

HbnnMall电子商城系统介绍(功能与技术栈)

今天在看我个人网站上的文章时&#xff0c;看到了曾经在2020年自己开发的电商系统。那时我已经入职小米有一段时间了&#xff0c;基本已经对各个业务线&#xff0c;各种业务知识有了系统性的了解和学习&#xff0c;所以想自己动手写一个电商系统&#xff0c;以便进一步提高自己…

书生·浦语训练营二期第二次笔记

文章目录 1. 部署 InternLM2-Chat-1.8B 模型进行智能对话1.1 配置环境1.2 下载 InternLM2-Chat-1.8B 模型 2. 实战&#xff1a;部署实战营优秀作品 八戒-Chat-1.8B 模型2.1 配置基础环境2.2 使用 git 命令来获得仓库内的 Demo 文件&#xff1a;2.3 下载运行 Chat-八戒 Demo 3. …

19.网络测试

考试频率低&#xff1b;主要是上午题&#xff1b; 主要议题&#xff1a; 1.网络全生命周期测试策略 2.网络设备评测指标 吞吐量&#xff1a;单位时间内完成xxxx的数量&#xff1b;如&#xff1a;不丢包情况下&#xff0c;系统最大的包转发速度&#xff1b; 丢包率&#xff…

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

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

C语言进阶课程学习记录-第22课 - 条件编译使用分析

C语言进阶课程学习记录-第22课 - 条件编译使用分析 条件编译基本概念条件编译实验条件编译本质实验-ifdefinclude本质实验-间接包含同一个头文件解决重复包含的方法-ifndef实验-条件编译的应用小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源…

Java spring 01 (图灵)

01.依赖注入 这里两个方法用到了datasource方法&#xff0c;不是bean这样的使用&#xff0c;没有autowird 会创建两个datasource configuration 会运行代理模式 会产生一个AppConfig的代理对象 这个代理对象会在spring的容器先找bean&#xff0c;datasource此时已经创建了be…

Linux-进程概念

1. 进程基本概念 书面概念&#xff1a;程序的一个执行实例&#xff0c;正在执行的程序等 内核概念&#xff1a;担当分配系统资源&#xff08;CPU时间&#xff0c;内存&#xff09;的实体。 2. 描述和组织进程-PCB PCB&#xff08;process contral block&#xff09;&#xff0…

RisingWave 在品高股份 Bingo IAM 中的应用

背景介绍 公司背景 品高股份&#xff0c;是国内专业的云计算及行业信息化服务提供商。公司成立于 2003 年&#xff0c;总部位于广州&#xff0c;下设多家子公司和分公司&#xff0c;目前员工总数近 900 人&#xff0c;其中 80 %以上是专业技术人员。 品高股份在 2008 年便开…