From b325933591cd1d0d534a90ad5a417c2d03a0c6f3 Mon Sep 17 00:00:00 2001 From: Pasca Date: Wed, 29 Mar 2017 16:46:28 +0100 Subject: Initial revision --- 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 +++++ 6 files changed, 1497 insertions(+) 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 (limited to 'main_func') 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