summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Python/CMakeLists.txt6
-rw-r--r--src/Python/FindAnacondaEnvironment.cmake7
-rw-r--r--src/Python/ccpi/imaging/Regularizer.py6
-rw-r--r--src/Python/ccpi/reconstruction/AstraDevice.py83
-rw-r--r--src/Python/ccpi/reconstruction/DeviceModel.py63
-rw-r--r--src/Python/ccpi/reconstruction/FISTAReconstructor.py119
-rw-r--r--src/Python/fista_module.cpp5
-rw-r--r--src/Python/test/test_reconstructor.py100
8 files changed, 320 insertions, 69 deletions
diff --git a/src/Python/CMakeLists.txt b/src/Python/CMakeLists.txt
index 1b73380..506159a 100644
--- a/src/Python/CMakeLists.txt
+++ b/src/Python/CMakeLists.txt
@@ -114,6 +114,8 @@ endif()
# fista reconstructor
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/ccpi/reconstruction/FISTAReconstructor.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/ccpi/reconstruction)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/ccpi/reconstruction/__init__.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/ccpi/reconstruction)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/ccpi/reconstruction/DeviceModel.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/ccpi/reconstruction)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/ccpi/reconstruction/AstraDevice.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/ccpi/reconstruction)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup-fista.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup-fista.py)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/fista-recipe)
@@ -158,13 +160,13 @@ add_custom_target(regularizers
)
add_custom_target(install-fista
- COMMAND conda
+ COMMAND ${CONDA_EXECUTABLE}
install --force --use-local ccpi-fista=${CIL_VERSION} -c ccpi -c conda-forge
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
add_custom_target(install-regularizers
- COMMAND conda
+ COMMAND ${CONDA_EXECUTABLE}
install --force --use-local ccpi-regularizers=${CIL_VERSION} -c ccpi -c conda-forge
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
diff --git a/src/Python/FindAnacondaEnvironment.cmake b/src/Python/FindAnacondaEnvironment.cmake
index fa4637a..6475128 100644
--- a/src/Python/FindAnacondaEnvironment.cmake
+++ b/src/Python/FindAnacondaEnvironment.cmake
@@ -97,7 +97,12 @@ function (findPythonForAnacondaEnvironment env)
set (PYTHON_VERSION_MINOR ${_PYTHON_VERSION_MINOR} PARENT_SCOPE)
set (PYTHON_VERSION_PATCH ${_PYTHON_VERSION_PATCH} PARENT_SCOPE)
message("My version found " ${PYTHON_VERSION_STRING})
-
+ ## find conda executable
+ if (WIN32)
+ set (CONDA_EXECUTABLE ${env}/Script/conda PARENT_SCOPE)
+ elseif(UNIX)
+ set (CONDA_EXECUTABLE ${env}/bin/conda PARENT_SCOPE)
+ endif()
endfunction()
diff --git a/src/Python/ccpi/imaging/Regularizer.py b/src/Python/ccpi/imaging/Regularizer.py
index fb9ae08..8ab6c6a 100644
--- a/src/Python/ccpi/imaging/Regularizer.py
+++ b/src/Python/ccpi/imaging/Regularizer.py
@@ -216,13 +216,13 @@ class Regularizer():
#assuming it's 3D
# run independent calls on each slice
out3d = input.copy()
- for i in range(np.shape(input)[2]):
- out = self.algorithm(input, regularization_parameter,
+ for i in range(np.shape(input)[0]):
+ out = self.algorithm(input[i], regularization_parameter,
self.pars['first_order_term'] ,
self.pars['second_order_term'] ,
self.pars['number_of_iterations'])
# copy the result in the 3D image
- out3d.T[i] = out[0].copy()
+ out3d[i] = out[0].copy()
# append the rest of the info that the algorithm returns
output = [out3d]
for i in range(1,len(out)):
diff --git a/src/Python/ccpi/reconstruction/AstraDevice.py b/src/Python/ccpi/reconstruction/AstraDevice.py
new file mode 100644
index 0000000..ac9206e
--- /dev/null
+++ b/src/Python/ccpi/reconstruction/AstraDevice.py
@@ -0,0 +1,83 @@
+import astra
+from ccpi.reconstruction.DeviceModel import DeviceModel
+import numpy
+
+class AstraDevice(DeviceModel):
+ '''Concrete class for Astra Device'''
+
+ def __init__(self,
+ device_type,
+ data_aquisition_geometry,
+ reconstructed_volume_geometry):
+
+ super(AstraDevice, self).__init__(device_type,
+ data_aquisition_geometry,
+ reconstructed_volume_geometry)
+
+ self.type = device_type
+ self.proj_geom = astra.creators.create_proj_geom(
+ device_type,
+ self.acquisition_data_geometry['detectorSpacingX'],
+ self.acquisition_data_geometry['detectorSpacingY'],
+ self.acquisition_data_geometry['cameraX'],
+ self.acquisition_data_geometry['cameraY'],
+ self.acquisition_data_geometry['angles'],
+ )
+
+ self.vol_geom = astra.creators.create_vol_geom(
+ self.reconstructed_volume_geometry['X'],
+ self.reconstructed_volume_geometry['Y'],
+ self.reconstructed_volume_geometry['Z']
+ )
+
+ def doForwardProject(self, volume):
+ '''Forward projects the volume according to the device geometry
+
+Uses Astra-toolbox
+'''
+
+ try:
+ sino_id, y = astra.creators.create_sino3d_gpu(
+ volume, self.proj_geom, self.vol_geom)
+ astra.matlab.data3d('delete', sino_id)
+ return y
+ except Exception(e):
+ print(e)
+ print("Value Error: ", self.proj_geom, self.vol_geom)
+
+ def doBackwardProject(self, projections):
+ '''Backward projects the projections according to the device geometry
+
+Uses Astra-toolbox
+'''
+ idx, volume = \
+ astra.creators.create_backprojection3d_gpu(
+ projections,
+ self.proj_geom,
+ self.vol_geom)
+
+ astra.matlab.data3d('delete', idx)
+ return volume
+
+ def createReducedDevice(self):
+ proj_geom = [
+ self.acquisition_data_geometry['cameraX'],
+ 1,
+ self.acquisition_data_geometry['detectorSpacingX'],
+ self.acquisition_data_geometry['detectorSpacingY'],
+ self.acquisition_data_geometry['angles']
+ ]
+
+ vol_geom = [
+ self.reconstructed_volume_geometry['X'],
+ self.reconstructed_volume_geometry['Y'],
+ 1
+ ]
+ return AstraDevice(self.type, proj_geom, vol_geom)
+
+
+
+if __name__=="main":
+ a = AstraDevice()
+
+
diff --git a/src/Python/ccpi/reconstruction/DeviceModel.py b/src/Python/ccpi/reconstruction/DeviceModel.py
new file mode 100644
index 0000000..eeb9a34
--- /dev/null
+++ b/src/Python/ccpi/reconstruction/DeviceModel.py
@@ -0,0 +1,63 @@
+from abc import ABCMeta, abstractmethod
+from enum import Enum
+
+class DeviceModel(metaclass=ABCMeta):
+ '''Abstract class that defines the device for projection and backprojection
+
+This class defines the methods that must be implemented by concrete classes.
+
+ '''
+
+ class DeviceType(Enum):
+ '''Type of device
+PARALLEL BEAM
+PARALLEL BEAM 3D
+CONE BEAM
+HELICAL'''
+
+ PARALLEL = 'parallel'
+ PARALLEL3D = 'parallel3d'
+ CONE_BEAM = 'cone-beam'
+ HELICAL = 'helical'
+
+ def __init__(self,
+ device_type,
+ data_aquisition_geometry,
+ reconstructed_volume_geometry):
+ '''Initializes the class
+
+Mandatory parameters are:
+device_type from DeviceType Enum
+data_acquisition_geometry: tuple (camera_X, camera_Y, detectorSpacingX,
+ detectorSpacingY, angles)
+reconstructed_volume_geometry: tuple (dimX,dimY,dimZ)
+'''
+ self.device_geometry = device_type
+ self.acquisition_data_geometry = {
+ 'cameraX': data_aquisition_geometry[0],
+ 'cameraY': data_aquisition_geometry[1],
+ 'detectorSpacingX' : data_aquisition_geometry[2],
+ 'detectorSpacingY' : data_aquisition_geometry[3],
+ 'angles' : data_aquisition_geometry[4],}
+ self.reconstructed_volume_geometry = {
+ 'X': reconstructed_volume_geometry[0] ,
+ 'Y': reconstructed_volume_geometry[1] ,
+ 'Z': reconstructed_volume_geometry[2] }
+
+ @abstractmethod
+ def doForwardProject(self, volume):
+ '''Forward projects the volume according to the device geometry'''
+ return NotImplemented
+
+
+ @abstractmethod
+ def doBackwardProject(self, projections):
+ '''Backward projects the projections according to the device geometry'''
+ return NotImplemented
+
+ @abstractmethod
+ def createReducedDevice(self):
+ '''Create a Device to do forward/backward projections on 2D slices'''
+ return NotImplemented
+
+
diff --git a/src/Python/ccpi/reconstruction/FISTAReconstructor.py b/src/Python/ccpi/reconstruction/FISTAReconstructor.py
index c903712..0607003 100644
--- a/src/Python/ccpi/reconstruction/FISTAReconstructor.py
+++ b/src/Python/ccpi/reconstruction/FISTAReconstructor.py
@@ -27,6 +27,7 @@ import numpy
from enum import Enum
import astra
+from ccpi.reconstruction.AstraDevice import AstraDevice
@@ -73,7 +74,10 @@ class FISTAReconstructor():
# 3. "A novel tomographic reconstruction method based on the robust
# Student's t function for suppressing data outliers" D. Kazantsev et.al.
# D. Kazantsev, 2016-17
- def __init__(self, projector_geometry, output_geometry, input_sinogram,
+ def __init__(self, projector_geometry,
+ output_geometry,
+ input_sinogram,
+ device,
**kwargs):
# handle parmeters:
# obligatory parameters
@@ -86,7 +90,10 @@ class FISTAReconstructor():
self.pars['number_of_angles'] = nangles
self.pars['SlicesZ'] = sliceZ
self.pars['output_volume'] = None
+ self.pars['device_model'] = device
+ self.use_device = True
+
print (self.pars)
# handle optional input parameters (at instantiation)
@@ -113,7 +120,9 @@ class FISTAReconstructor():
'output_volume',
'os_subsets',
'os_indices',
- 'os_bins')
+ 'os_bins',
+ 'device_model',
+ 'reduced_device_model')
self.acceptedInputKeywords = list(kw)
# handle keyworded parameters
@@ -170,9 +179,11 @@ class FISTAReconstructor():
if not 'initialize' in kwargs.keys():
self.pars['initialize'] = False
+ reduced_device = device.createReducedDevice()
+ self.setParameter(reduced_device_model=reduced_device)
+
-
-
+
def setParameter(self, **kwargs):
'''set named parameter for the reconstructor engine
@@ -436,46 +447,66 @@ class FISTAReconstructor():
X_t = X.copy()
# convenience variable storage
proj_geom , vol_geom, sino , \
- SlicesZ = self.getParameter([ 'projector_geometry' ,
+ SlicesZ , ring_lambda_R_L1 = self.getParameter([ 'projector_geometry' ,
'output_geometry',
'input_sinogram',
- 'SlicesZ' ])
+ 'SlicesZ' ,
+ 'ring_lambda_R_L1'])
t = 1
+
+ device = self.getParameter('device_model')
+ reduced_device = self.getParameter('reduced_device_model')
for i in range(self.getParameter('number_of_iterations')):
+ print("iteration", i)
X_old = X.copy()
t_old = t
r_old = self.r.copy()
- if self.getParameter('projector_geometry')['type'] == 'parallel' or \
- self.getParameter('projector_geometry')['type'] == 'fanflat' or \
- self.getParameter('projector_geometry')['type'] == 'fanflat_vec':
+ pg = self.getParameter('projector_geometry')['type']
+ if pg == 'parallel' or \
+ pg == 'fanflat' or \
+ pg == 'fanflat_vec':
# if the geometry is parallel use slice-by-slice
# projection-backprojection routine
#sino_updt = zeros(size(sino),'single');
- proj_geomT = proj_geom.copy()
- proj_geomT['DetectorRowCount'] = 1
- vol_geomT = vol_geom.copy()
- vol_geomT['GridSliceCount'] = 1;
- self.sino_updt = numpy.zeros(numpy.shape(sino), dtype=numpy.float)
- for kkk in range(SlicesZ):
- sino_id, self.sino_updt[kkk] = \
- astra.creators.create_sino3d_gpu(
- X_t[kkk:kkk+1], proj_geomT, vol_geomT)
- astra.matlab.data3d('delete', sino_id)
+
+ if self.use_device :
+ self.sino_updt = numpy.zeros(numpy.shape(sino), dtype=numpy.float)
+
+ for kkk in range(SlicesZ):
+ self.sino_updt[kkk] = \
+ reduced_device.doForwardProject( X_t[kkk:kkk+1] )
+ else:
+ proj_geomT = proj_geom.copy()
+ proj_geomT['DetectorRowCount'] = 1
+ vol_geomT = vol_geom.copy()
+ vol_geomT['GridSliceCount'] = 1;
+ self.sino_updt = numpy.zeros(numpy.shape(sino), dtype=numpy.float)
+ for kkk in range(SlicesZ):
+ sino_id, self.sino_updt[kkk] = \
+ astra.creators.create_sino3d_gpu(
+ X_t[kkk:kkk+1], proj_geomT, vol_geomT)
+ astra.matlab.data3d('delete', sino_id)
else:
# for divergent 3D geometry (watch the GPU memory overflow in
# ASTRA versions < 1.8)
#[sino_id, sino_updt] = astra_create_sino3d_cuda(X_t, proj_geom, vol_geom);
- sino_id, self.sino_updt = astra.creators.create_sino3d_gpu(
- X_t, proj_geom, vol_geom)
+
+ if self.use_device:
+ self.sino_updt = device.doForwardProject(X_t)
+ else:
+ sino_id, self.sino_updt = astra.creators.create_sino3d_gpu(
+ X_t, proj_geom, vol_geom)
+ astra.matlab.data3d('delete', sino_id)
## RING REMOVAL
- self.ringRemoval(i)
+ if ring_lambda_R_L1 != 0:
+ self.ringRemoval(i)
## Projection/Backprojection Routine
self.projectionBackprojection(X, X_t)
- astra.matlab.data3d('delete', sino_id)
+
## REGULARIZATION
X = self.regularize(X)
## Update Loop
@@ -523,6 +554,8 @@ class FISTAReconstructor():
'output_geometry',
'Lipschitz_constant'])
+ device, reduced_device = self.getParameter(['device_model',
+ 'reduced_device_model'])
if self.getParameter('projector_geometry')['type'] == 'parallel' or \
self.getParameter('projector_geometry')['type'] == 'fanflat' or \
@@ -530,27 +563,39 @@ class FISTAReconstructor():
# if the geometry is parallel use slice-by-slice
# projection-backprojection routine
#sino_updt = zeros(size(sino),'single');
- proj_geomT = proj_geom.copy()
- proj_geomT['DetectorRowCount'] = 1
- vol_geomT = vol_geom.copy()
- vol_geomT['GridSliceCount'] = 1;
x_temp = numpy.zeros(numpy.shape(X),dtype=numpy.float32)
-
- for kkk in range(SlicesZ):
- x_id, x_temp[kkk] = \
- astra.creators.create_backprojection3d_gpu(
- residual[kkk:kkk+1],
- proj_geomT, vol_geomT)
- astra.matlab.data3d('delete', x_id)
+ if self.use_device:
+ proj_geomT = proj_geom.copy()
+ proj_geomT['DetectorRowCount'] = 1
+ vol_geomT = vol_geom.copy()
+ vol_geomT['GridSliceCount'] = 1;
+
+ for kkk in range(SlicesZ):
+
+ x_id, x_temp[kkk] = \
+ astra.creators.create_backprojection3d_gpu(
+ residual[kkk:kkk+1],
+ proj_geomT, vol_geomT)
+ astra.matlab.data3d('delete', x_id)
+ else:
+ for kkk in range(SliceZ):
+ x_temp[kkk] = \
+ reduced_device.doBackwardProject(residual[kkk:kkk+1])
else:
- x_id, x_temp = \
+ if self.use_device:
+ x_id, x_temp = \
astra.creators.create_backprojection3d_gpu(
- residual, proj_geom, vol_geom)
+ residual, proj_geom, vol_geom)
+ astra.matlab.data3d('delete', x_id)
+ else:
+ x_temp = \
+ device.doBackwardProject(residual)
+
X = X_t - (1/L_const) * x_temp
#astra.matlab.data3d('delete', sino_id)
- astra.matlab.data3d('delete', x_id)
+
def regularize(self, X):
print ("FISTA Reconstructor: regularize")
diff --git a/src/Python/fista_module.cpp b/src/Python/fista_module.cpp
index aca3be0..f3add76 100644
--- a/src/Python/fista_module.cpp
+++ b/src/Python/fista_module.cpp
@@ -328,7 +328,6 @@ bp::list FGP_TV(np::ndarray input, double d_mu, int iter, double d_epsil, int me
else {
dim_array[2] = input.shape(2);
}
-
// Parameter handling is be done in Python
///*Handling Matlab input data*/
//if ((nrhs < 2) || (nrhs > 5)) mexErrMsgTxt("At least 2 parameters is required: Image(2D/3D), Regularization parameter. The full list of parameters: Image(2D/3D), Regularization parameter, iterations number, tolerance, penalty type ('iso' or 'l1')");
@@ -513,13 +512,11 @@ bp::list FGP_TV(np::ndarray input, double d_mu, int iter, double d_epsil, int me
P3_old = reinterpret_cast<float *>(npP3_old.get_data());
R1 = reinterpret_cast<float *>(npR1.get_data());
R2 = reinterpret_cast<float *>(npR2.get_data());
- R2 = reinterpret_cast<float *>(npR3.get_data());
+ R3 = reinterpret_cast<float *>(npR3.get_data());
/* begin iterations */
for (ll = 0; ll<iter; ll++) {
-
/* computing the gradient of the objective function */
Obj_func3D(A, D, R1, R2, R3, lambda, dimX, dimY, dimZ);
-
/*Taking a step towards minus of the gradient*/
Grad_func3D(P1, P2, P3, D, R1, R2, R3, lambda, dimX, dimY, dimZ);
diff --git a/src/Python/test/test_reconstructor.py b/src/Python/test/test_reconstructor.py
index 3342301..f4627d7 100644
--- a/src/Python/test/test_reconstructor.py
+++ b/src/Python/test/test_reconstructor.py
@@ -12,6 +12,9 @@ import numpy
from ccpi.reconstruction.FISTAReconstructor import FISTAReconstructor
import astra
import matplotlib.pyplot as plt
+from ccpi.imaging.Regularizer import Regularizer
+from ccpi.reconstruction.AstraDevice import AstraDevice
+from ccpi.reconstruction.DeviceModel import DeviceModel
def RMSE(signal1, signal2):
'''RMSE Root Mean Squared Error'''
@@ -22,7 +25,23 @@ def RMSE(signal1, signal2):
return err
else:
raise Exception('Input signals must have the same shape')
-
+
+def createAstraDevice(projector_geometry, output_geometry):
+ '''TODO remove'''
+
+ device = AstraDevice(DeviceModel.DeviceType.PARALLEL3D.value,
+ [projector_geometry['DetectorRowCount'] ,
+ projector_geometry['DetectorColCount'] ,
+ projector_geometry['DetectorSpacingX'] ,
+ projector_geometry['DetectorSpacingY'] ,
+ projector_geometry['ProjectionAngles']
+ ],
+ [
+ output_geometry['GridColCount'],
+ output_geometry['GridRowCount'],
+ output_geometry['GridSliceCount'] ] )
+ return device
+
filename = r'/home/ofn77899/Reconstruction/CCPi-FISTA_Reconstruction/demos/DendrData.h5'
nx = h5py.File(filename, "r")
#getEntry(nx, '/')
@@ -65,23 +84,23 @@ vol_geom = astra.creators.create_vol_geom( image_size_x,
## First pass the arguments to the FISTAReconstructor and test the
## Lipschitz constant
-fistaRecon = FISTAReconstructor(proj_geom,
- vol_geom,
- Sino3D ,
- weights=Weights3D)
-
-print ("Lipschitz Constant {0}".format(fistaRecon.pars['Lipschitz_constant']))
-fistaRecon.setParameter(number_of_iterations = 12)
-fistaRecon.setParameter(Lipschitz_constant = 767893952.0)
-fistaRecon.setParameter(ring_alpha = 21)
-fistaRecon.setParameter(ring_lambda_R_L1 = 0.002)
-
-reg = Regularizer(Regularizer.Algorithm.LLT_model)
-reg.setParameter(regularization_parameter=25,
- time_step=0.0003,
- tolerance_constant=0.0001,
- number_of_iterations=300)
-fistaRecon.setParameter(regularizer = reg)
+##fistaRecon = FISTAReconstructor(proj_geom,
+## vol_geom,
+## Sino3D ,
+## weights=Weights3D)
+##
+##print ("Lipschitz Constant {0}".format(fistaRecon.pars['Lipschitz_constant']))
+##fistaRecon.setParameter(number_of_iterations = 12)
+##fistaRecon.setParameter(Lipschitz_constant = 767893952.0)
+##fistaRecon.setParameter(ring_alpha = 21)
+##fistaRecon.setParameter(ring_lambda_R_L1 = 0.002)
+##
+##reg = Regularizer(Regularizer.Algorithm.LLT_model)
+##reg.setParameter(regularization_parameter=25,
+## time_step=0.0003,
+## tolerance_constant=0.0001,
+## number_of_iterations=300)
+##fistaRecon.setParameter(regularizer=reg)
## Ordered subset
if False:
@@ -294,16 +313,53 @@ if False:
## fprintf('%s %i %s %s %f \n', 'Iteration Number:', i, '|', 'Objective:', objective(i));
## end
else:
+
+ # create a device for forward/backprojection
+ astradevice = createAstraDevice(proj_geom, vol_geom)
fistaRecon = FISTAReconstructor(proj_geom,
vol_geom,
Sino3D ,
- weights=Weights3D)
+ device = astradevice,
+ weights=Weights3D
+ )
print ("Lipschitz Constant {0}".format(fistaRecon.pars['Lipschitz_constant']))
- fistaRecon.setParameter(number_of_iterations = 12)
+ fistaRecon.setParameter(number_of_iterations = 3)
fistaRecon.setParameter(Lipschitz_constant = 767893952.0)
fistaRecon.setParameter(ring_alpha = 21)
fistaRecon.setParameter(ring_lambda_R_L1 = 0.002)
+
+
+
fistaRecon.prepareForIteration()
- X = fistaRecon.iterate(numpy.load("X.npy"))
- numpy.save("X_out.npy", X)
+ X = numpy.load("X.npy")
+## rd = astradevice.createReducedDevice()
+## print ("rd proj_geom" , rd.proj_geom)
+##
+##
+## rd.doForwardProject(X[0:1])
+## proj_geomT = proj_geom.copy()
+## for ekey in rd.proj_geom.keys():
+## if ekey == 'ProjectionAngles':
+## valrd = numpy.shape(rd.proj_geom[ekey])
+## valg = numpy.shape(proj_geomT[ekey])
+## else:
+## valrd = rd.proj_geom[ekey]
+## valg = proj_geomT[ekey]
+##
+## print ("key {0}: RD {1} geomT {2}".format(ekey, valrd, valg))
+##
+##
+## proj_geomT['DetectorRowCount'] = 1
+## vol_geomT = vol_geom.copy()
+## vol_geomT['GridSliceCount'] = 1;
+## rd.proj_geom = proj_geomT.copy()
+## rd.vol_geom = vol_geomT.copy()
+##
+##
+##
+## sino_id, y = astra.creators.create_sino3d_gpu(
+## X[0:1], rd.proj_geom, rd.vol_geom)
+
+ X = fistaRecon.iterate(X)
+ #numpy.save("X_out.npy", X)