def scan_and_reconstruct(photons, material, phantom, scale, angles, mas=10000, alpha=0.001, correct=True): """ Simulation of the CT scanning process reconstruction = scan_and_reconstruct(photons, material, phantom, scale, angles, mas, alpha) takes the phantom data in phantom (samples x samples), scans it using the source photons and material information given, as well as the scale (in cm), number of angles, time-current product in mas, and raised-cosine power alpha for filtering. The output reconstruction is the same size as phantom.""" # convert source (photons per (mas, cm^2)) to photons # create sinogram from phantom data, with received detector values sinogram = ct_scan(photons, material, phantom, scale, angles, mas) # convert detector values into calibrated attenuation values sinogram = ct_calibrate(photons, material, sinogram, scale, correct) # Ram-Lak sinogram = ramp_filter(sinogram, scale, alpha) # Back-projection reconstruction = back_project(sinogram) # convert to Hounsfield Units reconstruction = hu(photons, material, reconstruction, scale) return reconstruction
def scan_and_backproject(photon_source, material_data, phantom, scale, angles, mas=10000, alpha=0.001): """ Scan and backproject without convolving with a RamLak filter. Use for report and demonstration only. """ # create sinogram from phantom data, with received detector values sinogram = ct_scan(photon_source, material_data, phantom, scale, angles, interpolation_order=1) # convert detector values into calibrated attenuation values total_attenuation = ct_calibrate(photon_source, material_data, sinogram, scale) # Back-projection backprojection = back_project(total_attenuation) return backprojection
def reconstruct_all(self, file, method=None, alpha=None): """ reconstruct_all( FILENAME, ALPHA ) creates a series of DICOM files for the Xtreme RSQ data. FILENAME is the base file name for the data, and ALPHA is the power of the raised cosine function used to filter the data. reconstruct_all( FILENAME, ALPHA, METHOD ) can be used to specify how the data is reconstructed. Possible options are: 'parallel' - reconstruct each slice separately using a fan to parallel conversion 'fdk' - approximate FDK algorithm for better reconstruction""" if alpha is None: alpha = 0.001 if method is None: method = 'parallel' # set frame number and DICOM UIDs for saving to multiple frames z = 1 seriesuid = pydicom.uid.generate_uid() studyuid = pydicom.uid.generate_uid() frameuid = pydicom.uid.generate_uid() time = datetime.datetime.now() # main loop over each z-fan for fan in range(0, self.scans, self.fan_scans): if method == 'fdk': # correct reconstruction using FDK method, self.fan_scans scans at a time pass else: # default method should reconstruct each slice separately sys.stdout.write('\n\n\n\n\n') for scan in range(fan + self.skip_scans, fan + self.fan_scans - self.skip_scans): if (scan < self.scans): sys.stdout.write( '\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[1A' + "Fan: " + str(fan // self.fan_scans + 1) + '\n') sys.stdout.write("Scan: " + str( (scan - self.skip_scans) % self.fan_scans + 1) + '\n') # get scan detector values, noise floor and reference sinogram, noise, ref = self.get_rsq_slice(scan) # convert detector values into calibrated attenuation values sinogram = -np.log((sinogram - noise) / (ref - noise)) # convert scan from fan to parallel sinogram = self.fan_to_parallel(sinogram) # apply Ram-Lak filter sinogram = ramp_filter(sinogram, self.scale, alpha) # back project reconstruction = back_project(sinogram) # convert to Hounsfield units reconstruction = 1000 * (reconstruction - 23.835e-3) / 23.835e-3 reconstruction = reconstruction.clip(min=-1024, max=3071) # save as dicom file create_dicom(reconstruction, file, self.scale, self.scale, z, studyuid, seriesuid, frameuid, time) z = z + 1 return
def reconstruct_all(self, file, method='parallel', alpha=0.001): """ reconstruct_all( FILENAME, ALPHA ) creates a series of DICOM files for the Xtreme RSQ data. FILENAME is the base file name for the data, and ALPHA is the power of the raised cosine function used to filter the data.""" # set frame number and DICOM UIDs for saving to multiple frames z = 1 seriesuid = pydicom.uid.generate_uid() studyuid = pydicom.uid.generate_uid() frameuid = pydicom.uid.generate_uid() time = datetime.datetime.now() # main loop over each z-fan for fan in range(0, self.scans, self.fan_scans): # reconstruct each slice separately for scan in range(fan + self.skip_scans, fan + self.fan_scans - self.skip_scans): if (scan < self.scans): # obtain slice data f, fmin, fmax = self.get_rsq_slice(scan) # remove noise f = f - fmin fmax = fmax - fmin # calibrate f = -np.log(f / fmax) # convert to a parallel sinogram p = self.fan_to_parallel(f) # filter sinogram (FBP) scale = self.scale / 10 filtered = ramp_filter(p, scale) # reconstruct reconstruction = back_project(filtered) # convert to Hu reconstruction = (reconstruction - 0.2192) * 1000 / 0.2192 reconstruction = np.where(reconstruction < -1024, -1024, reconstruction) reconstruction = np.where(reconstruction > 3072, 3072, reconstruction) plt.close() draw(reconstruction) # save as dicom file """create_dicom(x, filename, sp, sz, f) creates a new DICOM file with a name formed from the given filename and the frame number f, and containing data from x. The pixel scale is given by sp, and the frame spacing is given by sz, both of which are in mm.""" create_dicom(reconstruction, file, sp=scale, sz=scale, f=z, storage_directory='results', study_uid=studyuid, series_uid=seriesuid, time=time) z = z + 1 return