diff options
-rw-r--r-- | Readme.md | 82 | ||||
-rw-r--r-- | data/phantom_bone512.mat | bin | 0 -> 408035 bytes | |||
-rw-r--r-- | data/sino3D_dendrites.mat | bin | 0 -> 27797233 bytes | |||
-rw-r--r-- | data/sino_basalt.mat | bin | 0 -> 1109887 bytes | |||
-rw-r--r-- | demo/Demo1.m | 160 | ||||
-rw-r--r-- | demo/Demo2.m | 156 | ||||
-rw-r--r-- | demo/DemoRD1.m | 99 | ||||
-rw-r--r-- | demo/DemoRD2.m | 130 | ||||
-rw-r--r-- | license.txt | 27 | ||||
-rw-r--r-- | main_func/FISTA_REC.m | 338 | ||||
-rw-r--r-- | main_func/FISTA_TV.c | 331 | ||||
-rw-r--r-- | main_func/LLT_model.c | 431 | ||||
-rw-r--r-- | main_func/SplitBregman_TV.c | 346 | ||||
-rw-r--r-- | main_func/compile_mex.m | 4 | ||||
-rw-r--r-- | main_func/studentst.m | 47 | ||||
-rw-r--r-- | readme.doc | bin | 0 -> 21504 bytes | |||
-rw-r--r-- | readme.pdf | bin | 0 -> 61855 bytes | |||
-rw-r--r-- | supp/RMSE.m | 7 | ||||
-rw-r--r-- | supp/add_wedges.m | 30 | ||||
-rw-r--r-- | supp/filtersinc.m | 28 | ||||
-rw-r--r-- | supp/my_red_yellowMAP.mat | bin | 0 -> 1761 bytes | |||
-rw-r--r-- | supp/ssim_index.m | 181 | ||||
-rw-r--r-- | supp/subplot_tight.m | 1 | ||||
-rw-r--r-- | supp/zing_rings_add.m | 84 | ||||
-rw-r--r-- | ~$readme.doc | bin | 0 -> 162 bytes |
25 files changed, 2482 insertions, 0 deletions
diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..a530c72 --- /dev/null +++ b/Readme.md @@ -0,0 +1,82 @@ +# FISTA Reconstruction (Daniil Kazanteev) + +# General Description + +Software for reconstructing 2D/3D x-ray and neutron tomography datasets. The data can be undersampled, of poor contrast, noisy, and contain various artifacts. This is Matlab and C-omp implementation of iterative model-based algorithms with unconventional data fidelities and with various regularization terms (TV and higher-order LLT). The main optimization problem is solved using FISTA framework [1]. The presented algorithms are FBP, FISTA (Least-Squares), FISTA-LS-TV(LS-Total Variation), FISTA-GH(Group-Huber)-TV, and FISTA-Student-TV. More information about the algorithms can be found in papers [2,3]. Please cite [2] if the algorithms or data used in your research. + +## Requirements/Dependencies: + + * MATLAB (www.mathworks.com/products/matlab/) + * ASTRA toolbox (https://github.com/astra-toolbox/astra-toolbox) + * C/C++ compiler (run compile_mex in Matlab first to compile C-functions) + +## Package Contents: + +### Demos: + * Demo1: Synthetic phantom reconstruction with noise, stripes and zingers + * Demo2: Synthetic phantom reconstruction with noise, stripes, zingers, and the missing wedges + * DemoRD1: Real data reconstruction from sino_basalt.mat (see Data) + * DemoRD2: Real data reconstruction from sino3D_dendrites.mat (see Data) + +### Data: + * phantom_bone512.mat - a synthetic 2D phantom obtained from high-resolution x-ray scan + * sino_basalt.mat – 2D neutron (PSI) tomography sinogram (slice across a pack of basalt beads) + * sino3D_dendrites.mat – 3D (20 slices) x-ray synchrotron dataset (DLS) of growing dendrites + +### Main modules: + + * FISTA_REC.m – Matlab function to perform FISTA-based reconstruction + * FISTA_TV.c – C-omp function to solve for the weighted TV term using FISTA + * SplitBregman_TV.c – C-omp function to solve for the weighted TV term using Split-Bregman + * LLT_model.c – C-omp function to solve for the weighted LLT [3] term using explicit scheme + * studentst.m – Matlab function to calculate Students t penalty with 'auto-tuning' + +### Supplementary: + + * zing_rings_add.m Matlab script to generate proj. data, add noise, zingers and stripes + * add_wedges.m script to add the missing wedge to existing sinogram + * my_red_yellowMAP.mat – nice colormap for the phantom + * RMSE.m – Matlab function to calculate Root Mean Square Error + * subplot_tight – visualizing better subplots + * ssim_index – ssim calculation + +### Practical advices: + * Full 3D reconstruction provides much better results than 2D. In the case of ring artifacts, 3D is almost necessary + * Depending on data it is better to use TV-LLT combination in order to achieve piecewise-smooth solution. The DemoRD2 shows one possible example when smoother surfaces required. + * L (Lipshitz constant) if tweaked can lead to faster convergence than automatic values + * Convergence is normally much faster when using Fourier filtering before backprojection + * Students’t penalty is generally quite stable in practice, however some tweaking of L might require for the real data + * You can choose between SplitBregman-TV and FISTA-TV modules. The former is slower but requires less memory (for 3D volume U it can take up to 6 x U), the latter is faster but can take more memory (for 3D volume U it can take up to 11 x U). Also the SplitBregman is quite good in improving contrast. + + ### Compiling: + + It is very important to check that OMP support is activated for the optimal use of all CPU cores. +#### Windows + In Windows enable OMP support, e.g.: +edit C:\Users\Username\AppData\Roaming\MathWorks\MATLAB\R2012b\mexopts.bat +In the file, edit 'OPTIMFLAGS' line as shown below (add /openmp entry at the end): +OPTIMFLAGS = /O2 /Oy- /DNDEBUG /openmp +define and set the number of CPU cores +PROC = feature('numCores'); +PROCS = num2str(PROC); +setenv('OMP_NUM_THREADS', PROCS); % you can check how many CPU's by: getenv +OMP_NUM_THREADS + +#### Linux +In Linux in terminal: export OMP_NUM_THREADS = (the numer of CPU cores) +then run Matlab as normal +to compile with OMP support from Matlab in Windows run: +mex *.cpp CFLAGS="\$CFLAGS -fopenmp -Wall" LDFLAGS="\$LDFLAGS -fopenmp" +to compile with OMP support from Matlab in Linux ->rename *cpp to *c and compile: +mex *.c CFLAGS="\$CFLAGS -fopenmp -Wall" LDFLAGS="\$LDFLAGS -fopenmp" + + +### References: +[1] Beck, A. and Teboulle, M., 2009. A fast iterative shrinkage-thresholding algorithm for linear inverse problems. SIAM journal on imaging sciences,2(1), pp.183-202. +[2] Kazantsev, D., Bleichrodt, F., van Leeuwen, T., Kaestner, A., Withers, P.J., Batenburg, K.J., 2017. A novel tomographic reconstruction method based on the robust Student's t function for suppressing data outliers, IEEE TRANSACTIONS ON COMPUTATIONAL IMAGING (to appear) +[3] Lysaker, M., Lundervold, A. and Tai, X.C., 2003. Noise removal using fourth-order partial differential equation with applications to medical magnetic resonance images in space and time. IEEE Transactions on image processing, 12(12), pp.1579-1590. +[4] Paleo, P. and Mirone, A., 2015. Ring artifacts correction in compressed sensing tomographic reconstruction. Journal of synchrotron radiation, 22(5), pp.1268-1278. +[5] Sidky, E.Y., Jakob, H.J. and Pan, X., 2012. Convex optimization problem prototyping for image reconstruction in computed tomography with the Chambolle-Pock algorithm. Physics in medicine and biology, 57(10), p.3065. + +D. Kazantsev / Harwell Campus / 16.03.17 +any questions/comments please e-mail to daniil.kazantsev@manchester.ac.uk diff --git a/data/phantom_bone512.mat b/data/phantom_bone512.mat Binary files differnew file mode 100644 index 0000000..ae3c7d8 --- /dev/null +++ b/data/phantom_bone512.mat diff --git a/data/sino3D_dendrites.mat b/data/sino3D_dendrites.mat Binary files differnew file mode 100644 index 0000000..dc1400d --- /dev/null +++ b/data/sino3D_dendrites.mat diff --git a/data/sino_basalt.mat b/data/sino_basalt.mat Binary files differnew file mode 100644 index 0000000..164d144 --- /dev/null +++ b/data/sino_basalt.mat diff --git a/demo/Demo1.m b/demo/Demo1.m new file mode 100644 index 0000000..08d46e1 --- /dev/null +++ b/demo/Demo1.m @@ -0,0 +1,160 @@ +% Demonstration of tomographic reconstruction from noisy and corrupted by +% artifacts undersampled projection data using Students't penalty +% Optimisation problem is solved using FISTA algorithm (see Beck & Teboulle) + +% see ReadMe file for instructions +clear all +close all + +% adding paths +addpath('data/'); +addpath('main_func/'); +addpath('supp/'); + +load phantom_bone512.mat % load the phantom +load my_red_yellowMAP.mat % load the colormap +% load sino1.mat; % load noisy sinogram + +N = 512; % the size of the tomographic image NxN +theta = 1:1:180; % acquisition angles (in parallel beam from 0 to Pi) +theta_rad = theta*(pi/180); % conversion to radians +P = 2*ceil(N/sqrt(2))+1; % the size of the detector array +ROI = find(phantom > 0); + +zing_rings_add; % generating data, adding zingers and stripes + +%% +fprintf('%s\n', 'Direct reconstruction using FBP...'); +FBP_1 = iradon(sino_zing_rings', theta, N); + +fprintf('%s %.4f\n', 'RMSE for FBP reconstruction:', RMSE(FBP_1(:), phantom(:))); + +figure(1); +subplot_tight(1,2,1, [0.05 0.05]); imshow(FBP_1,[0 0.6]); title('FBP reconstruction of noisy and corrupted by artifacts sinogram'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); imshow((phantom - FBP_1).^2,[0 0.1]); title('residual: (ideal phantom - FBP)^2'); colorbar; +colormap(cmapnew); +%% +fprintf('%s\n', 'Reconstruction using FISTA-LS without regularization...'); +clear params +% define parameters +params.sino = sino_zing_rings; +params.N = N; % image size +params.angles = theta_rad; % angles in radians +params.iterFISTA = 180; %max number of outer iterations +params.X_ideal = phantom; % ideal phantom +params.ROI = ROI; % phantom region-of-interest +params.show = 0; % visualize reconstruction on each iteration +params.slice = 1; params.maxvalplot = 0.6; +params.weights = Dweights; % statistical weighting +tic; [X_FISTA, error_FISTA, obj_FISTA, sinoFISTA] = FISTA_REC(params); toc; + +fprintf('%s %.4f\n', 'Min RMSE for FISTA-LS reconstruction is:', min(error_FISTA(:))); + +figure(2); clf +%set(gcf, 'Position', get(0,'Screensize')); +subplot_tight(1,2,1, [0.05 0.05]); imshow(X_FISTA,[0 0.6]); title('FISTA-LS reconstruction'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); imshow((phantom - X_FISTA).^2,[0 0.1]); title('residual'); colorbar; +colormap(cmapnew); +figure(3); clf +subplot_tight(1,2,1, [0.05 0.05]); plot(error_FISTA); title('RMSE plot'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); plot(obj_FISTA); title('Objective plot'); colorbar; +colormap(cmapnew); +%% +fprintf('%s\n', 'Reconstruction using FISTA-LS-TV...'); +clear params +% define parameters +params.sino = sino_zing_rings; +params.N = N; % image size +params.angles = theta_rad; % angles in radians +params.iterFISTA = 200; % max number of outer iterations +params.lambdaTV = 5.39e-05; % regularization parameter for TV problem +params.tol = 1.0e-04; % tolerance to terminate TV iterations +params.iterTV = 20; % the max number of TV iterations +params.X_ideal = phantom; % ideal phantom +params.ROI = ROI; % phantom region-of-interest +params.weights = Dweights; % statistical weighting +params.show = 0; % visualize reconstruction on each iteration +params.slice = 1; params.maxvalplot = 0.6; +tic; [X_FISTA_TV, error_FISTA_TV, obj_FISTA_TV, sinoFISTA_TV] = FISTA_REC(params); toc; + +fprintf('%s %.4f\n', 'Min RMSE for FISTA-LS-TV reconstruction is:', min(error_FISTA_TV(:))); + +figure(4); clf +subplot_tight(1,2,1, [0.05 0.05]); imshow(X_FISTA_TV,[0 0.6]); title('FISTA-LS-TV reconstruction'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); imshow((phantom - X_FISTA_TV).^2,[0 0.1]); title('residual'); colorbar; +colormap(cmapnew); +figure(5); clf +subplot_tight(1,2,1, [0.05 0.05]); plot(error_FISTA_TV); title('RMSE plot'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); plot(obj_FISTA_TV); title('Objective plot'); colorbar; +colormap(cmapnew); +%% +fprintf('%s\n', 'Reconstruction using FISTA-GH-TV...'); +clear params +% define parameters +params.sino = sino_zing_rings; +params.N = N; % image size +params.angles = theta_rad; % angles in radians +params.iterFISTA = 60; % max number of outer iterations +params.lambdaTV = 0.002526; % regularization parameter for TV problem +params.tol = 1.0e-04; % tolerance to terminate TV iterations +params.iterTV = 20; % the max number of TV iterations +params.X_ideal = phantom; % ideal phantom +params.ROI = ROI; % phantom region-of-interest +params.weights = Dweights; % statistical weighting +params.lambdaR_L1 = 0.002; % parameter to sparsify the "rings vector" +params.show = 0; % visualize reconstruction on each iteration +params.slice = 1; params.maxvalplot = 0.6; +tic; [X_FISTA_GH_TV, error_FISTA_GH_TV, obj_FISTA_GH_TV, sinoFISTA_GH_TV] = FISTA_REC(params); toc; + +fprintf('%s %.4f\n', 'Min RMSE for FISTA-GH-TV reconstruction is:', min(error_FISTA_GH_TV(:))); + +figure(6); clf +subplot_tight(1,2,1, [0.05 0.05]); imshow(X_FISTA_GH_TV,[0 0.6]); title('FISTA-GH-TV reconstruction'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]);imshow((phantom - X_FISTA_GH_TV).^2,[0 0.1]); title('residual'); colorbar; +colormap(cmapnew); + +figure(7); clf +subplot_tight(1,2,1, [0.05 0.05]); plot(error_FISTA_GH_TV); title('RMSE plot'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); plot(obj_FISTA_GH_TV); title('Objective plot'); colorbar; +colormap(cmapnew); +%% +fprintf('%s\n', 'Reconstruction using FISTA-Student-TV...'); +clear params +% define parameters +params.sino = sino_zing_rings; +params.N = N; % image size +params.angles = theta_rad; % angles in radians +params.iterFISTA = 67; % max number of outer iterations +%params.L_const = 80000; % Lipshitz constant (can be chosen manually to accelerate convergence) +params.lambdaTV = 0.00152; % regularization parameter for TV problem +params.tol = 1.0e-04; % tolerance to terminate TV iterations +params.iterTV = 20; % the max number of TV iterations +params.X_ideal = phantom; % ideal phantom +params.ROI = ROI; % phantom region-of-interest +params.weights = Dweights; % statistical weighting +params.fidelity = 'student'; % selecting students t fidelity +params.show = 0; % visualize reconstruction on each iteration +params.slice = 1; params.maxvalplot = 0.6; +tic; [X_FISTA_student_TV, error_FISTA_student_TV, obj_FISTA_student_TV, sinoFISTA_student_TV] = FISTA_REC(params); toc; + +fprintf('%s %.4f\n', 'Min RMSE for FISTA-Student-TV reconstruction is:', min(error_FISTA_student_TV(:))); + +figure(8); +set(gcf, 'Position', get(0,'Screensize')); +subplot_tight(1,2,1, [0.05 0.05]); imshow(X_FISTA_student_TV,[0 0.6]); title('FISTA-Student-TV reconstruction'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); imshow((phantom - X_FISTA_student_TV).^2,[0 0.1]); title('residual'); colorbar; +colormap(cmapnew); + +figure(9); +subplot_tight(1,2,1, [0.05 0.05]); plot(error_FISTA_student_TV); title('RMSE plot'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); plot(obj_FISTA_student_TV); title('Objective plot'); colorbar; +colormap(cmapnew); +%% +% print all RMSE's +fprintf('%s\n', '--------------------------------------------'); +fprintf('%s %.4f\n', 'RMSE for FBP reconstruction:', RMSE(FBP_2(:), phantom(:))); +fprintf('%s %.4f\n', 'Min RMSE for FISTA-LS reconstruction:', min(error_FISTA(:))); +fprintf('%s %.4f\n', 'Min RMSE for FISTA-LS-TV reconstruction:', min(error_FISTA_TV(:))); +fprintf('%s %.4f\n', 'Min RMSE for FISTA-GH-TV reconstruction:', min(error_FISTA_GH_TV(:))); +fprintf('%s %.4f\n', 'Min RMSE for FISTA-Student-TV reconstruction:', min(error_FISTA_student_TV(:))); +%
\ No newline at end of file diff --git a/demo/Demo2.m b/demo/Demo2.m new file mode 100644 index 0000000..3c1592c --- /dev/null +++ b/demo/Demo2.m @@ -0,0 +1,156 @@ +% Demonstration of tomographic reconstruction from noisy and corrupted by +% artifacts undersampled projection data using Students t penalty +% This is the missing wedge demo, run it after DemoFISTA_StudT + +% see ReadMe file for instructions +% clear all +% close all + +load phantom_bone512.mat % load the phantom +load my_red_yellowMAP.mat % load the colormap +% load sino1.mat; % load noisy sinogram + +N = 512; % the size of the tomographic image NxN +theta = 1:1:180; % acquisition angles (in parallel beam from 0 to Pi) +theta_rad = theta*(pi/180); % conversion to radians +P = 2*ceil(N/sqrt(2))+1; % the size of the detector array +ROI = find(phantom > 0.0); + +add_wedges % apply the missing wedge mask + +%% +fprintf('%s\n', 'Direct reconstruction using FBP...'); +FBP_1 = iradon(MW_sino_artifacts', theta, N); + +fprintf('%s %.4f\n', 'RMSE for FBP reconstruction:', RMSE(FBP_1(:), phantom(:))); + +figure(1); +% set(gcf, 'Position', get(0,'Screensize')); +subplot_tight(1,2,1, [0.05 0.05]); imshow(FBP_1,[-2 0.8]); title('FBP reconstruction of noisy and corrupted by artifacts sinogram'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); imshow((phantom - FBP_1).^2,[0 0.1]); title('residual: (ideal phantom - FBP)^2'); colorbar; +colormap(cmapnew); +%% +fprintf('%s\n', 'Reconstruction using FISTA-LS without regularization...'); +clear params +% define parameters +params.sino = MW_sino_artifacts; +params.N = N; % image size +params.angles = theta_rad; % angles in radians +params.iterFISTA = 132; %max number of outer iterations +params.X_ideal = phantom; % ideal phantom +params.ROI = ROI; % phantom region-of-interest +params.show = 0; % visualize reconstruction on each iteration +params.slice = 1; params.maxvalplot = 0.6; +params.weights = Dweights; % statistical weighting +tic; [X_FISTA, error_FISTA, obj_FISTA, sinoFISTA] = FISTA_REC(params); toc; + +fprintf('%s %.4f\n', 'Min RMSE for FISTA-LS reconstruction:', min(error_FISTA(:))); + +figure(2); clf +%set(gcf, 'Position', get(0,'Screensize')); +subplot_tight(1,2,1, [0.05 0.05]); imshow(X_FISTA,[0 0.6]); title('FISTA-LS reconstruction'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); imshow((phantom - X_FISTA).^2,[0 0.1]); title('residual'); colorbar; +colormap(cmapnew); +figure(3); clf +subplot_tight(1,2,1, [0.05 0.05]); plot(error_FISTA); title('RMSE plot'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); plot(obj_FISTA); title('Objective plot'); colorbar; +colormap(cmapnew); +%% +fprintf('%s\n', 'Reconstruction using FISTA-LS-TV...'); +clear params +% define parameters +params.sino = MW_sino_artifacts; +params.N = N; % image size +params.angles = theta_rad; % angles in radians +params.iterFISTA = 200; % max number of outer iterations +params.lambdaTV = 5.39e-05; % regularization parameter for TV problem +params.tol = 1.0e-04; % tolerance to terminate TV iterations +params.iterTV = 20; % the max number of TV iterations +params.X_ideal = phantom; % ideal phantom +params.ROI = ROI; % phantom region-of-interest +params.weights = Dweights; % statistical weighting +params.show = 0; % visualize reconstruction on each iteration +params.slice = 1; params.maxvalplot = 0.6; +tic; [X_FISTA_TV, error_FISTA_TV, obj_FISTA_TV, sinoFISTA_TV] = FISTA_REC(params); toc; + +fprintf('%s %.4f\n', 'Min RMSE for FISTA-LS-TV reconstruction:', min(error_FISTA_TV(:))); + +figure(4); clf +subplot_tight(1,2,1, [0.05 0.05]); imshow(X_FISTA_TV,[0 0.6]); title('FISTA-LS-TV reconstruction'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); imshow((phantom - X_FISTA_TV).^2,[0 0.1]); title('residual'); colorbar; +colormap(cmapnew); +figure(5); clf +subplot_tight(1,2,1, [0.05 0.05]); plot(error_FISTA_TV); title('RMSE plot'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); plot(obj_FISTA_TV); title('Objective plot'); colorbar; +colormap(cmapnew); +%% +fprintf('%s\n', 'Reconstruction using FISTA-GH-TV...'); +clear params +% define parameters +params.sino = MW_sino_artifacts; +params.N = N; % image size +params.angles = theta_rad; % angles in radians +params.iterFISTA = 250; % max number of outer iterations +params.lambdaTV = 0.0019; % regularization parameter for TV problem +params.tol = 1.0e-04; % tolerance to terminate TV iterations +params.iterTV = 20; % the max number of TV iterations +params.X_ideal = phantom; % ideal phantom +params.ROI = ROI; % phantom region-of-interest +params.weights = Dweights; % statistical weighting +params.lambdaR_L1 = 0.002; % parameter to sparsify the "rings vector" +params.show = 0; % visualize reconstruction on each iteration +params.slice = 1; params.maxvalplot = 0.6; +tic; [X_FISTA_GH_TV, error_FISTA_GH_TV, obj_FISTA_GH_TV, sinoFISTA_GH_TV] = FISTA_REC(params); toc; + +fprintf('%s %.4f\n', 'Min RMSE for FISTA-GH-TV reconstruction:', min(error_FISTA_GH_TV(:))); + +figure(6); clf +subplot_tight(1,2,1, [0.05 0.05]); imshow(X_FISTA_GH_TV,[0 0.6]); title('FISTA-GH-TV reconstruction'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]);imshow((phantom - X_FISTA_GH_TV).^2,[0 0.1]); title('residual'); colorbar; +colormap(cmapnew); + +figure(7); clf +subplot_tight(1,2,1, [0.05 0.05]); plot(error_FISTA_GH_TV); title('RMSE plot'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); plot(obj_FISTA_GH_TV); title('Objective plot'); colorbar; +colormap(cmapnew); +%% +fprintf('%s\n', 'Reconstruction using FISTA-Student-TV...'); +clear params +% define parameters +params.sino = MW_sino_artifacts; +params.N = N; % image size +params.angles = theta_rad; % angles in radians +params.iterFISTA = 80; % max number of outer iterations +% params.L_const = 80000; % Lipshitz constant (can be chosen manually to accelerate convergence) +params.lambdaTV = 0.0016; % regularization parameter for TV problem +params.tol = 1.0e-04; % tolerance to terminate TV iterations +params.iterTV = 20; % the max number of TV iterations +params.X_ideal = phantom; % ideal phantom +params.ROI = ROI; % phantom region-of-interest +params.weights = Dweights; % statistical weighting +params.fidelity = 'student'; % selecting students t fidelity +params.show = 0; % visualize reconstruction on each iteration +params.slice = 1; params.maxvalplot = 0.6; +tic; [X_FISTA_student_TV, error_FISTA_student_TV, obj_FISTA_student_TV, sinoFISTA_student_TV] = FISTA_REC(params); toc; + +fprintf('%s %.4f\n', 'Min RMSE for FISTA-Student-TV reconstruction:', min(error_FISTA_student_TV(:))); + +figure(8); +set(gcf, 'Position', get(0,'Screensize')); +subplot_tight(1,2,1, [0.05 0.05]); imshow(X_FISTA_student_TV,[0 0.6]); title('FISTA-Student-TV reconstruction'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); imshow((phantom - X_FISTA_student_TV).^2,[0 0.1]); title('residual'); colorbar; +colormap(cmapnew); + +figure(9); +subplot_tight(1,2,1, [0.05 0.05]); plot(error_FISTA_student_TV); title('RMSE plot'); colorbar; +subplot_tight(1,2,2, [0.05 0.05]); plot(obj_FISTA_student_TV); title('Objective plot'); colorbar; +colormap(cmapnew); +%% +% print all RMSE's +fprintf('%s\n', '--------------------------------------------'); +fprintf('%s %.4f\n', 'RMSE for FBP reconstruction:', RMSE(FBP_2(:), phantom(:))); +fprintf('%s %.4f\n', 'Min RMSE for FISTA-LS reconstruction:', min(error_FISTA(:))); +fprintf('%s %.4f\n', 'Min RMSE for FISTA-LS-TV reconstruction:', min(error_FISTA_TV(:))); +fprintf('%s %.4f\n', 'Min RMSE for FISTA-GH-TV reconstruction:', min(error_FISTA_GH_TV(:))); +fprintf('%s %.4f\n', 'Min RMSE for FISTA-Student-TV reconstruction:', min(error_FISTA_student_TV(:))); +%
\ No newline at end of file diff --git a/demo/DemoRD1.m b/demo/DemoRD1.m new file mode 100644 index 0000000..9a43cb5 --- /dev/null +++ b/demo/DemoRD1.m @@ -0,0 +1,99 @@ +% Demonstration of tomographic reconstruction from neutron tomography +% dataset (basalt sample) using Student t data fidelity +clear all +close all + +% adding paths +addpath('data/'); +addpath('main_func/'); +addpath('supp/'); + +load('sino_basalt.mat') % load real neutron data + +size_det = size(sino_basalt, 1); % detector size +angSize = size(sino_basalt,2); % angles dim +recon_size = 650; % reconstruction size + +FBP = iradon(sino_basalt, rad2deg(angles),recon_size); +figure; imshow(FBP , [0, 0.45]); title ('FBP reconstruction'); + +%% +fprintf('%s\n', 'Reconstruction using FISTA-LS without regularization...'); +clear params +params.sino = sino_basalt'; +params.N = recon_size; +params.angles = angles; +params.iterFISTA = 50; +params.show = 0; +params.maxvalplot = 0.6; params.slice = 1; + +tic; [X_fista] = FISTA_REC(params); toc; +figure; imshow(X_fista , [0, 0.45]); title ('FISTA-LS reconstruction'); +%% +fprintf('%s\n', 'Reconstruction using FISTA-LS-TV...'); +clear params +params.sino = sino_basalt'; +params.N = recon_size; +params.angles = angles; +params.iterFISTA = 150; +params.lambdaTV = 0.0003; % TV regularization parameter +params.tol = 1.0e-04; +params.iterTV = 20; +params.show = 1; +params.maxvalplot = 0.6; params.slice = 1; + +tic; [X_fista_TV] = FISTA_REC(params); toc; +figure; imshow(X_fista_TV , [0, 0.45]); title ('FISTA-LS-TV reconstruction'); +%% +%% +fprintf('%s\n', 'Reconstruction using FISTA-GH-TV...'); +clear params +params.sino = sino_basalt'; +params.N = recon_size; +params.angles = angles; +params.iterFISTA = 350; +params.lambdaTV = 0.0003; % TV regularization parameter +params.tol = 1.0e-04; +params.iterTV = 20; +params.lambdaR_L1 = 0.001; % Soft-Thresh L1 ring variable parameter +params.show = 1; +params.maxvalplot = 0.6; params.slice = 1; + +tic; [X_fista_GH_TV] = FISTA_REC(params); toc; +figure; imshow(X_fista_GH_TV , [0, 0.45]); title ('FISTA-GH-TV reconstruction'); +%% +%% +fprintf('%s\n', 'Reconstruction using FISTA-Student-TV...'); +clear params +params.sino = sino_basalt'; +params.N = recon_size; +params.angles = angles; +params.iterFISTA = 350; +params.L_const = 7000; % Lipshitz constant +params.lambdaTV = 0.0003; % TV regularization parameter +params.tol = 1.0e-04; +params.iterTV = 20; +params.fidelity = 'student'; % choosing Student t penalty +params.show = 1; +params.maxvalplot = 0.6; params.slice = 1; + +tic; [X_fistaStudentTV] = FISTA_REC(params); toc; +figure; imshow(X_fistaStudentTV , [0, 0.45]); title ('FISTA-Student-TV reconstruction'); +%% + +fprintf('%s\n', 'Segmentation using OTSU method ...'); +level = graythresh(X_fista); +Segm_FISTA = im2bw(X_fista,level); +figure; imshow(Segm_FISTA, []); title ('Segmented FISTA-LS reconstruction'); + +level = graythresh(X_fista_TV); +Segm_FISTA_TV = im2bw(X_fista_TV,level); +figure; imshow(Segm_FISTA_TV, []); title ('Segmented FISTA-LS-TV reconstruction'); + +level = graythresh(X_fista_GH_TV); +BW_FISTA_GH_TV = im2bw(X_fista_GH_TV,level); +figure; imshow(BW_FISTA_GH_TV, []); title ('Segmented FISTA-GH-TV reconstruction'); + +level = graythresh(X_fistaStudentTV); +BW_FISTA_Student_TV = im2bw(X_fistaStudentTV,level); +figure; imshow(BW_FISTA_Student_TV, []); title ('Segmented FISTA-Student-LS reconstruction');
\ No newline at end of file diff --git a/demo/DemoRD2.m b/demo/DemoRD2.m new file mode 100644 index 0000000..a8ac2ca --- /dev/null +++ b/demo/DemoRD2.m @@ -0,0 +1,130 @@ +% Demonstration of tomographic 3D reconstruction from X-ray synchrotron +% dataset (dendrites) using various data fidelities +% clear all +% close all +% +% % adding paths + addpath('data/'); + addpath('main_func/'); + addpath('supp/'); + +load('sino3D_dendrites.mat') % load 3D normalized sinogram +angles_rad = angles*(pi/180); % conversion to radians + +angSize = size(Sino3D,1); % angles dim +size_det = size(Sino3D, 2); % detector size +recon_size = 850; % reconstruction size + +FBP = iradon(Sino3D(:,:,10)', angles,recon_size); +figure; imshow(FBP , [0, 3]); title ('FBP reconstruction'); + +%% +fprintf('%s\n', 'Reconstruction using FISTA-LS without regularization...'); +clear params +params.sino = Sino3D; +params.N = recon_size; +params.angles = angles_rad; +params.iterFISTA = 80; +params.precondition = 1; % switch on preconditioning +params.show = 0; +params.maxvalplot = 2.5; params.slice = 10; + +tic; [X_fista] = FISTA_REC(params); toc; +figure; imshow(X_fista(:,:,10) , [0, 2.5]); title ('FISTA-LS reconstruction'); +%% +fprintf('%s\n', 'Reconstruction using FISTA-LS-TV...'); +clear params +params.sino = Sino3D; +params.N = recon_size; +params.angles = angles_rad; +params.iterFISTA = 100; +params.lambdaTV = 0.001; % TV regularization parameter for FISTA-TV +params.tol = 1.0e-04; +params.iterTV = 20; +params.precondition = 1; % switch on preconditioning +params.show = 0; +params.maxvalplot = 2.5; params.slice = 10; + +tic; [X_fista_TV] = FISTA_REC(params); toc; +figure; imshow(X_fista_TV(:,:,10) , [0, 2.5]); title ('FISTA-LS-TV reconstruction'); +%% +%% +fprintf('%s\n', 'Reconstruction using FISTA-GH-TV...'); +clear params +params.sino = Sino3D; +params.N = recon_size; +params.angles = angles_rad; +params.iterFISTA = 100; +params.lambdaTV = 0.001; % TV regularization parameter for FISTA-TV +params.tol = 1.0e-04; +params.iterTV = 20; +params.lambdaR_L1 = 0.001; % Soft-Thresh L1 ring variable parameter +params.alpha_ring = 20; % to boost ring removal procedure +params.precondition = 1; % switch on preconditioning +params.show = 0; +params.maxvalplot = 2.5; params.slice = 10; + +tic; [X_fista_GH_TV] = FISTA_REC(params); toc; +figure; imshow(X_fista_GH_TV(:,:,10) , [0, 2.5]); title ('FISTA-GH-TV reconstruction'); +%% +%% +fprintf('%s\n', 'Reconstruction using FISTA-GH-TV-LLT...'); +clear params +params.sino = Sino3D; +params.N = recon_size; +params.angles = angles_rad; +params.iterFISTA = 100; +params.lambdaTV = 0.001; % TV regularization parameter for FISTA-TV +params.tol = 1.0e-04; +params.iterTV = 20; +params.lambdaHO = 35; % regularization parameter for LLT problem +params.tauHO = 0.00011; % time-step parameter for explicit scheme +params.iterHO = 70; % the max number of TV iterations +params.lambdaR_L1 = 0.001; % Soft-Thresh L1 ring variable parameter +params.alpha_ring = 20; % to boost ring removal procedure +params.precondition = 1; % switch on preconditioning +params.show = 0; +params.maxvalplot = 2.5; params.slice = 10; + +tic; [X_fista_GH_TVLLT] = FISTA_REC(params); toc; +figure; imshow(X_fista_GH_TVLLT(:,:,10) , [0, 2.5]); title ('FISTA-GH-TV-LLT reconstruction'); +%% +%% +% fprintf('%s\n', 'Reconstruction using FISTA-Student-TV...'); +% %%%%<<<< Not stable with this dataset! Requires more work >>>> %%%%% +% clear params +% params.sino = Sino3D(:,:,15); +% params.N = 950; +% params.angles = angles_rad; +% params.iterFISTA = 150; +% params.L_const = 30; % Lipshitz constant +% params.lambdaTV = 0.009; % TV regularization parameter for FISTA-TV +% params.tol = 1.0e-04; +% params.iterTV = 20; +% params.fidelity = 'student'; % choosing Student t penalty +% % params.precondition = 1; % switch on preconditioning +% params.show = 1; +% params.maxvalplot = 2.5; params.slice = 1; +% +% tic; [X_fistaStudentTV] = FISTA_REC(params); toc; +% figure; imshow(X_fistaStudentTV , [0, 2.5]); title ('FISTA-Student-TV reconstruction'); +%% +slice = 10; % if 3D reconstruction + +fprintf('%s\n', 'Segmentation using OTSU method ...'); +level = graythresh(X_fista(:,:,slice)); +Segm_FISTA = im2bw(X_fista(:,:,slice),level); +figure; imshow(Segm_FISTA, []); title ('Segmented FISTA-LS reconstruction'); + +level = graythresh(X_fista_TV(:,:,slice)); +Segm_FISTA_TV = im2bw(X_fista_TV(:,:,slice),level); +figure; imshow(Segm_FISTA_TV, []); title ('Segmented FISTA-LS-TV reconstruction'); + +level = graythresh(X_fista_GH_TV(:,:,slice)); +BW_FISTA_GH_TV = im2bw(X_fista_GH_TV(:,:,slice),level); +figure; imshow(BW_FISTA_GH_TV, []); title ('Segmented FISTA-GH-TV reconstruction'); + +level = graythresh(X_fista_GH_TVLLT(:,:,slice)); +BW_FISTA_GH_TVLLT = im2bw(X_fista_GH_TVLLT(:,:,slice),level); +figure; imshow(BW_FISTA_GH_TVLLT, []); title ('Segmented FISTA-GH-TV-LLT reconstruction'); +%%
\ No newline at end of file diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..827b7f3 --- /dev/null +++ b/license.txt @@ -0,0 +1,27 @@ +Copyright (c) 2017, Daniil Kazantsev, The University of Manchester. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + * Neither the name of the nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/main_func/FISTA_REC.m b/main_func/FISTA_REC.m new file mode 100644 index 0000000..79369a5 --- /dev/null +++ b/main_func/FISTA_REC.m @@ -0,0 +1,338 @@ +function [X, error, objective, residual] = FISTA_REC(params) + +% <<<< FISTA-based reconstruction algorithm using ASTRA-toolbox (parallel beam) >>>> +% ___Input___: +% params.[] file: +% - .sino (2D or 3D sinogram) [required] +% - .N (image dimension) [required] +% - .angles (in radians) [required] +% - .iterFISTA (iterations for the main loop) +% - .L_const (Lipschitz constant, default Power method) ) +% - .X_ideal (ideal image, if given) +% - .weights (statisitcal weights, size of sinogram) +% - .ROI (Region-of-interest, only if X_ideal is given) +% - .lambdaTV (TV regularization parameter, default 0 - reg. TV is switched off) +% - .tol (tolerance to terminate TV regularization, default 1.0e-04) +% - .iterTV (iterations for the TV penalty, default 0) +% - .lambdaHO (Higher Order LLT regularization parameter, default 0 - LLT reg. switched off) +% - .iterHO (iterations for HO penalty, default 50) +% - .tauHO (time step parameter for HO term) +% - .lambdaR_L1 (regularization parameter for L1 ring minimization, if lambdaR_L1 > 0 then switch on ring removal, default 0) +% - .alpha_ring (larger values can accelerate convergence but check stability, default 1) +% - .fidelity (choose between "LS" and "student" data fidelities) +% - .precondition (1 - switch on Fourier filtering before backprojection) +% - .show (visualize reconstruction 1/0, (0 default)) +% - .maxvalplot (maximum value to use for imshow[0 maxvalplot]) +% - .slice (for 3D volumes - slice number to imshow) +% ___Output___: +% 1. X - reconstructed image/volume +% 2. error - residual error (if X_ideal is given) +% 3. value of the objective function +% 4. forward projection(X) +% References: +% 1. "A Fast Iterative Shrinkage-Thresholding Algorithm for Linear Inverse +% Problems" by A. Beck and M Teboulle +% 2. "Ring artifacts correction in compressed sensing..." by P. Paleo +% 3. "A novel tomographic reconstruction method based on the robust +% Student's t function for suppressing data outliers" D. Kazantsev et.al. +% D. Kazantsev, 2016-17 + +% Dealing with input parameters +if (isfield(params,'sino')) + sino = params.sino; + [anglesNumb, Detectors, SlicesZ] = size(sino); + fprintf('%s %i %s %i %s %i %s \n', 'Sinogram has a dimension of', anglesNumb, 'projections;', Detectors, 'detectors;', SlicesZ, 'vertical slices.'); +else + fprintf('%s \n', 'Please provide a sinogram'); +end +if (isfield(params,'N')) + N = params.N; +else + fprintf('%s \n', 'Please provide N-size for the reconstructed image [N x N]'); +end +if (isfield(params,'N')) + angles = params.angles; + if (length(angles) ~= anglesNumb) + fprintf('%s \n', 'Sinogram angular dimension does not correspond to the angles dimension provided'); + end +else + fprintf('%s \n', 'Please provide a vector of angles'); +end +if (isfield(params,'iterFISTA')) + iterFISTA = params.iterFISTA; +else + iterFISTA = 30; +end +if (isfield(params,'L_const')) + L_const = params.L_const; +else + % using Power method (PM) to establish L constant + vol_geom = astra_create_vol_geom(N, N); + proj_geom = astra_create_proj_geom('parallel', 1.0, Detectors, angles); + + niter = 10; % number of iteration for PM + x = rand(N,N); + [sino_id, y] = astra_create_sino_cuda(x, proj_geom, vol_geom); + astra_mex_data2d('delete', sino_id); + + for i = 1:niter + x = astra_create_backprojection_cuda(y, proj_geom, vol_geom); + s = norm(x); + x = x/s; + [sino_id, y] = astra_create_sino_cuda(x, proj_geom, vol_geom); + astra_mex_data2d('delete', sino_id); + end + L_const = s; +end +if (isfield(params,'X_ideal')) + X_ideal = params.X_ideal; +else + X_ideal = 'none'; +end +if (isfield(params,'weights')) + weights = params.weights; +else + weights = 1; +end +if (isfield(params,'ROI')) + ROI = params.ROI; +else + ROI = find(X_ideal>=0.0); +end +if (isfield(params,'lambdaTV')) + lambdaTV = params.lambdaTV; +else + lambdaTV = 0; +end +if (isfield(params,'tol')) + tol = params.tol; +else + tol = 1.0e-04; +end +if (isfield(params,'iterTV')) + iterTV = params.iterTV; +else + iterTV = 10; +end +if (isfield(params,'lambdaHO')) + lambdaHO = params.lambdaHO; +else + lambdaHO = 0; +end +if (isfield(params,'iterHO')) + iterHO = params.iterHO; +else + iterHO = 50; +end +if (isfield(params,'tauHO')) + tauHO = params.tauHO; +else + tauHO = 0.0001; +end +if (isfield(params,'lambdaR_L1')) + lambdaR_L1 = params.lambdaR_L1; +else + lambdaR_L1 = 0; +end +if (isfield(params,'alpha_ring')) + alpha_ring = params.alpha_ring; % higher values can accelerate ring removal procedure +else + alpha_ring = 1; +end +if (isfield(params,'fidelity')) + fidelity = params.fidelity; +else + fidelity = 'LS'; +end +if (isfield(params,'precondition')) + precondition = params.precondition; +else + precondition = 0; +end +if (isfield(params,'show')) + show = params.show; +else + show = 0; +end +if (isfield(params,'maxvalplot')) + maxvalplot = params.maxvalplot; +else + maxvalplot = 1; +end +if (isfield(params,'slice')) + slice = params.slice; +else + slice = 1; +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% building geometry (parallel-beam) +vol_geom = astra_create_vol_geom(N, N); +proj_geom = astra_create_proj_geom('parallel', 1.0, Detectors, angles); +error = zeros(iterFISTA,1); % error vector +objective = zeros(iterFISTA,1); % obhective vector + +if (lambdaR_L1 > 0) + % do reconstruction WITH ring removal (Group-Huber fidelity) + t = 1; + X = zeros(N,N,SlicesZ, 'single'); + X_t = X; + + add_ring = zeros(anglesNumb, Detectors, SlicesZ, 'single'); % size of sinogram array + r = zeros(Detectors,SlicesZ, 'single'); % 2D array (for 3D data) of sparse "ring" vectors + r_x = r; + + % iterations loop + for i = 1:iterFISTA + + X_old = X; + t_old = t; + r_old = r; + + % all slices loop + for j = 1:SlicesZ + + [sino_id, sino_updt] = astra_create_sino_cuda(X_t(:,:,j), proj_geom, vol_geom); + + for kkk = 1:anglesNumb + add_ring(kkk,:,j) = sino(kkk,:,j) - alpha_ring.*r_x(:,j)'; + end + + residual = sino_updt - add_ring(:,:,j); + + if (precondition == 1) + residual = filtersinc(residual'); % filtering residual (Fourier preconditioning) + residual = residual'; + end + + vec = sum(residual); + r(:,j) = r_x(:,j) - (1/L_const).*vec'; + + x_temp = astra_create_backprojection_cuda(residual, proj_geom, vol_geom); + + X(:,:,j) = X_t(:,:,j) - (1/L_const).*x_temp; + astra_mex_data2d('delete', sino_id); + end + + if ((lambdaTV > 0) && (lambdaHO == 0)) + if (size(X,3) > 1) + [X] = FISTA_TV(single(X), lambdaTV, iterTV, tol); % TV regularization using FISTA + gradTV = 1; + else + [X, gradTV] = FISTA_TV(single(X), lambdaTV, iterTV, tol); % TV regularization using FISTA + end + objective(i) = 0.5.*norm(residual(:))^2 + norm(gradTV(:)); + % X = SplitBregman_TV(single(X), lambdaTV, iterTV, tol); % TV-Split Bregman regularization on CPU (memory limited) + elseif ((lambdaHO > 0) && (lambdaTV == 0)) + % Higher Order regularization + X = LLT_model(single(X), lambdaHO, tauHO, iterHO, tol, 0); % LLT higher order model + elseif ((lambdaTV > 0) && (lambdaHO > 0)) + %X1 = SplitBregman_TV(single(X), lambdaTV, iterTV, tol); % TV-Split Bregman regularization on CPU (memory limited) + X1 = FISTA_TV(single(X), lambdaTV, iterTV, tol); % TV regularization using FISTA + X2 = LLT_model(single(X), lambdaHO, tauHO, iterHO, tol, 0); % LLT higher order model + X = 0.5.*(X1 + X2); % averaged combination of two solutions + elseif ((lambdaTV == 0) && (lambdaHO == 0)) + objective(i) = 0.5.*norm(residual(:))^2; + end + + r = max(abs(r)-lambdaR_L1, 0).*sign(r); % soft-thresholding operator + + t = (1 + sqrt(1 + 4*t^2))/2; % updating t + X_t = X + ((t_old-1)/t).*(X - X_old); % updating X + r_x = r + ((t_old-1)/t).*(r - r_old); % updating r + + if (show == 1) + figure(10); imshow(X(:,:,slice), [0 maxvalplot]); + figure(11); plot(r); title('Rings offset vector') + pause(0.03); + end + if (strcmp(X_ideal, 'none' ) == 0) + error(i) = RMSE(X(ROI), X_ideal(ROI)); + fprintf('%s %i %s %s %.4f %s %s %.4f \n', 'Iteration Number:', i, '|', 'Error RMSE:', error(i), '|', 'Objective:', objective(i)); + else + fprintf('%s %i %s %s %.4f \n', 'Iteration Number:', i, '|', 'Objective:', objective(i)); + end + + end + +else + % WITHOUT ring removal + t = 1; + X = zeros(N,N,SlicesZ, 'single'); + X_t = X; + + % iterations loop + for i = 1:iterFISTA + + X_old = X; + t_old = t; + + % slices loop + for j = 1:SlicesZ + [sino_id, sino_updt] = astra_create_sino_cuda(X_t(:,:,j), proj_geom, vol_geom); + residual = weights.*(sino_updt - sino(:,:,j)); + + % employ students t fidelity term + if (strcmp(fidelity,'student') == 1) + res_vec = reshape(residual, anglesNumb*Detectors,1); + %s = 100; + %gr = (2)*res_vec./(s*2 + conj(res_vec).*res_vec); + [ff, gr] = studentst(res_vec,1); + residual = reshape(gr, anglesNumb, Detectors); + end + + if (precondition == 1) + residual = filtersinc(residual'); % filtering residual (Fourier preconditioning) + residual = residual'; + end + + x_temp = astra_create_backprojection_cuda(residual, proj_geom, vol_geom); + X(:,:,j) = X_t(:,:,j) - (1/L_const).*x_temp; + astra_mex_data2d('delete', sino_id); + end + + if ((lambdaTV > 0) && (lambdaHO == 0)) + if (size(X,3) > 1) + [X] = FISTA_TV(single(X), lambdaTV, iterTV, tol); % TV regularization using FISTA + gradTV = 1; + else + [X, gradTV] = FISTA_TV(single(X), lambdaTV, iterTV, tol); % TV regularization using FISTA + end + if (strcmp(fidelity,'student') == 1) + objective(i) = ff + norm(gradTV(:)); + else + objective(i) = 0.5.*norm(residual(:))^2 + norm(gradTV(:)); + end + % X = SplitBregman_TV(single(X), lambdaTV, iterTV, tol); % TV-Split Bregman regularization on CPU (memory limited) + elseif ((lambdaHO > 0) && (lambdaTV == 0)) + % Higher Order regularization + X = LLT_model(single(X), lambdaHO, tauHO, iterHO, tol, 0); % LLT higher order model + elseif ((lambdaTV > 0) && (lambdaHO > 0)) + X1 = SplitBregman_TV(single(X), lambdaTV, iterTV, tol); % TV-Split Bregman regularization on CPU (memory limited) + X2 = LLT_model(single(X), lambdaHO, tauHO, iterHO, tol, 0); % LLT higher order model + X = 0.5.*(X1 + X2); % averaged combination of two solutions + elseif ((lambdaTV == 0) && (lambdaHO == 0)) + objective(i) = 0.5.*norm(residual(:))^2; + end + + + t = (1 + sqrt(1 + 4*t^2))/2; % updating t + X_t = X + ((t_old-1)/t).*(X - X_old); % updating X + + if (show == 1) + figure(11); imshow(X(:,:,slice), [0 maxvalplot]); + pause(0.03); + end + if (strcmp(X_ideal, 'none' ) == 0) + error(i) = RMSE(X(ROI), X_ideal(ROI)); + fprintf('%s %i %s %s %.4f %s %s %.4f \n', 'Iteration Number:', i, '|', 'Error RMSE:', error(i), '|', 'Objective:', objective(i)); + else + fprintf('%s %i %s %s %.4f \n', 'Iteration Number:', i, '|', 'Objective:', objective(i)); + end + + + end + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +end diff --git a/main_func/FISTA_TV.c b/main_func/FISTA_TV.c new file mode 100644 index 0000000..87681bc --- /dev/null +++ b/main_func/FISTA_TV.c @@ -0,0 +1,331 @@ +#include "mex.h" +#include <matrix.h> +#include <math.h> +#include <stdlib.h> +#include <memory.h> +#include <stdio.h> +#include "omp.h" + +/* C-OMP implementation of FISTA-TV denoising-regularization model (2D/3D) + * + * Input Parameters: + * 1. Noisy image/volume + * 2. lambda - regularization parameter + * 3. Number of iterations + * 4. eplsilon - tolerance constant + * + * Output: + * Filtered/regularized image + * + * Example: + * figure; + * Im = double(imread('lena_gray_256.tif'))/255; % loading image + * u0 = Im + .05*randn(size(Im)); % adding noise + * u = FISTA_TV(single(u0), 0.05, 150, 1e-04); + * + * to compile with OMP support: mex FISTA_TV.c CFLAGS="\$CFLAGS -fopenmp -Wall" LDFLAGS="\$LDFLAGS -fopenmp" + * References: A. Beck & M. Teboulle + * + * D. Kazantsev, 2016* + */ + +float copyIm(float *A, float *B, int dimX, int dimY, int dimZ); +float Obj_func2D(float *A, float *D, float *R1, float *R2, float *grad, float lambda, int dimX, int dimY); +float Grad_func2D(float *P1, float *P2, float *D, float *R1, float *R2, float lambda, int dimX, int dimY); +float Proj_func2D(float *P1, float *P2, int dimX, int dimY); +float Rupd_func2D(float *P1, float *P1_old, float *P2, float *P2_old, float *R1, float *R2, float tkp1, float tk, int dimX, int dimY); + +float Obj_func3D(float *A, float *D, float *R1, float *R2, float *R3, float lambda, int dimX, int dimY, int dimZ); +float Grad_func3D(float *P1, float *P2, float *P3, float *D, float *R1, float *R2, float *R3, float lambda, int dimX, int dimY, int dimZ); +float Proj_func3D(float *P1, float *P2, float *P3, int dimX, int dimY, int dimZ); +float Rupd_func3D(float *P1, float *P1_old, float *P2, float *P2_old, float *P3, float *P3_old, float *R1, float *R2, float *R3, float tkp1, float tk, int dimX, int dimY, int dimZ); + + +void mexFunction( + int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) + +{ + int number_of_dims, iter, dimX, dimY, dimZ, ll, j, count; + const int *dim_array; + float *A, *grad=NULL, *D=NULL, *D_old=NULL, *P1=NULL, *P2=NULL, *P3=NULL, *P1_old=NULL, *P2_old=NULL, *P3_old=NULL, *R1=NULL, *R2=NULL, *R3=NULL, lambda, tk, tkp1, re, re1, re_old, epsil; + + number_of_dims = mxGetNumberOfDimensions(prhs[0]); + dim_array = mxGetDimensions(prhs[0]); + + if(nrhs != 4) mexErrMsgTxt("Four input parameters is reqired: Image(2D/3D), Regularization parameter, Iterations, Tolerance"); + + /*Handling Matlab input data*/ + A = (float *) mxGetData(prhs[0]); /*noisy image (2D/3D) */ + lambda = (float) mxGetScalar(prhs[1]); /* regularization parameter */ + iter = (int) mxGetScalar(prhs[2]); /* iterations number */ + epsil = (float) mxGetScalar(prhs[3]); /* tolerance constant */ + + /*Handling Matlab output data*/ + dimX = dim_array[0]; dimY = dim_array[1]; dimZ = dim_array[2]; + + tk = 1.0f; + tkp1=1.0f; + count = 1; + re_old = 0.0f; + + if (number_of_dims == 2) { + dimZ = 1; /*2D case*/ + D = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + grad = (float*)mxGetPr(plhs[1] = mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + D_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + P1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + P2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + P1_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + P2_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + R1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + R2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + + /* begin iterations */ + for(ll=0; ll<iter; ll++) { + + /*storing old values*/ + copyIm(D, D_old, dimX, dimY, dimZ); + copyIm(P1, P1_old, dimX, dimY, dimZ); + copyIm(P2, P2_old, dimX, dimY, dimZ); + tk = tkp1; + + /* computing the gradient of the objective function */ + Obj_func2D(A, D, R1, R2, grad, lambda, dimX, dimY); + + /*Taking a step towards minus of the gradient*/ + Grad_func2D(P1, P2, D, R1, R2, lambda, dimX, dimY); + + /* projection step */ + Proj_func2D(P1, P2, dimX, dimY); + + /*updating R and t*/ + tkp1 = (1.0f + sqrt(1.0f + 4.0f*tk*tk))*0.5f; + Rupd_func2D(P1, P1_old, P2, P2_old, R1, R2, tkp1, tk, dimX, dimY); + + /* calculate norm */ + re = 0.0f; re1 = 0.0f; + for(j=0; j<dimX*dimY*dimZ; j++) + { + re += pow(D[j] - D_old[j],2); + re1 += pow(D[j],2); + } + re = sqrt(re)/sqrt(re1); + if (re < epsil) count++; + if (count > 3) break; + + /* check that the residual norm is decreasing */ + if (ll > 2) { + if (re > re_old) break; } + + re_old = re; + /*printf("%f %i %i \n", re, ll, count); */ + } + printf("TV iterations stopped at iteration: %i\n", ll); + } + if (number_of_dims == 3) { + D = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + D_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + P1 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + P2 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + P3 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + P1_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + P2_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + P3_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + R1 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + R2 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + R3 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + + /* begin iterations */ + for(ll=0; ll<iter; ll++) { + + /*storing old values*/ + copyIm(D, D_old, dimX, dimY, dimZ); + copyIm(P1, P1_old, dimX, dimY, dimZ); + copyIm(P2, P2_old, dimX, dimY, dimZ); + copyIm(P3, P3_old, dimX, dimY, dimZ); + + tk = tkp1; + + /* computing the gradient of the objective function */ + Obj_func3D(A, D, R1, R2, R3,lambda, dimX, dimY, dimZ); + + /*Taking a step towards minus of the gradient*/ + Grad_func3D(P1, P2, P3, D, R1, R2, R3, lambda, dimX, dimY, dimZ); + + /* projection step */ + Proj_func3D(P1, P2, P3, dimX, dimY, dimZ); + + /*updating R and t*/ + tkp1 = (1.0f + sqrt(1.0f + 4.0f*tk*tk))*0.5f; + Rupd_func3D(P1, P1_old, P2, P2_old, P3, P3_old, R1, R2, R3, tkp1, tk, dimX, dimY, dimZ); + + /* calculate norm - stopping rules*/ + re = 0.0f; re1 = 0.0f; + for(j=0; j<dimX*dimY*dimZ; j++) + { + re += pow(D[j] - D_old[j],2); + re1 += pow(D[j],2); + } + re = sqrt(re)/sqrt(re1); + /* stop if the norm residual is less than the tolerance EPS */ + if (re < epsil) count++; + if (count > 3) break; + + /* check that the residual norm is decreasing */ + if (ll > 2) { + if (re > re_old) break; } + + re_old = re; + /*printf("%f %i %i \n", re, ll, count); */ + } + printf("TV iterations stopped at iteration: %i\n", ll); + } +} + +/* 2D-case related Functions */ +/*****************************************************************/ +float Obj_func2D(float *A, float *D, float *R1, float *R2, float *grad, float lambda, int dimX, int dimY) +{ + float val1, val2; + int i,j; +#pragma omp parallel for shared(A,D,R1,R2) private(i,j,val1,val2) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + /* symmetric boundary conditions (Neuman) */ + if (i == 0) {val1 = R1[(i+1)*dimY + (j)];} else {val1 = R1[(i-1)*dimY + (j)];} + if (j == 0) {val2 = R2[(i)*dimY + (j+1)];} else {val2 = R2[(i)*dimY + (j-1)];} + D[(i)*dimY + (j)] = A[(i)*dimY + (j)] - lambda*(R1[(i)*dimY + (j)] + R2[(i)*dimY + (j)] - val1 - val2); + grad[(i)*dimY + (j)] = lambda*(R1[(i)*dimY + (j)] + R2[(i)*dimY + (j)] - val1 - val2); + }} + return *D; +} +float Grad_func2D(float *P1, float *P2, float *D, float *R1, float *R2, float lambda, int dimX, int dimY) +{ + float val1, val2; + int i,j; +#pragma omp parallel for shared(P1,P2,D,R1,R2) private(i,j,val1,val2) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + /* symmetric boundary conditions (Neuman) */ + + if (i == dimX-1) {val1 = D[(i)*dimY + (j)] - D[(i-1)*dimY + (j)];} else {val1 = D[(i)*dimY + (j)] - D[(i+1)*dimY + (j)];} + if (j == dimY-1) {val2 = D[(i)*dimY + (j)] - D[(i)*dimY + (j-1)];} else {val2 = D[(i)*dimY + (j)] - D[(i)*dimY + (j+1)];} + + P1[(i)*dimY + (j)] = R1[(i)*dimY + (j)] + (1.0f/(8.0f*lambda))*val1; + P2[(i)*dimY + (j)] = R2[(i)*dimY + (j)] + (1.0f/(8.0f*lambda))*val2; + }} + return 1; +} +float Proj_func2D(float *P1, float *P2, int dimX, int dimY) +{ + float val1, val2; + int i,j; +#pragma omp parallel for shared(P1,P2) private(i,j,val1,val2) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + val1 = fabs(P1[(i)*dimY + (j)]); + val2 = fabs(P2[(i)*dimY + (j)]); + if (val1 < 1.0f) {val1 = 1.0f;} + if (val2 < 1.0f) {val2 = 1.0f;} + + P1[(i)*dimY + (j)] = P1[(i)*dimY + (j)]/val1; + P2[(i)*dimY + (j)] = P2[(i)*dimY + (j)]/val2; + }} + return 1; +} +float Rupd_func2D(float *P1, float *P1_old, float *P2, float *P2_old, float *R1, float *R2, float tkp1, float tk, int dimX, int dimY) +{ + int i,j; +#pragma omp parallel for shared(P1,P2,P1_old,P2_old,R1,R2) private(i,j) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + R1[(i)*dimY + (j)] = P1[(i)*dimY + (j)] + ((tk-1.0f)/tkp1)*(P1[(i)*dimY + (j)] - P1_old[(i)*dimY + (j)]); + R2[(i)*dimY + (j)] = P2[(i)*dimY + (j)] + ((tk-1.0f)/tkp1)*(P2[(i)*dimY + (j)] - P2_old[(i)*dimY + (j)]); + }} + return 1; +} + + +/* 3D-case related Functions */ +/*****************************************************************/ +float Obj_func3D(float *A, float *D, float *R1, float *R2, float *R3, float lambda, int dimX, int dimY, int dimZ) +{ + float val1, val2, val3; + int i,j,k; +#pragma omp parallel for shared(A,D,R1,R2,R3) private(i,j,k,val1,val2,val3) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + /* symmetric boundary conditions (Neuman) */ + if (i == 0) {val1 = R1[(dimX*dimY)*k + (i+1)*dimY + (j)];} else {val1 = R1[(dimX*dimY)*k + (i-1)*dimY + (j)];} + if (j == 0) {val2 = R2[(dimX*dimY)*k + (i)*dimY + (j+1)];} else {val2 = R2[(dimX*dimY)*k + (i)*dimY + (j-1)];} + if (k == 0) {val3 = R3[(dimX*dimY)*(k+1) + (i)*dimY + (j)];} else {val3 = R3[(dimX*dimY)*(k-1) + (i)*dimY + (j)];} + D[(dimX*dimY)*k + (i)*dimY + (j)] = A[(dimX*dimY)*k + (i)*dimY + (j)] - lambda*(R1[(dimX*dimY)*k + (i)*dimY + (j)] + R2[(dimX*dimY)*k + (i)*dimY + (j)] + R3[(dimX*dimY)*k + (i)*dimY + (j)] - val1 - val2 - val3); + }}} + return *D; +} +float Grad_func3D(float *P1, float *P2, float *P3, float *D, float *R1, float *R2, float *R3, float lambda, int dimX, int dimY, int dimZ) +{ + float val1, val2, val3; + int i,j,k; +#pragma omp parallel for shared(P1,P2,P3,D,R1,R2,R3) private(i,j,k,val1,val2,val3) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + /* symmetric boundary conditions (Neuman) */ + if (i == dimX-1) {val1 = D[(dimX*dimY)*k + (i)*dimY + (j)] - D[(dimX*dimY)*k + (i-1)*dimY + (j)];} else {val1 = D[(dimX*dimY)*k + (i)*dimY + (j)] - D[(dimX*dimY)*k + (i+1)*dimY + (j)];} + if (j == dimY-1) {val2 = D[(dimX*dimY)*k + (i)*dimY + (j)] - D[(dimX*dimY)*k + (i)*dimY + (j-1)];} else {val2 = D[(dimX*dimY)*k + (i)*dimY + (j)] - D[(dimX*dimY)*k + (i)*dimY + (j+1)];} + if (k == dimZ-1) {val3 = D[(dimX*dimY)*k + (i)*dimY + (j)] - D[(dimX*dimY)*(k-1) + (i)*dimY + (j)];} else {val3 = D[(dimX*dimY)*k + (i)*dimY + (j)] - D[(dimX*dimY)*(k+1) + (i)*dimY + (j)];} + + P1[(dimX*dimY)*k + (i)*dimY + (j)] = R1[(dimX*dimY)*k + (i)*dimY + (j)] + (1.0f/(8.0f*lambda))*val1; + P2[(dimX*dimY)*k + (i)*dimY + (j)] = R2[(dimX*dimY)*k + (i)*dimY + (j)] + (1.0f/(8.0f*lambda))*val2; + P3[(dimX*dimY)*k + (i)*dimY + (j)] = R3[(dimX*dimY)*k + (i)*dimY + (j)] + (1.0f/(8.0f*lambda))*val3; + }}} + return 1; +} +float Proj_func3D(float *P1, float *P2, float *P3, int dimX, int dimY, int dimZ) +{ + float val1, val2, val3; + int i,j,k; +#pragma omp parallel for shared(P1,P2,P3) private(i,j,k,val1,val2,val3) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + val1 = fabs(P1[(dimX*dimY)*k + (i)*dimY + (j)]); + val2 = fabs(P2[(dimX*dimY)*k + (i)*dimY + (j)]); + val3 = fabs(P3[(dimX*dimY)*k + (i)*dimY + (j)]); + if (val1 < 1.0f) {val1 = 1.0f;} + if (val2 < 1.0f) {val2 = 1.0f;} + if (val3 < 1.0f) {val3 = 1.0f;} + + P1[(dimX*dimY)*k + (i)*dimY + (j)] = P1[(dimX*dimY)*k + (i)*dimY + (j)]/val1; + P2[(dimX*dimY)*k + (i)*dimY + (j)] = P2[(dimX*dimY)*k + (i)*dimY + (j)]/val2; + P3[(dimX*dimY)*k + (i)*dimY + (j)] = P3[(dimX*dimY)*k + (i)*dimY + (j)]/val3; + }}} + return 1; +} +float Rupd_func3D(float *P1, float *P1_old, float *P2, float *P2_old, float *P3, float *P3_old, float *R1, float *R2, float *R3, float tkp1, float tk, int dimX, int dimY, int dimZ) +{ + int i,j,k; +#pragma omp parallel for shared(P1,P2,P3,P1_old,P2_old,P3_old,R1,R2,R3) private(i,j,k) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + R1[(dimX*dimY)*k + (i)*dimY + (j)] = P1[(dimX*dimY)*k + (i)*dimY + (j)] + ((tk-1.0f)/tkp1)*(P1[(dimX*dimY)*k + (i)*dimY + (j)] - P1_old[(dimX*dimY)*k + (i)*dimY + (j)]); + R2[(dimX*dimY)*k + (i)*dimY + (j)] = P2[(dimX*dimY)*k + (i)*dimY + (j)] + ((tk-1.0f)/tkp1)*(P2[(dimX*dimY)*k + (i)*dimY + (j)] - P2_old[(dimX*dimY)*k + (i)*dimY + (j)]); + R3[(dimX*dimY)*k + (i)*dimY + (j)] = P3[(dimX*dimY)*k + (i)*dimY + (j)] + ((tk-1.0f)/tkp1)*(P3[(dimX*dimY)*k + (i)*dimY + (j)] - P3_old[(dimX*dimY)*k + (i)*dimY + (j)]); + }}} + return 1; +} + +/* General Functions */ +/*****************************************************************/ +/* Copy Image */ +float copyIm(float *A, float *B, int dimX, int dimY, int dimZ) +{ + int j; +#pragma omp parallel for shared(A, B) private(j) + for(j=0; j<dimX*dimY*dimZ; j++) B[j] = A[j]; + return *B; +}
\ No newline at end of file diff --git a/main_func/LLT_model.c b/main_func/LLT_model.c new file mode 100644 index 0000000..a611e54 --- /dev/null +++ b/main_func/LLT_model.c @@ -0,0 +1,431 @@ +#include "mex.h" +#include <matrix.h> +#include <math.h> +#include <stdlib.h> +#include <memory.h> +#include <stdio.h> +#include "omp.h" + +#define EPS 0.001 + +/* C-OMP implementation of Lysaker, Lundervold and Tai (LLT) model of higher order regularization penalty + * + * Input Parameters: + * 1. U0 - origanal noise image/volume + * 2. lambda - regularization parameter + * 3. tau - time-step for explicit scheme + * 4. iter - iterations number + * 5. epsil - tolerance constant (to terminate earlier) + * 6. switcher - default is 0, switch to (1) to restrictive smoothing in Z dimension (in test) + * + * Output: + * Filtered/regularized image + * + * Example: + * figure; + * Im = double(imread('lena_gray_256.tif'))/255; % loading image + * u0 = Im + .03*randn(size(Im)); % adding noise + * [Den] = LLT_model(single(u0), 10, 0.1, 1); + * + * + * to compile with OMP support: mex LLT_model.c CFLAGS="\$CFLAGS -fopenmp -Wall -std=c99" LDFLAGS="\$LDFLAGS -fopenmp" + * References: Lysaker, Lundervold and Tai (LLT) 2003, IEEE + * + * 28.11.16/Harwell + */ +/* 2D functions */ +float der2D(float *U, float *D1, float *D2, int dimX, int dimY, int dimZ); +float div_upd2D(float *U0, float *U, float *D1, float *D2, int dimX, int dimY, int dimZ, float lambda, float tau); + +float der3D(float *U, float *D1, float *D2, float *D3, int dimX, int dimY, int dimZ); +float div_upd3D(float *U0, float *U, float *D1, float *D2, float *D3, unsigned short *Map, int switcher, int dimX, int dimY, int dimZ, float lambda, float tau); + +float calcMap(float *U, unsigned short *Map, int dimX, int dimY, int dimZ); +float cleanMap(unsigned short *Map, int dimX, int dimY, int dimZ); + +float copyIm(float *A, float *U, int dimX, int dimY, int dimZ); + +void mexFunction( + int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) + +{ + int number_of_dims, iter, dimX, dimY, dimZ, ll, j, count, switcher; + const int *dim_array; + float *U0, *U=NULL, *U_old=NULL, *D1=NULL, *D2=NULL, *D3=NULL, lambda, tau, re, re1, epsil, re_old; + unsigned short *Map=NULL; + + number_of_dims = mxGetNumberOfDimensions(prhs[0]); + dim_array = mxGetDimensions(prhs[0]); + + /*Handling Matlab input data*/ + U0 = (float *) mxGetData(prhs[0]); /*origanal noise image/volume*/ + if (mxGetClassID(prhs[0]) != mxSINGLE_CLASS) {mexErrMsgTxt("The input in single precision is required"); } + lambda = (float) mxGetScalar(prhs[1]); /*regularization parameter*/ + tau = (float) mxGetScalar(prhs[2]); /* time-step */ + iter = (int) mxGetScalar(prhs[3]); /*iterations number*/ + epsil = (float) mxGetScalar(prhs[4]); /* tolerance constant */ + switcher = (int) mxGetScalar(prhs[5]); /*switch on (1) restrictive smoothing in Z dimension*/ + + /*Handling Matlab output data*/ + dimX = dim_array[0]; dimY = dim_array[1]; dimZ = 1; + + if (number_of_dims == 2) { + /*2D case*/ + U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + U_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + D1 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + D2 = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + } + else if (number_of_dims == 3) { + /*3D case*/ + dimZ = dim_array[2]; + U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + U_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + D1 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + D2 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + D3 = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + if (switcher != 0) { + Map = (unsigned short*)mxGetPr(plhs[1] = mxCreateNumericArray(3, dim_array, mxUINT16_CLASS, mxREAL)); + } + } + else {mexErrMsgTxt("The input data should be 2D or 3D");} + + /*Copy U0 to U*/ + copyIm(U0, U, dimX, dimY, dimZ); + + count = 1; + re_old = 0.0f; + if (number_of_dims == 2) { + for(ll = 0; ll < iter; ll++) { + + copyIm(U, U_old, dimX, dimY, dimZ); + + /*estimate inner derrivatives */ + der2D(U, D1, D2, dimX, dimY, dimZ); + /* calculate div^2 and update */ + div_upd2D(U0, U, D1, D2, dimX, dimY, dimZ, lambda, tau); + + /* calculate norm to terminate earlier */ + re = 0.0f; re1 = 0.0f; + for(j=0; j<dimX*dimY*dimZ; j++) + { + re += pow(U_old[j] - U[j],2); + re1 += pow(U_old[j],2); + } + re = sqrt(re)/sqrt(re1); + if (re < epsil) count++; + if (count > 4) break; + + /* check that the residual norm is decreasing */ + if (ll > 2) { + if (re > re_old) break; + } + re_old = re; + + } /*end of iterations*/ + printf("HO iterations stopped at iteration: %i\n", ll); + } + /*3D version*/ + if (number_of_dims == 3) { + + if (switcher == 1) { + /* apply restrictive smoothing */ + calcMap(U, Map, dimX, dimY, dimZ); + /*clear outliers */ + cleanMap(Map, dimX, dimY, dimZ); + } + for(ll = 0; ll < iter; ll++) { + + copyIm(U, U_old, dimX, dimY, dimZ); + + /*estimate inner derrivatives */ + der3D(U, D1, D2, D3, dimX, dimY, dimZ); + /* calculate div^2 and update */ + div_upd3D(U0, U, D1, D2, D3, Map, switcher, dimX, dimY, dimZ, lambda, tau); + + /* calculate norm to terminate earlier */ + re = 0.0f; re1 = 0.0f; + for(j=0; j<dimX*dimY*dimZ; j++) + { + re += pow(U_old[j] - U[j],2); + re1 += pow(U_old[j],2); + } + re = sqrt(re)/sqrt(re1); + if (re < epsil) count++; + if (count > 4) break; + + /* check that the residual norm is decreasing */ + if (ll > 2) { + if (re > re_old) break; + } + re_old = re; + + } /*end of iterations*/ + printf("HO iterations stopped at iteration: %i\n", ll); + } +} + +float der2D(float *U, float *D1, float *D2, int dimX, int dimY, int dimZ) +{ + int i, j, i_p, i_m, j_m, j_p; + float dxx, dyy, denom_xx, denom_yy; +#pragma omp parallel for shared(U,D1,D2) private(i, j, i_p, i_m, j_m, j_p, denom_xx, denom_yy, dxx, dyy) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + /* symmetric boundary conditions (Neuman) */ + i_p = i + 1; if (i_p == dimX) i_p = i - 1; + i_m = i - 1; if (i_m < 0) i_m = i + 1; + j_p = j + 1; if (j_p == dimY) j_p = j - 1; + j_m = j - 1; if (j_m < 0) j_m = j + 1; + + dxx = U[i_p*dimY + j] - 2.0f*U[i*dimY + j] + U[i_m*dimY + j]; + dyy = U[i*dimY + j_p] - 2.0f*U[i*dimY + j] + U[i*dimY + j_m]; + + denom_xx = fabs(dxx) + EPS; + denom_yy = fabs(dyy) + EPS; + + D1[i*dimY + j] = dxx/denom_xx; + D2[i*dimY + j] = dyy/denom_yy; + }} + return 1; +} +float div_upd2D(float *U0, float *U, float *D1, float *D2, int dimX, int dimY, int dimZ, float lambda, float tau) +{ + int i, j, i_p, i_m, j_m, j_p; + float div, dxx, dyy; +#pragma omp parallel for shared(U,U0,D1,D2) private(i, j, i_p, i_m, j_m, j_p, div, dxx, dyy) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + /* symmetric boundary conditions (Neuman) */ + i_p = i + 1; if (i_p == dimX) i_p = i - 1; + i_m = i - 1; if (i_m < 0) i_m = i + 1; + j_p = j + 1; if (j_p == dimY) j_p = j - 1; + j_m = j - 1; if (j_m < 0) j_m = j + 1; + + dxx = D1[i_p*dimY + j] - 2.0f*D1[i*dimY + j] + D1[i_m*dimY + j]; + dyy = D2[i*dimY + j_p] - 2.0f*D2[i*dimY + j] + D2[i*dimY + j_m]; + + div = dxx + dyy; + + U[i*dimY + j] = U[i*dimY + j] - tau*div - tau*lambda*(U[i*dimY + j] - U0[i*dimY + j]); + }} + return *U0; +} + +float der3D(float *U, float *D1, float *D2, float *D3, int dimX, int dimY, int dimZ) +{ + int i, j, k, i_p, i_m, j_m, j_p, k_p, k_m; + float dxx, dyy, dzz, denom_xx, denom_yy, denom_zz; +#pragma omp parallel for shared(U,D1,D2,D3) private(i, j, k, i_p, i_m, j_m, j_p, k_p, k_m, denom_xx, denom_yy, denom_zz, dxx, dyy, dzz) + for(i=0; i<dimX; i++) { + /* symmetric boundary conditions (Neuman) */ + i_p = i + 1; if (i_p == dimX) i_p = i - 1; + i_m = i - 1; if (i_m < 0) i_m = i + 1; + for(j=0; j<dimY; j++) { + j_p = j + 1; if (j_p == dimY) j_p = j - 1; + j_m = j - 1; if (j_m < 0) j_m = j + 1; + for(k=0; k<dimZ; k++) { + k_p = k + 1; if (k_p == dimZ) k_p = k - 1; + k_m = k - 1; if (k_m < 0) k_m = k + 1; + + dxx = U[dimX*dimY*k + i_p*dimY + j] - 2.0f*U[dimX*dimY*k + i*dimY + j] + U[dimX*dimY*k + i_m*dimY + j]; + dyy = U[dimX*dimY*k + i*dimY + j_p] - 2.0f*U[dimX*dimY*k + i*dimY + j] + U[dimX*dimY*k + i*dimY + j_m]; + dzz = U[dimX*dimY*k_p + i*dimY + j] - 2.0f*U[dimX*dimY*k + i*dimY + j] + U[dimX*dimY*k_m + i*dimY + j]; + + denom_xx = fabs(dxx) + EPS; + denom_yy = fabs(dyy) + EPS; + denom_zz = fabs(dzz) + EPS; + + D1[dimX*dimY*k + i*dimY + j] = dxx/denom_xx; + D2[dimX*dimY*k + i*dimY + j] = dyy/denom_yy; + D3[dimX*dimY*k + i*dimY + j] = dzz/denom_zz; + + }}} + return 1; +} + +float div_upd3D(float *U0, float *U, float *D1, float *D2, float *D3, unsigned short *Map, int switcher, int dimX, int dimY, int dimZ, float lambda, float tau) +{ + int i, j, k, i_p, i_m, j_m, j_p, k_p, k_m; + float div, dxx, dyy, dzz; +#pragma omp parallel for shared(U,U0,D1,D2,D3) private(i, j, k, i_p, i_m, j_m, j_p, k_p, k_m, div, dxx, dyy, dzz) + for(i=0; i<dimX; i++) { + /* symmetric boundary conditions (Neuman) */ + i_p = i + 1; if (i_p == dimX) i_p = i - 1; + i_m = i - 1; if (i_m < 0) i_m = i + 1; + for(j=0; j<dimY; j++) { + j_p = j + 1; if (j_p == dimY) j_p = j - 1; + j_m = j - 1; if (j_m < 0) j_m = j + 1; + for(k=0; k<dimZ; k++) { + k_p = k + 1; if (k_p == dimZ) k_p = k - 1; + k_m = k - 1; if (k_m < 0) k_m = k + 1; +// k_p1 = k + 2; if (k_p1 >= dimZ) k_p1 = k - 2; +// k_m1 = k - 2; if (k_m1 < 0) k_m1 = k + 2; + + dxx = D1[dimX*dimY*k + i_p*dimY + j] - 2.0f*D1[dimX*dimY*k + i*dimY + j] + D1[dimX*dimY*k + i_m*dimY + j]; + dyy = D2[dimX*dimY*k + i*dimY + j_p] - 2.0f*D2[dimX*dimY*k + i*dimY + j] + D2[dimX*dimY*k + i*dimY + j_m]; + dzz = D3[dimX*dimY*k_p + i*dimY + j] - 2.0f*D3[dimX*dimY*k + i*dimY + j] + D3[dimX*dimY*k_m + i*dimY + j]; + + if ((switcher == 1) && (Map[dimX*dimY*k + i*dimY + j] == 0)) dzz = 0; + div = dxx + dyy + dzz; + +// if (switcher == 1) { + // if (Map2[dimX*dimY*k + i*dimY + j] == 0) dzz2 = 0; + //else dzz2 = D4[dimX*dimY*k_p1 + i*dimY + j] - 2.0f*D4[dimX*dimY*k + i*dimY + j] + D4[dimX*dimY*k_m1 + i*dimY + j]; +// div = dzz + dzz2; +// } + +// dzz = D3[dimX*dimY*k_p + i*dimY + j] - 2.0f*D3[dimX*dimY*k + i*dimY + j] + D3[dimX*dimY*k_m + i*dimY + j]; +// dzz2 = D4[dimX*dimY*k_p1 + i*dimY + j] - 2.0f*D4[dimX*dimY*k + i*dimY + j] + D4[dimX*dimY*k_m1 + i*dimY + j]; +// div = dzz + dzz2; + + U[dimX*dimY*k + i*dimY + j] = U[dimX*dimY*k + i*dimY + j] - tau*div - tau*lambda*(U[dimX*dimY*k + i*dimY + j] - U0[dimX*dimY*k + i*dimY + j]); + }}} + return *U0; + } + +// float der3D_2(float *U, float *D1, float *D2, float *D3, float *D4, int dimX, int dimY, int dimZ) +// { +// int i, j, k, i_p, i_m, j_m, j_p, k_p, k_m, k_p1, k_m1; +// float dxx, dyy, dzz, dzz2, denom_xx, denom_yy, denom_zz, denom_zz2; +// #pragma omp parallel for shared(U,D1,D2,D3,D4) private(i, j, k, i_p, i_m, j_m, j_p, k_p, k_m, denom_xx, denom_yy, denom_zz, denom_zz2, dxx, dyy, dzz, dzz2, k_p1, k_m1) +// for(i=0; i<dimX; i++) { +// /* symmetric boundary conditions (Neuman) */ +// i_p = i + 1; if (i_p == dimX) i_p = i - 1; +// i_m = i - 1; if (i_m < 0) i_m = i + 1; +// for(j=0; j<dimY; j++) { +// j_p = j + 1; if (j_p == dimY) j_p = j - 1; +// j_m = j - 1; if (j_m < 0) j_m = j + 1; +// for(k=0; k<dimZ; k++) { +// k_p = k + 1; if (k_p == dimZ) k_p = k - 1; +// k_m = k - 1; if (k_m < 0) k_m = k + 1; +// k_p1 = k + 2; if (k_p1 >= dimZ) k_p1 = k - 2; +// k_m1 = k - 2; if (k_m1 < 0) k_m1 = k + 2; +// +// dxx = U[dimX*dimY*k + i_p*dimY + j] - 2.0f*U[dimX*dimY*k + i*dimY + j] + U[dimX*dimY*k + i_m*dimY + j]; +// dyy = U[dimX*dimY*k + i*dimY + j_p] - 2.0f*U[dimX*dimY*k + i*dimY + j] + U[dimX*dimY*k + i*dimY + j_m]; +// dzz = U[dimX*dimY*k_p + i*dimY + j] - 2.0f*U[dimX*dimY*k + i*dimY + j] + U[dimX*dimY*k_m + i*dimY + j]; +// dzz2 = U[dimX*dimY*k_p1 + i*dimY + j] - 2.0f*U[dimX*dimY*k + i*dimY + j] + U[dimX*dimY*k_m1 + i*dimY + j]; +// +// denom_xx = fabs(dxx) + EPS; +// denom_yy = fabs(dyy) + EPS; +// denom_zz = fabs(dzz) + EPS; +// denom_zz2 = fabs(dzz2) + EPS; +// +// D1[dimX*dimY*k + i*dimY + j] = dxx/denom_xx; +// D2[dimX*dimY*k + i*dimY + j] = dyy/denom_yy; +// D3[dimX*dimY*k + i*dimY + j] = dzz/denom_zz; +// D4[dimX*dimY*k + i*dimY + j] = dzz2/denom_zz2; +// }}} +// return 1; +// } + +float calcMap(float *U, unsigned short *Map, int dimX, int dimY, int dimZ) +{ + int i,j,k,i1,j1,i2,j2,windowSize; + float val1, val2,thresh_val,maxval; + windowSize = 1; + thresh_val = 0.0001; /*thresh_val = 0.0035;*/ + + /* normalize volume first */ + maxval = 0.0f; + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + if (U[dimX*dimY*k + i*dimY + j] > maxval) maxval = U[dimX*dimY*k + i*dimY + j]; + }}} + + if (maxval != 0.0f) { + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + U[dimX*dimY*k + i*dimY + j] = U[dimX*dimY*k + i*dimY + j]/maxval; + }}} + } + else { + printf("%s \n", "Maximum value is zero!"); + return 0; + } + + #pragma omp parallel for shared(U,Map) private(i, j, k, i1, j1, i2, j2, val1, val2) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + + Map[dimX*dimY*k + i*dimY + j] = 0; +// Map2[dimX*dimY*k + i*dimY + j] = 0; + + val1 = 0.0f; val2 = 0.0f; + for(i1=-windowSize; i1<=windowSize; i1++) { + for(j1=-windowSize; j1<=windowSize; j1++) { + i2 = i+i1; + j2 = j+j1; + + if ((i2 >= 0) && (i2 < dimX) && (j2 >= 0) && (j2 < dimY)) { + if (k == 0) { + val1 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k+1) + i2*dimY + j2],2); +// val3 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k+2) + i2*dimY + j2],2); + } + else if (k == dimZ-1) { + val1 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k-1) + i2*dimY + j2],2); +// val3 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k-2) + i2*dimY + j2],2); + } +// else if (k == 1) { +// val1 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k-1) + i2*dimY + j2],2); +// val2 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k+1) + i2*dimY + j2],2); +// val3 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k+2) + i2*dimY + j2],2); +// } +// else if (k == dimZ-2) { +// val1 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k-1) + i2*dimY + j2],2); +// val2 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k+1) + i2*dimY + j2],2); +// val3 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k-2) + i2*dimY + j2],2); +// } + else { + val1 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k-1) + i2*dimY + j2],2); + val2 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k+1) + i2*dimY + j2],2); +// val3 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k-2) + i2*dimY + j2],2); +// val4 += pow(U[dimX*dimY*k + i2*dimY + j2] - U[dimX*dimY*(k+2) + i2*dimY + j2],2); + } + } + }} + + val1 = 0.111f*val1; val2 = 0.111f*val2; +// val3 = 0.111f*val3; val4 = 0.111f*val4; + if ((val1 <= thresh_val) && (val2 <= thresh_val)) Map[dimX*dimY*k + i*dimY + j] = 1; +// if ((val3 <= thresh_val) && (val4 <= thresh_val)) Map2[dimX*dimY*k + i*dimY + j] = 1; + }}} + return 1; +} + +float cleanMap(unsigned short *Map, int dimX, int dimY, int dimZ) +{ + int i, j, k, i1, j1, i2, j2, counter; + #pragma omp parallel for shared(Map) private(i, j, k, i1, j1, i2, j2, counter) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + + counter=0; + for(i1=-3; i1<=3; i1++) { + for(j1=-3; j1<=3; j1++) { + i2 = i+i1; + j2 = j+j1; + if ((i2 >= 0) && (i2 < dimX) && (j2 >= 0) && (j2 < dimY)) { + if (Map[dimX*dimY*k + i2*dimY + j2] == 0) counter++; + } + }} + if (counter < 24) Map[dimX*dimY*k + i*dimY + j] = 1; + }}} + return *Map; +} + + /* Copy Image */ + float copyIm(float *A, float *U, int dimX, int dimY, int dimZ) + { + int j; +#pragma omp parallel for shared(A, U) private(j) + for(j=0; j<dimX*dimY*dimZ; j++) U[j] = A[j]; + return *U; + } + /*********************3D *********************/
\ No newline at end of file diff --git a/main_func/SplitBregman_TV.c b/main_func/SplitBregman_TV.c new file mode 100644 index 0000000..691ccce --- /dev/null +++ b/main_func/SplitBregman_TV.c @@ -0,0 +1,346 @@ +#include "mex.h" +#include <matrix.h> +#include <math.h> +#include <stdlib.h> +#include <memory.h> +#include <stdio.h> +#include "omp.h" + +/* C-OMP implementation of Split Bregman - TV denoising-regularization model (2D/3D) + * + * Input Parameters: + * 1. Noisy image/volume + * 2. lambda - regularization parameter + * 3. Number of iterations + * 4. eplsilon - tolerance constant + * + * Output: + * Filtered/regularized image + * + * Example: + * figure; + * Im = double(imread('lena_gray_256.tif'))/255; % loading image + * u0 = Im + .05*randn(size(Im)); u0(u0 < 0) = 0; + * u = SplitBregman_TV(single(u0), 10, 30, 1e-04); + * + * to compile with OMP support: mex SplitBregman_TV.c CFLAGS="\$CFLAGS -fopenmp -Wall -std=c99" LDFLAGS="\$LDFLAGS -fopenmp" + * References: + * The Split Bregman Method for L1 Regularized Problems, by Tom Goldstein and Stanley Osher. + * D. Kazantsev, 2016* + */ + +float copyIm(float *A, float *B, int dimX, int dimY, int dimZ); +float gauss_seidel2D(float *U, float *A, float *Dx, float *Dy, float *Bx, float *By, int dimX, int dimY, float lambda, float mu); +float updDxDy_shrink2D(float *U, float *Dx, float *Dy, float *Bx, float *By, int dimX, int dimY, float lambda); +float updDxDy_shrinkAniso2D(float *U, float *Dx, float *Dy, float *Bx, float *By, int dimX, int dimY, float lambda); +float updBxBy2D(float *U, float *Dx, float *Dy, float *Bx, float *By, int dimX, int dimY); + +float gauss_seidel3D(float *U, float *A, float *Dx, float *Dy, float *Dz, float *Bx, float *By, float *Bz, int dimX, int dimY, int dimZ, float lambda, float mu); +float updDxDyDz_shrink3D(float *U, float *Dx, float *Dy, float *Dz, float *Bx, float *By, float *Bz, int dimX, int dimY, int dimZ, float lambda); +float updBxByBz3D(float *U, float *Dx, float *Dy, float *Dz, float *Bx, float *By, float *Bz, int dimX, int dimY, int dimZ); + +void mexFunction( + int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) + +{ + int number_of_dims, iter, dimX, dimY, dimZ, ll, j, count; + const int *dim_array; + float *A, *U=NULL, *U_old=NULL, *Dx=NULL, *Dy=NULL, *Dz=NULL, *Bx=NULL, *By=NULL, *Bz=NULL, lambda, mu, epsil, re, re1, re_old; + + number_of_dims = mxGetNumberOfDimensions(prhs[0]); + dim_array = mxGetDimensions(prhs[0]); + + if(nrhs != 4) mexErrMsgTxt("Four input parameters is reqired: Image(2D/3D), Regularization parameter, Iterations, Tolerance"); + + /*Handling Matlab input data*/ + A = (float *) mxGetData(prhs[0]); /*noisy image (2D/3D) */ + mu = (float) mxGetScalar(prhs[1]); /* regularization parameter */ + iter = (int) mxGetScalar(prhs[2]); /* iterations number */ + epsil = (float) mxGetScalar(prhs[3]); /* tolerance constant */ + + lambda = 2.0f*2.0f; + count = 1; + re_old = 0.0f; + /*Handling Matlab output data*/ + dimY = dim_array[0]; dimX = dim_array[1]; dimZ = dim_array[2]; + + + if (number_of_dims == 2) { + dimZ = 1; /*2D case*/ + U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + U_old = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + Dx = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + Dy = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + Bx = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + By = (float*)mxGetPr(mxCreateNumericArray(2, dim_array, mxSINGLE_CLASS, mxREAL)); + + copyIm(A, U, dimX, dimY, dimZ); /*initialize */ + + /* begin outer SB iterations */ + for(ll=0; ll<iter; ll++) { + + /*storing old values*/ + copyIm(U, U_old, dimX, dimY, dimZ); + + gauss_seidel2D(U, A, Dx, Dy, Bx, By, dimX, dimY, lambda, mu); + + updDxDy_shrink2D(U, Dx, Dy, Bx, By, dimX, dimY, lambda); + //updDxDy_shrinkAniso2D(U, Dx, Dy, Bx, By, dimX, dimY, lambda); + + updBxBy2D(U, Dx, Dy, Bx, By, dimX, dimY); + + /* calculate norm to terminate earlier */ + re = 0.0f; re1 = 0.0f; + for(j=0; j<dimX*dimY*dimZ; j++) + { + re += pow(U_old[j] - U[j],2); + re1 += pow(U_old[j],2); + } + re = sqrt(re)/sqrt(re1); + if (re < epsil) count++; + if (count > 4) break; + + /* check that the residual norm is decreasing */ + if (ll > 2) { + if (re > re_old) break; + } + re_old = re; + /*printf("%f %i %i \n", re, ll, count); */ + + /*copyIm(U_old, U, dimX, dimY, dimZ); */ + } + printf("SB iterations stopped at iteration: %i\n", ll); + } + if (number_of_dims == 3) { + U = (float*)mxGetPr(plhs[0] = mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + U_old = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + Dx = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + Dy = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + Dz = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + Bx = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + By = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + Bz = (float*)mxGetPr(mxCreateNumericArray(3, dim_array, mxSINGLE_CLASS, mxREAL)); + + copyIm(A, U, dimX, dimY, dimZ); /*initialize */ + + /* begin outer SB iterations */ + for(ll=0; ll<iter; ll++) { + + /*storing old values*/ + copyIm(U, U_old, dimX, dimY, dimZ); + + gauss_seidel3D(U, A, Dx, Dy, Dz, Bx, By, Bz, dimX, dimY, dimZ, lambda, mu); + + updDxDyDz_shrink3D(U, Dx, Dy, Dz, Bx, By, Bz, dimX, dimY, dimZ, lambda); + + updBxByBz3D(U, Dx, Dy, Dz, Bx, By, Bz, dimX, dimY, dimZ); + + /* calculate norm to terminate earlier */ + re = 0.0f; re1 = 0.0f; + for(j=0; j<dimX*dimY*dimZ; j++) + { + re += pow(U[j] - U_old[j],2); + re1 += pow(U[j],2); + } + re = sqrt(re)/sqrt(re1); + if (re < epsil) count++; + if (count > 4) break; + + /* check that the residual norm is decreasing */ + if (ll > 2) { + if (re > re_old) break; } + /*printf("%f %i %i \n", re, ll, count); */ + re_old = re; + } + printf("SB iterations stopped at iteration: %i\n", ll); + } +} + +/* 2D-case related Functions */ +/*****************************************************************/ +float gauss_seidel2D(float *U, float *A, float *Dx, float *Dy, float *Bx, float *By, int dimX, int dimY, float lambda, float mu) +{ + float sum, normConst; + int i,j,i1,i2,j1,j2; + normConst = 1.0f/(mu + 4.0f*lambda); + +#pragma omp parallel for shared(U) private(i,j,i1,i2,j1,j2,sum) + for(i=0; i<dimX; i++) { + /* symmetric boundary conditions (Neuman) */ + i1 = i+1; if (i1 == dimX) i1 = i-1; + i2 = i-1; if (i2 < 0) i2 = i+1; + for(j=0; j<dimY; j++) { + /* symmetric boundary conditions (Neuman) */ + j1 = j+1; if (j1 == dimY) j1 = j-1; + j2 = j-1; if (j2 < 0) j2 = j+1; + + sum = Dx[(i2)*dimY + (j)] - Dx[(i)*dimY + (j)] + Dy[(i)*dimY + (j2)] - Dy[(i)*dimY + (j)] - Bx[(i2)*dimY + (j)] + Bx[(i)*dimY + (j)] - By[(i)*dimY + (j2)] + By[(i)*dimY + (j)]; + sum += (U[(i1)*dimY + (j)] + U[(i2)*dimY + (j)] + U[(i)*dimY + (j1)] + U[(i)*dimY + (j2)]); + sum *= lambda; + sum += mu*A[(i)*dimY + (j)]; + U[(i)*dimY + (j)] = normConst*sum; + }} + return *U; +} + +float updDxDy_shrink2D(float *U, float *Dx, float *Dy, float *Bx, float *By, int dimX, int dimY, float lambda) +{ + int i,j,i1,j1; + float val1, val11, val2, val22; + + #pragma omp parallel for shared(U) private(i,j,i1,j1,val1,val11,val2,val22) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + /* symmetric boundary conditions (Neuman) */ + i1 = i+1; if (i1 == dimX) i1 = i-1; + j1 = j+1; if (j1 == dimY) j1 = j-1; + + val1 = (U[(i1)*dimY + (j)] - U[(i)*dimY + (j)]) + Bx[(i)*dimY + (j)]; + val2 = (U[(i)*dimY + (j1)] - U[(i)*dimY + (j)]) + By[(i)*dimY + (j)]; + + val11 = fabs(val1) - 1.0f/lambda; if (val11 < 0) val11 = 0; + val22 = fabs(val2) - 1.0f/lambda; if (val22 < 0) val22 = 0; + + if (val1 !=0) Dx[(i)*dimY + (j)] = (val1/fabs(val1))*val11; else Dx[(i)*dimY + (j)] = 0; + if (val2 !=0) Dy[(i)*dimY + (j)] = (val2/fabs(val2))*val22; else Dy[(i)*dimY + (j)] = 0; + + }} + return 1; +} +float updDxDy_shrinkAniso2D(float *U, float *Dx, float *Dy, float *Bx, float *By, int dimX, int dimY, float lambda) +{ + int i,j,i1,j1; + float val1, val11, val2, denom, denom_lam; + denom_lam = 1.0f/lambda; + + #pragma omp parallel for shared(U) private(i,j,i1,j1,val1,val11,val2,denom,denom_lam) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + /* symmetric boundary conditions (Neuman) */ + i1 = i+1; if (i1 == dimX) i1 = i-1; + j1 = j+1; if (j1 == dimY) j1 = j-1; + + val1 = (U[(i1)*dimY + (j)] - U[(i)*dimY + (j)]) + Bx[(i)*dimY + (j)]; + val2 = (U[(i)*dimY + (j1)] - U[(i)*dimY + (j)]) + By[(i)*dimY + (j)]; + + denom = sqrt(val1*val1 + val2*val2); + + val11 = (denom - denom_lam); if (val11 < 0) val11 = 0.0f; + + if (denom != 0.0f) { + Dx[(i)*dimY + (j)] = val11*(val1/denom); + Dy[(i)*dimY + (j)] = val11*(val2/denom); + } + else { + Dx[(i)*dimY + (j)] = 0; + Dy[(i)*dimY + (j)] = 0; + } + }} + return 1; +} +float updBxBy2D(float *U, float *Dx, float *Dy, float *Bx, float *By, int dimX, int dimY) +{ + int i,j,i1,j1; + #pragma omp parallel for shared(U) private(i,j,i1,j1) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + /* symmetric boundary conditions (Neuman) */ + i1 = i+1; if (i1 == dimX) i1 = i-1; + j1 = j+1; if (j1 == dimY) j1 = j-1; + + Bx[(i)*dimY + (j)] = Bx[(i)*dimY + (j)] + ((U[(i1)*dimY + (j)] - U[(i)*dimY + (j)]) - Dx[(i)*dimY + (j)]); + By[(i)*dimY + (j)] = By[(i)*dimY + (j)] + ((U[(i)*dimY + (j1)] - U[(i)*dimY + (j)]) - Dy[(i)*dimY + (j)]); + }} + return 1; +} + + +/* 3D-case related Functions */ +/*****************************************************************/ +float gauss_seidel3D(float *U, float *A, float *Dx, float *Dy, float *Dz, float *Bx, float *By, float *Bz, int dimX, int dimY, int dimZ, float lambda, float mu) +{ + float normConst, d_val, b_val, sum; + int i,j,i1,i2,j1,j2,k,k1,k2; + normConst = 1.0f/(mu + 6.0f*lambda); +#pragma omp parallel for shared(U) private(i,j,i1,i2,j1,j2,k,k1,k2,d_val,b_val,sum) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + /* symmetric boundary conditions (Neuman) */ + i1 = i+1; if (i1 == dimX) i1 = i-1; + i2 = i-1; if (i2 < 0) i2 = i+1; + j1 = j+1; if (j1 == dimY) j1 = j-1; + j2 = j-1; if (j2 < 0) j2 = j+1; + k1 = k+1; if (k1 == dimZ) k1 = k-1; + k2 = k-1; if (k2 < 0) k2 = k+1; + + d_val = Dx[(dimX*dimY)*k + (i2)*dimY + (j)] - Dx[(dimX*dimY)*k + (i)*dimY + (j)] + Dy[(dimX*dimY)*k + (i)*dimY + (j2)] - Dy[(dimX*dimY)*k + (i)*dimY + (j)] + Dz[(dimX*dimY)*k2 + (i)*dimY + (j)] - Dz[(dimX*dimY)*k + (i)*dimY + (j)]; + b_val = -Bx[(dimX*dimY)*k + (i2)*dimY + (j)] + Bx[(dimX*dimY)*k + (i)*dimY + (j)] - By[(dimX*dimY)*k + (i)*dimY + (j2)] + By[(dimX*dimY)*k + (i)*dimY + (j)] - Bz[(dimX*dimY)*k2 + (i)*dimY + (j)] + Bz[(dimX*dimY)*k + (i)*dimY + (j)]; + sum = d_val + b_val; + sum += U[(dimX*dimY)*k + (i1)*dimY + (j)] + U[(dimX*dimY)*k + (i2)*dimY + (j)] + U[(dimX*dimY)*k + (i)*dimY + (j1)] + U[(dimX*dimY)*k + (i)*dimY + (j2)] + U[(dimX*dimY)*k1 + (i)*dimY + (j)] + U[(dimX*dimY)*k2 + (i)*dimY + (j)]; + sum *= lambda; + sum += mu*A[(dimX*dimY)*k + (i)*dimY + (j)]; + U[(dimX*dimY)*k + (i)*dimY + (j)] = normConst*sum; + }}} + return *U; +} + +float updDxDyDz_shrink3D(float *U, float *Dx, float *Dy, float *Dz, float *Bx, float *By, float *Bz, int dimX, int dimY, int dimZ, float lambda) +{ + int i,j,i1,j1,k,k1; + float val1, val11, val2, val22, val3, val33; + + #pragma omp parallel for shared(U) private(i,j,i1,j1,k,k1,val1,val11,val2,val22,val3,val33) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + /* symmetric boundary conditions (Neuman) */ + i1 = i+1; if (i1 == dimX) i1 = i-1; + j1 = j+1; if (j1 == dimY) j1 = j-1; + k1 = k+1; if (k1 == dimZ) k1 = k-1; + + val1 = (U[(dimX*dimY)*k + (i1)*dimY + (j)] - U[(dimX*dimY)*k + (i)*dimY + (j)]) + Bx[(dimX*dimY)*k + (i)*dimY + (j)]; + val2 = (U[(dimX*dimY)*k + (i)*dimY + (j1)] - U[(dimX*dimY)*k + (i)*dimY + (j)]) + By[(dimX*dimY)*k + (i)*dimY + (j)]; + val3 = (U[(dimX*dimY)*k1 + (i)*dimY + (j)] - U[(dimX*dimY)*k + (i)*dimY + (j)]) + Bz[(dimX*dimY)*k + (i)*dimY + (j)]; + + val11 = fabs(val1) - 1.0f/lambda; if (val11 < 0) val11 = 0; + val22 = fabs(val2) - 1.0f/lambda; if (val22 < 0) val22 = 0; + val33 = fabs(val3) - 1.0f/lambda; if (val33 < 0) val33 = 0; + + if (val1 !=0) Dx[(dimX*dimY)*k + (i)*dimY + (j)] = (val1/fabs(val1))*val11; else Dx[(dimX*dimY)*k + (i)*dimY + (j)] = 0; + if (val2 !=0) Dy[(dimX*dimY)*k + (i)*dimY + (j)] = (val2/fabs(val2))*val22; else Dy[(dimX*dimY)*k + (i)*dimY + (j)] = 0; + if (val3 !=0) Dz[(dimX*dimY)*k + (i)*dimY + (j)] = (val3/fabs(val3))*val33; else Dz[(dimX*dimY)*k + (i)*dimY + (j)] = 0; + + }}} + return 1; +} +float updBxByBz3D(float *U, float *Dx, float *Dy, float *Dz, float *Bx, float *By, float *Bz, int dimX, int dimY, int dimZ) +{ + int i,j,k,i1,j1,k1; + #pragma omp parallel for shared(U) private(i,j,k,i1,j1,k1) + for(i=0; i<dimX; i++) { + for(j=0; j<dimY; j++) { + for(k=0; k<dimZ; k++) { + /* symmetric boundary conditions (Neuman) */ + i1 = i+1; if (i1 == dimX) i1 = i-1; + j1 = j+1; if (j1 == dimY) j1 = j-1; + k1 = k+1; if (k1 == dimZ) k1 = k-1; + + Bx[(dimX*dimY)*k + (i)*dimY + (j)] = Bx[(dimX*dimY)*k + (i)*dimY + (j)] + ((U[(dimX*dimY)*k + (i1)*dimY + (j)] - U[(dimX*dimY)*k + (i)*dimY + (j)]) - Dx[(dimX*dimY)*k + (i)*dimY + (j)]); + By[(dimX*dimY)*k + (i)*dimY + (j)] = By[(dimX*dimY)*k + (i)*dimY + (j)] + ((U[(dimX*dimY)*k + (i)*dimY + (j1)] - U[(dimX*dimY)*k + (i)*dimY + (j)]) - Dy[(dimX*dimY)*k + (i)*dimY + (j)]); + Bz[(dimX*dimY)*k + (i)*dimY + (j)] = Bz[(dimX*dimY)*k + (i)*dimY + (j)] + ((U[(dimX*dimY)*k1 + (i)*dimY + (j)] - U[(dimX*dimY)*k + (i)*dimY + (j)]) - Dz[(dimX*dimY)*k + (i)*dimY + (j)]); + + }}} + return 1; +} +/* General Functions */ +/*****************************************************************/ +/* Copy Image */ +float copyIm(float *A, float *B, int dimX, int dimY, int dimZ) +{ + int j; +#pragma omp parallel for shared(A, B) private(j) + for(j=0; j<dimX*dimY*dimZ; j++) B[j] = A[j]; + return *B; +}
\ No newline at end of file diff --git a/main_func/compile_mex.m b/main_func/compile_mex.m new file mode 100644 index 0000000..ea6e3a5 --- /dev/null +++ b/main_func/compile_mex.m @@ -0,0 +1,4 @@ +% compile mex's +mex LLT_model.c CFLAGS="\$CFLAGS -fopenmp -Wall -std=c99" LDFLAGS="\$LDFLAGS -fopenmp" +mex FISTA_TV.c CFLAGS="\$CFLAGS -fopenmp -Wall -std=c99" LDFLAGS="\$LDFLAGS -fopenmp" +mex SplitBregman_TV.c CFLAGS="\$CFLAGS -fopenmp -Wall -std=c99" LDFLAGS="\$LDFLAGS -fopenmp"
\ No newline at end of file diff --git a/main_func/studentst.m b/main_func/studentst.m new file mode 100644 index 0000000..99fed1e --- /dev/null +++ b/main_func/studentst.m @@ -0,0 +1,47 @@ +function [f,g,h,s,k] = studentst(r,k,s) +% Students T penalty with 'auto-tuning' +% +% use: +% [f,g,h,{k,{s}}] = studentst(r) - automatically fits s and k +% [f,g,h,{k,{s}}] = studentst(r,k) - automatically fits s +% [f,g,h,{k,{s}}] = studentst(r,k,s) - use given s and k +% +% input: +% r - residual as column vector +% s - scale (optional) +% k - degrees of freedom (optional) +% +% output: +% f - misfit (scalar) +% g - gradient (column vector) +% h - positive approximation of the Hessian (column vector, Hessian is a diagonal matrix) +% s,k - scale and degrees of freedom +% +% Tristan van Leeuwen, 2012. +% tleeuwen@eos.ubc.ca + +% fit both s and k +if nargin == 1 + opts = optimset('maxFunEvals',1e2); + tmp = fminsearch(@(x)st(r,x(1),x(2)),[1;2],opts); + s = tmp(1); + k = tmp(2); +end + + +if nargin == 2 + opts = optimset('maxFunEvals',1e2); + tmp = fminsearch(@(x)st(r,x,k),[1],opts); + s = tmp(1); +end + +% evaulate penalty +[f,g,h] = st(r,s,k); + + +function [f,g,h] = st(r,s,k) +n = length(r); +c = -n*(gammaln((k+1)/2) - gammaln(k/2) - .5*log(pi*s*k)); +f = c + .5*(k+1)*sum(log(1 + conj(r).*r/(s*k))); +g = (k+1)*r./(s*k + conj(r).*r); +h = (k+1)./(s*k + conj(r).*r); diff --git a/readme.doc b/readme.doc Binary files differnew file mode 100644 index 0000000..705cd01 --- /dev/null +++ b/readme.doc diff --git a/readme.pdf b/readme.pdf Binary files differnew file mode 100644 index 0000000..56f3ff7 --- /dev/null +++ b/readme.pdf diff --git a/supp/RMSE.m b/supp/RMSE.m new file mode 100644 index 0000000..734e4b8 --- /dev/null +++ b/supp/RMSE.m @@ -0,0 +1,7 @@ +function err = RMSE(signal1, signal2) +%RMSE Root Mean Squared Error + +err = sum((signal1 - signal2).^2)/length(signal1); % MSE +err = sqrt(err); % RMSE + +end
\ No newline at end of file diff --git a/supp/add_wedges.m b/supp/add_wedges.m new file mode 100644 index 0000000..8b8f2a7 --- /dev/null +++ b/supp/add_wedges.m @@ -0,0 +1,30 @@ +% create a wedge mask to simulate the missing wedge + +[rows, columns] = size(sino_zing_rings); +grayImage = ones(rows, columns, 'uint8'); +xCoords = [0 360 0]; +yCoords = [35 7 7]; +mask = poly2mask(xCoords, yCoords, rows, columns); +grayImage(mask) = 0; + +xCoords = [727 360 727]; +yCoords = [35 7 7]; +mask = poly2mask(xCoords, yCoords, rows, columns); +grayImage(mask) = 0; + +xCoords = [0 360 0]; +yCoords = [145 173 173]; +mask = poly2mask(xCoords, yCoords, rows, columns); +grayImage(mask) = 0; + +xCoords = [727 360 727]; +yCoords = [145 173 173]; +mask = poly2mask(xCoords, yCoords, rows, columns); +grayImage(mask) = 0; + +grayImage(1:7,:) = 0; +grayImage(173:end,:) = 0; + +%figure; imshow(grayImage, [0 1]); +MW_sino_artifacts = sino_zing_rings.*double(grayImage); +%Dweights = Dweights.*double(grayImage);
\ No newline at end of file diff --git a/supp/filtersinc.m b/supp/filtersinc.m new file mode 100644 index 0000000..6c29c98 --- /dev/null +++ b/supp/filtersinc.m @@ -0,0 +1,28 @@ +function g = filtersinc(PR) + + +% filtersinc.m +% +% Written by Waqas Akram +% +% "a": This parameter varies the filter magnitude response. +% When "a" is very small (a<<1), the response approximates |w| +% As "a" is increased, the filter response starts to +% roll off at high frequencies. +a = 1; + +[Length, Count] = size(PR); +w = [-pi:(2*pi)/Length:pi-(2*pi)/Length]; + +rn1 = abs(2/a*sin(a.*w./2)); +rn2 = sin(a.*w./2); +rd = (a*w)./2; +r = rn1*(rn2/rd)^2; + +f = fftshift(r); +for i = 1:Count + IMG = fft(PR(:,i)); + fimg = IMG.*f'; + g(:,i) = ifft(fimg); +end +g = real(g);
\ No newline at end of file diff --git a/supp/my_red_yellowMAP.mat b/supp/my_red_yellowMAP.mat Binary files differnew file mode 100644 index 0000000..c2a5b87 --- /dev/null +++ b/supp/my_red_yellowMAP.mat diff --git a/supp/ssim_index.m b/supp/ssim_index.m new file mode 100644 index 0000000..4fa7a79 --- /dev/null +++ b/supp/ssim_index.m @@ -0,0 +1,181 @@ +function [mssim, ssim_map] = ssim_index(img1, img2, K, window, L) + +%======================================================================== +%SSIM Index, Version 1.0 +%Copyright(c) 2003 Zhou Wang +%All Rights Reserved. +% +%This is an implementation of the algorithm for calculating the +%Structural SIMilarity (SSIM) index between two images. Please refer +%to the following paper: +% +%Z. Wang, A. C. Bovik, H. R. Sheikh, and E. P. Simoncelli, "Image +%quality assessment: From error visibility to structural similarity" +%IEEE Transactios on Image Processing, vol. 13, no. 4, pp.600-612, +%Apr. 2004. +% +%Kindly report any suggestions or corrections to zhouwang@ieee.org +% +%---------------------------------------------------------------------- +% +%Input : (1) img1: the first image being compared +% (2) img2: the second image being compared +% (3) K: constants in the SSIM index formula (see the above +% reference). defualt value: K = [0.01 0.03] +% (4) window: local window for statistics (see the above +% reference). default widnow is Gaussian given by +% window = fspecial('gaussian', 11, 1.5); +% (5) L: dynamic range of the images. default: L = 255 +% +%Output: (1) mssim: the mean SSIM index value between 2 images. +% If one of the images being compared is regarded as +% perfect quality, then mssim can be considered as the +% quality measure of the other image. +% If img1 = img2, then mssim = 1. +% (2) ssim_map: the SSIM index map of the test image. The map +% has a smaller size than the input images. The actual size: +% size(img1) - size(window) + 1. +% +%Default Usage: +% Given 2 test images img1 and img2, whose dynamic range is 0-255 +% +% [mssim ssim_map] = ssim_index(img1, img2); +% +%Advanced Usage: +% User defined parameters. For example +% +% K = [0.05 0.05]; +% window = ones(8); +% L = 100; +% [mssim ssim_map] = ssim_index(img1, img2, K, window, L); +% +%See the results: +% +% mssim %Gives the mssim value +% imshow(max(0, ssim_map).^4) %Shows the SSIM index map +% +%======================================================================== + + +if (nargin < 2 | nargin > 5) + ssim_index = -Inf; + ssim_map = -Inf; + return; +end + +if (size(img1) ~= size(img2)) + ssim_index = -Inf; + ssim_map = -Inf; + return; +end + +[M N] = size(img1); + +if (nargin == 2) + if ((M < 11) | (N < 11)) % ͼССû塣 + ssim_index = -Inf; + ssim_map = -Inf; + return + end + window = fspecial('gaussian', 11, 1.5); % һƫ1.511*11ĸ˹ͨ˲ + K(1) = 0.01; % default settings + K(2) = 0.03; % + L = 255; % +end + +if (nargin == 3) + if ((M < 11) | (N < 11)) + ssim_index = -Inf; + ssim_map = -Inf; + return + end + window = fspecial('gaussian', 11, 1.5); + L = 255; + if (length(K) == 2) + if (K(1) < 0 | K(2) < 0) + ssim_index = -Inf; + ssim_map = -Inf; + return; + end + else + ssim_index = -Inf; + ssim_map = -Inf; + return; + end +end + +if (nargin == 4) + [H W] = size(window); + if ((H*W) < 4 | (H > M) | (W > N)) + ssim_index = -Inf; + ssim_map = -Inf; + return + end + L = 255; + if (length(K) == 2) + if (K(1) < 0 | K(2) < 0) + ssim_index = -Inf; + ssim_map = -Inf; + return; + end + else + ssim_index = -Inf; + ssim_map = -Inf; + return; + end +end + +if (nargin == 5) + [H W] = size(window); + if ((H*W) < 4 | (H > M) | (W > N)) + ssim_index = -Inf; + ssim_map = -Inf; + return + end + if (length(K) == 2) + if (K(1) < 0 | K(2) < 0) + ssim_index = -Inf; + ssim_map = -Inf; + return; + end + else + ssim_index = -Inf; + ssim_map = -Inf; + return; + end +end +%% +C1 = (K(1)*L)^2; % C1Lxyá +C2 = (K(2)*L)^2; % C2ԱȶCxyá +window = window/sum(sum(window)); %˲һ +img1 = double(img1); +img2 = double(img2); + +mu1 = filter2(window, img1, 'valid'); % ͼ˲ӼȨ +mu2 = filter2(window, img2, 'valid'); % ͼ˲ӼȨ + +mu1_sq = mu1.*mu1; % Uxƽֵ +mu2_sq = mu2.*mu2; % Uyƽֵ +mu1_mu2 = mu1.*mu2; % Ux*Uyֵ + +sigma1_sq = filter2(window, img1.*img1, 'valid') - mu1_sq; % sigmax +sigma2_sq = filter2(window, img2.*img2, 'valid') - mu2_sq; % sigmay +sigma12 = filter2(window, img1.*img2, 'valid') - mu1_mu2; % sigmaxy + +if (C1 > 0 & C2 > 0) + ssim_map = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))./((mu1_sq + mu2_sq + C1).*(sigma1_sq + sigma2_sq + C2)); +else + numerator1 = 2*mu1_mu2 + C1; + numerator2 = 2*sigma12 + C2; + denominator1 = mu1_sq + mu2_sq + C1; + denominator2 = sigma1_sq + sigma2_sq + C2; + ssim_map = ones(size(mu1)); + index = (denominator1.*denominator2 > 0); + ssim_map(index) = (numerator1(index).*numerator2(index))./(denominator1(index).*denominator2(index)); + index = (denominator1 ~= 0) & (denominator2 == 0); + ssim_map(index) = numerator1(index)./denominator1(index); +end + +mssim = mean2(ssim_map); + +return
\ No newline at end of file diff --git a/supp/subplot_tight.m b/supp/subplot_tight.m new file mode 100644 index 0000000..0b0cbd5 --- /dev/null +++ b/supp/subplot_tight.m @@ -0,0 +1 @@ +function vargout=subplot_tight(m, n, p, margins, varargin)
%% subplot_tight
% A subplot function substitude with margins user tunabble parameter.
%
%% Syntax
% h=subplot_tight(m, n, p);
% h=subplot_tight(m, n, p, margins);
% h=subplot_tight(m, n, p, margins, subplotArgs...);
%
%% Description
% Our goal is to grant the user the ability to define the margins between neighbouring
% subplots. Unfotrtunately Matlab subplot function lacks this functionality, and the
% margins between subplots can reach 40% of figure area, which is pretty lavish. While at
% the begining the function was implememnted as wrapper function for Matlab function
% subplot, it was modified due to axes del;etion resulting from what Matlab subplot
% detected as overlapping. Therefore, the current implmenetation makes no use of Matlab
% subplot function, using axes instead. This can be problematic, as axis and subplot
% parameters are quie different. Set isWrapper to "True" to return to wrapper mode, which
% fully supports subplot format.
%
%% Input arguments (defaults exist):
% margins- two elements vector [vertical,horizontal] defining the margins between
% neighbouring axes. Default value is 0.04
%
%% Output arguments
% same as subplot- none, or axes handle according to function call.
%
%% Issues & Comments
% - Note that if additional elements are used in order to be passed to subplot, margins
% parameter must be defined. For default margins value use empty element- [].
% -
%
%% Example
% close all;
% img=imread('peppers.png');
% figSubplotH=figure('Name', 'subplot');
% figSubplotTightH=figure('Name', 'subplot_tight');
% nElems=17;
% subplotRows=ceil(sqrt(nElems)-1);
% subplotRows=max(1, subplotRows);
% subplotCols=ceil(nElems/subplotRows);
% for iElem=1:nElems
% figure(figSubplotH);
% subplot(subplotRows, subplotCols, iElem);
% imshow(img);
% figure(figSubplotTightH);
% subplot_tight(subplotRows, subplotCols, iElem, [0.0001]);
% imshow(img);
% end
%
%% See also
% - subplot
%
%% Revision history
% First version: Nikolay S. 2011-03-29.
% Last update: Nikolay S. 2012-05-24.
%
% *List of Changes:*
% 2012-05-24
% Non wrapping mode (based on axes command) added, to deal with an issue of disappearing
% subplots occuring with massive axes.
%% Default params
isWrapper=false;
if (nargin<4) || isempty(margins)
margins=[0.04,0.04]; % default margins value- 4% of figure
end
if length(margins)==1
margins(2)=margins;
end
%note n and m are switched as Matlab indexing is column-wise, while subplot indexing is row-wise :(
[subplot_col,subplot_row]=ind2sub([n,m],p);
height=(1-(m+1)*margins(1))/m; % single subplot height
width=(1-(n+1)*margins(2))/n; % single subplot width
% note subplot suppors vector p inputs- so a merged subplot of higher dimentions will be created
subplot_cols=1+max(subplot_col)-min(subplot_col); % number of column elements in merged subplot
subplot_rows=1+max(subplot_row)-min(subplot_row); % number of row elements in merged subplot
merged_height=subplot_rows*( height+margins(1) )- margins(1); % merged subplot height
merged_width= subplot_cols*( width +margins(2) )- margins(2); % merged subplot width
merged_bottom=(m-max(subplot_row))*(height+margins(1)) +margins(1); % merged subplot bottom position
merged_left=min(subplot_col)*(width+margins(2))-width; % merged subplot left position
pos=[merged_left, merged_bottom, merged_width, merged_height];
if isWrapper
h=subplot(m, n, p, varargin{:}, 'Units', 'Normalized', 'Position', pos);
else
h=axes('Position', pos, varargin{:});
end
if nargout==1
vargout=h;
end
\ No newline at end of file diff --git a/supp/zing_rings_add.m b/supp/zing_rings_add.m new file mode 100644 index 0000000..023ac27 --- /dev/null +++ b/supp/zing_rings_add.m @@ -0,0 +1,84 @@ +% uncomment this part of script to generate data with different noise characterisitcs + +fprintf('%s\n', 'Generating Projection Data...'); +multfactor = 1000; +% Creating RHS (b) - the sinogram (using a strip projection model) +vol_geom = astra_create_vol_geom(N, N); +proj_geom = astra_create_proj_geom('parallel', 1.0, P, theta_rad); +proj_id_temp = astra_create_projector('strip', proj_geom, vol_geom); +[sinogram_id, sinogramIdeal] = astra_create_sino(phantom./multfactor, proj_id_temp); +astra_mex_data2d('delete',sinogram_id); +astra_mex_algorithm('delete',proj_id_temp); +% +% % adding Gaussian noise +% eta = 0.04; % Relative noise level +% E = randn(size(sinogram)); +% sinogram = sinogram + eta*norm(sinogram,'fro')*E/norm(E,'fro'); % adding noise to the sinogram +% sinogram(sinogram<0) = 0; +% clear E; + +%% +% adding zingers +val_offset = 0; +sino_zing = sinogramIdeal; +vec1 = [60, 80, 80, 70, 70, 90, 90, 40, 130, 145, 155, 125]; +vec2 = [350, 450, 190, 500, 250, 530, 330, 230, 550, 250, 450, 195]; +for jj = 1:length(vec1) + for i1 = -2:2 + for j1 = -2:2 + sino_zing(vec1(jj)+i1, vec2(jj)+j1) = val_offset; + end + end +end + +% adding stripes into the signogram +sino_zing_rings = sino_zing; +coeff = linspace2(0.01,0.15,180); +vmax = max(sinogramIdeal(:)); +sino_zing_rings(1:180,120) = sino_zing_rings(1:180,120) + vmax*0.13; +sino_zing_rings(80:180,209) = sino_zing_rings(80:180,209) + vmax*0.14; +sino_zing_rings(50:110,210) = sino_zing_rings(50:110,210) + vmax*0.12; +sino_zing_rings(1:180,211) = sino_zing_rings(1:180,211) + vmax*0.14; +sino_zing_rings(1:180,300) = sino_zing_rings(1:180,300) + vmax*coeff(:); +sino_zing_rings(1:180,301) = sino_zing_rings(1:180,301) + vmax*0.14; +sino_zing_rings(10:100,302) = sino_zing_rings(10:100,302) + vmax*0.15; +sino_zing_rings(90:180,350) = sino_zing_rings(90:180,350) + vmax*0.11; +sino_zing_rings(60:140,410) = sino_zing_rings(60:140,410) + vmax*0.12; +sino_zing_rings(1:180,411) = sino_zing_rings(1:180,411) + vmax*0.14; +sino_zing_rings(1:180,412) = sino_zing_rings(1:180,412) + vmax*coeff(:); +sino_zing_rings(1:180,413) = sino_zing_rings(1:180,413) + vmax*coeff(:); +sino_zing_rings(1:180,500) = sino_zing_rings(1:180,500) - vmax*0.12; +sino_zing_rings(1:180,501) = sino_zing_rings(1:180,501) - vmax*0.12; +sino_zing_rings(1:180,550) = sino_zing_rings(1:180,550) + vmax*0.11; +sino_zing_rings(1:180,551) = sino_zing_rings(1:180,551) + vmax*0.11; +sino_zing_rings(1:180,552) = sino_zing_rings(1:180,552) + vmax*0.11; + +sino_zing_rings(sino_zing_rings < 0) = 0; +%% + +% adding Poisson noise +dose = 50000; +dataExp = dose.*exp(-sino_zing_rings); % noiseless raw data +dataPnoise = astra_add_noise_to_sino(dataExp,2*dose); % pre-log noisy raw data (weights) +Dweights = dataPnoise; +sinogram = log(dose./dataPnoise); %log corrected data -> sinogram +sinogram = abs(sinogram); +clear dataPnoise dataExp + +% normalizing +sinogram = sinogram.*multfactor; +sino_zing_rings = sinogram; +Dweights = multfactor./Dweights; + +% +% figure(1); +% set(gcf, 'Position', get(0,'Screensize')); +% subplot(1,2,1); imshow(phantom,[0 0.6]); title('Ideal Phantom'); colorbar; +% subplot(1,2,2); imshow(sinogram,[0 180]); title('Noisy Sinogram'); colorbar; +% colormap(cmapnew); + +% figure; +% set(gcf, 'Position', get(0,'Screensize')); +% subplot(1,2,1); imshow(sinogramIdeal,[0 180]); title('Ideal Sinogram'); colorbar; +% imshow(sino_zing_rings,[0 180]); title('Noisy Sinogram with zingers and stripes'); colorbar; +% colormap(cmapnew);
\ No newline at end of file diff --git a/~$readme.doc b/~$readme.doc Binary files differnew file mode 100644 index 0000000..3de8b72 --- /dev/null +++ b/~$readme.doc |