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 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[1]//2 if show_plot: show_2D_array('Bin efficiencies', acq_array[0,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 if show_plot: 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) # If desired, save attenuation factors if args['--output']: acq_data.write(args['--output']) # show 'bin efficiencies' acq_array = acq_data.as_array() acq_dim = acq_array.shape z = acq_dim[1]//2 if show_plot: show_2D_array('Bin efficiencies', acq_array[0,z,:,:])
def main(): # select acquisition data storage scheme AcquisitionData.set_storage_scheme(storage) # read acquisition data template acq_data_template = AcquisitionData(tmpl_file) # 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(acq_data_template) # old way (now just an alternative option) # 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 if show_plot: 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() if show_plot: 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 = (31, 111, 111) voxel_size = (3.375, 3, 3) # 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) if show_plot: # show reconstructed image at z = 20 image_array = image.as_array() show_2D_array('Reconstructed image at z = 20', image_array[20,:,:])
def main(): # direct all engine's messages to files msg_red = PET.MessageRedirector('info.txt', 'warn.txt', 'errr.txt') PET.AcquisitionData.set_storage_scheme('memory') # Create the Scatter Estimator # We can use a STIR parameter file like this # par_file_path = os.path.join(os.path.dirname(__file__), '..', '..', 'parameter_files') # se = PET.ScatterEstimator(PET.existing_filepath(par_file_path, 'scatter_estimation.par')) # However, we will just use all defaults here, and set variables below. se = PET.ScatterEstimator() prompts = PET.AcquisitionData(raw_data_file) se.set_input(prompts) se.set_attenuation_image(PET.ImageData(mu_map_file)) if randoms_data_file is None: randoms = None else: randoms = PET.AcquisitionData(randoms_data_file) se.set_randoms(randoms) if not(norm_file is None): se.set_asm(PET.AcquisitionSensitivityModel(norm_file)) if not(acf_file is None): se.set_attenuation_correction_factors(PET.AcquisitionData(acf_file)) # could set number of iterations if you want to se.set_num_iterations(1) print("number of scatter iterations that will be used: %d" % se.get_num_iterations()) se.set_output_prefix(output_prefix) se.set_up() se.process() scatter_estimate = se.get_output() ## show estimated scatter data scatter_estimate_as_array = scatter_estimate.as_array() show_2D_array('Scatter estimate', scatter_estimate_as_array[0, 0, :, :]) ## let's draw some profiles to check # we will average over all sinograms to reduce noise PET_plot_functions.plot_sinogram_profile(prompts, randoms=randoms, scatter=scatter_estimate)
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 = sirf.STIR.MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # raw data to be used as a template for the acquisition model acq_template = sirf.STIR.AcquisitionData(raw_data_file) # create image with suitable sizes image = acq_template.create_uniform_image() create_sample_image(image) image.write("simulated_image.hv") # z-pixel coordinate of the xy-cross-section to show z = image.dimensions()[0]//2 # show the phantom image image_array = image.as_array() show_2D_array('Phantom image', image_array[z,:,:]) # select acquisition model that implements the geometric # forward projection by a ray tracing matrix multiplication acq_model_matrix = sirf.STIR.SPECTUBMatrix(); acq_model = sirf.STIR.AcquisitionModelUsingMatrix(acq_model_matrix) # require same number slices and equal z-sampling for projection data & image image = image.zoom_image(zooms=(0.5, 1.0, 1.0), size=(12, -1, -1)) 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, 1, 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() middle_slice=simulated_data_as_array.shape[0]//2 show_2D_array('Forward projection', simulated_data_as_array[0, middle_slice,:,:]) print('backprojecting the forward projection...') # backproject the computed forward projection back_projected_image = acq_model.backward(simulated_data, 0, 1) back_projected_image_as_array = back_projected_image.as_array() show_2D_array('Backprojection', back_projected_image_as_array[z,:,:])
def main(): ## PET.AcquisitionData.set_storage_scheme('memory') # no info printing from the engine, warnings and errors sent to stdout msg_red = PET.MessageRedirector() # Create a template Acquisition Model #acq_template = AcquisitionData('Siemens mMR', 1, 0, 1) acq_template = PET.AcquisitionData( acq_template_filename) #q.get_uniform_copy() # create the attenuation image atten_image = PET.ImageData(acq_template) image_size = atten_image.dimensions() voxel_size = atten_image.voxel_sizes() # create a cylindrical water phantom water_cyl = PET.EllipticCylinder() water_cyl.set_length(image_size[0] * voxel_size[0]) water_cyl.set_radii((image_size[1]*voxel_size[1]*0.25, \ image_size[2]*voxel_size[2]*0.25)) water_cyl.set_origin((image_size[0] * voxel_size[0] * 0.5, 0, 0)) # add the shape to the image atten_image.add_shape(water_cyl, scale=9.687E-02) # z-pixel coordinate of the xy-crossection to show z = int(image_size[0] * 0.5) # show the phantom image atten_image_array = atten_image.as_array() show_2D_array('Attenuation image', atten_image_array[z, :, :]) # Create the activity image act_image = atten_image.clone() act_image.fill(0.0) # create the activity cylinder act_cyl = PET.EllipticCylinder() act_cyl.set_length(image_size[0] * voxel_size[0]) act_cyl.set_radii((image_size[1] * voxel_size[1] * 0.125, \ image_size[2] * voxel_size[2] * 0.125)) act_cyl.set_origin((0, image_size[1] * voxel_size[1] * 0.06, \ image_size[2] * voxel_size[2] * 0.06)) # add the shape to the image act_image.add_shape(act_cyl, scale=1) # z-pixel coordinate of the xy-crossection to show z = int(image_size[0] * 0.5) # show the phantom image act_image_array = act_image.as_array() show_2D_array('Activity image', act_image_array[z, :, :]) # Create the Single Scatter Simulation model sss = PET.SingleScatterSimulator() # Set the attenuation image sss.set_attenuation_image(atten_image) # set-up the scatter simulator sss.set_up(acq_template, act_image) # Simulate! sss_data = sss.forward(act_image) # show simulated scatter data simulated_scatter_as_array = sss_data.as_array() show_2D_array('scatter simulation', simulated_scatter_as_array[0, 0, :, :]) sss_data.write(output_file) ## let's also compute the unscattered counts (at the same low resolution) and compare acq_model = PET.AcquisitionModelUsingRayTracingMatrix() asm = PET.AcquisitionSensitivityModel(atten_image, acq_model) acq_model.set_acquisition_sensitivity(asm) acq_model.set_up(acq_template, act_image) #unscattered_data = acq_template.get_uniform_copy() unscattered_data = acq_model.forward(act_image) simulated_unscatter_as_array = unscattered_data.as_array() show_2D_array('unscattered simulation', simulated_unscatter_as_array[0, 0, :, :]) plt.figure() ax = plt.subplot(111) plt.plot(simulated_unscatter_as_array[0, 4, 0, :], label='unscattered') plt.plot(simulated_scatter_as_array[0, 4, 0, :], label='scattered') ax.legend() plt.show()
def main(): print(scanner_names()) ## 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') # raw data to be used as a template for the acquisition model acq_template = AcquisitionData(raw_data_file) # create an empty image image = acq_template.create_uniform_image(0.0, xy=111) image_size = image.dimensions() print('image size: %d by %d by %d' % image_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) if show_plot: # show the phantom image image_array = image.as_array() show_2D_array('Phantom image', image_array[z, :, :]) # 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 if show_plot: 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) print( '\n--- Computing the norm of the linear part A of acquisition model...' ) acqm_norm = acq_model.norm() image_norm = image.norm() acqd_norm = simulated_data.norm() print('\n--- The computed norm is |A| = %f, checking...' % acqm_norm) print(' image data x norm: |x| = %f' % image_norm) print(' forward projected data A x norm: |A x| = %f' % acqd_norm) acqd_bound = acqm_norm * image_norm msg = ' |A x| must be less than or equal to |A||x| = %f' if acqd_norm <= acqd_bound: msg += ' - ok\n' else: msg += ' - ???\n' print(msg % acqd_bound) if show_plot: # 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() if show_plot: show_2D_array('Backprojection', back_projected_image_as_array[z, :, :]) # backproject again, this time into pre-allocated image back_projected_image.fill(0.0) acq_model.backward(simulated_data, 0, 4, out=back_projected_image) back_projected_image_as_array = back_projected_image.as_array() if show_plot: msg = 'Backprojection into pre-allocated image' show_2D_array(msg, 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) if show_plot: 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) if show_plot: 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. try: acq_model.num_subsets = 4 acq_model.direct(image, simulated_data) except error as err: print('%s' % err.value) print('Extracting the linear acquisition model...') lin_acq_model = acq_model.get_linear_acquisition_model() lin_acq_model.direct(image, simulated_data) if show_plot: # 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. try: back_projected_image_adj = acq_model.adjoint(simulated_data) except error as err: print('%s' % err.value) print('Extracting the linear acquisition model...') lin_acq_model = acq_model.get_linear_acquisition_model() back_projected_image_adj = lin_acq_model.adjoint(simulated_data) if show_plot: 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(): ## 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 = sirf.STIR.MessageRedirector('info.txt', 'warn.txt', 'errr.txt') # raw data to be used as a template for the acquisition model acq_template = sirf.STIR.AcquisitionData(raw_data_file) # create image with suitable sizes image = acq_template.create_uniform_image() create_sample_image(image) image.write("simulated_image.hv") # create attenuation image uMap = acq_template.create_uniform_image() create_sample_image(uMap, attenuation=True) uMap.write("simulated_uMap.hv") # z-pixel coordinate of the xy-cross-section to show z = image.dimensions()[0] // 2 # show the phantom image image_array = image.as_array() show_2D_array('Phantom image', image_array[z, :, :]) # show the attenuation image uMap_array = uMap.as_array() show_2D_array('Attenuation image', uMap_array[z, :, :]) # require same number slices and equal z-sampling for projection data & image image = image.zoom_image(zooms=(0.5, 1.0, 1.0), size=(12, -1, -1)) uMap = uMap.zoom_image(zooms=(0.5, 1.0, 1.0), size=(12, -1, -1)) # select acquisition model that implements the geometric # forward projection by a ray tracing matrix multiplication acq_model_matrix = sirf.STIR.SPECTUBMatrix() acq_model_matrix.set_attenuation_image(uMap) # add attenuation acq_model = sirf.STIR.AcquisitionModelUsingMatrix(acq_model_matrix) 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, 1, simulated_data) if output_file is not None: simulated_data.write(output_file) # show simulated acquisition data simulated_data_as_array = simulated_data.as_array() middle_slice = simulated_data_as_array.shape[0] // 2 show_2D_array('Forward projection', simulated_data_as_array[0, middle_slice, :, :]) # create noisy data noisy_data = simulated_data.clone() noisy_data_as_array = np.random.poisson(simulated_data.as_array()) noisy_data.fill(noisy_data_as_array) show_2D_array('Forward projection with added noise', noisy_data_as_array[0, middle_slice, :, :]) # create objective function obj_fun = sirf.STIR.make_Poisson_loglikelihood(noisy_data) obj_fun.set_acquisition_model(acq_model) # create OSEM reconstructor object num_subsets = 30 # number of subsets for OSEM reconstruction num_subiters = 60 #number of subiterations (i.e two full iterations) OSEM_reconstructor = sirf.STIR.OSMAPOSLReconstructor() OSEM_reconstructor.set_objective_function(obj_fun) OSEM_reconstructor.set_num_subsets(num_subsets) OSEM_reconstructor.set_num_subiterations(num_subiters) # create initialisation image and set up reconstructor init_image = make_cylindrical_FOV(image.get_uniform_copy(1)) OSEM_reconstructor.set_up(init_image) # Reconstruct and show reconstructed image OSEM_reconstructor.reconstruct(init_image) out_image = OSEM_reconstructor.get_current_estimate() out_image_array = out_image.as_array() show_2D_array('Reconstructed image', out_image_array[z, :, :])
def main(): ########################################################################### # Parse input files ########################################################################### if trans_pattern is None: raise AssertionError("--trans missing") if sino_pattern is None: raise AssertionError("--sino missing") trans_files = sorted(glob(trans_pattern)) sino_files = sorted(glob(sino_pattern)) attn_files = sorted(glob(attn_pattern)) rand_files = sorted(glob(rand_pattern)) num_ms = len(sino_files) # Check some sinograms found if num_ms == 0: raise AssertionError("No sinograms found!") # Should have as many trans as sinos if num_ms != len(trans_files): raise AssertionError("#trans should match #sinos. " "#sinos = " + str(num_ms) + ", #trans = " + str(len(trans_files))) # If any rand, check num == num_ms if len(rand_files) > 0 and len(rand_files) != num_ms: raise AssertionError("#rand should match #sinos. " "#sinos = " + str(num_ms) + ", #rand = " + str(len(rand_files))) # For attn, there should be 0, 1 or num_ms images if len(attn_files) > 1 and len(attn_files) != num_ms: raise AssertionError("#attn should be 0, 1 or #sinos") ########################################################################### # Read input ########################################################################### if trans_type == "tm": trans = [reg.AffineTransformation(file) for file in trans_files] elif trans_type == "disp": trans = [ reg.NiftiImageData3DDisplacement(file) for file in trans_files ] elif trans_type == "def": trans = [reg.NiftiImageData3DDeformation(file) for file in trans_files] else: raise error("Unknown transformation type") sinos_raw = [pet.AcquisitionData(file) for file in sino_files] attns = [pet.ImageData(file) for file in attn_files] rands = [pet.AcquisitionData(file) for file in rand_files] # Loop over all sinograms sinos = [0] * num_ms for ind in range(num_ms): # If any sinograms contain negative values # (shouldn't be the case), set them to 0 sino_arr = sinos_raw[ind].as_array() if (sino_arr < 0).any(): print("Input sinogram " + str(ind) + " contains -ve elements. Setting to 0...") sinos[ind] = sinos_raw[ind].clone() sino_arr[sino_arr < 0] = 0 sinos[ind].fill(sino_arr) else: sinos[ind] = sinos_raw[ind] # If rebinning is desired segs_to_combine = 1 if args['--numSegsToCombine']: segs_to_combine = int(args['--numSegsToCombine']) views_to_combine = 1 if args['--numViewsToCombine']: views_to_combine = int(args['--numViewsToCombine']) if segs_to_combine * views_to_combine > 1: sinos[ind] = sinos[ind].rebin(segs_to_combine, views_to_combine) # only print first time if ind == 0: print(f"Rebinned sino dimensions: {sinos[ind].dimensions()}") ########################################################################### # Initialise recon image ########################################################################### if initial_estimate: image = pet.ImageData(initial_estimate) else: # Create image based on ProjData image = sinos[0].create_uniform_image(0.0, (nxny, nxny)) # If using GPU, need to make sure that image is right size. if use_gpu: dim = (127, 320, 320) spacing = (2.03125, 2.08626, 2.08626) # elif non-default spacing desired elif args['--dxdy']: dim = image.dimensions() dxdy = float(args['--dxdy']) spacing = (image.voxel_sizes()[0], dxdy, dxdy) if use_gpu or args['--dxdy']: image.initialise(dim=dim, vsize=spacing) image.fill(0.0) ########################################################################### # Set up resamplers ########################################################################### resamplers = [get_resampler(image, trans=tran) for tran in trans] ########################################################################### # Resample attenuation images (if necessary) ########################################################################### resampled_attns = None if len(attns) > 0: resampled_attns = [0] * num_ms # if using GPU, dimensions of attn and recon images have to match ref = image if use_gpu else None for i in range(len(attns)): # if we only have 1 attn image, then we need to resample into # space of each gate. However, if we have num_ms attn images, then # assume they are already in the correct position, so use None as # transformation. tran = trans[i] if len(attns) == 1 else None # If only 1 attn image, then resample that. If we have num_ms attn # images, then use each attn image of each frame. attn = attns[0] if len(attns) == 1 else attns[i] resam = get_resampler(attn, ref=ref, trans=tran) resampled_attns[i] = resam.forward(attn) ########################################################################### # Set up acquisition models ########################################################################### print("Setting up acquisition models...") if not use_gpu: acq_models = num_ms * [pet.AcquisitionModelUsingRayTracingMatrix()] else: acq_models = num_ms * [pet.AcquisitionModelUsingNiftyPET()] for acq_model in acq_models: acq_model.set_use_truncation(True) acq_model.set_cuda_verbosity(verbosity) # If present, create ASM from ECAT8 normalisation data asm_norm = None if norm_file: asm_norm = pet.AcquisitionSensitivityModel(norm_file) # Loop over each motion state for ind in range(num_ms): # Create attn ASM if necessary asm_attn = None if resampled_attns: asm_attn = get_asm_attn(sinos[ind], resampled_attns[i], acq_models[ind]) # Get ASM dependent on attn and/or norm asm = None if asm_norm and asm_attn: if ind == 0: print("ASM contains norm and attenuation...") asm = pet.AcquisitionSensitivityModel(asm_norm, asm_attn) elif asm_norm: if ind == 0: print("ASM contains norm...") asm = asm_norm elif asm_attn: if ind == 0: print("ASM contains attenuation...") asm = asm_attn if asm: acq_models[ind].set_acquisition_sensitivity(asm) if len(rands) > 0: acq_models[ind].set_background_term(rands[ind]) # Set up acq_models[ind].set_up(sinos[ind], image) ########################################################################### # Set up reconstructor ########################################################################### print("Setting up reconstructor...") # Create composition operators containing acquisition models and resamplers C = [ CompositionOperator(am, res, preallocate=True) for am, res in zip(*(acq_models, resamplers)) ] # Configure the PDHG algorithm if args['--normK'] and not args['--onlyNormK']: normK = float(args['--normK']) else: kl = [KullbackLeibler(b=sino, eta=(sino * 0 + 1e-5)) for sino in sinos] f = BlockFunction(*kl) K = BlockOperator(*C) # Calculate normK print("Calculating norm of the block operator...") normK = K.norm(iterations=10) print("Norm of the BlockOperator ", normK) if args['--onlyNormK']: exit(0) # Optionally rescale sinograms and BlockOperator using normK scale_factor = 1. / normK if args['--normaliseDataAndBlock'] else 1.0 kl = [ KullbackLeibler(b=sino * scale_factor, eta=(sino * 0 + 1e-5)) for sino in sinos ] f = BlockFunction(*kl) K = BlockOperator(*C) * scale_factor # If preconditioned if precond: def get_nonzero_recip(data): """Get the reciprocal of a datacontainer. Voxels where input == 0 will have their reciprocal set to 1 (instead of infinity)""" inv_np = data.as_array() inv_np[inv_np == 0] = 1 inv_np = 1. / inv_np data.fill(inv_np) tau = K.adjoint(K.range_geometry().allocate(1)) get_nonzero_recip(tau) tmp_sigma = K.direct(K.domain_geometry().allocate(1)) sigma = 0. * tmp_sigma get_nonzero_recip(sigma[0]) def precond_proximal(self, x, tau, out=None): """Modify proximal method to work with preconditioned tau""" pars = { 'algorithm': FGP_TV, 'input': np.asarray(x.as_array() / tau.as_array(), dtype=np.float32), 'regularization_parameter': self.lambdaReg, 'number_of_iterations': self.iterationsTV, 'tolerance_constant': self.tolerance, 'methodTV': self.methodTV, 'nonneg': self.nonnegativity, 'printingOut': self.printing } res, info = regularisers.FGP_TV(pars['input'], pars['regularization_parameter'], pars['number_of_iterations'], pars['tolerance_constant'], pars['methodTV'], pars['nonneg'], self.device) if out is not None: out.fill(res) else: out = x.copy() out.fill(res) out *= tau return out FGP_TV.proximal = precond_proximal print("Will run proximal with preconditioned tau...") # If not preconditioned else: sigma = float(args['--sigma']) # If we need to calculate default tau if args['--tau']: tau = float(args['--tau']) else: tau = 1 / (sigma * normK**2) if regularisation == 'none': G = IndicatorBox(lower=0) elif regularisation == 'FGP_TV': r_iterations = float(args['--reg_iters']) r_tolerance = 1e-7 r_iso = 0 r_nonneg = 1 r_printing = 0 device = 'gpu' if use_gpu else 'cpu' G = FGP_TV(r_alpha, r_iterations, r_tolerance, r_iso, r_nonneg, r_printing, device) else: raise error("Unknown regularisation") if precond: def PDHG_new_update(self): """Modify the PDHG update to allow preconditioning""" # save previous iteration self.x_old.fill(self.x) self.y_old.fill(self.y) # Gradient ascent for the dual variable self.operator.direct(self.xbar, out=self.y_tmp) self.y_tmp *= self.sigma self.y_tmp += self.y_old self.f.proximal_conjugate(self.y_tmp, self.sigma, out=self.y) # Gradient descent for the primal variable self.operator.adjoint(self.y, out=self.x_tmp) self.x_tmp *= -1 * self.tau self.x_tmp += self.x_old self.g.proximal(self.x_tmp, self.tau, out=self.x) # Update self.x.subtract(self.x_old, out=self.xbar) self.xbar *= self.theta self.xbar += self.x PDHG.update = PDHG_new_update # Get filename outp_file = outp_prefix if descriptive_fname: if len(attn_files) > 0: outp_file += "_wAC" if norm_file: outp_file += "_wNorm" if use_gpu: outp_file += "_wGPU" outp_file += "_Reg-" + regularisation if regularisation == 'FGP_TV': outp_file += "-alpha" + str(r_alpha) outp_file += "-riters" + str(r_iterations) if args['--normK']: outp_file += '_userNormK' + str(normK) else: outp_file += '_calcNormK' + str(normK) if args['--normaliseDataAndBlock']: outp_file += '_wDataScale' else: outp_file += '_noDataScale' if not precond: outp_file += "_sigma" + str(sigma) outp_file += "_tau" + str(tau) else: outp_file += "_wPrecond" outp_file += "_nGates" + str(len(sino_files)) if resamplers is None: outp_file += "_noMotion" pdhg = PDHG(f=f, g=G, operator=K, sigma=sigma, tau=tau, max_iteration=num_iters, update_objective_interval=update_obj_fn_interval, x_init=image, log_file=outp_file + ".log") def callback_save(iteration, objective_value, solution): """Callback function to save images""" if (iteration + 1) % save_interval == 0: out = solution if not nifti else reg.NiftiImageData(solution) out.write(outp_file + "_iters" + str(iteration + 1)) pdhg.run(iterations=num_iters, callback=callback_save, verbose=True, very_verbose=True) if visualisations: # show reconstructed image out = pdhg.get_output() out_arr = out.as_array() z = out_arr.shape[0] // 2 show_2D_array('Reconstructed image', out.as_array()[z, :, :]) pylab.show()
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 TOF bins %d (non-TOF) 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 nz, ny, nx = image.dimensions() vz, vy, vx = image.voxel_sizes() print('phantom dimensions: %dx%dx%d' % (nz, ny, nx)) print('phantom voxel sizes: %fx%fx%f' % (vz, vy, vx)) image_size = (int(nz), 111, 111) voxel_size = (float(vz), 3.0, 3.0) image = ImageData() 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) image_array = image.as_array() z = int(image_array.shape[0] / 2) if show_plot: 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[1] // 2 if show_plot: show_2D_array('Simulated acquisition data', acq_array[0, 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[1] // 2 if show_plot: show_2D_array('Simulated acquisition data', acq_array[0, 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) if show_plot: show_2D_array('Phantom', image_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 if show_plot: 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 if show_plot: 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() if show_plot: 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 if show_plot: 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() if show_plot: show_2D_array('Chain-normalized acquisition data', ad_array[0,z,:,:])