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)