From b325933591cd1d0d534a90ad5a417c2d03a0c6f3 Mon Sep 17 00:00:00 2001 From: Pasca Date: Wed, 29 Mar 2017 16:46:28 +0100 Subject: Initial revision --- Readme.md | 82 +++++++++ data/phantom_bone512.mat | Bin 0 -> 408035 bytes data/sino3D_dendrites.mat | Bin 0 -> 27797233 bytes data/sino_basalt.mat | Bin 0 -> 1109887 bytes demo/Demo1.m | 160 ++++++++++++++++ demo/Demo2.m | 156 ++++++++++++++++ demo/DemoRD1.m | 99 ++++++++++ demo/DemoRD2.m | 130 +++++++++++++ license.txt | 27 +++ main_func/FISTA_REC.m | 338 ++++++++++++++++++++++++++++++++++ main_func/FISTA_TV.c | 331 ++++++++++++++++++++++++++++++++++ main_func/LLT_model.c | 431 ++++++++++++++++++++++++++++++++++++++++++++ main_func/SplitBregman_TV.c | 346 +++++++++++++++++++++++++++++++++++ main_func/compile_mex.m | 4 + main_func/studentst.m | 47 +++++ readme.doc | Bin 0 -> 21504 bytes readme.pdf | Bin 0 -> 61855 bytes supp/RMSE.m | 7 + supp/add_wedges.m | 30 +++ supp/filtersinc.m | 28 +++ supp/my_red_yellowMAP.mat | Bin 0 -> 1761 bytes supp/ssim_index.m | 181 +++++++++++++++++++ supp/subplot_tight.m | 1 + supp/zing_rings_add.m | 84 +++++++++ ~$readme.doc | Bin 0 -> 162 bytes 25 files changed, 2482 insertions(+) create mode 100644 Readme.md create mode 100644 data/phantom_bone512.mat create mode 100644 data/sino3D_dendrites.mat create mode 100644 data/sino_basalt.mat create mode 100644 demo/Demo1.m create mode 100644 demo/Demo2.m create mode 100644 demo/DemoRD1.m create mode 100644 demo/DemoRD2.m create mode 100644 license.txt create mode 100644 main_func/FISTA_REC.m create mode 100644 main_func/FISTA_TV.c create mode 100644 main_func/LLT_model.c create mode 100644 main_func/SplitBregman_TV.c create mode 100644 main_func/compile_mex.m create mode 100644 main_func/studentst.m create mode 100644 readme.doc create mode 100644 readme.pdf create mode 100644 supp/RMSE.m create mode 100644 supp/add_wedges.m create mode 100644 supp/filtersinc.m create mode 100644 supp/my_red_yellowMAP.mat create mode 100644 supp/ssim_index.m create mode 100644 supp/subplot_tight.m create mode 100644 supp/zing_rings_add.m create mode 100644 ~$readme.doc 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 new file mode 100644 index 0000000..ae3c7d8 Binary files /dev/null and b/data/phantom_bone512.mat differ diff --git a/data/sino3D_dendrites.mat b/data/sino3D_dendrites.mat new file mode 100644 index 0000000..dc1400d Binary files /dev/null and b/data/sino3D_dendrites.mat differ diff --git a/data/sino_basalt.mat b/data/sino_basalt.mat new file mode 100644 index 0000000..164d144 Binary files /dev/null and b/data/sino_basalt.mat differ 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 +#include +#include +#include +#include +#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 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 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 +#include +#include +#include +#include +#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 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 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= 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= 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 maxval) maxval = U[dimX*dimY*k + i*dimY + j]; + }}} + + if (maxval != 0.0f) { + for(i=0; i= 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= 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 +#include +#include +#include +#include +#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 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 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 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 new file mode 100644 index 0000000..3de8b72 Binary files /dev/null and b/~$readme.doc differ -- cgit v1.2.3