# # geo.nVoxel = [64,64,64]' , hyper (approx=) 2.e8 # geo.nVoxel = [512,512,512]' , hyper (approx=) 2.e4 # Default: 2.e8 # 'tviter': Number of iterations of Im3ddenoise to use. Default: 20 # 'lambda': Multiplier for the tvlambda used, which is proportional to # L (hyper). Default: 0.1 # 'verbose': get feedback or not. Default: 1 # -------------------------------------------------------------------------- imgFISTA_default = algs.fista(noise_projections, geo, angles, 100) ## Adding a different hyper parameter imgFISTA_hyper = algs.fista(noise_projections, geo, angles, 100, hyper=2.0e6) ## Recon more significant tv parameters imgFISTA_hightv = algs.fista(noise_projections, geo, angles, 100, hyper=2.0e6, tviter=100, tvlambda=20) ## Plot results tigre.plotimg( np.concatenate([imgFISTA_default, imgFISTA_hyper, imgFISTA_hightv], axis=1), dim="z", clims=[0, 1], )
# does not store anything but the projecions) # if this is the case, the general way of tackling the problem is: # Step 1: define your geometry and angles geo=tigre.geometry() angles= # Step 2: Load projections: #store in NP array. Use whichever python lirbary will help you get the data loaded. # Step 3: validate tigre.plotproj(proj,angles) # you need to make sure that: # 1) white=metal/bone/high density and black=air. # If this is not the case proj=-np.log(proj/(np.max(proj+1)); # Beer-Lanbert law # 2) rotation happens left-right instead of top-bottom. # If its top bottom permute your data as required # Step 4: test imgfdk=algs.fdk(proj,geo,angles) tigre.plotimg(imgFDK,dim='z') # If this does not look good, possible things to check: # - Are the angles in the right direction? maybe they need to be inverted. # - Is your geometry properly defined? mistake on geometry will be # detrimental to image quality # - if microCT: halos around the features? COR correction needed.
# AwASD_POCS: adaptative weighted ASD_POCS # ========================================================================== # ========================================================================== # # This is a more edge preserving algorithms than ASD_POCS, in theory. # delta is the cuttof vlaue of anromalized edge exponential weight.... # not super clear, but it cotnrols at which point you accept something as real vs noise edge. imgAWASDPOCS = algs.awasd_pocs( noise_projections, geo, angles, 10, # these are very important tviter=ng, maxl2err=epsilon, alpha=alpha, # less important. lmbda=lmbda, lmbda_red=lambdared, rmax=ratio, verbose=verb, # AwASD_POCS params delta=np.array([-0.005]), ) #%% plot results # plot images tigre.plotimg( np.concatenate([imgAWASDPOCS, imgOSASDPOCS, imgASDPOCS, imgOSSART], axis=1), dim="z", step=2 )
roll = angles pitch = 0.7 * np.linspace(0, 1, len(angles)) yaw = 0.7 * np.linspace(0, 1, len(angles)) geo.rotDetector = np.array(np.vstack([roll, pitch, yaw])).T #%% # Load thorax phatom data head = sample_loader.load_head_phantom(geo.nVoxel) # generate projections projections = tigre.Ax(head, geo, angles) # add noise noise_projections = CTnoise.add(projections, Poisson=1e5, Gaussian=np.array([0, 10])) tigre.plotproj(projections, angles) #%% recon imgRotDet = algs.ossart(projections, geo, angles, 50) #% No rotation geo.rotDetector = np.array([0, 0, 0]) projections2 = tigre.Ax(head, geo, angles) imgnoRot = algs.ossart(projections2, geo, angles, 50) # %% Plot to show that indeed the reconstruction is right tigre.plotimg(np.concatenate([imgRotDet, imgnoRot], axis=1), dim="z")
tigre.plotproj(projections, angles) ## we will skip reconstruction of this tests because the image is outside the detector ## ##################################################################### #%% Second test: lets test variying offsets: geo.offDetector = np.vstack([10 * np.sin(angles), 20 * np.cos(angles) ]).T # Offset of Detector (mm) projections2 = tigre.Ax(head, geo, angles) ## lets see it tigre.plotproj(projections2, angles) ## reconstruction res = algs.sart(projections2, geo, angles, 10) tigre.plotimg(res, dim="z") #%% Third test: lets vary everything # Lets make the image smaller geo.nVoxel = np.array([128, 128, 128]) # number of voxels (vx) geo.sVoxel = np.array([256, 256, 256 ]) / 2 # total size of the image (mm) geo.dVoxel = geo.sVoxel / geo.nVoxel # size of each voxel (mm) head = sample_loader.load_head_phantom(geo.nVoxel) geo.offDetector = np.vstack([10 * np.sin(angles), 10 * np.cos(angles) ]).T # Offset of Detector (mm) geo.offOrigin = np.vstack( [40 * np.sin(angles), np.linspace(-30, 30, 100),
plt.gca().legend(("SIRT", "OS-SART", "SART")) plt.subplot(212) plt.plot( np.log10( np.vstack( (qualitySIRT[1, :], qualityOSSART[1, :], qualitySART[1, :]))).T) plt.title("Evolution of RMSE") plt.gca().legend(("SIRT", "OS-SART", "SART")) plt.xlabel("Iteration") plt.ylabel("$ log_{10}(RMSE) $") plt.show() #%% plot the results # It is clear that SART will get to better results for the same amoutn of # iterations, however, it takes x7 more time to run. # SART # OS-SART # SIRT tigre.plotimg(np.concatenate([imgSIRT, imgOSSART, imgSART], axis=1), dim="Z", savegif="sarts.gif") # plot error tigre.plotimg(np.abs( np.concatenate([head - imgSIRT, head - imgOSSART, head - imgSART], axis=1)), dim="Z")
#%% Define angles of projection and load phatom image angles = np.linspace(0, 2 * np.pi, 100) head = sample_loader.load_head_phantom(geo.nVoxel) projections = tigre.Ax(head, geo, angles) tigre.plotSinogram(projections, 0) #%% recosntruct imgOSSART = algs.ossart(projections, geo, angles, 40) imgCGLS = algs.cgls(projections, geo, angles, 40) #%% Plot tigre.plotimg(np.concatenate([head, imgOSSART, imgCGLS], axis=1), dim="z", slice=0, clims=[0, 0.5]) #%% And now Fan Beam ## # The same thing! Just remember to offset the detector half a pixel, so its # centered (this is a bug, it will be changed at some point) ## FAN BEAM 2D geo = tigre.geometry() # VARIABLE DESCRIPTION UNITS # ------------------------------------------------------------------------------------- # Distances geo.DSD = 1536 # Distance Source Detector (mm) geo.DSO = 1000 # Distance Source Origin (mm) # Image parameters
# use CGLS imgCGLS, errL2CGLS = algs.cgls(noise_projections, geo, angles, 60, computel2=True) # SIRT for comparison. imgSIRT, errL2SIRT = algs.sirt(noise_projections, geo, angles, 60, computel2=True) #%% plot results # # We can see that CGLS gets to the same L2 error in less amount of # iterations. plt.plot(np.vstack((errL2CGLS[0, :], errL2SIRT[0, :])).T) plt.title("L2 error") plt.xlabel("Iteration") plt.ylabel("$ log_{10}(|Ax-b|) $") plt.gca().legend(("CGLS", "SIRT")) plt.show() # plot images tigre.plotimg(np.concatenate([imgCGLS, imgSIRT], axis=1), dim="z", step=2) # plot errors tigre.plotimg(np.abs(np.concatenate([head - imgCGLS, head - imgSIRT], axis=1)), dim="z", slice=32)
# the FDK algorithm has been taken and modified from # 3D Cone beam CT (CBCT) projection backprojection FDK, iterative reconstruction Matlab examples # https://www.mathworks.com/matlabcentral/fileexchange/35548-3d-cone-beam-ct--cbct--projection-backprojection-fdk--iterative-reconstruction-matlab-examples # The algorithm takes, as eny of them, 3 mandatory inputs: # PROJECTIONS: Projection data # GEOMETRY : Geometry describing the system # ANGLES : Propjection angles # And has a single optional argument: # FILTER: filter type applied to the projections. Possible options are # 'ram_lak' (default) # 'shepp_logan' # 'cosine' # 'hamming' # 'hann' # The choice of filter will modify the noise and sopme discreatization # errors, depending on which is chosen. # imgFDK1 = algs.fdk(noise_projections, geo, angles, filter="hann") imgFDK2 = algs.fdk(noise_projections, geo, angles, filter="ram_lak") # They look quite the same tigre.plotimg(np.concatenate([imgFDK1, imgFDK2], axis=1), dim="Z") # but it can be seen that one has bigger errors in the whole image, while # hte other just in the boundaries tigre.plotimg(np.concatenate( [abs(head - imgFDK1), abs(head - imgFDK2)], axis=1), dim="Z")
# 'Clims': Defines the data limits for the color, usefull when one wants to # see some specific range. The default computes the 5% and 95% percentiles # of the data and uses that as limit. clims = [0, 1] # 'Savegif': allows to save the plotted figure as an animated gif, # specified by the given filename. giffilename = "demo5image.gif" # 'Slice': allows to plot a single slice .Will overwrite the behaviour # of 'Step' slice = 64 # Lets go for it tigre.plotimg(imgFDK, dim=dimension, step=step, clims=clims, colormap=colormap, savegif=giffilename) # Remember: You can always plot more than 1 image together! tigre.plotimg(np.concatenate([head, imgFDK, imgOSSART], axis=1), dim="z") # Or even the errors! tigre.plotImg(np.concatenate([np.abs(head - imgFDK), np.abs(head - imgOSSART)]), dim="z")
#%% Load data and generate projections # define angles angles = np.linspace(0, 2 * np.pi, 100) ## Define angles numProjs = 100 anglesY = np.linspace(0, 2 * np.pi, numProjs) anglesZ2 = anglesY anglesZ1 = np.pi * np.sin(np.linspace(0, 2 * np.pi, numProjs)) angles = np.vstack([anglesZ1, anglesY, anglesZ2]).T ## Get Image head = sample_loader.load_head_phantom(geo.nVoxel) ## Project projections = tigre.Ax(head, geo, angles) tigre.plotproj(projections, anglesY) # angle information not right in the title ## Reconstruct: # Note, FDK will not work. imgSIRT = algs.sirt(projections, geo, angles, 50) imgCGLS = algs.cgls(projections, geo, angles, 10) tigre.plotimg(np.concatenate([head, imgSIRT, imgCGLS], axis=1), dim="z")