图像处理tool
寒假的时候主要用到的工具是SimpleITK这个python库,可以直接在pytorch中找到下载
nii文件
相信大家已经了解了nii文件,我个人的理解,nii文件就是dicom文件的多层堆叠,最后可以通过不同的工具可以得到3D的模型。但是我们一般在处理图像的用到的网络大多数都是二维输入,特别是jpg和png格式的图片最多。
dicom->nii
import SimpleITK as sitk def dcm2nii(path_read, path_save): # GetGDCMSeriesIDs读取序列号相同的dcm文件 series_id = sitk.ImageSeriesReader.GetGDCMSeriesIDs(path_read) # GetGDCMSeriesFileNames读取序列号相同dcm文件的路径 series_file_names=sitk.ImageSeriesReader.GetGDCMSeriesFileNames(path_read, series_id[0]) # print(len(series_file_names)) series_reader = sitk.ImageSeriesReader() series_reader.SetFileNames(series_file_names) image3d = series_reader.Execute() sitk.WriteImage(image3d, path_save)
有些同学可能需要最后结果是nii的,可以采用上述代码,具体实现就是把所有读到的dicom文件叠起来成为一个nii文件
nii->jpg
import SimpleITK as sitk def single_make_datalist(image_path, label_path, file_list, image_name): save_path = r"xxx" image = sitk.ReadImage(image_path) label = sitk.ReadImage(label_path) slice_num = image.GetDepth() image_array = sitk.GetArrayFromImage(image) label_array = sitk.GetArrayFromImage(label) print(len(image_array)) image_name = image_name.replace(" ", "_") if not slice_num == label.GetDepth(): raise "image's slice num is not equal to label's slice num" for index in range(0,slice_num,1): image_dicom = image_array[index] label_dicom = label_array[index] image_save_path = 'E:\\FengRu2023\\valDataList\\image\\' + image_name + '_{:0>3d}'.format(index) + '.jpg' label_save_path = 'E:\\FengRu2023\\valDataList\\label\\' + image_name + '_{:0>3d}'.format(index) + '.png' # print(image_save_path) # print(label_save_path) # plt.imsave(image_save_path, image_dicom, cmap='gray') # plt.imsave(label_save_path, label_dicom, cmap='gray') line = image_save_path + " " + label_save_path file_list.append("xxxxx" + image_name+ '_{:0>3d}'.format(index) + '.png') return
这个是比较用的多的代码,因为nii大家好像都用不了,所以可能需要转成jpg,我这里写的并不好,没有做到面向对象的标准,比如image_save_path之类的路径其实更应该写在方法的参数里,大家记得改一下哈。我这里注释了几个plt的方法,其实是为了看看做的灰度图的效果图。最后的输出结果是为了得到一个所有图片路径的list,因为可能在把数据集喂进去的时候需要一个这样的list
值得注意的是image用jpg格式,mask用png格式(虽然不知道为什么)
nii的space
图像各维度上像素之间的距离,但是,是物理层面的,有单位,一般为mm。
咱们可以设想一个魔方,这个魔方是有好多好多小块。一个小块他是有长宽高三个尺度的。那么space就是他们在现实中的尺度大小。
那么我们可以想象一个器官,他被切成了n层,每一层都是一样厚度的。space_depth就是他的物理厚度,那么n*space_depth就是它的真实的厚度了。
需要注意的是nii文件默认保存数据的顺序是[x, y, z]
nii的size
如果你懂了上面我说的space,那么你很容易就懂了我这里的size就是我的切片的数量了,很明显,这是一个三维的参量
用的方法是 image.GetSize()
nii重采样
CT图像的重采样是为了使体数据中大小不同的体素变得大小相同
def single_resample(itk_image, out_spacing=None, is_label=False): if out_spacing is None: out_spacing = [1.0, 1.0, 1.0] original_spacing = itk_image.GetSpacing() original_size = itk_image.GetSize() out_size = [ int(np.round(original_size[0] * (original_spacing[0] / out_spacing[0]))), int(np.round(original_size[1] * (original_spacing[1] / out_spacing[1]))), int(np.round(original_size[2] * (original_spacing[2] / out_spacing[2]))) ] resample = sitk.ResampleImageFilter() resample.SetOutputSpacing(out_spacing) resample.SetSize(out_size) resample.SetOutputDirection(itk_image.GetDirection()) resample.SetOutputOrigin(itk_image.GetOrigin()) resample.SetTransform(sitk.Transform()) resample.SetDefaultPixelValue(itk_image.GetPixelIDValue()) if is_label: resample.SetInterpolator(sitk.sitkNearestNeighbor) else: resample.SetInterpolator(sitk.sitkBSpline) return resample.Execute(itk_image)
重采样的意思就是将nii图片分割不同的“刀数”其实就是切的薄厚的问题,有这个函数的原因是我看有些网络好像需要输入的每一组的切片的数量是一定的。当然,你可以选择直接扔掉一部分的图片。
上述我们提到的重采样的方法是需要直接设置的,但是这里介绍的是将一个文件夹下的所有nii图片都重采样归一化到一个space。
nii重采样的归一化
def twinning_getSpace(image_path, label_path): image = sitk.ReadImage(image_path) label = sitk.ReadImage(label_path) image_space = image.GetSpacing() label_space = label.GetSpacing() # print(image_space) # print(label_space) # print("*************************") return image_space, label_space def twinning_resample(data_root): save_folder = "E:\\FengRu2023\\nii_depth\\" file_list = os.listdir(data_root) for i in range(0, len(file_list) - 1, 2): print(i) tmp_image_path = data_root + "\\" + file_list[i + 1] tmp_label_path = data_root + "\\" + file_list[i] tmp_image = sitk.ReadImage(tmp_image_path) tmp_label = sitk.ReadImage(tmp_label_path) tmp_image_space, tmp_label_space = twinning_getSpace(tmp_image_path, tmp_label_path) print("tmp_info follow") print(tmp_label_space[2]) print(tmp_label.GetDepth()) print(tmp_image.GetDepth()) print("**************") if not tmp_image.GetDepth() == tmp_label.GetDepth(): target_depth = (tmp_label_space[2] * tmp_label.GetDepth()) / tmp_image.GetDepth() target_space = [tmp_image_space[0], tmp_image_space[1], target_depth] result_label = single_resample(tmp_label, target_space, True) image_array = sitk.GetArrayFromImage(tmp_image) label_array = sitk.GetArrayFromImage(result_label) sitk.WriteImage(tmp_image, save_folder + file_list[i + 1]) sitk.WriteImage(result_label, save_folder + file_list[i]) else: image_array = sitk.GetArrayFromImage(tmp_image) label_array = sitk.GetArrayFromImage(tmp_label) sitk.WriteImage(tmp_image, save_folder + file_list[i + 1]) sitk.WriteImage(tmp_label, save_folder + file_list[i]) return
这里写的主要是辨别label和image它们的重采样归一化,但是我发现似乎数据集大部分都是正确的,除非像一些网上的数据集是没有归一化的。
获得最终的数据集list
def single_make_datalist(image_path, label_path, file_list, image_name): save_path = r"xxx" image = sitk.ReadImage(image_path) label = sitk.ReadImage(label_path) slice_num = image.GetDepth() image_array = sitk.GetArrayFromImage(image) label_array = sitk.GetArrayFromImage(label) print(len(image_array)) image_name = image_name.replace(" ", "_") if not slice_num == label.GetDepth(): raise "image's slice num is not equal to label's slice num" for index in range(0,slice_num,1): image_dicom = image_array[index] label_dicom = label_array[index] image_save_path = 'E:\\FengRu2023\\valDataList\\image\\' + image_name + '_{:0>3d}'.format(index) + '.jpg' label_save_path = 'E:\\FengRu2023\\valDataList\\label\\' + image_name + '_{:0>3d}'.format(index) + '.png' # print(image_save_path) # print(label_save_path) # plt.imsave(image_save_path, image_dicom, cmap='gray') # plt.imsave(label_save_path, label_dicom, cmap='gray') line = image_save_path + " " + label_save_path file_list.append("xxxxx" + image_name+ '_{:0>3d}'.format(index) + '.png') return def p_make_datalist(data_root): file_list = [] file_line = os.listdir(data_root) file_line.sort() for i in range(0,len(file_line) - 1,2): image_name = file_line[i + 1] label_name = file_line[i] label_path = data_root + "\\" + label_name image_path = data_root + "\\" + image_name image_name = image_name.strip('.nii') label_name = label_name.strip('.nii') # print(image_name) # print(label_name) single_make_datalist(image_path,label_path,file_list,image_name) for k in file_list: k = k.strip("\n") print(k) return
这里调用了之前的单个nii的代码。做了一个小小的批处理
重命名
def rename(data_root): old_list = os.listdir(data_root) for old_name in old_list: print(old_name) old_name = data_root + "\\" + old_name new_name = old_name.replace(' ','_') os.rename(old_name, new_name) print(new_name) return
这里用了一个os库,因为我们组的同学说我们组的代码不能出现下划线,所以进行了一个重命名的操作。其实就是一个循环。