#%% Prepro:

proj = (proj - dark) / (flat.mean(0) - dark)
proj = -numpy.log(proj)

proj = array.raw2astra(proj)

display.display_slice(proj, title='Sinogram. What else?')

#%% Recon:

vol = numpy.zeros([50, 2000, 2000], dtype='float32')

# Initialize ASTRA geometries:
vol_geom = io.astra_vol_geom(meta['geometry'], vol.shape)
proj_geom = io.astra_proj_geom(meta['geometry'], proj.shape)

# This is ASTRAAA!!!
sin_id = astra.data3d.link('-sino', proj_geom, numpy.ascontiguousarray(proj))
vol_id = astra.data3d.link('-vol', vol_geom, numpy.ascontiguousarray(vol))

cfg = astra.astra_dict('FDK_CUDA')
cfg['ReconstructionDataId'] = vol_id
cfg['ProjectionDataId'] = sin_id

alg_id = astra.algorithm.create(cfg)
astra.algorithm.run(alg_id, 1)

astra.algorithm.delete(alg_id)
astra.data3d.delete(sin_id)
Exemple #2
0
def backproject(projections, volume, geometry, algorithm='BP3D_CUDA'):
    """
    Backproject using standard ASTRA functionality. If data array is memmap, backprojection is done in blocks to save RAM.
    Args:
        projections : input numpy.array (dtype = float32) with the following dimensions: [vrt, rot, hrz]
        volume      : output numpy.array (dtype = float32) with the following dimensions: [vrt, mag, hrz]
        geometry    : geometry description - one of threee types: 'simple', 'static_offsets', 'linear_offsets'
        algorithm   : ASTRA algorithm type ['BP3D_CUDA', 'FDK_CUDA' etc.]
    """
    global settings
    block_number = settings['block_number']
    mode = settings['mode']

    # Check if projections should be subsampled:
    sam = geometry['proj_sample']
    if sum(sam) > 3:
        projections = projections[::sam[0], ::sam[1], ::sam[2]]

    # Weight correction
    prj_weight = _astra_norm_(projections, volume, geometry, algorithm)

    # If algorithm is FDK we use single-block projection unless data is a memmap
    if block_number == 1:

        projections = _contiguous_check_(projections)

        # Initialize ASTRA geometries:
        vol_geom = io.astra_vol_geom(geometry, volume.shape)
        proj_geom = io.astra_proj_geom(geometry, projections.shape)

        # Progress bar:
        pbar = _pbar_start_(1)

        projections *= prj_weight

        # ASTRA here...
        _backproject_block_add_(projections, volume, proj_geom, vol_geom,
                                algorithm)

        projections /= prj_weight

        _pbar_update_(pbar)
        _pbar_close_(pbar)

    else:
        # Here is the multi-block version:

        # Initialize ASTRA volume geometry:
        vol_geom = io.astra_vol_geom(geometry, volume.shape)

        # Progress bar:
        pbar = _pbar_start_(block_number, 'block')

        # Loop over blocks:
        for ii in range(block_number):

            # Extract a block:
            index = _block_index_(ii, block_number, projections.shape[1], mode)
            if len(index) > 0:

                proj_geom = io.astra_proj_geom(geometry, projections.shape,
                                               index)

                # number of projections in a block can vary a bit and FDK is not aware of that...
                block = projections[:, index, :] * prj_weight * len(index)

                # BP and FDK behave differently in terms of normalization:
                if (algorithm == 'BP3D_CUDA'):
                    block *= block_number

                block = _contiguous_check_(block)

                # Backproject:
                _backproject_block_add_(block, volume, proj_geom, vol_geom,
                                        algorithm)

            _pbar_update_(pbar)

        _pbar_close_(pbar)

        # ASTRA is not aware of the number of blocks:
        volume /= projections.shape[1]
Exemple #3
0
"""
Test reading Flexray raw and writing ASTRA readable
"""
#%%

