医学图像预处理(一)——读取图片(dcm,nii,etc)

总结一下目前遇到的一些医学图像预处理步骤,但才开始自学两周,所以很多东西或许不对或者不全面,以后争取每两周回顾以前的文章,将内容完善,并提高质量。(2019-3-5)
由于篇幅原因,将预处理的步骤分开来,这一篇将总结读取图片的方法。

读取DICOM格式

用到的包:pydicom

例如,以公共数据集3Dircadb为例,其文件组织如下:
在这里插入图片描述
其中3Dircadb1.1代表病人1.1号,PATIENT_DICOM是病人腹部切面,LABELED_DICOM 是标记了不同部位的切片,MASKS_DICOM下面又有子文件夹,分别是不同部位的mask。所有的图片都是灰度图像。

读取dicom文件

label_path = '~/Downloads/3Dircadb/3Dircadb1.1/LABELLED_DICOM'
# 若不遍历读出,则获得的是单张切片,而非三维图片
slices = [pydicom.dcmread(label_path + '/' + s) for s in os.listdir(label_path)]
slices.sort(key = lambda x: int(x.InstanceNumber))
# slice[i]为pydicom.dataset.FileDataset,储存了切片相关的信息
# .pixel_array用来获取像素信息,每张切片都是灰度图
image = np.stack([s.pixel_array for s in slices])
image = image.astype(np.int16)
# 目前还不知道这些label值对应的器官分别是什么?
np.unique(image)
#array([  0,   1,  17,  33,  65,  97, 129, 193, 257, 321, 385, 449, 451,
#       453, 465, 481, 513], dtype=int16)
label_path = '~/Downloads/3Dircadb/3Dircadb1.1/MASKS_DICOM/artery'
slices = [pydicom.dcmread(label_path + '/' + s) for s in os.listdir(label_path)]
slices.sort(key = lambda x: int(x.InstanceNumber))
image = np.stack([s.pixel_array for s in slices])
image = image.astype(np.int16)
np.unique(image)
# array([  0, 255], dtype=int16)

读取普通的图片

用到的包 PIL,cv2, skimage
注意它们读取出的格式有细微的区别

图片通道 格式
PIL RGB Image channel_last
cv2 BGR array channel_last
skimage RGB array channel_last

还需要注意,对于opencv来说,无论读取灰度图还是彩图都是(H,W,3)的shape,灰度图的读取会把单通道复制三遍。因此,读取灰度图时得显示声明img = cv2.imread('gray.jpg', cv2.CV_LOAD_IMAGE_GRAYSCALE)或者img = cv2.imread('gray.jpg',0)其中0代表灰度图,1代表彩图,读出来的shape为(H,W)

案例一:skimage读取图片

下面使kaggle竞赛的一个数据集,数据集为超声波灰度图像。


import os
import numpy as np

from skimage.io import imsave, imread

data_path = '~/kaggle_Ultrasound'

image_rows = 420
image_cols = 580

train_data_path = os.path.join(data_path, 'train')
images = os.listdir(train_data_path)
# 原图为灰度图
imgs = np.ndarray((total, image_rows, image_cols), dtype=np.uint8)
for image_name in images:
	img = imread(os.path.join(train_data_path, image_name), as_grey=True)
	# 相当于扩维处理 由(420,580) --> (1,420,580)
	img = np.array([img])
	imgs[i] = img

# 可选项
np.save('imgs_train.npy', imgs)
"""
当然,在输入网络前还得进行处理,包括增加通道使格式与网络匹配、归一化处理等等。
"""

案例二,PIL读取图片

公共数据集DRIVE,分割眼球血管
文件组织格式:
在这里插入图片描述
下面的代码没有进行精简,可以只看重点

import os
import h5py
import numpy as np
from PIL import Image



def write_hdf5(arr,outfile):
  with h5py.File(outfile,"w") as f:
    f.create_dataset("image", data=arr, dtype=arr.dtype)


#------------Path of the images --------------------------------------------------------------
#train
original_imgs_train = "./DRIVE/training/images/"
groundTruth_imgs_train = "./DRIVE/training/1st_manual/"
borderMasks_imgs_train = "./DRIVE/training/mask/"
#test
original_imgs_test = "./DRIVE/test/images/"
groundTruth_imgs_test = "./DRIVE/test/1st_manual/"
borderMasks_imgs_test = "./DRIVE/test/mask/"
#---------------------------------------------------------------------------------------------

