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 test_astra_projection_geometry(): """Create ASTRA projection geometry from geometry objects.""" with pytest.raises(TypeError): astra_projection_geometry(None) apart = odl.uniform_partition(0, 2 * np.pi, 5) dpart = odl.uniform_partition(-40, 40, 10) # motion sampling grid, detector sampling grid but not uniform dpart_0 = odl.RectPartition(odl.IntervalProd(0, 3), odl.RectGrid([0, 1, 3])) geom_p2d = odl.tomo.Parallel2dGeometry(apart, dpart=dpart_0) with pytest.raises(ValueError): astra_projection_geometry(geom_p2d) # detector sampling grid, motion sampling grid geom_p2d = odl.tomo.Parallel2dGeometry(apart, dpart) astra_projection_geometry(geom_p2d) # Parallel 2D geometry geom_p2d = odl.tomo.Parallel2dGeometry(apart, dpart) astra_geom = astra_projection_geometry(geom_p2d) assert astra_geom['type'] == 'parallel' # Fan flat src_rad = 10 det_rad = 5 geom_ff = odl.tomo.FanBeamGeometry(apart, dpart, src_rad, det_rad) astra_geom = astra_projection_geometry(geom_ff) assert astra_geom['type'] == 'fanflat_vec' dpart = odl.uniform_partition([-40, -3], [40, 3], (10, 5)) # Parallel 3D geometry geom_p3d = odl.tomo.Parallel3dAxisGeometry(apart, dpart) astra_projection_geometry(geom_p3d) astra_geom = astra_projection_geometry(geom_p3d) assert astra_geom['type'] == 'parallel3d_vec' # Circular conebeam flat geom_ccf = odl.tomo.ConeBeamGeometry(apart, dpart, src_rad, det_rad) astra_geom = astra_projection_geometry(geom_ccf) assert astra_geom['type'] == 'cone_vec' # Helical conebeam flat pitch = 1 geom_hcf = odl.tomo.ConeBeamGeometry(apart, dpart, src_rad, det_rad, pitch=pitch) astra_geom = astra_projection_geometry(geom_hcf) assert astra_geom['type'] == 'cone_vec'
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 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.vol_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.vol_space.shape self.vol_array = np.empty(astra_vol_shape, dtype='float32', order='C') self.proj_array = np.empty(astra_proj_shape, dtype='float32', order='C') # Create ASTRA data structures vol_geom = astra_volume_geometry(self.vol_space) proj_geom = astra_projection_geometry(self.geometry) self.vol_id = astra_data( vol_geom, datatype='volume', ndim=self.vol_space.ndim, data=self.vol_array, allow_copy=False, ) proj_type = 'cuda' if proj_ndim == 2 else 'cuda3d' self.proj_id = astra_projector(proj_type, vol_geom, proj_geom, proj_ndim) self.sino_id = astra_data( proj_geom, datatype='projection', ndim=proj_ndim, data=self.proj_array, allow_copy=False, ) # Create algorithm self.algo_forward_id = astra_algorithm( 'forward', proj_ndim, self.vol_id, self.sino_id, self.proj_id, impl='cuda', ) # Create algorithm self.algo_backward_id = astra_algorithm( 'backward', proj_ndim, self.vol_id, self.sino_id, self.proj_id, impl='cuda', )
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_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_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 out_arr: vol_id = astra_data(vol_geom, datatype='volume', data=out_arr, ndim=reco_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(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_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_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_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_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_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