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

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文件。

  • 28
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 医学影像NII切图标注是一种利用JavaScript进行开发的技术,用于对医学影像数据进行切割和标注。 医学影像数据通常以NIfTI(Neuroimaging Informatics Technology Initiative)格式存储,这是一种常用于保存三维图像数据的格式。而切图标注是指将三维图像数据切割成多个二维图像,并对这些二维图像进行标注。 使用JavaScript进行开发医学影像NII切图标注有以下几个步骤: 1.读取NII文件:首先,需要使用JavaScript的文件读取功能,将NII文件从服务器或本地读入到内存中。 2.解析NII文件:将读取到的NII文件进行解析,获取图像的相关信息,例如图像的尺寸、体素分辨率等。 3.切图:根据所需的切图大小和切图间隔,将三维图像进行切割,生成多个二维图像。可以根据需要切割成等间隔的切片,也可以根据解剖结构进行特定位置的切割。 4.图像标注:在生成的二维图像上进行标注,可以使用JavaScript的图像处理库对图像进行绘制,例如绘制矩形框、线条、文本等。标注可以用于标记感兴趣区域、病变位置等信息。 5.保存标注结果:对进行标注的图像进行保存,可以保存为图片文件或者将标注信息存储到数据库中,以便后续使用和分析。 使用JavaScript开发医学影像NII切图标注具有跨平台、灵活性强的优点,而且JavaScript具有广泛的应用和开发生态,可以通过调用其他JavaScript库来实现更多的功能。同时,由于医学影像数据的特殊性,还需要了解医学影像学相关的知识,以便更好地理解和处理数据。 总之,使用JavaScript开发医学影像NII切图标注可以提供一种方便快捷的方式来处理医学影像数据,并进行相关的标注,有助于医学研究和临床应用中的图像分析和诊断。 ### 回答2: 在医学影像处理中,NII(Neuroimaging Informatics Technology Initiative)文件格式是常用的文件格式之一,用于存储医学影像数据NII文件包含了三维和四维医学图像的细节信息,如CT扫描、MRI扫描等。而JS(JavaScript)是一种常用的编程语言,可以用于开发Web应用。 在开发医学影像NII切图标注功能时,可以使用JS来实现相关功能。首先,需要在前端页面中加载NII文件,获取其中的医学影像数据。可以使用一些开源的JS库,如CornerstoneJS、AMI(Anatomical Model Imaging)等来处理NII文件,解析其中的图像数据。 接下来,可以使用JS来实现切图功能。可以通过在前端页面中添加一些交互元素,如滑动条、按钮等,来控制图像的切片和切面的显示。通过改变这些交互元素的值,可以实时改变图像的切片位置和显示。 同时,为了进行图像标注,可以使用JS中的Canvas API来实现。通过在前端页面中添加Canvas元素,将NII文件中的图像数据绘制到Canvas上,然后可以通过鼠标等交互事件,在Canvas上进行标注。可以实现标注工具的功能,如画笔、橡皮擦、文本等,来进行不同类型的标注。 此外,为了方便用户操作,可以考虑将上述功能封装为一个可视化的工具,并提供一些额外的功能,如图像对比、图像测量等。通过JS开发医学影像NII切图标注功能,可以实现在Web环境下对医学影像进行方便、快捷的切图和标注,提高医学影像处理的效率。 ### 回答3: 医学影像nii文件是一种常见的医学影像数据格式,通常用于存储医学影像数据。在进行医学影像分析或者研究时,我们经常需要对这些影像进行标注或者切图,以便进行进一步的分析和处理。 使用js进行医学影像nii切图标注可以带来很多方便和效率的优势。首先,js是一种广泛应用于前端开发的脚本语言,具有丰富的库和框架供我们使用,这为我们提供了非常便捷的开发环境。此外,js具有跨平台的特点,可以在多个操作系统上运行,无需进行额外的配置或安装。 对于nii切图,我们可以利用js的图像处理库或者Canvas API来实现。通过读取nii文件中的数据,我们可以将其转换为图片格式,并在网页上显示出来。然后,通过js的鼠标交互事件,我们可以实现对图像的放大、缩小、平移等操作。同时,我们还可以添加标注工具,如绘制线条、矩形或者文本等,帮助用户对图像进行标注。 在开发过程中,我们需要注意的是,医学影像数据常常非常庞大,因此需要考虑到性能的问题。我们可以使用前端的图像压缩算法,对图像进行压缩以减少数据的大小,并在需要时动态加载图像,以提高用户的操作体验。 总结而言,通过使用js开发医学影像nii切图标注工具,我们可以快速实现对医学影像的切图和标注功能,为医学影像分析和研究提供了便利。同时,js的跨平台特性也使得我们可以在不同的设备上使用这个工具,增加了使用的灵活度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钱多多先森

你的鼓励,是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值