from flexdata import io

#%% Read / write a geometry file:

path = '/ufs/ciacc/flexbox/al_test/90KV_no_filt/'

meta = io.read_meta(path, 'flexray')

io.write_toml(path + 'flexray.toml', meta)

#%% Read / write raw data files:

dark = io.read_tiffs(path, 'di00')
flat = io.read_tiffs(path, 'io00')
proj = io.read_tiffs(path, 'scan_')

#%% Read geometry and convert to ASTRA:

meta_1 = io.read_toml(path + 'flexray.toml')

vol_geom = io.astra_vol_geom(meta['geometry'], [100, 100, 100])
proj_geom = io.astra_proj_geom(meta['geometry'], proj.shape)

print(vol_geom)
print(proj_geom)
Exemple #4
0
def EM_step(projections, volume, geometry):
    """
    A single Expecrtation Maximization step. Supports blocking and subsets.
    """
    global settings
    norm_update = settings['norm_update']
    block_number = settings['block_number']
    bounds = settings['bounds']
    mode = settings['mode']

    prj_weight = _astra_norm_(projections, volume, geometry, 'BP3D_CUDA') * 2

    # Initialize ASTRA geometries:
    vol_geom = io.astra_vol_geom(geometry, volume.shape)

    # Norm update:
    norm = 0

    for ii in range(block_number):

        # Create index slice to address projections:
        index = _block_index_(ii, block_number, projections.shape[1], mode)
        if index is []: break

        # Extract a block:
        proj_geom = io.astra_proj_geom(geometry,
                                       projections.shape,
                                       index=index)

        # Copy data to a block or simply pass a pointer to data itself if block is one.
        if (mode == 'sequential') & (block_number == 1):
            block = projections

        else:
            block = (projections[:, index, :]).copy()

        # Reserve memory for a forward projection (keep it separate):
        resid = _contiguous_check_(numpy.zeros_like(block))

        # Forwardproject:
        _forwardproject_block_add_(resid, volume, proj_geom, vol_geom)

        # Compute residual:
        resid[resid < resid.max() / 100] = numpy.inf
        resid = (block / resid)

        # L2 norm (use the last block to update):
        if norm_update:
            res_pos = resid[resid > 0]
            norm += res_pos.std() / res_pos.mean()

        # Project
        _backproject_block_mult_(resid * prj_weight * block_number, volume,
                                 proj_geom, vol_geom, 'BP3D_CUDA')

    if norm_update:
        settings['norm'].append(norm / block_number)

    # Apply bounds
    if bounds:
        numpy.clip(volume, a_min=bounds[0], a_max=bounds[1], out=volume)
Exemple #5
0
def FISTA_step(projections, vol, vol_old, vol_t, t, geometry):
    """
    A single FISTA step. Supports blocking and subsets.
    """
    global settings
    norm_update = settings['norm_update']
    block_number = settings['block_number']
    bounds = settings['bounds']
    poisson_weight = settings['poisson_weight']
    mode = settings['mode']

    prj_weight = _astra_norm_(projections, vol, geometry, 'BP3D_CUDA')

    # Initialize ASTRA geometries:
    vol_geom = io.astra_vol_geom(geometry, vol.shape)

    vol_old[:] = vol.copy()

    t_old = t
    t = (1 + numpy.sqrt(1 + 4 * t**2)) / 2

    vol[:] = vol_t.copy()

    norm = 0

    for ii in range(block_number):

        # Create index slice to address projections:
        index = _block_index_(ii, block_number, projections.shape[1], mode)
        if index is []: break

        # Extract a block:
        proj_geom = io.astra_proj_geom(geometry,
                                       projections.shape,
                                       index=index)

        # Copy data to a block or simply pass a pointer to data itself if block is one.
        if (mode == 'sequential') & (block_number == 1):
            block = projections.copy()

        else:
            block = (projections[:, index, :]).copy()
            block = numpy.ascontiguousarray(block)

        # Forwardproject:
        _forwardproject_block_add_(block,
                                   vol_t,
                                   proj_geom,
                                   vol_geom,
                                   negative=True)

        # Take into account Poisson:
        if poisson_weight:
            # Some formula representing the effect of photon starvation...
            block *= numpy.exp(-projections[:, index, :])

        # Normalization of the backprojection (depends on ASTRA):
        block *= prj_weight * block_number

        # Apply ramp to reduce boundary effects:
        #block = block = flexData.ramp(block, 2, 5, mode = 'linear')
        #block = block = flexData.ramp(block, 0, 5, mode = 'linear')

        # L2 norm (use the last block to update):
        if norm_update:
            norm += numpy.sqrt((block**2).mean())

        # Project
        _backproject_block_add_(block, vol, proj_geom, vol_geom, 'BP3D_CUDA')

        vol_t[:] = vol + ((t_old - 1) / t) * (vol - vol_old)

    if norm_update:
        settings['norm'].append(norm / block_number)

    # Apply bounds
    if bounds is not None:
        numpy.clip(vol, a_min=bounds[0], a_max=bounds[1], out=vol)