Nimgs = 20
channels = 3
height = 584
width = 565
dataset_path = "./DRIVE_datasets_training_testing/"

def get_datasets(imgs_dir,groundTruth_dir,borderMasks_dir,train_test="null"):
    imgs = np.empty((Nimgs,height,width,channels))
    groundTruth = np.empty((Nimgs,height,width))
    border_masks = np.empty((Nimgs,height,width))
    for path, subdirs, files in os.walk(imgs_dir): #list all files, directories in the path
        for i in range(len(files)):
            #original
            print("original image: ",files[i])
            img = Image.open(imgs_dir+files[i])
            imgs[i] = np.asarray(img)
            #corresponding ground truth
            groundTruth_name = files[i][0:2] + "_manual1.gif"
            print("ground truth name: ", groundTruth_name)
            g_truth = Image.open(groundTruth_dir + groundTruth_name)
            groundTruth[i] = np.asarray(g_truth)
            #corresponding border masks
            border_masks_name = ""
            if train_test=="train":
                border_masks_name = files[i][0:2] + "_training_mask.gif"
            elif train_test=="test":
                border_masks_name = files[i][0:2] + "_test_mask.gif"
            else:
                print("specify if train or test!!")
                exit()
            print("border masks name: ", border_masks_name)
            b_mask = Image.open(borderMasks_dir + border_masks_name)
            border_masks[i] = np.asarray(b_mask)

    print("imgs max: ", str(np.max(imgs)))
    print("imgs min: ", str(np.min(imgs)))
    assert(np.max(groundTruth)==255 and np.max(border_masks)==255)
    assert(np.min(groundTruth)==0 and np.min(border_masks)==0)
    print("ground truth and border masks are correctly withih pixel value range 0-255 (black-white)")
    assert(imgs.shape == (Nimgs,height,width,channels))
    groundTruth = np.reshape(groundTruth,(Nimgs,height,width,1))
    border_masks = np.reshape(border_masks,(Nimgs,height,width,1))
    assert(groundTruth.shape == (Nimgs,height,width,1))
    assert(border_masks.shape == (Nimgs,height,width,1))
    return imgs, groundTruth, border_masks

if not os.path.exists(dataset_path):
    os.makedirs(dataset_path)
#getting the training datasets
imgs_train, groundTruth_train, border_masks_train = get_datasets(original_imgs_train,groundTruth_imgs_train,borderMasks_imgs_train,"train")
print("saving train datasets")
write_hdf5(imgs_train, dataset_path + "DRIVE_dataset_imgs_train.hdf5")
write_hdf5(groundTruth_train, dataset_path + "DRIVE_dataset_groundTruth_train.hdf5")
write_hdf5(border_masks_train,dataset_path + "DRIVE_dataset_borderMasks_train.hdf5")

读取nii格式

用到的包:SimpleITK

需要注意,一个nii文件就是一个三维图片,与上面的dcm格式不同,而且SimpleITK读取出来的是( z, y ,x) 即第一维是轴向,然后是高、宽。 seg = itk.ReadImage()读取出的顺序为(x,y,z),即(width, height, depth),可用seg.GetSize() seg.GetWidth() seg.GetHeight() image.GetDepth()验证,注意!!当将其转化为数组后
segimg = sitk.GetArrayFromImage(seg),通道顺序会反向,变成(z,y,x) ,即(depth, height, width)

用到的是公共数据集LITS17,原始文件组织:
在这里插入图片描述
为了方便处理,对其进行了分类处理:

unzip ./*.zip
mkdir ct 
mkdir seg
mv ./volume-*.nii ./ct/
mv ./segmentation-*.nii ./seg/

这样将ct与分割图像分开来,也就有了下面示例代码的路径写法

ct_dir = '~/LITS17/ct/'
seg_dir = '~/LITS17/seg/'
for ct_file in os.listdir(ct_dir):

    # 将CT和金标准入读内存
    ct = sitk.ReadImage(os.path.join(ct_dir, ct_file), sitk.sitkInt16)
    # ct_array:(629, 512, 512)
    # 注意读取出来是z y x,即切片数量在最前
    # 而 origin和position读取出来的是 x y z
    ct_array = sitk.GetArrayFromImage(ct)
    # vol_values=np.unique(ct_array) 有2708个值

    seg = sitk.ReadImage(os.path.join(seg_dir, ct_file.replace('volume', 'segmentation')), sitk.sitkInt8)
    seg_array = sitk.GetArrayFromImage(seg)

猜你喜欢

转载自blog.csdn.net/normol/article/details/88179044