"""Radon operator based on Astra Toolbox"""importwarningsimportnumpyasnpfromjoblibimportParallel,delayedASTRA_AVAILABLE=Truetry:importastraexceptImportError:ASTRA_AVAILABLE=Falsewarnings.warn('astra not installed')
[docs]classRadonBase:""" Base class for all Radon operators """
[docs]defop(self,img):""" Computes Radon direct operator Parameters ---------- img: numpy.ndarray input image Returns ------- result: numpy.ndarray sinogram of img """raiseNotImplementedError('op not implemented')
[docs]defadj_op(self,x):""" Computes Radon adjoint operator on Radon space data x Parameters ---------- x: numpy.ndarray input sinogram Returns ------- result: numpy.ndarray back projection of x """raiseNotImplementedError('adj_op not implemented')
[docs]classRadon2D(RadonBase):""" Radon operator based on Astra Toolbox """def__init__(self,angles,img_size,n_channels=1,normalized=True,gpu=False):""" Initializes operator Parameters ---------- angles: numpy.ndarray angles acquired (in degrees) img_size: int size of the images (only square images) normalized: bool, default True tells if the operator is normalized or not. n_channels: int, default 1 Number of images for multichannel reconstructions gpu: bool, default False use cuda implementation if True """ifnotASTRA_AVAILABLE:raiseValueError('astra-toolbox is not installed, this package must be '+'installed to use this class.')self.dtype=floatself.n_coils=n_channelsself.img_size=img_sizeself.shape=(img_size,)*2ifself.n_coils!=1:self.shape=(self.n_coils,)+self.shapeself.angles=anglesself.normalized=normalizedself.norm_const=np.sqrt(len(angles))ifnormalizedelse1.self.implementation='cuda'ifgpuelse'line'# Astra projectorself.proj_geom=astra.create_proj_geom('parallel',1.0,img_size,np.deg2rad(self.angles))self.vol_geom=astra.create_vol_geom(img_size,img_size)self.proj_id=astra.create_projector(self.implementation,self.proj_geom,self.vol_geom)
[docs]def_op(self,img):""" Computes sino of single image """return(astra.create_sino(data=np.array(img),proj_id=self.proj_id)[1]/self.norm_const)
[docs]defop(self,img):""" Returns sinogram of img as a vector Parameters ---------- img: numpy.ndarray((nchannels,) img_size, img_size) input image Returns ------- sinogram: np.array((n_channels,) len(theta), img_size) sinogram of the image """# Single channelifself.n_coils==1:returnself._op(img)# Multithreading for cpu multichannelelifself.implementation!='cuda':returnnp.array(Parallel(n_jobs=4,backend='threading',verbose=False)(delayed(self._op)(img[i])foriinnp.arange(self.n_coils)))# gpu multichannelelse:returnnp.asarray([self._op(img[i])foriinrange(self.n_coils)])
[docs]def_adj_op(self,x):""" Computes backprojection of single set of coefficients """return(astra.creators.create_backprojection(data=np.array(x),proj_id=self.proj_id)[1]/self.norm_const)
[docs]defadj_op(self,x):""" Returns backprojection of a sinogram x Parameters ---------- x: numpy.ndarray((n_channels,) len(theta), img_size) sinogram Returns ------- img: numpy.ndarray((n_channels,) img_size, img_size) the backprojection """# Single channelifself.n_coils==1:returnself._adj_op(x)# Multithreading for cpu multichannelelifself.implementation!='cuda':returnnp.array(Parallel(n_jobs=4,backend='threading',verbose=False)(delayed(self._adj_op)(x[i])foriinnp.arange(self.n_coils)))# gpu multichannelelse:returnnp.asarray([self._adj_op(x[i])foriinrange(self.n_coils)])
[docs]classRadon3D(RadonBase):""" Radon operator based on Astra Toolbox """def__init__(self,angles,img_size,nb_slices=None,n_channels=1,normalized=True):""" Initializes operator Parameters ---------- angles: numpy.ndarray angles acquired (in degrees) img_size: int size of the images (only square images) nb_slices: int, optional Number of slices. If None, assumes equal to img_size n_channels: int, default 1 Number of images for multichannel reconstructions normalized: bool, default True tells if the operator is normalized or not. """ifnotASTRA_AVAILABLE:raiseValueError('astra-toolbox is not installed, this package must be '+'installed to use this class.')ifnb_slicesisNone:nb_slices=img_sizeself.n_coils=n_channelsself.dtype=floatself.img_size=img_sizeself.shape=(nb_slices,img_size,img_size)ifself.n_coils!=1:self.shape=(self.n_coils,)+self.shapeself.angles=anglesself.normalized=normalizedself.norm_const=np.sqrt(len(angles))ifnormalizedelse1.# Astra projectorself.proj_geom=astra.create_proj_geom('parallel3d',1.0,1.0,nb_slices,img_size,np.deg2rad(self.angles))self.vol_geom=astra.create_vol_geom(img_size,img_size,nb_slices)self.sino=astra.create_sino3d_gpuself.back_projection=astra.creators.create_backprojection3d_gpu
[docs]def_op(self,img):""" Computes sino of a single image """returnself.sino(data=img,proj_geom=self.proj_geom,vol_geom=self.vol_geom)[1]/self.norm_const
[docs]defop(self,img):""" Returns sinogram of img as a vector Parameters ---------- img: numpy.ndarray((n_channels,) img_size, img_size, img_size) input image Returns ------- sinogram: numpy.ndarray((n_channels,) img_size, len(theta), img_size) sinogram of the image """ifself.n_coils==1:returnself._op(img)else:returnnp.asarray([self._op(img[i])foriinrange(self.n_coils)])
[docs]def_adj_op(self,x):""" Computes back projection of single set of coefficients """returnself.back_projection(data=x,proj_geom=self.proj_geom,vol_geom=self.vol_geom)[1]/self.norm_const
[docs]defadj_op(self,x):""" Returns backprojection of a sinogram x Parameters ---------- x: numpy.ndarray((n_channels,) img_size, len(theta), img_size) sinogram Returns ------- img: numpy.ndarray((n_channels,) img_size, img_size, img_size) the backprojection """ifself.n_coils==1:returnself._adj_op(x)else:returnnp.asarray([self._adj_op(x[i])foriinrange(self.n_coils)])