예제 #1
0
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, :, :])
예제 #2
0
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, :, :])
예제 #3
0
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, :, :])
예제 #4
0
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, :, :])
예제 #5
0
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, :, :])
예제 #6
0
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')
예제 #7
0
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, :, :])
예제 #8
0
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())
예제 #9
0
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, :, :])
예제 #10
0
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()
예제 #11
0
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)))
예제 #12
0
#%% 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.
#
예제 #13
0
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, :, :])
예제 #14
0
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, :, :])
예제 #15
0
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()
예제 #16
0
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())
예제 #17
0
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, :, :])
예제 #18
0
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, :, :])
예제 #19
0
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)