def main(): # output goes to files msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # obtain an acquisition data template template = AcquisitionData(temp_file) # create a uniform acquisition data from template acq_data = AcquisitionData(template) acq_data.fill(1.0) # create acquisition sensitivity model from ECAT8 normalization data asm = AcquisitionSensitivityModel(norm_file) asm.set_up(template) # apply normalization to the uniform acquisition data to obtain # bin efficiencies fwd_data = asm.forward(acq_data) # show bin efficiencies acq_array = fwd_data.as_array() acq_dim = acq_array.shape z = acq_dim[0] // 2 show_2D_array('Bin efficiencies', acq_array[z, :, :])
def main(): # direct all engine's messages to files msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # PET acquisition data to be read from this file raw_data_file = existing_filepath(data_path, data_file) print('raw data: %s' % raw_data_file) acq_data = AcquisitionData(raw_data_file) # copy the acquisition data into a Python array and display it acq_array = acq_data.as_array() acq_dim = acq_array.shape z = acq_dim[1] // 2 show_2D_array('Acquisition data', acq_array[0, z, :, :]) # create bin efficiencies sinograms bin_eff = acq_data.clone() bin_eff.fill(2.0) bin_eff_arr = bin_eff.as_array() bin_eff_arr[0, :, 10:50, :] = 0 show_2D_array('Bin efficiencies', bin_eff_arr[0, z, :, :]) bin_eff.fill(bin_eff_arr) # create acquisition sensitivity model from bin efficiencies asm = AcquisitionSensitivityModel(bin_eff) # apply normalization to acquisition data ad = acq_data.clone() asm.set_up(ad) asm.unnormalise(ad) ad_array = ad.as_array() show_2D_array('Normalized acquisition data', ad_array[0, z, :, :]) # create another bin efficiencies sinograms bin_eff_arr[0, :, 10:50, :] = 2.0 bin_eff_arr[0, :, 60:80, :] = 0 show_2D_array('Another bin efficiencies', bin_eff_arr[0, z, :, :]) bin_eff2 = acq_data.clone() bin_eff2.fill(bin_eff_arr) # create another acquisition sensitivity model from bin efficiencies asm2 = AcquisitionSensitivityModel(bin_eff2) # chain the two models asm12 = AcquisitionSensitivityModel(asm, asm2) asm12.set_up(acq_data) # apply the chain of models to acquisition data ad = acq_data.clone() asm12.unnormalise(ad) ad_array = ad.as_array() show_2D_array('Chain-normalized acquisition data', ad_array[0, z, :, :])
def main(): # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # create acquisition data from scanner parameters acq_data = AcquisitionData('Siemens_mMR') # set all values to 1.0 acq_data.fill(1.0) # copy the acquisition data into a Python array acq_array = acq_data.as_array() acq_dim = acq_array.shape print('acquisition data dimensions: %dx%dx%d' % acq_dim) z = acq_dim[0] // 2 show_2D_array('Acquisition data', acq_array[z, :, :])
def main(): # direct all engine's messages to files msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # obtain an acquisition data template template = AcquisitionData(temp_file) # create uniform acquisition data from template print('creating uniform acquisition data...') acq_data = AcquisitionData(template) acq_data.fill(1.0) # read attenuation image attn_image = ImageData(attn_file) attn_image_as_array = attn_image.as_array() z = attn_image_as_array.shape[0] // 2 show_2D_array('Attenuation image', attn_image_as_array[z, :, :]) # create acquisition model am = AcquisitionModelUsingRayTracingMatrix() am.set_up(template, attn_image) # create acquisition sensitivity model from attenuation image print('creating acquisition sensitivity model...') asm = AcquisitionSensitivityModel(attn_image, am) asm.set_up(template) am.set_acquisition_sensitivity(asm) ## print('projecting (please wait, may take a while)...') ## simulated_data = am.forward(attn_image) # apply attenuation to the uniform acquisition data to obtain # 'bin efficiencies' print('applying attenuation (please wait, may take a while)...') asm.unnormalise(acq_data) # show 'bin efficiencies' acq_array = acq_data.as_array() acq_dim = acq_array.shape z = acq_dim[0] // 2 show_2D_array('Bin efficiencies', acq_array[z, :, :])
def main(): # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # create listmode-to-sinograms converter object lm2sino = ListmodeToSinograms() # set input, output and template files lm2sino.set_input(list_file) lm2sino.set_output_prefix(sino_file) # the template is used to specify the sizes of the output sinogram. # see the acquisition_data_from_scanner_info demo for an example how to # make your own template file lm2sino.set_template(tmpl_file) # set interval lm2sino.set_time_interval(interval[0], interval[1]) # set some flags as examples (the following values are the defaults) lm2sino.flag_on('store_prompts') lm2sino.flag_off('interactive') # set up the converter lm2sino.set_up() # convert lm2sino.process() # get access to the sinograms acq_data = lm2sino.get_output() # copy the acquisition data into a Python array acq_array = acq_data.as_array() acq_dim = acq_array.shape print('acquisition data dimensions: %dx%dx%dx%d' % acq_dim) z = acq_dim[1] // 2 show_2D_array('Acquisition data', acq_array[0, z, :, :]) # compute randoms print('estimating randoms, please wait...') randoms = lm2sino.estimate_randoms() rnd_array = randoms.as_array() show_2D_array('Randoms', rnd_array[0, z, :, :])
def main(): # output goes to files msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # create acquisition model acq_model = AcquisitionModelUsingRayTracingMatrix() # PET acquisition data to be read from the file specified by --file option print('raw data: %s' % raw_data_file) acq_data = AcquisitionData(raw_data_file) # create filter that zeroes the image outside a cylinder of the same # diameter as the image xy-section size filter = TruncateToCylinderProcessor() # create initial image estimate image_size = (111, 111, 31) voxel_size = (3, 3, 3.375) # voxel sizes are in mm image = ImageData() image.initialise(image_size, voxel_size) image.fill(1.0) # create prior prior = QuadraticPrior() prior.set_penalisation_factor(0.5) prior.set_up(image) # create objective function obj_fun = make_Poisson_loglikelihood(acq_data) obj_fun.set_acquisition_model(acq_model) obj_fun.set_num_subsets(num_subsets) obj_fun.set_up(image) # reconstruct using your own SIRF-based implementation of OSMAPOSL image = my_osmaposl \ (image, obj_fun, prior, filter, num_subsets, num_subiterations) # show reconstructed image at z = 20 image_array = image.as_array() show_2D_array('Reconstructed image at z = 20', image_array[20, :, :]) image.write('my_image.hv')
def main(): # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # create listmode-to-sinograms converter object lm2sino = ListmodeToSinograms() # set input, output and template files lm2sino.set_input(prefix + h_file) lm2sino.set_output(s_file) lm2sino.set_template(prefix + t_file) # set interval lm2sino.set_interval(interval[0], interval[1]) # set flags lm2sino.flag_on('store_prompts') lm2sino.flag_off('interactive') try: lm2sino.flag_on('make cofee') except error as err: print('%s' % err.value) # set up the converter lm2sino.set_up() # convert lm2sino.process() # get access to the sinograms acq_data = AcquisitionData(s_file + '_f1g1d0b0.hs') # copy the acquisition data into a Python array acq_array = acq_data.as_array() acq_dim = acq_array.shape print('acquisition data dimensions: %dx%dx%d' % acq_dim) z = acq_dim[0] // 2 show_2D_array('Acquisition data', acq_array[z, :, :])
def main(): # direct all engine's messages to files msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # PET acquisition data to be read from this file raw_data_file = existing_filepath(data_path, data_file) print('raw data: %s' % raw_data_file) acq_data = AcquisitionData(raw_data_file) # copy the acquisition data into a Python array and display acq_array = acq_data.as_array() print('data dimensions: %d x %d x %d' % acq_array.shape) acq_dim = acq_array.shape z = acq_dim[0]//2 show_2D_array('Acquisition data', acq_array[z,:,:]) # rebin the acquisition data new_acq_data = acq_data.rebin(3) acq_array = new_acq_data.as_array() print('rebinned data dimensions: %d x %d x %d' % acq_array.shape) # clone the acquisition data new_acq_data = acq_data.clone() # display the cloned data acq_array = new_acq_data.as_array() show_2D_array('Cloned acquisition data', acq_array[z,:,:]) print('Checking acquisition data algebra:') s = acq_data.norm() t = acq_data * acq_data print('norm of acq_data.as_array(): %f' % numpy.linalg.norm(acq_array)) print('acq_data.norm(): %f' % s) print('sqrt(acq_data * acq_data): %f' % math.sqrt(t)) diff = new_acq_data - acq_data print('norm of acq_data.clone() - acq_data: %f' % diff.norm()) new_acq_data = acq_data * 10.0 print('norm of acq_data*10: %f' % new_acq_data.norm()) # display the scaled data acq_array = new_acq_data.as_array() show_2D_array('Scaled acquisition data', acq_array[z,:,:]) print('Checking images algebra:') image = acq_data.create_uniform_image(10.0) image_array = image.as_array() print('image dimensions: %d x %d x %d' % image_array.shape) s = image.norm() t = image * image print('norm of image.as_array(): %f' % numpy.linalg.norm(image_array)) print('image.norm(): %f' % s) print('sqrt(image * image): %f' % math.sqrt(t)) image = image*10 print('norm of image*10: %f' % image.norm()) diff = image.clone() - image print('norm of image.clone() - image: %f' % diff.norm())
def main(): # create acquisition data from scanner parameters print('creating acquisition data...') acq_data = AcquisitionData('Siemens_mMR') # set all values to 1.0 acq_data.fill(1.0) # copy the acquisition data into a Python array acq_array = acq_data.as_array() acq_dim = acq_array.shape print('acquisition data dimensions: %dx%dx%d' % acq_dim) z = acq_dim[0] // 2 show_2D_array('Acquisition data', acq_array[z, :, :]) image = acq_data.create_uniform_image(2.0) # show the image image_array = image.as_array() print('image dimensions: %dx%dx%d' % image_array.shape[2::-1]) z = int(image_array.shape[0] / 2) show_2D_array('Image', image_array[z, :, :]) print('writing acquisition data...') acq_data.write('ones') print('writing image...') image.write('twos') acq = AcquisitionData('ones.hs') acq_array = acq.as_array() acq_dim = acq_array.shape print('acquisition data dimensions: %dx%dx%d' % acq_dim) z = acq_dim[0] // 2 show_2D_array('Acquisition data', acq_array[z, :, :]) img = ImageData() img.read_from_file('twos.hv') image_array = img.as_array() print('image dimensions: %dx%dx%d' % image_array.shape[2::-1]) z = int(image_array.shape[0] / 2) show_2D_array('Image', image_array[z, :, :])
def main(): # engine's messages go to files, except error messages, which go to stdout msg_red = MessageRedirector('info.txt', 'warn.txt') # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # First step is to create AcquisitionData ("sinograms") from the # listmode file. # See the listmode_to_sinograms demo for some more information on this step. # create listmode-to-sinograms converter object # See also the listmode_to_sinograms demo lm2sino = ListmodeToSinograms() # set input, output and template files lm2sino.set_input(list_file) lm2sino.set_output_prefix(sino_file) lm2sino.set_template(tmpl_file) if count_threshold is None: interval = input_interval else: time_shift = lm2sino.get_time_at_which_num_prompts_exceeds_threshold(count_threshold) if time_shift < 0: print("No time found at which count rate exceeds " + str(time_shift) + ", not modifying interval") interval = (input_interval[0]+time_shift, input_interval[1]+time_shift) print("Time at which count rate exceeds " + str(count_threshold) + " = " + str(time_shift) + " s.") print("Input intervals: " + str(input_interval[0]) + ", " + str(input_interval[1])) print("Modified intervals: " + str(interval[0]) + ", " + str(interval[1])) # set interval lm2sino.set_time_interval(interval[0], interval[1]) # set up the converter lm2sino.set_up() # convert lm2sino.process() # Get the randoms randoms = lm2sino.estimate_randoms() # get access to the sinograms acq_data = lm2sino.get_output() # copy the acquisition data into a Python array acq_array = acq_data.as_array() acq_dim = acq_array.shape print('acquisition data dimensions: %dx%dx%dx%d' % acq_dim) if visualisations: # select a slice appropriate for the NEMA acquisition data z = 71 #z = acq_dim[0]//2 show_2D_array('Acquisition data', acq_array[0,z,:,:]) # create initial image estimate of dimensions and voxel sizes # compatible with the scanner geometry (included in the AcquisitionData # object acq_data) and initialize each voxel to 1.0 if not use_gpu: image = acq_data.create_uniform_image(1.0, nxny) # If using GPU, need to make sure that image is right size. else: image.initialise(dim=(127,320,320), vsize=(2.03125,2.08626,2.08626)) image.fill(1.0) # read attenuation image attn_image = ImageData(attn_file) if visualisations: attn_image_as_array = attn_image.as_array() show_2D_array('Attenuation image', attn_image_as_array[z,:,:]) # If gpu, make sure that attn. image dimensions match image if use_gpu: resampler = sirf.Reg.NiftyResampler() resampler.set_reference_image(image) resampler.set_floating_image(attn_image) resampler.set_interpolation_type_to_linear() set_padding_value(0.0) resampler.forward(attn_image, image) if not use_gpu: # select acquisition model that implements the geometric # forward projection by a ray tracing matrix multiplication acq_model = AcquisitionModelUsingRayTracingMatrix() acq_model.set_num_tangential_LORs(10) else: acq_model = AcquisitionModelUsingNiftyPET() # create acquisition sensitivity model from ECAT8 normalisation data asm_norm = AcquisitionSensitivityModel(norm_file) # create attenuation factors asm_attn = AcquisitionSensitivityModel(attn_image, acq_model) # converting attenuation image into attenuation factors (one for every bin) asm_attn.set_up(acq_data) ac_factors = acq_data.get_uniform_copy(value=1) print('applying attenuation (please wait, may take a while)...') asm_attn.unnormalise(ac_factors) asm_attn = AcquisitionSensitivityModel(ac_factors) # scatter estimation print('estimating scatter (this will take a while!)') scatter_estimator = ScatterEstimator() scatter_estimator.set_input(acq_data) scatter_estimator.set_attenuation_image(attn_image) scatter_estimator.set_randoms(randoms) scatter_estimator.set_asm(asm_norm) # invert attenuation factors to get the correction factors, # as this is unfortunately what a ScatterEstimator needs acf_factors=acq_data.get_uniform_copy() acf_factors.fill(1/ac_factors.as_array()) scatter_estimator.set_attenuation_correction_factors(acf_factors) scatter_estimator.set_output_prefix(sino_file + '_scatter') scatter_estimator.set_num_iterations(3) scatter_estimator.set_up() scatter_estimator.process() scatter_estimate = scatter_estimator.get_output() if visualisations: scatter_estimate_as_array = scatter_estimate.as_array() show_2D_array('Scatter estimate (first sinogram)', scatter_estimate_as_array[0, 0, :, :]) PET_plot_functions.plot_sinogram_profile(acq_data, randoms=randoms, scatter=scatter_estimate) # chain attenuation and ECAT8 normalisation asm = AcquisitionSensitivityModel(asm_norm, asm_attn) asm.set_up(acq_data) acq_model.set_acquisition_sensitivity(asm) acq_model.set_background_term(randoms + scatter_estimate) # define objective function to be maximized as # Poisson logarithmic likelihood (with linear model for mean) obj_fun = make_Poisson_loglikelihood(acq_data) obj_fun.set_acquisition_model(acq_model) # select Ordered Subsets Maximum A-Posteriori One Step Late as the # reconstruction algorithm (since we are not using a penalty, or prior, in # this example, we actually run OSEM); # this algorithm does not converge to the maximum of the objective function # but is used in practice to speed-up calculations # See the reconstruction demos for more complicated examples recon = OSMAPOSLReconstructor() recon.set_objective_function(obj_fun) recon.set_num_subsets(num_subsets) recon.set_num_subiterations(num_subiterations) # set up the reconstructor based on a sample image # (checks the validity of parameters, sets up objective function # and other objects involved in the reconstruction, which involves # computing/reading sensitivity image etc etc.) print('setting up, please wait...') recon.set_up(image) # set the initial image estimate recon.set_current_estimate(image) # reconstruct print('reconstructing, please wait...') recon.process() out = recon.get_output() if not args['--nifti']: out.write(outp_file) else: sirf.Reg.NiftiImageData(out).write(outp_file) if visualisations: # show reconstructed image image_array = out.as_array() show_2D_array('Reconstructed image', image_array[z,:,:]) pylab.show()
def main(): # engine's messages go to files msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # create acquisition model acq_model = AcquisitionModelUsingRayTracingMatrix() # PET acquisition data to be read from the file specified by --file option print('raw data: %s' % raw_data_file) acq_data = AcquisitionData(raw_data_file) # create filter that zeroes the image outside a cylinder of the same # diameter as the image xy-section size filter = TruncateToCylinderProcessor() # create initial image estimate nx = 111 ny = 111 nz = 31 image_size = (nz, ny, nx) voxel_size = (3.375, 3, 3) # sizes are in mm image = ImageData() image.initialise(image_size, voxel_size) image.fill(1.0) # apply the filter to the image filter.apply(image) # create objective function of Poisson logarithmic likelihood type # compatible with the acquisition data type obj_fun = make_Poisson_loglikelihood(acq_data) obj_fun.set_acquisition_model(acq_model) obj_fun.set_num_subsets(12) obj_fun.set_up(image) # display the initial image image_as_3D_array = image.as_array() show_2D_array('Initial image', image_as_3D_array[20,:,:]) print('computing initial objective function value...') print('objective function value: %e' % (obj_fun.value(image))) if verbose: disp = 3 if step < 0: print('NOTE: below f(x) is the negative of the objective function value') else: disp = 0 eps = 1e-6 # single precision round-off error level for iter in range(steps): # obtain gradient for subset = iter grad = obj_fun.get_subset_gradient(image, iter) # zero the gradient outside the cylindric FOV filter.apply(grad) grad_as_3D_array = grad.as_array() max_image = image_as_3D_array.max() max_grad = abs(grad_as_3D_array).max() delta = max_grad*eps # find maximal steepest ascent step parameter x in image + x*grad # such that the new image remains positive; # since image is non-negative, the step size is limited by negative # gradients: it should not exceed -image/grad = abs(image/grad) at # points where grad is negative, thus, maximal x is min(abs(image/grad)) # taken over such points # avoid division by zero at the next step grad_as_3D_array[abs(grad_as_3D_array) < delta] = delta # take the ratio of image to gradient ratio = abs(image_as_3D_array/grad_as_3D_array) # select points that are (i) inside cylindric FOV and (ii) such that # the gradient at them is negative select = numpy.logical_and(image_as_3D_array > 0, grad_as_3D_array < 0) if select.any(): # at least one point is selected maxstep = ratio[select].min() else: # no such points - use a plausible value based on 'step' and # image-to-gradient ratio maxstep = abs(step)*max_image/max_grad # at some voxels image values may be close to zero and the gradient may # also be close to zero there; hence, both may become negative because # of round-off errors; # find such voxels and freeze them exclude = numpy.logical_and(image_as_3D_array <= 0, grad_as_3D_array < 0) grad_as_3D_array[exclude] = 0 grad.fill(grad_as_3D_array) if step < 0: # find the optimal step size x fun = lambda x: -obj_fun.value(image + x*grad) x = scipy.optimize.fminbound \ (fun, 0, maxstep, xtol = 1e-4, maxfun = 3, disp = disp) else: # x is such that the relative change in image is not greater than 'step' x = step*max_image/max_grad if x > maxstep: x = maxstep # perform steepest descent step print('step %d, max change in image %e' % (iter, x*max_grad)) image = image + x*grad # filter the new image filter.apply(image) # display the current image estimate image_as_3D_array = image.as_array() show_2D_array('Current image', image_as_3D_array[20,:,:]) # quit if the new image has substantially negative values min_image = image_as_3D_array.min() if min_image < -eps: print('image minimum is negative: %e' % min_image) break if step > 0 or disp == 0: print('computing attained objective function value...') print('objective function value: %e' % (obj_fun.value(image)))
#%% reconstruct the image # First create a new image to use for the reconstruction # We will just use the original as a 'template' to have the same voxel sizes etc reconstructed_image = image.clone() # Set its values to 1 to create a uniform image reconstructed_image.fill(1) # set up the reconstructor recon.set_up(reconstructed_image) # do actual recon recon.reconstruct(reconstructed_image) #%% display of image reconstructed_array = reconstructed_image.as_array() # slice=reconstructed_array.shape[0]//3; slice_num = 9 show_2D_array('reconstructed image after 5 sub-iterations', reconstructed_array[slice_num, :, :, ]) #%% do a another set of iterations recon.reconstruct(reconstructed_image) reconstructed_array = reconstructed_image.as_array() show_2D_array('reconstructed image after 10 sub-iterations', reconstructed_array[slice_num, :, :, ]) #%% We now add a multiplicative term to the acquisition model # In PET, detector-pairs have different efficiencies. We want to include # this in our 'forward' model such that the reconstruction can # take this into account. # # The way to do this in SIRF is to include 'bin efficiencies' in the model, # i.e. one multiplicative factor for each bin in the data. #
def main(): # engine's messages go to files, except error messages, which go to stdout msg_red = MessageRedirector('info.txt', 'warn.txt') acq_template = AcquisitionData(templ_file) ## # create acquisition data from scanner parameters to be used as a template ## print('creating Siemens_mMR acquisition data...') ## acq_template = AcquisitionData('Siemens_mMR') ## acq_dim = acq_template.dimensions() ## print('acquisition data dimensions: %d sinograms, %d views, %d tang. pos.' \ ## % acq_dim) ## # rebin to reduce the acquisition data size ## print('rebinning...') ## acq_template = acq_template.rebin(15) acq_dim = acq_template.dimensions() print('acquisition data dimensions: %d sinograms, %d views, %d tang. pos.' \ % acq_dim) # create image of dimensions and voxel sizes compatible with the scanner # geometry (stored in the AcquisitionData object ad) # and initialize each voxel to 1.0 print('creating compatible phantom') image = acq_template.create_uniform_image() # show the image nx, ny, nz = image.dimensions() vx, vy, vz = image.voxel_sizes() ## print('phantom dimensions: %dx%dx%d' % (nx, ny, nz)) ## print('phantom voxel sizes: %fx%fx%f' % (vx, vy, vz)) image_size = (111, 111, int(nz)) voxel_size = (3.0, 3.0, float(vz)) image = ImageData() image.initialise(image_size, voxel_size) # create a shape shape = EllipticCylinder() shape.set_length(400) shape.set_radii((100, 40)) shape.set_origin((0, 60, 10)) # add the shape to the image image.add_shape(shape, scale=1) # add another shape shape.set_radii((30, 30)) shape.set_origin((60, -30, 10)) image.add_shape(shape, scale=1.5) # add another shape shape.set_origin((-60, -30, 10)) image.add_shape(shape, scale=0.75) image_array = image.as_array() z = int(image_array.shape[0] / 2) show_2D_array('Phantom', image_array[z, :, :]) # select acquisition model that implements the geometric # forward projection by a ray tracing matrix multiplication acq_model = AcquisitionModelUsingRayTracingMatrix() print('setting up the acquisition model...') acq_model.set_up(acq_template, image) # project the image to obtain simulated acquisition data print('projecting the phantom to create simulated acquisition data...') simulated_data = acq_model.forward(image) # copy the acquisition data into a Python array acq_array = simulated_data.as_array() acq_dim = acq_array.shape ## print('acquisition data dimensions: %dx%dx%d' % acq_dim) z = acq_dim[0] // 2 show_2D_array('Simulated acquisition data', acq_array[z, :, :]) # write acquisition data and image to files print('writing acquisition data...') simulated_data.write(acq_file) print('writing image...') image.write(img_file) # read acquisition data and image from files acq_data = AcquisitionData('simulated_data.hs') acq_array = acq_data.as_array() acq_dim = acq_array.shape ## print('acquisition data dimensions: %dx%dx%d' % acq_dim) z = acq_dim[0] // 2 show_2D_array('Simulated acquisition data', acq_array[z, :, :]) # show the image again img = ImageData() img.read_from_file('phantom.hv') image_array = img.as_array() ## print('phantom dimensions: %dx%dx%d' % image_array.shape[2::-1]) z = int(image_array.shape[0] / 2) show_2D_array('Phantom', image_array[z, :, :])
def main(): ## AcquisitionData.set_storage_scheme('mem') # no info printing from the engine, warnings and errors sent to stdout msg_red = MessageRedirector() # output goes to files ## msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # create an empty image image = ImageData() image_size = (31, 111, 111) voxel_size = (3.375, 3, 3) # voxel sizes are in mm image.initialise(image_size, voxel_size) # create a shape shape = EllipticCylinder() shape.set_length(400) shape.set_radii((40, 100)) shape.set_origin((10, 60, 0)) # add the shape to the image image.add_shape(shape, scale=1) # add another shape shape.set_radii((30, 30)) shape.set_origin((10, -30, 60)) image.add_shape(shape, scale=1.5) # add another shape shape.set_origin((10, -30, -60)) image.add_shape(shape, scale=0.75) # apply Gaussian filter filter = SeparableGaussianImageFilter() filter.set_fwhms((10, 20, 30)) filter.set_max_kernel_sizes((10, 10, 2)) filter.set_normalise() filter.set_up(image) filter.apply(image) # z-pixel coordinate of the xy-crossection to show z = int(image_size[0] / 2) # show the phantom image image_array = image.as_array() show_2D_array('Phantom image', image_array[z, :, :]) # raw data to be used as a template for the acquisition model acq_template = AcquisitionData(raw_data_file) # select acquisition model that implements the geometric # forward projection by a ray tracing matrix multiplication acq_model = AcquisitionModelUsingRayTracingMatrix() # testing bin efficiencies bin_eff = acq_template.clone() bin_eff.fill(beff) bin_eff_arr = bin_eff.as_array() # As an example, if bin efficiencies are non-trivial, set a portion of them to zero; # this should zero the corresponding portion of forward projection # and 'damage' the backprojection making it look less like the # actual image if beff != 1: bin_eff_arr[0, :, 10:50, :] = 0 show_2D_array('Bin efficiencies', bin_eff_arr[0, 0, :, :]) bin_eff.fill(bin_eff_arr) asm = AcquisitionSensitivityModel(bin_eff) acq_model.set_acquisition_sensitivity(asm) # As an example, add both an additive term and background term # (you normally wouldn't do this for real data) add = acq_template.clone() add.fill(addv) acq_model.set_additive_term(add) bck = acq_template.clone() bck.fill(back) acq_model.set_background_term(bck) print('projecting image...') # project the image to obtain simulated acquisition data # data from raw_data_file is used as a template acq_model.set_up(acq_template, image) simulated_data = acq_template.get_uniform_copy() acq_model.forward(image, 0, 4, simulated_data) # simulated_data = acq_model.forward(image, 0, 4) if output_file is not None: simulated_data.write(output_file) # show simulated acquisition data simulated_data_as_array = simulated_data.as_array() show_2D_array('Forward projection (subset 0/4)', simulated_data_as_array[0, 0, :, :]) print('backprojecting the forward projection...') # backproject the computed forward projection # note that the backprojection takes the acquisition sensitivy model asm into account as well back_projected_image = acq_model.backward(simulated_data, 0, 4) back_projected_image_as_array = back_projected_image.as_array() show_2D_array('Backprojection', back_projected_image_as_array[z, :, :]) # do same with pre-smoothing (often used for resolution modelling) print('Using some PSF modelling for comparison') smoother = SeparableGaussianImageFilter() smoother.set_fwhms((6, 11, 12)) acq_model.set_image_data_processor(smoother) acq_model.set_up(acq_template, image) simulated_data_PSF = acq_template.get_uniform_copy() acq_model.forward(image, 0, 4, simulated_data_PSF) simulated_data_PSF_as_array = simulated_data_PSF.as_array() plt.figure() plt.plot(simulated_data_as_array[0, 0, 0, :], label="no PSF") plt.plot(simulated_data_PSF_as_array[0, 0, 0, :], label="PSF") plt.title('Diff Forward projection without/ with smoothing (first view)') plt.legend() # backprojection back_projected_image_PSF = acq_model.backward(simulated_data, 0, 4) back_projected_image_PSF_as_array = back_projected_image_PSF.as_array() y = back_projected_image_as_array.shape[1] // 2 plt.figure() plt.plot(back_projected_image_as_array[z, y, :], label="no PSF") plt.plot(back_projected_image_PSF_as_array[z, y, :], label="PSF") plt.title( 'Diff Back projection without/ with smoothing (central horizontal line)' ) plt.legend() # direct is alias for the forward method for a linear AcquisitionModel # raises error if the AcquisitionModel is not linear. acq_model.direct(image, 0, 4, simulated_data) # show simulated acquisition data simulated_data_as_array_direct = simulated_data.as_array() show_2D_array('Direct projection', simulated_data_as_array_direct[0, 0, :, :]) # adjoint is an alias for the backward method for a linear AcquisitionModel # raises error if the AcquisitionModel is not linear. back_projected_image_adj = acq_model.adjoint(simulated_data, 0, 4) back_projected_image_as_array_adj = back_projected_image_adj.as_array() show_2D_array('Adjoint projection', back_projected_image_as_array_adj[z, :, :])
def main(): # engine's messages go to files, except error messages, which go to stdout msg_red = MessageRedirector('info.txt', 'warn.txt') # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # First step is to create AcquisitionData ("sinograms") from the # listmode file. # See the listmode_to_sinograms demo for some more information on this step. # create listmode-to-sinograms converter object # See also the listmode_to_sinograms demo lm2sino = ListmodeToSinograms() # set input, output and template files lm2sino.set_input(list_file) lm2sino.set_output_prefix(sino_file) lm2sino.set_template(tmpl_file) if count_threshold is None: interval = input_interval else: time_shift = lm2sino.get_time_at_which_prompt_rate_exceeds_threshold( count_threshold) if time_shift < 0: print("No time found at which count rate exceeds " + str(time_shift) + ", not modifying interval") interval = (input_interval[0] + time_shift, input_interval[1] + time_shift) print("Time at which count rate exceeds " + str(count_threshold) + " = " + str(time_shift) + " s.") print("Input intervals: " + str(input_interval[0]) + ", " + str(input_interval[1])) print("Modified intervals: " + str(interval[0]) + ", " + str(interval[1])) # set interval lm2sino.set_time_interval(interval[0], interval[1]) # set up the converter lm2sino.set_up() # convert lm2sino.process() # Get the randoms randoms = lm2sino.estimate_randoms() # get access to the sinograms acq_data = lm2sino.get_output() # copy the acquisition data into a Python array acq_array = acq_data.as_array() acq_dim = acq_array.shape print('acquisition data dimensions: %dx%dx%dx%d' % acq_dim) if visualisations: # select a slice appropriate for the NEMA acquisition data z = 71 #z = acq_dim[0]//2 show_2D_array('Acquisition data', acq_array[0, z, :, :]) # read attenuation image attn_image = ImageData(attn_file) if visualisations: attn_image_as_array = attn_image.as_array() show_2D_array('Attenuation image', attn_image_as_array[z, :, :]) # create initial image estimate of dimensions and voxel sizes # compatible with the scanner geometry (included in the AcquisitionData # object acq_data) and initialize each voxel to 1.0 image = acq_data.create_uniform_image(1.0, nxny) # select acquisition model that implements the geometric # forward projection by a ray tracing matrix multiplication acq_model = AcquisitionModelUsingRayTracingMatrix() acq_model.set_num_tangential_LORs(10) # create acquisition sensitivity model from ECAT8 normalisation data asm_norm = AcquisitionSensitivityModel(norm_file) asm_attn = AcquisitionSensitivityModel(attn_image, acq_model) # temporary fix pending attenuation offset fix in STIR: # converting attenuation into 'bin efficiency' asm_attn.set_up(acq_data) bin_eff = AcquisitionData(acq_data) bin_eff.fill(1.0) print('applying attenuation (please wait, may take a while)...') asm_attn.unnormalise(bin_eff) asm_attn = AcquisitionSensitivityModel(bin_eff) # chain attenuation and ECAT8 normalisation asm = AcquisitionSensitivityModel(asm_norm, asm_attn) acq_model.set_acquisition_sensitivity(asm) acq_model.set_background_term(randoms) # define objective function to be maximized as # Poisson logarithmic likelihood (with linear model for mean) obj_fun = make_Poisson_loglikelihood(acq_data) obj_fun.set_acquisition_model(acq_model) # select Ordered Subsets Maximum A-Posteriori One Step Late as the # reconstruction algorithm (since we are not using a penalty, or prior, in # this example, we actually run OSEM); # this algorithm does not converge to the maximum of the objective function # but is used in practice to speed-up calculations # See the reconstruction demos for more complicated examples recon = OSMAPOSLReconstructor() recon.set_objective_function(obj_fun) recon.set_num_subsets(num_subsets) recon.set_num_subiterations(num_subiterations) # set up the reconstructor based on a sample image # (checks the validity of parameters, sets up objective function # and other objects involved in the reconstruction, which involves # computing/reading sensitivity image etc etc.) print('setting up, please wait...') recon.set_up(image) # set the initial image estimate recon.set_current_estimate(image) # reconstruct print('reconstructing, please wait...') recon.process() recon.get_output().write(outp_file) if visualisations: # show reconstructed image image_array = recon.get_current_estimate().as_array() show_2D_array('Reconstructed image', image_array[z, :, :]) pylab.show()
def main(): # output goes to files msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # select acquisition data storage scheme # storage = 'file' (default): # all acquisition data generated by the script is kept in # scratch files deleted after the script terminates # storage = 'memory': # all acquisition data generated by the script is kept in RAM # (avoid if data is very large) AcquisitionData.set_storage_scheme(storage) # PET acquisition data to be read from this file raw_data_file = existing_filepath(data_path, data_file) print('raw data: %s' % raw_data_file) acq_data = AcquisitionData(raw_data_file) # copy the acquisition data into a Python array acq_array = acq_data.as_array() acq_dim = acq_array.shape z = acq_dim[0] // 2 show_2D_array('Acquisition data', acq_array[z, :, :]) # clone the acquisition data new_acq_data = acq_data.clone() # display the cloned data acq_array = new_acq_data.as_array() show_2D_array('Cloned acquisition data', acq_array[z, :, :]) print('Checking acquisition data algebra:') print('data dimensions: %d x %d x %d' % acq_array.shape) s = acq_data.norm() t = acq_data * acq_data print('norm of acq_data.as_array(): %f' % numpy.linalg.norm(acq_array)) print('acq_data.norm(): %f' % s) print('sqrt(acq_data * acq_data): %f' % math.sqrt(t)) diff = new_acq_data - acq_data print('norm of acq_data.clone() - acq_data: %f' % diff.norm()) new_acq_data = acq_data * 10.0 print('norm of acq_data*10: %f' % new_acq_data.norm()) # display the scaled data acq_array = new_acq_data.as_array() show_2D_array('Scaled acquisition data', acq_array[z, :, :]) print('Checking images algebra:') image = acq_data.create_uniform_image(10.0) image_array = image.as_array() print('image dimensions: %d x %d x %d' % image_array.shape) s = image.norm() t = image * image print('norm of image.as_array(): %f' % numpy.linalg.norm(image_array)) print('image.norm(): %f' % s) print('sqrt(image * image): %f' % math.sqrt(t)) image = image * 10 print('norm of image*10: %f' % image.norm()) diff = image.clone() - image print('norm of image.clone() - image: %f' % diff.norm())
def main(): # output goes to files msg_red = pet.MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # create acquisition model acq_model = pet.AcquisitionModelUsingRayTracingMatrix() # PET acquisition data to be read from the file specified by --file option print('raw data: %s' % raw_data_file) acq_data = pet.AcquisitionData(raw_data_file) # Noisy data for testing noisy_data = acq_data.clone() noisy_data_array = np.random.poisson(acq_data.as_array() / 10) noisy_data.fill(noisy_data_array) # create filter that zeroes the image outside a cylinder of the same # diameter as the image xy-section size filter = pet.TruncateToCylinderProcessor() # create initial image estimate image_size = (111, 111, 31) voxel_size = (3, 3, 3.375) # voxel sizes are in mm image = pet.ImageData() image.initialise(image_size, voxel_size) image.fill(1.0) filter.apply(image) # create objective function obj_fun = pet.make_Poisson_loglikelihood(noisy_data) obj_fun.set_acquisition_model(acq_model) obj_fun.set_num_subsets(num_subsets) obj_fun.set_up(image) # create new obj_fun to get sensitivity image for one subset for dePierro MAPEM obj_fun2 = pet.make_Poisson_loglikelihood(noisy_data) obj_fun2.set_acquisition_model(acq_model) obj_fun2.set_num_subsets(1) obj_fun2.set_up(image) sensitivity_image = obj_fun2.get_subset_sensitivity(0) # create new noise-free obj_fun to use for guidance in dePierro MAPEM obj_fun3 = pet.make_Poisson_loglikelihood(acq_data) obj_fun3.set_acquisition_model(acq_model) obj_fun3.set_num_subsets(num_subsets) obj_fun3.set_up(image) # uniform weights weightsUniform = np.ones([sensitivity_image.as_array().size, 27], dtype='float') weightsUniform = weightsUniform / 27.0 # noise free recon with beta = 0 for guidance image_noiseFree = my_dePierroMap \ (image, obj_fun3, 0, filter, num_subsets, num_subiterations, weightsUniform, sensitivity_image) # create a Prior for computing Bowsher weights myPrior = pr.Prior(sensitivity_image.as_array().shape) weightsBowsher = myPrior.BowshserWeights(image_noiseFree.as_array(), 10) weightsBowsher = np.float64(weightsBowsher / 10.0) # dePierro MAPEM with uniform and Bowsher weights beta = 5000.0 image_dp_b = my_dePierroMap \ (image, obj_fun, beta, filter, num_subsets, num_subiterations, weightsBowsher, sensitivity_image) image_dp_u = my_dePierroMap \ (image, obj_fun, beta, filter, num_subsets, num_subiterations, weightsUniform, sensitivity_image) # show reconstructed images at z = 20 image_dp_b_array = image_dp_b.as_array() image_dp_u_array = image_dp_u.as_array() show_2D_array('Noise free image', image_noiseFree.as_array()[20, :, :]) show_2D_array('DP Bowsher', image_dp_b_array[20, :, :]) show_2D_array('DP Uniform', image_dp_u_array[20, :, :]) show_2D_array('Difference DP', image_dp_b_array[20, :, :] - image_dp_u_array[20, :, :])
def main(): # no info printing from the engine, warnings and errors sent to stdout msg_red = MessageRedirector() # output goes to files ## msg_red = MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # create an empty image image = ImageData() image_size = (111, 111, 31) voxel_size = (3, 3, 3.375) # voxel sizes are in mm image.initialise(image_size, voxel_size) # create a shape shape = EllipticCylinder() shape.set_length(400) shape.set_radii((100, 40)) shape.set_origin((0, 60, 10)) # add the shape to the image image.add_shape(shape, scale=1) # add another shape shape.set_radii((30, 30)) shape.set_origin((60, -30, 10)) image.add_shape(shape, scale=1.5) # add another shape shape.set_origin((-60, -30, 10)) image.add_shape(shape, scale=0.75) # z-pixel coordinate of the xy-crossection to show z = int(image_size[2] / 2) # show the phantom image image_array = image.as_array() show_2D_array('Phantom image', image_array[z, :, :]) # raw data to be used as a template for the acquisition model acq_template = AcquisitionData(raw_data_file) # select acquisition model that implements the geometric # forward projection by a ray tracing matrix multiplication acq_model = AcquisitionModelUsingRayTracingMatrix() # testing bin efficiencies bin_eff = acq_template.clone() bin_eff.fill(beff) bin_eff_arr = bin_eff.as_array() # As an example, if bin efficiencies are non-trivial, set a portion of them to zero; # this should zero the corresponding portion of forward projection # and 'damage' the backprojection making it look less like the # actual image if beff != 1: bin_eff_arr[:, 10:50, :] = 0 show_2D_array('Bin efficiencies', bin_eff_arr[0, :, :]) bin_eff.fill(bin_eff_arr) asm = AcquisitionSensitivityModel(bin_eff) acq_model.set_acquisition_sensitivity(asm) # As an example, add both an additive term and background term # (you normally wouldn't do this for real data) add = acq_template.clone() add.fill(addv) acq_model.set_additive_term(add) bck = acq_template.clone() bck.fill(back) acq_model.set_background_term(bck) print('projecting image...') # project the image to obtain simulated acquisition data # data from raw_data_file is used as a template acq_model.set_up(acq_template, image) simulated_data = acq_model.forward(image) if output_file is not None: simulated_data.write('simulated_data') # show simulated acquisition data simulated_data_as_array = simulated_data.as_array() show_2D_array('Forward projection', simulated_data_as_array[0, :, :]) print('backprojecting the forward projection...') # backproject the computed forward projection # note that the backprojection takes the acquisition sensitivy model asm into account as well back_projected_image = acq_model.backward(simulated_data) back_projected_image_as_array = back_projected_image.as_array() show_2D_array('Backprojection', back_projected_image_as_array[z, :, :])
rand_file = 'my_data_rand.hs' mr_file = 'my_data_MR_SIRF.hv' # output goes to files msg_red = MessageRedirector('info.txt', 'warn.txt', 'error.txt') acq_data = AcquisitionData(data_path + sino_file) #%% # copy the acquisition data into a Python array acq_array = acq_data.as_array() print('acquisition data dimensions: %dx%dx%d' % acq_array.shape) # use a slice number for display that is appropriate for the NEMA phantom z = 71 show_2D_array('Acquisition data', acq_array[z, :, :]) # create acquisition model acq_model = AcquisitionModelUsingRayTracingMatrix() acq_model.set_num_tangential_LORs(10) #%% Correction sinograms norm_file = 'data-norm.n.hdr' asm_norm = AcquisitionSensitivityModel(data_path + norm_file) acq_model.set_acquisition_sensitivity(asm_norm) # ---------------- taken from the example----------------------------------- attn_image = ImageData(data_path + attn_file) attn_acq_model = AcquisitionModelUsingRayTracingMatrix() attn_acq_model.set_num_tangential_LORs(10) asm_attn = AcquisitionSensitivityModel(attn_image, attn_acq_model)