From 2bc0d98c413fee4108115f26aa337f65337eec55 Mon Sep 17 00:00:00 2001 From: Daan Pelt Date: Thu, 26 Mar 2015 16:40:38 +0100 Subject: Add SPOT-like object for Python (overrides `__mul__` and works with scipy.sparse.linalg) --- python/astra/ASTRAProjector.py | 135 ------------------------- python/astra/__init__.py | 2 +- python/astra/operator.py | 208 +++++++++++++++++++++++++++++++++++++++ python/docSRC/ASTRAProjector.rst | 8 -- python/docSRC/index.rst | 2 +- python/docSRC/operator.rst | 8 ++ 6 files changed, 218 insertions(+), 145 deletions(-) delete mode 100644 python/astra/ASTRAProjector.py create mode 100644 python/astra/operator.py delete mode 100644 python/docSRC/ASTRAProjector.rst create mode 100644 python/docSRC/operator.rst (limited to 'python') diff --git a/python/astra/ASTRAProjector.py b/python/astra/ASTRAProjector.py deleted file mode 100644 index f282618..0000000 --- a/python/astra/ASTRAProjector.py +++ /dev/null @@ -1,135 +0,0 @@ -#----------------------------------------------------------------------- -#Copyright 2013 Centrum Wiskunde & Informatica, Amsterdam -# -#Author: Daniel M. Pelt -#Contact: D.M.Pelt@cwi.nl -#Website: http://dmpelt.github.io/pyastratoolbox/ -# -# -#This file is part of the Python interface to the -#All Scale Tomographic Reconstruction Antwerp Toolbox ("ASTRA Toolbox"). -# -#The Python interface to the ASTRA Toolbox 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. -# -#The Python interface to the ASTRA Toolbox 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 the Python interface to the ASTRA Toolbox. If not, see . -# -#----------------------------------------------------------------------- - -import math -from . import creators as ac -from . import data2d - - -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): - """Helps with various common ASTRA Toolbox 2D operations. - - This class can perform several often used toolbox operations, such as: - - * Forward projecting - * Back projecting - * Reconstructing - - Note that this class has a some computational overhead, because it - copies a lot of data. If you use many repeated operations, directly - using the PyAstraToolbox methods directly is faster. - - 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 proj_type: Projector type, such as ``'line'``, ``'linear'``, ... - :type proj_type: :class:`string` - """ - - def __init__(self, proj_geom, vol_geom, proj_type): - 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 - self.proj_id = ac.create_projector(proj_type, proj_geom, vol_geom) - self.T = ASTRAProjector2DTranspose(self) - - def backProject(self, data): - """Backproject a sinogram. - - :param data: The sinogram data or ID. - :type data: :class:`numpy.ndarray` or :class:`int` - :returns: :class:`numpy.ndarray` -- The backprojection. - - """ - vol_id, vol = ac.create_backprojection( - data, self.proj_id, returnData=True) - data2d.delete(vol_id) - return vol - - def forwardProject(self, data): - """Forward project an image. - - :param data: The image data or ID. - :type data: :class:`numpy.ndarray` or :class:`int` - :returns: :class:`numpy.ndarray` -- The forward projection. - - """ - sin_id, sino = ac.create_sino(data, self.proj_id, returnData=True) - data2d.delete(sin_id) - return sino - - def reconstruct(self, data, method, **kwargs): - """Reconstruct an image from a sinogram. - - :param data: The sinogram data or ID. - :type data: :class:`numpy.ndarray` or :class:`int` - :param method: Name of the reconstruction algorithm. - :type method: :class:`string` - :param kwargs: Additional named parameters to pass to :func:`astra.creators.create_reconstruction`. - :returns: :class:`numpy.ndarray` -- The reconstruction. - - Example of a SIRT reconstruction using CUDA:: - - proj = ASTRAProjector2D(...) - rec = proj.reconstruct(sinogram,'SIRT_CUDA',iterations=1000) - - """ - kwargs['returnData'] = True - rec_id, rec = ac.create_reconstruction( - method, self.proj_id, data, **kwargs) - data2d.delete(rec_id) - return rec - - def __mul__(self, data): - return self.forwardProject(data) diff --git a/python/astra/__init__.py b/python/astra/__init__.py index 063dc16..8c1740c 100644 --- a/python/astra/__init__.py +++ b/python/astra/__init__.py @@ -27,7 +27,6 @@ from . import matlab as m from .creators import astra_dict,create_vol_geom, create_proj_geom, create_backprojection, create_sino, create_reconstruction, create_projector,create_sino3d_gpu, create_backprojection3d_gpu from .functions import data_op, add_noise_to_sino, clear, move_vol_geom from .extrautils import clipCircle -from .ASTRAProjector import ASTRAProjector2D from . import data2d from . import astra from . import data3d @@ -36,6 +35,7 @@ from . import projector from . import projector3d from . import matrix from . import log +from .operator import OpTomo import os try: diff --git a/python/astra/operator.py b/python/astra/operator.py new file mode 100644 index 0000000..a3abd5a --- /dev/null +++ b/python/astra/operator.py @@ -0,0 +1,208 @@ +#----------------------------------------------------------------------- +#Copyright 2013 Centrum Wiskunde & Informatica, Amsterdam +# +#Author: Daniel M. Pelt +#Contact: D.M.Pelt@cwi.nl +#Website: http://dmpelt.github.io/pyastratoolbox/ +# +# +#This file is part of the Python interface to the +#All Scale Tomographic Reconstruction Antwerp Toolbox ("ASTRA Toolbox"). +# +#The Python interface to the ASTRA Toolbox 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. +# +#The Python interface to the ASTRA Toolbox 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 the Python interface to the ASTRA Toolbox. If not, see . +# +#----------------------------------------------------------------------- + +from . import data2d +from . import data3d +from . import projector +from . import projector3d +from . import creators +from . import algorithm +from . import functions +import numpy as np +from six.moves import range, reduce +import operator +import scipy.sparse.linalg + +class OpTomo(scipy.sparse.linalg.LinearOperator): + """Object that imitates a projection matrix with a given projector. + + This object can do forward projection by using the ``*`` operator:: + + W = astra.OpTomo(proj_id) + fp = W*image + bp = W.T*sinogram + + It can also be used in minimization methods of the :mod:`scipy.sparse.linalg` module:: + + W = astra.OpTomo(proj_id) + output = scipy.sparse.linalg.lsqr(W,sinogram) + + :param proj_id: ID to a projector. + :type proj_id: :class:`int` + """ + + def __init__(self,proj_id): + self.dtype = np.float32 + try: + self.vg = projector.volume_geometry(proj_id) + self.pg = projector.projection_geometry(proj_id) + self.data_mod = data2d + self.appendString = "" + if projector.is_cuda(proj_id): + self.appendString += "_CUDA" + except Exception: + self.vg = projector3d.volume_geometry(proj_id) + self.pg = projector3d.projection_geometry(proj_id) + self.data_mod = data3d + self.appendString = "3D" + if projector3d.is_cuda(proj_id): + self.appendString += "_CUDA" + + self.vshape = functions.geom_size(self.vg) + self.vsize = reduce(operator.mul,self.vshape) + self.sshape = functions.geom_size(self.pg) + self.ssize = reduce(operator.mul,self.sshape) + + self.shape = (self.ssize, self.vsize) + + self.proj_id = proj_id + + self.T = OpTomoTranspose(self) + + def __checkArray(self, arr, shp): + if len(arr.shape)==1: + arr = arr.reshape(shp) + if arr.dtype != np.float32: + arr = arr.astype(np.float32) + if arr.flags['C_CONTIGUOUS']==False: + arr = np.ascontiguousarray(arr) + return arr + + def matvec(self,v): + """Implements the forward operator. + + :param v: Volume to forward project. + :type v: :class:`numpy.ndarray` + """ + v = self.__checkArray(v, self.vshape) + vid = self.data_mod.link('-vol',self.vg,v) + s = np.zeros(self.sshape,dtype=np.float32) + sid = self.data_mod.link('-sino',self.pg,s) + + cfg = creators.astra_dict('FP'+self.appendString) + cfg['ProjectionDataId'] = sid + cfg['VolumeDataId'] = vid + cfg['ProjectorId'] = self.proj_id + fp_id = algorithm.create(cfg) + algorithm.run(fp_id) + + algorithm.delete(fp_id) + self.data_mod.delete([vid,sid]) + return s.flatten() + + def rmatvec(self,s): + """Implements the transpose operator. + + :param s: The projection data. + :type s: :class:`numpy.ndarray` + """ + s = self.__checkArray(s, self.sshape) + sid = self.data_mod.link('-sino',self.pg,s) + v = np.zeros(self.vshape,dtype=np.float32) + vid = self.data_mod.link('-vol',self.vg,v) + + cfg = creators.astra_dict('BP'+self.appendString) + cfg['ProjectionDataId'] = sid + cfg['ReconstructionDataId'] = vid + cfg['ProjectorId'] = self.proj_id + bp_id = algorithm.create(cfg) + algorithm.run(bp_id) + + algorithm.delete(bp_id) + self.data_mod.delete([vid,sid]) + return v.flatten() + + def matmat(self,m): + """Implements the forward operator with a matrix. + + :param m: Volumes to forward project, arranged in columns. + :type m: :class:`numpy.ndarray` + """ + out = np.zeros((self.ssize,m.shape[1]),dtype=np.float32) + for i in range(m.shape[1]): + out[:,i] = self.matvec(m[:,i].flatten()) + return out + + def __mul__(self,v): + """Provides easy forward operator by *. + + :param v: Volume to forward project. + :type v: :class:`numpy.ndarray` + """ + return self.matvec(v) + + def reconstruct(self, method, s, iterations=1, extraOptions = {}): + """Reconstruct an object. + + :param method: Method to use for reconstruction. + :type method: :class:`string` + :param s: The projection data. + :type s: :class:`numpy.ndarray` + :param iterations: Number of iterations to use. + :type iterations: :class:`int` + :param extraOptions: Extra options to use during reconstruction (i.e. for cfg['option']). + :type extraOptions: :class:`dict` + """ + self.__checkArray(s, self.sshape) + sid = self.data_mod.link('-sino',self.pg,s) + v = np.zeros(self.vshape,dtype=np.float32) + vid = self.data_mod.link('-vol',self.vg,v) + cfg = creators.astra_dict(method) + cfg['ProjectionDataId'] = sid + cfg['ReconstructionDataId'] = vid + cfg['ProjectorId'] = self.proj_id + cfg['option'] = extraOptions + alg_id = algorithm.create(cfg) + algorithm.run(alg_id,iterations) + algorithm.delete(alg_id) + self.data_mod.delete([vid,sid]) + return v + +class OpTomoTranspose(scipy.sparse.linalg.LinearOperator): + """This object provides the transpose operation (``.T``) of the OpTomo object. + + Do not use directly, since it can be accessed as member ``.T`` of + an :class:`OpTomo` object. + """ + def __init__(self,parent): + self.parent = parent + self.dtype = np.float32 + self.shape = (parent.shape[1], parent.shape[0]) + + def matvec(self, s): + return self.parent.rmatvec(s) + + def rmatvec(self, v): + return self.parent.matvec(v) + + def matmat(self, m): + out = np.zeros((self.vsize,m.shape[1]),dtype=np.float32) + for i in range(m.shape[1]): + out[:,i] = self.matvec(m[:,i].flatten()) + return out + + def __mul__(self,v): + return self.matvec(v) diff --git a/python/docSRC/ASTRAProjector.rst b/python/docSRC/ASTRAProjector.rst deleted file mode 100644 index 1c267e3..0000000 --- a/python/docSRC/ASTRAProjector.rst +++ /dev/null @@ -1,8 +0,0 @@ -Helper class: the :mod:`ASTRAProjector` module -============================================== - -.. automodule:: astra.ASTRAProjector - :members: - :undoc-members: - :show-inheritance: - diff --git a/python/docSRC/index.rst b/python/docSRC/index.rst index 8d17a4a..b7cc6d6 100644 --- a/python/docSRC/index.rst +++ b/python/docSRC/index.rst @@ -18,7 +18,7 @@ Contents: matrix creators functions - ASTRAProjector + operator matlab astra .. astra diff --git a/python/docSRC/operator.rst b/python/docSRC/operator.rst new file mode 100644 index 0000000..f5369fa --- /dev/null +++ b/python/docSRC/operator.rst @@ -0,0 +1,8 @@ +OpTomo class: the :mod:`operator` module +============================================== + +.. automodule:: astra.operator + :members: + :undoc-members: + :show-inheritance: + -- cgit v1.2.3 From b9b9c82f8634f9c77416de5e857d107005cccbdf Mon Sep 17 00:00:00 2001 From: "Daniel M. Pelt" Date: Thu, 7 May 2015 15:40:17 +0200 Subject: Use superclass __mul__ in Python OpTomo __mul__ --- python/astra/operator.py | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) (limited to 'python') diff --git a/python/astra/operator.py b/python/astra/operator.py index a3abd5a..0c37353 100644 --- a/python/astra/operator.py +++ b/python/astra/operator.py @@ -91,7 +91,7 @@ class OpTomo(scipy.sparse.linalg.LinearOperator): arr = np.ascontiguousarray(arr) return arr - def matvec(self,v): + def _matvec(self,v): """Implements the forward operator. :param v: Volume to forward project. @@ -135,24 +135,16 @@ class OpTomo(scipy.sparse.linalg.LinearOperator): self.data_mod.delete([vid,sid]) return v.flatten() - def matmat(self,m): - """Implements the forward operator with a matrix. - - :param m: Volumes to forward project, arranged in columns. - :type m: :class:`numpy.ndarray` - """ - out = np.zeros((self.ssize,m.shape[1]),dtype=np.float32) - for i in range(m.shape[1]): - out[:,i] = self.matvec(m[:,i].flatten()) - return out - def __mul__(self,v): """Provides easy forward operator by *. :param v: Volume to forward project. :type v: :class:`numpy.ndarray` """ - return self.matvec(v) + # Catch the case of a forward projection of a 2D/3D image + if isinstance(v, np.ndarray) and v.shape==self.vshape: + return self._matvec(v) + return scipy.sparse.linalg.LinearOperator.__mul__(self, v) def reconstruct(self, method, s, iterations=1, extraOptions = {}): """Reconstruct an object. @@ -192,17 +184,14 @@ class OpTomoTranspose(scipy.sparse.linalg.LinearOperator): self.dtype = np.float32 self.shape = (parent.shape[1], parent.shape[0]) - def matvec(self, s): + def _matvec(self, s): return self.parent.rmatvec(s) def rmatvec(self, v): return self.parent.matvec(v) - def matmat(self, m): - out = np.zeros((self.vsize,m.shape[1]),dtype=np.float32) - for i in range(m.shape[1]): - out[:,i] = self.matvec(m[:,i].flatten()) - return out - - def __mul__(self,v): - return self.matvec(v) + def __mul__(self,s): + # Catch the case of a backprojection of 2D/3D data + if isinstance(s, np.ndarray) and s.shape==self.parent.sshape: + return self._matvec(s) + return scipy.sparse.linalg.LinearOperator.__mul__(self, s) -- cgit v1.2.3 From 89da933904262d6b7e80e8adf85ca9d1273881b3 Mon Sep 17 00:00:00 2001 From: "Daniel M. Pelt" Date: Fri, 8 May 2015 13:48:39 +0200 Subject: Rename optomo.py for Python 2 support --- python/astra/__init__.py | 2 +- python/astra/operator.py | 197 ----------------------------------------------- python/astra/optomo.py | 197 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 198 deletions(-) delete mode 100644 python/astra/operator.py create mode 100644 python/astra/optomo.py (limited to 'python') diff --git a/python/astra/__init__.py b/python/astra/__init__.py index 8c1740c..6c15d30 100644 --- a/python/astra/__init__.py +++ b/python/astra/__init__.py @@ -35,7 +35,7 @@ from . import projector from . import projector3d from . import matrix from . import log -from .operator import OpTomo +from .optomo import OpTomo import os try: diff --git a/python/astra/operator.py b/python/astra/operator.py deleted file mode 100644 index 0c37353..0000000 --- a/python/astra/operator.py +++ /dev/null @@ -1,197 +0,0 @@ -#----------------------------------------------------------------------- -#Copyright 2013 Centrum Wiskunde & Informatica, Amsterdam -# -#Author: Daniel M. Pelt -#Contact: D.M.Pelt@cwi.nl -#Website: http://dmpelt.github.io/pyastratoolbox/ -# -# -#This file is part of the Python interface to the -#All Scale Tomographic Reconstruction Antwerp Toolbox ("ASTRA Toolbox"). -# -#The Python interface to the ASTRA Toolbox 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. -# -#The Python interface to the ASTRA Toolbox 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 the Python interface to the ASTRA Toolbox. If not, see . -# -#----------------------------------------------------------------------- - -from . import data2d -from . import data3d -from . import projector -from . import projector3d -from . import creators -from . import algorithm -from . import functions -import numpy as np -from six.moves import range, reduce -import operator -import scipy.sparse.linalg - -class OpTomo(scipy.sparse.linalg.LinearOperator): - """Object that imitates a projection matrix with a given projector. - - This object can do forward projection by using the ``*`` operator:: - - W = astra.OpTomo(proj_id) - fp = W*image - bp = W.T*sinogram - - It can also be used in minimization methods of the :mod:`scipy.sparse.linalg` module:: - - W = astra.OpTomo(proj_id) - output = scipy.sparse.linalg.lsqr(W,sinogram) - - :param proj_id: ID to a projector. - :type proj_id: :class:`int` - """ - - def __init__(self,proj_id): - self.dtype = np.float32 - try: - self.vg = projector.volume_geometry(proj_id) - self.pg = projector.projection_geometry(proj_id) - self.data_mod = data2d - self.appendString = "" - if projector.is_cuda(proj_id): - self.appendString += "_CUDA" - except Exception: - self.vg = projector3d.volume_geometry(proj_id) - self.pg = projector3d.projection_geometry(proj_id) - self.data_mod = data3d - self.appendString = "3D" - if projector3d.is_cuda(proj_id): - self.appendString += "_CUDA" - - self.vshape = functions.geom_size(self.vg) - self.vsize = reduce(operator.mul,self.vshape) - self.sshape = functions.geom_size(self.pg) - self.ssize = reduce(operator.mul,self.sshape) - - self.shape = (self.ssize, self.vsize) - - self.proj_id = proj_id - - self.T = OpTomoTranspose(self) - - def __checkArray(self, arr, shp): - if len(arr.shape)==1: - arr = arr.reshape(shp) - if arr.dtype != np.float32: - arr = arr.astype(np.float32) - if arr.flags['C_CONTIGUOUS']==False: - arr = np.ascontiguousarray(arr) - return arr - - def _matvec(self,v): - """Implements the forward operator. - - :param v: Volume to forward project. - :type v: :class:`numpy.ndarray` - """ - v = self.__checkArray(v, self.vshape) - vid = self.data_mod.link('-vol',self.vg,v) - s = np.zeros(self.sshape,dtype=np.float32) - sid = self.data_mod.link('-sino',self.pg,s) - - cfg = creators.astra_dict('FP'+self.appendString) - cfg['ProjectionDataId'] = sid - cfg['VolumeDataId'] = vid - cfg['ProjectorId'] = self.proj_id - fp_id = algorithm.create(cfg) - algorithm.run(fp_id) - - algorithm.delete(fp_id) - self.data_mod.delete([vid,sid]) - return s.flatten() - - def rmatvec(self,s): - """Implements the transpose operator. - - :param s: The projection data. - :type s: :class:`numpy.ndarray` - """ - s = self.__checkArray(s, self.sshape) - sid = self.data_mod.link('-sino',self.pg,s) - v = np.zeros(self.vshape,dtype=np.float32) - vid = self.data_mod.link('-vol',self.vg,v) - - cfg = creators.astra_dict('BP'+self.appendString) - cfg['ProjectionDataId'] = sid - cfg['ReconstructionDataId'] = vid - cfg['ProjectorId'] = self.proj_id - bp_id = algorithm.create(cfg) - algorithm.run(bp_id) - - algorithm.delete(bp_id) - self.data_mod.delete([vid,sid]) - return v.flatten() - - def __mul__(self,v): - """Provides easy forward operator by *. - - :param v: Volume to forward project. - :type v: :class:`numpy.ndarray` - """ - # Catch the case of a forward projection of a 2D/3D image - if isinstance(v, np.ndarray) and v.shape==self.vshape: - return self._matvec(v) - return scipy.sparse.linalg.LinearOperator.__mul__(self, v) - - def reconstruct(self, method, s, iterations=1, extraOptions = {}): - """Reconstruct an object. - - :param method: Method to use for reconstruction. - :type method: :class:`string` - :param s: The projection data. - :type s: :class:`numpy.ndarray` - :param iterations: Number of iterations to use. - :type iterations: :class:`int` - :param extraOptions: Extra options to use during reconstruction (i.e. for cfg['option']). - :type extraOptions: :class:`dict` - """ - self.__checkArray(s, self.sshape) - sid = self.data_mod.link('-sino',self.pg,s) - v = np.zeros(self.vshape,dtype=np.float32) - vid = self.data_mod.link('-vol',self.vg,v) - cfg = creators.astra_dict(method) - cfg['ProjectionDataId'] = sid - cfg['ReconstructionDataId'] = vid - cfg['ProjectorId'] = self.proj_id - cfg['option'] = extraOptions - alg_id = algorithm.create(cfg) - algorithm.run(alg_id,iterations) - algorithm.delete(alg_id) - self.data_mod.delete([vid,sid]) - return v - -class OpTomoTranspose(scipy.sparse.linalg.LinearOperator): - """This object provides the transpose operation (``.T``) of the OpTomo object. - - Do not use directly, since it can be accessed as member ``.T`` of - an :class:`OpTomo` object. - """ - def __init__(self,parent): - self.parent = parent - self.dtype = np.float32 - self.shape = (parent.shape[1], parent.shape[0]) - - def _matvec(self, s): - return self.parent.rmatvec(s) - - def rmatvec(self, v): - return self.parent.matvec(v) - - def __mul__(self,s): - # Catch the case of a backprojection of 2D/3D data - if isinstance(s, np.ndarray) and s.shape==self.parent.sshape: - return self._matvec(s) - return scipy.sparse.linalg.LinearOperator.__mul__(self, s) diff --git a/python/astra/optomo.py b/python/astra/optomo.py new file mode 100644 index 0000000..0c37353 --- /dev/null +++ b/python/astra/optomo.py @@ -0,0 +1,197 @@ +#----------------------------------------------------------------------- +#Copyright 2013 Centrum Wiskunde & Informatica, Amsterdam +# +#Author: Daniel M. Pelt +#Contact: D.M.Pelt@cwi.nl +#Website: http://dmpelt.github.io/pyastratoolbox/ +# +# +#This file is part of the Python interface to the +#All Scale Tomographic Reconstruction Antwerp Toolbox ("ASTRA Toolbox"). +# +#The Python interface to the ASTRA Toolbox 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. +# +#The Python interface to the ASTRA Toolbox 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 the Python interface to the ASTRA Toolbox. If not, see . +# +#----------------------------------------------------------------------- + +from . import data2d +from . import data3d +from . import projector +from . import projector3d +from . import creators +from . import algorithm +from . import functions +import numpy as np +from six.moves import range, reduce +import operator +import scipy.sparse.linalg + +class OpTomo(scipy.sparse.linalg.LinearOperator): + """Object that imitates a projection matrix with a given projector. + + This object can do forward projection by using the ``*`` operator:: + + W = astra.OpTomo(proj_id) + fp = W*image + bp = W.T*sinogram + + It can also be used in minimization methods of the :mod:`scipy.sparse.linalg` module:: + + W = astra.OpTomo(proj_id) + output = scipy.sparse.linalg.lsqr(W,sinogram) + + :param proj_id: ID to a projector. + :type proj_id: :class:`int` + """ + + def __init__(self,proj_id): + self.dtype = np.float32 + try: + self.vg = projector.volume_geometry(proj_id) + self.pg = projector.projection_geometry(proj_id) + self.data_mod = data2d + self.appendString = "" + if projector.is_cuda(proj_id): + self.appendString += "_CUDA" + except Exception: + self.vg = projector3d.volume_geometry(proj_id) + self.pg = projector3d.projection_geometry(proj_id) + self.data_mod = data3d + self.appendString = "3D" + if projector3d.is_cuda(proj_id): + self.appendString += "_CUDA" + + self.vshape = functions.geom_size(self.vg) + self.vsize = reduce(operator.mul,self.vshape) + self.sshape = functions.geom_size(self.pg) + self.ssize = reduce(operator.mul,self.sshape) + + self.shape = (self.ssize, self.vsize) + + self.proj_id = proj_id + + self.T = OpTomoTranspose(self) + + def __checkArray(self, arr, shp): + if len(arr.shape)==1: + arr = arr.reshape(shp) + if arr.dtype != np.float32: + arr = arr.astype(np.float32) + if arr.flags['C_CONTIGUOUS']==False: + arr = np.ascontiguousarray(arr) + return arr + + def _matvec(self,v): + """Implements the forward operator. + + :param v: Volume to forward project. + :type v: :class:`numpy.ndarray` + """ + v = self.__checkArray(v, self.vshape) + vid = self.data_mod.link('-vol',self.vg,v) + s = np.zeros(self.sshape,dtype=np.float32) + sid = self.data_mod.link('-sino',self.pg,s) + + cfg = creators.astra_dict('FP'+self.appendString) + cfg['ProjectionDataId'] = sid + cfg['VolumeDataId'] = vid + cfg['ProjectorId'] = self.proj_id + fp_id = algorithm.create(cfg) + algorithm.run(fp_id) + + algorithm.delete(fp_id) + self.data_mod.delete([vid,sid]) + return s.flatten() + + def rmatvec(self,s): + """Implements the transpose operator. + + :param s: The projection data. + :type s: :class:`numpy.ndarray` + """ + s = self.__checkArray(s, self.sshape) + sid = self.data_mod.link('-sino',self.pg,s) + v = np.zeros(self.vshape,dtype=np.float32) + vid = self.data_mod.link('-vol',self.vg,v) + + cfg = creators.astra_dict('BP'+self.appendString) + cfg['ProjectionDataId'] = sid + cfg['ReconstructionDataId'] = vid + cfg['ProjectorId'] = self.proj_id + bp_id = algorithm.create(cfg) + algorithm.run(bp_id) + + algorithm.delete(bp_id) + self.data_mod.delete([vid,sid]) + return v.flatten() + + def __mul__(self,v): + """Provides easy forward operator by *. + + :param v: Volume to forward project. + :type v: :class:`numpy.ndarray` + """ + # Catch the case of a forward projection of a 2D/3D image + if isinstance(v, np.ndarray) and v.shape==self.vshape: + return self._matvec(v) + return scipy.sparse.linalg.LinearOperator.__mul__(self, v) + + def reconstruct(self, method, s, iterations=1, extraOptions = {}): + """Reconstruct an object. + + :param method: Method to use for reconstruction. + :type method: :class:`string` + :param s: The projection data. + :type s: :class:`numpy.ndarray` + :param iterations: Number of iterations to use. + :type iterations: :class:`int` + :param extraOptions: Extra options to use during reconstruction (i.e. for cfg['option']). + :type extraOptions: :class:`dict` + """ + self.__checkArray(s, self.sshape) + sid = self.data_mod.link('-sino',self.pg,s) + v = np.zeros(self.vshape,dtype=np.float32) + vid = self.data_mod.link('-vol',self.vg,v) + cfg = creators.astra_dict(method) + cfg['ProjectionDataId'] = sid + cfg['ReconstructionDataId'] = vid + cfg['ProjectorId'] = self.proj_id + cfg['option'] = extraOptions + alg_id = algorithm.create(cfg) + algorithm.run(alg_id,iterations) + algorithm.delete(alg_id) + self.data_mod.delete([vid,sid]) + return v + +class OpTomoTranspose(scipy.sparse.linalg.LinearOperator): + """This object provides the transpose operation (``.T``) of the OpTomo object. + + Do not use directly, since it can be accessed as member ``.T`` of + an :class:`OpTomo` object. + """ + def __init__(self,parent): + self.parent = parent + self.dtype = np.float32 + self.shape = (parent.shape[1], parent.shape[0]) + + def _matvec(self, s): + return self.parent.rmatvec(s) + + def rmatvec(self, v): + return self.parent.matvec(v) + + def __mul__(self,s): + # Catch the case of a backprojection of 2D/3D data + if isinstance(s, np.ndarray) and s.shape==self.parent.sshape: + return self._matvec(s) + return scipy.sparse.linalg.LinearOperator.__mul__(self, s) -- cgit v1.2.3