[f84ece]: / utils / dicom_utils.py

Download this file

123 lines (108 with data), 5.9 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import pydicom #for loading dicom
import SimpleITK as sitk #for loading mhd/raw
import os
import numpy as np
import scipy.ndimage
#DICOM: send path to any *.dcm file where containing dir has the other slices (dcm files), or path to the dir itself
#MHD/RAW: send path to the *.mhd file where containing die has the coorisponding *.raw file
def load_scan(path2scan):
if (path2scan.split('.')[-1] == 'mhd') or (path2scan.split('.')[-1] == 'raw'):
return load_mhd(path2scan)
elif path2scan.split('.')[-1] == 'dcm':
return load_dicom(os.path.split(path2scan)[0]) #pass containing directory
elif os.listdir(path2scan)[0].split('.')[-1] == 'dcm':
return load_dicom(path2scan)
else:
raise Exception('No valid scan [series] found in given file/directory')
def load_mhd(path2scan):
itkimage = sitk.ReadImage(path2scan)
scan = sitk.GetArrayFromImage(itkimage)
spacing = np.flip(np.array(itkimage.GetSpacing()),axis=0)
orientation = np.transpose(np.array(itkimage.GetDirection()).reshape((3, 3)))
origin = np.flip(np.array(itkimage.GetOrigin()),axis=0) #origionally in yxz format (read xy in viewers but sanved as yx)
return scan, spacing, orientation, origin, None #output in zyx format
def load_dicom(path2scan_dir):
dicom_folder = path2scan_dir
dcms = os.listdir(dicom_folder)
first_slice_data = pydicom.read_file(path2scan_dir + '\\' + dcms[0])
first_slice = first_slice_data.pixel_array
orientation = np.transpose(first_slice_data.ImageOrientationPatient) #zyx format
spacing_xy = np.array(first_slice_data.PixelSpacing, dtype=float)
spacing_z = np.float(first_slice_data.SliceThickness)
spacing = np.array([spacing_z, spacing_xy[1], spacing_xy[0]]) #zyx format
scan = np.zeros((len(dcms),first_slice.shape[0],first_slice.shape[1]))
raw_slices=[]
indexes = []
for dcm in dcms:
slice_data = pydicom.read_file(dicom_folder + '\\' + dcm)
slice_data.filename = dcm
raw_slices.append(slice_data)
indexes.append(float(slice_data.ImagePositionPatient[2]))
indexes = np.array(indexes,dtype=float)
raw_slices = [x for _, x in sorted(zip(indexes, raw_slices))]
origin = np.array(raw_slices[0][0x00200032].value) #origin is assumed to be the image location of the first slice
if origin is None:
origin = np.zeros(3)
else:
origin = np.array([origin[2],origin[1],origin[0]]) #change from x,y,z to z,y,x
for i, slice in enumerate(raw_slices):
scan[i, :, :] = slice.pixel_array
# for i, index in enumerate(indexes):
# for slice in raw_slices:
# if int(slice.InstanceNumber) == index:
# scan[i,:,:] = slice._pixel_data_numpy()
return scan, spacing, orientation, origin, raw_slices
#point to directory of folders conting dicom scans only (subdirs only), runs aon all folders..
# ref directory is used to copy the m
def save_dicom(scan, origional_raw_slices, dst_directory): # \\dfds\\ format
os.makedirs(dst_directory,exist_ok=True)
for i, slice in enumerate(origional_raw_slices):
slice.pixel_array.flat = scan[i,:,:].flat
slice.PixelData = slice.pixel_array.tobytes()
slice.save_as(os.path.join(dst_directory,slice.filename))#.dcmwrite(os.path.join(dst_directory,slice.filename),slice)
#img_array: 3d numpy matrix, z,y,x
def toDicom(save_dir, img_array, pixel_spacing, orientation):
ref_scan = pydicom.read_file('utils/ref_scan.dcm') #slice from soem real scan so we can copy the meta data
#write dcm file for each slice in scan
for i, slice in enumerate(img_array):
ref_scan.pixel_array.flat = img_array[i,:,:].flat
ref_scan.PixelData = ref_scan.pixel_array.tobytes()
ref_scan.RefdSOPInstanceUID = str(i)
ref_scan.SOPInstanceUID = str(i)
ref_scan.InstanceNumber = str(i)
ref_scan.SliceLocation = str(i)
ref_scan.ImagePositionPatient[2] = str(i*pixel_spacing[0])
ref_scan.RescaleIntercept = 0
ref_scan.Rows = img_array.shape[1]
ref_scan.Columns = img_array.shape[2]
ref_scan.PixelSpacing = [str(pixel_spacing[2]),str(pixel_spacing[1])]
ref_scan.SliceThickness = pixel_spacing[0]
#Pixel Spacing DS: ['0.681641', '0.681641']
#Image Position (Patient) DS: ['-175.500000', '-174.500000', '49']
#Image Orientation (Patient) DS: ['1.000000', '0.000000', '0.000000', '0.000000', '1.000000', '0.000000']
#Rows US: 512
#Columns US: 512
os.makedirs(save_dir,exist_ok=True)
ref_scan.save_as(os.path.join(save_dir,str(i)+'.dcm'))
def scale_scan(scan,spacing,factor=1):
resize_factor = factor * spacing
new_real_shape = scan.shape * resize_factor
new_shape = np.round(new_real_shape)
real_resize_factor = new_shape / scan.shape
new_spacing = spacing / real_resize_factor
scan_resized = scipy.ndimage.interpolation.zoom(scan, real_resize_factor, mode='nearest')
return scan_resized, resize_factor
# get the shape of an orthotope in 1:1:1 ratio AFTER scaling to the given spacing
def get_scaled_shape(shape, spacing):
new_real_shape = shape * spacing
return np.round(new_real_shape).astype(int)
def scale_vox_coord(coord, spacing, factor=1):
resize_factor = factor * spacing
return (coord*resize_factor).astype(int)
def world2vox(world_coord, spacing, orientation, origin):
world_coord = np.dot(np.linalg.inv(np.dot(orientation, np.diag(spacing))), world_coord - origin)
if orientation[0, 0] < 0:
vox_coord = (np.array([world_coord[0], world_coord[2], world_coord[1]])).astype(int)
else:
vox_coord = (np.array([world_coord[0], world_coord[1], world_coord[2]])).astype(int)
return vox_coord