Esempio n. 1
0
def test_astra_algorithm():
    """Create ASTRA algorithm object."""
    direction = 'forward'
    ndim = 2
    impl = 'cpu'
    vol_id = astra_data(VOL_GEOM_2D, 'volume', ndim=ndim)
    sino_id = astra_data(PROJ_GEOM_2D, 'projection', ndim=ndim)
    proj_id = astra_projector('linear', VOL_GEOM_2D, PROJ_GEOM_2D, ndim=ndim)

    # Checks
    with pytest.raises(ValueError):
        astra_algorithm('none', ndim, vol_id, sino_id, proj_id, impl)
    with pytest.raises(ValueError):
        astra_algorithm(direction, 0, vol_id, sino_id, proj_id, impl)
    with pytest.raises(ValueError):
        astra_algorithm('none', ndim, vol_id, sino_id, proj_id, 'none')
    with pytest.raises(ValueError):
        astra_algorithm(
            'backward', ndim, vol_id, sino_id, proj_id=None, impl='cpu'
        )
    alg_id = astra_algorithm(direction, ndim, vol_id, sino_id, proj_id, impl)
    astra.algorithm.delete(alg_id)

    # 2D CPU
    ndim = 2
    impl = 'cpu'
    for direction in {'forward', 'backward'}:
        alg_id = astra_algorithm(
            direction, ndim, vol_id, sino_id, proj_id, impl
        )
        astra.algorithm.delete(alg_id)
Esempio n. 2
0
    def create_ids(self):
        """Create ASTRA objects."""
        # Create input and output arrays
        if self.geometry.motion_partition.ndim == 1:
            motion_shape = self.geometry.motion_partition.shape
        else:
            # Need to flatten 2- or 3-dimensional angles into one axis
            motion_shape = (np.prod(self.geometry.motion_partition.shape), )

        proj_shape = motion_shape + self.geometry.det_partition.shape
        proj_ndim = len(proj_shape)

        if proj_ndim == 2:
            astra_proj_shape = proj_shape
            astra_vol_shape = self.reco_space.shape
        elif proj_ndim == 3:
            # The `u` and `v` axes of the projection data are swapped,
            # see explanation in `astra_*_3d_geom_to_vec`.
            astra_proj_shape = (proj_shape[1], proj_shape[0], proj_shape[2])
            astra_vol_shape = self.reco_space.shape

        self.in_array = np.empty(astra_vol_shape, dtype='float32', order='C')
        self.out_array = np.empty(astra_proj_shape, dtype='float32', order='C')

        # Create ASTRA data structures
        vol_geom = astra_volume_geometry(self.reco_space)
        proj_geom = astra_projection_geometry(self.geometry)
        self.vol_id = astra_data(vol_geom,
                                 datatype='volume',
                                 ndim=self.reco_space.ndim,
                                 data=self.in_array,
                                 allow_copy=False)

        self.proj_id = astra_projector('nearest',
                                       vol_geom,
                                       proj_geom,
                                       ndim=proj_ndim,
                                       impl='cuda')

        self.sino_id = astra_data(proj_geom,
                                  datatype='projection',
                                  ndim=proj_ndim,
                                  data=self.out_array,
                                  allow_copy=False)

        # Create algorithm
        self.algo_id = astra_algorithm('forward',
                                       proj_ndim,
                                       self.vol_id,
                                       self.sino_id,
                                       proj_id=self.proj_id,
                                       impl='cuda')
Esempio n. 3
0
def test_volume_data_2d():
    """Create ASTRA data structure in 2D."""
    # From scratch
    data_id = astra_data(VOL_GEOM_2D, 'volume', ndim=2)
    data_out = astra.data2d.get_shared(data_id)
    assert data_out.shape == (10, 20)

    # From existing
    discr_dom = _discrete_domain(2)
    data_in = discr_dom.element(np.ones((10, 20), dtype='float32'))
    data_id = astra_data(VOL_GEOM_2D, 'volume', data=data_in)
    data_out = astra.data2d.get_shared(data_id)
    assert data_out.shape == (10, 20)
