医学图像预处理----①小白入门
- 1、dicom格式转Niii。
- 2、显示NII格式代码用于整理数据集。(窗体窗位显示的更好)
- 3、制作表格显示主要信息,便于数据集的使用(多套数据集)
- 4、对数据集进行切片处理减少干扰(可利用ITK-SNAP查看)
- 5、重采样得到训练所需体素大小,归一化处理
真的是纯小白,一看代码就头疼,但是任务来了也不能不做,毕竟还是要毕业的。所以呢我就搜啊搜啊搜,终于经过老师指导与查阅文献终于有点处理思路,已完成预处理所有的工作在此做个汇总。本次预处理主要为图像配准时使用
1、dicom格式转Niii。
这一块比较简单,直接上代码了
import numpy as npimport shutilimport osimport SimpleITK as sitkdef dcm2nii_sitk(path_read, path_save): reader = sitk.ImageSeriesReader() seriesIDs = reader.GetGDCMSeriesIDs(path_read) N = len(seriesIDs) lens = np.zeros([N]) for i in range(N): dicom_names = reader.GetGDCMSeriesFileNames(path_read, seriesIDs[i]) lens[i] = len(dicom_names) N_MAX = np.argmax(lens) dicom_names = reader.GetGDCMSeriesFileNames(path_read, seriesIDs[N_MAX]) reader.SetFileNames(dicom_names) image = reader.Execute() if not os.path.exists(path_save): os.mkdir(path_save) sitk.WriteImage(image, path_save+'/data.nii.gz')DICOMpath = r"DATA_MD/Changhai_Int_anonymize/dc4f80590509403da78a763bd7f64baa/0a324d770c46429b9e2e08fee5b88761" #dicom文件夹路径Midpath = r"SavePng" #处理中间数据路径Resultpath = r"SaveRaw" #保存路径cases = os.listdir(DICOMpath) #获取dicom文件夹路径子文件夹名for c in cases: #遍历dicom文件夹路径子文件 path_mid = os.path.join(DICOMpath , c) #获取dicom文件夹下每一套数据的路径 dcm2nii_sitk(path_mid , Midpath ) #将dicom转换为nii,并保存在Midpath中 shutil.copy(os.path.join(Midpath , "data.nii.gz"), os.path.join(Resultpath , c + ".nii.gz")) #重新对保存后的nii文件名进行命名,并复制到Resultpath下
2、显示NII格式代码用于整理数据集。(窗体窗位显示的更好)
下面展示一些 第一次显示窗位的代码。
import torchio as tioimport numpy as npimport SimpleITK as sitkimport osimport nibabel as nibfrom matplotlib import pyplot as pltold_path="put/P_06"cases = os.listdir(old_path)i=0def window(img): win_min = -300 win_max = 350 for i in range(img.shape[0]): img[i] = 255.0 * (img[i] - win_min) / (win_max - win_min) min_index = img[i] < 0 img[i][min_index] = 0 max_index = img[i] > 255 img[i][max_index] = 255 img[i] = img[i] - img[i].min() if img[i].max()!=0 : c = float(255) / img[i].max() img[i] = img[i] * c return img.astype(np.uint8)for c in cases: #遍历p文件子文件 i = i + 1 path_mid = os.path.join(old_path ,c) #获取dicom文件夹下每一套数据的路径 img = sitk.ReadImage(path_mid) img = sitk.GetArrayFromImage(img) img = window(img) out = sitk.GetImageFromArray(img) sitk.WriteImage(out, '{0}.nii.gz'.format(i)) t1_img = tio.ScalarImage('{0}.nii.gz'.format(i)) t1_img.plot() img = nib.load(path_mid) img_hdr = img.header voxel = [img_hdr['qoffset_x']] voxel.append(img_hdr['qoffset_y']) voxel.append(img_hdr['qoffset_z']) print("----{0}的头文件信息是,坐标信息是------n{1}n{2}".format(c,img,voxel))print(i)
下面展示一些 优化显示窗位的代码。在调用函数之前写好w_width与w_center
'''调窗'''def adjustMethod1(data_resampled,w_width,w_center): val_min = w_center - (w_width / 2) val_max = w_center + (w_width / 2) data_adjusted = data_resampled.copy() data_adjusted[data_resampled < val_min] = val_min data_adjusted[data_resampled > val_max] = val_max return data_adjusted
3、制作表格显示主要信息,便于数据集的使用(多套数据集)
4、对数据集进行切片处理减少干扰(可利用ITK-SNAP查看)
下面展示一些 遍历三维图像找到边界值。
import matplotlib.pyplot as pltimport osimport SimpleITK as sitkimport cv2old_path="DATA_MD_01/p_18"cases = os.listdir(old_path)ori_data=sitk.ReadImage(os.path.join(old_path,cases[0]))pic_Size=ori_data.GetSize()data=sitk.GetArrayFromImage(ori_data)y_list = []x_list = []def cv_show(img, name): cv2.imshow(name, img) cv2.waitKey() cv2.destroyAllWindows()def norm_img(image): # 归一化像素值到(0,1)之间,且将溢出值取边界值 image = (image - MIN_BOUND) / (MAX_BOUND - MIN_BOUND) image[image > 1] = 1. image[image < 0] = 0. return imagefor i in range(0,pic_Size[2]): image=data[i,:,:] MIN_BOUND = -1000.0 MAX_BOUND = 700.0 image=norm_img(image) plt.imshow(image) plt.show() draw_img = image.copy() ret, thresh = cv2.threshold(draw_img, 0.2, 255, cv2.THRESH_BINARY) for x in range(0,512): for y in range(0,512): if thresh[x][y]==255: y_list.append(x) break for y in range(0,512): for x in range(0,512): if thresh[x][y]==255: x_list.append(y) break x_list.sort() y_list.sort() x_max_list=x_list[len(x_list)-1] y_max_list=y_list[len(y_list)-1] x_min=x_list[0] y_min=y_list[0]print(x_min,x_max_list)print(y_min,y_max_list)
下面展示一些 通过边界值找到切分边界对多余信息切除。
import torchio as tioimport numpy as npimport SimpleITK as sitkimport osdef copy_geometry(image: sitk.Image, ref: sitk.Image): image.SetOrigin(ref.GetOrigin()) image.SetDirection(ref.GetDirection()) image.SetSpacing(ref.GetSpacing()) return imageold_path="DATA_MD_01/p_03"cases = os.listdir(old_path)print(cases)i=-1for c in cases: i=i+1 # print(c) path_img=os.path.join(old_path,c) img=sitk.ReadImage(path_img) # img = sitk.GetArrayFromImage(image) # img = sitk.GetImageFromArray(img) # img_out_itk = copy_geometry(img, image) direction = img.GetDirection() pic_Origin=img.GetOrigin() pic_Spacing=img.GetSpacing() pic_Size=img.GetSize() print("原图位置:{}n原图体素:{}n原图大小{}".format(pic_Origin,pic_Spacing,pic_Size)) data_clip = img[10:510,60:390,:] data_clip.SetSpacing(pic_Spacing) data_clip.SetOrigin(pic_Origin) data_clip.SetDirection(direction) # print(data_clip) Resultpath = r"ab" # 保存路径 sitk.WriteImage(data_clip, os.path.join(Resultpath, c + ".gz")) t1_img = tio.ScalarImage(os.path.join(Resultpath, c + ".gz")) t1_img.plot() print("裁剪后位置:{}n裁剪后体素:{}n裁剪后大小{}".format(pic_Origin,pic_Spacing,pic_Size))
5、重采样得到训练所需体素大小,归一化处理
下面展示一些 遍历切除后生成的.nii格式文件进行重采样并且通过填充的形式进行调节大小格式一致。
对喽,这个地方的重采样貌似有好多方法,我自己就尝试了三种方法,把感觉最好用的拿出来分享一下
import numpy as npimport osimport SimpleITK as sitkimport torchio as tio'''调窗'''def adjustMethod1(data_resampled,w_width,w_center): val_min = w_center - (w_width / 2) val_max = w_center + (w_width / 2) data_adjusted = data_resampled.copy() data_adjusted[data_resampled < val_min] = val_min data_adjusted[data_resampled > val_max] = val_max return data_adjusted'''处理-1000——700数据'''def pretreatmentImage(image): image[image < -1000] = -1000 image[image > 700] = 700 return image'''归一化操作'''def MinMax(image): if np.max(image) - np.min(image) != 0: image =(image - np.min(image)) / (np.max(image) - np.min(image)) # 归一化 return image'''重采样'''def img_resmaple(ori_data,new_spacing=[2.5, 2.5, 2.5]): original_spacing = ori_data.GetSpacing() original_size = ori_data.GetSize() transform=sitk.Transform() transform.SetIdentity() new_shape = [ int(np.round(original_spacing[0] * original_size[0] / new_spacing[0])), int(np.round(original_spacing[1] * original_size[1] / new_spacing[1])), int(np.round(original_spacing[2] * original_size[2] / new_spacing[2])), ] # print("新的形状大小为",new_shape) resmaple = sitk.ResampleImageFilter()#初始化 #初始化一个图像重采样滤波器resampler resmaple.SetInterpolator(sitk.sitkLinear) resmaple.SetTransform(transform) resmaple.SetDefaultPixelValue(0) resmaple.SetOutputSpacing(new_spacing) resmaple.SetOutputOrigin(ori_data.GetOrigin()) resmaple.SetOutputDirection(ori_data.GetDirection()) resmaple.SetSize(new_shape) data = resmaple.Execute(ori_data) return datadef copy_geometry(image: sitk.Image, ref: sitk.Image): image.SetOrigin(ref.GetOrigin()) image.SetDirection(ref.GetDirection()) image.SetSpacing(ref.GetSpacing()) return imageif __name__ == "__main__": '''读取数据''' old_path = "ab" cases = os.listdir(old_path) for c in cases: ori_data = sitk.ReadImage(os.path.join(old_path, c)) data_res = sitk.GetArrayFromImage(ori_data) img_pre = pretreatmentImage(data_res) img_Min = MinMax(img_pre) img = sitk.GetImageFromArray(img_Min) img_out_itk = copy_geometry(img, ori_data) data_pre=img_resmaple(img_out_itk) data=sitk.GetArrayFromImage(data_pre) # print(data.shape) if data.shape[2]<200 or data.shape[1]<160 or data.shape[0]<168: # print("我进入for循环了") x_up=int((200-data.shape[2])/2) if data.shape[2]%2!=0: x_down=x_up+1 else: x_down=x_up y_left=int((160-data.shape[1])/2) if data.shape[1]%2!=0: y_right=y_left+1 else: y_right=x_up z_front= int((168 - data.shape[0]) / 2) if data.shape[0] % 2 != 0: z_after = z_front + 1 else: z_after = z_front data_pad=np.pad(data,((z_front,z_after),(y_left,y_right),(x_up,x_down)),'constant', constant_values=(0,0)) # print('------------------------',data_pad.shape) Resultpath = r"SaveRaw" # 保存路径 img = sitk.GetImageFromArray(data_pad) img_out_itk = copy_geometry(img, data_pre) sitk.WriteImage(img_out_itk, os.path.join(Resultpath,c)) '''验证存储信息是否正确''' ori = sitk.ReadImage(os.path.join(Resultpath,c )) pic_Origin=ori.GetOrigin() pic_Spacing=ori.GetSpacing() pic_Size=ori.GetSize() print("--------{}文件预处理重采样后的信息------".format(c)) print("裁剪后位置:{}n裁剪后体素:{}n裁剪后大小{}".format(pic_Origin, pic_Spacing, pic_Size)) img=sitk.GetArrayFromImage(ori) # print("数组信息为",img) t1_img = tio.ScalarImage(os.path.join(Resultpath, c)) t1_img.plot()
附加另一种重采样方法zoom或者resize;具体实现自己搞一下很简单
class Resize_img(Base): def __init__(self, shape): self.shape = shape def tf(self, img, k=0): if k == 1: img = resize(img, (img.shape[0], self.shape[0], self.shape[1], self.shape[2]), anti_aliasing=False, order=0) else: img = resize(img, (img.shape[0], self.shape[0], self.shape[1], self.shape[2]), anti_aliasing=False, order=3) return img
class Resize_img(Base): def __init__(self, shape): self.shape = shape def tf(self, img, k=0): if img.shape[-1] == 3: img = zoom(img, (1, (self.shape[0] / img.shape[1]), (self.shape[1]/img.shape[2]), 1), order=3) else: img = zoom(img, (1, (self.shape[0] / img.shape[1]), (self.shape[1] / img.shape[2])), order=3) #resize(img, (img.shape[0], self.shape[0], self.shape[1]), anti_aliasing=False, order=3) return img
良心点我也给大家附上吧
import numpy as npimport osimport SimpleITK as sitkfrom scipy.ndimage import zoomimport collectionsfrom skimage.transform import rescale, resizefrom scipy import ndimage, miscimport torchio as tio'''处理-1000——700数据'''def pretreatmentImage(image): image[image < -1000] = -1000 image[image > 700] = 700 return image'''归一化操作'''def MinMax(image): if np.max(image) - np.min(image) != 0: image = (image - np.min(image)) / (np.max(image) - np.min(image)) # 归一化 return imagedef adjustMethod1(data_resampled,w_width,w_center): val_min = w_center - (w_width / 2) val_max = w_center + (w_width / 2) data_adjusted = data_resampled.copy() data_adjusted[data_resampled < val_min] = val_min data_adjusted[data_resampled > val_max] = val_max return data_adjusted'''重采样'''def resize_01(img): original_spacing = ori_data.GetSpacing() img =zoom(img,(original_spacing[2]/2.5, original_spacing[1]/2.5, original_spacing[0]/2.5),order=3) print(img.shape) return imgdef copy_geometry(image: sitk.Image, ref: sitk.Image): image.SetOrigin(ref.GetOrigin()) image.SetDirection(ref.GetDirection()) image.SetSpacing([2.5,2.5,2.5]) return imageif __name__ == "__main__": w_width = 400 w_center = 40 old_path = "ab" cases = os.listdir(old_path) ori_data = sitk.ReadImage(os.path.join(old_path, cases[0])) data = sitk.GetArrayFromImage(ori_data) img_pre = pretreatmentImage(data) img_Min = MinMax(img_pre) img_re=resize_01(img_Min) img = sitk.GetImageFromArray(img_re) img_out_itk = copy_geometry(img,ori_data) Resultpath = r"SaveRaw" # 保存路径 sitk.WriteImage(img_out_itk, os.path.join(Resultpath,cases[0])) '''验证存储信息是否正确''' ori = sitk.ReadImage(os.path.join(Resultpath, cases[0])) print('保存的图像信息', ori) img = sitk.GetArrayFromImage(ori) # print("数组信息为",img) image = adjustMethod1(img, w_width, w_center) t1_img = tio.ScalarImage(os.path.join(Resultpath, cases[0])) t1_img.plot()
总结一下子,老师是真的牛我是真的菜,一会说一下我检索的来源与出处
如果感兴趣来自哪里就搜一下吧
- 我没用到这个,但是他给我提供了下处理流程的思路: 医学nii图像 预处理——图像裁剪 重采样 灰度区域 归一化 修改图像尺寸
- 我主要用这个调的窗体窗位Python CT图像预处理——nii格式读取、重采样、窗宽窗位设置
- 这是我模仿的这个哥哥的整体思路 CT医学图像的预处理(重采样)
- 这个是主要做输出用0填充背景让大小一致的numpy.pad()函数使用详解
重采样方法之二ZOOM(当时没看明白多搜了几个自己看看吧) - scipy.ndimage.zoom矩阵放缩
- Python scipy.ndimage.zoom用法及代码示例
- Python scipy ndimage.zoom()函数
- CT图像预处理之重采样
行了,要是有不对的记得留言告诉我毕竟我也是刚学,那我走了,时间2023年5月8日,当然我会跟着项目尽量到训练模型跑出整个项目出来啊。毕竟论文还是要写的