[医学图像预处理1]使用nibabel和SimpleITK读取处理Nifiti文件(MRI图像)

1. NIFTI格式(.Nii)数据格式分析

https://brainder.org/2012/09/23/the-nifti-file-format/

https://blog.csdn.net/DoronLee/article/details/78597868

2. 使用nibabel读取nii文件并显示其中1个slice

import matplotlib.pyplot as plt
import numpy as np
import os

NII_DIR='./nii_dir'    #nii文件所在root目录

import nibabel as nib

def read_nii_file1(nii_path):
    '''
    根据nii文件路径读取nii图像
    '''
    nii_image=nib.load(nii_path)
    return nii_image

def nii_one_slice1(image):
    '''
    显示nii image中的其中1张slice
    '''
    image_arr=image.get_data()
    print(type(image_arr))
    print(image_arr.shape)
    #注意:nibabel读出的image的data的数组顺序为:Width,Height,Channel
    #将2d数组转置,让plt正常显示
    image_2d=image_arr[:,:,85].transpose((1,0))
    plt.imshow(image_2d, cmap = 'gray')
    plt.show()

nii_image1=read_nii_file1(os.path.join(NII_DIR,'Brats18_2013_2_1','Brats18_2013_2_1_flair.nii.gz'))
nii_one_slice1(nii_image1)

 输出:

<class 'numpy.ndarray'>
(240, 240, 155)

3.使用SimpleITK读取nii文件并显示其中1个slice

import SimpleITK as sitk

def read_nii_file2(img_path):
    '''
    根据nii文件路径读取nii图像
    '''
    nii_image = sitk.ReadImage(img_path)
    return nii_image

def nii_one_slice2(image):
    '''
    显示nii image中的其中1张slice
    '''
    #C,H,W
    #SimpleITK读出的image的data的数组顺序为:Channel,Height,Width
    image_arr=sitk.GetArrayFromImage(image)
    print(type(image_arr))
    print(image_arr.shape)
    image_2d=image_arr[85,:,:]
    plt.imshow(image_2d, cmap = 'gray')
    plt.show()

nii_image2=read_nii_file2(os.path.join(NII_DIR,'Brats18_2013_2_1','Brats18_2013_2_1_flair.nii.gz'))
nii_one_slice2(nii_image2)

4. 对nii图像进行随机变换

from nilearn.image import new_img_like,resample_to_img

获取nii image的仿射矩阵affine

nii_img=nib.load(os.path.join(NII_DIR,'Brats18_2013_2_1','Brats18_2013_2_1_flair.nii.gz'))
print(nii_img.affine.shape)
print(nii_img.affine)

输出:

(4, 4)
[[ -1.   0.   0.  -0.]
 [  0.  -1.   0. 239.]
 [  0.   0.   1.   0.]
 [  0.   0.   0.   1.]]

仿射矩阵是对图像实现仿射变换的矩阵,仿射变换包括平移、放缩、旋转。

3D图像的仿射矩阵大小为4*4,最后一行固定为[0,0,0,1]不起作用.

第4列控制平移变换,主对角线控制放缩变换,affine[:3,:3]控制旋转变换。

放缩--修改nii image的仿射矩阵affine

def random_scale_factor(n_dim=3, mean=1, std=0.25):
    return np.random.normal(mean, std, n_dim)

def scale_image(image, scale_factor):
    scale_factor = np.asarray(scale_factor)
    new_affine = np.copy(image.affine)
    #修改原image的affine
    #乘以放缩系数
    new_affine[:3, :3] = image.affine[:3, :3] * scale_factor
    #修改第4列的前3个值,保证放缩后的图像居中
    #1-scale_factor:缩小时,右移;放大时,左移
    new_affine[:, 3][:3] = image.affine[:, 3][:3] + (image.shape * np.diag(image.affine)[:3] * (1 - scale_factor)) / 2
    #返回创建新的nii image,data与原image相同,affine更新
    return new_img_like(image, data=image.get_data(), affine=new_affine)

new_image1=scale_image(nii_img,(0.5,0.5,0.5))
#print(nii_img.get_data()[120,120,:])
print(nii_img.affine)
#print(new_image1.get_data()[120,120,:])
print(new_image1.affine)
nii_one_slice1(new_image1)

#调用resample_to_img实现重采样
image_scaled=resample_to_img(new_image1,nii_img,interpolation="continuous")
nii_one_slice1(image_scaled)

输出:

