def skimage_radon_forward_projector(volume, geometry, proj_space, out=None):
    """Calculate forward projection using skimage.

    Parameters
    ----------
    volume : `DiscretizedSpaceElement`
        The volume to project.
    geometry : `Geometry`
        The projection geometry to use.
    proj_space : `DiscretizedSpace`
        Space in which the projections (sinograms) live.
    out : ``proj_space`` element, optional
        Element to which the result should be written.

    Returns
    -------
    sinogram : ``proj_space`` element
        Result of the forward projection. If ``out`` was given, the returned
        object is a reference to it.
    """
    # Lazy import due to significant import time
    from skimage.transform import radon

    # Check basic requirements. Fully checking should be in wrapper
    assert volume.shape[0] == volume.shape[1]

    theta = np.degrees(geometry.angles)
    skimage_range = skimage_proj_space(geometry, volume.space, proj_space)

    # Rotate volume from (x, y) to (rows, cols), then project
    sino_arr = radon(
        np.rot90(volume.asarray(), 1), theta=theta, circle=False
    )
    sinogram = skimage_range.element(sino_arr.T)

    if out is None:
        out = proj_space.element()

    with writable_array(out) as out_arr:
        point_collocation(
            clamped_interpolation(skimage_range, sinogram),
            proj_space.grid.meshgrid,
            out=out_arr,
        )

    scale = volume.space.cell_sides[0]
    out *= scale

    return out
Exemple #2
0
def astra_cuda_forward_projector(vol_data, geometry, proj_space, out=None):
    """Run an ASTRA forward projection on the given data using the GPU.

    Parameters
    ----------
    vol_data : `DiscreteLpElement`
        Volume data to which the projector is applied
    geometry : `Geometry`
        Geometry defining the tomographic setup
    proj_space : `DiscreteLp`
        Space to which the calling operator maps
    out : ``proj_space`` element, optional
        Element of the projection space to which the result is written. If
        ``None``, an element in ``proj_space`` is created.

    Returns
    -------
    out : ``proj_space`` element
        Projection data resulting from the application of the projector.
        If ``out`` was provided, the returned object is a reference to it.
    """
    if not isinstance(vol_data, DiscreteLpElement):
        raise TypeError('volume data {!r} is not a DiscreteLpElement '
                        'instance'.format(vol_data))
    if not isinstance(geometry, Geometry):
        raise TypeError('geometry  {!r} is not a Geometry instance'
                        ''.format(geometry))
    if vol_data.ndim != geometry.ndim:
        raise ValueError('dimensions {} of volume data and {} of geometry '
                         'do not match'
                         ''.format(vol_data.ndim, geometry.ndim))
    if not isinstance(proj_space, DiscreteLp):
        raise TypeError('projection space {!r} is not a DiscreteLp '
                        'instance'.format(proj_space))
    if out is not None:
        if not isinstance(out, DiscreteLpElement):
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpElement instance'.format(out))

    ndim = vol_data.ndim

    # Create astra geometries
    vol_geom = astra_volume_geometry(vol_data.space)
    proj_geom = astra_projection_geometry(geometry)

    # Create ASTRA data structures

    # In the case dim == 3, we need to swap axes, so can't perform the FP
    # in-place
    vol_id = astra_data(vol_geom, datatype='volume', data=vol_data,
                        allow_copy=True)

    # Create projector
    proj_id = astra_projector('nearest', vol_geom, proj_geom, ndim,
                              impl='cuda')

    if ndim == 2:
        if out is None:
            out = proj_space.element()

        # Wrap the array in correct dtype etc if needed
        with writable_array(out, dtype='float32', order='C') as arr:
            sino_id = astra_data(proj_geom, datatype='projection', data=arr)

            # Create algorithm
            algo_id = astra_algorithm('forward', ndim, vol_id, sino_id,
                                      proj_id=proj_id, impl='cuda')

            # Run algorithm
            astra.algorithm.run(algo_id)
    elif ndim == 3:
        sino_id = astra_data(proj_geom, datatype='projection',
                             ndim=proj_space.ndim)

        # Create algorithm
        algo_id = astra_algorithm('forward', ndim, vol_id, sino_id,
                                  proj_id=proj_id, impl='cuda')

        # Run algorithm
        astra.algorithm.run(algo_id)

        if out is None:
            out = proj_space.element(np.rollaxis(astra.data3d.get(sino_id),
                                                 0, 3))
        else:
            out[:] = np.rollaxis(astra.data3d.get(sino_id), 0, 3)
    else:
        raise RuntimeError('unknown ndim')

    # Fix inconsistent scaling
    if isinstance(geometry, Parallel2dGeometry):
        # parallel2d scales with pixel stride
        out *= 1 / float(geometry.det_partition.cell_sides[0])

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    if ndim == 2:
        astra.data2d.delete((vol_id, sino_id))
        astra.projector.delete(proj_id)
    else:
        astra.data3d.delete((vol_id, sino_id))
        astra.projector3d.delete(proj_id)

    return out
