美文网首页
医疗影像读取与写入

医疗影像读取与写入

作者: SnorlaxSE | 来源:发表于2019-10-10 18:55 被阅读0次

参考:
SimpleITK处理dicom数据
【python】SimpleITK 和 Nibabel 读取医学图像 nii 数据(2D显示)

使用须知

  • SimpleITK读取的图像数据sitk.ReadImage()的坐标顺序为C*H*W,即从多少张切片到单张切片的宽和高;而据SimpleITK Image获取的origin和spacing的坐标顺序则是H*W*C

  • nib.load()读取文件,会将图像向左旋转90°,坐标顺序为W*H*C(不太推荐)

SimpleITK

  • ReadImage
import SimpleITK as sitk
import skimage.io as io

def read_img_sitk(filepath):
    image = sitk.ReadImage(filepath)  # <class 'SimpleITK.SimpleITK.Image'> # 支持dcm\nrrd\nii
    image_array = sitk.GetArrayFromImage(image)  # z,y,x
    return image_array

def show_img_sitk(data, slice_num=None):
    if slice_num:
        io.imshow(data[slice_num, :, :], cmap='gray')
        io.show()
    else:
        for i in range(data.shape[0]):
            io.imshow(data[i, :, :], cmap='gray')
            print(i)
            io.show()

path = "origin.nii"
data_arr = read_img_sitk(path)
show_img_sitk(data_arr, slice_num=45)
PET slice
  • WriteImage
image = sitk.GetImageFromArray(array_data)
sitk.WriteImage(image, 'xxxx')

示例: nii 与 npy 相互转化

import SimpleITK as sitk
import numpy as np

# load input file
origin_data = read_img_sitk(filepath="origin.nii")
print(origin_data.shape)  # (90, 128, 128)

# save as npy file
np.save(file='origin.npy', arr=origin_data)

# load npy file
npy_data = np.load('origin.npy')
print(npy_data.shape)  # (90, 128, 128)

# save as nii file
image = sitk.GetImageFromArray(npy_data)
sitk.WriteImage(image, 'origin_npy.nii')

# load saved file
origin_npy_data = read_img_sitk(filepath="origin_npy.nii")
print(origin_npy_data.shape)  # (90, 128, 128)

左右图数据应该是一致的,左图由Mango自动翻转slice排序显示,故而显示有所差异 ⤴️

show_img_sitk(origin_data, 45)
show_img_sitk(origin_npy_data, 45)

Nibabel

  • ReadImage
import nibabel as nib
import matplotlib.pyplot as plt
 
def read_img_nib(filepath):
    image_data = nib.load(filepath).get_data()  # <class 'numpy.memmap'>
    return image_data
 
def show_img_nib(data, slice_num):
    import matplotlib.pyplot as plt
    plt.imshow(data[:, :, slice_num], cmap='gray')  # channel_last
    plt.show()
 
path = 'origin.nii'
data = read_img_nib(path)  # (W, H, C, 1)
print(data.shape)  # (128, 128, 90, 1)
data_arr = np.asarray(data)  # <class 'numpy.memmap'> -> <class 'numpy.ndarray'>
data_arr = np.squeeze(data_arr)  # (W, H, C)  # or data.squeeze()
print(data_arr.shape)  # (128, 128, 90)
show_img_nib(data_arr, 45)

# correction nib offset
data_arr = data_arr.transpose((1, 0, 2))  # (H, W, C)
print(data_arr.shape)  # (128, 128, 90)
show_img_nib(data_arr, 45)
transpose PET slice
  • WriteImage
# load input file
origin_data = read_img_nib(filepath="origin.nii")
print(origin_data.shape)  # (128, 128, 90, 1)  (W*H*C*1)

# save as npy file
np.save(file='origin.npy', arr=origin_data)

# load npy file
npy_data = np.load('origin.npy')
print(npy_data.shape)  # (128, 128, 90, 1)  (W*H*C*1)
npy_data = np.squeeze(npy_data)
print(npy_data.shape)  # (128, 128, 90)   (W*H*C)

# save as nii file
# 默认以(C' * W' * H')传入,即误认为传入128张128*90的切片
image = sitk.GetImageFromArray(npy_data)  
sitk.WriteImage(image, 'origin_npy.nii')

# load saved file
origin_npy_data = read_img_nib(filepath="origin_npy.nii")
print(origin_npy_data.shape)  # (90, 128, 128)   (W*H*C)  # 128张128*90的切片 ❎

显然,右图是未对origin_data做像素归一化所致 ⤴️,修改如下:

def normalization(data):
    data = np.asarray(data).squeeze()
    max, min = np.max(data), np.min(data)
    data = (data - min) / (max - min)
    return data

# load input file
origin_data = read_img_nib(filepath="origin.nii")  # (128, 128, 90, 1)  (W*H*C*1)
origin_data = normalization(origin_data)  # (128, 128, 90)  (W*H*C)

# save as npy file
np.save(file='origin.npy', arr=origin_data)

# load npy file
npy_data = np.load('origin.npy') 
print(npy_data.shape)  # (128, 128, 90)  # (W, H, C)

# save as nii file
npy_data = npy_data.transpose(2, 1, 0)
print(npy_data.shape)  # (90, 128, 128)  # (C, H, W)
image = sitk.GetImageFromArray(npy_data)
sitk.WriteImage(image, 'nib_origin_npy.nii')

# load saved file
origin_npy_data = read_img_nib(filepath="nib_origin_npy.nii")
print(origin_npy_data.shape)  # (128, 128, 90)  (W*H*C)  # 90张128*128的切片 ✅
show_img_nib(origin_data, 45)
show_img_nib(origin_npy_data, 45)

中心裁剪

  • 数组操作
def center_crop_sitk(img_arr, width_target, height_target):
    """
    中心裁剪任意尺寸的图片(以中心为原点)
    """
    channel, height, width = img_arr.shape

    if width < width_target:
        raise Exception(
            "The width of image must be at least {} pixels!".format(width_target))
    elif height < height_target:
        raise Exception(
            "The height of image must be at least {} pixels!".format(height_target))

    width_crop = (width - width_target) // 2
    height_crop = (height - height_target) // 2

    if width_crop > 0:
        img_arr = img_arr[:, :, width_crop:-width_crop]
    if height_crop > 0:
        img_arr = img_arr[:, height_crop:-height_crop, :]

    return img_arr
# load input file
origin_data = read_img_sitk(filepath="origin.nii")
print(origin_data.shape)  # (90, 128, 128)

# crop with size
crop_data = center_crop_sitk(origin_data, width_target=88, height_target=112)
print(crop_data.shape)  # (90, 112, 88)  (C*W*H) sitk

# compare
show_img_sitk(origin_data, 45)
show_img_sitk(crop_data, 45)
  • 自定义裁剪
origin_data = read_img_sitk(filepath="origin.nii")  # sitk
print(origin_data.shape)  # (90, 128, 128)

# height_target[0] + height_target[1] <  origin_data.shape[1]
height_target = (0, 20)  # 确实修改的是高度 ➜ origin_data.shape[1] 确实代表的是H ➜ origin_data.shape:C*H*W (可验证)
# width_target[0] + width_target[1] <  origin_data.shape[2]
width_target = (10, 10) 
crop_data = origin_data[:, height_target[0]:-height_target[1], width_target[0]:-width_target[1]]
print(crop_data.shape)  # (90, 108, 108)

# compare
show_img_sitk(origin_data, 45)
show_img_sitk(crop_data, 45)

相关文章

网友评论

      本文标题:医疗影像读取与写入

      本文链接:https://www.haomeiwen.com/subject/hrripctx.html