Exemple #6
0
def L2_step(projections, volume, geometry):
    """
    A single L2 minimization step. Supports blocking and subsets.
    """
    global settings
    norm_update = settings['norm_update']
    block_number = settings['block_number']
    bounds = settings['bounds']
    poisson_weight = settings['poisson_weight']
    mode = settings['mode']

    prj_weight = _astra_norm_(projections, volume, geometry, 'BP3D_CUDA')

    # Initialize ASTRA geometries:
    vol_geom = io.astra_vol_geom(geometry, volume.shape)

    norm = 0

    for ii in range(block_number):

        # Create index slice to address projections:
        index = _block_index_(ii, block_number, projections.shape[1], mode)
        if index is []: break

        # Extract a block:
        proj_geom = io.astra_proj_geom(geometry,
                                       projections.shape,
                                       index=index)

        # The block will contain the discrepancy eventually (that's why we need a copy):
        if (mode == 'sequential') & (block_number == 1):
            block = projections.copy()

        else:
            block = (projections[:, index, :]).copy()
            block = _contiguous_check_(block)

        # Forwardproject:
        _forwardproject_block_add_(block,
                                   volume,
                                   proj_geom,
                                   vol_geom,
                                   negative=True)

        # Take into account Poisson:
        if poisson_weight:

            # Some formula representing the effect of photon starvation...
            block *= numpy.exp(-projections[:, index, :])

        block *= prj_weight * block_number

        # Apply ramp to reduce boundary effects:
        #block = array.ramp(block, 0, 5, mode = 'linear')
        #block = array.ramp(block, 2, 5, mode = 'linear')

        # L2 norm (use the last block to update):
        if norm_update:
            norm = numpy.sqrt((block**2).mean())

        # Project
        _backproject_block_add_(block, volume, proj_geom, vol_geom,
                                'BP3D_CUDA')

    if norm_update:
        settings['norm'].append(norm / block_number)

    # Apply bounds
    if bounds:
        numpy.clip(volume, a_min=bounds[0], a_max=bounds[1], out=volume)