def test_astra_algorithm_gpu():
    """Create ASTRA algorithm object on GPU."""

    direction = 'forward'
    ndim = 2
    vol_id = astra_data(VOL_GEOM_2D, 'volume', ndim=ndim)
    rec_id = astra_data(VOL_GEOM_2D, 'volume', ndim=ndim)
    sino_id = astra_data(PROJ_GEOM_2D, 'projection', ndim=ndim)

    # 2D CUDA
    proj_id = astra_projector('cuda', VOL_GEOM_2D, PROJ_GEOM_2D, ndim=ndim)

    # 2D CUDA FP
    alg_id = astra_algorithm('forward',
                             ndim,
                             vol_id,
                             sino_id,
                             proj_id=proj_id,
                             impl='cuda')
    astra.algorithm.delete(alg_id)

    # 2D CUDA BP
    alg_id = astra_algorithm('backward',
                             ndim,
                             rec_id,
                             sino_id,
                             proj_id=proj_id,
                             impl='cuda')
    astra.algorithm.delete(alg_id)

    ndim = 3
    vol_id = astra_data(VOL_GEOM_3D, 'volume', ndim=ndim)
    sino_id = astra_data(PROJ_GEOM_3D, 'projection', ndim=ndim)
    proj_id = astra_projector('cuda3d', VOL_GEOM_3D, PROJ_GEOM_3D, ndim=ndim)

    with pytest.raises(NotImplementedError):
        astra_algorithm(direction,
                        ndim,
                        vol_id,
                        sino_id,
                        proj_id=proj_id,
                        impl='cpu')

    for direction in {'forward', 'backward'}:
        astra_algorithm(direction,
                        ndim,
                        vol_id,
                        sino_id,
                        proj_id=proj_id,
                        impl='cuda')
Esempio n. 5
0
    def create_ids(self):
        """Create ASTRA objects."""
        ndim = self.geometry.ndim

        # Create input and output arrays
        if ndim == 2:
            astra_proj_shape = self.proj_space.shape
        elif ndim == 3:
            astra_proj_shape = (self.proj_space.shape[2],
                                self.proj_space.shape[0],
                                self.proj_space.shape[1])

        self.in_array = np.empty(self.reco_space.shape,
                                 dtype='float32',
                                 order='C')
        self.out_array = np.empty(astra_proj_shape, dtype='float32', order='C')

        # Create ASTRA data structures
        vol_geom = astra_volume_geometry(self.reco_space)
        proj_geom = astra_projection_geometry(self.geometry)
        self.vol_id = astra_data(vol_geom,
                                 datatype='volume',
                                 ndim=self.reco_space.ndim,
                                 data=self.in_array,
                                 allow_copy=False)

        self.proj_id = astra_projector('nearest',
                                       vol_geom,
                                       proj_geom,
                                       ndim,
                                       impl='cuda')

        self.sino_id = astra_data(proj_geom,
                                  datatype='projection',
                                  ndim=self.proj_space.ndim,
                                  data=self.out_array,
                                  allow_copy=False)

        # Create algorithm
        self.algo_id = astra_algorithm('forward',
                                       ndim,
                                       self.vol_id,
                                       self.sino_id,
                                       proj_id=self.proj_id,
                                       impl='cuda')
Esempio n. 6
0
    def create_ids(self):
        """Create ASTRA objects."""
        ndim = self.geometry.ndim

        # Create input and output arrays
        if ndim == 2:
            astra_proj_shape = self.proj_space.shape
        elif ndim == 3:
            astra_proj_shape = (self.proj_space.shape[2],
                                self.proj_space.shape[0],
                                self.proj_space.shape[1])

        self.in_array = np.empty(self.reco_space.shape,
                                 dtype='float32', order='C')
        self.out_array = np.empty(astra_proj_shape,
                                  dtype='float32', order='C')

        # Create ASTRA data structures
        vol_geom = astra_volume_geometry(self.reco_space)
        proj_geom = astra_projection_geometry(self.geometry)
        self.vol_id = astra_data(vol_geom,
                                 datatype='volume',
                                 ndim=self.reco_space.ndim,
                                 data=self.in_array,
                                 allow_copy=False)

        self.proj_id = astra_projector('nearest', vol_geom, proj_geom,
                                       ndim, impl='cuda')

        self.sino_id = astra_data(proj_geom,
                                  datatype='projection',
                                  ndim=self.proj_space.ndim,
                                  data=self.out_array,
                                  allow_copy=False)

        # Create algorithm
        self.algo_id = astra_algorithm(
            'forward', ndim, self.vol_id, self.sino_id,
            proj_id=self.proj_id, impl='cuda')
