├── README.md ├── algorithms ├── admm.py ├── art.py ├── cg.py ├── class_admm_param.py ├── class_art_param.py ├── class_projectors_bspline.py ├── class_projectors_grid.py ├── class_projectors_radon.py ├── class_sir_param.py ├── create_virtual_sino.py ├── gridding_adjoint.py ├── gridding_forward.py ├── sir.py └── utils.py ├── common ├── myimage │ ├── my_image_display.py │ ├── my_image_io.py │ ├── my_image_process.py │ ├── my_image_transform.py │ └── my_print.py └── operators │ ├── bspline_module │ ├── bspline_functions.py │ ├── bspline_functions_backup.py │ ├── generalized_radon_transform.c │ └── pymodule_genradon │ │ ├── compile.py │ │ ├── create_module.py │ │ ├── gen_backproj.c │ │ ├── gen_forwproj.c │ │ ├── gen_forwproj_omp.c │ │ ├── genradon.c │ │ └── genradon.pyx │ ├── filters_module │ └── filters.py │ ├── gridrec_module │ ├── gridrec_lut.py │ └── pymodule_gridrec_v4 │ │ ├── COMPILE.txt │ │ ├── compile.py │ │ ├── create_module.py │ │ ├── fft.c │ │ ├── fft.h │ │ ├── filters.c │ │ ├── filters.h │ │ ├── gridrec_v4.c │ │ ├── gridrec_v4.pyx │ │ ├── gridrec_v4_backproj.c │ │ ├── gridrec_v4_forwproj.c │ │ ├── profile.py │ │ ├── profile.wis │ │ └── test.c │ ├── radon_module │ ├── pymodule_radon_distance_driven │ │ ├── clean.py │ │ ├── compile.py │ │ ├── create_module.py │ │ ├── radon_dd.c │ │ ├── radon_distance_driven.c │ │ └── radon_distance_driven.pyx │ ├── pymodule_radon_pixel_driven │ │ ├── clean.py │ │ ├── compile.py │ │ ├── create_module.py │ │ ├── radon_pd.c │ │ ├── radon_pixel_driven.c │ │ └── radon_pixel_driven.pyx │ ├── pymodule_radon_ray_driven │ │ ├── clean.py │ │ ├── compile.py │ │ ├── create_module.py │ │ ├── radon_ray_driven.c │ │ ├── radon_ray_driven.pyx │ │ └── radon_rd.c │ └── pymodule_radon_slant_stacking │ │ ├── clean.py │ │ ├── compile.py │ │ ├── create_module.py │ │ ├── radon_slant_stacking.c │ │ ├── radon_slant_stacking.pyx │ │ ├── radon_ss.c │ │ └── radon_ss.c__backup │ └── regularization_module │ ├── compile.py │ ├── create_penalty_module.py │ ├── haar.c │ ├── huber.c │ ├── my_image_denoise.py │ ├── penalty.c │ ├── penalty.pyx │ ├── penalty_weights.c │ ├── penalty_weights.h │ ├── pymodule_split_bregman_nltv │ ├── __init__.py │ ├── clean.py │ ├── compile.py │ ├── create_penalty_module.py │ ├── nl_weights.c │ ├── sbnltv.c │ ├── split_bregman_nltv.c │ └── split_bregman_nltv.pyx │ └── tikhonov.c ├── data ├── shepp_logan_pix0256.tif ├── shepp_logan_pix0256_ang0304_sino.tif ├── shepp_logan_pix0256_ang0304_sino_reco.tif └── sl3d.zip ├── scripts ├── test_create_sinogram.py ├── test_create_sinogram_3d.py ├── test_reconstr_admm_01.py ├── test_reconstr_admm_02.py ├── test_reconstr_admm_03.py ├── test_reconstr_admm_3d.py ├── test_reconstr_anal.py ├── test_reconstr_anal_3d.py ├── test_reconstr_sir_01.py ├── test_reconstr_sir_02.py ├── test_reconstr_sir_3d_01.py ├── test_reconstr_sir_3d_02.py ├── test_reconstr_sirt.py └── test_reconstr_sirt_3d.py └── setup.py /README.md: -------------------------------------------------------------------------------- 1 | ITERATIVE TOMOGRAPHIC RECONSTRUCTION ALGORITHMS 2 | =============================================== 3 | 4 | 5 | 6 | ## Brief description 7 | This repository contains different iterative reconstruction algorithms for 8 | parallel beam tomography. 9 | 10 | The following algorithms are included: 11 | 12 | * the **Simultaneous Iterative Reconstruction Technique (SIRT)**; 13 | 14 | * the **Maximum-Likelihood Expectation-Maximization (MLEM)**; 15 | 16 | * the **Separable Paraboloidal Surrogate (SPS)**; 17 | 18 | * the **Alternate Directions Method of Multipliers (ADMM)**. 19 | 20 | 21 | 22 | ## Installation 23 | Basic compilers like gcc and g++ and the FFTW library are required. 24 | The simplest way to use the code is with an Anaconda environment equipped with 25 | python-2.7, scipy, scikit-image and Cython. 26 | 27 | Procedure: 28 | 29 | 1. Create the Anaconda environment (if not created yet): `conda create -n iter-rec python=2.7 anaconda`. 30 | 31 | 2. Install required Python packages: `conda install -n iter-rec scipy scikit-image Cython`. 32 | 33 | 3. Activate the environment: `source activate iter-rec`. 34 | 35 | 4. `git clone git@github.com:arcaduf/iterative_tomographic_reconstruction_algorithms.git`. 36 | 37 | 5. Install routines in C: `python setup.py`. 38 | 39 | If `setup.py` runs without giving any error all subroutines in C have been installed and 40 | your python version meets all dependencies. 41 | 42 | If you run `python setup.py 1` (you can use any other character than 1), the 43 | all executables, temporary and build folders are deleted, the test data are 44 | placed in .zip files. In this way, the repository is restored to its original 45 | status, right after the download. 46 | 47 | 48 | 49 | ## Test the package 50 | Go inside the folder "scripts/" and run: `python run_all_tests.py` 51 | 52 | -------------------------------------------------------------------------------- /algorithms/class_admm_param.py: -------------------------------------------------------------------------------- 1 | from __future__ import division , print_function 2 | import numpy as np 3 | import sys 4 | import os 5 | import shutil 6 | sys.path.append( '../common/myimage/' ) 7 | import my_image_io as io 8 | 9 | 10 | myint = np.int 11 | myfloat = np.float32 12 | 13 | 14 | class admm_param: 15 | def __init__( self , npix , nang , nz , ctr , labelout , args ): 16 | ## Entries with no check 17 | self.ctr = ctr 18 | self.nang = nang 19 | self.nz = nz 20 | self.n_iter = args.n_iter 21 | self.n_iter_pcg = args.n_iter_pcg 22 | self.eps = args.eps 23 | self.plot = args.plot 24 | self.logfile = args.logfile 25 | self.init_object = args.init_object 26 | self.pc = args.pc 27 | self.mask = [] 28 | self.lt = args.lt 29 | self.num_cores = args.num_cores 30 | 31 | 32 | ## Tomographic projectors 33 | self.projector = args.projector 34 | 35 | 36 | ## Enable DPC reconstruction 37 | if args.dpc is True or args.dbp is True: 38 | self.radon_degree = 1 39 | else: 40 | self.radon_degree = 0 41 | 42 | 43 | ## Check point 44 | if self.radon_degree == 1: 45 | if self.projector == 'pix-driv' or self.projector == 'ray-driv' \ 46 | or self.projector == 'dist-driv': 47 | sys.exit( '\nERROR: selected projector ' + self.projector + \ 48 | ' cannot be used for differentiated tomography!!\n') 49 | 50 | 51 | ## Handling regularization 52 | if args.reg is not None: 53 | self.reg = args.reg 54 | 55 | if args.param is None: 56 | if self.reg == 'lasso' or self.reg == 'lasso-tv': 57 | param = '1.0:1.0:1.0' 58 | elif self.reg == 'pp-breg': 59 | param = '1.0:100.0:1.0' 60 | elif self.reg != 'cg': 61 | param = args.param 62 | 63 | 64 | ## Handling parameters 65 | if self.reg != 'cg': 66 | param = param.split( ':' ) 67 | self.lambd1 = myfloat( param[0] ) 68 | self.lambd2 = myfloat( param[1] ) 69 | self.mu = myfloat( param[2] ) 70 | self.relax = 1.0 71 | self.z1 = args.inner_padding 72 | 73 | 74 | ## Handling edge padding 75 | if args.lt is True: 76 | pad_factor = 0.87 77 | elif args.lt is False and args.edge_padding: 78 | pad_factor = args.edge_padding 79 | else: 80 | pad_factor = 0.0 81 | 82 | if pad_factor == 0: 83 | self.index_start = 0 84 | self.index_end = npix 85 | self.edge_padding = False 86 | self.npix_op = npix 87 | else: 88 | npad = int( pad_factor * npix ) 89 | npix_new = npix + 2 * npad 90 | self.index_start = myint( 0.5 * ( npix_new - npix ) ) 91 | self.index_end = self.index_start + npix 92 | self.edge_padding = True 93 | self.npix_op = npix_new 94 | 95 | 96 | ## Output name root 97 | if self.reg != 'cg': 98 | root = '_admm' 99 | 100 | if self.reg == 'lasso': 101 | root += '_lasso' 102 | elif self.reg == 'lasso-tv': 103 | root += '_lassotv' 104 | elif self.reg == 'pp-breg': 105 | root += '_ppbreg' 106 | elif args.reg == 'pp-rof': 107 | root += '_pprof' 108 | elif args.reg == 'pp-breg': 109 | root += '_ppbreg' 110 | elif args.reg == 'pp-chamb': 111 | root += '_ppchamb' 112 | elif args.reg == 'pp-nlmeans': 113 | root += '_ppnlmeans' 114 | elif args.reg == 'pp-tgv': 115 | root += '_pptgv' 116 | elif args.reg == 'pp-nltv': 117 | root += '_ppnltv' 118 | 119 | string = '_lambd1_' + str( self.lambd1 ) + \ 120 | '_lambd2_' + str( self.lambd2 ) + \ 121 | '_mu_' + str( self.mu ) 122 | root += string 123 | else: 124 | root = '_cgls' 125 | 126 | if self.projector == 'grid-kb': 127 | root += '_grid_kb' 128 | elif self.projector == 'grid-pswf': 129 | root += '_grid_pswf' 130 | elif self.projector == 'bspline': 131 | root += '_bspline' 132 | elif self.projector == 'radon': 133 | root += '_radon' 134 | elif self.projector == 'pix-driv': 135 | root += '_pd' 136 | elif self.projector == 'ray-driv': 137 | root += '_rd' 138 | elif self.projector == 'dist-driv': 139 | root += '_dd' 140 | 141 | self.root = root 142 | 143 | 144 | ## Saving each iteration result for analysis 145 | if args.checkit is not None: 146 | self.checkit = True 147 | path_rmse = '_rmse_folder' 148 | path_rmse = args.checkit + path_rmse + '/' 149 | if not os.path.exists( path_rmse ): 150 | os.makedirs( path_rmse ) 151 | else: 152 | shutil.rmtree( path_rmse ) 153 | os.makedirs( path_rmse ) 154 | self.path_rmse = path_rmse 155 | else: 156 | self.checkit = False 157 | 158 | 159 | ## Object support 160 | if args.mask is not None: 161 | self.mask = io.readImage( args.mask ).astype( np.uint8 ) 162 | else: 163 | self.mask = None 164 | 165 | 166 | ## Additional masks 167 | if args.mask_add is not None: 168 | if args.mask_add.find( ',' ) != -1: 169 | files = args.mask_add.split( ',' ) 170 | nfiles = len( files ) 171 | self.mask_add = [] 172 | for i in range( nfiles ): 173 | self.mask_add.append( io.readImage( files[i] ).astype( np.uint8 ) ) 174 | self.mask_add_n = nfiles 175 | else: 176 | files = args.mask_add 177 | nfiles = 1 178 | self.mask_add = [] 179 | self.mask_add.append( io.readImage( files ).astype( np.uint8 ) ) 180 | self.mask_add_n = nfiles 181 | else: 182 | self.mask_add = None 183 | -------------------------------------------------------------------------------- /algorithms/class_art_param.py: -------------------------------------------------------------------------------- 1 | from __future__ import division , print_function 2 | import numpy as np 3 | import sys 4 | import os 5 | import shutil 6 | sys.path.append( '../common/myimage/' ) 7 | import my_image_io as io 8 | 9 | 10 | myint = np.int 11 | myfloat = np.float32 12 | 13 | 14 | class art_param: 15 | def __init__( self , npix , nang , nz , ctr , labelout , args ): 16 | ## Entries with no check 17 | self.ctr = ctr 18 | self.nang = nang 19 | self.nz = nz 20 | self.n_iter = args.n_iter 21 | self.eps = args.eps 22 | self.plot = args.plot 23 | self.logfile = args.logfile 24 | self.init_object = args.init_object 25 | self.pc = args.pc 26 | self.mask = [] 27 | self.lt = args.lt 28 | self.tv = args.tv 29 | self.num_cores = args.num_cores 30 | 31 | 32 | ## Tomographic projectors 33 | self.projector = args.projector 34 | 35 | 36 | ## Algorithm 37 | self.algorithm = args.algorithm 38 | 39 | 40 | ## Enable DPC reconstruction 41 | if args.dpc is True or args.dbp is True: 42 | self.radon_degree = 1 43 | else: 44 | self.radon_degree = 0 45 | 46 | 47 | ## Check point 48 | if self.radon_degree == 1: 49 | if self.projector == 'pix-driv' or self.projector == 'ray-driv' \ 50 | or self.projector == 'dist-driv': 51 | sys.exit( '\nERROR: selected projector ' + self.projector + \ 52 | ' cannot be used for differentiated tomography!!\n') 53 | 54 | 55 | ## Handling edge padding 56 | if args.lt is True: 57 | pad_factor = 0.87 58 | elif args.lt is False and args.edge_padding: 59 | pad_factor = args.edge_padding 60 | else: 61 | pad_factor = 0.0 62 | 63 | if pad_factor == 0: 64 | self.index_start = 0 65 | self.index_end = npix 66 | self.edge_padding = False 67 | self.npix_op = npix 68 | else: 69 | npad = int( pad_factor * npix ) 70 | npix_new = npix + 2 * npad 71 | self.index_start = myint( 0.5 * ( npix_new - npix ) ) 72 | self.index_end = self.index_start + npix 73 | self.edge_padding = True 74 | self.npix_op = npix_new 75 | 76 | 77 | 78 | ## Output name root 79 | if self.algorithm == 'sirt': 80 | root = '_sirt' 81 | 82 | if self.projector == 'grid-kb': 83 | root += '_grid_kb' 84 | elif self.projector == 'grid-pswf': 85 | root += '_grid_pswf' 86 | elif self.projector == 'bspline': 87 | root += '_bspline' 88 | elif self.projector == 'slant': 89 | root += '_slant' 90 | elif self.projector == 'pix-driv': 91 | root += '_pd' 92 | elif self.projector == 'ray-driv': 93 | root += '_rd' 94 | elif self.projector == 'dist-driv': 95 | root += '_dd' 96 | 97 | self.root = root 98 | 99 | 100 | ## Saving each iteration result for analysis 101 | if args.checkit is not None: 102 | self.checkit = True 103 | path_rmse = '_rmse_folder' 104 | path_rmse = args.checkit + path_rmse + '/' 105 | if not os.path.exists( path_rmse ): 106 | os.makedirs( path_rmse ) 107 | else: 108 | shutil.rmtree( path_rmse ) 109 | os.makedirs( path_rmse ) 110 | self.path_rmse = path_rmse 111 | else: 112 | self.checkit = False 113 | 114 | 115 | ## Object support 116 | if args.mask is not None: 117 | self.mask = io.readImage( args.mask ).astype( np.uint8 ) 118 | else: 119 | self.mask = None 120 | 121 | 122 | ## Additional masks 123 | if args.mask_add is not None: 124 | if args.mask_add.find( ',' ) != -1: 125 | files = args.mask_add.split( ',' ) 126 | nfiles = len( files ) 127 | self.mask_add = [] 128 | for i in range( nfiles ): 129 | self.mask_add.append( io.readImage( files[i] ).astype( np.uint8 ) ) 130 | self.mask_add_n = nfiles 131 | else: 132 | files = args.mask_add 133 | nfiles = 1 134 | self.mask_add = [] 135 | self.mask_add.append( io.readImage( files ).astype( np.uint8 ) ) 136 | self.mask_add_n = nfiles 137 | else: 138 | self.mask_add = None 139 | -------------------------------------------------------------------------------- /algorithms/class_projectors_bspline.py: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | ########################################################## 3 | #### #### 4 | #### CLASS TO HANDLE THE TOMOGRAPHIC #### 5 | #### FORWARD AND BACKPROJECTOR #### 6 | #### #### 7 | ########################################################## 8 | ########################################################## 9 | 10 | 11 | 12 | 13 | #### PYTHON MODULES 14 | import sys 15 | import numpy as np 16 | 17 | 18 | 19 | 20 | #### MY GRIDREC MODULE 21 | cpath = '../common/' 22 | sys.path.append( cpath + 'operators/bspline_module/' ) 23 | sys.path.append( cpath + 'operators/bspline_module/pymodule_genradon/' ) 24 | sys.path.append( cpath + 'operators/filters_module' ) 25 | import bspline_functions as bfun 26 | import genradon as gr 27 | import my_image_display as dis 28 | import filters as fil 29 | 30 | 31 | 32 | 33 | #### MY FORMAT VARIABLES 34 | myfloat = np.float32 35 | myint = np.int 36 | 37 | 38 | 39 | 40 | #### CLASS PROJECTORS 41 | class projectors: 42 | 43 | ## Init class projectors 44 | def __init__( self , npix , angles , param , ctr=0.0 , bspline_degree = 3 , proj_support_y=4 , 45 | nsamples_y=2048 , radon_degree=1 ): 46 | 47 | ## Compute regridding look-up-table and deapodizer 48 | nang = len( angles ) 49 | angles = np.arange( nang ) 50 | angles = ( angles * 180.0 )/myfloat( nang ) 51 | lut = bfun.init_lut_bspline( nsamples_y , angles , bspline_degree , 52 | radon_degree , proj_support_y ) 53 | lut0 = bfun.init_lut_bspline( nsamples_y , angles , bspline_degree , 54 | 0 , proj_support_y ) 55 | 56 | 57 | ## Assign parameters 58 | self.nang = nang 59 | self.filt = 'ramp' 60 | self.plot = True 61 | self.radon_degree = radon_degree 62 | self.lut = lut.astype( myfloat ) 63 | self.lut0 = lut0.astype( myfloat ) 64 | self.angles = angles.astype( myfloat ) 65 | self.param_spline = np.array( [ lut.shape[1] , proj_support_y ] , dtype=myfloat ) 66 | 67 | 68 | 69 | 70 | ## Forward projector 71 | def A( self , x ): 72 | return gr.forwproj( x.astype( myfloat ) , self.angles , self.lut , self.param_spline ) 73 | 74 | 75 | 76 | ## Backprojector 77 | def At( self , x ): 78 | return gr.backproj( x.astype( myfloat ) , self.angles , self.lut , self.param_spline ) 79 | 80 | 81 | 82 | ## Filtered backprojection 83 | def fbp( self , x ): 84 | ## Option DPC 85 | x0 = x[:,::-1].copy() 86 | if self.radon_degree == 0: 87 | dpc = False 88 | else: 89 | dpc = True 90 | 91 | 92 | ## Filtering projection 93 | x0[:] = fil.filter_proj( x0 , ftype=self.filt , dpc=dpc ) 94 | 95 | 96 | ## Backprojection 97 | reco = gr.backproj( x0.astype( myfloat ) , self.angles , self.lut0 , self.param_spline ) 98 | 99 | 100 | ## Normalization 101 | if dpc is True: 102 | reco *= np.pi / ( 1.0 * self.nang ) 103 | else: 104 | reco *= np.pi / ( 2.0 * self.nang ) 105 | 106 | reco[:] = reco[::-1,::-1] 107 | 108 | return reco 109 | 110 | -------------------------------------------------------------------------------- /algorithms/class_projectors_grid.py: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | ########################################################## 3 | #### #### 4 | #### CLASS TO HANDLE THE TOMOGRAPHIC #### 5 | #### FORWARD AND BACKPROJECTOR #### 6 | #### #### 7 | ########################################################## 8 | ########################################################## 9 | 10 | 11 | 12 | 13 | #### PYTHON MODULES 14 | from __future__ import division , print_function 15 | import sys 16 | import numpy as np 17 | 18 | 19 | 20 | 21 | #### MY GRIDREC MODULE 22 | cpath = '../common/' 23 | sys.path.append( cpath + 'operators/gridrec_module/' ); 24 | sys.path.append( cpath + 'operators/gridrec_module/pymodule_gridrec_v4/' ) 25 | import gridrec_lut as glut 26 | import gridrec_v4 as grid 27 | 28 | 29 | 30 | 31 | #### MY FORMAT VARIABLES 32 | myfloat = np.float32 33 | myint = np.int 34 | 35 | 36 | 37 | 38 | #### LIST OF FILTERS 39 | filter_list = np.array( [ ['none',''] , ['ramp',''] , ['shepp-logan','shlo'] , 40 | ['hanning','hann'] , ['hamming','hamm'] , 41 | ['lanczos','lanc'] , ['parzen','parz' ] ] ) 42 | 43 | 44 | 45 | 46 | #### CLASS PROJECTORS 47 | class projectors: 48 | 49 | ## Init class projectors 50 | def __init__( self , npix , angles , ctr=0.0 , kernel='pswf' , oversampl=2.0 , interp='nn' , 51 | radon_degree=0 , W=7.0 , errs=1e-3 , filt=None , args=None ): 52 | 53 | ## Get parameters from input arguments 54 | if args is not None: 55 | kernel = args.kernel 56 | oversampl = args.oversampl 57 | interp = args.kernel_interp 58 | W = args.kernel_size 59 | errs = args.errs 60 | 61 | if hasattr( args , 'dpc' ) is True and args.dpc is True: 62 | radon_degree = 1 63 | else: 64 | radon_degree = 0 65 | 66 | if hasattr( args , 'dbp' ) is True and args.dbp is True: 67 | if args.dbp is True: 68 | radon_degree = 1 69 | else: 70 | radon_degree = 0 71 | 72 | if hasattr( args , 'filt' ) is True: 73 | filt = args.filt 74 | else: 75 | filt = None 76 | 77 | 78 | 79 | ## Compute regridding look-up-table and deapodizer 80 | W , lut , deapod = glut.configure_regridding( npix , kernel , oversampl , interp , W , errs ) 81 | 82 | 83 | 84 | ## Assign parameters 85 | if interp == 'nn': 86 | interp1 = 0 87 | else: 88 | interp1 = 1 89 | 90 | 91 | 92 | ## Filter flag 93 | if filt is None: 94 | filt = 1 95 | else: 96 | filt = np.argwhere( filter_list == filt )[0][0] 97 | 98 | 99 | 100 | ## Setting for forward nad backprojection 101 | param_nofilt = np.array( [ ctr , 0 , 0 , oversampl , interp1 , 102 | len( lut ) - 5 , W , radon_degree ] ) 103 | 104 | 105 | 106 | ## Setting for filtered backprojection 107 | param_filt = np.array( [ ctr , filt , 0 , oversampl , interp1 , 108 | len( lut ) - 5 , W , radon_degree ] ) 109 | 110 | 111 | 112 | ## Convert all input arrays to float 32 113 | self.lut = lut.astype( myfloat ) 114 | self.deapod = deapod.astype( myfloat ) 115 | self.angles = angles.astype( myfloat ) 116 | self.param_nofilt = param_nofilt.astype( myfloat ) 117 | self.param_filt = param_filt.astype( myfloat ) 118 | 119 | 120 | 121 | ## Overview of the projector settings 122 | print('\nSetting of the regridding projectors:') 123 | print('Kernel type: ', kernel) 124 | print('Oversampling: ', oversampl) 125 | print('Interpolation:', interp) 126 | print('LUT resolution: ', len( self.lut )) 127 | print('Kernel size: ' , W) 128 | print('Approx. error of LUT: ', errs) 129 | print('Filter for backprojection: ' , filter_list[filt,0]) 130 | print('Radon transform degree: ', radon_degree) 131 | 132 | 133 | 134 | ## Forward projector 135 | def A( self , x ): 136 | return grid.forwproj( x.astype( myfloat ) , self.angles , self.param_nofilt , 137 | self.lut , self.deapod ) 138 | 139 | 140 | 141 | ## Backprojector 142 | def At( self , x ): 143 | return grid.backproj( x.astype( myfloat ) , self.angles , self.param_nofilt , 144 | self.lut , self.deapod ) 145 | 146 | 147 | 148 | ## Filtered backprojection (with ramp filter as default) 149 | def fbp( self , x ): 150 | return grid.backproj( x , self.angles , self.param_filt , 151 | self.lut , self.deapod ) 152 | -------------------------------------------------------------------------------- /algorithms/class_projectors_radon.py: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | ########################################################## 3 | #### #### 4 | #### CLASS TO HANDLE THE TOMOGRAPHIC #### 5 | #### FORWARD AND BACKPROJECTOR #### 6 | #### #### 7 | ########################################################## 8 | ########################################################## 9 | 10 | 11 | 12 | 13 | #### PYTHON MODULES 14 | import sys 15 | import numpy as np 16 | 17 | 18 | 19 | 20 | #### MY GRIDREC MODULE 21 | cpath = '../common/' 22 | sys.path.append( cpath + 'operators/filters_module/' ) 23 | import filters as fil 24 | sys.path.append( cpath + 'operators/radon_module/pymodule_radon_pixel_driven' ) 25 | sys.path.append( cpath + 'operators/radon_module/pymodule_radon_ray_driven' ) 26 | sys.path.append( cpath + 'operators/radon_module/pymodule_radon_distance_driven' ) 27 | sys.path.append( cpath + 'operators/radon_module/pymodule_radon_slant_stacking' ) 28 | import radon_pixel_driven as rpd 29 | import radon_ray_driven as rrd 30 | import radon_distance_driven as rdd 31 | import radon_slant_stacking as rss 32 | 33 | 34 | 35 | 36 | #### MY FORMAT VARIABLES 37 | myfloat = np.float32 38 | myint = np.int 39 | 40 | 41 | 42 | 43 | #### CLASS PROJECTORS 44 | class projectors: 45 | 46 | ## Init class projectors 47 | def __init__( self , npix , angles , oper='pd' , ctr=0.0 ): 48 | nang = len( angles ) 49 | angles1 = angles * np.pi / 180.0 50 | 51 | self.nang = nang 52 | self.filt = 'ramp' 53 | self.plot = True 54 | self.angles = angles1.astype( myfloat ) 55 | self.oper = oper 56 | 57 | 58 | 59 | ## Forward projector 60 | def A( self , x ): 61 | if self.oper == 'pd': 62 | return rpd.forwproj( x.astype( myfloat ) , self.angles , 1 ) 63 | elif self.oper == 'rd': 64 | return rrd.forwproj( x.astype( myfloat ) , self.angles ) 65 | elif self.oper == 'dd': 66 | return rdd.forwproj( x.astype( myfloat ) , self.angles ) 67 | elif self.oper == 'ss': 68 | return rss.forwproj( x.astype( myfloat ) , self.angles , 1 ) 69 | 70 | 71 | 72 | ## Backprojector 73 | def At( self , x ): 74 | if self.oper == 'pd': 75 | return rpd.backproj( x.astype( myfloat ) , self.angles , 1 ) 76 | elif self.oper == 'rd': 77 | return rrd.backproj( x.astype( myfloat ) , self.angles ) 78 | elif self.oper == 'dd': 79 | return rdd.backproj( x.astype( myfloat ) , self.angles ) 80 | elif self.oper == 'ss': 81 | return rss.backproj( x.astype( myfloat ) , self.angles , 1 ) 82 | 83 | 84 | 85 | ## Filtered backprojection 86 | def fbp( self , x ): 87 | ## Option DPC 88 | x0 = x[:,::-1].copy() 89 | 90 | 91 | ## Filtering projection 92 | x0[:] = fil.filter_proj( x0 , ftype=self.filt , dpc=dpc ) 93 | 94 | 95 | ## Backprojection 96 | if self.oper == 'pd': 97 | reco = rpd.backproj( x.astype( myfloat ) , self.angles , 1 ) 98 | elif self.oper == 'rd': 99 | reco = rrd.backproj( x.astype( myfloat ) , self.angles ) 100 | elif self.oper == 'dd': 101 | reco = rdd.backproj( x.astype( myfloat ) , self.angles ) 102 | elif self.oper == 'ss': 103 | reco = rss.backproj( x.astype( myfloat ) , self.angles , 1 ) 104 | 105 | 106 | ## Normalization 107 | reco *= np.pi / ( 2.0 * self.nang ) 108 | reco[:] = reco[::-1,::-1] 109 | 110 | return reco 111 | -------------------------------------------------------------------------------- /algorithms/class_sir_param.py: -------------------------------------------------------------------------------- 1 | from __future__ import division , print_function 2 | import numpy as np 3 | import sys 4 | import os 5 | import shutil 6 | 7 | myint = np.int 8 | 9 | 10 | class sir_param: 11 | def __init__( self , nang , npix , nz , ctr , labelout , args ): 12 | ## Entries with no check 13 | self.ctr = ctr 14 | self.nang = nang 15 | self.nz = nz 16 | self.eps = args.eps 17 | self.n_iter = args.n_iter 18 | self.plot = args.plot 19 | self.logfile = args.logfile 20 | self.reg_cost = args.reg_cost 21 | self.huber_cost = args.huber_cost 22 | self.init_object = args.init_object 23 | self.algorithm = args.algorithm 24 | self.num_cores = args.num_cores 25 | 26 | 27 | ## Tomographic projectors 28 | self.projector = args.projector 29 | 30 | 31 | ## Handling edge padding 32 | if args.lt is True: 33 | pad_factor = 0.87 34 | elif args.lt is False and args.edge_padding: 35 | pad_factor = args.edge_padding 36 | else: 37 | pad_factor = 0.0 38 | 39 | if pad_factor == 0: 40 | self.index_start = 0 41 | self.index_end = npix 42 | self.edge_padding = False 43 | self.npix_op = npix 44 | else: 45 | npad = int( pad_factor * npix ) 46 | npix_new = npix + 2 * npad 47 | self.index_start = myint( 0.5 * ( npix_new - npix ) ) 48 | self.index_end = self.index_start + npix 49 | self.edge_padding = True 50 | self.npix_op = npix_new 51 | 52 | 53 | ## Handling regularization 54 | self.reg = None 55 | reg = args.regularization 56 | if reg is not None: 57 | if reg == 'h' or reg == 'huber': 58 | self.reg = 'huber' 59 | elif reg == 't' or reg == 'tikhonov': 60 | self.reg = 'tikhonov' 61 | elif reg == 'a' or reg == 'haar': 62 | self.reg = 'haar' 63 | else: 64 | print( 'Warning regularization "', reg,'" is not available!' ) 65 | print( 'Reconstructing with no regularization' ) 66 | 67 | 68 | ## Output name root 69 | if args.algorithm == 'sps': 70 | root = '_sps' 71 | elif args.algorithm == 'em': 72 | root = '_em' 73 | 74 | if self.reg == 'huber': 75 | root += '_huber' 76 | elif self.reg == 'tikhonov': 77 | root += '_tikh' 78 | elif self.reg == 'haar': 79 | root += '_haar' 80 | 81 | if self.projector == 'grid-kb': 82 | root += '_grid_kb' 83 | elif self.projector == 'grid-pswf': 84 | root += '_grid_pswf' 85 | elif self.projector == 'bspline': 86 | root += '_bspline' 87 | elif self.projector == 'radon': 88 | root += '_radon' 89 | elif self.projector == 'pix-driv': 90 | root += '_pd' 91 | elif self.projector == 'ray-driv': 92 | root += '_rd' 93 | elif self.projector == 'dist-driv': 94 | root += '_dd' 95 | 96 | self.root = root 97 | 98 | 99 | ## Handling stopping criterion 100 | if self.eps is None: 101 | self.eps = -1.0 102 | elif self.n_iter is None: 103 | self.n_iter = 1e20 104 | 105 | 106 | ## Saving each iteration result for analysis 107 | if args.checkit is not None: 108 | self.checkit = True 109 | path_rmse = '_rmse_folder' 110 | path_rmse = args.checkit + path_rmse + '/' 111 | if not os.path.exists( path_rmse ): 112 | os.makedirs( path_rmse ) 113 | else: 114 | shutil.rmtree( path_rmse ) 115 | os.makedirs( path_rmse ) 116 | self.path_rmse = path_rmse 117 | else: 118 | self.checkit = False 119 | -------------------------------------------------------------------------------- /algorithms/create_virtual_sino.py: -------------------------------------------------------------------------------- 1 | ################################################################################# 2 | ################################################################################# 3 | ################################################################################# 4 | ####### ####### 5 | ####### CREATE VIRTUAL SINOGRAM ####### 6 | ####### ####### 7 | ####### Author: Filippo Arcadu, arcusfil@gmail.com, 22/02/2016 ####### 8 | ####### ####### 9 | ################################################################################# 10 | ################################################################################# 11 | ################################################################################# 12 | 13 | 14 | 15 | 16 | #### PYTHON MODULES 17 | from __future__ import division , print_function 18 | import time 19 | import datetime 20 | import argparse 21 | import sys 22 | import numpy as np 23 | from skimage.morphology import erosion, dilation, opening, closing, white_tophat 24 | from skimage.morphology import black_tophat, skeletonize, convex_hull_image 25 | from skimage.morphology import disk 26 | from scipy.stats import norm 27 | 28 | 29 | 30 | 31 | #### MY PYTHON MODULES 32 | path_common = '../common/' 33 | sys.path.append( path_common + 'pymodule_myimage/' ) 34 | import my_image_io as io 35 | import my_image_display as dis 36 | import my_image_process as proc 37 | import utils 38 | 39 | 40 | 41 | 42 | #### MY PROJECTOR CLASS 43 | import class_projectors_grid as cpj 44 | 45 | 46 | 47 | 48 | #### MY FORMAT VARIABLES & CONSTANTS 49 | myfloat = np.float32 50 | myint = np.int 51 | pi = np.pi 52 | eps = 1e-8 53 | 54 | 55 | 56 | 57 | ########################################################## 58 | ########################################################## 59 | #### #### 60 | #### GET INPUT ARGUMENTS #### 61 | #### #### 62 | ########################################################## 63 | ########################################################## 64 | 65 | def getArgs(): 66 | parser = argparse.ArgumentParser(description='Create virtual sinogram', 67 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 68 | 69 | parser.add_argument('-Di', dest='pathin', default='./', 70 | help='Specify path to input data') 71 | 72 | parser.add_argument('-i', dest='filein', 73 | help='Specify name of input sinogram') 74 | 75 | parser.add_argument('-Do', dest='pathout', 76 | help='Specify path to output data') 77 | 78 | parser.add_argument('-o', dest='fileout', 79 | help='Specify name of output reconstruction') 80 | 81 | parser.add_argument('-f',dest='filt', default='hamm', 82 | help = 'Specify filter: ' 83 | + 'none ---> non-filtered backprojection' 84 | + 'ramp ---> ramp or Ram-Lak filter' 85 | + 'shlo ---> Shepp-Logan filter' 86 | + 'hann ---> Hanning filter' 87 | + 'hamm ---> Hamming filter' 88 | + 'parz ---> Parzen filter' 89 | + 'lanc ---> Lanczos filter' 90 | + 'ramp-ext ---> Ramp external' 91 | + 'conv-ext ---> Convolution external') 92 | 93 | parser.add_argument('-c', dest='ctr', type=myfloat, 94 | help='Centre of rotation (default: center of the image);' 95 | + ' -1 ---> search for the center of rotation') 96 | 97 | parser.add_argument('-e', dest='edge_pad', action='store_true', 98 | help='Enable edge-padding') 99 | 100 | parser.add_argument('-r', dest='back_rem', action='store_true', 101 | help='Background equalization') 102 | 103 | parser.add_argument('-p',dest='plot', action='store_true', 104 | help='Display check-plots during the run of the code') 105 | 106 | args = parser.parse_args() 107 | 108 | return args 109 | 110 | 111 | 112 | 113 | ########################################################## 114 | ########################################################## 115 | #### #### 116 | #### BACKGROUND EQUALIZATION #### 117 | #### #### 118 | ########################################################## 119 | ########################################################## 120 | 121 | def background_equalization( img , size=20 ): 122 | nx , ny = img.shape 123 | tracer = np.min( img ) - 100 124 | se = disk( size ) 125 | yy , xx = np.meshgrid( np.arange( ny ) , np.arange( nx ) ) 126 | 127 | 128 | img_e = erosion( img , se ) 129 | img_e = img_e.reshape( -1 ) 130 | 131 | 132 | mu , std = norm.fit( img_e ) 133 | img_e[ ( img_e < mu - 0.3 * std ) | ( img_e > mu + 0.3 * std ) ] = tracer 134 | it = np.argwhere( img_e != tracer ).reshape( -1 ) 135 | 136 | 137 | X = np.hstack( ( np.ones( ( nx * ny , 1 ) ) , 138 | xx.reshape( nx * ny , 1 ) , 139 | yy.reshape( nx * ny , 1 ) ) ) 140 | X1 = X[it,:] 141 | M = np.linalg.lstsq( X1 , img_e[it] )[0].reshape( 3 , 1 ) 142 | back = np.array( np.dot( X , M ) ).reshape( nx , ny ) 143 | new = img - back 144 | new = ( new - np.min( new ) ) * 255 / ( np.max( new ) - np.min( new ) ) 145 | new = new.astype( np.uint8 ) 146 | 147 | return new 148 | 149 | 150 | 151 | ########################################################## 152 | ########################################################## 153 | #### #### 154 | #### MAIN #### 155 | #### #### 156 | ########################################################## 157 | ########################################################## 158 | 159 | def main(): 160 | ## Initial print 161 | print('\n') 162 | print('########################################################') 163 | print('#### CREATE VIRTUAL SINOGRAM ####') 164 | print('########################################################') 165 | print('\n') 166 | 167 | 168 | 169 | ## Get arguments 170 | args = getArgs() 171 | 172 | 173 | 174 | ## Get input/output directory 175 | pathin , pathout = utils.get_io_path( args ) 176 | 177 | print('\nInput path:\n', pathin) 178 | print('\nOutput path:\n', pathout) 179 | 180 | 181 | 182 | ## Get input files 183 | file_list , file1 , nimg , ext = utils.get_input( args , pathin ) 184 | 185 | print('\nNumber of input files: ' , nimg) 186 | print('Extension of the files: ', ext) 187 | 188 | 189 | 190 | ## Read first sinogram 191 | sino = io.readImage( pathin + file1 ).astype( myfloat ) 192 | nang , npix = sino.shape 193 | 194 | print('\nSinogram to reconstruct:\n', file1) 195 | print('Number of projection angles: ', nang) 196 | print('Number of pixels: ', npix) 197 | 198 | if args.plot is True: 199 | dis.plot( sino , 'Input sinogram' ) 200 | 201 | 202 | 203 | ## Set center of rotation axis 204 | if args.ctr == None: 205 | ctr = 0.0 206 | print('Center of rotation axis placed at pixel: ', npix * 0.5) 207 | else: 208 | ctr = args.ctr 209 | print('Center of rotation axis placed at pixel: ', ctr) 210 | 211 | 212 | 213 | ## Enable edge-padding 214 | if args.edge_pad is True: 215 | sino = proc.sino_edge_padding( sino , 0.5 ).astype( myfloat ) 216 | i1 = int( ( sino.shape[1] - npix ) * 0.5 ) 217 | i2 = i1 + npix 218 | npix = sino.shape[1] 219 | 220 | if ctr != 0.0: 221 | ctr += i1 222 | 223 | if args.plot is True: 224 | dis.plot( sino , 'Edge padded sinogram' ) 225 | else: 226 | i1 = 0 227 | i2 = npix 228 | 229 | 230 | 231 | ## Prepare projectors 232 | ang = np.arange( nang ) * 180.0 / myfloat( nang ) 233 | tp = cpj.projectors( npix , ang , kernel='kb' , oversampl=2.32 , 234 | W=6.6 , errs=6.0e-6 , interp='lin' , 235 | radon_degree=0 , filt=args.filt , ctr=ctr ) 236 | 237 | 238 | 239 | ## Reconstruct 240 | reco = tp.fbp( sino ) 241 | if args.plot is True: 242 | dis.plot( reco , 'Reconstruction' ) 243 | #reco = reco[i1:i2,i1:i2] 244 | 245 | 246 | 247 | ## Zero-out pixels outside resolution circle 248 | reco_new = reco.copy(); reco_new[:] = 0.0 249 | io.writeImage( 'reco.DMP' , reco[i1:i2,i1:i2] ) 250 | reco_new[i1:i2,i1:i2] = utils.resol_circle_constr( reco[i1:i2,i1:i2] ) 251 | reco[:] = reco_new[:] 252 | 253 | if args.plot is True: 254 | dis.plot( reco , 'Constrained reconstruction' ) 255 | io.writeImage( 'reco_circle.DMP' , reco ) 256 | 257 | 258 | 259 | ## Background equalization 260 | reco[:] = background_equalization( reco ) 261 | 262 | if args.plot is True: 263 | dis.plot( reco , 'Equalized reconstruction' ) 264 | io.writeImage( 'reco_equaliz.DMP' , reco ) 265 | 266 | 267 | 268 | ## Forward projection 269 | nang_new = np.int( npix * np.pi / 2.0 ) 270 | ang_new = np.arange( nang_new ) * 180.0 / np.float32( nang_new ) 271 | tp = cpj.projectors( npix , ang_new , kernel='kb' , oversampl=2.32 , 272 | W=6.6 , errs=6.0e-6 , interp='lin' , 273 | radon_degree=0 ) 274 | sino = tp.A( reco ) 275 | #sino = sino[:,i1:i2] 276 | 277 | if args.plot is True: 278 | dis.plot( sino , 'Forward projection' ) 279 | io.writeImage( 'sino_circle.DMP' , sino ) 280 | 281 | 282 | 283 | ## Save output file 284 | if args.fileout is None: 285 | filein = args.filein 286 | extension = filein[len(filein)-4:] 287 | fileout = filein[:len(filein)-4] + '_virt.tif' 288 | else: 289 | fileout = args.fileout 290 | io.writeImage( pathout + fileout , sino ) 291 | print( '\nWritten output file:\n' , pathout + fileout ) 292 | 293 | 294 | 295 | 296 | ########################################################## 297 | ########################################################## 298 | #### #### 299 | #### MAIN #### 300 | #### #### 301 | ########################################################## 302 | ########################################################## 303 | 304 | if __name__ == '__main__': 305 | main() 306 | -------------------------------------------------------------------------------- /algorithms/utils.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | ######################################################################## 3 | #### #### 4 | #### Utilities for iterative reconstruction #### 5 | #### #### 6 | #### Author: Filippo Arcadu, arcusfil@gmail.com, 12/02/2015 #### 7 | #### #### 8 | ######################################################################## 9 | ######################################################################## 10 | 11 | 12 | 13 | 14 | #### PYTHON MODULES 15 | from __future__ import division , print_function 16 | import numpy as np 17 | import sys 18 | import os 19 | import shutil 20 | import glob 21 | 22 | 23 | 24 | 25 | #### MY VARIABLE TYPE 26 | myint = np.int 27 | myfloat = np.float32 28 | 29 | 30 | 31 | 32 | ############################################################# 33 | ############################################################# 34 | #### #### 35 | #### HANDLING INPUT ARGUMENTS #### 36 | #### #### 37 | ############################################################# 38 | ############################################################# 39 | 40 | ## Deal with input and output path 41 | def get_io_path( args ): 42 | pathin = args.pathin 43 | if pathin[len(pathin)-1] != '/': 44 | pathin += '/' 45 | 46 | if args.pathout is None: 47 | pathout = pathin 48 | else: 49 | pathout = args.pathout 50 | if pathout[len(pathout)-1] != '/': 51 | pathout += '/' 52 | if os.path.exists( pathout ) is True and args.filein is None: 53 | shutil.rmtree( pathout ) 54 | os.makedirs( pathout ) 55 | elif os.path.exists( pathout ) is False: 56 | os.makedirs( pathout ) 57 | 58 | return pathin , pathout 59 | 60 | 61 | 62 | ## Deal with the input data (single file or stack) 63 | def get_input( args , pathin ): 64 | file_list = [] 65 | if args.filein is not None: 66 | file1 = args.filein 67 | print('\nReading single input file:\n', file1) 68 | ext = file1[len(file1)-4:] 69 | nimg = 1 70 | 71 | else: 72 | print('\nReading stack of images placed\n' ) 73 | curr_dir = os.getcwd() 74 | os.chdir( pathin ) 75 | 76 | for f in os.listdir('./'): 77 | if f.endswith( '.DMP' ) is True: 78 | ext = '.DMP' 79 | break 80 | elif f.endswith( '.tif' ) is True: 81 | ext = '.tif' 82 | break 83 | else: 84 | sys.exit('\nERROR: no .DMP or .tif file found in:\n' + pathin) 85 | 86 | file_list.append( sorted( glob.glob( '*' + ext ) ) ) 87 | nimg = len( file_list[0] ) 88 | file1 = file_list[0][0] 89 | os.chdir( curr_dir ) 90 | 91 | return file_list , file1 , nimg , ext 92 | 93 | 94 | 95 | 96 | ############################################################# 97 | ############################################################# 98 | #### #### 99 | #### CREATE PROJECTION ANGLES #### 100 | #### #### 101 | ############################################################# 102 | ############################################################# 103 | 104 | def create_equally_spaced_angles( nang , angle_start , angle_end ): 105 | angles = np.linspace( angle_start , angle_end , nang , endpoint=False ) 106 | return angles 107 | 108 | 109 | 110 | def create_pseudo_polar_angles( nang ): 111 | if nang % 4 != 0: 112 | raise Exception('\n\tError inside createPseudoPolarAngles:' 113 | +'\n\t nang (input) is not divisible by 4 !\n') 114 | n = nang 115 | pseudo_angles = np.zeros(n,dtype=myfloat) 116 | pseudoAlphaArr = np.zeros(n,dtype=myfloat) 117 | pseudoGridIndex = np.zeros(n,dtype=int) 118 | index = np.arange(int(n/4)+1,dtype=int) 119 | 120 | pseudo_angles[0:int(n/4)+1] = np.arctan(4*index[:]/myfloat(n)) 121 | pseudo_angles[int(n/2):int(n/4):-1] = np.pi/2-pseudo_angles[0:int(n/4)] 122 | pseudo_angles[int(n/2)+1:] = np.pi-pseudo_angles[int(n/2)-1:0:-1] 123 | 124 | pseudo_angles *= 180.0 / np.pi 125 | 126 | return pseudo_angles 127 | 128 | 129 | 130 | ## Note: this function gives as output angles in degrees, 131 | ## excluding the right extreme, e.g., [ 0.0 , 180 ) 132 | 133 | def create_projection_angles( nang=None , start=0.0 , end=180.0 , 134 | pseudo=0 , wedge=False , textfile=None ): 135 | 136 | ## Create angles 137 | if textfile is None: 138 | ## Create equally angularly space angles in [alpha_{0},alpha_{1}) 139 | if pseudo == 0: 140 | if wedge is False: 141 | angles = create_equally_spaced_angles( nang , start , end ) 142 | 143 | else: 144 | angles1 = create_equally_spaced_angles( nang , 0.0 , start ) 145 | angles2 = create_equally_spaced_angles( nang , end , 180.0 ) 146 | angles = np.concatenate( ( angles1 , angles2 ) , axis=1 ) 147 | 148 | ## Create equally sloped angles 149 | else: 150 | angles = create_pseudo_polar_angles( nang ) 151 | 152 | 153 | ## Read angles from text file 154 | else: 155 | angles = np.fromfile( textfile , sep="\t" ) 156 | 157 | 158 | return angles 159 | 160 | 161 | 162 | 163 | ############################################################# 164 | ############################################################# 165 | #### #### 166 | #### APPLY PHYSICAL CONSTRAINTS #### 167 | #### #### 168 | ############################################################# 169 | ############################################################# 170 | 171 | def phys_constr( x , beta = 0.9 ): 172 | i = np.argwhere( x < 0 ) 173 | 174 | x[i[:,0],i[:,1],i[:,2]] *= beta 175 | 176 | return x 177 | 178 | 179 | 180 | 181 | ############################################################# 182 | ############################################################# 183 | #### #### 184 | #### SOFT THRESHOLDING #### 185 | #### #### 186 | ############################################################# 187 | ############################################################# 188 | 189 | def soft_thres( x , beta ): 190 | if beta <=0: 191 | raise Error( 'beta in soft thresholding has to be > 0' ) 192 | 193 | i1 = np.argwhere( x > beta ) 194 | i2 = np.argwhere( np.abs( x ) <= beta ) 195 | i3 = np.argwhere( x < -beta ) 196 | 197 | x[i1[:,0],i1[:,1],i1[:,2]] -= beta 198 | x[i2[:,0],i2[:,1],i2[:,2]] = 0 199 | x[i3[:,0],i3[:,1],i3[:,2]] += beta 200 | 201 | return x 202 | 203 | 204 | 205 | 206 | ############################################################# 207 | ############################################################# 208 | #### #### 209 | #### SOFT THRESHOLDING #### 210 | #### #### 211 | ############################################################# 212 | ############################################################# 213 | 214 | def hard_thres( x , beta ): 215 | t = np.maximum( 0 , x - beta ) - np.maximum( 0 , -x -beta ) 216 | return t 217 | 218 | 219 | 220 | 221 | ############################################################# 222 | ############################################################# 223 | #### #### 224 | #### ZERO-OUT REGIONS OUT OF THE RESOLUTION CIRCLE #### 225 | #### #### 226 | ############################################################# 227 | ############################################################# 228 | 229 | def resol_circle_constr( img ): 230 | if img.ndim == 2: 231 | m , n = img.shape 232 | else: 233 | nz , m , n = img.shape 234 | 235 | ctr_x = m * 0.5 - 0.5 236 | ctr_y = n * 0.5 - 0.5 237 | 238 | d = np.min( ( ctr_x , ctr_y ) ) 239 | 240 | x = np.arange( m ) 241 | y = np.arange( n ) 242 | x , y = np.meshgrid( x , y ) 243 | 244 | i = np.argwhere( np.sqrt( ( x - ctr_x )**2 + ( y - ctr_y )**2 ) > d ) 245 | 246 | if img.ndim == 2: 247 | img[i[:,0],i[:,1]] = 0 248 | else: 249 | img[:,i[:,0],i[:,1]] = 0 250 | 251 | return img 252 | 253 | 254 | 255 | 256 | ############################################################# 257 | ############################################################# 258 | #### #### 259 | #### SUPPORT CONSTRAINT #### 260 | #### #### 261 | ############################################################# 262 | ############################################################# 263 | 264 | def supp_constr( x , mask ): 265 | x[ mask == 0 ] = 0.0 266 | return x 267 | 268 | 269 | 270 | 271 | ############################################################# 272 | ############################################################# 273 | #### #### 274 | #### MASK CONSTRAINT #### 275 | #### #### 276 | ############################################################# 277 | ############################################################# 278 | 279 | def mask_constr( x , mask ): 280 | x[ mask == 1 ] = np.mean( x[ mask == 1 ] ) 281 | return x 282 | -------------------------------------------------------------------------------- /common/myimage/my_image_display.py: -------------------------------------------------------------------------------- 1 | ########################################################### 2 | ########################################################### 3 | #### #### 4 | #### MY IMAGE DISPLAY #### 5 | #### #### 6 | #### Collection of functions to display images #### 7 | #### #### 8 | #### Author: Filippo Arcadu #### 9 | #### arcusfil@gmail.com, 15/05/2013 #### 10 | #### #### 11 | ########################################################### 12 | ########################################################### 13 | 14 | 15 | 16 | #### PYTHON MODULES 17 | from __future__ import print_function,division 18 | import sys 19 | import matplotlib.pyplot as plt 20 | import matplotlib.cm as cm 21 | 22 | 23 | 24 | 25 | ########################################################### 26 | ########################################################### 27 | #### #### 28 | #### CHECK PLOT #### 29 | #### #### 30 | ########################################################### 31 | ########################################################### 32 | 33 | ## Routine to display 1 or more images in gray scale with 34 | ## the origin placed at the left bottom corner and with the 35 | ## possibility to display the pixel value by moving the cursor; 36 | ## in input, you can give a list of image arrays , a list of 37 | ## titles for the subplots and one title for the entire plot 38 | 39 | def plot( img , title=None , colorbar=False , axis=True ): 40 | 41 | ## Initialize figure enviroment 42 | fig = plt.figure() 43 | 44 | ax = fig.add_subplot( 111 ) 45 | 46 | ax.imshow( img , origin="lower" , 47 | cmap = cm.Greys_r , 48 | interpolation='nearest' ) 49 | 50 | def format_coord( x , y): 51 | nrows, ncols = img.shape 52 | col = int(x+0.5) 53 | row = int(y+0.5) 54 | 55 | if col>=0 and col=0 and row=0 and col=0 and row .DMP ####### 11 | ####### 2) TIF ---> .tif ####### 12 | ####### 3) JPEG ---> .jpg ####### 13 | ####### Available conversions: ####### 14 | ####### 1) DMP ---> tif ####### 15 | ####### 2) tif ---> DMP ####### 16 | ####### ####### 17 | ####### Author: Filippo Arcadu, arcusfil@gmail.com, 13/08/2013 ####### 18 | ####### ####### 19 | ################################################################################# 20 | ################################################################################# 21 | ################################################################################# 22 | 23 | 24 | 25 | 26 | #### PYTHON PACKAGES 27 | import numpy as np 28 | import scipy 29 | from scipy import misc as misc 30 | import skimage.io as skio 31 | from PIL import Image 32 | 33 | 34 | 35 | 36 | #### PYTHON PLOTTING MODULES 37 | import matplotlib.pyplot as plt 38 | import matplotlib.cm as cm 39 | 40 | 41 | 42 | 43 | #### EQUIVALENT EXTENSION 44 | ext_dmp = [ 'dmp' , 'DMP' ] 45 | ext_tif = [ 'tif' , 'tiff' , 'TIF' , 'TIFF' ] 46 | ext_jpg = [ 'jpg' , 'jpeg' , 'JPG' , 'JPEG' ] 47 | ext_png = [ 'png' , 'PNG' ] 48 | ext_raw = [ 'raw' , '.RAW' ] 49 | 50 | 51 | 52 | 53 | #################################################### 54 | ############# ############# 55 | ############# PARAMETERS FOR I/O ############# 56 | ############# ############# 57 | #################################################### 58 | ## 59 | ## Class which contains some useful parameters to either 60 | ## read or write images of different formats 61 | 62 | class paramIO: 63 | def __init__ ( self ): 64 | ## Name of the file to read/write 65 | self.filename = None 66 | 67 | ## File extension 68 | self.extension = None 69 | 70 | ## Dimensions ( for raw headerless file ) 71 | self.dims = None 72 | 73 | ## Type ( for raw headerless file ) 74 | self.type = None 75 | 76 | ## Array to write 77 | self.imarray = None 78 | 79 | ## Depth of TIFF images: it can be 8 or 16 bits 80 | self.tifDepth = 8 81 | 82 | ## Compression level for JPEG: it can range from 0 to 100 83 | self.jpegCompr = 0 84 | 85 | def getImageType( self ): 86 | chunks = self.filename.split('.') 87 | self.extension = chunks[len( chunks )-1] 88 | 89 | 90 | 91 | 92 | #################################################### 93 | ############# ############# 94 | ############# CHECK ARGS ############# 95 | ############# ############# 96 | #################################################### 97 | ## 98 | ## Check input arguments for reading operations 99 | 100 | def checkArgsRead( args , obj ): 101 | ## Get name of the file 102 | obj.filename = args[0] 103 | 104 | ## Get file extension 105 | obj.getImageType() 106 | 107 | ## If it a raw headerless file get sizes 108 | if obj.extension in ext_raw: 109 | if len( args ) < 3: 110 | raise ValueError( 'Not specified dimensions and type \ 111 | of RAW file to read !!' ) 112 | obj.dims = np.array( args[1] ).astype( np.int ) 113 | obj.type = args[2] 114 | 115 | 116 | 117 | ## Check input arguments for writing operations 118 | 119 | def checkArgsWrite( args , obj ): 120 | ## Get name of the file 121 | obj.filename = args[0] 122 | 123 | ## Get file extension 124 | obj.getImageType() 125 | 126 | ## Get array to be written 127 | obj.imarray = args[1] 128 | 129 | ## Get other parameters for writing 130 | if len( args ) == 3: 131 | ## get the depth if it is a TIFF image 132 | if obj.extension in ext_tif: 133 | obj.tifDepth = int( args[2] ) 134 | 135 | ## get level of jpeg compression 136 | elif obj.extension in ext_jpeg: 137 | obj.jpegCompr = int( args[2] ) 138 | 139 | elif len( args ) == 4: 140 | obj.dims = args[2] 141 | obj.type = args[3] 142 | 143 | 144 | 145 | 146 | #################################################### 147 | ############# ############# 148 | ############# GENERAL I/O ROUTINES ############# 149 | ############# ############# 150 | #################################################### 151 | ## 152 | ## READ IMAGE 153 | ## This routine automatically recognizes the image format and opens it 154 | 155 | def readImage( *args ): 156 | ## Initialize class I/O 157 | obj = paramIO() 158 | 159 | ## Check input arguments 160 | checkArgsRead( args , obj ) 161 | 162 | ## Select which image format you have to deal with 163 | if obj.extension in ext_dmp: 164 | return readImageDmp( obj ) 165 | 166 | elif obj.extension in ext_tif: 167 | return readImageTif( obj ) 168 | 169 | elif obj.extension in ext_jpg: 170 | return readImageJpeg( obj ) 171 | 172 | elif obj.extension in ext_png: 173 | return readImagePng( obj ) 174 | 175 | elif obj.extension in ext_raw: 176 | return readImageRaw( obj ) 177 | 178 | else: 179 | raise Exception('\nI/O of files ' + obj.extension + ' not supported yet!\n') 180 | 181 | 182 | ## WRITE IMAGE 183 | ## This routine automatically recognizes the image format and writes it 184 | 185 | def writeImage( *args ): 186 | ## Initialize class I/O 187 | obj = paramIO() 188 | 189 | ## Check input arguments 190 | checkArgsWrite( args , obj ) 191 | 192 | ## Select which image format you have to deal with 193 | if obj.extension in ext_dmp: 194 | writeImageDmp( obj ) 195 | 196 | elif obj.extension in ext_tif: 197 | writeImageTif( obj ) 198 | 199 | elif obj.extension in ext_jpg: 200 | writeImageJpeg( obj ) 201 | 202 | elif obj.extension in ext_png: 203 | writeImagePng( obj ) 204 | 205 | elif obj.extension in ext_raw: 206 | writeImageRaw( obj ) 207 | 208 | else: 209 | raise Exception('\nI/O of files ' + obj.extension + ' not supported yet!\n') 210 | 211 | 212 | 213 | 214 | ################################################# 215 | ############# ############# 216 | ############# DMP I/O FUNCTIONS ############# 217 | ############# ############# 218 | ################################################# 219 | ## 220 | ## DUMP IMAGE READER 221 | 222 | def readImageDmp( obj ): 223 | fd = open( obj.filename , 'rb' ) 224 | datatype = 'h' 225 | numberOfHeaderValues = 3 226 | headerData = np.zeros(numberOfHeaderValues) 227 | headerData = np.fromfile(fd, datatype, numberOfHeaderValues) 228 | imageShape = (headerData[1], headerData[0]) 229 | imageData = np.fromfile(fd, np.float32, -1) 230 | imageData = imageData.reshape(imageShape) 231 | 232 | return imageData.astype(np.float32) 233 | 234 | 235 | ## DUMP IMAGE WRITER 236 | 237 | def writeImageDmp( obj ): 238 | fd = open( obj.filename , 'wb') 239 | np_array = obj.imarray 240 | width = np.array(np_array.shape[1]) 241 | height = np.array(np_array.shape[0]) 242 | header = np.array([width, height, 0], np.uint16) 243 | header.tofile(fd) 244 | if np_array.dtype != 'float32': 245 | np_array = np_array.astype(np.float32) 246 | np_array.tofile(fd) 247 | 248 | 249 | 250 | 251 | ################################################# 252 | ############# ############# 253 | ############# TIF I/O FUNCTIONS ############# 254 | ############# ############# 255 | ################################################# 256 | ## 257 | ## TIF IMAGE READER 258 | 259 | def readImageTif( obj ): 260 | image = skio.imread( obj.filename ) 261 | return image 262 | 263 | 264 | ## 8 BIT TIF IMAGE WRITER 265 | 266 | def writeImageTif( obj ): 267 | skio.imsave( obj.filename , obj.imarray ) 268 | 269 | 270 | 271 | 272 | ################################################# 273 | ############# ############# 274 | ############# JPEG I/O FUNCTIONS ############# 275 | ############# ############# 276 | ################################################# 277 | ## 278 | ## JPEG IMAGE READER 279 | 280 | def readImageJpeg( obj ): 281 | fileName = obj.filename 282 | return misc.imread( fileName ) 283 | 284 | 285 | ## JPEG IMAGE WRITER 286 | def writeImageJpeg( obj ): 287 | fileName = obj.filename 288 | comprss = obj.jpegCompr 289 | visual = obj.imarray 290 | visual = ( visual - visual.min() ) / ( visual.max() - visual.min() ) 291 | imageAs2DArray = ( visual * 255 ).astype( np.uint8 ) 292 | image = Image.fromarray( imageAs2DArray ) 293 | q = obj.jpegCompr 294 | image.save( fileName , quality=q ) 295 | 296 | 297 | 298 | 299 | ################################################# 300 | ############# ############# 301 | ############# PNG I/O FUNCTIONS ############# 302 | ############# ############# 303 | ################################################# 304 | ## 305 | ## PNG IMAGE READER 306 | 307 | def readImagePng( obj ): 308 | fileName = obj.filename 309 | return misc.imread( fileName ) 310 | 311 | 312 | ## PNG IMAGE WRITER 313 | def writeImagePng( obj ): 314 | fileName = obj.filename 315 | imageAs2DArray = obj.imarray 316 | misc.imsave( fileName , imageAs2DArray ) 317 | 318 | 319 | 320 | 321 | ################################################# 322 | ############# ############# 323 | ############# RAW I/O FUNCTIONS ############# 324 | ############# ############# 325 | ################################################# 326 | ## 327 | ## RAW IMAGE READER 328 | 329 | def readImageRaw( obj ): 330 | fp = open( obj.filename , 'rb' ) 331 | image = np.fromfile( fp , obj.type ).reshape( obj.dims ) 332 | fp.close() 333 | return image 334 | 335 | 336 | ## RAW IMAGE WRITER 337 | 338 | def writeImageRaw( obj ): 339 | fp = open( obj.filename , 'wb' ) 340 | fp.write( obj.imarray ) 341 | fp.close() 342 | 343 | 344 | 345 | 346 | ########################################### 347 | ############# ############# 348 | ############# CONVERSIONS ############# 349 | ############# ############# 350 | ########################################### 351 | 352 | def convert( file1 , file2 ): 353 | imageData = readImage( file1 ) 354 | 355 | if imageData.ndim == 3 and file2[len(file2)-4:] == '.DMP': 356 | imageData = imageData.astype( np.float32 ) 357 | imageData = 0.2126 * imageData[:,:,0] + 0.7152 * imageData[:,:,1] \ 358 | + 0.0722 * imageData[:,:,2] 359 | 360 | writeImage( file2 , imageData ) 361 | 362 | 363 | 364 | 365 | ################################################## 366 | ############# ############# 367 | ############# PLOTTING FUNCTIONS ############# 368 | ############# ############# 369 | ################################################## 370 | 371 | ## GRAY SCALE PLOT OF DMP IMAGE 372 | 373 | def plotImageDmp( filename ): 374 | imageDmp = readImageDmp( filename ) 375 | plt.imshow( imageDmp , cmap = cm.Greys_r ) 376 | plt.show() 377 | -------------------------------------------------------------------------------- /common/operators/bspline_module/bspline_functions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import misc 3 | from scipy import signal 4 | from scipy import ndimage 5 | 6 | 7 | 8 | 9 | #### CONSTANTS 10 | eps = 1e-8 11 | 12 | 13 | 14 | 15 | ########################################################## 16 | ########################################################## 17 | #### #### 18 | #### CHANGE FROM PIXEL TO BSPLINE BASIS #### 19 | #### #### 20 | ########################################################## 21 | ########################################################## 22 | 23 | def pixel_basis_to_bspline( image , bspline_degree ): 24 | m = bspline_degree 25 | if m == 0: 26 | bspline_image = image 27 | elif m == 2: 28 | bspline_image = signal.qspline2d( image ) 29 | elif m == 3: 30 | bspline_image = signal.cspline2d( image ) 31 | else: 32 | sys.exit('\nERROR: B-spline degree not supported !!') 33 | return bspline_image 34 | 35 | 36 | 37 | 38 | ########################################################## 39 | ########################################################## 40 | #### #### 41 | #### COMPUTE R^{n} OF B_SPLINE GENERAL SLOW FORMULA #### 42 | #### #### 43 | ########################################################## 44 | ########################################################## 45 | 46 | def positive_power( p ): 47 | def positive_power_p( x ): 48 | if np.float32( x ) > 0.0: 49 | return np.power( np.float32( x ) , p ) 50 | else: 51 | return 0.0 52 | return positive_power_p 53 | 54 | 55 | def scalar_product(lamba, f): 56 | def spf(x): 57 | return float(lamba)*f(x) 58 | return spf 59 | 60 | 61 | def finite_difference(f, h): 62 | def finite_difference_function(x): 63 | if h != 0.0: 64 | return ( f( x + 0.5*h ) - f( x - 0.5*h ) ) / np.float32( h ) 65 | else: 66 | sys.exit('\nError in "finite_difference": h = 0 !!') 67 | return finite_difference_function 68 | 69 | 70 | def radon_n_bspline_general( y , theta , m , n ): 71 | exponent = 2*m - n + 1 72 | y_plus = positive_power( exponent ) 73 | 74 | 75 | ## Calculate m+1 fold derivative (analytically) 76 | deriv_positive_power = scalar_product( ( misc.factorial( exponent ) / \ 77 | misc.factorial( exponent - (m +1) ) ) , positive_power( m - n ) ) 78 | 79 | if theta == 0.0 or theta == np.pi/2.0 or theta == np.pi: 80 | y_plus = deriv_positive_power 81 | 82 | 83 | ## Consider special case of theta = 0 , pi 84 | if np.abs( theta - np.pi/2.0 ) > eps: 85 | for i in range( m + 1 ): 86 | y_plus = finite_difference( y_plus, np.cos(theta) ) 87 | 88 | 89 | ## Consider special case of theta = pi/2 90 | if np.abs( theta ) > eps and np.abs( theta - np.pi ) > eps: 91 | for i in range( m + 1 ): 92 | y_plus = finite_difference( y_plus , np.sin(theta) ) 93 | 94 | return y_plus(y)/float( misc.factorial( exponent ) ) 95 | 96 | 97 | 98 | 99 | ########################################################## 100 | ########################################################## 101 | #### #### 102 | #### INIT B-SPLINE LOOK UP TABLE #### 103 | #### #### 104 | ########################################################## 105 | ########################################################## 106 | 107 | ## Formula n. 28 of M. Nilchian's paper: 108 | ## "Fast iterative reconstruction of differential phase contrast 109 | ## X-ray tomograms", M. Nilchian et al., Optics Express, 2013. 110 | ## 111 | ## R^{n}{beta(x)}( y , theta ) = sum_{k1=0}^{m+1} sum_{k2=0}^{m+1} (-1)^{k1+k2} * 112 | ## * comb( m+1 , k1 ) * comb( m+1 , k2 ) * ( y + ( (m+1)/2 - k1 )cos(theta) + 113 | ## ( (m+1)/2 - k2 )sin(theta) )_{+}^{2m-n+1} / [( 2m-n+1 )! * cos(theta)^{m+1} * 114 | ## * sin(theta)^{m+1}] 115 | 116 | def init_lut_bspline( nsamples_y , angles , bspline_degree , rt_degree , proj_support_y ): 117 | m = bspline_degree 118 | n = rt_degree 119 | exponent = 2*m - n + 1 120 | 121 | 122 | ## Define y-range as equally spaced points between -(m+1)/2, ... ,(m+1)/1 . 123 | ## Correct is to adopt a rectangular support with length nsamples_y * sqrt(2) , 124 | ## but we do without (function values in edges are very small) . 125 | ## nsamples_y should be even 126 | yrange = proj_support_y 127 | yarray = np.arange( nsamples_y ).astype( np.float32 ) 128 | yarray -= nsamples_y / 2.0 129 | yarray /= np.float32( nsamples_y ) 130 | yarray *= yrange 131 | yarray_tile = yarray.reshape( 1 , yarray.shape[0] ) 132 | 133 | 134 | ## Define the theta-range as equally spaced points between 0 ... pi 135 | nsamples_theta = len( angles ) 136 | theta_array_tile = angles.reshape( len( angles ) , 1 ) 137 | 138 | 139 | ## Repeat the tile yarray_tile vertically for nsamples_theta times 140 | y_matrix = np.tile( yarray_tile , ( nsamples_theta , 1 ) ) 141 | 142 | 143 | ## Repeat the tile theta_array_tile for nsamples_y times 144 | theta_matrix = np.tile( theta_array_tile , ( 1 , nsamples_y ) ) 145 | 146 | 147 | ## Precalculate sin and cos of all the angles 148 | sin_theta_matrix = np.sin( theta_matrix ) 149 | cos_theta_matrix = np.cos( theta_matrix ) 150 | 151 | 152 | ## Prepare denominator matrix: fact( 2m-n+1 ) * cos(theta)^{m+1} * sin(theta)^{m+1} 153 | ## Take care to reassign to angles 0 , pi/2 , pi values different from 0 154 | power_matrix = np.power( sin_theta_matrix * cos_theta_matrix , m+1 ) 155 | divisor = np.float32( misc.factorial( exponent ) ) * power_matrix 156 | 157 | ind_0 = np.argwhere( np.abs( angles ) < eps ) 158 | ind_90 = np.argwhere( np.abs( angles - np.pi/2.0 ) < eps ) 159 | ind_180 = np.argwhere( np.abs( angles - np.pi ) < eps ) 160 | 161 | if len( ind_0 ) !=0: 162 | divisor[ind_0, :] = 1.0 163 | 164 | if len( ind_90 ) != 0: 165 | divisor[ind_90, :] = 1.0 166 | 167 | if len( ind_180 ) != 0: 168 | divisor[ind_180, :] = 1.0 169 | 170 | 171 | ## Allocate memory for the radon transform coefficients 172 | ## of the B-splines functions 173 | result = np.zeros( ( nsamples_theta , nsamples_y ) ) 174 | 175 | 176 | ## Compute numerator matrix 177 | for k_1 in range( 0, m+1+1 ): 178 | for k_2 in range(0, m+1+1 ): 179 | num = y_matrix + ( m/2.0 + 0.5 - k_1 ) * cos_theta_matrix + \ 180 | ( m/2.0 + 0.5 - k_2 ) * sin_theta_matrix 181 | num[ num < 0.0] = 0.0 182 | num_power = np.power( num , exponent ) 183 | num_power *= np.power( -1.0 , k_1 + k_2 ) * misc.comb( m + 1 , k_1 ) * misc.comb( m + 1 , k_2 ) 184 | result += num_power 185 | 186 | 187 | ## Divide for the divisor which is indipendent from the sums on k_1 and k_2 188 | result /= divisor 189 | 190 | 191 | ## Correcting for the general values 0 , pi/2 , pi with slow general formula 192 | if len( ind_0 ) != 0 or len( ind_90 ) != 0 or len( ind_180 ) != 0: 193 | for y in range( nsamples_y ): 194 | if len( ind_0 ) != 0: 195 | result[ ind_0 , y ] = radon_n_bspline_general( yarray[y] , 0.0 , m , n ) 196 | if len( ind_90 ) != 0: 197 | result[ ind_90 , y ] = radon_n_bspline_general( yarray[y] , np.pi/2.0 , m , n ) 198 | if len( ind_180 ) != 0: 199 | result[ ind_180 , y ] = radon_n_bspline_general( yarray[y] , np.pi , m , n ) 200 | 201 | return result 202 | 203 | 204 | 205 | 206 | ########################################################## 207 | ########################################################## 208 | #### #### 209 | #### CONVERT FROM B-SPLINE TO PIXEL BASIS #### 210 | #### #### 211 | ########################################################## 212 | ########################################################## 213 | 214 | def b_spline( x , degree ): 215 | h = 1.0 216 | f = positive_power( degree ) 217 | for i in range(degree + 1): 218 | f = finite_difference( f , h ) 219 | return f( x ) / float( misc.factorial( degree ) ) 220 | 221 | 222 | def tensor_bspline( x , y , m ): 223 | return b_spline(x, m)*b_spline(y, m) 224 | 225 | 226 | def calc_bspline_grid_points( m ): 227 | bspline_grid_points = np.zeros( ( m + 1 + 1 , m + 1 + 1 ) ) 228 | for i in range(m + 1 + 1): 229 | y = i - (m + 1)/2.0 230 | for j in range(m + 1 + 1): 231 | x = j - (m + 1)/2.0 232 | bspline_grid_points[i,j] = tensor_bspline( x , y , m ) 233 | return bspline_grid_points 234 | 235 | 236 | def convert_from_bspline_to_pixel_basis( image_bspline , bspline_degree ): 237 | convolvent = calc_bspline_grid_points( bspline_degree ) 238 | return ndimage.filters.convolve( image_bspline , convolvent ) 239 | 240 | -------------------------------------------------------------------------------- /common/operators/bspline_module/bspline_functions_backup.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import misc 3 | from scipy import signal 4 | from scipy import ndimage 5 | 6 | 7 | 8 | 9 | #### CONSTANTS 10 | eps = 1e-8 11 | 12 | 13 | 14 | 15 | ########################################################## 16 | ########################################################## 17 | #### #### 18 | #### CHANGE FROM PIXEL TO BSPLINE BASIS #### 19 | #### #### 20 | ########################################################## 21 | ########################################################## 22 | 23 | def pixel_basis_to_bspline( image , bspline_degree ): 24 | m = bspline_degree 25 | if m == 0: 26 | bspline_image = image 27 | elif m == 2: 28 | bspline_image = signal.qspline2d( image ) 29 | elif m == 3: 30 | bspline_image = signal.cspline2d( image ) 31 | else: 32 | sys.exit('\nERROR: B-spline degree not supported !!') 33 | return bspline_image 34 | 35 | 36 | 37 | 38 | ########################################################## 39 | ########################################################## 40 | #### #### 41 | #### COMPUTE R^{n} OF B_SPLINE GENERAL SLOW FORMULA #### 42 | #### #### 43 | ########################################################## 44 | ########################################################## 45 | 46 | def positive_power( p ): 47 | def positive_power_p( x ): 48 | if np.float32( x ) > 0.0: 49 | return np.power( np.float32( x ) , p ) 50 | else: 51 | return 0.0 52 | return positive_power_p 53 | 54 | 55 | def scalar_product(lamba, f): 56 | def spf(x): 57 | return float(lamba)*f(x) 58 | return spf 59 | 60 | 61 | def finite_difference(f, h): 62 | def finite_difference_function(x): 63 | if h != 0.0: 64 | return ( f( x + 0.5*h ) - f( x - 0.5*h ) ) / np.float32( h ) 65 | else: 66 | sys.exit('\nError in "finite_difference": h = 0 !!') 67 | return finite_difference_function 68 | 69 | 70 | def radon_n_bspline_general( y , theta , m , n ): 71 | exponent = 2*m - n + 1 72 | y_plus = positive_power( exponent ) 73 | 74 | 75 | ## Calculate m+1 fold derivative (analytically) 76 | deriv_positive_power = scalar_product( ( misc.factorial( exponent ) / \ 77 | misc.factorial( exponent - (m +1) ) ) , positive_power( m - n ) ) 78 | 79 | if theta == 0.0 or theta == np.pi/2.0 or theta == np.pi: 80 | y_plus = deriv_positive_power 81 | 82 | 83 | ## Consider special case of theta = 0 , pi 84 | if np.abs( theta - np.pi/2.0 ) > eps: 85 | for i in range( m + 1 ): 86 | y_plus = finite_difference( y_plus, np.cos(theta) ) 87 | 88 | 89 | ## Consider special case of theta = pi/2 90 | if np.abs( theta ) > eps and np.abs( theta - np.pi ) > eps: 91 | for i in range( m + 1 ): 92 | y_plus = finite_difference( y_plus , np.sin(theta) ) 93 | 94 | return y_plus(y)/float( misc.factorial( exponent ) ) 95 | 96 | 97 | 98 | 99 | ########################################################## 100 | ########################################################## 101 | #### #### 102 | #### INIT B-SPLINE LOOK UP TABLE #### 103 | #### #### 104 | ########################################################## 105 | ########################################################## 106 | 107 | ## Formula n. 28 of M. Nilchian's paper: 108 | ## "Fast iterative reconstruction of differential phase contrast 109 | ## X-ray tomograms", M. Nilchian et al., Optics Express, 2013. 110 | ## 111 | ## R^{n}{beta(x)}( y , theta ) = sum_{k1=0}^{m+1} sum_{k2=0}^{m+1} (-1)^{k1+k2} * 112 | ## * comb( m+1 , k1 ) * comb( m+1 , k2 ) * ( y + ( (m+1)/2 - k1 )cos(theta) + 113 | ## ( (m+1)/2 - k2 )sin(theta) )_{+}^{2m-n+1} / [( 2m-n+1 )! * cos(theta)^{m+1} * 114 | ## * sin(theta)^{m+1}] 115 | 116 | def init_lut_bspline( nsamples_y , angles , bspline_degree , rt_degree , proj_support_y ): 117 | m = bspline_degree 118 | n = rt_degree 119 | exponent = 2*m - n + 1 120 | 121 | 122 | ## Define y-range as equally spaced points between -(m+1)/2, ... ,(m+1)/1 . 123 | ## Correct is to adopt a rectangular support with length nsamples_y * sqrt(2) , 124 | ## but we do without (function values in edges are very small) . 125 | ## nsamples_y should be even 126 | yrange = proj_support_y 127 | yarray = np.arange( nsamples_y ).astype( np.float32 ) 128 | yarray -= nsamples_y / 2.0 129 | yarray /= np.float32( nsamples_y ) 130 | yarray *= yrange 131 | yarray_tile = yarray.reshape( 1 , yarray.shape[0] ) 132 | 133 | 134 | ## Define the theta-range as equally spaced points between 0 ... pi 135 | nsamples_theta = len( angles ) 136 | theta_array_tile = angles.reshape( len( angles ) , 1 ) 137 | 138 | 139 | ## Repeat the tile yarray_tile vertically for nsamples_theta times 140 | y_matrix = np.tile( yarray_tile , ( nsamples_theta , 1 ) ) 141 | 142 | 143 | ## Repeat the tile theta_array_tile for nsamples_y times 144 | theta_matrix = np.tile( theta_array_tile , ( 1 , nsamples_y ) ) 145 | 146 | 147 | ## Precalculate sin and cos of all the angles 148 | sin_theta_matrix = np.sin( theta_matrix ) 149 | cos_theta_matrix = np.cos( theta_matrix ) 150 | 151 | 152 | ## Prepare denominator matrix: fact( 2m-n+1 ) * cos(theta)^{m+1} * sin(theta)^{m+1} 153 | ## Take care to reassign to angles 0 , pi/2 , pi values different from 0 154 | power_matrix = np.power( sin_theta_matrix * cos_theta_matrix , m+1 ) 155 | divisor = np.float32( misc.factorial( exponent ) ) * power_matrix 156 | 157 | ind_0 = np.argwhere( np.abs( angles ) < eps ) 158 | ind_90 = np.argwhere( np.abs( angles - np.pi/2.0 ) < eps ) 159 | ind_180 = np.argwhere( np.abs( angles - np.pi ) < eps ) 160 | 161 | if len( ind_0 ) !=0: 162 | divisor[ind_0, :] = 1.0 163 | 164 | if len( ind_90 ) != 0: 165 | divisor[ind_90, :] = 1.0 166 | 167 | if len( ind_180 ) != 0: 168 | divisor[ind_180, :] = 1.0 169 | 170 | 171 | ## Allocate memory for the radon transform coefficients 172 | ## of the B-splines functions 173 | result = np.zeros( ( nsamples_theta , nsamples_y ) ) 174 | 175 | 176 | ## Compute numerator matrix 177 | for k_1 in range( 0, m+1+1 ): 178 | for k_2 in range(0, m+1+1 ): 179 | num = y_matrix + ( m/2.0 + 0.5 - k_1 ) * cos_theta_matrix + \ 180 | ( m/2.0 + 0.5 - k_2 ) * sin_theta_matrix 181 | num[ num < 0.0] = 0.0 182 | num_power = np.power( num , exponent ) 183 | num_power *= np.power( -1.0 , k_1 + k_2 ) * misc.comb( m + 1 , k_1 ) * misc.comb( m + 1 , k_2 ) 184 | result += num_power 185 | 186 | 187 | ## Divide for the divisor which is indipendent from the sums on k_1 and k_2 188 | result /= divisor 189 | 190 | 191 | ## Correcting for the general values 0 , pi/2 , pi with slow general formula 192 | if len( ind_0 ) != 0 or len( ind_90 ) != 0 or len( ind_180 ) != 0: 193 | for y in range( nsamples_y ): 194 | if len( ind_0 ) != 0: 195 | result[ ind_0 , y ] = radon_n_bspline_general( yarray[y] , 0.0 , m , n ) 196 | if len( ind_90 ) != 0: 197 | result[ ind_90 , y ] = radon_n_bspline_general( yarray[y] , np.pi/2.0 , m , n ) 198 | if len( ind_180 ) != 0: 199 | result[ ind_180 , y ] = radon_n_bspline_general( yarray[y] , np.pi , m , n ) 200 | 201 | return result 202 | 203 | 204 | 205 | 206 | ########################################################## 207 | ########################################################## 208 | #### #### 209 | #### CONVERT FROM B-SPLINE TO PIXEL BASIS #### 210 | #### #### 211 | ########################################################## 212 | ########################################################## 213 | 214 | def b_spline( x , degree ): 215 | h = 1.0 216 | f = positive_power( degree ) 217 | for i in range(degree + 1): 218 | f = finite_difference( f , h ) 219 | return f( x ) / float( misc.factorial( degree ) ) 220 | 221 | 222 | def tensor_bspline( x , y , m ): 223 | return b_spline(x, m)*b_spline(y, m) 224 | 225 | 226 | def calc_bspline_grid_points( m ): 227 | bspline_grid_points = np.zeros( ( m + 1 + 1 , m + 1 + 1 ) ) 228 | for i in range(m + 1 + 1): 229 | y = i - (m + 1)/2.0 230 | for j in range(m + 1 + 1): 231 | x = j - (m + 1)/2.0 232 | bspline_grid_points[i,j] = tensor_bspline( x , y , m ) 233 | return bspline_grid_points 234 | 235 | 236 | def convert_from_bspline_to_pixel_basis( image_bspline , bspline_degree ): 237 | convolvent = calc_bspline_grid_points( bspline_degree ) 238 | return ndimage.filters.convolve( image_bspline , convolvent ) 239 | 240 | -------------------------------------------------------------------------------- /common/operators/bspline_module/generalized_radon_transform.c: -------------------------------------------------------------------------------- 1 | //This is a matrix form of the Radon Transform. Input lives in B-spline space (sample/image is expanded in Riesz basis), 2 | //output is represented in 'canonical' basis in Radon space (i.e pixel basis, hence the sinogram can be made visible directly) 3 | //compile 4 | //gcc -O3 -fPIC -c generalized_radon_transform.c -o generalized_radon_transform.o 5 | //make shared object 6 | //gcc -shared -Wl -o generalized_radon_transform.so generalized_radon_transform.o 7 | // 8 | // Remember: 9 | // lut_size ---> is supposed to be even 10 | // half_pixel ---> it may be set to 0.5, but for npix even, 0 is better 11 | 12 | 13 | 14 | #include 15 | #include 16 | 17 | int radon( float *result , float *input , int do_adjoint , float *lut , int lut_size , int npix , float *angles , int nang , float support_bspline) 18 | { 19 | const float lut_step = ( lut_size * 0.5 ) / ( support_bspline * 0.5 ); 20 | const int lut_max_index = lut_size - 1; 21 | const int lut_half_size = lut_size/2; 22 | const double half_pixel = 0.0; 23 | const double middle_right_det = 0.5 * npix + half_pixel; 24 | const double middle_left_det = 0.5 * npix - half_pixel; 25 | const int delta_s_plus = (int)( 0.5 * support_bspline + 0.5 ); 26 | 27 | float theta , COS , SIN , kx , ky , proj_shift , y , lut_arg_y; 28 | int theta_index , kx_index , ky_index , s , y_index, lut_arg_index; 29 | 30 | 31 | for( theta_index = 0 ; theta_index < nang ; theta_index++ ) 32 | { 33 | theta = angles[theta_index]; 34 | COS = cos(theta); 35 | SIN = sin(theta); 36 | 37 | for( ky_index = 0 ; ky_index < npix ; ky_index++ ) 38 | { 39 | ky = -ky_index + middle_left_det; 40 | 41 | for ( kx_index = 0 ; kx_index < npix ; kx_index++ ) 42 | { 43 | kx = kx_index - middle_left_det; 44 | proj_shift = COS * kx + SIN * ky; 45 | int image_index = ky_index * npix + kx_index; 46 | 47 | for ( s = -delta_s_plus ; s <= delta_s_plus ; s++ ) 48 | { 49 | y_index = (int)( proj_shift + s + middle_right_det ); 50 | 51 | if (y_index >= npix || y_index < 0) continue; 52 | 53 | y = y_index - middle_left_det; 54 | 55 | lut_arg_y = y - proj_shift; 56 | lut_arg_index = (int)( lut_arg_y * lut_step + half_pixel ) + lut_half_size; 57 | 58 | if (lut_arg_index >= lut_max_index || lut_arg_index < 0) continue; 59 | 60 | int sino_index = theta_index * npix + y_index; 61 | int lut_arg_index_c = theta_index * lut_size + lut_arg_index; 62 | 63 | if (do_adjoint) 64 | result[image_index] += input[sino_index] * lut[lut_arg_index_c]; 65 | else 66 | result[sino_index] += input[image_index] * lut[lut_arg_index_c]; 67 | } 68 | } 69 | } 70 | } 71 | return 0; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /common/operators/bspline_module/pymodule_genradon/compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'genradon.c' ) is True: 9 | os.remove( 'genradon.c' ) 10 | 11 | if os.path.isfile( 'genradon.so' ) is True: 12 | os.remove( 'genradon.so' ) 13 | 14 | os.system('python create_module.py build_ext --inplace') 15 | -------------------------------------------------------------------------------- /common/operators/bspline_module/pymodule_genradon/create_module.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Distutils import build_ext 4 | 5 | import numpy 6 | 7 | setup( 8 | cmdclass = {'build_ext': build_ext}, 9 | ext_modules = [Extension("genradon", 10 | sources=[ "genradon.pyx" , 11 | "gen_forwproj.c" , 12 | "gen_backproj.c" 13 | ], 14 | include_dirs=[numpy.get_include()],libraries=['gcov'], 15 | extra_compile_args=['-w','-O3','-march=native','-ffast-math','-fprofile-generate','-fopenmp'],extra_link_args=['-fprofile-generate'])], 16 | ) 17 | -------------------------------------------------------------------------------- /common/operators/bspline_module/pymodule_genradon/gen_backproj.c: -------------------------------------------------------------------------------- 1 | //This is a matrix form of the Radon Transform. Input lives in B-spline space (sample/image is expanded in Riesz basis), 2 | //output is represented in 'canonical' basis in Radon space (i.e pixel basis, hence the sinogram can be made visible directly) 3 | //compile 4 | //gcc -O3 -fPIC -c generalized_radon_transform.c -o generalized_radon_transform.o 5 | //make shared object 6 | //gcc -shared -Wl -o generalized_radon_transform.so generalized_radon_transform.o 7 | // 8 | // Remember: 9 | // lut_size ---> is supposed to be even 10 | // half_pixel ---> it may be set to 0.5, but for npix even, 0 is better 11 | 12 | 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #define pi 3.141592653589793 19 | 20 | 21 | 22 | void gen_backproj( float* sino , int npix , float *angles , int nang , float *lut , 23 | int lut_size , float support_bspline , float *image ) 24 | { 25 | const float lut_step = ( lut_size * 0.5 ) / ( support_bspline * 0.5 ); 26 | const int lut_max_index = lut_size - 1; 27 | const int lut_half_size = lut_size/2; 28 | const double half_pixel = 0.0; 29 | const double middle_right_det = 0.5 * npix + half_pixel; 30 | const double middle_left_det = 0.5 * npix - half_pixel; 31 | const int delta_s_plus = (int)( 0.5 * support_bspline + 0.5 ); 32 | 33 | float theta , COS , SIN , kx , ky , proj_shift , y , lut_arg_y; 34 | int theta_index , kx_index , ky_index , s , y_index, lut_arg_index; 35 | 36 | 37 | for( theta_index = 0 ; theta_index < nang ; theta_index++ ) 38 | { 39 | theta = angles[theta_index] * pi / 180.0; 40 | COS = cos(theta); 41 | SIN = sin(theta); 42 | 43 | for( ky_index = 0 ; ky_index < npix ; ky_index++ ) 44 | { 45 | ky = -ky_index + middle_left_det; 46 | 47 | for ( kx_index = 0 ; kx_index < npix ; kx_index++ ) 48 | { 49 | kx = kx_index - middle_left_det; 50 | proj_shift = COS * kx + SIN * ky; 51 | int image_index = ky_index * npix + kx_index; 52 | 53 | for ( s = -delta_s_plus ; s <= delta_s_plus ; s++ ) 54 | { 55 | y_index = (int)( proj_shift + s + middle_right_det ); 56 | 57 | if (y_index >= npix || y_index < 0) continue; 58 | 59 | y = y_index - middle_left_det; 60 | 61 | lut_arg_y = y - proj_shift; 62 | lut_arg_index = (int)( lut_arg_y * lut_step + half_pixel ) + lut_half_size; 63 | 64 | if (lut_arg_index >= lut_max_index || lut_arg_index < 0) continue; 65 | 66 | int sino_index = theta_index * npix + y_index; 67 | int lut_arg_index_c = theta_index * lut_size + lut_arg_index; 68 | 69 | image[image_index] += sino[sino_index] * lut[lut_arg_index_c]; 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /common/operators/bspline_module/pymodule_genradon/gen_forwproj.c: -------------------------------------------------------------------------------- 1 | //This is a matrix form of the Radon Transform. Input lives in B-spline space (sample/image is expanded in Riesz basis), 2 | //output is represented in 'canonical' basis in Radon space (i.e pixel basis, hence the sinogram can be made visible directly) 3 | //compile 4 | //gcc -O3 -fPIC -c generalized_radon_transform.c -o generalized_radon_transform.o 5 | //make shared object 6 | //gcc -shared -Wl -o generalized_radon_transform.so generalized_radon_transform.o 7 | // 8 | // Remember: 9 | // lut_size ---> is supposed to be even 10 | // half_pixel ---> it may be set to 0.5, but for npix even, 0 is better 11 | 12 | 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #define pi 3.141592653589793 19 | 20 | 21 | 22 | void gen_forwproj( float* image , int npix , float *angles , int nang , float *lut , 23 | int lut_size , float support_bspline , int num_cores , float *sino ) 24 | { 25 | float lut_step = ( lut_size * 0.5 ) / ( support_bspline * 0.5 ); 26 | int lut_max_index = lut_size - 1; 27 | int lut_half_size = lut_size/2; 28 | double half_pixel = 0.0; 29 | double middle_right_det = 0.5 * npix + half_pixel; 30 | double middle_left_det = 0.5 * npix - half_pixel; 31 | int delta_s_plus = (int)( 0.5 * support_bspline + 0.5 ); 32 | 33 | int image_index , sino_index , lut_arg_index_c; 34 | 35 | float theta , COS , SIN , kx , ky , proj_shift , y , lut_arg_y; 36 | int theta_index , kx_index , ky_index , s , y_index, lut_arg_index; 37 | 38 | int chunk = ( int ) floor( nang / ( num_cores * 1.0 ) ); 39 | 40 | 41 | for( theta_index = 0 ; theta_index < nang ; theta_index++ ) 42 | { 43 | theta = angles[theta_index] * pi / 180.0; 44 | COS = cos(theta); 45 | SIN = sin(theta); 46 | 47 | for( ky_index = 0 ; ky_index < npix ; ky_index++ ) 48 | { 49 | ky = -ky_index + middle_left_det; 50 | 51 | for ( kx_index = 0 ; kx_index < npix ; kx_index++ ) 52 | { 53 | kx = kx_index - middle_left_det; 54 | proj_shift = COS * kx + SIN * ky; 55 | image_index = ky_index * npix + kx_index; 56 | 57 | for ( s = -delta_s_plus ; s <= delta_s_plus ; s++ ) 58 | { 59 | y_index = (int)( proj_shift + s + middle_right_det ); 60 | 61 | if (y_index >= npix || y_index < 0) continue; 62 | 63 | y = y_index - middle_left_det; 64 | 65 | lut_arg_y = y - proj_shift; 66 | lut_arg_index = (int)( lut_arg_y * lut_step + half_pixel ) + lut_half_size; 67 | 68 | if (lut_arg_index >= lut_max_index || lut_arg_index < 0) continue; 69 | 70 | sino_index = theta_index * npix + y_index; 71 | lut_arg_index_c = theta_index * lut_size + lut_arg_index; 72 | 73 | sino[sino_index] += image[image_index] * lut[lut_arg_index_c]; 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /common/operators/bspline_module/pymodule_genradon/gen_forwproj_omp.c: -------------------------------------------------------------------------------- 1 | //This is a matrix form of the Radon Transform. Input lives in B-spline space (sample/image is expanded in Riesz basis), 2 | //output is represented in 'canonical' basis in Radon space (i.e pixel basis, hence the sinogram can be made visible directly) 3 | //compile 4 | //gcc -O3 -fPIC -c generalized_radon_transform.c -o generalized_radon_transform.o 5 | //make shared object 6 | //gcc -shared -Wl -o generalized_radon_transform.so generalized_radon_transform.o 7 | // 8 | // Remember: 9 | // lut_size ---> is supposed to be even 10 | // half_pixel ---> it may be set to 0.5, but for npix even, 0 is better 11 | 12 | 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #define pi 3.141592653589793 19 | 20 | 21 | 22 | void gen_forwproj( float* image , int npix , float *angles , int nang , float *lut , 23 | int lut_size , float support_bspline , int num_cores , float *sino ) 24 | { 25 | float lut_step = ( lut_size * 0.5 ) / ( support_bspline * 0.5 ); 26 | int lut_max_index = lut_size - 1; 27 | int lut_half_size = lut_size/2; 28 | double half_pixel = 0.0; 29 | double middle_right_det = 0.5 * npix + half_pixel; 30 | double middle_left_det = 0.5 * npix - half_pixel; 31 | int delta_s_plus = (int)( 0.5 * support_bspline + 0.5 ); 32 | 33 | int image_index , sino_index , lut_arg_index_c; 34 | 35 | float theta , COS , SIN , kx , ky , proj_shift , y , lut_arg_y; 36 | int theta_index , kx_index , ky_index , s , y_index, lut_arg_index; 37 | 38 | int chunk = ( int ) floor( nang / ( num_cores * 1.0 ) ); 39 | 40 | #pragma omp parallel shared( image , npix , angles , nang , lut , lut_size , \ 41 | support_bspline , sino , chunk , middle_left_det , \ 42 | delta_s_plus , middle_right_det , lut_step , \ 43 | half_pixel , lut_half_size ) \ 44 | private( theta_index , theta , COS , SIN , ky_index , ky , \ 45 | kx_index , kx , proj_shift , image_index , s , \ 46 | y_index , lut_arg_y , lut_arg_index , sino_index , \ 47 | lut_arg_index_c ) 48 | { 49 | #pragma omp for schedule( dynamic , chunk ) 50 | for( theta_index = 0 ; theta_index < nang ; theta_index++ ) 51 | { 52 | theta = angles[theta_index] * pi / 180.0; 53 | COS = cos(theta); 54 | SIN = sin(theta); 55 | 56 | for( ky_index = 0 ; ky_index < npix ; ky_index++ ) 57 | { 58 | ky = -ky_index + middle_left_det; 59 | 60 | for ( kx_index = 0 ; kx_index < npix ; kx_index++ ) 61 | { 62 | kx = kx_index - middle_left_det; 63 | proj_shift = COS * kx + SIN * ky; 64 | image_index = ky_index * npix + kx_index; 65 | 66 | for ( s = -delta_s_plus ; s <= delta_s_plus ; s++ ) 67 | { 68 | y_index = (int)( proj_shift + s + middle_right_det ); 69 | 70 | if (y_index >= npix || y_index < 0) continue; 71 | 72 | y = y_index - middle_left_det; 73 | 74 | lut_arg_y = y - proj_shift; 75 | lut_arg_index = (int)( lut_arg_y * lut_step + half_pixel ) + lut_half_size; 76 | 77 | if (lut_arg_index >= lut_max_index || lut_arg_index < 0) continue; 78 | 79 | sino_index = theta_index * npix + y_index; 80 | lut_arg_index_c = theta_index * lut_size + lut_arg_index; 81 | 82 | sino[sino_index] += image[image_index] * lut[lut_arg_index_c]; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /common/operators/bspline_module/pymodule_genradon/genradon.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | 3 | import numpy as np 4 | import multiprocessing as mproc 5 | cimport numpy as np 6 | 7 | 8 | cdef extern void gen_forwproj( float* image , int npix , float* angles , int nang , float* lut , 9 | int lut_size , float support_bspline , int num_cores , float* sino ) 10 | 11 | cdef extern void gen_backproj( float* sino , int npix , float* angles , int nang , float* lut , 12 | int lut_size , float support_bspline , float* image ) 13 | 14 | 15 | 16 | 17 | @cython.boundscheck( False ) 18 | @cython.wraparound( False ) 19 | def forwproj( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 20 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None , 21 | np.ndarray[ float , ndim=2 , mode="c" ] lut not None , 22 | np.ndarray[ float , ndim=1 , mode="c" ] param not None ): 23 | 24 | cdef int nang, npix , lut_size , num_cores 25 | cdef float support_bspline 26 | 27 | npix = image.shape[0] 28 | nang = len( angles ) 29 | myfloat = image.dtype 30 | lut_size = int( param[0] ) 31 | support_bspline = np.float32( param[1] ) 32 | 33 | num_cores = mproc.cpu_count() 34 | 35 | sino = np.zeros( ( nang , npix ) , dtype=myfloat, order='C' ) 36 | 37 | cdef float [:,::1] csino = sino 38 | 39 | gen_forwproj( &image[0,0] , npix , &angles[0] , nang , &lut[0,0] , 40 | lut_size , support_bspline , num_cores , &csino[0,0] ) 41 | 42 | return sino 43 | 44 | 45 | 46 | 47 | @cython.boundscheck( False ) 48 | @cython.wraparound( False ) 49 | def backproj( np.ndarray[ float , ndim=2 , mode="c" ] sino not None , 50 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None , 51 | np.ndarray[ float , ndim=2 , mode="c" ] lut not None , 52 | np.ndarray[ float , ndim=1 , mode="c" ] param not None ): 53 | 54 | cdef int nang, npix 55 | 56 | nang , npix = sino.shape[0] , sino.shape[1] 57 | myfloat = sino.dtype 58 | lut_size = int( param[0] ) 59 | support_bspline = np.float32( param[1] ) 60 | 61 | image = np.zeros( ( npix , npix ) , dtype=myfloat, order='C' ) 62 | 63 | cdef float [:,::1] cimage = image 64 | 65 | gen_backproj( &sino[0,0] , npix , &angles[0] , nang , &lut[0,0] , 66 | lut_size , support_bspline , &cimage[0,0] ) 67 | 68 | return image 69 | -------------------------------------------------------------------------------- /common/operators/filters_module/filters.py: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | ########################################################## 3 | #### #### 4 | #### SINOGRAM FILTERING FOR BACKPROJECTION #### 5 | #### #### 6 | ########################################################## 7 | ########################################################## 8 | 9 | 10 | 11 | 12 | #### PYTHON MODULES 13 | import sys 14 | import numpy as np 15 | 16 | 17 | 18 | 19 | #### MY FORMAT VARIABLES 20 | myfloat = np.float32 21 | myint = np.int 22 | mycomplex = np.complex64 23 | 24 | 25 | 26 | 27 | ########################################################## 28 | ########################################################## 29 | #### #### 30 | #### CALCULATE FILTERING ARRAY #### 31 | #### #### 32 | ########################################################## 33 | ########################################################## 34 | 35 | def calc_filter( nfreq , ftype='ramp' , dpc=False ): 36 | ## Any filtering for backprojection 37 | if ftype != 'none': 38 | ## Half ramp ftypeer 39 | if dpc is False: 40 | filtarr = 2 * np.arange( nfreq + 1 ) / myfloat( 2 * nfreq ) 41 | w = 2 * np.pi * np.arange( nfreq + 1 ) / myfloat( 2 * nfreq ) 42 | d = 1.0 43 | 44 | ## Half Hilbert ftypeer 45 | else: 46 | filtarr = np.ones( nfreq + 1 , dtype=mycomplex ) * ( 1.0j ) / ( 2 * np.pi ) 47 | 48 | 49 | ## Superimposing noise-dumping ftypeers 50 | if ftype == 'shepp-logan': 51 | filtarr[1:] *= np.sin( w[1:] ) / ( 2.0 * d * 2.0 * d * w[1:] ) 52 | 53 | elif ftype == 'cosine': 54 | filtarr[1:] *= np.cos( w[1:] ) / ( 2.0 * d * w[1:] ) 55 | 56 | elif ftype == 'hamming': 57 | filtarr[1:] *= ( 0.54 + 0.46 * np.cos( w[1:] )/d ) 58 | 59 | elif ftype == 'hanning': 60 | filtarr[1:] *= ( 1.0 + np.cos( w[1:]/d )/2.0 ) 61 | 62 | 63 | ## Compute second half of ftypeer for absorp. reconstr. 64 | if dpc is False: 65 | filtarr = np.concatenate( ( filtarr , filtarr[nfreq-1:0:-1] ) , axis=0 ) 66 | 67 | ## Compute second half of ftypeer for dpc reconstr. 68 | else: 69 | filtarr = np.concatenate( ( filtarr , np.conjugate( filtarr[nfreq-1:0:-1] ) ) , axis=0 ) 70 | 71 | 72 | ## No ftypeering 73 | else: 74 | filtarr = np.ones( 2 * nfreq ) 75 | 76 | return filtarr 77 | 78 | 79 | 80 | 81 | ########################################################## 82 | ########################################################## 83 | #### #### 84 | #### PROJECTION FILTERING #### 85 | #### #### 86 | ########################################################## 87 | ########################################################## 88 | 89 | def filter_proj( sino , ftype='ramp' , dpc=False ): 90 | ## Compute oversamples array length 91 | nang , npix = sino.shape 92 | nfreq = 2 * int( 2**( int( np.ceil( np.log2( npix ) ) ) ) ) 93 | 94 | 95 | ## Compute filtering array 96 | filtarr = calc_filter( nfreq , ftype=ftype , dpc=dpc ) 97 | 98 | 99 | ## Zero-pad projections 100 | sino_filt = np.concatenate( ( sino , np.zeros( ( nang , 2*nfreq - npix ) ) ) , axis=1 ) 101 | 102 | 103 | ## Filtering in Fourier space 104 | for i in range( nang ): 105 | sys.stdout.write( 'Filtering projection number %d\r' % ( i + 1 , ) ) 106 | sys.stdout.flush() 107 | sino_filt[i,:] = np.real( np.fft.ifft( np.fft.fft( sino_filt[i,:] ) * filtarr ) ) 108 | 109 | 110 | ## Replace values in the original array 111 | sino[:,:] = sino_filt[:,:npix] 112 | 113 | return sino 114 | 115 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/gridrec_lut.py: -------------------------------------------------------------------------------- 1 | #### PYTHON MODULES 2 | import numpy as np 3 | 4 | 5 | 6 | #### MY FORMAT VARIABLES & CONSTANTS 7 | myfloat = np.float32 8 | myint = np.int 9 | pi = np.pi 10 | eps = 1e-8 11 | 12 | 13 | 14 | 15 | ########################################################## 16 | ########################################################## 17 | #### #### 18 | #### CLASS GRIDDING #### 19 | #### #### 20 | ########################################################## 21 | ########################################################## 22 | 23 | class gridding(): 24 | 25 | ################################################# 26 | ################################################# 27 | #### #### 28 | #### INITIALIZE CLASS #### 29 | #### #### 30 | ################################################# 31 | ################################################# 32 | 33 | def __init__( self , kernel , ns_grid , ns_ker , 34 | width_ker , oversampl , interp , 35 | errs ): 36 | self.kernel = kernel 37 | self.ns_grid = ns_grid 38 | self.ns_ker = ns_ker 39 | self.width_ker = width_ker 40 | self.oversampl = oversampl 41 | self.interp = interp 42 | self.errs = errs 43 | 44 | 45 | 46 | 47 | ################################################# 48 | ################################################# 49 | #### #### 50 | #### CREATE INTERPOLATION LUT AND DEAPODIZER #### 51 | #### #### 52 | ################################################# 53 | ################################################# 54 | 55 | def create_lut_deapod( self ): 56 | if self.kernel == 'prolate' or self.kernel == 'pswf': 57 | lut , deapod = self.prolate() 58 | 59 | elif self.kernel == 'kaiser-bessel' or self.kernel == 'kb': 60 | lut , deapod = self.kaiser_bessel() 61 | 62 | nh = int( self.ns_grid * 0.5 ) 63 | for i in range( 1 , nh + 1 ): 64 | if i % 2 == 0: 65 | sign = 1.0 66 | else: 67 | sign = -1.0 68 | 69 | if i != nh: 70 | deapod[ nh + i ] *= sign 71 | deapod[ nh - i ] *= sign 72 | elif i == nh: 73 | deapod[ 0 ] *= sign 74 | deapod[nh] *= sign 75 | 76 | deapod = np.outer( deapod , deapod ) 77 | 78 | 79 | return lut , deapod 80 | 81 | 82 | 83 | ################################################# 84 | ################################################# 85 | #### #### 86 | #### KAISER-BESSEL INTERPOLATION KERNEL #### 87 | #### #### 88 | ################################################# 89 | ################################################# 90 | 91 | def kaiser_bessel( self ): 92 | ## Compute the optimal beta 93 | W = self.width_ker 94 | alpha = self.oversampl 95 | err = self.errs 96 | interp = self.interp 97 | ns_grid = self.ns_grid 98 | 99 | beta = pi * np.sqrt( ( ( W / alpha ) * ( alpha - 0.5 ) )**2 - 0.8 ) 100 | 101 | 102 | ## Compute the optimal sampling density for the kernel 103 | if interp == 'nn': 104 | ns_ker = int( 0.91 / ( err * alpha ) ) 105 | elif interp == 'lin': 106 | ns_ker = int( np.sqrt( 0.37 / err ) * 1.0/ alpha ) 107 | 108 | 109 | ## Compute kernel LUT 110 | k_max = W / myfloat( 2 * ns_grid ) 111 | k = np.linspace( 0 , k_max , ns_ker ) 112 | lut = ns_grid / myfloat( W ) * np.i0( beta * np.sqrt( 1 - ( 2 * ns_grid * k / W )**2 ) ) 113 | lut /= lut[0] 114 | lut = np.pad( lut , ( 0 , 4 ) , 'constant' , constant_values = 0 ) 115 | 116 | 117 | ## Compute deapodization matrix 118 | nh = int( ns_grid * 0.5 ) 119 | x = np.linspace( -nh , nh , ns_grid ) 120 | f = ( np.pi * W * x / ns_grid )**2 - beta**2 121 | f = np.sqrt( f.astype(complex) ) 122 | deapod = np.sin( f )/ f 123 | deapod = np.abs( deapod ) 124 | 125 | deapod[:] /= deapod[nh] 126 | deapod += eps 127 | 128 | lmbda = self.normalize_kaiser_bessel() 129 | 130 | norm = myfloat( np.sqrt( 1.0 / ( W * lmbda ) ) ) 131 | deapod[:] = norm / deapod 132 | 133 | 134 | ## Assign values to the class 135 | self.ns_ker = ns_ker 136 | 137 | return lut , deapod 138 | 139 | 140 | 141 | ################################################# 142 | ################################################# 143 | #### #### 144 | #### CALCULATE NORMALIZATION FOR KB #### 145 | #### #### 146 | ################################################# 147 | ################################################# 148 | 149 | def normalize_kaiser_bessel( self ): 150 | alpha = self.oversampl 151 | 152 | lmbda_lut = np.array( [ 153 | [ 2.0 , 0.6735177854455913 ] , \ 154 | [ 1.9 , 0.6859972251713252 ] , \ 155 | [ 1.8 , 0.7016852052327338 ] , \ 156 | [ 1.7 , 0.7186085894388523 ] , \ 157 | [ 1.6 , 0.7391321589920385 ] , \ 158 | [ 1.5 , 0.7631378768943125 ] , \ 159 | [ 1.4 , 0.7915122594523216 ] , \ 160 | [ 1.3 , 0.8296167052898601 ] , \ 161 | [ 1.2 , 0.8762210440212161 ] , \ 162 | [ 1.1 , 0.9373402868152573 ] , \ 163 | ], dtype = myfloat ) 164 | 165 | lmbda_lut[:,:] = lmbda_lut[::-1,:] 166 | 167 | ind_lmbda = np.argwhere( np.abs( lmbda_lut - alpha ) == \ 168 | np.min( np.abs( lmbda_lut - alpha ) ) ) 169 | 170 | if alpha != lmbda_lut[ind_lmbda[0,0],0]: 171 | if alpha > lmbda_lut[ind_lmbda[0,0],0]: 172 | d = alpha - lmbda_lut[ind_lmbda[0,0],0] 173 | norm_factor = ( 1 - d ) * lmbda_lut[ ind_lmbda[0,0] , 1 ] + \ 174 | d * lmbda_lut[ ind_lmbda[0,0] , 1 ] 175 | else: 176 | d = alpha - lmbda_lut[ind_lmbda[0,0]-1,0] 177 | norm_factor = ( 1 - d ) * lmbda_lut[ ind_lmbda[0,0] - 1 , 1 ] + \ 178 | d * lmbda_lut[ ind_lmbda[0,0] , 1 ] 179 | else: 180 | norm_factor = lmbda_lut[ind_lmbda[0,0],1] 181 | 182 | return norm_factor 183 | 184 | 185 | 186 | ################################################# 187 | ################################################# 188 | #### #### 189 | #### PSWF INTERPOLATION KERNEL #### 190 | #### #### 191 | ################################################# 192 | ################################################# 193 | 194 | def prolate( self ): 195 | ## Get parameters 196 | W = self.width_ker 197 | alpha = self.oversampl 198 | err = self.errs 199 | interp = self.interp 200 | ns_grid = self.ns_grid 201 | ns_ker = self.ns_ker 202 | 203 | 204 | ## Compute the optimal kernel density 205 | if ns_ker != 2048: 206 | if interp == 'nn': 207 | ns_ker = int( 0.91 / ( err * alpha ) ) 208 | elif interp == 'lin': 209 | ns_ker = int( ( 0.37 / err ) / alpha ) 210 | 211 | 212 | ## Compute kernel LUT 213 | coeffs = np.array([ 0.5767616E+02 , 0.0 ,-0.8931343E+02 , 0.0 , 0.4167596E+02 , 0.0 , \ 214 | -0.1053599E+02 , 0.0 , 0.1662374E+01 , 0.0 ,-0.1780527E-00 , 0.0 , \ 215 | 0.1372983E-01 , 0.0 ,-0.7963169E-03 , 0.0 , 0.3593372E-04 , 0.0 , \ 216 | -0.1295941E-05 , 0.0 , 0.3817796E-07 ]) 217 | 218 | x = np.arange( 0 , ns_ker ) / myfloat( ns_ker ) 219 | 220 | lut = np.polynomial.legendre.legval( x , coeffs )/ \ 221 | np.polynomial.legendre.legval( 0.0 , coeffs ) 222 | 223 | lut = np.pad( lut , ( 0 , 5 ) , 'constant' , constant_values = 0 ) 224 | 225 | 226 | ## Compute deapodization matrix 227 | nh = int( ns_grid * 0.5 ) 228 | lmbda = 0.99998546 229 | norm = myfloat( np.sqrt( 1.0 / ( W * lmbda ) ) ) 230 | scale_ratio = myfloat( ns_ker )/myfloat( nh + 0.5 ) 231 | deapod = np.zeros( ns_grid , dtype=myfloat ) 232 | 233 | deapod[nh] = norm / lut[ nn( 0.0 ) ] 234 | for i in range( 1 , nh + 1 ): 235 | if i != nh: 236 | deapod[ nh + i] = norm / ( lut[ nn( i * scale_ratio ) ] + eps ) 237 | deapod[ nh - i ] = norm / ( lut[ nn( i * scale_ratio ) ] + eps ) 238 | elif i == nh: 239 | deapod[ 0 ] = norm / ( lut[ nn( i * scale_ratio ) ] + eps ) 240 | 241 | return lut , deapod 242 | 243 | 244 | 245 | 246 | ########################################################## 247 | ########################################################## 248 | #### #### 249 | #### NEAREST NEIGHBOUR INTERPOLATION #### 250 | #### #### 251 | ########################################################## 252 | ########################################################## 253 | 254 | def nn( x ): 255 | return np.round( x ).astype( myint ) 256 | 257 | 258 | 259 | 260 | ########################################################## 261 | ########################################################## 262 | #### #### 263 | #### CREATE LUT AND DEAPODIZER FOR REGRIDDING #### 264 | #### #### 265 | ########################################################## 266 | ########################################################## 267 | 268 | def configure_regridding( npix , kernel , oversampl , interp , W , errs ): 269 | ## Kernel size 270 | ## this quantity is fixed, since the regridding 271 | ## does not work with smaller and bigger sizes 272 | W *= 2 / np.pi 273 | 274 | 275 | 276 | ## Get size of the Fourier Cartesian grid 277 | nfreq = int( 2**( np.ceil( np.log2( npix ) ) ) * oversampl ) 278 | while nfreq % 4 != 0: 279 | nfreq += 1 280 | 281 | 282 | 283 | ## Fixed configuration for gridrec 284 | if kernel == 'prolate' and oversampl == 2.0 and interp == 'nn': 285 | ltbl = 2048 286 | else: 287 | ltbl = None 288 | 289 | 290 | 291 | ## Create regridding object 292 | gridd = gridding( kernel , nfreq , ltbl , W , oversampl , 293 | interp , errs ) 294 | 295 | 296 | 297 | 298 | ## Create LUT and deapodizer for regridding 299 | lut , deapod = gridd.create_lut_deapod() 300 | 301 | 302 | return W , lut , deapod 303 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/COMPILE.txt: -------------------------------------------------------------------------------- 1 | python create_gridrec_module.py build_ext --inplace 2 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'gridrec_v4.c' ) is True: 9 | os.remove( 'gridrec_v4.c' ) 10 | 11 | if os.path.isfile( 'gridrec_v4.so' ) is True: 12 | os.remove( 'gridrec_v4.so' ) 13 | 14 | os.system('python create_module.py build_ext --inplace') 15 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/create_module.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Distutils import build_ext 4 | 5 | import numpy 6 | 7 | setup( 8 | cmdclass = {'build_ext': build_ext}, 9 | ext_modules = [Extension("gridrec_v4", 10 | sources=[ "gridrec_v4.pyx" , 11 | "gridrec_v4_backproj.c" , 12 | "gridrec_v4_forwproj.c" , 13 | "filters.c" , 14 | "fft.c" 15 | ], 16 | include_dirs=[numpy.get_include()],libraries=['fftw3f','gcov'],extra_compile_args=['-w','-O3','-march=native','-ffast-math','-fprofile-generate'],extra_link_args=['-fprofile-generate'])], 17 | ) 18 | 19 | 20 | ''' 21 | import gridrec_v4 22 | gridrec_v4.createFFTWWisdomFile(2016, "profile.wis") 23 | 24 | import os 25 | import sys 26 | os.system(sys.executable + " profile.py") 27 | 28 | os.remove('gridrec_v4.so') 29 | 30 | setup( 31 | cmdclass = {'build_ext': build_ext}, 32 | ext_modules = [Extension("gridrec_v4", 33 | sources=[ "gridrec_v4.pyx" , 34 | "gridrec_v4_backproj.c" , 35 | "gridrec_v4_forwproj.c" , 36 | "filters.c" , 37 | "fft.c" 38 | ], 39 | include_dirs=[numpy.get_include()],libraries=['fftw3f'],extra_compile_args=['-O3','-march=native','-ffast-math','-fprofile-use'],extra_link_args=['-fprofile-use'])], 40 | ) 41 | ''' 42 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/fft.c: -------------------------------------------------------------------------------- 1 | #define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr 2 | #define PI 3.141592653589793 3 | 4 | 5 | /*******************************************************************/ 6 | /*******************************************************************/ 7 | /* 8 | * FFT & IFFT 1D 9 | */ 10 | void four1(float data[], unsigned long nn, int isign) 11 | { 12 | unsigned long n,mmax,m,j,istep,i; 13 | float wtemp,wr,wpr,wpi,wi,theta; 14 | float tempr,tempi; 15 | 16 | n=nn << 1; 17 | 18 | j=1; 19 | for (i=1;i i) { 21 | SWAP(data[j],data[i]); 22 | SWAP(data[j+1],data[i+1]); 23 | } 24 | m=n >> 1; 25 | while (m >= 2 && j > m) { 26 | j -= m; 27 | m >>= 1; 28 | } 29 | j += m; 30 | } 31 | mmax=2; 32 | while (n > mmax) { 33 | istep=mmax << 1; 34 | theta=isign*(6.28318530717959/mmax); 35 | wtemp=sin(0.5*theta); 36 | wpr = -2.0*wtemp*wtemp; 37 | wpi=sin(theta); 38 | wr=1.0; 39 | wi=0.0; 40 | for (m=1;m for the FFT 66 | // sqrt(number_elements) ---> for the IFFT 67 | // But, since the implementation in C of the IFFT, 68 | // is the matrhematical IFFT multiplied for the 69 | // number_elements the norm_factor is again 70 | // 1/sqrt(number_elements) as it is for the FFT 71 | // Added by F. Arcadu on the 13/11/2013 72 | float norm_factor = 1.0/sqrt(nn); 73 | 74 | 75 | four1(data-1,nn,isign); 76 | 77 | // Normalization 78 | for(i=0;i<2*nn;i++) 79 | data[i] *= norm_factor; 80 | } 81 | 82 | 83 | 84 | /*******************************************************************/ 85 | /*******************************************************************/ 86 | /* 87 | * FFT & IFFT nD 88 | */ 89 | void fourn( float data[] , unsigned long nn[] , int ndim , int isign) 90 | { 91 | int idim; 92 | unsigned long i1,i2,i3,i2rev,i3rev,ip1,ip2,ip3,ifp1,ifp2; 93 | unsigned long ibit,k1,k2,n,nprev,nrem,ntot; 94 | float tempi,tempr; 95 | float theta,wi,wpi,wpr,wr,wtemp; 96 | 97 | for (ntot=1,idim=1;idim<=ndim;idim++) 98 | ntot *= nn[idim]; 99 | 100 | nprev=1; 101 | for (idim=ndim;idim>=1;idim--) { 102 | n=nn[idim]; 103 | nrem=ntot/(n*nprev); 104 | ip1=nprev << 1; 105 | ip2=ip1*n; 106 | ip3=ip2*nrem; 107 | i2rev=1; 108 | for (i2=1;i2<=ip2;i2+=ip1) { 109 | if (i2 < i2rev) { 110 | for (i1=i2;i1<=i2+ip1-2;i1+=2) { 111 | for (i3=i1;i3<=ip3;i3+=ip2) { 112 | i3rev=i2rev+i3-i2; 113 | SWAP(data[i3],data[i3rev]); 114 | SWAP(data[i3+1],data[i3rev+1]); 115 | } 116 | } 117 | } 118 | ibit=ip2 >> 1; 119 | while (ibit >= ip1 && i2rev > ibit) { 120 | i2rev -= ibit; 121 | ibit >>= 1; 122 | } 123 | i2rev += ibit; 124 | } 125 | ifp1=ip1; 126 | while (ifp1 < ip2) { 127 | ifp2=ifp1 << 1; 128 | theta=isign*6.28318530717959/(ifp2/ip1); 129 | wtemp=sin(0.5*theta); 130 | wpr = -2.0*wtemp*wtemp; 131 | wpi=sin(theta); 132 | wr=1.0; 133 | wi=0.0; 134 | for (i3=1;i3<=ifp1;i3+=ip1) { 135 | for (i1=i3;i1<=i3+ip1-2;i1+=2) { 136 | for (i2=i1;i2<=ip3;i2+=ifp2) { 137 | k1=i2; 138 | k2=k1+ifp1; 139 | tempr=(float)wr*data[k2]-(float)wi*data[k2+1]; 140 | tempi=(float)wr*data[k2+1]+(float)wi*data[k2]; 141 | data[k2]=data[k1]-tempr; 142 | data[k2+1]=data[k1+1]-tempi; 143 | data[k1] += tempr; 144 | data[k1+1] += tempi; 145 | } 146 | } 147 | wr=(wtemp=wr)*wpr-wi*wpi+wr; 148 | wi=wi*wpr+wtemp*wpi+wi; 149 | } 150 | ifp1=ifp2; 151 | } 152 | nprev *= n; 153 | } 154 | } 155 | 156 | 157 | 158 | void myFour2( float *data , unsigned long nc , int isign ) { 159 | 160 | int ndim = 2; 161 | unsigned long i,j,ntot,idim,ncD; 162 | unsigned long nn[2]; 163 | float norm_factor; 164 | 165 | ntot = 2*nc*nc; 166 | 167 | nn[0] = nn[1] = nc; 168 | ncD = 2 * nc; 169 | 170 | // Normalization of FFT and IFFT in such way that 171 | // one operator is the adjoint of the other one. 172 | // 1/number_elements ---> for the FFT N-dim 173 | // number_elements ---> for the IFFT N-dim 174 | // But, since the implementation in C of the IFFT N-dim, 175 | // is the matrhematical IFFT N-dim multiplied for the 176 | // number_elements the norm_factor is again 177 | // 1/number_elements as it is for the FFT N-dim 178 | // Added by F. Arcadu on the 13/11/2013 179 | norm_factor = 1.0/(float)nc; 180 | 181 | // Apply FFT/IFFT 2D 182 | fourn( data-1 , nn-1 , ndim , isign ); 183 | 184 | // Normalization 185 | for( i=0 ; i 7 | 8 | #define PI 3.141592653589793 9 | #define myAbs(X) ((X)<0 ? -(X) : (X)) 10 | 11 | 12 | 13 | /* 14 | * RAMP FILTER 15 | */ 16 | 17 | void ramp( int size , fftwf_complex *ramp_array ) { 18 | 19 | int i, c; 20 | int sizeH = (int)( size * 0.5 ); 21 | 22 | for ( i=0 ; i fm/2. && myAbs( x ) <= fm ) 130 | return (float)( 2 * (1-ffm) * (1-ffm) * (1-ffm) ); 131 | 132 | else 133 | return 0.0; 134 | } 135 | 136 | 137 | 138 | 139 | /* 140 | * FILTER FUNCTION 141 | */ 142 | 143 | void calc_filter( float *filter, unsigned long nang, unsigned long N , float center , int type_filter , 144 | int radon_degree ) 145 | { 146 | long i; long j; long k; 147 | long N2 = (long)(N * 0.5); 148 | float x; 149 | float filter_weight = 1.0; 150 | float rtmp1 = (float)( 2*PI*center/N ); 151 | float rtmp2; 152 | float norm = (float)( PI/N/nang ); 153 | fftwf_complex *ramp_array; 154 | float fm = 0.5; 155 | float tmp1; 156 | 157 | 158 | 159 | /* 160 | * Create ramp filter if some filtering is selected 161 | */ 162 | 163 | if( type_filter && radon_degree==0 ){ 164 | ramp_array = (fftwf_complex *)fftwf_malloc( N * sizeof(fftwf_complex) ); 165 | 166 | for( i=0 ; i0 ){ 251 | tmp1 = filter[j]; 252 | filter[j] = filter[j+1] * ( -1 ) / ( 2.f * PI ); 253 | filter[j+1] = tmp1 / ( 2.f * PI ); 254 | } 255 | else{ 256 | filter[j] = 0.0; 257 | filter[j+1] = 0.0; 258 | } 259 | } 260 | else{ 261 | if ( j>0 ){ 262 | tmp1 = filter[j]; 263 | filter[j] = filter[j+1] * ( - 2 * PI * k ) / ( 1.0 * N ); 264 | filter[j+1] = tmp1 * ( 2 * PI * k ) / ( 1.0 * N ); 265 | } 266 | else{ 267 | filter[j] = 0.0; 268 | filter[j+1] = 0.0; 269 | } 270 | } 271 | } 272 | } 273 | 274 | 275 | if ( type_filter && radon_degree == 0 ) 276 | fftwf_free( ramp_array ); 277 | } 278 | 279 | 280 | 281 | 282 | /* 283 | * KERNEL INTERPOLATION METHODS 284 | */ 285 | 286 | float convolv_nn( float x , float *lut ){ 287 | return lut[ (int) round( x ) ]; 288 | } 289 | 290 | 291 | float convolv_lin( float x , float *lut ){ 292 | float x_inf, d; 293 | x_inf = floor( x ); 294 | d = x - x_inf; 295 | return ( 1 - d ) * lut[ (int)x_inf ] + d * lut[ (int)x_inf + 1 ]; 296 | } 297 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/filters.h: -------------------------------------------------------------------------------- 1 | void calc_filter( float *filter, unsigned long nang, unsigned long N , float center , int type_filter , int radon_degree ); 2 | float convolv_nn( float x , float *lut ); 3 | float convolv_lin( float x , float *lut ); 4 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/gridrec_v4.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | 3 | import numpy as np 4 | cimport numpy as np 5 | 6 | 7 | cdef extern void gridrec_v4_backproj( float *S , int npix , int nang , float *angles , float *param , 8 | float *lut , float *deapod , float *filt , float *I , char *fftwfn) 9 | 10 | cdef extern void gridrec_v4_forwproj( float *S , int npix , int nang , float *angles , float *param , 11 | float *lut , float *deapod , float *I , char *fftwfn) 12 | 13 | cdef extern void create_fftw_wisdom_file(char *fn, int npix) 14 | 15 | 16 | @cython.boundscheck( False ) 17 | @cython.wraparound( False ) 18 | def createFFTWWisdomFile( npix, \ 19 | fftw_wisdom_file_name 20 | ): 21 | cdef char* cfn = fftw_wisdom_file_name 22 | create_fftw_wisdom_file(cfn,npix); 23 | 24 | 25 | 26 | @cython.boundscheck( False ) 27 | @cython.wraparound( False ) 28 | def backproj( np.ndarray[ float , ndim=2 , mode="c" ] sinogram not None , 29 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None , 30 | np.ndarray[ float , ndim=1 , mode="c" ] param not None , 31 | np.ndarray[ float , ndim=1 , mode="c" ] lut not None , 32 | np.ndarray[ float , ndim=2 , mode="c" ] deapod not None , 33 | np.ndarray[ float , ndim=1 , mode="c" ] filt = None ): 34 | 35 | cdef int nang, npix 36 | cdef char* cfn 37 | 38 | fftw_wisdom_file_name = None 39 | if fftw_wisdom_file_name==None: 40 | cfn = "/dev/null" 41 | else: 42 | cfn = "~/tomcat/Programs/pymodule_gridrec_v4/profile.wis" 43 | 44 | nang , npix = sinogram.shape[0], sinogram.shape[1] 45 | myFloat = sinogram.dtype 46 | 47 | image = np.zeros( ( npix , npix ) , dtype=myFloat, order='C' ) 48 | 49 | cdef float [:,::1] cimage = image 50 | 51 | gridrec_v4_backproj( &sinogram[0,0] , npix , nang , &angles[0] , ¶m[0] , 52 | &lut[0] , &deapod[0,0] , &filt[0] , &cimage[0,0], cfn ) 53 | 54 | return image 55 | 56 | 57 | 58 | @cython.boundscheck( False ) 59 | @cython.wraparound( False ) 60 | def forwproj( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 61 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None , 62 | np.ndarray[ float , ndim=1 , mode="c" ] param not None , 63 | np.ndarray[ float , ndim=1 , mode="c" ] lut not None , 64 | np.ndarray[ float , ndim=2 , mode="c" ] deapod not None , 65 | fftw_wisdom_file_name=None 66 | ): 67 | 68 | cdef int nang, npix 69 | cdef char* cfn 70 | if fftw_wisdom_file_name==None: 71 | cfn = "/dev/null" 72 | else: 73 | cfn = fftw_wisdom_file_name 74 | 75 | npix = image.shape[0] 76 | nang = len( angles ) 77 | myFloat = image.dtype 78 | 79 | sino = np.zeros( ( nang , npix ) , dtype=myFloat, order='C' ) 80 | 81 | cdef float [:,::1] csino = sino 82 | 83 | gridrec_v4_forwproj( &csino[0,0] , npix , nang , &angles[0] , ¶m[0] , 84 | &lut[0] , &deapod[0,0] , &image[0,0], cfn ) 85 | 86 | return sino 87 | 88 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/profile.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import gridrec 3 | 4 | sino = np.zeros((901,2016),dtype=np.float32) 5 | 6 | reco = gridrec.backproj(sino,np.linspace(0,180,901).astype(np.float32),np.array([1008,1],dtype=np.float32),np.zeros(1,dtype=np.float32),"profile.wis") 7 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/profile.wis: -------------------------------------------------------------------------------- 1 | (fftw-3.3.3 fftwf_wisdom #xca4daf64 #xc8f59ea6 #x586875c9 #x14018994 2 | (fftwf_dft_indirect_register 0 #x10bdd #x10bdd #x0 #x2b7b4493 #xbe4ade0f #x648f1045 #x0e8e5d5a) 3 | (fftwf_codelet_t1_16 0 #x10bdd #x10bdd #x0 #xc002797e #x7d9138a6 #x1534d100 #x16e78a94) 4 | (fftwf_codelet_t1_16 0 #x10bdd #x10bdd #x0 #x5670aef7 #xe7460533 #xf89368f4 #x49586eaf) 5 | (fftwf_codelet_n1_64 0 #x10bdd #x10bdd #x0 #x0071f536 #xc5e578df #x7bc9b630 #xb4b03a82) 6 | (fftwf_dft_r2hc_register 0 #x10bdd #x10bdd #x0 #xc4aeb9fc #x3e1b38f5 #xea12e68d #xbba6a003) 7 | (fftwf_rdft_rank0_register 0 #x11bdd #x11bdd #x0 #xf784ec12 #x8793c7a6 #xbbe7053f #x751ff6f0) 8 | (fftwf_dft_buffered_register 0 #x11bdd #x11bdd #x0 #x8a7db16b #x4ed5e492 #x0da06290 #x0941fc20) 9 | (fftwf_dft_vrank_geq1_register 0 #x11bdd #x11bdd #x0 #xe7971e5e #xa5b5c8e9 #xcf22f4f5 #xc0251899) 10 | (fftwf_dft_vrank_geq1_register 0 #x10bdd #x10bdd #x0 #x1f77ca29 #x452f0114 #xaecd4818 #xe80ccb2c) 11 | (fftwf_dft_buffered_register 0 #x11bdd #x11bdd #x0 #xd676d958 #x75f15a4b #xd65df885 #xdce3b85b) 12 | (fftwf_dft_r2hc_register 0 #x11bdd #x11bdd #x0 #x106c9f18 #xd28c8f52 #xe6847f2e #x6a8a6483) 13 | (fftwf_dft_buffered_register 1 #x11bdd #x11bdd #x0 #x9a30874c #x50e74c2f #x64d89904 #xdfc12cd2) 14 | (fftwf_codelet_n1_64 0 #x10bdd #x10bdd #x0 #xa8e4219e #x27982aff #xb2b7b665 #x577089c6) 15 | (fftwf_dft_nop_register 0 #x11bdd #x11bdd #x0 #x3c1f9406 #x733b7c78 #x5d1bdc5f #xa3318868) 16 | (fftwf_rdft_rank0_register 4 #x11bdd #x11bdd #x0 #x10e7d2df #x15ca391c #x0d2b0f65 #xd28320c8) 17 | (fftwf_codelet_t1_8 0 #x10fdd #x10fdd #x0 #xd676d958 #x75f15a4b #xd65df885 #xdce3b85b) 18 | (fftwf_dft_r2hc_register 0 #x11bdd #x11bdd #x0 #x65b7bf41 #x50a59e9d #x1ee79bd1 #xcf34e55c) 19 | (fftwf_rdft_rank0_register 2 #x10bdd #x10bdd #x0 #x188b1aa3 #x542fdbe2 #x2e1d3086 #xa4c33a34) 20 | (fftwf_codelet_t1_4 0 #x10bdd #x10bdd #x0 #xe66c09b6 #xb29f4cfd #x053aeada #x08a8cff4) 21 | (fftwf_dft_r2hc_register 0 #x11bdd #x11bdd #x0 #x882716e5 #xec99cbbc #x027a7c04 #x47f5cfeb) 22 | (fftwf_dft_vrank_geq1_register 0 #x10fdd #x10fdd #x0 #x457d51e5 #x3de0142b #x5de19feb #xc0754192) 23 | (fftwf_dft_rank_geq2_register 0 #x11bdd #x11bdd #x0 #x26162fda #xbc27eb6a #xd314de0d #xb41e8112) 24 | (fftwf_codelet_t1_4 0 #x10bdd #x10bdd #x0 #x5f0f170a #x3b05f3de #x12cbed53 #xb4befd75) 25 | (fftwf_dft_nop_register 0 #x11bdd #x11bdd #x0 #x5dcd19fd #xde8af8ac #x23c79ac1 #x45474918) 26 | (fftwf_dft_vrank_geq1_register 0 #x10fdd #x10fdd #x0 #x5c18db91 #x24b9221a #xbe92c18a #xc63f02ef) 27 | (fftwf_dft_vrank_geq1_register 0 #x10bdd #x10bdd #x0 #x07b55aef #x8b36d4a5 #x3eef482e #xc7604e5a) 28 | (fftwf_codelet_n1_64 0 #x10fdd #x10fdd #x0 #x875bec7f #xdf7c30d3 #xebe45c1f #x84e73cd7) 29 | (fftwf_codelet_q1_8 0 #x10fdd #x10fdd #x0 #x403a3ec4 #xcf7c7747 #x6c598325 #xd55d1f19) 30 | (fftwf_dft_nop_register 0 #x11bdd #x11bdd #x0 #xa9e941ff #xb0a10202 #x399cb37c #xb1510d78) 31 | ) 32 | -------------------------------------------------------------------------------- /common/operators/gridrec_module/pymodule_gridrec_v4/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main() 3 | { 4 | int N = 4096; 5 | 6 | double *test = (double*)malloc(2*N*N*sizeof(double)); 7 | 8 | fftw_complex *in, *out; 9 | 10 | fftw_plan p; 11 | in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N*N); 12 | int i,j; 13 | for(i=0;i 9 | #include 10 | #include 11 | #include 12 | 13 | #define pi 3.141592653589793 14 | #define eps 1.0e-7 15 | 16 | 17 | typedef struct{ 18 | float xcoor; 19 | short int label; 20 | short int index; 21 | } Proj; 22 | 23 | 24 | 25 | 26 | void quick_sort( Proj *a , int n ) { 27 | int i, j; 28 | float p; 29 | Proj t; 30 | 31 | if ( n < 2 ) 32 | return; 33 | 34 | p = a[n/2].xcoor; 35 | 36 | for( i = 0 , j=n-1;; i++ , j-- ){ 37 | while( a[i].xcoor < p ) 38 | i++; 39 | while( p < a[j].xcoor ) 40 | j--; 41 | if( i >= j ) 42 | break; 43 | t = a[i]; 44 | a[i] = a[j]; 45 | a[j] = t; 46 | } 47 | quick_sort( a , i ); 48 | quick_sort( a + i , n - i ); 49 | } 50 | 51 | 52 | 53 | 54 | void radon_dd( float* image , int npix , float *angles , int nang , int oper , float *sino ) 55 | { 56 | int v, i, j, k, nh, nt, flag1, flag2, i_d, i_p; 57 | float s, c, x, y, theta, xi, xd, diff; 58 | float *xcoor; 59 | int *label, *index; 60 | 61 | 62 | nh = ( int )( npix * 0.5 ); 63 | nt = 2 * ( npix + 1 ); 64 | 65 | 66 | Proj *proj = ( Proj * )malloc( nt * sizeof( Proj ) ); 67 | 68 | 69 | for( v=0 ; v pi/2 ){ 100 | for( i=0 ; i=0 ; k-- ){ 150 | if( proj[k].label == 1 && proj[k].index != npix ){ 151 | flag2 = 1; 152 | break; 153 | } 154 | } 155 | if( flag2 ){ 156 | i_p = proj[j-1].index; 157 | i_d = proj[k].index; 158 | diff = fabs( proj[j].xcoor - proj[j-1].xcoor ); 159 | } 160 | } 161 | 162 | else if( proj[j].label == 1 && proj[j-1].label == 1 ){ 163 | for( k=j-1 ; k>=0 ; k-- ){ 164 | if( proj[k].label == 0 && proj[k].index != npix ){ 165 | flag2 = 1; 166 | break; 167 | } 168 | } 169 | if( flag2 ){ 170 | i_d = proj[j-1].index; 171 | i_p = proj[k].index; 172 | diff = fabs( proj[j].xcoor - proj[j-1].xcoor ); 173 | } 174 | } 175 | 176 | if( proj[j].index == npix ) 177 | break; 178 | 179 | if( flag2 ){ 180 | if( oper == 0 ) 181 | sino[ ( nang -1 - v ) * npix + i_d ] += diff * image[ i * npix + i_p ]; 182 | else 183 | image[ i * npix + i_p ] += diff * sino[ ( nang -1 - v ) * npix + i_d ]; 184 | flag2 = 0; 185 | i_d = -1; 186 | i_p = -1; 187 | diff = -1; 188 | } 189 | } 190 | } 191 | } 192 | 193 | 194 | else if( theta < pi/2.0 && theta != 0.0 ){ 195 | for( i=0 ; i=0 ; k-- ){ 242 | if( proj[k].label == 1 && proj[k].index != npix ){ 243 | flag2 = 1; 244 | break; 245 | } 246 | } 247 | if( flag2 ){ 248 | i_p = proj[j-1].index; 249 | i_d = proj[k].index; 250 | diff = fabs( proj[j].xcoor - proj[j-1].xcoor ); 251 | } 252 | } 253 | 254 | else if( proj[j].label == 1 && proj[j-1].label == 1 ){ 255 | for( k=j-1 ; k>=0 ; k-- ){ 256 | if( proj[k].label == 0 && proj[k].index != npix ){ 257 | flag2 = 1; 258 | break; 259 | } 260 | } 261 | if( flag2 ){ 262 | i_d = proj[j-1].index; 263 | i_p = proj[k].index; 264 | diff = fabs( proj[j].xcoor - proj[j-1].xcoor ); 265 | } 266 | } 267 | 268 | if( proj[j].index == npix ) 269 | break; 270 | 271 | if( flag2 ){ 272 | if( oper == 0 ) 273 | sino[ ( nang -1 - v ) * npix + i_d ] += diff * image[ i_p * npix + i ]; 274 | else 275 | image[ i_p * npix + i ] += diff * sino[ ( nang -1 - v ) * npix + i_d ]; 276 | flag2 = 0; 277 | i_d = -1; 278 | i_p = -1; 279 | diff = -1; 280 | } 281 | } 282 | } 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_distance_driven/radon_distance_driven.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | 3 | import numpy as np 4 | cimport numpy as np 5 | 6 | 7 | cdef extern void radon_dd( float* image , int npix , float* angles , int nang , int oper , float* sino ) 8 | 9 | 10 | 11 | 12 | @cython.boundscheck( False ) 13 | @cython.wraparound( False ) 14 | def forwproj( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 15 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None ): 16 | 17 | cdef int nang , npix , oper 18 | 19 | npix = image.shape[0] 20 | nang = len( angles ) 21 | myfloat = image.dtype 22 | 23 | oper = 0 24 | 25 | if np.max( angles ) > 2 * np.pi: 26 | angles *= np.pi / 180.0 27 | angles = np.fft.fftshift( angles ) 28 | 29 | sino = np.zeros( ( nang , npix ) , dtype=myfloat, order='C' ) 30 | 31 | cdef float [:,::1] csino = sino 32 | 33 | radon_dd( &image[0,0] , npix , &angles[0] , nang , oper , &csino[0,0] ) 34 | 35 | return sino 36 | 37 | 38 | 39 | 40 | @cython.boundscheck( False ) 41 | @cython.wraparound( False ) 42 | def backproj( np.ndarray[ float , ndim=2 , mode="c" ] sino not None , 43 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None ): 44 | 45 | cdef int nang , npix , oper 46 | 47 | nang , npix = sino.shape[0] , sino.shape[1] 48 | myfloat = sino.dtype 49 | 50 | oper = 1 51 | 52 | if np.max( angles ) > 2 * np.pi: 53 | angles *= np.pi / 180.0 54 | angles = np.fft.fftshift( angles ) 55 | 56 | image = np.zeros( ( npix , npix ) , dtype=myfloat, order='C' ) 57 | 58 | cdef float [:,::1] cimage = image 59 | 60 | radon_dd( &cimage[0,0] , npix , &angles[0] , nang , oper , &sino[0,0] ) 61 | 62 | return image 63 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_pixel_driven/clean.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'radon_pixel_driven.c' ) is True: 9 | os.remove( 'radon_pixel_driven.c' ) 10 | 11 | if os.path.isfile( 'radon_pixel_driven.so' ) is True: 12 | os.remove( 'radon_pixel_driven.so' ) 13 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_pixel_driven/compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'radon_pixel_driven.c' ) is True: 9 | os.remove( 'radon_pixel_driven.c' ) 10 | 11 | os.system('python create_module.py build_ext --inplace') 12 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_pixel_driven/create_module.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Distutils import build_ext 4 | 5 | import numpy 6 | 7 | setup( 8 | cmdclass = {'build_ext': build_ext}, 9 | ext_modules = [Extension("radon_pixel_driven", 10 | sources=[ "radon_pixel_driven.pyx" , 11 | "radon_pd.c" 12 | ], 13 | include_dirs=[numpy.get_include()],libraries=['gcov'], 14 | extra_compile_args=['-w','-O3','-march=native','-ffast-math','-fprofile-generate','-fopenmp'],extra_link_args=['-fprofile-generate'])], 15 | ) 16 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_pixel_driven/radon_pd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * IMPLEMENTATION OF THE PIXEL-DRIVEN TOMOGRAPHIC PROJECTORS 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define pi 3.141592653589793 11 | 12 | float delta[ 8 ] = { 13 | -0.25 , -0.25 , 0.25 , -0.25 , 14 | -0.25 , 0.25 , 0.25 , 0.25 15 | }; 16 | 17 | 18 | 19 | 20 | void radon_pd( float* image , int npix , float *angles , int nang , int oper , int method , float *sino ) 21 | { 22 | int v, i, j, k, l, u, nh; 23 | float x0, y0, x, y, theta, s, c, t, uf, lf; 24 | 25 | nh = (int)( npix * 0.5 ); 26 | 27 | int counter = 0; 28 | 29 | for( v=0 ; v x*s/c ) 47 | t = t; 48 | else if( theta > pi/2 && y > x*s/c ) 49 | t = t; 50 | else if( theta == 0 && y > 0 ) 51 | t = t; 52 | else if( theta == pi/2 && x < 0 ) 53 | t = t; 54 | else 55 | t = -t; 56 | 57 | if( method == 0 ){ 58 | l = (int)round( nh - 0.5 - t ); 59 | 60 | if( oper == 0 ) 61 | sino[v*npix + l] += 0.25 * image[(npix-1-i)*npix + j]; 62 | else 63 | image[(npix-1-i)*npix + j] += 0.25 * sino[v*npix + l]; 64 | } 65 | 66 | else{ 67 | t = nh - t; 68 | lf = (float)floor( t ); 69 | uf = (float)ceil( t ); 70 | l = (int)round( lf - 0.5 ); 71 | u = (int)round( uf - 0.5 ); 72 | 73 | if( oper == 0 ){ 74 | if( l > 0 && l < npix ) 75 | sino[v*npix + l] += 0.25 * image[(npix-1-i)*npix + j] * fabs( uf - t ); 76 | if( u > 0 && u < npix ) 77 | sino[v*npix + u] += 0.25 * image[(npix-1-i)*npix + j] * fabs( t - lf ); 78 | } 79 | else{ 80 | if( l > 0 && l < npix ) 81 | image[(npix-1-i)*npix + j] += 0.25 * sino[v*npix + l] * fabs( uf - t ); 82 | if( u > 0 && u < npix ) 83 | image[(npix-1-i)*npix + j] += 0.25 * sino[v*npix + u] * fabs( t - lf ); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_pixel_driven/radon_pixel_driven.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | 3 | import numpy as np 4 | cimport numpy as np 5 | 6 | 7 | cdef extern void radon_pd( float* sino , int npix , float* angles , int nang , 8 | int oper , int method , float* image ) 9 | 10 | 11 | 12 | 13 | @cython.boundscheck( False ) 14 | @cython.wraparound( False ) 15 | def forwproj( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 16 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None , 17 | np.int method ): 18 | 19 | cdef int nang , npix , oper 20 | 21 | npix = image.shape[0] 22 | nang = len( angles ) 23 | myfloat = image.dtype 24 | 25 | oper = 0 26 | 27 | if np.max( angles ) > 2 * np.pi: 28 | angles *= 2 * np.pi / 180.0 29 | 30 | angles = np.fft.fftshift( angles ) 31 | 32 | if method is None: 33 | method = 0 34 | 35 | sino = np.zeros( ( nang , npix ) , dtype=myfloat, order='C' ) 36 | 37 | cdef float [:,::1] csino = sino 38 | 39 | radon_pd( &image[0,0] , npix , &angles[0] , nang , oper , method , &csino[0,0] ) 40 | 41 | return sino 42 | 43 | 44 | 45 | 46 | @cython.boundscheck( False ) 47 | @cython.wraparound( False ) 48 | def backproj( np.ndarray[ float , ndim=2 , mode="c" ] sino not None , 49 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None , 50 | np.int method ): 51 | 52 | cdef int nang , npix , oper 53 | 54 | nang , npix = sino.shape[0] , sino.shape[1] 55 | myfloat = sino.dtype 56 | 57 | oper = 1 58 | 59 | if np.max( angles ) > 2 * np.pi: 60 | angles *= 2 * np.pi / 180.0 61 | 62 | angles = np.fft.fftshift( angles ) 63 | 64 | if method is None: 65 | method = 0 66 | 67 | image = np.zeros( ( npix , npix ) , dtype=myfloat, order='C' ) 68 | 69 | cdef float [:,::1] cimage = image 70 | 71 | radon_pd( &cimage[0,0] , npix , &angles[0] , nang , oper , method , &sino[0,0] ) 72 | 73 | return image 74 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_ray_driven/clean.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'radon_ray_driven.c' ) is True: 9 | os.remove( 'radon_ray_driven.c' ) 10 | 11 | if os.path.isfile( 'radon_ray_driven.so' ) is True: 12 | os.remove( 'radon_ray_driven.so' ) 13 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_ray_driven/compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'radon_ray_driven.c' ) is True: 9 | os.remove( 'radon_ray_driven.c' ) 10 | 11 | os.system('python create_module.py build_ext --inplace') 12 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_ray_driven/create_module.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Distutils import build_ext 4 | 5 | import numpy 6 | 7 | setup( 8 | cmdclass = {'build_ext': build_ext}, 9 | ext_modules = [Extension("radon_ray_driven", 10 | sources=[ "radon_ray_driven.pyx" , 11 | "radon_rd.c" 12 | ], 13 | include_dirs=[numpy.get_include()],libraries=['gcov'], 14 | extra_compile_args=['-w','-O3','-march=native','-ffast-math','-fprofile-generate','-fopenmp'],extra_link_args=['-fprofile-generate'])], 15 | ) 16 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_ray_driven/radon_ray_driven.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | 3 | import numpy as np 4 | cimport numpy as np 5 | 6 | 7 | cdef extern void radon_rd( float* image , int npix , float* angles , int nang , 8 | int oper , float* sino ) 9 | 10 | 11 | 12 | 13 | @cython.boundscheck( False ) 14 | @cython.wraparound( False ) 15 | def forwproj( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 16 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None ): 17 | 18 | cdef int nang , npix , oper 19 | 20 | npix = image.shape[0] 21 | nang = len( angles ) 22 | myfloat = image.dtype 23 | 24 | oper = 0 25 | 26 | if np.max( angles ) > 2 * np.pi: 27 | angles *= np.pi / 180.0 28 | 29 | sino = np.zeros( ( nang , npix ) , dtype=myfloat, order='C' ) 30 | 31 | cdef float [:,::1] csino = sino 32 | 33 | radon_rd( &image[0,0] , npix , &angles[0] , nang , oper , &csino[0,0] ) 34 | 35 | return sino 36 | 37 | 38 | 39 | 40 | @cython.boundscheck( False ) 41 | @cython.wraparound( False ) 42 | def backproj( np.ndarray[ float , ndim=2 , mode="c" ] sino not None , 43 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None ): 44 | 45 | cdef int nang , npix , oper 46 | 47 | nang , npix = sino.shape[0] , sino.shape[1] 48 | myfloat = sino.dtype 49 | 50 | oper = 1 51 | 52 | if np.max( angles ) > 2 * np.pi: 53 | angles *= np.pi / 180.0 54 | 55 | image = np.zeros( ( npix , npix ) , dtype=myfloat, order='C' ) 56 | 57 | cdef float [:,::1] cimage = image 58 | 59 | radon_rd( &cimage[0,0] , npix , &angles[0] , nang , oper , &sino[0,0] ) 60 | 61 | return image 62 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_ray_driven/radon_rd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * IMPLEMENTATION OF THE PIXEL-DRIVEN TOMOGRAPHIC PROJECTORS 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define pi 3.141592653589793 11 | #define eps 1e-7 12 | 13 | 14 | 15 | void siddon( float *sino , float *image , float norm , int npix , float *p1 , float *p2 , int index , int oper ){ 16 | int ii, jj, i, j, nh; 17 | float alpha_old, alpha_x, alpha_y, alpha, dx, dy; 18 | float incr, l; 19 | 20 | nh = (int)( npix * 0.5 ); 21 | 22 | incr = sqrt( ( p2[0] - p1[0] ) * ( p2[0] - p1[0] ) + ( p2[1] - p1[1] ) * ( p2[1] - p1[1] ) ); 23 | 24 | ii = (int)round( p1[0] ); 25 | jj = (int)round( p1[1] ); 26 | 27 | if( p2[0] > p1[0] ) 28 | dx = 1.0; 29 | else 30 | dx = -1.0; 31 | 32 | if( p2[1] > p1[1] ) 33 | dy = 1.0; 34 | else 35 | dy = -1.0; 36 | 37 | 38 | alpha_old = 0.0; 39 | 40 | while( alpha <= 1 ){ 41 | alpha_y = ( jj + 0.5 * dy - p1[1] ) / ( p2[1] - p1[1] ); 42 | alpha_x = ( ii + 0.5 * dx - p1[0] ) / ( p2[0] - p1[0] ); 43 | 44 | if( alpha_x < alpha_y ){ 45 | alpha = alpha_x; 46 | ii += dx; 47 | } 48 | else if( alpha_x > alpha_y ){ 49 | alpha = alpha_y; 50 | jj += dy; 51 | } 52 | else if( alpha_x == alpha_y ){ 53 | alpha = alpha_y; 54 | ii += dx; 55 | jj += dy; 56 | } 57 | 58 | l = ( alpha - alpha_old ) * incr; 59 | i = ii + nh; 60 | j = jj + nh; 61 | 62 | if( i>0 && i0 && j= 3*npix && theta < pi/2 ) 128 | t = -t; 129 | if( i < 3*npix && theta > pi/2 ) 130 | t = -t; 131 | 132 | // Compute entry and exit point 133 | if( theta < pi/2 ){ 134 | y1 = -nh * s/c + t/c; 135 | x1 = -nh * c/s - t/s; 136 | y2 = nh * s/c + t/c; 137 | x2 = nh * c/s - t/s; 138 | 139 | if( y1 >= -nh && y1 <= nh ){ 140 | p1[0] = -nh; 141 | p1[1] = y1; 142 | } 143 | else{ 144 | p1[0] = x1; 145 | p1[1] = -nh; 146 | } 147 | if( y2 >= -nh && y2 <= nh ){ 148 | p2[0] = nh; 149 | p2[1] = y2; 150 | } 151 | else{ 152 | p2[0] = x2; 153 | p2[1] = nh; 154 | } 155 | } 156 | 157 | else{ 158 | y1 = nh * s/c - t/c; 159 | x1 = -nh * c/s + t/s; 160 | y2 = -nh * s/c - t/c; 161 | x2 = nh * c/s + t/s; 162 | 163 | if( y1 >= -nh && y1 <= nh ){ 164 | p1[0] = nh; 165 | p1[1] = y1; 166 | } 167 | else{ 168 | p1[0] = x1; 169 | p1[1] = -nh; 170 | } 171 | if( y2 >= -nh && y2 <= nh ){ 172 | p2[0] = -nh; 173 | p2[1] = y2; 174 | } 175 | else{ 176 | p2[0] = x2; 177 | p2[1] = nh; 178 | } 179 | } 180 | 181 | siddon( sino , image , norm , npix , p1 , p2 , index , oper ); 182 | } 183 | 184 | else if( i % 6 == 0 && i != 0 ) 185 | index++; 186 | } 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_slant_stacking/clean.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'radon_slant_stacking.c' ) is True: 9 | os.remove( 'radon_slant_stacking.c' ) 10 | 11 | if os.path.isfile( 'radon_slant_stacking.so' ) is True: 12 | os.remove( 'radon_slant_stacking.so' ) 13 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_slant_stacking/compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'radon_slant_stacking.c' ) is True: 9 | os.remove( 'radon_slant_stacking.c' ) 10 | 11 | os.system('python create_module.py build_ext --inplace') 12 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_slant_stacking/create_module.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Distutils import build_ext 4 | 5 | import numpy 6 | 7 | setup( 8 | cmdclass = {'build_ext': build_ext}, 9 | ext_modules = [Extension("radon_slant_stacking", 10 | sources=[ "radon_slant_stacking.pyx" , 11 | "radon_ss.c" 12 | ], 13 | include_dirs=[numpy.get_include()],libraries=['gcov'], 14 | extra_compile_args=['-w','-O3','-march=native','-ffast-math','-fprofile-generate','-fopenmp'],extra_link_args=['-fprofile-generate'])], 15 | ) 16 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_slant_stacking/radon_slant_stacking.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | 3 | import numpy as np 4 | cimport numpy as np 5 | 6 | 7 | cdef extern void radon_ss( float* sino , int npix , float* angles , int nang , 8 | int oper , int method , float* image ) 9 | 10 | 11 | 12 | 13 | @cython.boundscheck( False ) 14 | @cython.wraparound( False ) 15 | def forwproj( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 16 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None , 17 | np.int method ): 18 | 19 | cdef int nang , npix , oper 20 | 21 | npix = image.shape[0] 22 | nang = len( angles ) 23 | myfloat = image.dtype 24 | 25 | oper = 0 26 | 27 | if np.max( angles ) > 2 * np.pi: 28 | angles *= 2 * np.pi / 180.0 29 | 30 | #angles = np.fft.fftshift( angles ) 31 | 32 | if method is None: 33 | method = 0 34 | 35 | sino = np.zeros( ( nang , npix ) , dtype=myfloat, order='C' ) 36 | 37 | cdef float [:,::1] csino = sino 38 | 39 | radon_ss( &image[0,0] , npix , &angles[0] , nang , oper , method , &csino[0,0] ) 40 | 41 | return sino 42 | 43 | 44 | 45 | 46 | @cython.boundscheck( False ) 47 | @cython.wraparound( False ) 48 | def backproj( np.ndarray[ float , ndim=2 , mode="c" ] sino not None , 49 | np.ndarray[ float , ndim=1 , mode="c" ] angles not None , 50 | np.int method ): 51 | 52 | cdef int nang , npix , oper 53 | 54 | nang , npix = sino.shape[0] , sino.shape[1] 55 | myfloat = sino.dtype 56 | 57 | oper = 1 58 | 59 | if np.max( angles ) > 2 * np.pi: 60 | angles *= 2 * np.pi / 180.0 61 | 62 | #angles = np.fft.fftshift( angles ) 63 | 64 | if method is None: 65 | method = 0 66 | 67 | image = np.zeros( ( npix , npix ) , dtype=myfloat, order='C' ) 68 | 69 | cdef float [:,::1] cimage = image 70 | 71 | radon_ss( &cimage[0,0] , npix , &angles[0] , nang , oper , method , &sino[0,0] ) 72 | 73 | return image 74 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_slant_stacking/radon_ss.c: -------------------------------------------------------------------------------- 1 | /* 2 | * IMPLEMENTATION OF THE PIXEL-DRIVEN TOMOGRAPHIC PROJECTORS 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define pi 3.141592653589793 11 | #define eps 1e-5 12 | #define s45 0.70710678118654757 13 | 14 | 15 | 16 | void quick_sort ( float *a , int n ) { 17 | int i, j; 18 | float p, t; 19 | 20 | if ( n < 2 ) 21 | return; 22 | 23 | p = a[ n / 2 ]; 24 | 25 | for ( i = 0 , j = n - 1 ;; i++ , j-- ){ 26 | while ( a[i] < p ) 27 | i++; 28 | while ( p < a[j] ) 29 | j--; 30 | if ( i >= j ) 31 | break; 32 | t = a[i]; 33 | a[i] = a[j]; 34 | a[j] = t; 35 | } 36 | quick_sort( a , i ); 37 | quick_sort( a + i , n - i ); 38 | } 39 | 40 | 41 | 42 | 43 | void ray_limits( float* lim, int t , float s , float c , int npix ){ 44 | float x1, y1, x2, y2; 45 | float x_min, x_max, y_min, y_max; 46 | 47 | int nh = (int)( npix * 0.5 ); 48 | 49 | 50 | // Theta = 0 51 | if( fabs( s ) < eps ){ 52 | lim[0] = 0; 53 | lim[1] = 0; 54 | lim[2] = -nh; 55 | lim[3] = nh-1; 56 | } 57 | 58 | 59 | // Theta = pi/2 60 | else if( fabs( c ) < eps ){ 61 | lim[0] = -nh; 62 | lim[1] = nh-1; 63 | lim[2] = 0; 64 | lim[3] = 0; 65 | 66 | } 67 | 68 | 69 | // All other angles 70 | else{ 71 | x1 = t/c + nh * s/c; 72 | y1 = t/s + nh * c/s; 73 | x2 = t/c - (nh-1) * s/c; 74 | y2 = t/s - (nh-1) * c/s; 75 | 76 | lim[0] = x1; lim[1] = x2; lim[2] = -nh; lim[3] = nh-1; 77 | quick_sort( lim , 4 ); 78 | x_min = lim[1]; 79 | x_max = lim[2]; 80 | 81 | lim[0] = y1; lim[1] = y2; lim[2] = -nh; lim[3] = nh-1; 82 | quick_sort( lim , 4 ); 83 | y_min = lim[1]; 84 | y_max = lim[2]; 85 | 86 | lim[0] = x_min; lim[1] = x_max; lim[2] = y_min; lim[3] = y_max; 87 | } 88 | } 89 | 90 | 91 | 92 | 93 | void radon_ss( float* image , int npix , float *angles , int nang , int oper , int method , float *sino ) 94 | { 95 | int v, j, k, t, nh, x1, y1, x, y; 96 | float theta, s, c, w, xf, yf; 97 | 98 | float *lim = ( float* )calloc( 4 , sizeof( int ) ); 99 | 100 | nh = (int)( npix * 0.5 ); 101 | 102 | 103 | for( v=0 ; v= 0 && x1<= npix-1 ) 142 | sino[ j * npix + k ] += 1.0/fabs(c) * ( 1 - w ) * image[ y1 * npix + x1 ]; 143 | if( x1+1 >= 0 && x1+1<= npix-1 ) 144 | sino[ j * npix + k ] += 1.0/fabs(c) * w * image[ y1 * npix + x1 + 1 ]; 145 | } 146 | else{ 147 | if( x1 >= 0 && x1<= npix-1 ) 148 | image[ y1 * npix + x1 ] += 1.0/fabs(c) * ( 1 - w ) * sino[ j * npix + k ]; 149 | if( x1+1 >= 0 && x1+1<= npix-1 ) 150 | image[ y1 * npix + x1 + 1 ] += 1.0/fabs(c) * w * sino[ j * npix + k ]; 151 | } 152 | } 153 | } 154 | } 155 | 156 | 157 | // pi/4 < theta < 3pi/4 158 | else{ 159 | for( x=(int)round(lim[0]) ; x<(int)round(lim[1]) ; x++ ){ 160 | // Nearest neighbour interpolation 161 | if( method == 0){ 162 | y1 = (int)round( t/s - x*c/s ) + nh; 163 | x1 = x + nh; 164 | 165 | if( oper == 0 ) 166 | sino[ j * npix + k ] += 1.0/fabs(s) * image[ y1 * npix + x1 ]; 167 | else 168 | image[ y1 * npix + x1 ] += 1.0/fabs(s) * sino[ j * npix + k ]; 169 | } 170 | 171 | // Linear interpolation 172 | else{ 173 | yf = t/s - x*c/s; 174 | w = yf - floor( yf ); 175 | y1 = (int)floor( yf ) + nh; 176 | x1 = x + nh; 177 | 178 | if( oper == 0 ){ 179 | if( y1 >= 0 && y1<= npix-1 ) 180 | sino[ j * npix + k ] += 1.0/fabs(s) * ( 1 - w ) * image[ y1 * npix + x1 ]; 181 | if( y1+1 >= 0 && y1+1<= npix-1 ) 182 | sino[ j * npix + k ] += 1.0/fabs(s) * w * image[ ( y1 + 1 ) * npix + x1 ]; 183 | } 184 | else{ 185 | if( y1 >= 0 && y1<= npix-1 ) 186 | image[ y1 * npix + x1 ] += 1.0/fabs(s) * ( 1 - w ) * sino[ j * npix + k ]; 187 | if( y1+1 >= 0 && y1+1<= npix-1 ) 188 | image[ ( y1 + 1 ) * npix + x1 ] += 1.0/fabs(s) * w * sino[ j * npix + k ]; 189 | } 190 | } 191 | } 192 | } 193 | } 194 | } 195 | free( lim ); 196 | } 197 | -------------------------------------------------------------------------------- /common/operators/radon_module/pymodule_radon_slant_stacking/radon_ss.c__backup: -------------------------------------------------------------------------------- 1 | /* 2 | * IMPLEMENTATION OF THE PIXEL-DRIVEN TOMOGRAPHIC PROJECTORS 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define pi 3.141592653589793 11 | # define eps 1e-5 12 | #define s45 0.70710678118654757 13 | 14 | 15 | 16 | 17 | void radon_ss( float* image , int npix , float *angles , int nang , int oper , int method , float *sino ) 18 | { 19 | int v, i, j, t, nh; 20 | float theta, s, c, pho, w; 21 | 22 | nh = (int)( npix * 0.5 ); 23 | 24 | int counter = 0; 25 | 26 | for( v=0 ; v s45 ){ 70 | if( method == 0 ){ 71 | j = ( int )round( -c/s * ( i + 0.5 - nh ) + pho/s ) + nh; 72 | //printf("\ni = %d t = %d j = %d", i, t, j); 73 | 74 | if( j >=0 && j=0 && j=0 && j+1=0 && j=0 && j+1=0 && j=0 && j=0 && j+1=0 && j=0 && j+1 18 | #include 19 | #include "penalty_weights.h" 20 | 21 | 22 | 23 | 24 | /* 25 | * Macros 26 | */ 27 | 28 | #define myabs(X) ( (X)<0 ? -(X) : (X) ) 29 | 30 | 31 | 32 | 33 | float pot_l1( float x ){ 34 | if( x >= 0 ) 35 | return x; 36 | else 37 | return -x; 38 | } 39 | 40 | 41 | 42 | /* 43 | * Huber's penalty numerator for 44 | * statistical reconstruction 45 | */ 46 | 47 | void haar( float *I , int nr , int nc , float *R ){ 48 | 49 | /* 50 | * Define variables 51 | */ 52 | 53 | int i, j, k, k1 , k2; 54 | 55 | 56 | 57 | /* 58 | * Pre-compute weights 59 | */ 60 | 61 | float *w = penalty_weights(); 62 | 63 | 64 | 65 | /* 66 | * Compute penalty 67 | */ 68 | 69 | for( i=0 ; i < nr ; i++ ){ 70 | for( j=0; j < nc ; j++ ){ 71 | for( k=0 , k1=-1 ; k1 <= 1 ; k1++ ){ 72 | for( k2=-1 ; k2 <= 1 ; k2++ , k++ ){ 73 | if( i+k1 >=0 && i+k1 <=nr-1 && j+k2 >=0 && j+k2 <=nc-1){ 74 | R[ i*nc + j ] += w[k] * pot_l1( I[ i*nc + j ] - I[ (i+k1)*nc + j + k2 ] ); 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /common/operators/regularization_module/huber.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ******************************************************************** 3 | *** *** 4 | *** HUBER PENALTY NUMERATOR FOR STATISTICAL RECONSTRUCTION *** 5 | *** *** 6 | *** Written by F. Arcadu on the 20/02/2015 *** 7 | *** *** 8 | ******************************************************************** 9 | ********************************************************************/ 10 | 11 | 12 | 13 | /* 14 | * Headers 15 | */ 16 | 17 | #include 18 | #include 19 | #include "penalty_weights.h" 20 | 21 | 22 | 23 | 24 | /* 25 | * Macros 26 | */ 27 | 28 | #define myabs(X) ( (X)<0 ? -(X) : (X) ) 29 | 30 | 31 | 32 | 33 | 34 | float pot_huber_num( float x , float delta ){ 35 | return x / ( 1.0 + myabs( x / delta ) ); 36 | } 37 | 38 | 39 | float pot_huber_den( float x , float delta ){ 40 | return 1.0 / ( 1.0 + myabs( x / delta ) ); 41 | } 42 | 43 | 44 | 45 | /* 46 | * Huber's penalty numerator for 47 | * statistical reconstruction 48 | */ 49 | 50 | void huber( float *I , int nr , int nc , float delta , char*term , float *R ){ 51 | 52 | /* 53 | * Define variables 54 | */ 55 | 56 | int i, j, k, k1 , k2; 57 | 58 | 59 | 60 | /* 61 | * Pre-compute weights 62 | */ 63 | 64 | float *w = penalty_weights(); 65 | 66 | 67 | 68 | /* 69 | * Compute penalty 70 | */ 71 | 72 | if( term[0] == 'n' ){ 73 | for( i=0 ; i < nr ; i++ ){ 74 | for( j=0; j < nc ; j++ ){ 75 | for( k=0 , k1=-1 ; k1 <= 1 ; k1++ ){ 76 | for( k2=-1 ; k2 <= 1 ; k2++ , k++ ){ 77 | if( i+k1 >=0 && i+k1 <=nr-1 && j+k2 >=0 && j+k2 <=nc-1){ 78 | R[ i*nc + j ] += w[k] * pot_huber_num( I[ i*nc + j ] - I[ (i+k1)*nc + j + k2 ] , 79 | delta ); 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | else{ 88 | for( i=0 ; i < nr ; i++ ){ 89 | for( j=0; j < nc ; j++ ){ 90 | for( k=0 , k1=-1 ; k1 <= 1 ; k1++ ){ 91 | for( k2=-1 ; k2 <= 1 ; k2++ , k++ ){ 92 | if( i+k1 >=0 && i+k1 <=nr-1 && j+k2 >=0 && j+k2 <=nc-1){ 93 | R[ i*nc + j ] += 2 * w[k] * pot_huber_den( I[ i*nc + j ] - I[ (i+k1)*nc + j + k2 ] , 94 | delta ); 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /common/operators/regularization_module/penalty.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | 3 | import numpy as np 4 | cimport numpy as np 5 | 6 | 7 | cdef extern void huber( float *I , int nr , int nc , float delta , char* term , float *R ) 8 | cdef extern void tikhonov( float *I , int nr , int nc , float *R ) 9 | cdef extern void haar( float *I , int nr , int nc , float *R ) 10 | 11 | 12 | 13 | 14 | @cython.boundscheck( False ) 15 | @cython.wraparound( False ) 16 | def huber_num( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 17 | float delta ): 18 | nr , nc = image.shape[0] , image.shape[1] 19 | myfloat = image.dtype 20 | 21 | penalty = np.zeros( ( nr , nc ) , dtype=myfloat, order='C' ) 22 | cdef float [:,::1] cpenalty = penalty 23 | 24 | huber( &image[0,0] , nr , nc , delta , 'nom' , &cpenalty[0,0] ) 25 | 26 | return penalty 27 | 28 | 29 | 30 | 31 | @cython.boundscheck( False ) 32 | @cython.wraparound( False ) 33 | def huber_den( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 34 | float delta ): 35 | nr , nc = image.shape[0] , image.shape[1] 36 | myfloat = image.dtype 37 | 38 | 39 | penalty = np.zeros( ( nr , nc ) , dtype=myfloat, order='C' ) 40 | cdef float [:,::1] cpenalty = penalty 41 | 42 | huber( &image[0,0], nr , nc , delta , 'den' , &cpenalty[0,0] ) 43 | 44 | return penalty 45 | 46 | 47 | 48 | @cython.boundscheck( False ) 49 | @cython.wraparound( False ) 50 | def tikhonov_num( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 51 | float gamma ): 52 | nr , nc = image.shape[0] , image.shape[1] 53 | myfloat = image.dtype 54 | 55 | penalty = np.zeros( ( nr , nc ) , dtype=myfloat, order='C' ) 56 | cdef float [:,::1] cpenalty = penalty 57 | 58 | tikhonov( &image[0,0] , nr , nc , &cpenalty[0,0] ) 59 | 60 | return penalty 61 | 62 | 63 | 64 | 65 | @cython.boundscheck( False ) 66 | @cython.wraparound( False ) 67 | def tikhonov_den( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 68 | float gamma ): 69 | nr , nc = image.shape[0] , image.shape[1] 70 | myfloat = image.dtype 71 | 72 | penalty = np.zeros( ( nr , nc ) , dtype=myfloat, order='C' ) 73 | penalty[:] = 2 74 | 75 | return penalty 76 | 77 | 78 | 79 | 80 | @cython.boundscheck( False ) 81 | @cython.wraparound( False ) 82 | def haar_num( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 83 | float gamma ): 84 | nr , nc = image.shape[0] , image.shape[1] 85 | myfloat = image.dtype 86 | 87 | penalty = np.zeros( ( nr , nc ) , dtype=myfloat, order='C' ) 88 | cdef float [:,::1] cpenalty = penalty 89 | 90 | haar( &image[0,0] , nr , nc , &cpenalty[0,0] ) 91 | 92 | return penalty 93 | 94 | 95 | 96 | 97 | @cython.boundscheck( False ) 98 | @cython.wraparound( False ) 99 | def haar_den( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 100 | float gamma ): 101 | nr , nc = image.shape[0] , image.shape[1] 102 | myfloat = image.dtype 103 | 104 | penalty = np.zeros( ( nr , nc ) , dtype=myfloat, order='C' ) 105 | 106 | return penalty 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /common/operators/regularization_module/penalty_weights.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | 7 | float* penalty_weights(){ 8 | float *w = ( float * ) malloc( 9 * sizeof( float ) ); 9 | 10 | w[0] = w[2] = w[6] = w[8] = 1.0 / sqrt( 2.0 ); 11 | w[1] = w[3] = w[5] = w[7] = 1.0; 12 | w[4] = 4.0; 13 | 14 | return w; 15 | } 16 | -------------------------------------------------------------------------------- /common/operators/regularization_module/penalty_weights.h: -------------------------------------------------------------------------------- 1 | float* penalty_weights(); 2 | -------------------------------------------------------------------------------- /common/operators/regularization_module/pymodule_split_bregman_nltv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arcaduf/iterative_tomographic_reconstruction_algorithms/c4fe95fe19e93db6bcf62f12d5add32b54527638/common/operators/regularization_module/pymodule_split_bregman_nltv/__init__.py -------------------------------------------------------------------------------- /common/operators/regularization_module/pymodule_split_bregman_nltv/clean.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile( 'split_bregman_nltv.c' ) is True: 9 | os.remove( 'split_bregman_nltv.c' ) 10 | 11 | if os.path.isfile( 'split_bregman_nltv.so' ) is True: 12 | os.remove( 'split_bregman_nltv.so' ) 13 | -------------------------------------------------------------------------------- /common/operators/regularization_module/pymodule_split_bregman_nltv/compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | if os.path.exists('build'): 6 | shutil.rmtree('build') 7 | 8 | if os.path.isfile('split_bregman_nltv.so'): 9 | os.remove('split_bregman_nltv.so') 10 | 11 | if os.path.isfile('split_bregman_nltv.c'): 12 | os.remove('split_bregman_nltv.c') 13 | 14 | os.system('python create_penalty_module.py build_ext --inplace') 15 | -------------------------------------------------------------------------------- /common/operators/regularization_module/pymodule_split_bregman_nltv/create_penalty_module.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Distutils import build_ext 4 | 5 | import numpy 6 | 7 | setup( 8 | cmdclass = {'build_ext': build_ext}, 9 | ext_modules = [Extension("split_bregman_nltv", 10 | sources=[ "split_bregman_nltv.pyx" , 11 | "nl_weights.c" , 12 | "sbnltv.c" , 13 | ], 14 | include_dirs=[numpy.get_include()],libraries=['gcov'],extra_compile_args=['-w','-O3','-march=native','-ffast-math','-fprofile-generate'],extra_link_args=['-fprofile-generate'])], 15 | ) 16 | -------------------------------------------------------------------------------- /common/operators/regularization_module/pymodule_split_bregman_nltv/split_bregman_nltv.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | 3 | import numpy as np 4 | cimport numpy as np 5 | 6 | 7 | cdef extern void nl_weights( float *I , float *param , float *cout0 ,int *cout1 , int *cout2 ) 8 | 9 | cdef extern void sbnltv( float *cu , float *I , float *W , int *Y , int *SY , float *param , float *cout0 ) 10 | 11 | 12 | 13 | 14 | @cython.boundscheck( False ) 15 | @cython.wraparound( False ) 16 | def nl_weights_cy( np.ndarray[ float , ndim=2 , mode="c" ] image not None , 17 | np.ndarray[ float , ndim=1 , mode="c" ] param not None ): 18 | myfloat = image.dtype 19 | 20 | iNx = np.int( param[0] ); iNy = np.int( param[1] ); iNbNeigh = np.int( param[5] ) 21 | iw = np.int( param[3] ); icn = np.int( param[6] ) 22 | 23 | if iNbNeigh > iw * iw - 4: 24 | iNbNeigh = iw * iw 25 | else: 26 | if icn: 27 | iNbNeigh += 4 28 | iN3 = iNbNeigh * 2 * 2 29 | 30 | out0 = np.zeros( ( iNx * iNy * iN3 ) , dtype=myfloat, order='C' ) 31 | cdef float [::1]cout0 = out0 32 | 33 | out1 = np.zeros( ( iNx * iNy * iN3 ) , dtype=np.int32, order='C' ) 34 | cdef int [::1]cout1 = out1 35 | 36 | out2 = np.zeros( ( iNx , iNy ) , dtype=np.int32, order='C' ) 37 | cdef int [:,::1] cout2 = out2 38 | 39 | nl_weights( &image[0,0] , ¶m[0] , &cout0[0] , &cout1[0] , &cout2[0,0] ) 40 | 41 | return out0 , out1 , out2 42 | 43 | 44 | 45 | 46 | @cython.boundscheck( False ) 47 | @cython.wraparound( False ) 48 | def sbnltv_cy( np.ndarray[ float , ndim=2 , mode="c" ] I not None , 49 | np.ndarray[ float , ndim=1 , mode="c" ] W not None , 50 | np.ndarray[ int , ndim=1 , mode="c" ] Y not None , 51 | np.ndarray[ int , ndim=2 , mode="c" ] SY not None , 52 | np.ndarray[ float , ndim=1 , mode="c" ] param not None , 53 | ): 54 | myfloat = I.dtype 55 | 56 | iNx = param[0]; iNy = param[1]; iNbNeigh = param[4]; iw = np.int( param[3] ) 57 | 58 | if iNbNeigh > iw * iw - 4: 59 | iNbNeigh = iw * iw 60 | iN3 = iNbNeigh * 2 * 2 61 | 62 | u = np.zeros( ( iNx , iNy ) , dtype=myfloat, order='C' ) 63 | u[:] = I 64 | cdef float [:,::1] cu = u 65 | 66 | out0 = np.zeros( ( iNx , iNy ) , dtype=myfloat, order='C' ) 67 | cdef float [:,::1] cout0 = out0 68 | 69 | sbnltv( &cu[0,0] , &I[0,0] , &W[0] , &Y[0] , &SY[0,0] , ¶m[0] , &cout0[0,0] ) 70 | 71 | return out0 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /common/operators/regularization_module/tikhonov.c: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | *********************************************************************** 3 | *** *** 4 | *** TIKHONOV PENALTY NUMERATOR FOR STATISTICAL RECONSTRUCTION *** 5 | *** *** 6 | *** Written by F. Arcadu on the 20/02/2015 *** 7 | *** *** 8 | *********************************************************************** 9 | ***********************************************************************/ 10 | 11 | 12 | 13 | /* 14 | * Headers 15 | */ 16 | 17 | #include 18 | #include 19 | #include "penalty_weights.h" 20 | 21 | 22 | 23 | 24 | /* 25 | * Macros 26 | */ 27 | 28 | #define myabs(X) ( (X)<0 ? -(X) : (X) ) 29 | 30 | 31 | 32 | float pot_l2( float x ){ 33 | return 2 * x; 34 | } 35 | 36 | 37 | 38 | 39 | /* 40 | * Huber's penalty numerator for 41 | * statistical reconstruction 42 | */ 43 | 44 | void tikhonov( float *I , int nr , int nc , float *R ){ 45 | 46 | /* 47 | * Define variables 48 | */ 49 | 50 | int i, j, k, k1 , k2; 51 | 52 | 53 | 54 | /* 55 | * Pre-compute weights 56 | */ 57 | 58 | float *w = penalty_weights(); 59 | 60 | 61 | 62 | /* 63 | * Compute penalty 64 | */ 65 | 66 | for( i=0 ; i < nr ; i++ ){ 67 | for( j=0; j < nc ; j++ ){ 68 | for( k=0 , k1=-1 ; k1 <= 1 ; k1++ ){ 69 | for( k2=-1 ; k2 <= 1 ; k2++ , k++ ){ 70 | if( i+k1 >=0 && i+k1 <=nr-1 && j+k2 >=0 && j+k2 <=nc-1){ 71 | R[ i*nc + j ] += w[k] * pot_l2( I[ i*nc + j ] - I[ (i+k1)*nc + j + k2 ] ); 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /data/shepp_logan_pix0256.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arcaduf/iterative_tomographic_reconstruction_algorithms/c4fe95fe19e93db6bcf62f12d5add32b54527638/data/shepp_logan_pix0256.tif -------------------------------------------------------------------------------- /data/shepp_logan_pix0256_ang0304_sino.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arcaduf/iterative_tomographic_reconstruction_algorithms/c4fe95fe19e93db6bcf62f12d5add32b54527638/data/shepp_logan_pix0256_ang0304_sino.tif -------------------------------------------------------------------------------- /data/shepp_logan_pix0256_ang0304_sino_reco.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arcaduf/iterative_tomographic_reconstruction_algorithms/c4fe95fe19e93db6bcf62f12d5add32b54527638/data/shepp_logan_pix0256_ang0304_sino_reco.tif -------------------------------------------------------------------------------- /data/sl3d.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arcaduf/iterative_tomographic_reconstruction_algorithms/c4fe95fe19e93db6bcf62f12d5add32b54527638/data/sl3d.zip -------------------------------------------------------------------------------- /scripts/test_create_sinogram.py: -------------------------------------------------------------------------------- 1 | ## In this test a Shepp-Logan phantom is forward projected with the 2 | ## forward gridding projector with minimal oversampling for 304 3 | ## different angles in [0,180) 4 | 5 | from __future__ import division , print_function 6 | import os 7 | 8 | os.chdir( '../algorithms' ) 9 | command = 'python -W ignore gridding_forward.py -Di ../data/ -i shepp_logan_pix0256.tif -n 304' 10 | print( '\n\nCommand line:\n', command ) 11 | os.system( command ) 12 | -------------------------------------------------------------------------------- /scripts/test_create_sinogram_3d.py: -------------------------------------------------------------------------------- 1 | ## In this test a 3D Shepp-Logan phantom, consisting of z-slices saved 2 | ## in separate files, is forward projected with the forward gridding 3 | ## projector with minimal oversmapling. 4 | ## The outcome is a new folder containing sinograms with 304 views for 5 | ## each z-slice. 6 | 7 | from __future__ import division , print_function 8 | import os 9 | 10 | os.chdir( '../algorithms' ) 11 | command = 'python -W ignore gridding_forward.py -Di ../data/sl3d/sl3d_128_phantom -Do ../data/sl3d/sl3d_128_sino_ang0200 -n 200' 12 | print( '\n\nCommand line:\n', command ) 13 | os.system( command ) 14 | -------------------------------------------------------------------------------- /scripts/test_reconstr_admm_01.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan is reconstructed with the ADMM 2 | ## Plug-and-Play, using the Split-Bregman TV method as regularization 3 | ## (-r pp-breg), the gridding projectors with minimal oversampling 4 | ## (-pr grid-kb), 4 iterations (-n1 4), 4 CG-subiterations (-n2 4), 5 | ## physical constraints, i.e., setting to zero all negative pixels 6 | ## at each iteration (-pc 0.0). 7 | 8 | from __future__ import division , print_function 9 | import os 10 | 11 | os.chdir( '../algorithms' ) 12 | command = 'python -W ignore admm.py -Di ../data/ -i shepp_logan_pix0256_ang0304_sino.tif -o shepp_logan_pix0256_ang0304_sino_reco.tif -pr grid-kb -r pp-breg -n1 4 -n2 4 -pp 1.0:1.0:1.0:1.0 -pc 0.0' 13 | print( '\n\nCommand line:\n', command ) 14 | os.system( command ) 15 | -------------------------------------------------------------------------------- /scripts/test_reconstr_admm_02.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan is reconstructed with the ADMM 2 | ## LASSO-TV, using the TV shrinkage-thresholding method as regularization 3 | ## (-r lasso-tv), the projectors based on the distance-driven approach 4 | ## (-pr dist-driv), 4 iterations (-n1 3), 4 CG-subiterations (-n2 4), 5 | ## physical constraints, i.e., setting to zero all negative pixels 6 | ## at each iteration (-pc 0.0), using as center of rotation 128.5 7 | ## instead of the default mid pixel, i.e. 128 (-c 128.5). 8 | 9 | ## N.B.: the input sinogram in this example was created with gridding 10 | ## projectors with minimal oversampling that are not centered as the 11 | ## projectors based on the distance-driven approach. This is why the 12 | ## test requires to specify the rotation center shifted by half pixel. 13 | ## It is just a minor problem regarding the implementations of these 14 | ## projectors. 15 | 16 | from __future__ import division , print_function 17 | import os 18 | 19 | os.chdir( '../algorithms' ) 20 | command = 'python -W ignore admm.py -Di ../data/ -i shepp_logan_pix0256_ang0304_sino.tif -o shepp_logan_pix0256_ang0304_sino_reco.tif -pr dist-driv -r lasso-tv -n1 3 -n2 4 -pp 1.0:1.0:1.0:1.0 -pc 0.0 -c 128.5' 21 | print( '\n\nCommand line:\n', command ) 22 | os.system( command ) 23 | -------------------------------------------------------------------------------- /scripts/test_reconstr_admm_03.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan is reconstructed with the ADMM 2 | ## Plug-and-Play, using the SPlit-Bregman non-local TV method as regularization 3 | ## (-r pp-nltv), the projectors based on a cubic B-spline tensor product 4 | ## (-pr bspline), 4 iterations (-n1 3), 4 CG-subiterations (-n2 4), 5 | ## physical constraints, i.e., setting to zero all negative pixels 6 | ## at each iteration (-pc 0.0). 7 | 8 | from __future__ import division , print_function 9 | import os 10 | 11 | os.chdir( '../algorithms' ) 12 | command = 'python -W ignore admm.py -Di ../data/ -i shepp_logan_pix0256_ang0304_sino.tif -o shepp_logan_pix0256_ang0304_sino_reco.tif -pr bspline -r pp-nltv -n1 3 -n2 4 -pp 1.0:1.0:1.0:1.0 -pc 0.0' 13 | print( '\n\nCommand line:\n', command ) 14 | os.system( command ) 15 | -------------------------------------------------------------------------------- /scripts/test_reconstr_admm_3d.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan is reconstructed with the ADMM 2 | ## Plug-and-Play, using the Split-Bregman TV method as regularization 3 | ## (-r pp-breg), the gridding projectors with minimal oversampling 4 | ## (-pr grid-kb), 4 iterations (-n1 4), 4 CG-subiterations (-n2 4), 5 | ## physical constraints, i.e., setting to zero all negative pixels 6 | ## at each iteration (-pc 0.0). 7 | 8 | from __future__ import division , print_function 9 | import os 10 | 11 | os.chdir( '../algorithms' ) 12 | command = 'python -W ignore admm.py -Di ../data/sl3d/sl3d_128_sino_ang0200/ -Do ../data/sl3d/sl3d_128_reco_ang0200/ -pr grid-kb -r lasso -n1 4 -n2 4 -pp 1.0:1.0:1.0:1.0 -pc 0.0' 13 | print( '\n\nCommand line:\n', command ) 14 | os.system( command ) 15 | -------------------------------------------------------------------------------- /scripts/test_reconstr_anal.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan with 256 pixels X 304 views is 2 | ## reconstructed with the adjoint gridding projector with minimal oversampling 3 | ## using a parzen window superimposed to the ramp filter. 4 | 5 | from __future__ import division , print_function 6 | import os 7 | 8 | os.chdir( '../algorithms' ) 9 | command = 'python -W ignore gridding_adjoint.py -Di ../data/ -i shepp_logan_pix0256_ang0304_sino.tif -f parzen' 10 | print( '\n\nCommand line:\n', command ) 11 | os.system( command ) 12 | -------------------------------------------------------------------------------- /scripts/test_reconstr_anal_3d.py: -------------------------------------------------------------------------------- 1 | ## In this test the forward projection of a 3D Shepp-Logan with 304 views 2 | ## is reconstructed with the adjoint gridding projector with minimal 3 | ## oversampling using a hanning window superimposed to the ramp filter. 4 | ## The sinograms correspond to separate files in ../data/sl3d/sl3d_256_sino_ang0304. 5 | ## The reconstructions correspond to separate files as well, saved in the newly 6 | ## created folder ../data/sl3d/sl3d_256_reco_ang0304. 7 | 8 | from __future__ import division , print_function 9 | import os 10 | 11 | os.chdir( '../algorithms' ) 12 | command = 'python -W ignore gridding_adjoint.py -Di ../data/sl3d/sl3d_128_sino_ang0200 -Do ../data/sl3d/sl3d_128_reco_ang0200 -f hanning' 13 | print( '\n\nCommand line:\n', command ) 14 | os.system( command ) 15 | -------------------------------------------------------------------------------- /scripts/test_reconstr_sir_01.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan is reconstructed with the 2 | ## Maximum-Likelihood Expectation-Maximization (-a em) the gridding 3 | ## projectors with minimal oversampling (-pr grid-kb), 100 iterations 4 | ## (-n 100). 5 | 6 | from __future__ import division , print_function 7 | import os 8 | 9 | os.chdir( '../algorithms' ) 10 | command = 'python -W ignore sir.py -a em -Di ../data/ -i shepp_logan_pix0256_ang0304_sino.tif -o shepp_logan_pix0256_ang0304_sino_reco.tif -pr grid-pswf -n 12' 11 | print( '\n\nCommand line:\n', command ) 12 | os.system( command ) 13 | -------------------------------------------------------------------------------- /scripts/test_reconstr_sir_02.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan is reconstructed with the 2 | ## Maximum-Likelihood Expectation-Maximization (-a em) the gridding 3 | ## projectors with minimal oversampling (-pr grid-kb), 100 iterations 4 | ## (-n 100). 5 | 6 | from __future__ import division , print_function 7 | import os 8 | 9 | os.chdir( '../algorithms' ) 10 | command = 'python -W ignore sir.py -a sps -Di ../data/ -i shepp_logan_pix0256_ang0304_sino.tif -o shepp_logan_pix0256_ang0304_sino_reco.tif -pr grid-pswf -r huber -hc 10.0 -n 30' 11 | print( '\n\nCommand line:\n', command ) 12 | os.system( command ) 13 | -------------------------------------------------------------------------------- /scripts/test_reconstr_sir_3d_01.py: -------------------------------------------------------------------------------- 1 | ## In this test a volume of sinograms of Shepp-Logan is reconstructed with the 2 | ## Maximum-Likelihood Expectation-Maximization (-a em) the gridding 3 | ## projectors with minimal oversampling (-pr grid-kb), 10 iterations 4 | ## (-n 100). 5 | 6 | from __future__ import division , print_function 7 | import os 8 | 9 | os.chdir( '../algorithms' ) 10 | command = 'python -W ignore sir.py -a em -Di ../data/sl3d/sl3d_128_sino_ang0200/ -Do ../data/sl3d/sl3d_128_reco_ang0200/ -pr grid-pswf -hc 10.0 -n 10' 11 | print( '\n\nCommand line:\n', command ) 12 | os.system( command ) 13 | -------------------------------------------------------------------------------- /scripts/test_reconstr_sir_3d_02.py: -------------------------------------------------------------------------------- 1 | ## In this test a volume of sinograms of Shepp-Logan is reconstructed with the 2 | ## Separable Paraboloidal Surrogate (-a sps) the gridding 3 | ## projectors with minimal oversampling (-pr grid-kb), 30 iterations 4 | ## (-n 100). 5 | 6 | from __future__ import division , print_function 7 | import os 8 | 9 | os.chdir( '../algorithms' ) 10 | command = 'python -W ignore sir.py -a sps -Di ../data/sl3d/sl3d_128_sino_ang0200/ -Do ../data/sl3d/sl3d_128_reco_ang0200/ -pr grid-pswf -r huber -hc 0.1 -n 10' 11 | print( '\n\nCommand line:\n', command ) 12 | os.system( command ) 13 | -------------------------------------------------------------------------------- /scripts/test_reconstr_sirt.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan is reconstructed with the ADMM 2 | ## Plug-and-Play, using the Split-Bregman TV method as regularization 3 | ## (-r pp-breg), the gridding projectors with minimal oversampling 4 | ## (-pr grid-kb), 4 iterations (-n1 4), 4 CG-subiterations (-n2 4), 5 | ## physical constraints, i.e., setting to zero all negative pixels 6 | ## at each iteration (-pc 0.0). 7 | 8 | from __future__ import division , print_function 9 | import os 10 | 11 | os.chdir( '../algorithms' ) 12 | command = 'python -W ignore art.py -a sirt -Di ../data/ -i shepp_logan_pix0256_ang0304_sino.tif -o shepp_logan_pix0256_ang0304_sino_reco.tif -pr grid-kb -n 50 -pc 0.0' 13 | print( '\n\nCommand line:\n', command ) 14 | os.system( command ) 15 | -------------------------------------------------------------------------------- /scripts/test_reconstr_sirt_3d.py: -------------------------------------------------------------------------------- 1 | ## In this test a sinogram of Shepp-Logan is reconstructed with the ADMM 2 | ## Plug-and-Play, using the Split-Bregman TV method as regularization 3 | ## (-r pp-breg), the gridding projectors with minimal oversampling 4 | ## (-pr grid-kb), 4 iterations (-n1 4), 4 CG-subiterations (-n2 4), 5 | ## physical constraints, i.e., setting to zero all negative pixels 6 | ## at each iteration (-pc 0.0). 7 | 8 | from __future__ import division , print_function 9 | import os 10 | 11 | os.chdir( '../algorithms' ) 12 | command = 'python -W ignore art.py -a sirt -Di ../data/sl3d/sl3d_128_sino_ang0200/ -Do ../data/sl3d/sl3d_128_reco_ang0200/ -pr grid-kb -n50 -pc 0.0' 13 | print( '\n\nCommand line:\n', command ) 14 | os.system( command ) 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | ########################################################################### 2 | ########################################################################### 3 | #### #### 4 | #### COMPILE ALL SUBROUTINES IN C #### 5 | #### #### 6 | ########################################################################### 7 | ########################################################################### 8 | 9 | ## Usage: 10 | ## MODE-1: python setup.py --> install all subroutines in C 11 | ## MODE-2: python setup.py 1 --> delete all compiles files '.o', '.so' and all 12 | ## folders 'debug/', 'build/' and '__pycache__' 13 | 14 | 15 | 16 | 17 | ## Python packages 18 | from __future__ import division , print_function 19 | import os 20 | import sys 21 | import shutil 22 | 23 | 24 | 25 | 26 | ## Choose whether to use the script in MODE-1 or in MODE-2 27 | if len( sys.argv ) == 1: 28 | compile = True 29 | else: 30 | compile = False 31 | 32 | 33 | ## Remove all compile files and related folders 34 | curr_dir = os.getcwd() 35 | 36 | for path , subdirs , files in os.walk( './' ): 37 | for i in range( len( subdirs ) ): 38 | folderin = subdirs[i] 39 | if folderin == 'build' or folderin == 'debug': 40 | shutil.rmtree( os.path.join( path , folderin ) ) 41 | if folderin == '__pycache__': 42 | shutil.rmtree( os.path.join( path , folderin ) ) 43 | 44 | for i in range( len( files ) ): 45 | filein = files[i] 46 | 47 | if filein.endswith( '.pyc' ) is True or \ 48 | filein.endswith( '.pyo' ) is True or \ 49 | filein.endswith( '.o' ) is True or \ 50 | filein.endswith( '.so' ) is True or \ 51 | filein.endswith( '.swp' ) is True or \ 52 | filein.endswith( '.swo' ) is True or \ 53 | filein.endswith( '.swn' ) is True or \ 54 | filein.endswith( '~' ) is True: 55 | os.remove( os.path.join( path , filein ) ) 56 | 57 | 58 | ## Compile all subroutines in C if enabled 59 | cpath = 'common/operators/' 60 | 61 | if compile is True: 62 | path = cpath + 'regularization_module/' 63 | os.chdir( path ) 64 | os.system( 'python compile.py' ) 65 | os.chdir( curr_dir ) 66 | 67 | path = cpath + 'regularization_module/pymodule_split_bregman_nltv/' 68 | os.chdir( path ) 69 | os.system( 'python compile.py' ) 70 | os.chdir( curr_dir ) 71 | 72 | path = cpath + 'gridrec_module/pymodule_gridrec_v4/' 73 | os.chdir( path ) 74 | os.system( 'python compile.py' ) 75 | os.chdir( curr_dir ) 76 | 77 | path = cpath + 'bspline_module/pymodule_genradon/' 78 | os.chdir( path ) 79 | os.system( 'python compile.py' ) 80 | os.chdir( curr_dir ) 81 | 82 | path = cpath + 'radon_module/pymodule_radon_pixel_driven/' 83 | os.chdir( path ) 84 | os.system( 'python compile.py' ) 85 | os.chdir( curr_dir ) 86 | 87 | path = cpath + 'radon_module/pymodule_radon_ray_driven/' 88 | os.chdir( path ) 89 | os.system( 'python compile.py' ) 90 | os.chdir( curr_dir ) 91 | 92 | path = cpath + 'radon_module/pymodule_radon_distance_driven/' 93 | os.chdir( path ) 94 | os.system( 'python compile.py' ) 95 | os.chdir( curr_dir ) 96 | 97 | path = cpath + 'radon_module/pymodule_radon_slant_stacking/' 98 | os.chdir( path ) 99 | os.system( 'python compile.py' ) 100 | os.chdir( curr_dir ) 101 | 102 | os.chdir( 'data/' ) 103 | os.system( 'unzip sl3d.zip' ) 104 | os.system( 'rm sl3d.zip' ) 105 | 106 | 107 | else: 108 | os.chdir( curr_dir ) 109 | os.chdir( 'data/' ) 110 | os.system( 'zip -r sl3d.zip sl3d/' ) 111 | shutil.rmtree( 'sl3d' ) 112 | --------------------------------------------------------------------------------