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)
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')
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')
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')
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')
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_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
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
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
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
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
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
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
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 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
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
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