Exemple #7
0
def MULTI_PWLS(projections,
               volume,
               geometries,
               iterations=10,
               student=False,
               weight_power=1):
    '''
    Penalized Weighted Least Squares based on multiple inputs.
    '''
    #error log:
    global settings
    norm = settings['norm']
    block_number = settings['block_number']
    mode = settings['mode']

    norm = []

    fac = volume.shape[2] * geometries[0]['img_pixel'] * numpy.sqrt(2)

    print('PWLS-ing in progress...')
    sleep(0.5)

    # Iterations:
    for ii in tqdm(range(iterations)):

        # Error:
        L_mean = 0

        #Blocks:
        for jj in range(block_number):

            # Volume update:
            vol_tmp = numpy.zeros_like(volume)
            bwp_w = numpy.zeros_like(volume)

            for kk, projs in enumerate(projections):

                index = _block_index_(jj, block_number, projs.shape[1], mode)

                proj = numpy.ascontiguousarray(projs[:, index, :])
                geom = geometries[kk]

                proj_geom = io.astra_proj_geom(geom, projs.shape, index=index)
                vol_geom = io.astra_vol_geom(geom, volume.shape)

                prj_tmp = numpy.zeros_like(proj)

                # Compute weights:
                if student:
                    fwp_w = numpy.ones_like(proj)

                else:
                    me = proj.max() * weight_power / 5
                    fwp_w = numpy.exp(-proj * weight_power / me)

                #fwp_w = scipy.ndimage.morphology.grey_erosion(fwp_w, size=(3,1,3))

                _backproject_block_add_(fwp_w, bwp_w, proj_geom, vol_geom,
                                        'BP3D_CUDA')
                _forwardproject_block_add_(prj_tmp, volume, proj_geom,
                                           vol_geom)

                prj_tmp = (proj - prj_tmp) * fwp_w / fac

                if student:
                    prj_tmp = _studentst_(prj_tmp, 5)

                _backproject_block_add_(prj_tmp, vol_tmp, proj_geom, vol_geom,
                                        'BP3D_CUDA')

                # Mean L for projection
                L_mean += (prj_tmp**2).mean()

            eps = bwp_w.max() / 1000
            bwp_w[bwp_w < eps] = eps

            volume += vol_tmp / bwp_w
            volume[volume < 0] = 0

            #print((volume<0).sum())

        norm.append(L_mean / block_number / len(projections))

    display.plot(numpy.array(norm), semilogy=True)
Exemple #8
0
def forwardproject(projections, volume, geometry):
    """
    Forwardproject using standard ASTRA functionality. If data array is memmap, projection is done in blocks to save RAM.
    Args:
        projections : output numpy.array (dtype = float32) with the following dimensions: [vrt, rot, hrz]
        volume      : input numpy.array (dtype = float32) with the following dimensions: [vrt, mag, hrz]
        geometry    : geometry description - one of threee types: 'simple', 'static_offsets', 'linear_offsets'
    """
    global settings
    block_number = settings['block_number']
    mode = settings['mode']

    # Check if projections should be subsampled:
    sam = geometry['vol_sample']
    if sum(sam) > 3:
        volume = volume[sam[0], sam[1], sam[2]]

    # Non-memmap case is a single block:
    volume = _contiguous_check_(volume)

    # Forward project will always use blocks:
    if block_number == 1:

        # Initialize ASTRA geometries:
        vol_geom = io.astra_vol_geom(geometry, volume.shape)
        proj_geom = io.astra_proj_geom(geometry, projections.shape)

        # Progress bar:
        pbar = _pbar_start_(1)

        _forwardproject_block_add_(projections, volume, proj_geom, vol_geom)

        _pbar_update_(pbar)
        _pbar_close_(pbar)

    else:
        # Multi-block:

        # Initialize ASTRA geometries:
        vol_geom = io.astra_vol_geom(geometry, volume.shape)

        # Progress bar:
        pbar = _pbar_start_(unit='block', total=block_number)

        # Loop over blocks:
        for ii in range(block_number):

            index = _block_index_(ii, block_number, projections.shape[1], mode)
            if len(index) > 0:

                # Extract a block:
                proj_geom = io.astra_proj_geom(geometry, projections.shape,
                                               index)
                block = projections[:, index, :]
                block = _contiguous_check_(block)

                # Backproject:
                _forwardproject_block_add_(block, volume, proj_geom, vol_geom)

                projections[:, index, :] = block

                _pbar_update_(pbar)

        _pbar_close_(pbar)