summaryrefslogtreecommitdiffstats
path: root/src/Python
diff options
context:
space:
mode:
Diffstat (limited to 'src/Python')
-rw-r--r--src/Python/ccpi/reconstruction/FISTAReconstructor.py2
-rw-r--r--src/Python/test/create_phantom_projections.py49
-rw-r--r--src/Python/test/test_reconstructor-os_phantom.py480
3 files changed, 530 insertions, 1 deletions
diff --git a/src/Python/ccpi/reconstruction/FISTAReconstructor.py b/src/Python/ccpi/reconstruction/FISTAReconstructor.py
index af6275f..e40ad24 100644
--- a/src/Python/ccpi/reconstruction/FISTAReconstructor.py
+++ b/src/Python/ccpi/reconstruction/FISTAReconstructor.py
@@ -233,7 +233,7 @@ class FISTAReconstructor():
#x1 = rand(N,N,1);
x1 = numpy.random.rand(1,N,N)
#sqweight = sqrt(weights(:,:,1));
- sqweight = numpy.sqrt(weights[0])
+ sqweight = numpy.sqrt(weights[0:1,:,:])
proj_geomT = proj_geom.copy();
proj_geomT['DetectorRowCount'] = 1;
vol_geomT = vol_geom.copy();
diff --git a/src/Python/test/create_phantom_projections.py b/src/Python/test/create_phantom_projections.py
new file mode 100644
index 0000000..20a9278
--- /dev/null
+++ b/src/Python/test/create_phantom_projections.py
@@ -0,0 +1,49 @@
+from ccpi.reconstruction.AstraDevice import AstraDevice
+from ccpi.reconstruction.DeviceModel import DeviceModel
+import h5py
+import numpy
+import matplotlib.pyplot as plt
+
+nx = h5py.File('phant3D_256.h5', "r")
+phantom = numpy.asarray(nx.get('/dataset1'))
+pX,pY,pZ = numpy.shape(phantom)
+
+filename = r'/home/ofn77899/Reconstruction/CCPi-FISTA_Reconstruction/demos/DendrData.h5'
+nxa = h5py.File(filename, "r")
+#getEntry(nx, '/')
+# I have exported the entries as children of /
+entries = [entry for entry in nxa['/'].keys()]
+print (entries)
+
+angles_rad = numpy.asarray(nxa.get('/angles_rad'), dtype="float32")
+
+
+device = AstraDevice(
+ DeviceModel.DeviceType.PARALLEL3D.value,
+ [ pX , pY , 1., 1., angles_rad],
+ [ pX, pY, pZ ] )
+
+
+proj = device.doForwardProject(phantom)
+stack = [proj[:,i,:] for i in range(len(angles_rad))]
+stack = numpy.asarray(stack)
+
+
+fig = plt.figure()
+a=fig.add_subplot(1,2,1)
+a.set_title('proj')
+imgplot = plt.imshow(proj[:,100,:])
+a=fig.add_subplot(1,2,2)
+a.set_title('stack')
+imgplot = plt.imshow(stack[100])
+plt.show()
+
+pf = h5py.File("phantom3D256_projections.h5" , "w")
+pf.create_dataset("/projections", data=stack)
+pf.create_dataset("/sinogram", data=proj)
+pf.create_dataset("/angles", data=angles_rad)
+pf.create_dataset("/reconstruction_volume" , data=numpy.asarray([pX, pY, pZ]))
+pf.create_dataset("/camera/size" , data=numpy.asarray([pX , pY ]))
+pf.create_dataset("/camera/spacing" , data=numpy.asarray([1.,1.]))
+pf.flush()
+pf.close()
diff --git a/src/Python/test/test_reconstructor-os_phantom.py b/src/Python/test/test_reconstructor-os_phantom.py
new file mode 100644
index 0000000..01f1354
--- /dev/null
+++ b/src/Python/test/test_reconstructor-os_phantom.py
@@ -0,0 +1,480 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Wed Aug 23 16:34:49 2017
+
+@author: ofn77899
+Based on DemoRD2.m
+"""
+
+import h5py
+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
+
+#from ccpi.viewer.CILViewer2D import *
+
+
+def RMSE(signal1, signal2):
+ '''RMSE Root Mean Squared Error'''
+ if numpy.shape(signal1) == numpy.shape(signal2):
+ err = (signal1 - signal2)
+ err = numpy.sum( err * err )/numpy.size(signal1); # MSE
+ err = sqrt(err); # RMSE
+ return err
+ else:
+ raise Exception('Input signals must have the same shape')
+
+filename = r'/home/ofn77899/Reconstruction/CCPi-FISTA_Reconstruction/src/Python/test/phantom3D256_projections.h5'
+nx = h5py.File(filename, "r")
+#getEntry(nx, '/')
+# I have exported the entries as children of /
+entries = [entry for entry in nx['/'].keys()]
+print (entries)
+
+projections = numpy.asarray(nx.get('/projections'), dtype="float32")
+#Weights3D = numpy.asarray(nx.get('/Weights3D'), dtype="float32")
+#angSize = numpy.asarray(nx.get('/angSize'), dtype=int)[0]
+angles_rad = numpy.asarray(nx.get('/angles'), dtype="float32")
+angSize = numpy.size(angles_rad)
+image_size_x, image_size_y, image_size_z = \
+ numpy.asarray(nx.get('/reconstruction_volume'), dtype=int)
+det_col_count, det_row_count = \
+ numpy.asarray(nx.get('/camera/size'))
+#slices_tot = numpy.asarray(nx.get('/slices_tot'), dtype=int)[0]
+detectorSpacingX, detectorSpacingY = numpy.asarray(nx.get('/camera/spacing'), dtype=int)
+
+Z_slices = 20
+#det_row_count = image_size_y
+# next definition is just for consistency of naming
+#det_col_count = image_size_x
+
+detectorSpacingX = 1.0
+detectorSpacingY = detectorSpacingX
+
+
+proj_geom = astra.creators.create_proj_geom('parallel3d',
+ detectorSpacingX,
+ detectorSpacingY,
+ det_row_count,
+ det_col_count,
+ angles_rad)
+
+#vol_geom = astra_create_vol_geom(recon_size,recon_size,Z_slices);
+##image_size_x = recon_size
+##image_size_y = recon_size
+##image_size_z = Z_slices
+vol_geom = astra.creators.create_vol_geom( image_size_x,
+ image_size_y,
+ image_size_z)
+
+## First pass the arguments to the FISTAReconstructor and test the
+## Lipschitz constant
+astradevice = AstraDevice(DeviceModel.DeviceType.PARALLEL3D.value,
+ [proj_geom['DetectorRowCount'] ,
+ proj_geom['DetectorColCount'] ,
+ proj_geom['DetectorSpacingX'] ,
+ proj_geom['DetectorSpacingY'] ,
+ proj_geom['ProjectionAngles']
+ ],
+ [
+ vol_geom['GridColCount'],
+ vol_geom['GridRowCount'],
+ vol_geom['GridSliceCount'] ] )
+## create the sinogram
+Sino3D = numpy.transpose(projections, axes=[1,0,2])
+
+fistaRecon = FISTAReconstructor(proj_geom,
+ vol_geom,
+ Sino3D ,
+ #weights=Weights3D,
+ device=astradevice)
+
+print ("Lipschitz Constant {0}".format(fistaRecon.pars['Lipschitz_constant']))
+fistaRecon.setParameter(number_of_iterations = 4)
+#fistaRecon.setParameter(Lipschitz_constant = 767893952.0)
+fistaRecon.setParameter(ring_alpha = 21)
+fistaRecon.setParameter(ring_lambda_R_L1 = 0.002)
+#fistaRecon.setParameter(ring_lambda_R_L1 = 0)
+subsets = 8
+fistaRecon.setParameter(subsets=subsets)
+
+
+#reg = Regularizer(Regularizer.Algorithm.FGP_TV)
+#reg.setParameter(regularization_parameter=0.005,
+# number_of_iterations=50)
+reg = Regularizer(Regularizer.Algorithm.FGP_TV)
+reg.setParameter(regularization_parameter=5e6,
+ tolerance_constant=0.0001,
+ number_of_iterations=50)
+
+#fistaRecon.setParameter(regularizer=reg)
+#lc = fistaRecon.getParameter('Lipschitz_constant')
+#reg.setParameter(regularization_parameter=5e6/lc)
+
+## Ordered subset
+if True:
+ #subsets = 8
+ fistaRecon.setParameter(subsets=subsets)
+ fistaRecon.createOrderedSubsets()
+else:
+ angles = fistaRecon.getParameter('projector_geometry')['ProjectionAngles']
+ #binEdges = numpy.linspace(angles.min(),
+ # angles.max(),
+ # subsets + 1)
+ binsDiscr, binEdges = numpy.histogram(angles, bins=subsets)
+ # get rearranged subset indices
+ IndicesReorg = numpy.zeros((numpy.shape(angles)))
+ counterM = 0
+ for ii in range(binsDiscr.max()):
+ counter = 0
+ for jj in range(subsets):
+ curr_index = ii + jj + counter
+ #print ("{0} {1} {2}".format(binsDiscr[jj] , ii, counterM))
+ if binsDiscr[jj] > ii:
+ if (counterM < numpy.size(IndicesReorg)):
+ IndicesReorg[counterM] = curr_index
+ counterM = counterM + 1
+
+ counter = counter + binsDiscr[jj] - 1
+
+
+if True:
+ print ("Lipschitz Constant {0}".format(fistaRecon.pars['Lipschitz_constant']))
+ print ("prepare for iteration")
+ fistaRecon.prepareForIteration()
+
+
+
+ print("initializing ...")
+ if True:
+ # if X doesn't exist
+ #N = params.vol_geom.GridColCount
+ N = vol_geom['GridColCount']
+ print ("N " + str(N))
+ X = numpy.asarray(numpy.ones((image_size_x,image_size_y,image_size_z)),
+ dtype=numpy.float) * 0.001
+ X = numpy.asarray(numpy.zeros((image_size_x,image_size_y,image_size_z)),
+ dtype=numpy.float)
+ else:
+ #X = fistaRecon.initialize()
+ X = numpy.load("X.npy")
+
+ print (numpy.shape(X))
+ X_t = X.copy()
+ print ("initialized")
+ proj_geom , vol_geom, sino , \
+ SlicesZ, weights , alpha_ring = fistaRecon.getParameter(
+ ['projector_geometry' , 'output_geometry',
+ 'input_sinogram', 'SlicesZ' , 'weights', 'ring_alpha'])
+ lambdaR_L1 , alpha_ring , weights , L_const= \
+ fistaRecon.getParameter(['ring_lambda_R_L1',
+ 'ring_alpha' , 'weights',
+ 'Lipschitz_constant'])
+
+ #fistaRecon.setParameter(number_of_iterations = 3)
+ iterFISTA = fistaRecon.getParameter('number_of_iterations')
+ # errors vector (if the ground truth is given)
+ Resid_error = numpy.zeros((iterFISTA));
+ # objective function values vector
+ objective = numpy.zeros((iterFISTA));
+
+
+ t = 1
+
+
+ ## additional for
+ proj_geomSUB = proj_geom.copy()
+ fistaRecon.residual2 = numpy.zeros(numpy.shape(fistaRecon.pars['input_sinogram']))
+ residual2 = fistaRecon.residual2
+ sino_updt_FULL = fistaRecon.residual.copy()
+ r_x = fistaRecon.r.copy()
+
+ results = []
+ print ("starting iterations")
+## % Outer FISTA iterations loop
+ for i in range(fistaRecon.getParameter('number_of_iterations')):
+## % With OS approach it becomes trickier to correlate independent subsets, hence additional work is required
+## % one solution is to work with a full sinogram at times
+## if ((i >= 3) && (lambdaR_L1 > 0))
+## [sino_id2, sino_updt2] = astra_create_sino3d_cuda(X, proj_geom, vol_geom);
+## astra_mex_data3d('delete', sino_id2);
+## end
+ # With OS approach it becomes trickier to correlate independent subsets,
+ # hence additional work is required one solution is to work with a full
+ # sinogram at times
+
+
+ #t_old = t
+ SlicesZ, anglesNumb, Detectors = \
+ numpy.shape(fistaRecon.getParameter('input_sinogram'))
+ ## https://github.com/vais-ral/CCPi-FISTA_Reconstruction/issues/4
+ r_old = fistaRecon.r.copy()
+
+ if (i > 1 and lambdaR_L1 > 0) :
+ for kkk in range(anglesNumb):
+
+ residual2[:,kkk,:] = (weights[:,kkk,:]).squeeze() * \
+ ((sino_updt_FULL[:,kkk,:]).squeeze() - \
+ (sino[:,kkk,:]).squeeze() -\
+ (alpha_ring * r_x)
+ )
+ #r_old = fistaRecon.r.copy()
+ vec = fistaRecon.residual.sum(axis = 1)
+ #if SlicesZ > 1:
+ # vec = vec[:,1,:] # 1 or 0?
+ r_x = fistaRecon.r_x
+ # update ring variable
+ fistaRecon.r = (r_x - (1./L_const) * vec)
+
+ # subset loop
+ counterInd = 1
+ geometry_type = fistaRecon.getParameter('projector_geometry')['type']
+ angles = fistaRecon.getParameter('projector_geometry')['ProjectionAngles']
+
+## if geometry_type == 'parallel' or \
+## geometry_type == 'fanflat' or \
+## geometry_type == 'fanflat_vec' :
+##
+## for kkk in range(SlicesZ):
+## sino_id, sinoT[kkk] = \
+## astra.creators.create_sino3d_gpu(
+## X_t[kkk:kkk+1], proj_geomSUB, vol_geom)
+## sino_updt_Sub[kkk] = sinoT.T.copy()
+##
+## else:
+## sino_id, sino_updt_Sub = \
+## astra.creators.create_sino3d_gpu(X_t, proj_geomSUB, vol_geom)
+##
+## astra.matlab.data3d('delete', sino_id)
+
+ for ss in range(fistaRecon.getParameter('subsets')):
+ print ("Subset {0}".format(ss))
+ X_old = X.copy()
+ t_old = t
+ print ("X[0][0][0] {0} t {1}".format(X[0][0][0], t))
+
+ # the number of projections per subset
+ numProjSub = fistaRecon.getParameter('os_bins')[ss]
+ CurrSubIndices = fistaRecon.getParameter('os_indices')\
+ [counterInd:counterInd+numProjSub]
+ shape = list(numpy.shape(fistaRecon.getParameter('input_sinogram')))
+ shape[1] = numProjSub
+ sino_updt_Sub = numpy.zeros(shape)
+
+ #print ("Len CurrSubIndices {0}".format(numProjSub))
+ mask = numpy.zeros(numpy.shape(angles), dtype=bool)
+ cc = 0
+ for j in range(len(CurrSubIndices)):
+ mask[int(CurrSubIndices[j])] = True
+
+ ## this is a reduced device
+ rdev = fistaRecon.getParameter('device_model')\
+ .createReducedDevice(proj_par={'angles' : angles[mask]},
+ vol_par={})
+ proj_geomSUB['ProjectionAngles'] = angles[mask]
+
+
+
+ if geometry_type == 'parallel' or \
+ geometry_type == 'fanflat' or \
+ geometry_type == 'fanflat_vec' :
+
+ for kkk in range(SlicesZ):
+ sino_id, sinoT = astra.creators.create_sino3d_gpu (
+ X_t[kkk:kkk+1] , proj_geomSUB, vol_geom)
+ sino_updt_Sub[kkk] = sinoT.T.copy()
+ astra.matlab.data3d('delete', sino_id)
+ else:
+ # for 3D geometry (watch the GPU memory overflow in ASTRA < 1.8)
+ sino_id, sino_updt_Sub = \
+ astra.creators.create_sino3d_gpu (X_t,
+ proj_geomSUB,
+ vol_geom)
+
+ astra.matlab.data3d('delete', sino_id)
+
+
+
+
+ ## RING REMOVAL
+ residual = fistaRecon.residual
+
+
+ if lambdaR_L1 > 0 :
+ print ("ring removal")
+ residualSub = numpy.zeros(shape)
+ ## for a chosen subset
+ ## for kkk = 1:numProjSub
+ ## indC = CurrSubIndeces(kkk);
+ ## residualSub(:,kkk,:) = squeeze(weights(:,indC,:)).*(squeeze(sino_updt_Sub(:,kkk,:)) - (squeeze(sino(:,indC,:)) - alpha_ring.*r_x));
+ ## sino_updt_FULL(:,indC,:) = squeeze(sino_updt_Sub(:,kkk,:)); % filling the full sinogram
+ ## end
+ for kkk in range(numProjSub):
+ #print ("ring removal indC ... {0}".format(kkk))
+ indC = int(CurrSubIndices[kkk])
+ residualSub[:,kkk,:] = weights[:,indC,:].squeeze() * \
+ (sino_updt_Sub[:,kkk,:].squeeze() - \
+ sino[:,indC,:].squeeze() - alpha_ring * r_x)
+ # filling the full sinogram
+ sino_updt_FULL[:,indC,:] = sino_updt_Sub[:,kkk,:].squeeze()
+
+ else:
+ #PWLS model
+ # I guess we need to use mask here instead
+ residualSub = weights[:,CurrSubIndices,:] * \
+ ( sino_updt_Sub - \
+ sino[:,CurrSubIndices,:].squeeze() )
+ # it seems that in the original code the following like is not
+ # calculated in the case of ring removal
+ objective[i] = 0.5 * numpy.linalg.norm(residualSub)
+
+ #backprojection
+ if geometry_type == 'parallel' or \
+ geometry_type == 'fanflat' or \
+ geometry_type == 'fanflat_vec' :
+ # if geometry is 2D use slice-by-slice projection-backprojection
+ # routine
+ 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(
+ residualSub[kkk:kkk+1],
+ proj_geomSUB, vol_geom)
+ astra.matlab.data3d('delete', x_id)
+
+ else:
+ x_id, x_temp = \
+ astra.creators.create_backprojection3d_gpu(
+ residualSub, proj_geomSUB, vol_geom)
+
+ astra.matlab.data3d('delete', x_id)
+
+ X = X_t - (1/L_const) * x_temp
+
+
+
+ ## REGULARIZATION
+ ## SKIPPING FOR NOW
+ ## Should be simpli
+ # regularizer = fistaRecon.getParameter('regularizer')
+ # for slices:
+ # out = regularizer(input=X)
+ print ("regularizer")
+ reg = fistaRecon.getParameter('regularizer')
+
+ if reg is not None:
+ X = reg(input=X,
+ output_all=False)
+
+ t = (1 + numpy.sqrt(1 + 4 * t **2))/2
+ X_t = X + (((t_old -1)/t) * (X-X_old))
+ counterInd = counterInd + numProjSub - 1
+ if i == 1:
+ r_old = fistaRecon.r.copy()
+
+ ## FINAL
+ print ("final")
+ lambdaR_L1 = fistaRecon.getParameter('ring_lambda_R_L1')
+ if lambdaR_L1 > 0:
+ fistaRecon.r = numpy.max(
+ numpy.abs(fistaRecon.r) - lambdaR_L1 , 0) * \
+ numpy.sign(fistaRecon.r)
+ # updating r
+ r_x = fistaRecon.r + ((t_old-1)/t) * (fistaRecon.r - r_old)
+
+
+ if fistaRecon.getParameter('region_of_interest') is None:
+ string = 'Iteration Number {0} | Objective {1} \n'
+ print (string.format( i, objective[i]))
+ else:
+ ROI , X_ideal = fistaRecon.getParameter('region_of_interest',
+ 'ideal_image')
+
+ Resid_error[i] = RMSE(X*ROI, X_ideal*ROI)
+ string = 'Iteration Number {0} | RMS Error {1} | Objective {2} \n'
+ print (string.format(i,Resid_error[i], objective[i]))
+
+ results.append(X[10])
+ numpy.save("X_out_os.npy", X)
+
+else:
+
+
+
+ astradevice = AstraDevice(DeviceModel.DeviceType.PARALLEL3D.value,
+ [proj_geom['DetectorRowCount'] ,
+ proj_geom['DetectorColCount'] ,
+ proj_geom['DetectorSpacingX'] ,
+ proj_geom['DetectorSpacingY'] ,
+ proj_geom['ProjectionAngles']
+ ],
+ [
+ vol_geom['GridColCount'],
+ vol_geom['GridRowCount'],
+ vol_geom['GridSliceCount'] ] )
+ regul = Regularizer(Regularizer.Algorithm.FGP_TV)
+ regul.setParameter(regularization_parameter=5e6,
+ number_of_iterations=50,
+ tolerance_constant=1e-4,
+ TV_penalty=Regularizer.TotalVariationPenalty.isotropic)
+
+ fistaRecon = FISTAReconstructor(proj_geom,
+ vol_geom,
+ Sino3D ,
+ weights=Weights3D,
+ device=astradevice,
+ #regularizer = regul,
+ subsets=8)
+
+ print ("Lipschitz Constant {0}".format(fistaRecon.pars['Lipschitz_constant']))
+ fistaRecon.setParameter(number_of_iterations = 1)
+ fistaRecon.setParameter(Lipschitz_constant = 767893952.0)
+ fistaRecon.setParameter(ring_alpha = 21)
+ fistaRecon.setParameter(ring_lambda_R_L1 = 0.002)
+ #fistaRecon.setParameter(subsets=8)
+
+ #lc = fistaRecon.getParameter('Lipschitz_constant')
+ #fistaRecon.getParameter('regularizer').setParameter(regularization_parameter=5e6/lc)
+
+ fistaRecon.prepareForIteration()
+ X = fistaRecon.iterate(numpy.load("X.npy"))
+
+
+# plot
+fig = plt.figure()
+cols = 3
+
+## add the difference
+rd = []
+for i in range(1,len(results)):
+ rd.append(results[i-1])
+ rd.append(results[i])
+ rd.append(results[i] - results[i-1])
+
+rows = (lambda x: int(numpy.floor(x/cols) + 1) if x%cols != 0 else int(x/cols)) \
+ (len (rd))
+for i in range(len (results)):
+ a=fig.add_subplot(rows,cols,i+1)
+ imgplot = plt.imshow(results[i], vmin=0, vmax=1)
+ a.text(0.05, 0.95, "iteration {0}".format(i),
+ verticalalignment='top')
+## i = i + 1
+## a=fig.add_subplot(rows,cols,i+1)
+## imgplot = plt.imshow(results[i], vmin=0, vmax=10)
+## a.text(0.05, 0.95, "iteration {0}".format(i),
+## verticalalignment='top')
+
+## a=fig.add_subplot(rows,cols,i+2)
+## imgplot = plt.imshow(results[i]-results[i-1], vmin=0, vmax=10)
+## a.text(0.05, 0.95, "difference {0}-{1}".format(i, i-1),
+## verticalalignment='top')
+
+
+
+plt.show()