Last active
August 10, 2024 17:10
-
-
Save prerakmody/d0803cb36baa4c4da4f6b1b57f0d1354 to your computer and use it in GitHub Desktop.
DICOM (CT and RTDOSE) mapping in matplotlib
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Step 1 - Read Dose and CT .dcm files | |
Step 2 - Resample dose to CT | |
Step 3 - Plot | |
""" | |
import pdb | |
import pydicom | |
import traceback | |
import numpy as np | |
import SimpleITK as sitk | |
from pathlib import Path | |
if __name__ == "__main__": | |
try: | |
pathCT = Path('...', '{CTFolder}') | |
pathDoseA5 = Path('...', '{RTDOSE}.dcm') | |
sliceId = 94 # [77, 94, 115] | |
if Path(pathCT).exists() and Path(pathDoseA5).exists(): | |
doseArray, ctArray = [], [] | |
doseSpacing, ctSpacing = [], [] | |
doseImagePositionPatient, ctImagePositionPatientMin, ctImagePositionPatientMax = [], [], [] | |
# Step 1.1 - Read Dose .dcm | |
try: | |
dsDose = pydicom.dcmread(pathDoseA5) | |
doseArray = dsDose.pixel_array * dsDose.DoseGridScaling | |
doseSpacing = [float(each) for each in dsDose.PixelSpacing] + [float(dsDose.SliceThickness)] | |
assert doseArray.shape[0] == float(dsDose.NumberOfFrames) | |
doseImagePositionPatient = [float(each) for each in dsDose.ImagePositionPatient] | |
except: | |
traceback.print_exc() | |
pdb.set_trace() | |
# Step 1.2 - Read CT .dcms | |
try: | |
dsCTs = [] | |
for pathCTDicom in Path(pathCT).iterdir(): dsCTs.append(pydicom.dcmread(pathCTDicom)) | |
ctSpacing = [float(each) for each in dsCTs[0].PixelSpacing] + [float(dsCTs[0].SliceThickness)] | |
dsCTs.sort(key=lambda x: x.InstanceNumber) | |
ctImagePositionPatientMin = [float(each) for each in dsCTs[0].ImagePositionPatient] | |
ctImagePositionPatientMax = [float(each) for each in dsCTs[-1].ImagePositionPatient] | |
for dsCT in dsCTs: | |
ctArray.append(dsCT.pixel_array * dsCT.RescaleSlope + dsCT.RescaleIntercept) | |
ctArray = np.array(ctArray[::-1]) | |
except: | |
traceback.print_exc() | |
pdb.set_trace() | |
if len(doseArray) and len(ctArray): | |
print ('') | |
print (f' - doseArray : {doseArray.shape} | ctArray: {ctArray.shape} ') # [Slices,H,W] | |
print (f' ---- Dose Max: {np.max(doseArray)} | Dose Min: {np.min(doseArray)} ') | |
print (f' - doseSpacing: {doseSpacing} | ctSpacing: {ctSpacing} ') # [H,W,Slices] | |
print (f' - doseImagePositionPatient: {doseImagePositionPatient} ') | |
print (f' - ctImagePositionPatientMin: {ctImagePositionPatientMin}, ctImagePositionPatientMax: {ctImagePositionPatientMax}') | |
print ('') | |
# Step 2 - Resample dose to CT | |
doseImage = sitk.GetImageFromArray(doseArray) | |
doseImage.SetSpacing(doseSpacing) | |
resampler = sitk.ResampleImageFilter() | |
resampler.SetOutputSpacing(ctSpacing) | |
resampler.SetSize(ctArray.shape[::-1]) # SimpleITK convention: [H,W,Slices], numpy convention: [Slices,H,W] | |
resampled_image = resampler.Execute(doseImage) | |
doseArrayResampled = sitk.GetArrayFromImage(resampled_image) | |
print (f' - doseArrayResampled : {doseArrayResampled.shape} | ctArray: {ctArray.shape} ') | |
dx, dy, dz = ((np.array(doseImagePositionPatient) - np.array(ctImagePositionPatientMax)) / np.array(ctSpacing)).astype(int) | |
print (f' - dx, dy, dz: {dx, dy, dz} ') | |
from scipy.ndimage.interpolation import shift | |
doseArrayResampled = shift(doseArrayResampled, (dz, dy, dx)) | |
# Step 3 - Plot | |
import matplotlib.pyplot as plt | |
import matplotlib.colors as mcolors | |
boundaries = [0 , 13.56 , 27.12 , 37.97 , 43.40 , 48.82 , 51.54 , 54.25 , 58.05 , 66.50 , 70.00 , 74.90] | |
rgbColors = np.array([[0,0,0,0], [192,192,192,128], [192,192,255,128], [128,128,255,128], [64,64,192,128], [0,0,128,128], [0,255,0,128], [0,128,0,128], [255,255,255,128], [255,255,128,128], [255,192,0,128], [255,0,0,128]])/255.0 | |
cmap = mcolors.ListedColormap(rgbColors) | |
norm = mcolors.BoundaryNorm(boundaries, cmap.N, clip=True) | |
sliceDoseA = doseArrayResampled[sliceId-1] | |
sliceCTA = ctArray[sliceId-1] | |
sliceDoseB = doseArrayResampled[sliceId] | |
sliceCTB = ctArray[sliceId] | |
sliceDoseC = doseArrayResampled[sliceId+1] | |
sliceCTC = ctArray[sliceId+1] | |
X = np.linspace(0, sliceDoseA.shape[1]-1, sliceDoseA.shape[1]) | |
Y = np.linspace(0, sliceDoseA.shape[0]-1, sliceDoseA.shape[0]) | |
X, Y = np.meshgrid(X, Y) | |
f,axarr = plt.subplots(1,3) | |
axarr[0].imshow(sliceCTA, cmap='gray', interpolation='bicubic') | |
axarr[0].contourf(X,Y,sliceDoseA, levels=boundaries, colors=rgbColors) | |
axarr[0].set_title(f'Slice={sliceId}') | |
axarr[1].imshow(sliceCTB, cmap='gray', interpolation='bicubic') | |
axarr[1].contourf(X,Y,sliceDoseB, levels=boundaries, colors=rgbColors) | |
axarr[1].set_title(f'Slice={sliceId+1}') | |
axarr[2].imshow(sliceCTC, cmap='gray', interpolation='bicubic') | |
doseIm = axarr[2].contourf(X,Y,sliceDoseC, levels=boundaries, colors=rgbColors) | |
axarr[2].set_title(f'Slice={sliceId+2}') | |
from mpl_toolkits.axes_grid1 import make_axes_locatable | |
divider = make_axes_locatable(axarr[2]) | |
cax = divider.append_axes("right", size="5%", pad=0.05) | |
f.colorbar(doseIm, cax=cax, boundaries=boundaries, norm=norm, ticks=boundaries) | |
plt.show(block=False) | |
pdb.set_trace() | |
else: | |
print (f' - ERROR: pathCT: {Path(pathCT).exists()} | pathDoseA5: {Path(pathDoseA5).exists()} ') | |
pdb.set_trace() | |
except: | |
traceback.print_exc() | |
pdb.set_trace() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment