Ejemplo n.º 1
0
def forward_spectral(vol,
                     proj,
                     geometry,
                     materials,
                     energy,
                     spectrum,
                     n_phot=1e8):
    """
    Simulate spectral data using labeled volume.
    """

    max_label = int(vol.max())

    if max_label != len(materials):
        raise ValueError(
            'Number of materials is not the same as the number of labels in the volume!'
        )

    # Normalize spectrum:
    spectrum /= (spectrum * energy).sum()

    # Simulate intensity images:
    lab_proj = []
    for jj in range(max_label):

        # Forward project:
        proj_j = numpy.zeros_like(proj)
        vol_j = numpy.float32(vol == (jj + 1))
        projector.forwardproject(proj_j, vol_j, geometry)

        lab_proj.append(proj_j)

    for ii in range(len(energy)):

        # Monochromatic components:
        monochrome = numpy.ones_like(proj)

        for jj in range(max_label):

            mu = linear_attenuation(energy, materials[jj]['material'],
                                    materials[jj]['density'])

            monochrome *= numpy.exp(-lab_proj[jj] * mu[ii])

        monochrome *= spectrum[ii]
        monochrome = apply_noise(monochrome, 'poisson', n_phot) / n_phot

        # Detector response is assumed to be proportional to E
        proj += energy[ii] * monochrome
Ejemplo n.º 2
0
import numpy

#%% Create volume and forward project (32 projections):
    
# Initialize images:    
proj = numpy.zeros([30, 64, 256], dtype = 'float32')

# Define a simple projection geometry:
geom = geometry.circular(src2obj = 100, det2obj = 100, det_pixel = 0.01, ang_range = [0, 360])

# Create phantom and project into proj:
vol = phantom.abstract_nudes([30, 256, 256], geom, complexity = 8)
display.slice(vol, title = 'Phantom')

# Forward project:
projector.forwardproject(proj, vol, geom)
display.slice(proj, title = 'Sinogram')

#%% Unfiltered back-project

# Make volume:
vol_rec = numpy.zeros_like(vol)

# Backproject:
projector.backproject(proj, vol_rec, geom)
display.slice(vol_rec, title = 'Backprojection')

#%% Filtered back-project

# Make volume:
vol_rec = numpy.zeros_like(vol)
Ejemplo n.º 3
0
geom_b.parameters['vol_rot'] = [0.0, 0.0, 90.0]
geom_b.parameters['vol_tra'] = [0, -0.5, 0]

# Create a phantom:
vol = phantom.random_spheroids([100, 100, 100], geom_a, 10)

display.slice(vol, dim=1, title='Phantom')

#%% Forward model:

# Initialize images:
proj_a = numpy.zeros([128, 32, 128], dtype='float32')
proj_b = numpy.zeros([128, 32, 128], dtype='float32')

# Forward project:
projector.forwardproject(proj_a, vol, geom_a)
projector.forwardproject(proj_b, vol, geom_b)

display.slice(proj_a, dim=1, title='Proj A')
display.slice(proj_b, dim=1, title='Proj B')

#%% Preview reconstructions:
geom_b = geom_a.copy()

# First volume:
vola = projector.init_volume(proj_a)
projector.FDK(proj_a, vola, geom_a)

# Second volume:
volb = projector.init_volume(proj_b)
projector.FDK(proj_b, volb, geom_b)
Ejemplo n.º 4
0
def calibrate_spectrum(projections,
                       volume,
                       geometry,
                       compound='Al',
                       density=2.7,
                       threshold=None,
                       iterations=1000,
                       n_bin=10):
    '''
    Use the projection stack of a homogeneous object to estimate system's 
    effective spectrum.
    Can be used by process.equivalent_thickness to produce an equivalent 
    thickness projection stack.
    Please, use conventional geometry. 
    '''

    #import random

    # Find the shape of the object:
    if threshold:
        t = binary_threshold(volume, mode='constant', threshold=threshold)

        segmentation = numpy.float32()
    else:
        t = binary_threshold(volume, mode='otsu')
        segmentation = numpy.float32(volume > t)

    display.slice(segmentation, dim=0, title='Segmentation')

    # Crop:
    #height = segmentation.shape[0]
    #w = 15

    #length = length[height//2-w:height//2 + w, : ,:]

    # Forward project the shape:
    print('Calculating the attenuation length.')

    length = numpy.zeros_like(projections)
    length = numpy.ascontiguousarray(length)
    projector.forwardproject(length, segmentation, geometry)

    projections[projections < 0] = 0
    intensity = numpy.exp(-projections)

    # Crop to avoid cone artefacts:
    height = intensity.shape[0] // 2
    window = 10
    intensity = intensity[height - window:height + window, :, :]
    length = length[height - window:height + window, :, :]

    # Make 1D:
    intensity = intensity[length > 0].ravel()
    length = length[length > 0].ravel()

    lmax = length.max()
    lmin = length.min()

    print('Maximum reprojected length:', lmax)
    print('Minimum reprojected length:', lmin)

    print('Selecting a random subset of points.')

    # Rare the sample to avoid slow times:
    #index = random.sample(range(length.size), 1000000)
    #length = length[index]
    #intensity = intensity[index]

    print('Computing the intensity-length transfer function.')

    # Bin number for lengthes:
    bin_n = 128
    bins = numpy.linspace(lmin, lmax, bin_n)

    # Sample the midslice:
    #segmentation = segmentation[height//2-w:height//2 + w, : ,:]
    #projections_ = projections[height//2-w:height//2 + w, : ,:]

    #import flexModel
    #ctf = flexModel.get_ctf(length.shape[::2], 'gaussian', [1, 1])
    #length = flexModel.apply_ctf(length, ctf)

    # TODO: Some cropping might be needed to avoid artefacts at the edges

    #flexUtil.slice(length, title = 'length sinogram')
    #flexUtil.slice(projections_, title = 'apparent sinogram')

    # Rebin:
    idx = numpy.digitize(length, bins)

    # Rebin length and intensity:
    length_0 = bins + (bins[1] - bins[0]) / 2
    intensity_0 = [numpy.median(intensity[idx == k]) for k in range(bin_n)]

    # In case some bins are empty:
    intensity_0 = numpy.array(intensity_0)
    length_0 = numpy.array(length_0)
    length_0 = length_0[numpy.isfinite(intensity_0)]
    intensity_0 = intensity_0[numpy.isfinite(intensity_0)]

    # Get rid of tales:
    rim = len(length_0) // 20
    length_0 = length_0[rim:-rim]
    intensity_0 = intensity_0[rim:-rim]

    # Dn't trust low counts!
    length_0 = length_0[intensity_0 > 0.05]
    intensity_0 = intensity_0[intensity_0 > 0.05]

    # Get rid of long rays (they are typically wrong...)
    #intensity_0 = intensity_0[length_0 < 35]
    #length_0 = length_0[length_0 < 35]

    # Enforce zero-one values:
    length_0 = numpy.insert(length_0, 0, 0)
    intensity_0 = numpy.insert(intensity_0, 0, 1)

    #flexUtil.plot(length_0, intensity_0, title = 'Length v.s. Intensity')

    print('Intensity-length curve rebinned.')

    print('Computing the spectrum by Expectation Maximization.')

    volts = geometry.description.get('voltage')
    if not volts: volts = 100

    energy = numpy.linspace(5, max(100, volts), n_bin)

    mu = model.linear_attenuation(energy, compound, density)
    exp_matrix = numpy.exp(-numpy.outer(length_0, mu))

    # Initial guess of the spectrum:
    spec = model.bremsstrahlung(energy, volts)
    spec *= model.scintillator_efficiency(energy, 'Si', rho=5, thickness=0.5)
    spec *= model.total_transmission(energy, 'H2O', 1, 1)
    spec *= energy
    spec /= spec.sum()

    #spec = numpy.ones_like(energy)
    #spec[0] = 0
    #spec[-1] = 0

    norm_sum = exp_matrix.sum(0)
    spec0 = spec.copy()
    #spec *= 0

    # exp_matrix at length == 0 is == 1. Sum of that is n_bin

    # EM type:
    for ii in range(iterations):
        frw = exp_matrix.dot(spec)

        epsilon = frw.max() / 100
        frw[frw < epsilon] = epsilon

        spec = spec * exp_matrix.T.dot(intensity_0 / frw) / norm_sum

        # Make sure that the total count of spec is 1 - that means intensity at length = 0 is equal to 1
        spec = spec / spec.sum()

    print('Spectrum computed.')

    #flexUtil.plot(length_0, title = 'thickness')
    #flexUtil.plot(mu, title = 'mu')
    #flexUtil.plot(_intensity, title = 'synth_counts')

    # synthetic intensity for a check:
    _intensity = exp_matrix.dot(spec)

    import matplotlib.pyplot as plt

    # Display:
    plt.figure()
    plt.semilogy(length[::200], intensity[::200], 'b.', lw=4, alpha=.8)
    plt.semilogy(length_0, intensity_0, 'g--')
    plt.semilogy(length_0, _intensity, 'r-', lw=3, alpha=.6)

    #plt.scatter(length[::100], -numpy.log(intensity[::100]), color='b', alpha=.2, s=4)
    plt.axis('tight')
    plt.title('Log intensity v.s. absorption length.')
    plt.legend(['raw', 'binned', 'solution'])
    plt.show()

    # Display:
    plt.figure()
    plt.plot(energy, spec, 'b')
    plt.plot(energy, spec0, 'r:')
    plt.axis('tight')
    plt.title('Calculated spectrum')
    plt.legend(['computed', 'initial guess'])
    plt.show()

    return energy, spec