Esempio n. 7
0
def astra_cpu_forward_projector(vol_data, geometry, proj_space, out=None):
    """Run an ASTRA forward projection on the given data using the CPU.

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

    Returns
    -------
    out : ``proj_space`` `element`
        Projection data resulting from the application of the projector
    """
    if not isinstance(vol_data, DiscreteLpVector):
        raise TypeError('volume data {!r} is not a DiscreteLpVector '
                        'instance'.format(vol_data))
    if not isinstance(vol_data.space.dspace, Ntuples):
        raise TypeError('data type {!r} of the volume space is not an '
                        'instance of Ntuples'.format(vol_data.space.dspace))
    if not isinstance(geometry, Geometry):
        raise TypeError('geometry  {!r} is not a Geometry instance'
                        ''.format(geometry))
    if not isinstance(proj_space, DiscreteLp):
        raise TypeError('projection space {!r} is not a DiscreteLp '
                        'instance'.format(proj_space))
    if not isinstance(proj_space.dspace, Ntuples):
        raise TypeError('data type {!r} of the reconstruction space is not an '
                        'instance of Ntuples'.format(proj_space.dspace))
    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 out is None:
        out = proj_space.element()
    else:
        if not isinstance(out, DiscreteLpVector):
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpVector 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
    vol_id = astra_data(vol_geom, datatype='volume', data=vol_data)
    sino_id = astra_data(proj_geom,
                         datatype='projection',
                         data=out,
                         ndim=proj_space.ndim)

    # Create projector
    if not all(s == vol_data.space.interp[0] for s in vol_data.space.interp):
        raise ValueError('volume interpolation must be the same in each '
                         'dimension, got {}'.format(vol_data.space.interp))
    vol_interp = vol_data.space.interp[0]
    proj_id = astra_projector(vol_interp,
                              vol_geom,
                              proj_geom,
                              ndim,
                              impl='cpu')

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

    # Run algorithm
    astra.algorithm.run(algo_id)

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out
Esempio n. 8
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 : `DiscreteLpVector`, optional
            Vector in the reconstruction space to which the result is written.
            If `None` creates an element in the reconstruction space
            ``reco_space``

        Returns
        -------
        out : ``reco_space`` element
            Reconstruction data resulting from the application of the backward
            projector
        """
    if not isinstance(proj_data, DiscreteLpVector):
        raise TypeError('projection data {!r} is not a DiscreteLpVector '
                        '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, DiscreteLpVector):
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpVector instance'.format(out))

    ndim = proj_data.ndim

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

    # Create data structures
    if out is None:
        out = reco_space.element()

    vol_id = astra_data(vol_geom, datatype='volume', data=out,
                        ndim=reco_space.ndim)

    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)

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

    # 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)

    # Angular integration weighting factor
    # angle interval weight by approximate cell volume
    extent = float(geometry.motion_partition.extent())
    size = float(geometry.motion_partition.size)
    scaling_factor = extent / size

    # Fix inconsistent scaling
    if isinstance(geometry, Parallel2dGeometry):
        # scales with 1 / (cell volume)
        scaling_factor *= float(reco_space.cell_volume)
    elif isinstance(geometry, FanFlatGeometry):
        # scales with 1 / (cell volume)
        scaling_factor *= float(reco_space.cell_volume)
        # magnification correction
        src_radius = geometry.src_radius
        det_radius = geometry.det_radius
        scaling_factor *= ((src_radius + det_radius) / src_radius)
    elif isinstance(geometry, Parallel3dAxisGeometry):
        # scales with voxel stride
        # currently only square voxels are supported
        extent = reco_space.partition.extent()
        shape = np.array(reco_space.shape, dtype=float)
        scaling_factor /= float(extent[0] / shape[0])
    elif isinstance(geometry, HelicalConeFlatGeometry):
        # cone scales with voxel stride
        # currently only square voxels are supported
        extent = reco_space.partition.extent()
        shape = np.array(reco_space.shape, dtype=float)
        scaling_factor /= float(extent[0] / shape[0])
        # magnification correction
        src_radius = geometry.src_radius
        det_radius = geometry.det_radius
        scaling_factor *= ((src_radius + det_radius) / src_radius) ** 2
    out *= scaling_factor

    # 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
Esempio n. 9
0
def astra_cpu_forward_projector(vol_data, geometry, proj_space, out=None):
    """Run an ASTRA forward projection on the given data using the CPU.

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

    Returns
    -------
    out : ``proj_space`` `element`
        Projection data resulting from the application of the projector
    """
    if not isinstance(vol_data, DiscreteLpVector):
        raise TypeError('volume data {!r} is not a DiscreteLpVector '
                        'instance'.format(vol_data))
    if not isinstance(vol_data.space.dspace, Ntuples):
        raise TypeError('data type {!r} of the volume space is not an '
                        'instance of Ntuples'.format(vol_data.space.dspace))
    if not isinstance(geometry, Geometry):
        raise TypeError('geometry  {!r} is not a Geometry instance'
                        ''.format(geometry))
    if not isinstance(proj_space, DiscreteLp):
        raise TypeError('projection space {!r} is not a DiscreteLp '
                        'instance'.format(proj_space))
    if not isinstance(proj_space.dspace, Ntuples):
        raise TypeError('data type {!r} of the reconstruction space is not an '
                        'instance of Ntuples'.format(proj_space.dspace))
    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 out is None:
        out = proj_space.element()
    else:
        if not isinstance(out, DiscreteLpVector):
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpVector 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
    vol_id = astra_data(vol_geom, datatype='volume', data=vol_data)
    sino_id = astra_data(proj_geom, datatype='projection', data=out,
                         ndim=proj_space.ndim)

    # Create projector
    if not all(s == vol_data.space.interp[0] for s in vol_data.space.interp):
        raise ValueError('volume interpolation must be the same in each '
                         'dimension, got {}'.format(vol_data.space.interp))
    vol_interp = vol_data.space.interp[0]
    proj_id = astra_projector(vol_interp, vol_geom, proj_geom, ndim,
                              impl='cpu')

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

    # Run algorithm
    astra.algorithm.run(algo_id)

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out
def astra_cpu_forward_projector(vol_data, geometry, proj_space, out=None,
                                astra_proj_type=None):
    """Run an ASTRA forward projection on the given data using the CPU.

    Parameters
    ----------
    vol_data : `DiscretizedSpaceElement`
        Volume data to which the forward projector is applied.
    geometry : `Geometry`
        Geometry defining the tomographic setup.
    proj_space : `DiscretizedSpace`
        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.
    astra_proj_type : str, optional
        Type of projector that should be used. See `the ASTRA documentation
        <http://www.astra-toolbox.com/docs/proj2d.html>`_ for details.
        By default, a suitable projector type for the given geometry is
        selected, see `default_astra_proj_type`.

    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, DiscretizedSpaceElement):
        raise TypeError(
            'volume data {!r} is not a `DiscretizedSpaceElement` instance'
            ''.format(vol_data)
        )
    if vol_data.space.impl != 'numpy':
        raise TypeError(
            "`vol_data.space.impl` must be 'numpy', got {!r}"
            "".format(vol_data.space.impl)
        )
    if not isinstance(geometry, Geometry):
        raise TypeError(
            'geometry {!r} is not a Geometry instance'.format(geometry)
        )
    if not isinstance(proj_space, DiscretizedSpace):
        raise TypeError(
            '`proj_space` {!r} is not a DiscretizedSpace instance.'
            ''.format(proj_space)
        )
    if proj_space.impl != 'numpy':
        raise TypeError(
            "`proj_space.impl` must be 'numpy', got {!r}"
            "".format(proj_space.impl)
        )
    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 out is None:
        out = proj_space.element()
    else:
        if out not in proj_space:
            raise TypeError(
                '`out` {} is neither None nor a `DiscretizedSpaceElement` '
                '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 projector
    if astra_proj_type is None:
        astra_proj_type = default_astra_proj_type(geometry)
    proj_id = astra_projector(astra_proj_type, vol_geom, proj_geom, ndim)

    # Create ASTRA data structures
    vol_data_arr = np.asarray(vol_data)
    vol_id = astra_data(vol_geom, datatype='volume', data=vol_data_arr,
                        allow_copy=True)

    with writable_array(out, dtype='float32', order='C') as out_arr:
        sino_id = astra_data(proj_geom, datatype='projection', data=out_arr,
                             ndim=proj_space.ndim)

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

        # Run algorithm
        astra.algorithm.run(algo_id)

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out
Esempio n. 11
0
def astra_cpu_forward_projector(vol_data, geometry, proj_space, out=None):
    """Run an ASTRA forward projection on the given data using the CPU.

    Parameters
    ----------
    vol_data : `DiscreteLpElement`
        Volume data to which the forward 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 vol_data.space.impl != 'numpy':
        raise TypeError('dspace {!r} of the volume is not an '
                        'instance of `NumpyNtuples`'
                        ''.format(vol_data.space.dspace))
    if not isinstance(geometry, Geometry):
        raise TypeError('geometry  {!r} is not a Geometry instance'
                        ''.format(geometry))
    if not isinstance(proj_space, DiscreteLp):
        raise TypeError('projection space {!r} is not a DiscreteLp '
                        'instance.'.format(proj_space))
    if proj_space.impl != 'numpy':
        raise TypeError('data type {!r} of the reconstruction space is not an '
                        'instance of NumpyNtuples'.format(proj_space.dspace))
    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 out is None:
        out = proj_space.element()
    else:
        if out not in proj_space:
            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 projector
    if not all(s == vol_data.space.interp_by_axis[0]
               for s in vol_data.space.interp_by_axis):
        raise ValueError('volume interpolation must be the same in each '
                         'dimension, got {}'.format(vol_data.space.interp))
    vol_interp = vol_data.space.interp
    proj_id = astra_projector(vol_interp, vol_geom, proj_geom, ndim,
                              impl='cpu')

    # Create ASTRA data structures
    vol_id = astra_data(vol_geom, datatype='volume', data=vol_data,
                        allow_copy=True)

    with writable_array(out, dtype='float32', order='C') as arr:
        sino_id = astra_data(proj_geom, datatype='projection', data=arr,
                             ndim=proj_space.ndim)

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

        # Run algorithm
        astra.algorithm.run(algo_id)

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out
Esempio n. 12
0
def astra_cpu_back_projector(proj_data, geometry, reco_space, out=None):
    """Run an ASTRA backward projection on the given data using the CPU.

    Parameters
    ----------
    proj_data : `DiscreteLpVector`
        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 : `DiscreteLpVector` or `None`, optional
        Vector in the reconstruction space to which the result is written. If
        `None` creates an element in the reconstruction space ``reco_space``

    Returns
    -------
    out : ``reco_space`` element
        Reconstruction data resulting from the application of the backward
        projector
    """
    if not isinstance(proj_data, DiscreteLpVector):
        raise TypeError('projection data {!r} is not a DiscreteLpVector '
                        'instance'.format(proj_data))
    if not isinstance(proj_data.space.dspace, Ntuples):
        raise TypeError('data type {!r} of the projection space is not an '
                        'instance of Ntuples'.format(proj_data.shape.dspace))
    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 not isinstance(reco_space.dspace, Ntuples):
        raise TypeError('data type {!r} of the reconstruction space is not an '
                        'instance of Ntuples'.format(reco_space.dspace))
    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 None:
        out = reco_space.element()
    else:
        if not isinstance(out, DiscreteLpVector):
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpVector instance'.format(out))

    ndim = proj_data.ndim

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

    # Create ASTRA data structures
    vol_id = astra_data(vol_geom, datatype='volume', data=out,
                        ndim=reco_space.ndim)
    sino_id = astra_data(proj_geom, datatype='projection', data=proj_data)

    # Create projector
    # TODO: implement with different schemes for angles and detector
    if not all(s == proj_data.space.interp[0] for s in proj_data.space.interp):
        raise ValueError('data interpolation must be the same in each '
                         'dimension, got {}'.format(proj_data.space.interp))
    proj_interp = proj_data.space.interp[0]
    proj_id = astra_projector(proj_interp, vol_geom, proj_geom, ndim,
                              impl='cpu')

    # Create algorithm
    algo_id = astra_algorithm('backward', ndim, vol_id, sino_id, proj_id,
                              impl='cpu')

    # Run algorithm and delete it
    astra.algorithm.run(algo_id)

    # Angular integration weighting factor
    # angle interval weight by approximate cell volume
    extent = float(geometry.motion_partition.extent())
    size = float(geometry.motion_partition.size)
    scaling_factor = extent / size

    # Fix inconsistent scaling: parallel2d & fanflat scale with (voxel
    # stride)**2 / (pixel stride), currently only square voxels are supported
    scaling_factor *= float(geometry.det_partition.cell_sides[0])
    scaling_factor /= float(reco_space.partition.cell_sides[0]) ** 2

    out *= scaling_factor

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out
Esempio n. 13
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 : `DiscreteLpVector`
        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 : `DiscreteLpVector`, optional
        Vector in the projection space to which the result is written. If
        `None` creates an element in the projection space ``proj_space``

    Returns
    -------
    out : ``proj_space`` element
        Projection data resulting from the application of the projector
    """
    if not isinstance(vol_data, DiscreteLpVector):
        raise TypeError('volume data {!r} is not a DiscreteLpVector '
                        '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, DiscreteLpVector):
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpVector 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
    if out is None and ndim == 2:
        out = proj_space.element()

    vol_id = astra_data(vol_geom, datatype='volume', data=vol_data)

    # needs to be improved, cuda does not use out anyway
    data_out = out if ndim == 2 else None
    sino_id = astra_data(proj_geom, datatype='projection', data=data_out,
                         ndim=proj_space.ndim)

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

    # 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)

    # Wrap data
    if ndim == 3:
        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)

    # 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
Esempio n. 14
0
def astra_cpu_back_projector(proj_data, geometry, reco_space, out=None):
    """Run an ASTRA backward projection on the given data using the CPU.

    Parameters
    ----------
    proj_data : `DiscreteLpVector`
        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 : `DiscreteLpVector` or `None`, optional
        Vector in the reconstruction space to which the result is written. If
        `None` creates an element in the reconstruction space ``reco_space``

    Returns
    -------
    out : ``reco_space`` element
        Reconstruction data resulting from the application of the backward
        projector
    """
    if not isinstance(proj_data, DiscreteLpVector):
        raise TypeError('projection data {!r} is not a DiscreteLpVector '
                        'instance'.format(proj_data))
    if not isinstance(proj_data.space.dspace, Ntuples):
        raise TypeError('data type {!r} of the projection space is not an '
                        'instance of Ntuples'.format(proj_data.shape.dspace))
    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 not isinstance(reco_space.dspace, Ntuples):
        raise TypeError('data type {!r} of the reconstruction space is not an '
                        'instance of Ntuples'.format(reco_space.dspace))
    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 None:
        out = reco_space.element()
    else:
        if not isinstance(out, DiscreteLpVector):
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpVector instance'.format(out))

    ndim = proj_data.ndim

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

    # Create ASTRA data structures
    vol_id = astra_data(vol_geom,
                        datatype='volume',
                        data=out,
                        ndim=reco_space.ndim)
    sino_id = astra_data(proj_geom, datatype='projection', data=proj_data)

    # Create projector
    # TODO: implement with different schemes for angles and detector
    if not all(s == proj_data.space.interp[0] for s in proj_data.space.interp):
        raise ValueError('data interpolation must be the same in each '
                         'dimension, got {}'.format(proj_data.space.interp))
    proj_interp = proj_data.space.interp[0]
    proj_id = astra_projector(proj_interp,
                              vol_geom,
                              proj_geom,
                              ndim,
                              impl='cpu')

    # Create algorithm
    algo_id = astra_algorithm('backward',
                              ndim,
                              vol_id,
                              sino_id,
                              proj_id,
                              impl='cpu')

    # Run algorithm and delete it
    astra.algorithm.run(algo_id)

    # Angular integration weighting factor
    # angle interval weight by approximate cell volume
    extent = float(geometry.motion_partition.extent())
    size = float(geometry.motion_partition.size)
    scaling_factor = extent / size

    # Fix inconsistent scaling: parallel2d & fanflat scale with (voxel
    # stride)**2 / (pixel stride), currently only square voxels are supported
    scaling_factor *= float(geometry.det_partition.cell_sides[0])
    scaling_factor /= float(reco_space.partition.cell_sides[0])**2

    out *= scaling_factor

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out
Esempio n. 15
0
def astra_cpu_back_projector(proj_data, geometry, reco_space, out=None):
    """Run an ASTRA back-projection on the given data using the CPU.

    Parameters
    ----------
    proj_data : `DiscreteLpElement`
        Projection data to which the back-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 proj_data.space.impl != 'numpy':
        raise TypeError('data type {!r} of the projection space is not an '
                        'instance of NumpyNtuples'
                        ''.format(proj_data.shape.dspace))
    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.impl != 'numpy':
        raise TypeError('data type {!r} of the reconstruction space is not an '
                        'instance of NumpyNtuples'.format(reco_space.dspace))
    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 None:
        out = reco_space.element()
    else:
        if out not in reco_space:
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpElement instance'.format(out))

    ndim = proj_data.ndim

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

    # Create ASTRA data structure
    sino_id = astra_data(proj_geom,
                         datatype='projection',
                         data=proj_data,
                         allow_copy=True)

    # Create projector
    # TODO: implement with different schemes for angles and detector
    if not all(s == proj_data.space.interp_byaxis[0]
               for s in proj_data.space.interp_byaxis):
        raise ValueError('data interpolation must be the same in each '
                         'dimension, got {}'
                         ''.format(proj_data.space.interp_byaxis))
    proj_interp = proj_data.space.interp
    proj_id = astra_projector(proj_interp,
                              vol_geom,
                              proj_geom,
                              ndim,
                              impl='cpu')

    # convert out to correct dtype and order 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,
                                  impl='cpu')

        # Run algorithm and delete it
        astra.algorithm.run(algo_id)

    # Weight the adjoint by appropriate weights
    scaling_factor = float(proj_data.space.weighting.const)
    scaling_factor /= float(reco_space.weighting.const)

    out *= scaling_factor

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out
Esempio n. 16
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
Esempio n. 17
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
Esempio n. 18
0
def astra_cpu_forward_projector(vol_data, geometry, proj_space, out=None):
    """Run an ASTRA forward projection on the given data using the CPU.

    Parameters
    ----------
    vol_data : `DiscreteLpElement`
        Volume data to which the forward 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 vol_data.space.impl != 'numpy':
        raise TypeError('dspace {!r} of the volume is not an '
                        'instance of `NumpyNtuples`'
                        ''.format(vol_data.space.dspace))
    if not isinstance(geometry, Geometry):
        raise TypeError('geometry  {!r} is not a Geometry instance'
                        ''.format(geometry))
    if not isinstance(proj_space, DiscreteLp):
        raise TypeError('projection space {!r} is not a DiscreteLp '
                        'instance.'.format(proj_space))
    if proj_space.impl != 'numpy':
        raise TypeError('data type {!r} of the reconstruction space is not an '
                        'instance of NumpyNtuples'.format(proj_space.dspace))
    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 out is None:
        out = proj_space.element()
    else:
        if out not in proj_space:
            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 projector
    if not all(s == vol_data.space.interp_byaxis[0]
               for s in vol_data.space.interp_byaxis):
        raise ValueError('volume interpolation must be the same in each '
                         'dimension, got {}'.format(vol_data.space.interp))
    vol_interp = vol_data.space.interp
    proj_id = astra_projector(vol_interp,
                              vol_geom,
                              proj_geom,
                              ndim,
                              impl='cpu')

    # Create ASTRA data structures
    vol_id = astra_data(vol_geom,
                        datatype='volume',
                        data=vol_data,
                        allow_copy=True)

    with writable_array(out, dtype='float32', order='C') as arr:
        sino_id = astra_data(proj_geom,
                             datatype='projection',
                             data=arr,
                             ndim=proj_space.ndim)

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

        # Run algorithm
        astra.algorithm.run(algo_id)

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out
Esempio n. 19
0
def astra_cpu_back_projector(proj_data,
                             geometry,
                             vol_space,
                             out=None,
                             astra_proj_type=None):
    """Run an ASTRA back-projection on the given data using the CPU.

    Parameters
    ----------
    proj_data : `DiscreteLpElement`
        Projection data to which the back-projector is applied.
    geometry : `Geometry`
        Geometry defining the tomographic setup.
    vol_space : `DiscreteLp`
        Space to which the calling operator maps.
    out : ``vol_space`` element, optional
        Element of the reconstruction space to which the result is written.
        If ``None``, an element in ``vol_space`` is created.
    astra_proj_type : str, optional
        Type of projector that should be used. See `the ASTRA documentation
        <http://www.astra-toolbox.com/docs/proj2d.html>`_ for details.
        By default, a suitable projector type for the given geometry is
        selected, see `default_astra_proj_type`.

    Returns
    -------
    out : ``vol_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 proj_data.space.impl != 'numpy':
        raise TypeError('`proj_data` must be a `numpy.ndarray` based, '
                        "container got `impl` {!r}"
                        "".format(proj_data.space.impl))
    if not isinstance(geometry, Geometry):
        raise TypeError('geometry  {!r} is not a Geometry instance'
                        ''.format(geometry))
    if not isinstance(vol_space, DiscreteLp):
        raise TypeError('volume space {!r} is not a DiscreteLp '
                        'instance'.format(vol_space))
    if vol_space.impl != 'numpy':
        raise TypeError("`vol_space.impl` must be 'numpy', got {!r}"
                        "".format(vol_space.impl))
    if vol_space.ndim != geometry.ndim:
        raise ValueError('dimensions {} of reconstruction space and {} of '
                         'geometry do not match'.format(
                             vol_space.ndim, geometry.ndim))
    if out is None:
        out = vol_space.element()
    else:
        if out not in vol_space:
            raise TypeError('`out` {} is neither None nor a '
                            'DiscreteLpElement instance'.format(out))

    ndim = proj_data.ndim

    # Create astra geometries
    vol_geom = astra_volume_geometry(vol_space)
    proj_geom = astra_projection_geometry(geometry)

    # Create ASTRA data structure
    sino_id = astra_data(proj_geom,
                         datatype='projection',
                         data=proj_data,
                         allow_copy=True)

    # Create projector
    if astra_proj_type is None:
        astra_proj_type = default_astra_proj_type(geometry)
    proj_id = astra_projector(astra_proj_type, vol_geom, proj_geom, ndim)

    # Convert out to correct dtype and order if needed.
    with writable_array(out, dtype='float32', order='C') as out_arr:
        vol_id = astra_data(vol_geom,
                            datatype='volume',
                            data=out_arr,
                            ndim=vol_space.ndim)
        # Create algorithm
        algo_id = astra_algorithm('backward',
                                  ndim,
                                  vol_id,
                                  sino_id,
                                  proj_id,
                                  impl='cpu')

        # Run algorithm
        astra.algorithm.run(algo_id)

    # Weight the adjoint by appropriate weights
    scaling_factor = float(proj_data.space.weighting.const)
    scaling_factor /= float(vol_space.weighting.const)

    out *= scaling_factor

    # Delete ASTRA objects
    astra.algorithm.delete(algo_id)
    astra.data2d.delete((vol_id, sino_id))
    astra.projector.delete(proj_id)

    return out