【医学影像数据处理】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 nib

img = nib.load(r"./data/DET0000101.nii.gz")
 
# Convert them to numpy format,
data = img.get_fdata()
affine = img.affine
head = img.header

print("数组大小为\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 itk

nii_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_z
    print(pixdim)
    n+=1

    image_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 nib

def read_nii_itk(nii_path):
    image = itk.array_from_image(itk.imread(nii_path))
    return image

def read_nii_nib(nii_path):
    I = nib.load(nii_path)
    I_affine = I.affine
    I = I.get_fdata()
    return I, I_affine

def 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 itk
 
nii_path = os.path.join(r'./images', 'brain.nii.gz')
 
image = sitk.ReadImage(nii_path)
print('image size:', image.GetSize())   # depth、height、width
 
imgs = 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文件。

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

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

相关文章

原理图设计的通用规范

原理图各页内容依次为&#xff1a;封面、目录、电源、时钟、CPU、存储器、逻辑、背板&#xff08;母板&#xff09;接口等。 原理图上所有的文字方向应该统一&#xff0c;文字的上方应该朝向原理图的上方&#xff08;正放文字&#xff09;或左方&#xff08;侧放文字&#xff…

Linux的Docker:解析容器化技术的革命

在当今科技领域&#xff0c;容器化技术的兴起被视为软件开发和部署的一场革命。而在这场革命中&#xff0c;Docker是无疑的领头羊。作为一种轻量级、可移植、自包含的软件容器解决方案&#xff0c;Docker已经深刻地改变了开发者们构建、交付和运行应用程序的方式。本文将深入探…

创建vue3项目及基本常用配置

1、创建vue3项目 1.1 创建vue3项目 确保电脑中安装了nodejs&#xff0c;新建文件夹&#xff0c;输入以下命令&#xff1a; npm create vuelatest 看是否为自己需要的vue版本&#xff0c;选择Y 各配置具体如下&#xff0c;根据自己的需求选择是或者否 npm create vuelatest …

内部类(来自类和对象的补充)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&a…

SON序列化解决方案

JSON&#xff08;JavaScript Object Notation&#xff09;是一种用于数据交换的轻量级数据格式。在我们日常Python编程中&#xff0c;通常可以使用内置的json模块来进行JSON序列化和反序列化。那么关于使用json模块进行JSON序列化和反序列化的问题解决方案&#xff0c;可以参考…

了解以太坊虚拟机(EVM)

了解以太坊虚拟机&#xff08;EVM&#xff09; 以太坊虚拟机&#xff08;Ethereum Virtual Machine&#xff0c;简称EVM&#xff09;是以太坊网络的核心组件之一&#xff0c;它承担着智能合约执行的重要任务 特点 智能合约执行环境&#xff1a;EVM提供了一个安全的环境&#xf…

mysql 运算符 语句 字符集 校队集

mysql 运算符 使用select语句可以输出运算的结果 mysql标识符不区分大小写 算数运算符 1./除法 得到的结果是一个小数 %是整数,省略小数 2、除以0不会报错,得到的结果是 null 3.数宇和字符串做加法运算,并不会拼接 比较运算符 1.mysql里面的=是比较运算符,而不是赋值运算…

电工技术学习笔记——直流电路及其分析方法

一、直流电路 电路的组成 1. 电压和电流的参考方向 电压&#xff08;Voltage&#xff09;&#xff1a;电压是电场力对电荷产生的作用&#xff0c;表示为电荷单位正电荷所具有的能量。在电路中&#xff0c;电压通常被定义为两点之间的电势差&#xff0c;具有方向性&#xff0c;…

(免费分享)基于springboot,vue房屋租赁管理系统

功能说明&#xff1a; * 普通用户角色&#xff1a; 1. 寻找房源功能--提供了两种寻找房源的功能&#xff0c;一种是普通用户在平台上搜索、筛选主动寻找房源的功能&#xff0c;另一种是用户填写征集房源的条件&#xff0c;系统会持续将最新符合条件的房源推送给用户。 2. …

【HBase】

什么是HBase HBase是Google Bigtable的开源实现,类似Google Bigtable利用GFS作为其文件存储系统,HBase利用Hadoop HDFS作为其文件存储系统;Google运行MapReduce来处理Bigtable中的海量数据,HBase同样利用Hadoop MapReduce来处理HBase中的海量数据。 访问层次&#xff08;数据…

mac电脑安装redis教程

1、下载地址 Download | RedisRedisYou can download the last Redis source files here. For additional options, see the Redis downloads section below.Stable (7.2)Redis 7.2 …https://redis.io/download/#redis-downloads 2、安装 2.1 解压下载后的压缩文件 2.2 进入…

Redis数据库:持久化策略与性能管理

目录 前言 一、Redis持久化 1、Redis持久化功能 2、Redis持久化的两种方式 3、RDB持久化 3.1 触发条件 3.2 执行流程 3.3 启动时加载 4、AOF持久化 4.1 开启AOF 4.2 执行流程 4.2.1 执行流程详解 4.2.2 执行流程图 4.3 启动时加载 5、RDB与AOF持久化的优缺点 5…

【STM32】ST-LINK 下载时遇到的问题

如果出现“ST-Link USB communication error”ST-Link USB通信错误&#xff0c;则需要启动STM32 ST-LINK Utility&#xff0c;点击【ST-LINK】->【Firmaware】更新固件&#xff0c;然后打开Kei&#xff0c;点击魔术棒->->Debug->Settings&#xff0c;开到出现类似“…

2023年CSP-J第一轮题目讲解

大家好&#xff0c;我是极风。由于当年的初赛考的很差&#xff08;没考过70分&#xff09;&#xff0c;所以现在打算拿出来再细看一下。 一、 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 1. 在…

2024.04.04 健身打卡第 45 天

别让别人告诉你&#xff0c;你成不了才&#xff0c;如果你有梦想的话就要去捍卫它&#xff0c;那些一事无成的人&#xff0c;想告诉你你也成不了大器&#xff0c;如果你有理想的话&#xff0c;就要去努力实现。 2024.04.04 健身打卡第 45 天

电商系列之促销

> 插&#xff1a;AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…

[实验报告]--基于端口安全

[实验报告] 目录 [实验报告] 一、项目背景 二、实验环境 三、项目规划设计 四、项目实施 五、验证项目成果 基于端口安全的 Jan16 公司网络组建 一、项目背景 Jan16 公司开发部为重要部门&#xff0c;所有员工使用指定的计算机工作&#xff0c;为防止员工或访客使 用个…

蓝桥杯真题:七段码

import java.util.Scanner; import java.util.ArrayList; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args){// 连接关系图int[][] map new int[][]{{0, 1, 0, 0, 0, 1, 0},{1, 0, 1, 0, 0, 0, 1},{0, 1, 0, 1, 0, 0…

如何保护IP地址不被泄露?

当互联网成为每个家庭的重要组成部分后&#xff0c;IP地址就成了你的虚拟地址。您的请求从该地址开始&#xff0c;然后 Internet 将消息发送回该地址。那么&#xff0c;您担心您的地址被泄露吗&#xff1f; 对于安全意识高或者某些业务需求的用户&#xff0c;如果您正在寻找保护…

数据结构——图的概念,图的存储结构,图的遍历(dfs,bfs)

目录 1.图的定义和术语 2.案例引入 1.六度空间理论 3.图的类型定义 4.图的存储结构 1.邻接矩阵 1.无向图的邻接矩阵表示法 2.有向图的邻接矩阵表示法 3.网&#xff08;有权图&#xff09;的邻接矩阵表示法 代码示例&#xff1a; 2.采用邻接矩阵表示法创建无向图…