diff --git a/examples/ASTRAExample.py b/examples/ASTRAExample.py
index 75f0476..2668303 100644
--- a/examples/ASTRAExample.py
+++ b/examples/ASTRAExample.py
@@ -23,34 +23,38 @@
#
#-----------------------------------------------------------------------
-import mrfbp
-from mrfbp.ASTRAProjector import ASTRAProjector2D
import astra
import numpy as np
+# Register MR-FBP plugin with ASTRA
+import mrfbp
+astra.plugin.register(mrfbp.plugin)
+
# Create ASTRA geometries
vol_geom = astra.create_vol_geom(256,256)
proj_geom = astra.create_proj_geom('parallel',1.0,256,np.linspace(0,np.pi,32,False))
-# Create the ASTRA projector
-p = ASTRAProjector2D(proj_geom,vol_geom)
+# Create the ASTRA projector (change 'linear' to 'cuda' to use GPU)
+pid = astra.create_projector('linear', proj_geom, vol_geom)
+p = astra.OpTomo(pid)
# Load the phantom from disk
testPhantom = np.load('phantom.npy')
# Calculate the forward projection of the phantom
-testSino = p*testPhantom
+testSino = (p*testPhantom).reshape(p.sshape)
# Add some noise to the sinogram
testSino = astra.add_noise_to_sino(testSino,10**4)
-# Create the MR-FBP Reconstructor
-rec = mrfbp.Reconstructor(p)
-
# Reconstruct the image using MR-FBP, FBP, and SIRT.
-mrRec = rec.reconstruct(testSino)
-fbpRec = p.reconstruct('FBP_CUDA',testSino)
-sirtRec = p.reconstruct('SIRT_CUDA',testSino,200)
+mrRec = p.reconstruct('MR-FBP',testSino)
+if astra.projector.is_cuda(pid):
+ fbpRec = p.reconstruct('FBP_CUDA',testSino)
+ sirtRec = p.reconstruct('SIRT_CUDA',testSino,200)
+else:
+ fbpRec = p.reconstruct('FBP',testSino)
+ sirtRec = p.reconstruct('SIRT',testSino,200)
# Show the different reconstructions on screen
import pylab
diff --git a/mrfbp/ASTRAProjector.py b/mrfbp/ASTRAProjector.py
deleted file mode 100644
index e70b391..0000000
--- a/mrfbp/ASTRAProjector.py
+++ /dev/null
@@ -1,217 +0,0 @@
-#-----------------------------------------------------------------------
-#Copyright 2014 Daniel M. Pelt
-#
-#Contact: D.M.Pelt@cwi.nl
-#Website: http://www.dmpelt.com
-#
-#
-#This file is part of the PyMR-FBP, a Python implementation of the
-#MR-FBP tomographic reconstruction method.
-#
-#PyMR-FBP is free software: you can redistribute it and/or modify
-#it under the terms of the GNU General Public License as published by
-#the Free Software Foundation, either version 3 of the License, or
-#(at your option) any later version.
-#
-#PyMR-FBP is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-#GNU General Public License for more details.
-#
-#You should have received a copy of the GNU General Public License
-#along with PyMR-FBP. If not, see .
-#
-#-----------------------------------------------------------------------
-import astra as at
-import math
-import numpy as np
-
-class ASTRAProjector2DTranspose():
- """Implements the ``proj.T`` functionality.
-
- Do not use directly, since it can be accessed as member ``.T`` of
- an :class:`ASTRAProjector2D` object.
-
- """
- def __init__(self,parentProj):
- self.parentProj = parentProj
-
- def __mul__(self,data):
- return self.parentProj.backProject(data)
-
-class ASTRAProjector2D(object):
-
- """Implementation of the projector interface using the ASTRA toolbox with CUDA.
-
- A projector needs to implement:
-
- * Forward projecting
- * Back projecting
- * Creating a FBP reconstruction with a custom filter
-
- You can use this class as an abstracted weight matrix :math:`W`: multiplying an instance
- ``proj`` of this class by an image results in a forward projection of the image, and multiplying
- ``proj.T`` by a sinogram results in a backprojection of the sinogram::
-
- proj = ASTRAProjector2D(...)
- fp = proj*image
- bp = proj.T*sinogram
-
- :param proj_geom: The projection geometry.
- :type proj_geom: :class:`dict`
- :param vol_geom: The volume geometry.
- :type vol_geom: :class:`dict`
- :param offsets: Optional offsets for the detectors
- :type offsets: :class:`numpy.ndarray`
- """
-
- def __init__(self,proj_geom,vol_geom,offsets=None):
- self.vol_geom = vol_geom
- self.recSize = vol_geom['GridColCount']
- self.angles = proj_geom['ProjectionAngles']
- self.nDet = proj_geom['DetectorCount']
- nexpow = int(pow(2, math.ceil(math.log(2*self.nDet, 2))))
- self.filterSize = nexpow/2 +1
- self.nProj = self.angles.shape[0]
- self.proj_geom = proj_geom
-
- if not offsets==None:
- self.proj_geom['option'] = {}
- self.proj_geom['option']['ExtraDetectorOffset'] = offsets
- filt_proj_geom['option'] = {}
- filt_proj_geom['option']['ExtraDetectorOffset'] = offsets
-
- filt_proj_geom = at.create_proj_geom('parallel',1.0,self.filterSize,self.angles)
- self.filt_id = at.data2d.create('-sino', filt_proj_geom, 0)
- self.sino_id = at.data2d.create('-sino', self.proj_geom, 0)
- self.vol_id = at.data2d.create('-vol',self.vol_geom,0)
- forwProjString = 'FP_CUDA'
- backProjString = 'BP_CUDA'
- cfg = at.astra_dict(backProjString)
- cfg['ProjectionDataId'] = self.sino_id
- cfg['ReconstructionDataId'] = self.vol_id
- self.backProjAlgorithm = at.algorithm.create(cfg)
-
- cfg = at.astra_dict(forwProjString)
- cfg['ProjectionDataId'] = self.sino_id
- cfg['VolumeDataId'] = self.vol_id
- self.forwProjAlgorithm = at.algorithm.create(cfg)
-
- self.T = ASTRAProjector2DTranspose(self)
-
- self.__setOutCircle()
-
- def __setOutCircle(self):
- '''Creates a :class:`numpy.ndarray` mask of a circle'''
- xx, yy = np.mgrid[:self.recSize, :self.recSize]
- mid = (self.recSize-1.)/2.
- circle = (xx - mid) ** 2 + (yy - mid) ** 2
- bnd = self.recSize**2/4.
- self.outCircle=mask_id = at.data2d.create('-vol', self.vol_geom, np.array(circle<=bnd,dtype=np.float))
-
- def backProject(self,sinogram):
- """Backproject a sinogram.
-
- :param sinogram: The sinogram data
- :type sinogram: :class:`numpy.ndarray`
- :returns: :class:`numpy.ndarray` -- The backprojection.
-
- """
- at.data2d.store(self.sino_id,sinogram)
- at.algorithm.run(self.backProjAlgorithm)
- return at.data2d.get(self.vol_id)
-
- def forwProject(self,image):
- """Forward project an image.
-
- :param image: The image data.
- :type image: :class:`numpy.ndarray`
- :returns: :class:`numpy.ndarray` -- The forward projection.
-
- """
- at.data2d.store(self.vol_id,image)
- at.algorithm.run(self.forwProjAlgorithm)
- return at.data2d.get(self.sino_id)
-
- def reconstructWithFullFilter(self,sinogram,filt):
- """Create a FBP reconstruction of the sinogram with a custom filter
-
- :param sinogram: The sinogram data
- :type sinogram: :class:`numpy.ndarray`
- :param filt: 2D custom filter
- :type filt: :class:`numpy.ndarray`
- :returns: :class:`numpy.ndarray` -- The reconstruction.
-
- """
- at.data2d.store(self.sino_id,sinogram)
- at.data2d.store(self.filt_id,filt)
- cfg = at.astra_dict('FBP_CUDA')
- cfg['ProjectionDataId'] = self.sino_id
- cfg['ReconstructionDataId'] = self.vol_id
- cfg['FilterType'] = 'sinogram'
- cfg['FilterSinogramId'] = self.filt_id
- recoAlg = at.algorithm.create(cfg)
- at.algorithm.run(recoAlg,1)
- at.algorithm.delete(recoAlg)
- return at.data2d.get(self.vol_id)
-
- def reconstructWithFilter(self,sinogram,filt):
- """Create a FBP reconstruction of the sinogram with a custom filter
-
- :param sinogram: The sinogram data
- :type sinogram: :class:`numpy.ndarray`
- :param filt: 1D custom filter
- :type filt: :class:`numpy.ndarray`
- :returns: :class:`numpy.ndarray` -- The reconstruction.
-
- """
- at.data2d.store(self.sino_id,sinogram)
- at.data2d.store(self.filt_id,np.tile(filt,(self.nProj,1)))
- cfg = at.astra_dict('FBP_CUDA')
- cfg['ProjectionDataId'] = self.sino_id
- cfg['ReconstructionDataId'] = self.vol_id
- cfg['FilterType'] = 'sinogram'
- cfg['FilterSinogramId'] = self.filt_id
- recoAlg = at.algorithm.create(cfg)
- at.algorithm.run(recoAlg,1)
- at.algorithm.delete(recoAlg)
- return at.data2d.get(self.vol_id)
-
- def reconstruct(self,method,sinogram,nIters=1,fbpfilter=None,maskCircle=False):
- """Helper function to reconstruct a sinogram using the ASTRA toolbox.
-
- This function does not have to be implemented by other projectors, as
- it is not used by PyNN-FBP.
-
- :param method: Name of the reconstruction algorithm.
- :type method: :class:`string`
- :param sinogram: The sinogram data
- :type sinogram: :class:`numpy.ndarray`
- :param nIters: Number of iterations to run.
- :type nIters: :class:`int`
- :param fbpfilter: Optional string to specify FBP filter (hamming, hann, etc)
- :type fbpfilter: :class:`string`
- :param maskCircle: Whether to reconstruct only inside unit circle (default: ``False``)
- :type maskCircle: :class:`bool`
- :returns: :class:`numpy.ndarray` -- The reconstruction.
-
- """
- cfg = at.astra_dict(method)
- if not 'CUDA' in method: raise Exception('Use CUDA algorithms only')
- cfg['ProjectionDataId'] = self.sino_id
- cfg['ReconstructionDataId'] = self.vol_id
- if not fbpfilter==None:
- cfg['FilterType']=fbpfilter
- if maskCircle:
- cfg['options'] = {}
- cfg['options']['ReconstructionMaskId'] = self.outCircle
- recoAlg = at.algorithm.create(cfg)
- at.data2d.store(self.sino_id,sinogram)
- at.data2d.store(self.vol_id,0)
- at.algorithm.run(recoAlg,nIters)
- at.algorithm.delete(recoAlg)
- return at.data2d.get(self.vol_id)
-
- def __mul__(self,data):
- return self.forwProject(data)
-
diff --git a/mrfbp/Reconstructor.py b/mrfbp/Reconstructor.py
deleted file mode 100644
index 2a8b032..0000000
--- a/mrfbp/Reconstructor.py
+++ /dev/null
@@ -1,151 +0,0 @@
-#-----------------------------------------------------------------------
-#Copyright 2014 Daniel M. Pelt
-#
-#Contact: D.M.Pelt@cwi.nl
-#Website: http://www.dmpelt.com
-#
-#
-#This file is part of the PyMR-FBP, a Python implementation of the
-#MR-FBP tomographic reconstruction method.
-#
-#PyMR-FBP is free software: you can redistribute it and/or modify
-#it under the terms of the GNU General Public License as published by
-#the Free Software Foundation, either version 3 of the License, or
-#(at your option) any later version.
-#
-#PyMR-FBP is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-#GNU General Public License for more details.
-#
-#You should have received a copy of the GNU General Public License
-#along with PyMR-FBP. If not, see .
-#
-#-----------------------------------------------------------------------
-
-from . import Reductors
-import numpy as np
-import numpy.linalg as na
-import scipy.ndimage.filters as snf
-
-class Reconstructor:
- '''The standard MR-FBP reconstructor.
-
- :param projector: Projector object that implements reconstructWithFilter
- :param reductor: Reductor to use, if ``None``, ``Reductors.LogSymReductor`` is used
- :param projectorFP: Optional different projector to use for final forward projection.
- '''
- def __init__(self,projector,reductor=None,projectorFP=None):
- self.projector = projector
- if projectorFP==None:
- self.projectorFP = self.projector
- else:
- self.projectorFP = projectorFP
- if reductor==None:
- reductor = Reductors.LogSymReductor(self.projector.filterSize,self.projectorFP.filterSize)
- self.reductor = reductor
- self.__setOutCircle()
-
- def __setOutCircle(self):
- '''Creates a :class:`numpy.ndarray` mask of a circle'''
- xx, yy = np.mgrid[:self.projector.recSize, :self.projector.recSize]
- mid = (self.projector.recSize-1.)/2.
- circle = (xx - mid) ** 2 + (yy - mid) ** 2
- bnd = self.projector.recSize**2/4.
- self.outCircle=circle>bnd
-
- def _calca(self,sinogram):
- '''Returns the MR-FBP system matrix A
-
- :param sinogram: Sinogram to calculate A with
- :type sinogram: :class:`numpy.ndarray`
- '''
- a = np.zeros((self.projectorFP.nDet*self.projectorFP.nProj,int(self.reductor.outSize)),dtype=np.float32)
- for i in xrange(self.reductor.outSize):
- img = self.projector.reconstructWithFilter(sinogram,self.reductor.filters[:,i])
- img[self.outCircle]=0
- a[:,i] = self.projectorFP.forwProject(img).flatten()
- return a
-
- def reconstruct(self,sinogram,cSinogram=None):
- '''Returns MR-FBP reconstruction
-
- :param sinogram: Sinogram to reconstruct
- :type sinogram: :class:`numpy.ndarray`
- :param cSinogram: Optional other sinogram to use as right-hand side.
- :type cSinogram: :class:`numpy.ndarray`
- '''
- if cSinogram==None: cSinogram=sinogram
- a = self._calca(sinogram)
- out = na.lstsq(a,cSinogram.flatten())
- f = self.reductor.getFilter(out[0])
- self.f = f
- return self.projector.reconstructWithFilter(sinogram,f)
-
-class ReconstructorGradient:
- '''The MR-FBP_GM reconstructor.
-
- :param projector: Projector object that implements reconstructWithFilter
- :param reductor: Reductor to use, if ``None``, ``Reductors.LogSymReductor`` is used
- :param projectorFP: Optional different projector to use for final forward projection.
- '''
- def __init__(self,projector,reductor=None,projectorFP=None):
- self.projector = projector
- if projectorFP==None:
- self.projectorFP = self.projector
- else:
- self.projectorFP = projectorFP
- if reductor==None:
- reductor = Reductors.LogSymReductor(self.projector.filterSize,self.projectorFP.filterSize)
- self.reductor = reductor
- self.__setOutCircle()
-
- def __setOutCircle(self):
- '''Creates a :class:`numpy.ndarray` mask of a circle'''
- xx, yy = np.mgrid[:self.projector.recSize, :self.projector.recSize]
- mid = (self.projector.recSize-1.)/2.
- circle = (xx - mid) ** 2 + (yy - mid) ** 2
- bnd = self.projector.recSize**2/4.
- self.outCircle=circle>bnd
-
- def _calca(self,sinogram,lam):
- '''Returns the MR-FBP_GM system matrix A
-
- :param sinogram: Sinogram to calculate A with
- :type sinogram: :class:`numpy.ndarray`
- :param lam: Lambda to relative weight to give to gradient error
- :type lam: :class:`float`
- '''
- nfp = self.projectorFP.nDet*self.projectorFP.nProj
- ngr = self.projector.nDet*self.projector.nDet
- a = np.zeros((nfp+2*ngr,int(self.reductor.outSize)),dtype=np.float32)
- for i in xrange(self.reductor.outSize):
- img = self.projector.reconstructWithFilter(sinogram,self.reductor.filters[:,i])
- xs = snf.sobel(img,0)
- ys = snf.sobel(img,1)
- img[self.outCircle]=0
- xs[self.outCircle]=0
- ys[self.outCircle]=0
- a[0:nfp,i] = self.projectorFP.forwProject(img).flatten()
- a[nfp:nfp+ngr,i] = lam*xs.flatten()
- a[nfp+ngr:nfp+2*ngr,i] = lam*ys.flatten()
- return a
-
- def reconstruct(self,sinogram,lam,cSinogram=None):
- '''Returns MR-FBP_GM reconstruction
-
- :param sinogram: Sinogram to reconstruct
- :type sinogram: :class:`numpy.ndarray`
- :param lam: Lambda to relative weight to give to gradient error
- :type lam: :class:`float`
- :param cSinogram: Optional other sinogram to use as right-hand side.
- :type cSinogram: :class:`numpy.ndarray`
- '''
-
- if cSinogram==None: cSinogram=sinogram
- a = self._calca(sinogram,lam)
- out = na.lstsq(a,np.hstack((cSinogram.flatten(),np.zeros(2*self.projector.nDet*self.projector.nDet))))
- f = self.reductor.getFilter(out[0])
- self.f = f
- return self.projector.reconstructWithFilter(sinogram,f)
-
diff --git a/mrfbp/Reductors.py b/mrfbp/Reductors.py
deleted file mode 100644
index 04b23a4..0000000
--- a/mrfbp/Reductors.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#-----------------------------------------------------------------------
-#Copyright 2014 Daniel M. Pelt
-#
-#Contact: D.M.Pelt@cwi.nl
-#Website: http://www.dmpelt.com
-#
-#
-#This file is part of the PyMR-FBP, a Python implementation of the
-#MR-FBP tomographic reconstruction method.
-#
-#PyMR-FBP is free software: you can redistribute it and/or modify
-#it under the terms of the GNU General Public License as published by
-#the Free Software Foundation, either version 3 of the License, or
-#(at your option) any later version.
-#
-#PyMR-FBP is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-#GNU General Public License for more details.
-#
-#You should have received a copy of the GNU General Public License
-#along with PyMR-FBP. If not, see .
-#
-#-----------------------------------------------------------------------
-
-import math
-import numpy as np
-
-class Reductor(object):
- '''Base object of a ``Reductor``, that takes input data and reduces it.
-
- Implementing objects should define `outSize`, the number of elements after
- reduction, and a ``filters`` :class:`numpy.ndarray` of size ``(inSize,outSize)``, where
- each row is a basis vector in Fourier space.
-
- :param inSize: Input size of vectors.
- :type inSize: :class:`int`
- '''
- def __init__(self,inSize):
- self.size = inSize
- self.inSize = self.size
- def getFilter(self,weights):
- '''Returns actual FBP filters, given the resulting weights of a trained neural network.'''
- return np.dot(self.filters,weights)
-
-class LogSymReductor(Reductor):
- '''An implementation of a ``Reductor`` with exponentially growing bin widths, and symmetric bins.
-
- :param nLinear: Number of bins of width 1 before starting exponential growth.'
- :type nLinear: :class:`int`
- '''
- def __init__(self,size,fpSize,nLinear=2):
- Reductor.__init__(self,size)
- self.name="LogSym"
- cW=0
- nW=0
- width=1
- nL = nLinear
- while cW<(fpSize-1)/2:
- if nL>0:
- nL-=1
- cW += 1
- else:
- cW += width
- width*=2
- nW+=1
-
- self.filters = np.zeros((size,nW))
- x = np.linspace(0,2*np.pi,size,False)
- cW=0
- nW=0
- width=1
- nL = nLinear
- while cW<(fpSize-1)/2:
- if nL>0:
- nL-=1
- eW = cW+1
- else:
- eW = cW+width
- width*=2
- self.filters[:,nW] += np.cos(np.outer(np.arange(cW,eW),x)).sum(0)
- cW=eW
- nW+=1
- self.outSize = nW
diff --git a/mrfbp/__init__.py b/mrfbp/__init__.py
index 6729c2d..78f61a5 100644
--- a/mrfbp/__init__.py
+++ b/mrfbp/__init__.py
@@ -23,5 +23,4 @@
#
#-----------------------------------------------------------------------
-from .Reconstructor import *
from .astra_plugin import plugin
\ No newline at end of file