def skimage_radon_back_projector(sinogram, geometry, vol_space, out=None):
    """Calculate forward projection using skimage.

    Parameters
    ----------
    sinogram : `DiscretizedSpaceElement`
        Sinogram (projections) to backproject.
    geometry : `Geometry`
        The projection geometry to use.
    vol_space : `DiscretizedSpace`
        Space in which reconstructed volumes live.
    out : ``vol_space`` element, optional
        An element to which the result should be written.

    Returns
    -------
    volume : ``vol_space`` element
        Result of the back-projection. If ``out`` was given, the returned
        object is a reference to it.
    """
    # Lazy import due to significant import time
    from skimage.transform import iradon

    theta = np.degrees(geometry.angles)
    skimage_range = skimage_proj_space(geometry, vol_space, sinogram.space)

    skimage_sinogram = skimage_range.element()
    with writable_array(skimage_sinogram) as sino_arr:
        point_collocation(
            clamped_interpolation(sinogram.space, sinogram),
            skimage_range.grid.meshgrid,
            out=sino_arr,
        )

    if out is None:
        out = vol_space.element()
    else:
        # Only do asserts here since these are backend functions
        assert out in vol_space

    # Rotate back from (rows, cols) to (x, y), then back-project (no filter)
    backproj = iradon(
        skimage_sinogram.asarray().T,
        theta,
        output_size=vol_space.shape[0],
        filter=None,
        circle=False,
    )
    out[:] = np.rot90(backproj, -1)

    # Empirically determined value, gives correct scaling
    scaling_factor = 4 * geometry.motion_params.length / (2 * np.pi)

    # Correct in case of non-weighted spaces
    proj_volume = np.prod(sinogram.space.partition.extent)
    proj_size = sinogram.space.partition.size
    proj_weighting = proj_volume / proj_size

    scaling_factor *= sinogram.space.weighting.const / proj_weighting
    scaling_factor /= vol_space.weighting.const / vol_space.cell_volume

    # Correctly scale the output
    out *= scaling_factor

    return out
Exemple #4
0
def astra_cuda_back_projector(proj_data, geometry, reco_space, out=None):
    """Run an ASTRA backward projection on the given data using the GPU.

    Parameters
    ----------
    proj_data : `DiscreteLp` element
        Projection data to which the backward projector is applied
    geometry : `Geometry`
        Geometry defining the tomographic setup
    reco_space : `DiscreteLp`
        Space to which the calling operator maps
    out : ``reco_space`` element, optional
        Element of the reconstruction space to which the result is written.
        If ``None``, an element in ``reco_space`` is created.

    Returns
    -------
    out : ``reco_space`` element
        Reconstruction data resulting from the application of the backward
        projector. If ``out`` was provided, the returned object is a
        reference to it.
        """
    if not isinstance(proj_data, DiscreteLpElement):
        raise TypeError('projection data {!r} is not a DiscreteLpElement '
                        'instance'.format(proj_data))
    if not isinstance(geometry, Geometry):
        raise TypeError('geometry  {!r} is not a Geometry instance'
                        ''.format(geometry))
    if not isinstance(reco_space, DiscreteLp):
        raise TypeError('reconstruction space {!r} is not a DiscreteLp '
                        'instance'.format(reco_space))
    if reco_space.ndim != geometry.ndim:
        raise ValueError('dimensions {} of reconstruction space and {} of '
                         'geometry do not match'.format(reco_space.ndim,
                                                        geometry.ndim))
    if out is not None:
        if not isinstance(out, DiscreteLpElement):
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpElement instance'.format(out))

    ndim = proj_data.ndim

    # Create geometries
    vol_geom = astra_volume_geometry(reco_space)
    proj_geom = astra_projection_geometry(geometry)

    if ndim == 2:
        swapped_proj_data = proj_data
    else:
        swapped_proj_data = np.ascontiguousarray(
            np.rollaxis(proj_data.asarray(), 2, 0))

    sino_id = astra_data(proj_geom, datatype='projection',
                         data=swapped_proj_data, allow_copy=True)

    # Create projector
    proj_id = astra_projector('nearest', vol_geom, proj_geom, ndim,
                              impl='cuda')

    # Reconstruction volume
    if out is None:
        out = reco_space.element()

    # Wrap the array in correct dtype etc if needed
    with writable_array(out, dtype='float32', order='C') as arr:
        vol_id = astra_data(vol_geom, datatype='volume', data=arr,
                            ndim=reco_space.ndim)
        # Create algorithm
        algo_id = astra_algorithm('backward', ndim, vol_id, sino_id,
                                  proj_id=proj_id, impl='cuda')

        # Run algorithm
        astra.algorithm.run(algo_id)

    out *= astra_cuda_bp_scaling_factor(reco_space, geometry)

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    if ndim == 2:
        astra.data2d.delete((vol_id, sino_id))
        astra.projector.delete(proj_id)
    else:
        astra.data3d.delete((vol_id, sino_id))
        astra.projector3d.delete(proj_id)

    return out