Hi everyone,
I am trying to write code that creates an attenuation map of the CT dicom and then performs attenuation correction on the PET dicom. I assumed the input CT and PET are already registered.
I also used the bilinear transformation that was mentioned in the this article. but I couldn’t get the desired result. I will be so glad If you give me suggestions to make my code work better.
I am also aware of two drawbacks in my code:
- my attenuation map is in 140 Kev, but I need it for 511 Kev. I didn’t make adjustment to accurately reflect the attenuation at 511 keV.
- I applied the correction directly on the PET pixel array. I don’t know wether I need a sinogram for my calculations or not.
I don’t know how can I fix these issues.
note that the energy of the CT images that I used for AC is 140 kev.
import pydicom
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
class AttenuationCorretion:
def __init__(self, dicom_ct, dicom_pet):
self.dicom_ct = pydicom.dcmread(dicom_ct)
self.dicom_pet = pydicom.dcmread(dicom_pet)
self.pixel_pet_uint16 = self.dicom_pet.pixel_array.astype(np.uint16)
def hounsfield_unit(self):
self.pixel_ct_hu = self.dicom_ct.pixel_array * self.dicom_ct.RescaleSlope + \
self.dicom_ct.RescaleIntercept
def att_coe(self):
self.AC_coef = np.zeros((512,512), dtype=float)
for i, hu in enumerate(self.pixel_ct_hu.flatten()):
if hu < 0:
self.AC_coef.flat[i] = (hu * (0.151 - 0.000225)) / 1000
elif hu > 0:
self.AC_coef.flat[i] = 0.151 + (hu * 0.151 * (0.217 - 0.151)) / (1000 * (0.217 - 0.151))
return self.AC_coef.reshape(pixel_ct_hu.shape)
def down_sample(self):
self.pixel_ct_am_resized = cv.resize(self.AC_coef, (128,128))
self.pixel_ct_am_resized_gfilter = cv.GaussianBlur(self.pixel_ct_am_resized, (3,3), 0)
def nor_corr(self):
self.pixel_ac_pet = self.pixel_pet_uint16 * np.exp(self.pixel_ct_am_resized_gfilter)
self.normalized_pixel_ac_pet = (self.pixel_ac_pet - self.pixel_ac_pet.min()) / \
(self.pixel_ac_pet.max() - self.pixel_ac_pet.min())
def visualization(self):
# create figure
fig = plt.figure(figsize=(10, 7))
# setting values to rows and column variables
rows = 2
columns = 2
# Adds a subplot at the 1st position
fig.add_subplot(rows, columns, 1)
# showing image
plt.imshow(self.dicom_ct.pixel_array, cmap = 'gray')
plt.axis('off')
plt.title("CT")
# Adds a subplot at the 2nd position
fig.add_subplot(rows, columns, 2)
# showing image
plt.imshow(self.pixel_pet_uint16, cmap = 'gray')
plt.axis('off')
plt.title("nonAC PET")
# Adds a subplot at the 3rd position
fig.add_subplot(rows, columns, 3)
# showing image
plt.imshow(self.pixel_ct_am_resized_gfilter, cmap = 'gray')
plt.axis('off')
plt.title("Attenuation Map")
# Adds a subplot at the 4th position
fig.add_subplot(rows, columns, 4)
# showing image
plt.imshow(self.normalized_pixel_ac_pet, cmap = 'gray')
plt.axis('off')
plt.title("AC PET")
return fig
def process(self):
self.hounsfield_unit()
self.att_coe()
self.down_sample()
self.nor_corr()
self.visualization()
attenuation_map = AttenuationCorretion(‘ctdcm/ct_file.dcm’,
‘petdcm/pet_file.dcm’)