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