[[ -1.   0.   0.  -0.]
 [  0.  -1.   0. 239.]
 [  0.   0.   1.   0.]
 [  0.   0.   0.   1.]]
[[ -0.5    0.     0.   -60.  ]
 [  0.    -0.5    0.   179.  ]
 [  0.     0.     0.5   38.75]
 [  0.     0.     0.     1.  ]]
<class 'numpy.ndarray'>
(240, 240, 155)

<class 'numpy.ndarray'>
(240, 240, 155)

翻转--使用np.flip(new_data,axis=...)直接修改nii image的data数组

def random_boolean():
    return np.random.choice([True, False])

#随机选择翻转的axis
def random_flip_dimensions(n_dimensions):
    axis = list()
    for dim in range(n_dimensions):
        if random_boolean():
            axis.append(dim)
    return axis

def flip_image(image, axis):
    try:
        new_data = np.copy(image.get_data())
        for axis_index in axis:
            #根据axis对image数组进行翻转
            new_data = np.flip(new_data, axis=axis_index)
    except TypeError:
        new_data = np.flip(image.get_data(), axis=axis)
    return new_img_like(image, data=new_data)

#翻转axis=1,即上下翻转
new_image2=flip_image(nii_img,axis=[1])

print(nii_img.get_data()[120,120,:])
print(new_image2.get_data()[120,120,:])
nii_one_slice1(new_image2)

#使用resample_to_img实现重采样和不使用效果一样
image_flipped=resample_to_img(new_image2,nii_img,interpolation="continuous")
print(image_flipped.get_data()[120,120,:])
nii_one_slice1(image_flipped)

输出:

[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0  60 119 124 130
 126 137 188 248 308 312 273 262 284 328 339 365 418 424 367 271 176 111
  96 105 127 159 215 286 320 322 249 134 125 216 304 322 329 371 398 401
 413 409 383 374 370 415 481 490 496 506 506 513 503 491 482 483 493 392
 198 129 161 162 168 187 209 221 217 214 217 216 217 214 199 168 132 110
 112 121 107  79  62  53  48  47  48  54  82  87  39   1   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0]
[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  68 120
 130 148 206 281 358 353 296 293 329 363 368 398 429 412 345 252 160  94
  71  78  98 135 196 262 316 303 222 146 176 273 326 333 344 380 398 404
 414 415 394 379 380 434 489 497 500 501 505 509 497 487 477 479 481 383
 195 139 189 184 174 193 220 229 222 218 219 230 238 228 204 169 134 115
 121 126 109  91  77  66  59  55  49  65  96  85  38   4   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0]
<class 'numpy.ndarray'>
(240, 240, 155)

[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  68 120
 130 148 206 281 358 353 296 293 329 363 368 398 429 412 345 252 160  94
  71  78  98 135 196 262 316 303 222 146 176 273 326 333 344 380 398 404
 414 415 394 379 380 434 489 497 500 501 505 509 497 487 477 479 481 383
 195 139 189 184 174 193 220 229 222 218 219 230 238 228 204 169 134 115
 121 126 109  91  77  66  59  55  49  65  96  85  38   4   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0]
<class 'numpy.ndarray'>
(240, 240, 155)

nii image随机增强--使用随机生成的放缩系数和翻转的axis封装的函数

#放缩+翻转的封装函数
def distort_image(image, flip_axis=None, scale_factor=None):
    if flip_axis:
        image = flip_image(image, flip_axis)
    if scale_factor is not None:
        image = scale_image(image, scale_factor)
    return image

def get_image(data, affine, nib_class=nib.Nifti1Image):
    #将数组data转换成nifiti1图像
    return nib_class(dataobj=data, affine=affine)

def augment_data(data, truth, affine, scale_deviation=None, flip=True):
    '''
    data shape;[num_motailities,W,H,C]
    truth:[W,H,C]
    '''
    #随机的生成放缩尺度和翻转的axis
    n_dim = len(truth.shape)
    if scale_deviation:
        scale_factor = random_scale_factor(n_dim, std=scale_deviation)
    else:
        scale_factor = None
    if flip:
        flip_axis = random_flip_dimensions(n_dim)
    else:
        flip_axis = None
    data_list = list()
    #数据的模态索引
    for data_index in range(data.shape[0]):
        #hdf5文件中保存的每个模态的image得数组转换为nii image
        #调用resample_to_img实现仿射变换
        image = get_image(data[data_index], affine)
        data_list.append(resample_to_img(distort_image(image, flip_axis=flip_axis,scale_factor=scale_factor), 
                                         image,
                                         interpolation="continuous").get_data())
    data = np.asarray(data_list)
    #hdf5文件中保存的truth image得数组转换为nii image
    truth_image = get_image(truth, affine)
    #调用resample_to_img实现仿射变换
    truth_data = resample_to_img(distort_image(truth_image, flip_axis=flip_axis, scale_factor=scale_factor),
                                 truth_image, 
                                 interpolation="nearest").get_data()
    return data, truth_data

nii image的permutation变换

import itertools
import random

def generate_permutation_keys():
    """
    This function returns a set of "keys" that represent the 48 unique rotations &
    reflections of a 3D matrix.

    Each item of the set is a tuple:
    ((rotate_y, rotate_x), flip_x, flip_y, flip_z, transpose)

    As an example, ((0, 1), 0, 1, 0, 1) represents a permutation in which the data is
    rotated 90 degrees around the z-axis, then reversed on the y-axis, and then
    transposed.

    48 unique rotations & reflections:
    https://en.wikipedia.org/wiki/Octahedral_symmetry#The_isometries_of_the_cube
    """
    ##((0,0)/(0,1)/(1,1),0/1,0/1,0/1,0/1)=3*16=48种组合的笛卡尔积集合
    return set(itertools.product(
        itertools.combinations_with_replacement(range(2), 2), range(2), range(2), range(2), range(2)))


def random_permutation_key():
    """
    Generates and randomly selects a permutation key. See the documentation for the
    "generate_permutation_keys" function.
    """
    #随机选择1种permutation
    return random.choice(list(generate_permutation_keys()))

def permute_data(data, key):
    """
    Permutes the given data according to the specification of the given key. Input data
    must be of shape (n_modalities, x, y, z).

    Input key is a tuple: (rotate_y, rotate_x), flip_x, flip_y, flip_z, transpose)

    As an example, ((0, 1), 0, 1, 0, 1) represents a permutation in which the data is
    rotated 90 degrees around the x-axis, then reversed on the y-axis, and then
    transposed.
    """
    data = np.copy(data)
    (rotate_y, rotate_x), flip_x, flip_y, flip_z, transpose = key

    if rotate_y != 0:
        data = np.rot90(data, rotate_y, axes=(1, 3))
    if rotate_x != 0:
        data = np.rot90(data, rotate_x, axes=(2, 3))
    #翻转x/y/z轴
    if flip_x:
        data = data[:, ::-1]
    if flip_y:
        data = data[:, :, ::-1]
    if flip_z:
        data = data[:, :, :, ::-1]
    #转置
    if transpose:
        new_data=np.zeros((1,data.shape[-1],data.shape[-2],data.shape[-3]))
        for i in range(data.shape[0]):
            new_data[i] = data[i].T
    return new_data


def random_permutation_x_y(x_data, y_data):
    """
    Performs random permutation on the data.
    :param x_data: numpy array containing the data. Data must be of shape (n_modalities, x, y, z).
    :param y_data: numpy array containing the data. Data must be of shape (n_modalities, x, y, z).
    :return: the permuted data
    """
    key = random_permutation_key()
    return permute_data(x_data, key), permute_data(y_data, key)

测试:

#绕y轴(H)旋转90度
key1=((1, 0), 0, 0, 0, 0)
data_permuted1=permute_data(data_ori,key1)
nii_one_slice1(nii_img)
nii_one_slice1(new_img_like(nii_img,data=data_permuted1[0,:]))

 输出:

<class 'numpy.ndarray'>
(240, 240, 155)

<class 'numpy.ndarray'>
(155, 240, 240)

 测试:

#绕x轴(W)旋转90度
key2=((0, 1), 0, 0, 0, 0)
data_permuted2=permute_data(data_ori,key2)
nii_one_slice1(nii_img)
nii_one_slice1(new_img_like(nii_img,data=data_permuted2[0,:]))

输出:

<class 'numpy.ndarray'>
(240, 240, 155)

 

<class 'numpy.ndarray'>
(240, 155, 240)

测试:

#转置
key3=((0, 0), 0, 0, 0, 1)
data_permuted3=permute_data(data_ori,key3)
nii_one_slice1(nii_img)
nii_one_slice1(new_img_like(nii_img,data=data_permuted3[0,:]))

 输出:

<class 'numpy.ndarray'>
(240, 240, 155)

 

<class 'numpy.ndarray'>
(155, 240, 240)

 

 

 

  • 22
    点赞
  • 131
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值