├── tomo.png ├── comb_projmat.m ├── test ├── test_00_imtest.m ├── build_comp_area.m ├── test_WeightMatrix.m ├── test_combWeightProj.m ├── build_comp_accuracy.m ├── demo_crop.m ├── test_WeightMatrix_area.m ├── test_10_noise_fbp.m ├── test_01_FilterCompare.m ├── build_comp.m ├── demo_weightmat.m ├── test_08_rand_vs_all.m ├── test_09_aniso_fbp.m ├── test_12_radon_hough.m ├── test_04_ART.m ├── test_art_guess.m ├── test_11_continuity.m ├── test_06_LSQR.m ├── ART2Dreconst.m ├── test_03_FBP.m ├── build2.m ├── test_02_Reconstruction.m ├── build1.m ├── test_05_3D_reconstruction.m ├── build5.m ├── build7.m ├── build3.m ├── build8.m ├── build6.m ├── build4.m ├── test_07_Compare_simple_area_weightmat.m ├── demo_tomo.m └── test_buildweight.m ├── imtool ├── create_filter_bank.m ├── impiece.m ├── immedfilt.m ├── imrotatesymm.m ├── imblendlabel.m ├── imblend.m ├── testmat.m ├── create_sample_grid.m ├── flow_warp.m ├── imblendmask.m ├── imwarpH.m ├── flow_unwarp.m ├── nonuniform_sampling.m ├── chessmat.m ├── foveated.m ├── imscale.m ├── immontage.m └── imtest.m ├── tomo_reconstruction_sart.m ├── tomo_reconstruction_lsqr.m ├── solver └── solver_sart.m ├── tomo_projection_2d.m ├── utils ├── filtersinc.m ├── hpf.m └── impad.m ├── tomo_reconstruction_bp.m ├── combWeightProj.m ├── tomo_reconstruction.m ├── README.md ├── tomo_reconstruction_fbp.m ├── demo.m └── backproj.m /tomo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phymhan/matlab-tomo-2d/HEAD/tomo.png -------------------------------------------------------------------------------- /comb_projmat.m: -------------------------------------------------------------------------------- 1 | function projmat_art = comb_projmat(proj_mat1,proj_mat2)%,angles1,angles2) 2 | projmat_art = [proj_mat1,fliplr(proj_mat2)]; 3 | end 4 | -------------------------------------------------------------------------------- /test/test_00_imtest.m: -------------------------------------------------------------------------------- 1 | %TEST 00: Test Image 2 | clear 3 | clc 4 | fprintf('TEST 00: Show test images.\r'); 5 | for k = 0:10 6 | figure 7 | imshow(imtest(k)) 8 | end 9 | -------------------------------------------------------------------------------- /imtool/create_filter_bank.m: -------------------------------------------------------------------------------- 1 | function fb = create_filter_bank(fsize, fnum) 2 | sigma = linspace(0.05, (fsize-1)/4, fnum); 3 | fb = zeros([fsize fsize 1 fnum]); 4 | for i = 1:fnum 5 | fb(:,:,1,i) = fspecial('Gaussian', fsize, sigma(i)); 6 | end -------------------------------------------------------------------------------- /test/build_comp_area.m: -------------------------------------------------------------------------------- 1 | clear 2 | clc 3 | im = testmat(109,105); 4 | a = 0:17:179; 5 | [W5,p5,t5] = build5(im,a); 6 | [W7,p7,t7] = build7(im,a); 7 | [W8,p8,t8] = build8(im,a); 8 | fprintf(['\nElapsed time:\nbuild5 %f seconds'... 9 | '\nbuild7 %f seconds\nbuild8 %f seconds\r'],... 10 | t5,t7,t8); 11 | %Variance 12 | % var_78 = var(W7(:)-W8(:)); 13 | % fprintf('\nVariance8: %f\r',var_78); 14 | -------------------------------------------------------------------------------- /tomo_reconstruction_sart.m: -------------------------------------------------------------------------------- 1 | function im_rec = tomo_reconstruction_sart(W, p, siz, n_it) 2 | % Tomographic reconstruction using SART method. 3 | % 4 | % 08-Aug-2013 20:44:52 5 | % hanligong@gmail.com 6 | 7 | if nargin < 4 8 | n_it = 100; 9 | end 10 | %siz = size(im); 11 | %[W, p, ~, ~] = buildWeightMatrix(im,angles); 12 | f = solver_sart(W,p,n_it); 13 | im_rec = reshape(f,siz); 14 | end 15 | -------------------------------------------------------------------------------- /imtool/impiece.m: -------------------------------------------------------------------------------- 1 | function imp = impiece(im1,im2,ix) 2 | % For each layer, im1(ix) = im2(ix). Be sure im1 and im2 have the same 3 | % scale. 4 | 5 | for kc = 1:size(im1,3) 6 | m1 = im1(:,:,kc); 7 | m2 = im2(:,:,kc); 8 | m1(ix) = m2(ix); 9 | im1(:,:,kc) = m1; 10 | end 11 | if nargout < 1 12 | caller = inputname(1); 13 | assignin('caller',caller,im1); 14 | end 15 | imp = im1; 16 | end 17 | -------------------------------------------------------------------------------- /tomo_reconstruction_lsqr.m: -------------------------------------------------------------------------------- 1 | function im_rec = tomo_reconstruction_lsqr(W, p, siz, tol, n_it) 2 | % Tomographic reconstruction using LSQR method. 3 | % 4 | % 09-Aug-2013 09:53:51 5 | % hanligong@gmail.com 6 | 7 | if nargin < 5 8 | n_it = 100; 9 | if nargin < 4 10 | tol = 1e-6; 11 | end 12 | end 13 | %siz = size(im); 14 | %[W, p, ~, ~] = buildWeightMatrix(im,angles); 15 | f = lsqr(W,p,tol,n_it); 16 | im_rec = reshape(f,siz); 17 | end 18 | -------------------------------------------------------------------------------- /solver/solver_sart.m: -------------------------------------------------------------------------------- 1 | function x = solver_sart(A,b,n_it) 2 | N = size(A,2); 3 | x = zeros(N,1); 4 | Z1 = sum(A,1); 5 | Z2 = sum(A,2); 6 | lambda =1; 7 | cnt = 0; 8 | for ki = 1:n_it 9 | cnt = cnt+1; 10 | if cnt >= n_it/10 11 | fprintf('\nIteration %d\r',ki); 12 | cnt = 0; 13 | end 14 | % for kj = 1:N 15 | % x(kj) = x(kj)+lambda*sum((b-A*x)./Z2.*A(:,kj),1)/Z1(kj); 16 | % end 17 | x = x+(lambda*sum(bsxfun(@times,A,(b-A*x)./Z2))./Z1)'; 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /imtool/immedfilt.m: -------------------------------------------------------------------------------- 1 | function imOut = immedfilt(im, siz, padopt, outType) 2 | % function immedfilt(im, siz, padopt, outType) 3 | 4 | if ~exist('outType', 'var') || isempty(outType) 5 | outType = class(im); 6 | end 7 | if ~exist('padopt', 'var') || isempty(padopt) 8 | padopt = 'symmetric'; 9 | end 10 | if ~exist('siz', 'var') || isempty(siz) 11 | siz = [3 3]; 12 | end 13 | [height, width, nChnl] = size(im); 14 | imOut = zeros(height, width, nChnl); 15 | for iChnl = 1:nChnl 16 | imOut(:,:,iChnl) = medfilt2(im(:,:,iChnl), siz, padopt); 17 | end 18 | imOut = cast(im, outType); 19 | -------------------------------------------------------------------------------- /test/test_WeightMatrix.m: -------------------------------------------------------------------------------- 1 | clear 2 | clc 3 | n = 9; 4 | x = testmat(n)'; 5 | figure('name','original x'); 6 | imagesc(x) 7 | %imagesc(x(:)) 8 | colormap gray 9 | fprintf('original matrix x:\r') 10 | disp(x) 11 | [W,p] = buildWeightMatrix(x,[0 90]); 12 | fprintf('weighting factor matrix W:\r') 13 | disp(W) 14 | fprintf('p:\r') 15 | disp(p) 16 | x_sol = lsqr(W,p,1e-6,1000); 17 | y = reshape(x_sol,[n n]); 18 | fprintf('solution y:\r') 19 | disp(y) 20 | figure('name','solution(lsqr)'); 21 | imagesc(y) 22 | %imagesc(y(:)) 23 | colormap gray 24 | diff_var = var(x(:)-y(:)); 25 | fprintf('\nVariance: %f\r',diff_var); 26 | -------------------------------------------------------------------------------- /imtool/imrotatesymm.m: -------------------------------------------------------------------------------- 1 | function imOut = imrotatesymm(A, angled, method) 2 | % function imOut = IMROTATESYMM(A, angled, method) 3 | % July 18, 2016 4 | 5 | if ~exist('method', 'var') || isempty(method) 6 | method = 'bilinear'; 7 | end 8 | [height, width, ~] = size(A); 9 | imLr = fliplr(A); 10 | imUd = flipud(A); 11 | imPi = fliplr(imUd); 12 | imSymm = cat(1, cat(2, imPi, imUd, imPi), cat(2, imLr, A, imLr), ... 13 | cat(2, imPi, imUd, imPi)); 14 | imRot = imrotate(imSymm, angled, method, 'crop'); 15 | trans = [0 0]; 16 | imOut = imRot(trans(1)+height+1:trans(1)+height+height, ... 17 | trans(2)+width+1:trans(2)+width+width, :); 18 | -------------------------------------------------------------------------------- /test/test_combWeightProj.m: -------------------------------------------------------------------------------- 1 | % im = imtest('head',128); 2 | % a = 0:6:179; 3 | % sz = size(im); 4 | % [projmat,~] = tomoproj2d(im,a); 5 | % [W,ix] = buildWeightMatrixArea(sz,a,0); 6 | % [W,p] = combWeightProj(W,ix,projmat); 7 | % im_rec = tomo_recon_lsqr(W,p,sz,1e-3,3500); 8 | % im_rec = uint8(imscale(im_rec)); 9 | % imshow(im_rec); 10 | 11 | fuck 12 | sz = [192 192]; 13 | a0 = 0; 14 | da = 2; 15 | a1 = 179.99; 16 | a = a0:da:a1; 17 | [W,ix] = buildWeightMatrixArea(sz,a,0); 18 | filename = [num2str(sz(1)) 'x' num2str(sz(2)) '_' ... 19 | num2str(a0) '..' num2str(da) '..' num2str(a1) '.mat']; 20 | save(filename,'W','ix') 21 | figure('color',[0 1 0]) 22 | -------------------------------------------------------------------------------- /test/build_comp_accuracy.m: -------------------------------------------------------------------------------- 1 | clear 2 | clc 3 | n = 97; 4 | ang = 0:6:179; 5 | %x = testmat(n)'; 6 | x = rand(n); 7 | figure('name','original x'); 8 | imagesc(x) 9 | colormap gray 10 | [W7,p7,~] = build7(x,ang); 11 | [W8,p8,~] = build8(x,ang); 12 | x_sol7 = lsqr(W7,p7,1e-6,1000); 13 | x_sol8 = lsqr(W8,p8,1e-6,1000); 14 | y7 = reshape(x_sol7,[n n]); 15 | y8 = reshape(x_sol8,[n n]); 16 | figure('name','7') 17 | imagesc(y7) 18 | colormap gray 19 | figure('name','8') 20 | imagesc(y8) 21 | colormap gray 22 | diff_var7 = var(x(:)-y7(:)); 23 | fprintf('\nVariance7: %f\r',diff_var7); 24 | diff_var8 = var(x(:)-y8(:)); 25 | fprintf('\nVariance8: %f\r',diff_var8); 26 | -------------------------------------------------------------------------------- /imtool/imblendlabel.m: -------------------------------------------------------------------------------- 1 | function Ic = imblendlabel(I, L, C, alpha) 2 | % I, original image 3 | % L, segmentation result 4 | % C, color code where i-th row defines color for i-th label 5 | % alpha, Ic = (1-alpha)*I + alpha*L 6 | % Ic, output image with colorized label 7 | 8 | if ~exist('alpha', 'var') || isempty(alpha), alpha = 0.8; end 9 | sz = [size(I,1) size(I,2)]; 10 | if size(I,3) == 1, I = repmat(I, [1 1 3]); end 11 | L = imresize(L, sz, 'nearest'); 12 | Lc = zeros([sz 3]); 13 | for l = setdiff(unique(L(:))', 0) 14 | mask = L==l; 15 | for j = 1:3 16 | Lc(:,:,j) = Lc(:,:,j) + mask*C(l,j); 17 | end 18 | end 19 | Ic = (1-alpha)*I + alpha*cast(Lc,'like',I); 20 | -------------------------------------------------------------------------------- /test/demo_crop.m: -------------------------------------------------------------------------------- 1 | im = imtest('cat',512); 2 | angles = 0:1:179; 3 | im = double(im); 4 | %% Compute projection matrix 5 | [projmat,D] = tomo_projection_2d(im,angles); 6 | %% Reconstruct image 7 | im_rec = tomo_reconstruction_fbp(projmat,angles); 8 | %% Crop reconstructed image 9 | [m,n] = size(im); 10 | m_pad = floor((D-m)/2); 11 | n_pad = floor((D-n)/2); 12 | im_crop = im_rec(m_pad+1:m_pad+m,n_pad+1:n_pad+n); 13 | im_crop = uint8(imscale(im_crop)); 14 | figure 15 | imshow(im_crop) 16 | %% Crop reconstructed image by calling function 'imcrop_tomo' 17 | im_crop2 = imcrop_tomo(im_rec,[m,n]); 18 | im_crop2 = uint8(imscale(im_crop2)); 19 | figure 20 | imshow(im_crop2) 21 | -------------------------------------------------------------------------------- /test/test_WeightMatrix_area.m: -------------------------------------------------------------------------------- 1 | clear 2 | clc 3 | n = 9; 4 | %x = testmat(n)'; 5 | x = rand(n); 6 | figure('name','original x'); 7 | imagesc(x) 8 | %imagesc(x(:)) 9 | colormap gray 10 | fprintf('original matrix x:\r') 11 | %disp(x) 12 | [W,p,~] = build7(x,0:18:179); 13 | fprintf('weighting factor matrix W:\r') 14 | %disp(W) 15 | fprintf('p:\r') 16 | %disp(p) 17 | x_sol = lsqr(W,p,1e-6,1000); 18 | %x_sol = myart_solve(W,p,1000); 19 | y = reshape(x_sol,[n n]); 20 | fprintf('solution y:\r') 21 | %disp(y) 22 | figure('name','solution(lsqr)'); 23 | imagesc(y) 24 | %imagesc(y(:)) 25 | colormap gray 26 | diff_var = var(x(:)-y(:)); 27 | fprintf('\nVariance: %f\r',diff_var); 28 | -------------------------------------------------------------------------------- /tomo_projection_2d.m: -------------------------------------------------------------------------------- 1 | function [proj_mat, D] = tomo_projection_2d(im,angles) 2 | %TOMO_PROJECTION_2D [proj_mat,D] = tomo_projection_2d(im,angles) 3 | % unit of angles: Degree; 4 | % projection direction: When angle == 0, X-ray passes through vertical 5 | % (up-down) direction. 6 | % 7 | % 02-Aug-2013 14:07:06 8 | % hanligong@gmail.com 9 | 10 | %Pad image 11 | % [im_pad,D] = impad(im); 12 | [im_pad,D] = impad(im,'diagonal',1); %set im_margin to 1 13 | %Calculate projection 14 | num_proj = length(angles); 15 | proj_mat = zeros(num_proj,D); 16 | for k = 1:num_proj 17 | im_rot = imrotate(im_pad,-angles(k),'bilinear','crop'); 18 | proj_mat(k,:) = sum(im_rot,1); 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /utils/filtersinc.m: -------------------------------------------------------------------------------- 1 | function g = filtersinc(PR) 2 | 3 | % filtersinc.m 4 | % 5 | % Written by Waqas Akram 6 | % 7 | % "a": This parameter varies the filter magnitude response. 8 | % When "a" is very small (a<<1), the response approximates |w| 9 | % As "a" is increased, the filter response starts to 10 | % roll off at high frequencies. 11 | a = 1; 12 | 13 | [Length, Count] = size(PR); 14 | w = [-pi:(2*pi)/Length:pi-(2*pi)/Length]; 15 | 16 | rn1 = abs(2/a*sin(a.*w./2)); 17 | rn2 = sin(a.*w./2); 18 | rd = (a*w)./2; 19 | r = rn1*(rn2/rd)^2; 20 | 21 | f = fftshift(r); 22 | for i = 1:Count 23 | IMG = fft(PR(:,i)); 24 | fimg = IMG.*f'; 25 | g(:,i) = ifft(fimg); 26 | end 27 | g = real(g); -------------------------------------------------------------------------------- /imtool/imblend.m: -------------------------------------------------------------------------------- 1 | function J = imblend(I, ix) 2 | % function J = imblend(I, ix) 3 | 4 | if numel(I) == 1, J = I{1}; return; end 5 | cast_ = @(x) cast(x, 'like', I{1}); 6 | I = cellfun(@single, I, 'UniformOutput', 0); 7 | masks = cellfun(@create_mask, ix, 'UniformOutput', 0); 8 | mask = sum(cat(3, masks{:}), 3); 9 | masks = cellfun(@(x) x./mask, masks, 'UniformOutput', 0); 10 | J = cellfun(@(x,y) bsxfun(@times,x,y), masks, I, 'UniformOutput', 0); 11 | J = sum(cat(4, J{:}), 4); 12 | J = cast_(J); 13 | 14 | function mask = create_mask(ix) 15 | mask = bwboundary(ix); 16 | mask = bwdist(mask, 'city'); 17 | mask = mask.*ix; 18 | mask = mask/max(max(mask)); 19 | 20 | function b = bwboundary(ix) 21 | se = strel('disk', 1); 22 | b = xor(imdilate(ix, se), ix); 23 | -------------------------------------------------------------------------------- /test/test_10_noise_fbp.m: -------------------------------------------------------------------------------- 1 | %TEST 10: Noise FBP 2 | clear 3 | clc 4 | fprintf('TEST 10: Noise FBP\r'); 5 | angles = 0:1:179.999; 6 | %Phantom 7 | im = imtest('phan',256); 8 | im = im+uint8(abs(25.5*5*rand(256,256)+double(im))); 9 | [M,N] = size(im); 10 | D = ceil(sqrt(M^2+N^2)); 11 | M_pad = ceil((D-M)/2)+1; 12 | N_pad = ceil((D-N)/2)+1; 13 | figure('name','Original Image: Phantom') 14 | imshow(im) 15 | [projmat,~] = tomoproj2d(im,angles); 16 | im_rec = tomo_recon_anisofbp(projmat,angles); 17 | im_rec = im_rec(M_pad:D-M_pad,N_pad:D-N_pad); 18 | im_rec = uint8(imscale(im_rec)); 19 | figure 20 | imshow(im_rec) 21 | im_rec = tomo_recon_fbp(projmat,angles); 22 | im_rec = im_rec(M_pad:D-M_pad,N_pad:D-N_pad); 23 | im_rec = uint8(imscale(im_rec)); 24 | figure 25 | imshow(im_rec) -------------------------------------------------------------------------------- /test/test_01_FilterCompare.m: -------------------------------------------------------------------------------- 1 | %TEST 01: Compare Anisotropic Filter and Bilateral Filter. 2 | clear 3 | clc 4 | n_it = 15; 5 | tao = 0.1; 6 | lambda = 30; 7 | gn = 1; 8 | fprintf('TEST 01: Compare Anisotropic Filter and Bilateral Filter.\r'); 9 | fprintf('Parameters:\n\nAnisoDiff:\niteration %d\ntao %f\nlambda %f\r',n_it,tao,lambda); 10 | fprintf('Bilateral:\ninteration %d\nmask size %dx%d\nlambda_s AUTO\nlambda_r AUTO\r',n_it,2*gn+1,2*gn+1); 11 | load bone01; 12 | im0 = rgb2gray(im0); 13 | im1 = anisodiff(im0,n_it,tao,lambda,1); 14 | im2 = bilateral_filter(im0,n_it,gn); 15 | figure('name','Original Image') 16 | imshow(uint8(im0)); 17 | figure('name','Anisotropic Filter') 18 | imshow(uint8(im1)); 19 | figure('name','Bilateral Filter') 20 | imshow(uint8(im2)); 21 | -------------------------------------------------------------------------------- /utils/hpf.m: -------------------------------------------------------------------------------- 1 | function yn = hpf(yo) 2 | % High-frequency Pass Filter at the same domain of y 3 | % 4 | % 05-Aug-2013 18:08:10 5 | % hanligong@gmail.com 6 | 7 | N = length(yo); 8 | %Correction function 9 | f_corr = -2*abs((1:N)-(N/2+1))/N+1; % ramp filter 10 | %Fourier transform 11 | f = fft(yo); 12 | %Enhance high-frequency 13 | f = f.*f_corr; 14 | %Inverse Fourier transform 15 | yn = ifft(f); 16 | yn = real(yn); 17 | 18 | % N = length(yo); 19 | % yo(end+1:end+N) = 0; N = 2*N; 20 | % %Correction function 21 | % f_corr = -2*abs((1:N)-(N/2+1))/N+1; % ramp filter 22 | % %Fourier transform 23 | % f = fft(yo); 24 | % %Enhance high-frequency 25 | % f = f.*f_corr; 26 | % %Inverse Fourier transform 27 | % yn = ifft(f); 28 | % yn = real(yn); 29 | % yn = yn(1:N/2); 30 | end 31 | -------------------------------------------------------------------------------- /test/build_comp.m: -------------------------------------------------------------------------------- 1 | clear 2 | clc 3 | im = testmat(96); 4 | a = 0:6:179; 5 | [~,~,t1] = build1(im,a); 6 | [~,~,t2] = build2(im,a); 7 | [~,~,t4] = build4(im,a); 8 | [~,~,t5] = build5(im,a); 9 | [~,~,t6] = build6(im,a); 10 | ixstr = {'build1','build2','build4','build5','build6'}; 11 | [tmin,ix] = min([t1,t2,t4,t5,t6]); 12 | fprintf(['\nElapsed time:\nbuild1 %f seconds\nbuild2 %f seconds'... 13 | '\nbuild4 %f seconds\nbuild5 %f seconds\nbuild6 %f seconds\r'],... 14 | t1,t2,t4,t5,t6); 15 | fprintf(['\nBest Method: ' ixstr{ix} ', %f seconds\r'],tmin) 16 | % NOTE: 17 | % build1: compute [x,y] in a for-loop 18 | % build2: compute [x,y] in a matrix 19 | % build4: store W in cell 20 | % build5: if W is small, W is a matrix, else W is a sparse 21 | % build6: combine matrix and sparse 22 | -------------------------------------------------------------------------------- /imtool/testmat.m: -------------------------------------------------------------------------------- 1 | function m = testmat(rows,cols,mattype) 2 | n_arg = nargin; 3 | if n_arg == 2 4 | if ischar(cols) 5 | mattype = cols; 6 | cols = rows; 7 | else 8 | mattype = 'sequence'; 9 | end 10 | end 11 | if n_arg == 1 12 | if ischar(rows) 13 | mattype = rows; 14 | rows = 3; 15 | cols = 3; 16 | else 17 | mattype = 'sequence'; 18 | cols = rows; 19 | end 20 | end 21 | if n_arg == 0 22 | mattype = 'sequence'; 23 | rows = 3; 24 | cols = 3; 25 | end 26 | if strncmpi(mattype,'sequence',3) 27 | m = reshape(1:rows*cols,cols,rows)'; 28 | elseif strncmpi(mattype,'stage',3) 29 | m = repmat((0:rows-1)',1,cols)+repmat(1:cols,rows,1); 30 | else 31 | fprintf('unspecified ''%s''\n',mattype) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /imtool/create_sample_grid.m: -------------------------------------------------------------------------------- 1 | function [saGrid, sx, sy] = create_sample_grid(imageSize, opt) 2 | % opt: 'trombone', 'zun', 'gaussian', 'quadratic' 3 | if ~exist('opt', 'var') || isempty(opt) 4 | opt = 'trombone'; 5 | end 6 | 7 | % sample points range: [-1 1] 8 | x = linspace(-1, 1, imageSize(2)); 9 | y = linspace(-1, 1, imageSize(1)); 10 | [X, Y] = meshgrid(x, y); 11 | 12 | n = 2; 13 | switch lower(opt) 14 | case 'zun' 15 | r = max(cat(3, 1-(X-1).^n, 1-(X+1).^n, 1-(Y-1).^n, 1-(Y+1).^n), [], 3); 16 | case 'trombone' 17 | r = sqrt(X.^2+Y.^2).^(1/n)/sqrt(2)^(1/n); 18 | case 'gaussian' 19 | r = 1-exp(-(X.^2+Y.^2)/0.4); 20 | case 'quadratic' 21 | r = (X.^n+Y.^n)/2; 22 | end 23 | [TH, R] = cart2pol(X, Y); 24 | R = R.*r; 25 | [sx, sy] = pol2cart(TH, R); 26 | saGrid = cat(3, sx, sy); -------------------------------------------------------------------------------- /test/demo_weightmat.m: -------------------------------------------------------------------------------- 1 | clear; 2 | clc; 3 | im = imtest('phantom',[128,128]); 4 | sz = size(im); 5 | ang = 0:12:179.99; 6 | %% Method 1 7 | fprintf('The first way to call ''build_weight_matrix''\r') 8 | [proj_mat,D] = tomo_projection_2d(im,ang); 9 | [W, ix] = build_weight_matrix(im, ang, false, 'area'); 10 | p = proj_mat'; 11 | p = p(:); 12 | W = sparse(W); 13 | W(~ix,:) = []; 14 | p(~ix) = []; 15 | im_rec = tomo_reconstruction_lsqr(W, p, sz, 0.5*1e-3, 200); 16 | im_rec = uint8(imscale(im_rec)); 17 | figure('name','The first method') 18 | imshow(im_rec); 19 | %% Method 2 20 | fprintf('The second way to call ''build_weight_matrix''\r') 21 | [W, p, ~, ~] = build_weight_matrix(im, ang, true, 'area'); 22 | im_rec = tomo_reconstruction_lsqr(W, p, sz, 0.5*1e-3, 200); 23 | im_rec = uint8(imscale(im_rec)); 24 | figure('name','The second method') 25 | imshow(im_rec); 26 | -------------------------------------------------------------------------------- /test/test_08_rand_vs_all.m: -------------------------------------------------------------------------------- 1 | %TEST 08: Rand v.s. All 2 | clear 3 | clc 4 | fprintf('TEST 08: Rand v.s. All\r'); 5 | im = imtest('phan',175); 6 | angles = 0:3:180; 7 | figure('name','Original Image: Phantom') 8 | imshow(im) 9 | siz = size(im); 10 | eq_num_ratio = 0.5; 11 | load weightmat.mat 12 | [W,p] = buildWeightMatrixArea(im,angles); 13 | %180 Angles, W4 p4 14 | n_eq_all = size(W,1); 15 | n_eq_rand = ceil(n_eq_all*eq_num_ratio); 16 | ix = randperm(n_eq_all,n_eq_rand); 17 | fprintf('60 angles, iteration 3500\r') 18 | im_rec = tomo_recon_lsqr(W(1:n_eq_all,:), p(1:n_eq_all,:), siz, 1e-3, 3500); 19 | im_rec = uint8(imscale(im_rec)); 20 | figure('name','60 angles, use all equations') 21 | imshow(im_rec); 22 | im_rec = tomo_recon_lsqr(W(ix,:), p(ix), siz, 1e-3, 3500); 23 | im_rec = uint8(imscale(im_rec)); 24 | figure('name','60 angles, use random equations') 25 | imshow(im_rec); 26 | -------------------------------------------------------------------------------- /tomo_reconstruction_bp.m: -------------------------------------------------------------------------------- 1 | function im_rec = tomo_reconstruction_bp(projmat_bp,angles,show_animation) 2 | % Tomographic reconstruction using Back Projection (BP) algorithm. 3 | % 4 | % 05-Aug-2013 17:19:20 5 | % hanligong@gmail.com 6 | 7 | if nargin < 3 8 | show_animation = false; 9 | else 10 | figure('name','BP') 11 | end 12 | [n_proj,D] = size(projmat_bp); 13 | if length(angles) ~= n_proj 14 | fprintf('Size of proj_mat is incorrect.\r') 15 | im_rec = []; 16 | return 17 | end 18 | im_rec = zeros(D); 19 | for k = 1:n_proj 20 | proj_vec = projmat_bp(k,:); 21 | im_rot = repmat(proj_vec/D,D,1); 22 | im_rot = imrotate(im_rot, angles(k), 'bilinear', 'crop'); 23 | im_rec = im_rec+im_rot/n_proj; 24 | %DEBUG 25 | if show_animation 26 | imshow(uint8(imscale(im_rec))) 27 | pause(0.01) 28 | end 29 | % % 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/test_09_aniso_fbp.m: -------------------------------------------------------------------------------- 1 | %TEST 09: Filtered Back Projection using anisotropic filter 2 | clear 3 | clc 4 | fprintf('TEST 03: Filtered Back Projection using anisotropic filter\r'); 5 | angles = 0:1:179.999; 6 | %Phantom 7 | im = imtest('phan',256); 8 | [M,N] = size(im); 9 | D = ceil(sqrt(M^2+N^2)); 10 | M_pad = ceil((D-M)/2)+1; 11 | N_pad = ceil((D-N)/2)+1; 12 | figure('name','Original Image: Phantom') 13 | imshow(im) 14 | [projmat,~] = tomoproj2d(im,angles); 15 | fprintf('ANISOFBP method\r') 16 | im_rec1 = tomo_recon_anisofbp(projmat,angles); 17 | im_rec1 = im_rec1(M_pad:D-M_pad,N_pad:D-N_pad); 18 | im_rec1 = uint8(imscale(im_rec1)); 19 | figure('name','ANISOFBP') 20 | imshow(im_rec1); 21 | fprintf('FBP method\r') 22 | im_rec2 = tomo_recon_fbp(projmat,angles); 23 | im_rec2 = im_rec2(M_pad:D-M_pad,N_pad:D-N_pad); 24 | im_rec2 = uint8(imscale(im_rec2)); 25 | figure('name','FBP') 26 | imshow(im_rec2); 27 | -------------------------------------------------------------------------------- /combWeightProj.m: -------------------------------------------------------------------------------- 1 | function [W,p] = combWeightProj(W,ix,projmat) 2 | %COMBWEIGHTPROJ Combine weight matrix and projection matrix and generate 3 | % projection vector p, which is used for solving linear equations. The 4 | % output argument W will not contain all zero rows. 5 | % 6 | % Phymhan 7 | % 25-Aug-2013 13:40:36 8 | 9 | %M = length(ix); 10 | [n_p,D] = size(projmat); 11 | if n_p*D < size(W,1) 12 | offset = -1; 13 | D = D+2; 14 | else 15 | offset = 0; 16 | end 17 | n_eq = length(find(ix)); 18 | p = zeros(n_eq,1); 19 | cnt = 0; 20 | for kp = 1:n_p 21 | for k = 1:D 22 | if ix((kp-1)*D+k) 23 | cnt = cnt+1; 24 | p(cnt) = projmat(kp,k+offset); 25 | end 26 | end 27 | end 28 | try 29 | W(~ix,:) = []; 30 | catch expr 31 | fprintf([expr.message '\nConverting to sparse...\r']) 32 | W = sparse(W); 33 | W(~ix,:) = []; 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /tomo_reconstruction.m: -------------------------------------------------------------------------------- 1 | function im_rec = tomo_reconstruction(im,angles,method,n_it) 2 | % TOMO_RECON FBP, BP, ART 3 | % 4 | % 09-Aug-2013 10:19:03 5 | % hanligong@gmail.com 6 | % 7 | 8 | switch nargin 9 | case 3 10 | n_it = 100; 11 | case 2 12 | method = 'fbp'; 13 | end 14 | if strcmpi(method,'fbp') 15 | [projmat,~] = tomo_projection_2d(im,angles); 16 | im_rec = tomo_reconstruction_fbp(projmat,angles); 17 | elseif strcmpi(method,'bp') 18 | [projmat,~] = tomoproj2d(im,angles); 19 | im_rec = tomo_reconstruction_bp(projmat,angles); 20 | elseif strcmpi(method,'art') 21 | im_rec = tomo_reconstruction_art_im(im,angles,n_it); 22 | elseif strcmpi(method,'sart') 23 | im_rec = tomo_reconstruction_sart_im(im,angles,n_it); 24 | elseif strcmpi(method,'lsqr') 25 | im_rec = tomo_reconstruction_lsqr_im(im,angles,1e-6,n_it); 26 | else 27 | fprintf('Comming soon...\r') 28 | im_rec = []; 29 | return 30 | end 31 | -------------------------------------------------------------------------------- /imtool/flow_warp.m: -------------------------------------------------------------------------------- 1 | function J = flow_warp(I, flow, padding, interpolation, extrapval) 2 | 3 | if ~exist('interpolation', 'var') 4 | interpolation = 'bilinear'; 5 | end 6 | if ~exist('padding', 'var') 7 | padding = 'symmetric'; 8 | end 9 | sz = [size(I,1) size(I,2)]; 10 | if sz(1)~=size(flow,1) || sz(2)~=size(flow,2) 11 | flow = imresize(flow, sz, 'bicubic'); 12 | end 13 | if strcmpi(padding, 'symmetric') 14 | I_ = padarray(double(I), floor(sz/2), 'symmetric'); 15 | [x_, y_] = meshgrid(... 16 | -floor(sz(2)/2):sz(2)+floor(sz(2)/2)-1, ... 17 | -floor(sz(1)/2):sz(1)+floor(sz(1)/2)-1); 18 | elseif strcmpi(padding, 'none') 19 | I_ = double(I); 20 | [x_, y_] = meshgrid(0:sz(2)-1, 0:sz(1)-1); 21 | else 22 | error('Not implemented error.') 23 | end 24 | [x, y] = meshgrid(0:sz(2)-1, 0:sz(1)-1); 25 | xq = x + flow(:,:,1); 26 | yq = y + flow(:,:,2); 27 | J = zeros(size(I), 'like', I); 28 | for c = 1:size(I,3) 29 | J(:,:,c) = interp2(x_, y_, I_(:,:,c), xq, yq, interpolation, extrapval); 30 | end 31 | -------------------------------------------------------------------------------- /test/test_12_radon_hough.m: -------------------------------------------------------------------------------- 1 | %TEST 12: Radon Transform and Hough Transform 2 | clear 3 | clc 4 | fprintf('TEST 12: Radon Transform and Hough Transform\r'); 5 | % im = zeros(128); 6 | % im(31:32,63:64) = 255; 7 | % im(63:64,96:97) = 255; 8 | % im(96:97,63:64) = 255; 9 | % im(63:64,31:32) = 255; 10 | % figure 11 | % imshow(uint8(im)); 12 | % a = 0:1:179; 13 | % [pm,~] = tomoproj2d(im,a); 14 | % figure 15 | % imshow(uint8(imscale(pm))); 16 | 17 | % figure('color',[1 1 1]) 18 | % axes('xlim',[0 2*pi],'ylim',[0 90]) 19 | % box on 20 | % xlabel('\phi') 21 | % ylabel('r') 22 | % line(pi,32,'marker','o','markeredgecolor',[1 0 0]) 23 | % line(pi/2,32,'marker','o','markeredgecolor',[1 0 0]) 24 | % line(0,32,'marker','o','markeredgecolor',[1 0 0]) 25 | % line(3*pi/2,32,'marker','o','markeredgecolor',[1 0 0]) 26 | 27 | im = zeros(128); 28 | im(83:84,63:64) = 255; 29 | % figure 30 | % imshow(uint8(im)); 31 | im(83:84,31:32) = 255; 32 | % figure 33 | % imshow(uint8(im)); 34 | im(83:84,01:02) = 255; 35 | % figure 36 | % imshow(uint8(im)); 37 | im(83:84,96:97) = 255; 38 | figure 39 | imshow(uint8(im)); 40 | -------------------------------------------------------------------------------- /imtool/imblendmask.m: -------------------------------------------------------------------------------- 1 | function im = imblendmask(im, mask, color, alpha, outType) 2 | % function im = imblendmask(im, mask, color, alpha) 3 | % alpha: layer(mask) = layer(mask)*(1-alpha)+color(k)*alpha; 4 | 5 | thisType = class(im); 6 | if ~exist('outType', 'var') 7 | outType = thisType; % or 'uint8' for images 8 | elseif strcmp(outType, 'same') || isempty(outType) 9 | outType = thisType; 10 | end 11 | if ~exist('alpha', 'var'), alpha = 0.5; end 12 | if size(im, 3) == 1, im = cat(3, im, im, im); end %### 13 | if max(color) <= 1, color = color*255; end 14 | color = color.'; % one row defines one color 15 | for c = 1:size(im, 3) 16 | x = double(im(:,:,c)); 17 | x(mask) = alpha(1).*color(c) + (1-alpha(1)).*x(mask); 18 | im(:,:,c) = x; 19 | end 20 | if numel(color) > 3 21 | % add boundaries 22 | alpha = [alpha 1]; 23 | color = color(4:end); 24 | bd = edge(mask); 25 | for c = 1:size(im, 3) 26 | x = double(im(:,:,c)); 27 | x(bd) = alpha(2).*color(c) + (1-alpha(2)).*x(bd); 28 | im(:,:,c) = x; 29 | end 30 | end 31 | if ~isa(thisType, outType), im = cast(im, outType); end 32 | -------------------------------------------------------------------------------- /test/test_04_ART.m: -------------------------------------------------------------------------------- 1 | %TEST 04: Algebraic Reconstruction Techniques 2 | clear 3 | clc 4 | fprintf('TEST 04: Algebraic Reconstruction Techniques\rCompare ART and SART\r'); 5 | angles = 0:6:179; 6 | %Phantom 7 | im = imtest('s4',64); 8 | siz = size(im); 9 | figure('name','Original Image: Phantom') 10 | imshow(im) 11 | [W, p, ~, ~] = buildWeightMatrix(im,angles); 12 | %Iteration 100 13 | fprintf('ART method\niteration 100:\r') 14 | im_rec1 = tomo_recon_myart(W,p,siz,100); 15 | im_rec1 = uint8(imscale(im_rec1)); 16 | figure('name','ART') 17 | imshow(im_rec1); 18 | fprintf('SART method\niteration 100:\r') 19 | im_rec2 = tomo_recon_sart(W,p,siz,100); 20 | im_rec2 = uint8(imscale(im_rec2)); 21 | figure('name','SART') 22 | imshow(im_rec2); 23 | %Iteration 1000 24 | fprintf('ART method\niteration 1000:\r') 25 | im_rec1 = tomo_recon_myart(W,p,siz,1000); 26 | im_rec1 = uint8(imscale(im_rec1)); 27 | figure('name','ART') 28 | imshow(im_rec1); 29 | fprintf('SART method\niteration 1000:\r') 30 | %Code efficiency is too low! 31 | %im_rec2 = tomo_recon_mysart(W,p,siz,1000); 32 | %figure('name','ART') 33 | %imshow(im_rec2); 34 | -------------------------------------------------------------------------------- /imtool/imwarpH.m: -------------------------------------------------------------------------------- 1 | function imgWarp = imwarpH(img, T, outRange, magnification, interpMethod, newClass) 2 | % function outputImage = imwarpH(inputImage, T, outRange, magnification, interpMethod, newClass) 3 | % [x; y; 1] = T * [u; v; 1]; 4 | % out_range = [xmin, xmax, ymin, ymax]; 5 | % 12/04/2015 6 | 7 | oldClass = class(img); 8 | if ~exist('newClass', 'var') 9 | newClass = oldClass; 10 | end 11 | if ~exist('interpMethod', 'var') 12 | interpMethod = 'linear'; 13 | end 14 | if ~exist('magnification', 'var') 15 | magnification = 1; 16 | end 17 | if length(outRange) == 2 18 | outRange = [1 outRange(2) 1 outRange(1)]; 19 | end 20 | nc = size(img, 3); 21 | step = 1/magnification; 22 | [x, y] = meshgrid(outRange(1):step:outRange(2), outRange(3):step:outRange(4)); 23 | [M, N] = size(x); 24 | coordi = T\[x(:)'; y(:)'; ones(1, M*N)]; 25 | xi = reshape(coordi(1,:)./coordi(3,:), [M N]); 26 | yi = reshape(coordi(2,:)./coordi(3,:), [M N]); 27 | img = double(img); 28 | imgWarp = zeros(M, N, nc); 29 | for k = 1:size(img, 3) 30 | imgWarp(:,:,k) = interp2(img(:,:,k), xi, yi, interpMethod); 31 | end 32 | imgWarp = cast(imgWarp, newClass); 33 | -------------------------------------------------------------------------------- /test/test_art_guess.m: -------------------------------------------------------------------------------- 1 | clear 2 | clc 3 | % im = imtest('c2'); 4 | % im = double(im); 5 | n_it = 10; 6 | D = 25; 7 | im = rand(D); 8 | SumO = sum(im(:)); 9 | cSumO = repmat(sum(im,1),D,1); %surf(cSumO); 10 | rSumO = repmat(sum(im,2),1,D); %surf(rSumO); 11 | var_it1 = zeros(1,n_it); 12 | var_it2 = zeros(1,n_it); 13 | imG1 = SumO/D^2*ones(D); 14 | imG2 = (cSumO+rSumO)/(2*D); 15 | for kk = 1:n_it 16 | cSumG1 = repmat(sum(imG1,1),D,1); %surf(cSumG1); 17 | rSumG1 = repmat(sum(imG1,2),1,D); %surf(rSumG1); 18 | cSumG2 = repmat(sum(imG2,1),D,1); %surf(cSumG2); 19 | rSumG2 = repmat(sum(imG2,2),1,D); %surf(rSumG2); 20 | imG1 = imG1+(cSumO-cSumG1)/(2*D)+(rSumO-rSumG1)/(2*D); %surf(imG1); 21 | imG2 = imG2+(cSumO-cSumG2)/(2*D)+(rSumO-rSumG2)/(2*D); %surf(imG2); 22 | % surf(imG1); 23 | % disp(imG1); 24 | x1 = (cSumO(1,:)-sum(imG1,1)) ; 25 | y1 = (rSumO(:,1)-sum(imG1,2))'; 26 | x2 = (cSumO(1,:)-sum(imG2,1)) ; 27 | y2 = (rSumO(:,1)-sum(imG2,2))'; 28 | var_it1(kk) = sum((x1.^2+y1.^2))/D; 29 | var_it2(kk) = sum((x2.^2+y2.^2))/D; 30 | end 31 | %imshow(uint8(imscale(imG1))); 32 | figure 33 | plot(var_it1,'k'); 34 | hold on 35 | plot(var_it2,'r'); 36 | -------------------------------------------------------------------------------- /test/test_11_continuity.m: -------------------------------------------------------------------------------- 1 | %TEST 11: Continuity of f(x,y) 2 | clear 3 | clc 4 | fprintf('TEST 11: Continuity of f(x,y)\r'); 5 | angles = 0:1:179.999; 6 | %Phantom 7 | im = imtest('noise',256); 8 | im = double(im); 9 | [M,N] = size(im); 10 | D = ceil(sqrt(M^2+N^2)); 11 | M_pad = ceil((D-M)/2)+1; 12 | N_pad = ceil((D-N)/2)+1; 13 | stp = 2; 14 | % % 15 | MM = ceil(M/stp); 16 | NN = ceil(N/stp); 17 | idx = false(M,N);idx(1:stp:M,1:stp:N) = true; 18 | figure('name','origin','color',[1 1 1]) 19 | surf(reshape(im(idx),MM,NN)) 20 | box on 21 | camproj perspective 22 | [projmat,~] = tomoproj2d(im,angles); 23 | im_rec = tomo_recon_fbp(projmat,angles); 24 | im_rec = im_rec(M_pad:D-M_pad,N_pad:D-N_pad); 25 | im_rec = imscale(im_rec); 26 | M = M-2; 27 | N = N-2; 28 | MM = ceil(M/stp); 29 | NN = ceil(N/stp); 30 | idx = false(M,N);idx(1:stp:M,1:stp:N) = true; 31 | figure('name','recon','color',[1 1 1]) 32 | surf(reshape(im_rec(idx),MM,NN)) 33 | box on 34 | camproj perspective 35 | im = im(2:M+1,2:N+1); 36 | err = im-im_rec; 37 | figure('name','err','color',[1 1 1]) 38 | surf(reshape(err(idx),MM,NN)) 39 | box on 40 | camproj perspective 41 | im_std = std(err(:)); 42 | fprintf('standard deviation: %f\r',im_std); 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tools for 2-D Tomographic Reconstruction 2 | 3 | ![](tomo.png) 4 | 5 | There are two main methods for **tomographic reconstruction**: 6 | 1. **Filtered back-projection** or **FBP** which is based on [Radon transform](https://en.wikipedia.org/wiki/Radon_transform) and its inverse transform; 7 | 2. **Algebraic reconstruction technique** or **ART** which is based on solving linear algebra equations. 8 | 9 | The MATLAB code contains functions for both FBPs and ARTs (the built-in [lsqr](https://www.mathworks.com/help/matlab/ref/lsqr.html) solver is recommended): 10 | 11 | - `tomo_projection_2d`, computes projections of a given image; 12 | - `build_weight_matrix`, builds weighting factor matrix used for algebraic methods; 13 | - `tomo_reconstruction_bp`, reconstructs the image from its projections using BP method; 14 | - `tomo_reconstruction_fbp`, reconstructs the image from its projections using FBP method; 15 | - `tomo_reconstruction_art`, reconstructs the image from its projections using ART method; 16 | - `tomo_reconstruction_sart`, reconstructs the image from its projections using SART method; 17 | - `tomo_reconstruction_lsqr`, reconstructs the image from its projections, using the build-in lsqr solver. 18 | 19 | See `demo.m` for details. 20 | -------------------------------------------------------------------------------- /test/test_06_LSQR.m: -------------------------------------------------------------------------------- 1 | %TEST 06: LSQR Method 2 | clear 3 | clc 4 | fprintf('TEST 06: LSQR Method\r'); 5 | im = imtest('phan',128); 6 | figure('name','Original Image: Phantom') 7 | imshow(im) 8 | siz = size(im); 9 | load weightmat.mat 10 | %10 Angles 11 | fprintf('10 angles:\r') 12 | fprintf('iteration 1000\r') 13 | im_rec1 = tomo_recon_lsqr(W1, p1, siz, 1e-6, 1000); 14 | im_rec1 = uint8(imscale(im_rec1)); 15 | figure('name','10 angles, LSQR(iteration 1000)') 16 | imshow(im_rec1); 17 | %30 Angles 18 | fprintf('30 angles:\r') 19 | fprintf('iteration 1000\r') 20 | im_rec2 = tomo_recon_lsqr(W2, p2, siz, 1e-6, 1000); 21 | im_rec2 = uint8(imscale(im_rec2)); 22 | figure('name','30 angles, LSQR(iteration 1000)') 23 | imshow(im_rec2); 24 | %60 Angles 25 | fprintf('60 angles:\r') 26 | fprintf('iteration 1000\r') 27 | im_rec3 = tomo_recon_lsqr(W3, p3, siz, 1e-6, 1000); 28 | im_rec3 = uint8(imscale(im_rec3)); 29 | figure('name','60 angles, LSQR(iteration 1000)') 30 | imshow(im_rec3); 31 | %180 Angles 32 | fprintf('180 angles:\r') 33 | fprintf('iteration 1000\r') 34 | im_rec4 = tomo_recon_lsqr(W4, p4, siz, 1e-6, 1000); 35 | im_rec4 = uint8(imscale(im_rec4)); 36 | figure('name','180 angles, LSQR(iteration 1000)') 37 | imshow(im_rec4); 38 | -------------------------------------------------------------------------------- /tomo_reconstruction_fbp.m: -------------------------------------------------------------------------------- 1 | function im_rec = tomo_reconstruction_fbp(projmat_bp,angles,show_animation) 2 | % Tomographic reconstruction using Filtered Back Projection (FBP) 3 | % algorithm. 4 | % 5 | % 05-Aug-2013 17:23:35 6 | % hanligong@gmail.com 7 | 8 | if nargin < 3 9 | show_animation = false; 10 | else 11 | figure('name','FBP') 12 | end 13 | [n_proj,D] = size(projmat_bp); 14 | if length(angles) ~= n_proj 15 | fprintf('Size of proj_mat is incorrect.\r') 16 | im_rec = []; 17 | return 18 | end 19 | im_rec = zeros(D); 20 | 21 | % N = 3; 22 | % h = zeros(1,N); 23 | % m = floor((N+1)/2); 24 | % for n = 1:N 25 | % if n == m 26 | % h(n) = 1/4; 27 | % elseif rem(n, 2) == 0 28 | % h(n) = 0; 29 | % else 30 | % h(n) = -1/(pi*(n-m))^2; 31 | % end 32 | % end 33 | 34 | for k = 1:n_proj 35 | proj_vec = hpf(projmat_bp(k,:)); 36 | %proj_vec = filtersinc(projmat_bp(k,:)); 37 | %proj_vec = conv(projmat_bp(k,:), h, 'same'); 38 | im_rot = repmat(proj_vec/D,D,1); 39 | im_rot = imrotate(im_rot, angles(k), 'bilinear', 'crop'); 40 | im_rec = im_rec+im_rot/n_proj; 41 | %DEBUG 42 | if show_animation 43 | imshow(uint8(imscale(im_rec))) 44 | pause(0.01) 45 | end 46 | % % 47 | end 48 | -------------------------------------------------------------------------------- /imtool/flow_unwarp.m: -------------------------------------------------------------------------------- 1 | function [I, cnt] = flow_unwarp(J, flow, inpaint) 2 | if ~exist('inpaint', 'var') || isempty(inpaint) 3 | inpaint = true; 4 | end 5 | 6 | sz = [size(J,1) size(J,2)]; 7 | if sz(1)~=size(flow,1) || sz(2)~=size(flow,2) 8 | flow = imresize(flow, sz, 'bicubic'); 9 | end 10 | I = nan(size(J)); 11 | cnt = zeros(sz); 12 | 13 | ind = 1:prod(sz); 14 | [y, x] = ind2sub(sz, ind'); 15 | x_ = x + reshape(flow(:,:,1), [], 1); 16 | y_ = y + reshape(flow(:,:,2), [], 1); 17 | x1 = round(x_); 18 | y1 = round(y_); 19 | for i = ind 20 | if x1(i)>=1 && x1(i)<=sz(2) && y1(i)>=1 && y1(i)<=sz(1) 21 | if cnt(y1(i),x1(i)) == 0 22 | I(y1(i),x1(i),:) = J(y(i),x(i),:); 23 | else 24 | % average: 25 | %(cnt(y1(i),x1(i))*I(y1(i),x1(i))+J(y(i),x(i),:))/(cnt(y1(i),x1(i))+1); 26 | % random pick: 27 | if rand < 1/(cnt(y1(i),x1(i))+1) 28 | I(y1(i),x1(i),:) = J(y(i),x(i),:); 29 | end 30 | end 31 | cnt(y1(i),x1(i)) = cnt(y1(i),x1(i))+1; 32 | end 33 | end 34 | 35 | if inpaint 36 | se = strel('disk', 5); 37 | ix = cnt > 0; 38 | ix = imerode(imdilate(ix, se), se); 39 | I(~ix) = 0; 40 | I = inpaintn(I); 41 | I(~ix) = nan; 42 | end 43 | I = cast(I, 'like', J); 44 | -------------------------------------------------------------------------------- /test/ART2Dreconst.m: -------------------------------------------------------------------------------- 1 | clear all; 2 | close all; 3 | 4 | I=imread('flame1.jpg'); 5 | figure; 6 | imshow(I); 7 | 8 | I1=rgb2gray(I); 9 | im = I1; 10 | 11 | %Creating Sinogram. 12 | fprintf(1,'Demo 01: Compute projections\r'); 13 | % im=imtest('I1'); 14 | ang=0:1:179.99; 15 | [projmat,~]=tomo_projection_2d(im,ang); 16 | improj=uint8(imscale(projmat)); 17 | figure('name','Projections'); 18 | imshow(improj); 19 | 20 | %Build weight matrix. 21 | fprintf(1,'Demo 02: Build weight matrix\r'); 22 | sz=[128,128]; 23 | ang=0:2:179.99; 24 | [W,~]=build_weight_matrix(sz,ang,0,'area'); 25 | figure('name','Weight Matrix'); 26 | spy(W); 27 | 28 | %ART reconstructions. 29 | fprintf(1,'Demo 03:ART reconstruction\r'); 30 | clear; 31 | clc; 32 | im=imtest('I1',[256,256]); 33 | sz=size(im); 34 | ang=0:3:179.99; 35 | [W,p,~,~]=build_weight_matrix(im,ang,1,'area'); 36 | fprintf('ART solver\r') 37 | im_rec=tomo_reconstruction_art(W,p,sz); 38 | im_rec=uint8(imscale(im_rec)); 39 | figure('name','ART') 40 | imshow(im_rec); 41 | 42 | fprintf(1,'Demo 04: ART reconstruction\r'); 43 | clear; 44 | clc; 45 | im=imtest('I1',[256,256]); 46 | sz=size(im); 47 | ang=0:12:179.99; 48 | [W,p,~,~]=build_weight_matrix(im,ang,1,'area'); 49 | fprintf('ART solver\r') 50 | im_rec=tomo_reconstruction_art(W,p,sz); 51 | im_rec=uint8(imscale(im_rec)); 52 | figure('name','ART') 53 | imshow(im_rec); -------------------------------------------------------------------------------- /imtool/nonuniform_sampling.m: -------------------------------------------------------------------------------- 1 | function im1 = nonuniform_sampling(im0, sampleGrid, opt) 2 | 3 | if ~exist('opt', 'var') || isempty(opt) 4 | opt = 'trombone'; 5 | end 6 | if ~exist('sampleGrid', 'var') || isempty(sampleGrid) 7 | imageSize = size(im0); 8 | [sx, sy] = create_sample_grid(imageSize, opt); 9 | else 10 | imageSize = size(im0); 11 | [sx, sy] = deal(sampleGrid(:,:,1), sampleGrid(:,:,2)); 12 | end 13 | im0 = double(im0); 14 | x = linspace(-1, 1, imageSize(2)); 15 | y = linspace(-1, 1, imageSize(1)); 16 | % im1 = interp2(x, y, im0, sx, sy, 'linear', 0); 17 | im1 = zeros(size(im0), 'like', im0); 18 | for c = 1:size(im0, 3) 19 | im1_ = interp2(x, y, im0(:,:,c), sx, sy); 20 | im1_(isnan(im1_)) = mean(im1_(~isnan(im1_))); 21 | im1(:,:,c) = im1_; 22 | end 23 | 24 | %% 25 | function [sx, sy] = create_sample_grid(imageSize, opt) 26 | % % opt: 'trombone', 'zun', 'gaussian', 'quadratic' 27 | 28 | % sample points range: [-1 1] 29 | x = linspace(-1, 1, imageSize(2)); 30 | y = linspace(-1, 1, imageSize(1)); 31 | [X, Y] = meshgrid(x, y); 32 | 33 | n = 2; 34 | switch lower(opt) 35 | case 'zun' 36 | r = max(cat(3, 1-(X-1).^n, 1-(X+1).^n, 1-(Y-1).^n, 1-(Y+1).^n), [], 3); 37 | case 'trombone' 38 | r = sqrt(X.^2+Y.^2).^(1/n)/sqrt(2)^(1/n); 39 | case 'gaussian' 40 | r = 1-exp(-(X.^2+Y.^2)/0.4); 41 | case 'quadratic' 42 | r = (X.^n+Y.^n)/2; 43 | end 44 | [TH, R] = cart2pol(X, Y); 45 | R = R.*r; 46 | [sx, sy] = pol2cart(TH, R); 47 | -------------------------------------------------------------------------------- /test/test_03_FBP.m: -------------------------------------------------------------------------------- 1 | %TEST 03: Filtered Back Projection 2 | clear 3 | clc 4 | fprintf('TEST 03: Filtered Back Projection\r'); 5 | angles = 0:1:179.999; 6 | %Phantom 7 | im = imtest('phan',256); 8 | [M,N] = size(im); 9 | D = ceil(sqrt(M^2+N^2)); 10 | M_pad = ceil((D-M)/2)+1; 11 | N_pad = ceil((D-N)/2)+1; 12 | figure('name','Original Image: Phantom') 13 | imshow(im) 14 | [projmat,~] = tomoproj2d(im,angles); 15 | fprintf('BP method\r') 16 | im_rec1 = tomo_recon_bp(projmat,angles); 17 | im_rec1 = im_rec1(M_pad:D-M_pad,N_pad:D-N_pad); 18 | im_rec1 = uint8(imscale(im_rec1)); 19 | figure('name','BP') 20 | imshow(im_rec1); 21 | fprintf('FBP method\r') 22 | im_rec2 = tomo_recon_fbp(projmat,angles); 23 | im_rec2 = im_rec2(M_pad:D-M_pad,N_pad:D-N_pad); 24 | im_rec2 = uint8(imscale(im_rec2)); 25 | figure('name','FBP') 26 | imshow(im_rec2); 27 | %Lena 28 | im = imtest('lena',256); 29 | [M,N] = size(im); 30 | D = ceil(sqrt(M^2+N^2)); 31 | M_pad = ceil((D-M)/2)+1; 32 | N_pad = ceil((D-N)/2)+1; 33 | figure('name','Original Image: Lena') 34 | imshow(im) 35 | [projmat,~] = tomoproj2d(im,angles); 36 | fprintf('BP method\r') 37 | im_rec1 = tomo_recon_bp(projmat,angles); 38 | im_rec1 = im_rec1(M_pad:D-M_pad,N_pad:D-N_pad); 39 | im_rec1 = uint8(imscale(im_rec1)); 40 | figure('name','BP') 41 | imshow(im_rec1); 42 | fprintf('FBP method\r') 43 | im_rec2 = tomo_recon_fbp(projmat,angles); 44 | im_rec2 = im_rec2(M_pad:D-M_pad,N_pad:D-N_pad); 45 | im_rec2 = uint8(imscale(im_rec2)); 46 | figure('name','FBP') 47 | imshow(im_rec2); 48 | -------------------------------------------------------------------------------- /test/build2.m: -------------------------------------------------------------------------------- 1 | function [W, p, t] = build2(im, angles) 2 | t_s = toc; 3 | %Pad image 4 | [im_pad,D] = impad(im,'diag',0); 5 | sz = size(im); 6 | m = sz(1); %rows 7 | n = sz(2); %cols 8 | n_proj = length(angles); 9 | N = m*n; %number of unknown variables 10 | M = D*n_proj; %number of equations 11 | try 12 | W = zeros(M,N); %weighting factor matrix 13 | catch expr 14 | fprintf([expr.message '\nGenerating a sparse...\r']) 15 | W = sparse(M,N); 16 | end 17 | %proj_info = zeros(M,2); %[theta No.] 18 | p = zeros(M,1); %projection vector 19 | ix = false(M,1); 20 | rc = [m;n]/2+0.5; 21 | %cnt = 0; 22 | fprintf('Building Weight Matrix...\r') 23 | [x,y] = ind2sub(sz,1:N); 24 | %xy = [x;y]; %x,y coordinate 25 | %x,y coordinate with respect to rotation center 26 | xy_c = [x;y]-repmat(rc,1,N); 27 | for kp = 1:n_proj 28 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 29 | pvec = sum(im_rot,1); 30 | t = -angles(kp)/180*pi; 31 | R = [cos(t) -sin(t);sin(t) cos(t)]; 32 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 33 | xy_rot = R*xy_c+D/2+0.5; 34 | idx = round(xy_rot(2,:)); 35 | ixM = D*(kp-1)+idx; %corresponding indice in W and p matrix 36 | for kn = 1:N 37 | W(ixM(kn),kn) = 1; 38 | p(ixM(kn)) = pvec(idx(kn)); 39 | ix(ixM(kn)) = true; 40 | end 41 | end 42 | % % 43 | W = sparse(W); 44 | 45 | %Delete all-zero rows in W 46 | fprintf('\nDelete all-zero rows in W...\r') 47 | %ix = sum(W,2)==0; 48 | W(~ix,:) = []; 49 | p(~ix) = []; 50 | n_eq = size(W,1); 51 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 52 | figure('name','Sparsity pattern of matrix W'); 53 | spy(W) 54 | t_e = toc; 55 | t = t_e-t_s; 56 | fprintf('\nTotal: %f seconds\n\r',t); 57 | end 58 | -------------------------------------------------------------------------------- /imtool/chessmat.m: -------------------------------------------------------------------------------- 1 | function m = chessmat(rows,cols,l,offset) 2 | %CHESSMAT returns a chessboard-like matrix 3 | % Syntax m = chessmat(rows,cols,l,offset) 4 | % where l is the width of one block, and offset defines the style of the 5 | % matrix, that offset == 0 means 1 6 | 7 | switch nargin 8 | case 0 9 | rows = 3; 10 | cols = 3; 11 | l = 1; 12 | offset = 0; 13 | case 1 14 | cols = rows; 15 | l = 1; 16 | offset = 0; 17 | case 2 18 | if ischar(cols) 19 | l = cols; 20 | cols = rows; 21 | else 22 | l = 1; 23 | end 24 | offset = 0; 25 | case 3 26 | if ischar(cols) 27 | offset = l; 28 | l = cols; 29 | cols = rows; 30 | else 31 | offset = 0; 32 | end 33 | end 34 | if ischar(l) 35 | l = fix(str2double(l)); 36 | end 37 | if ischar(offset) 38 | switch lower(offset) 39 | case {'0', 'zero'} 40 | offset = 0.1; 41 | case {'1', 'one'} 42 | offset = 1; 43 | case {'-0', 'zero_enlarge'} 44 | offset = -0.1; 45 | case {'-1', 'one_enlarge'} 46 | offset = -1; 47 | otherwise 48 | fprintf('''%s'' is not an optional value of "offset".\n',offset); 49 | m = []; 50 | return; 51 | end 52 | end 53 | if abs(offset) < 1 54 | idx_rem = 0; 55 | else 56 | idx_rem = 1; 57 | end 58 | l = abs(l)+(l == 0); 59 | m = zeros(ceil(rows/l)*l,ceil(cols/l)*l); 60 | for k1 = 0:ceil(rows/l)-1 61 | for k2 = 0:ceil(cols/l)-1 62 | if rem(k1+k2,2) == idx_rem 63 | m((k1*l+1):(k1*l+l),(k2*l+1):(k2*l+l)) = 1; 64 | end 65 | end 66 | end 67 | if offset >= 0 68 | m = m(1:rows,1:cols); 69 | end 70 | 71 | end 72 | -------------------------------------------------------------------------------- /test/test_02_Reconstruction.m: -------------------------------------------------------------------------------- 1 | %TEST 02: Tomographic Reconstruction 2 | clear 3 | clc 4 | fprintf('TEST 02: Tomographic Reconstruction.\nCompare two methods: FBP and ART\r'); 5 | im = imtest('cat',128); 6 | [M,N] = size(im); 7 | D = ceil(sqrt(M^2+N^2)); 8 | M_pad = ceil((D-M)/2)+1; 9 | N_pad = ceil((D-N)/2)+1; 10 | figure('name','Original Image') 11 | imshow(im) 12 | % %Reconstruct from 10 angles 13 | % fprintf('reconstruct from 10 angles...\r') 14 | % angles = 0:18:179; 15 | % fprintf('FBP method\r') 16 | % [projmat,~] = tomoproj2d(im,angles); 17 | % im_rec1 = tomo_recon_fbp(projmat,angles); 18 | % im_rec1 = im_rec1(M_pad:D-M_pad,N_pad:D-N_pad); 19 | % im_rec1 = uint8(imscale(im_rec1)); 20 | % figure('name','FBP') 21 | % imshow(im_rec1); 22 | % fprintf('ART method\niteration 1000\r') 23 | % im_rec2 = tomo_recon_myart_im(im,angles,1000); 24 | % figure('name','ART') 25 | % im_rec2 = uint8(imscale(im_rec2)); 26 | % imshow(im_rec2) 27 | % %Reconstruct from 30 angles 28 | % fprintf('reconstruct from 30 angles...\r') 29 | % angles = 0:6:179; 30 | % fprintf('FBP method\r') 31 | % [projmat,~] = tomoproj2d(im,angles); 32 | % im_rec1 = tomo_recon_fbp(projmat,angles); 33 | % im_rec1 = im_rec1(M_pad:D-M_pad,N_pad:D-N_pad); 34 | % im_rec1 = uint8(imscale(im_rec1)); 35 | % figure('name','FBP') 36 | % imshow(im_rec1); 37 | % fprintf('ART method\niteration 1000\r') 38 | % im_rec2 = tomo_recon_myart_im(im,angles,1000); 39 | % figure('name','ART') 40 | % im_rec2 = uint8(imscale(im_rec2)); 41 | % imshow(im_rec2) 42 | 43 | angles = 0:1:179.999; 44 | [projmat,~] = tomoproj2d(im,angles); 45 | im_rec = tomo_recon_fbp(projmat,angles); 46 | im_rec = im_rec(M_pad:D-M_pad,N_pad:D-N_pad); 47 | im_rec = uint8(imscale(im_rec)); 48 | figure('name','FBP') 49 | imshow(im_rec); 50 | [W,p] = buildWeightMatrixArea(im,angles); 51 | im_rec = tomo_recon_lsqr(W,p,[M N],1e-3,3500); 52 | figure('name','LSQR') 53 | im_rec = uint8(imscale(im_rec)); 54 | imshow(im_rec) 55 | -------------------------------------------------------------------------------- /imtool/foveated.m: -------------------------------------------------------------------------------- 1 | function im1 = foveated(im0, filterBank, opt) 2 | 3 | if ~exist('opt', 'var') || isempty(opt) 4 | opt = 'imfilter'; 5 | end 6 | 7 | if ~exist('filterBank', 'var') || isempty(filterBank) 8 | imageSize = size(im0); 9 | filterSize = floor(max(imageSize)/6.4/2)*2+1; 10 | filterNum = 20; 11 | filterBank = create_filter_bank(filterSize, filterNum); 12 | else 13 | imageSize = size(im0); 14 | filterSize = size(filterBank, 1); 15 | filterNum = size(filterBank, 4); 16 | end 17 | 18 | im0 = double(im0); 19 | filterBank = double(filterBank); 20 | 21 | x = linspace(-1, 1, imageSize(2)); 22 | y = linspace(-1, 1, imageSize(1)); 23 | [X, Y] = meshgrid(x, y); 24 | I = sqrt(X.^2+Y.^2); 25 | I = round(I/sqrt(2)*(filterNum-1)+1); 26 | 27 | c = size(im0, 3); 28 | im1 = zeros([imageSize(1) imageSize(2) c]); 29 | 30 | for j = 1:size(im0, 3) 31 | im0_ = im0(:,:,j); 32 | if strcmpi(opt, 'imfilter') 33 | % % use imfilter 34 | IM1 = zeros([imageSize(1) imageSize(2) filterNum], 'double'); 35 | for i = 1:filterNum 36 | IM1(:,:,i) = imfilter(im0_, filterBank(:,:,1,i), 'symmetric'); 37 | end 38 | elseif strcmpi(opt, 'vl_nnconv') 39 | % % use vl_nnconv 40 | % pad = floor((fsize-1)/2); 41 | % IM1 = vl_nnconv(im0, filterBank, zeros(fnum, 1), 'pad', pad); 42 | im0_ = single(im0_); 43 | filterBank = single(filterBank); 44 | pad = floor((filterSize-1)/2); 45 | IM1 = vl_nnconv(im0_, filterBank, zeros(filterNum, 1, 'single'), 'pad', pad); 46 | IM1 = double(IM1); 47 | else 48 | error('Not implemented error.'); 49 | end 50 | % % pack 51 | for i = 1:filterNum 52 | ix = I == i; 53 | IM1(:,:,i) = IM1(:,:,i).*ix; 54 | end 55 | im1(:,:,j) = sum(IM1, 3); 56 | end 57 | 58 | %% 59 | function fb = create_filter_bank(fsize, fnum) 60 | sigma = linspace(0.05, (fsize-1)/4, fnum); 61 | fb = zeros([fsize fsize 1 fnum]); 62 | for i = 1:fnum 63 | fb(:,:,1,i) = fspecial('Gaussian', fsize, sigma(i)); 64 | end 65 | -------------------------------------------------------------------------------- /test/build1.m: -------------------------------------------------------------------------------- 1 | function [W, p, t] = build1(im, angles) 2 | t_s = toc; 3 | %Pad image 4 | [im_pad,D] = impad(im,'diag',0); 5 | sz = size(im); 6 | m = sz(1); %rows 7 | n = sz(2); %cols 8 | n_proj = length(angles); 9 | N = m*n; %number of unknown variables 10 | M = D*n_proj; %number of equations 11 | try 12 | W = zeros(M,N); %weighting factor matrix 13 | catch expr 14 | fprintf([expr.message '\nGenerates a sparse.\r']) 15 | W = sparse(M,N); 16 | end 17 | %proj_info = zeros(M,2); %[theta No.] 18 | p = zeros(M,1); %projection vector 19 | ix = false(M,1); 20 | rc = [m;n]/2+0.5; 21 | %cnt = 0; 22 | fprintf('Building Weight Matrix...\r') 23 | for kp = 1:n_proj 24 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 25 | pvec = sum(im_rot,1); 26 | t = -angles(kp)/180*pi; 27 | R = [cos(t) -sin(t);sin(t) cos(t)]; 28 | %L = m*abs(sin(t))+n*abs(cos(t)); 29 | %offset = ceil((D-L)/2); 30 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 31 | for kn = 1:N 32 | [x,y] = ind2sub(sz,kn); 33 | xy_rot = R*([x;y]-rc)+D/2+0.5; 34 | idx = round(xy_rot(2));%# 35 | %corresponding indice in W and p matrix 36 | ixM = D*(kp-1)+idx; 37 | W(ixM,kn) = 1; 38 | %W(D*(kp-1)+idx,kn) = 1; 39 | %W(cnt+idx-offset,kn) = 1; 40 | %p(cnt+idx-offset) = projmat(kp,idx);%# 41 | if ~ix(ixM) 42 | p(ixM) = pvec(idx); 43 | ix(ixM) = true; 44 | end 45 | %p(D*(kp-1)+idx) = projmat(kp,idx); 46 | %proj_info(cnt+idx-offset,:) = [kp,idx]; 47 | end 48 | %cnt = cnt+(D-2*offset); 49 | %fprintf('%d equations built.\r',D-2*offset) 50 | end 51 | % % 52 | W = sparse(W); 53 | 54 | %Delete all-zero rows in W 55 | fprintf('\nDelete all-zero rows in W...\r') 56 | %ix = sum(W,2)==0; 57 | W(~ix,:) = []; 58 | p(~ix) = []; 59 | n_eq = size(W,1); 60 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 61 | figure('name','Sparsity pattern of matrix W'); 62 | spy(W) 63 | t_e = toc; 64 | t = t_e-t_s; 65 | fprintf('\nTotal: %f seconds\n\r',t); 66 | end 67 | -------------------------------------------------------------------------------- /test/test_05_3D_reconstruction.m: -------------------------------------------------------------------------------- 1 | %TEST 04: 3D Reconstruction 2 | clear 3 | close all 4 | clc 5 | fprintf('TEST 05: 3D Reconstruction\nUsing a downloaded toolbox ''tomobox.''\r'); 6 | %% Dimensions to use in simulation 7 | r1_max = 17; 8 | N = 2*r1_max+1; % 3D image side length 9 | dims = N*[1,1,1]; 10 | u_max = 23; % Projection side length 11 | nuv = [1,1]; % Number of subpixels, see buildSystemMatrix 12 | vpRatio = 1; % Voxel-to-pixel ratio, see buildSystemMatrix 13 | num_proj = 30; % Number of projections to reconstruct from 14 | rnl = 0.01; % Relative noise level 15 | 16 | %% Set up 3D test image 17 | X_true = phantom3d('Modified Shepp-Logan',N); 18 | x_true = X_true(:); 19 | 20 | %% Choose a number of random projection directions 21 | 22 | % Choose x,y,z as Gaussian triple and normalize. Since Gaussians are 23 | % rotation-symmetric, the directions obtained are samples from the uniform 24 | % distribution over the unit sphere. 25 | v_list = randn(num_proj,3); 26 | v_listnorm = sqrt(sum(v_list.^2,2)); 27 | v_list = v_list./repmat(v_listnorm,1,3); 28 | 29 | %% Set up the parallel beam system matrix 30 | [A,p_all] = buildSystemMatrix(r1_max,u_max,v_list,nuv,vpRatio); 31 | 32 | %% Compute projections 33 | b_orig = A*x_true; 34 | 35 | %% Add Gaussian noise 36 | e = getNoise(rnl,b_orig); 37 | b = b_orig + e; 38 | B = reshape(b,2*u_max+1,2*u_max+1,num_proj); 39 | 40 | %% Reconstruct 41 | tol = 1e-6; 42 | maxit = 200; 43 | x_sol = lsqr(A,b,tol,maxit); 44 | X_sol = reshape(x_sol,dims); 45 | %% Display 46 | 47 | figure('name','Original') 48 | plotLayers(X_true) 49 | suptitle('Original') 50 | 51 | figure('name','Projections') 52 | plotLayers(B) 53 | suptitle('Projections') 54 | 55 | figure('name','Reconstruction') 56 | plotLayers(X_sol) 57 | suptitle(['Reconstruction, iteration ',num2str(maxit)]) 58 | 59 | %% 60 | %myslicer 61 | figure('name','MySlicer') 62 | X_true = myimadj(X_true); 63 | %T = [1 0 0 0;0 1 0 0;0 0 2.5 0]; 64 | myslicer(X_true); 65 | colormap gray; 66 | axis off; 67 | -------------------------------------------------------------------------------- /test/build5.m: -------------------------------------------------------------------------------- 1 | function [W, p, t] = build5(im, angles) 2 | t_s = toc; 3 | %Pad image 4 | [im_pad,D] = impad(im,'diag',0); 5 | sz = size(im); 6 | m = sz(1); %rows 7 | n = sz(2); %cols 8 | n_proj = length(angles); 9 | N = m*n; %number of unknown variables 10 | M = D*n_proj; %number of equations 11 | %Weighting Factor Matrix 12 | try 13 | W = zeros(M,N); 14 | catch expr 15 | fprintf([expr.message '\nGenerates a sparse.\r']) 16 | W = sparse(M,N); 17 | end 18 | %proj_info = zeros(M,2); %[theta No.] 19 | p = zeros(M,1); %projection vector 20 | ix = false(M,1); 21 | rc = [m;n]/2+0.5; 22 | fprintf('Building Weight Matrix...\r') 23 | [x,y] = ind2sub(sz,1:N); 24 | %xy = [x;y]; %x,y coordinate 25 | %x,y coordinate with respect to rotation center 26 | xy_c = [x;y]-repmat(rc,1,N); 27 | try 28 | A = zeros(D,N); 29 | catch expr 30 | fprintf(['Building A...\n' ... 31 | expr.message '\nGenerating a sparse...\r']) 32 | A = sparse(D,N); 33 | end 34 | for kp = 1:n_proj 35 | A(:) = 0; %# 36 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 37 | pvec = sum(im_rot,1); 38 | t = -angles(kp)/180*pi; 39 | R = [cos(t) -sin(t);sin(t) cos(t)]; 40 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 41 | xy_rot = R*xy_c+D/2+0.5; 42 | idx = round(xy_rot(2,:)); 43 | ixM = D*(kp-1)+idx; %corresponding indice in W and p matrix 44 | for kn = 1:N 45 | %W(ixM(kn),kn) = 1; 46 | A(idx(kn),kn) = 1; 47 | p(ixM(kn)) = pvec(idx(kn)); 48 | ix(ixM(kn)) = true; 49 | end 50 | W((D*(kp-1)+(1:D)),:) = A; %# 51 | end 52 | % % 53 | %Delete all-zero rows in W 54 | fprintf('\nDelete all-zero rows in W...\r') 55 | %ix = sum(W,2)==0; 56 | try 57 | W(~ix,:) = []; 58 | catch expr 59 | fprintf([expr.message '\nConverting to sparse...\r']) 60 | W = sparse(W); 61 | W(~ix,:) = []; 62 | end 63 | p(~ix) = []; 64 | n_eq = size(W,1); 65 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 66 | figure('name','Sparsity pattern of matrix W'); 67 | spy(W) 68 | t_e = toc; 69 | t = t_e-t_s; 70 | fprintf('\nTotal: %f seconds\n\r',t); 71 | end 72 | -------------------------------------------------------------------------------- /imtool/imscale.m: -------------------------------------------------------------------------------- 1 | function [im, scl] = imscale(im, scl, otyp) 2 | % function im = imscale(im, scl) 3 | % im, image 4 | % scl, can by a 1-by-4 vector that defines the low, high, and range value for 5 | % scaling (scl = [im_lo im_hi sc_lo sc_hi]). scl can also be 'auto' for auto 6 | % scaling, or a function handle. 7 | % 11/14/2015 8 | 9 | currtyp = class(im); 10 | if ~exist('otyp', 'var') 11 | otyp = currtyp; % or 'uint8' for images 12 | elseif strcmp(otyp, 'same') || isempty(otyp) 13 | otyp = currtyp; 14 | end 15 | if ~exist('scl', 'var') 16 | scl = 'auto'; 17 | elseif isempty(scl) 18 | scl = 'same'; 19 | end 20 | % scl: [im_lo im_hi sc_lo sc_hi] 21 | if isa(scl, 'function_handle') 22 | fun = scl; 23 | elseif strcmp(scl, 'same') 24 | % fun = @(M) M; 25 | im_casting(otyp); 26 | return 27 | else 28 | if strcmp(scl, 'auto') 29 | im_lo = min(im(:)); 30 | im_hi = max(im(:)); 31 | sc_lo = 0; 32 | sc_hi = 255; 33 | elseif length(scl) == 2 34 | im_lo = min(im(:)); 35 | im_hi = max(im(:)); 36 | sc_lo = scl(1); 37 | sc_hi = scl(2); 38 | else 39 | % scl: [im_lo im_hi sc_lo sc_hi] 40 | im_lo = scl(1); 41 | im_hi = scl(2); 42 | sc_lo = scl(3); 43 | sc_hi = scl(4); 44 | end 45 | a = im_lo-sc_lo; 46 | if im_hi == im_lo 47 | l = 1; 48 | else 49 | l = (sc_hi-sc_lo)/(im_hi-im_lo); 50 | end 51 | fun = @(M) (M-a).*l; 52 | scl = [im_lo im_hi sc_lo sc_hi]; 53 | end 54 | % Scaling 55 | im = fun(im); 56 | % Casting 57 | im_cast(otyp); 58 | 59 | function im_cast(outType) 60 | if ~isa(currtyp, outType) 61 | im = cast(im, outType); 62 | end 63 | end 64 | 65 | end 66 | 67 | % %% old version here: 68 | % % Scale matrix im to a given range (default: 0~255) 69 | % 70 | % if nargin < 3 71 | % cut = [-inf inf]; 72 | % if nargin < 2 73 | % scl = 255; 74 | % end 75 | % end 76 | % im((im < cut(1)) | (im > cut(2))) = nan; 77 | % m_max = max(im(:)); 78 | % m_min = min(im(:)); 79 | % im = (im-m_min)/(m_max-m_min)*scl; 80 | % end 81 | -------------------------------------------------------------------------------- /utils/impad.m: -------------------------------------------------------------------------------- 1 | function [im_pad, imsize] = impad(im, padsize, im_margin) 2 | %IMPAD [im_pad, mn] = impad(im, padsize, immargin) 3 | % Pad array, only support 2-D or 3-D matrix. 4 | % If padsize is not specified, the width as well as height of the padded 5 | % image equals the length of diagonal. 6 | % 7 | % 08-Aug-2013 16:51:42 8 | % hanligong@gmail.com 9 | 10 | [M,N,L] = size(im); 11 | switch nargin 12 | case 3 13 | if ischar(padsize) 14 | padsize = 'diagonal'; 15 | else 16 | if length(padsize) == 1 17 | padsize = [1 1 1]*padsize; 18 | end 19 | end 20 | case 2 21 | im_margin = 0; 22 | if ischar(padsize) 23 | padsize = 'diagonal'; 24 | else 25 | if length(padsize) == 1 26 | padsize = [1 1 1]*padsize; 27 | end 28 | end 29 | case 1 30 | im_margin = 0; 31 | padsize = 'diagonal'; 32 | end 33 | if L == 1 %2-D 34 | if strcmpi(padsize,'diagonal') 35 | D = ceil(sqrt(M^2+N^2))+2*im_margin; 36 | M_pad = floor((D-M)/2); 37 | N_pad = floor((D-N)/2); 38 | m = D; 39 | n = D; 40 | imsize = D; 41 | else 42 | M_pad = ceil(padsize(1))+im_margin; 43 | N_pad = ceil(padsize(2))+im_margin; 44 | m = M+2*M_pad; 45 | n = N+2*N_pad; 46 | imsize = [m n]; 47 | end 48 | im_pad = zeros(m,n); 49 | im_pad(M_pad+1:M_pad+M,N_pad+1:N_pad+N) = im; 50 | else %3-D 51 | if strcmpi(padsize,'diagonal') 52 | D = ceil(sqrt(M^2+N^2+L^2))+2*im_margin; 53 | M_pad = floor((D-M)/2); 54 | N_pad = floor((D-N)/2); 55 | L_pad = floor((D-L)/2); 56 | m = D; 57 | n = D; 58 | l = D; 59 | imsize = D; 60 | else 61 | M_pad = ceil(padsize(1))+im_margin; 62 | N_pad = ceil(padsize(2))+im_margin; 63 | L_pad = ceil(padsize(3))+im_margin; 64 | m = M+2*M_pad; 65 | n = N+2*N_pad; 66 | l = L+2*L_pad; 67 | if L == 1 68 | imsize = [m n]; 69 | else 70 | imsize = [m n l]; 71 | end 72 | end 73 | im_pad = zeros(m,n,l); 74 | im_pad(M_pad+1:M_pad+M,N_pad+1:N_pad+N,L_pad+1:L_pad+L) = im; 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /imtool/immontage.m: -------------------------------------------------------------------------------- 1 | function varargout = immontage(imgCellArray, gridSize, border, boxLen, fcnHandle, padVal) 2 | %IMMONTAGE Display multiple image frames as rectangular montage. 3 | % Syntax: 4 | % out = immontage(imgCellArray, gridSize, border, boxLen, fcnHandle, padVal) 5 | % 6 | % IMG_ARRAY can be either cell array or 4D array 7 | 8 | if ~iscell(imgCellArray) 9 | imgArray = imgCellArray; 10 | numImg = size(imgArray, 4); 11 | imgCellArray = cell(1, numImg); 12 | for iImg = 1:numImg 13 | imgCellArray{iImg} = imgArray(:,:,:,iImg); 14 | end 15 | end 16 | if ~exist('gridSize', 'var') || isempty(gridSize) 17 | [numRow, numCol] = size(imgCellArray); 18 | else 19 | numRow = gridSize(1); 20 | numCol = gridSize(2); 21 | end 22 | if ~exist('border', 'var') || isempty(border) 23 | border = 0; 24 | end 25 | if ~exist('boxLen', 'var') || isempty(boxLen) 26 | boxLen = 128; 27 | end 28 | if ~exist('fcnHandle', 'var') || isempty(fcnHandle) 29 | fcnHandle = @(x) x; 30 | end 31 | if ~exist('padVal', 'var') || isempty(padVal) 32 | padVal = 255; 33 | end 34 | imgCellArray = cellfun(fcnHandle, imgCellArray, 'UniformOutput', false); 35 | imgClass = class(imgCellArray{1}); 36 | imgOut = padVal*ones(numRow*boxLen, numCol*boxLen, 3, imgClass); 37 | numImg = numel(imgCellArray); 38 | boxLen_ = boxLen-2*border; 39 | for iRow = 1:numRow 40 | for iCol = 1:numCol 41 | ii = sub2ind([numRow numCol], iRow, iCol); 42 | if ii > numImg 43 | break 44 | end 45 | img = imgCellArray{ii}; 46 | [height, width, nChnl] = size(img); 47 | if height < width 48 | img = imresize(img, boxLen_/width); 49 | [height, width, ~] = size(img); 50 | offset_h = fix((boxLen_-height)/2); 51 | offset_w = 0; 52 | elseif height > width 53 | img = imresize(img, boxLen_/height); 54 | [height, width, ~] = size(img); 55 | offset_w = fix((boxLen_-width)/2); 56 | offset_h = 0; 57 | else 58 | img = imresize(img, boxLen_/width); 59 | height = boxLen_; 60 | width = boxLen_; 61 | offset_w = 0; 62 | offset_h = 0; 63 | end 64 | if nChnl == 1 65 | img = repmat(img, [1 1 3]); 66 | end 67 | imgOut((iRow-1)*boxLen+1+offset_h+border:(iRow-1)*boxLen+height+offset_h+border, ... 68 | (iCol-1)*boxLen+1+offset_w+border:(iCol-1)*boxLen+width+offset_w+border,:) = img; 69 | end 70 | end 71 | if nargout > 0 72 | varargout{1} = imgOut; 73 | else 74 | imshow(imgOut); 75 | end 76 | -------------------------------------------------------------------------------- /test/build7.m: -------------------------------------------------------------------------------- 1 | function [W, p, t] = build7(im, angles) 2 | t_s = toc; 3 | %Pad image 4 | [im_pad,D] = impad(im,'diag',1); 5 | sz = size(im); 6 | m = sz(1); %rows 7 | n = sz(2); %cols 8 | n_proj = length(angles); 9 | N = m*n; %number of unknown variables 10 | M = D*n_proj; %number of equations 11 | %Weighting Factor Matrix 12 | try 13 | W = zeros(M,N); 14 | catch expr 15 | fprintf([expr.message '\nGenerates a sparse.\r']) 16 | W = sparse(M,N); 17 | end 18 | %proj_info = zeros(M,2); %[theta No.] 19 | p = zeros(M,1); %projection vector 20 | ix = false(M,1); 21 | rc = [m;n]/2+0.5; 22 | fprintf('Building Weight Matrix...\r') 23 | [x,y] = ind2sub(sz,1:N); 24 | %xy = [x;y]; %x,y coordinate 25 | %x,y coordinate with respect to rotation center 26 | xy_c = [x;y]-repmat(rc,1,N); 27 | d = 1; %side length of square 28 | A0 = d^2; 29 | H = @heaviside; 30 | try 31 | A = zeros(D,N); 32 | catch expr 33 | fprintf(['Building A...\n' ... 34 | expr.message '\nGenerating a sparse...\r']) 35 | A = sparse(D,N); 36 | end 37 | for kp = 1:n_proj 38 | A(:) = 0; %# 39 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 40 | pvec = sum(im_rot,1); 41 | t = -angles(kp)/180*pi; 42 | R = [cos(t) -sin(t);sin(t) cos(t)]; 43 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 44 | xy_rot = R*xy_c+D/2+0.5; 45 | idx = round(xy_rot(2,:)); 46 | ixM = D*(kp-1)+idx; %corresponding indice in W and p matrix 47 | %Calculate Area 48 | x1 = d*min(abs([sin(t) cos(t)]))+eps; 49 | x2 = d*max(abs([sin(t) cos(t)]))-eps; 50 | l = x1+x2; 51 | h = d^2/x2; 52 | %A0 = d^2; 53 | A1 = x1*h/2; 54 | G1 = @(x) H(x)-H(x-x1); 55 | G2 = @(x) H(x-x1)-H(x-x2); 56 | G3 = @(x) H(x-x2)-H(x-l); 57 | f_A = @(x) (x/x1).^2*A1.*G1(x)+... 58 | (A1+h*(x-x1)).*G2(x)+... 59 | (A0-((l-x)/x1).^2.*A1).*G3(x)+... 60 | A0*H(x-l); 61 | x0 = xy_rot(2,:)-l/2; 62 | S1 = f_A((idx-0.5)-x0); 63 | S2 = f_A((idx+0.5)-x0); 64 | for kn = 1:N 65 | A(idx(kn)-1,kn) = S1(kn); 66 | A(idx(kn) ,kn) = S2(kn)-S1(kn); 67 | A(idx(kn)+1,kn) = 1-S2(kn); 68 | p(ixM(kn)) = pvec(idx(kn)); 69 | ix(ixM(kn)) = true; 70 | end 71 | W((D*(kp-1)+(1:D)),:) = A; %# 72 | end 73 | % % 74 | %Delete all-zero rows in W 75 | fprintf('\nDelete all-zero rows in W...\r') 76 | %ix = sum(W,2)==0; 77 | try 78 | W(~ix,:) = []; 79 | catch expr 80 | fprintf([expr.message '\nConverting to sparse...\r']) 81 | W = sparse(W); 82 | W(~ix,:) = []; 83 | end 84 | p(~ix) = []; 85 | n_eq = size(W,1); 86 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 87 | figure('name','Sparsity pattern of matrix W'); 88 | spy(W) 89 | t_e = toc; 90 | t = t_e-t_s; 91 | fprintf('\nTotal: %f seconds\n\r',t); 92 | end 93 | -------------------------------------------------------------------------------- /test/build3.m: -------------------------------------------------------------------------------- 1 | function [W, p, t] = build3(im, angles) 2 | t_s = toc; 3 | %Pad image 4 | [im_pad,D] = impad(im,'diag',0); 5 | sz = size(im); 6 | m = sz(1); %rows 7 | n = sz(2); %cols 8 | n_proj = length(angles); 9 | N = m*n; %number of unknown variables 10 | M = D*n_proj; %number of equations 11 | try 12 | W = zeros(M,N); %weighting factor matrix 13 | catch expr 14 | fprintf([expr.message '\nGenerates a sparse.\r']) 15 | W = sparse(M,N); 16 | end 17 | %proj_info = zeros(M,2); %[theta No.] 18 | p = zeros(M,1); %projection vector 19 | ix = false(M,1); 20 | rc = [m;n]/2+0.5; 21 | %cnt = 0; 22 | fprintf('Building Weight Matrix...\r') 23 | % for kp = 1:n_proj 24 | % im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 25 | % projmat(kp,:) = sum(im_rot,1); 26 | % t = -angles(kp)/180*pi; 27 | % R = [cos(t) -sin(t);sin(t) cos(t)]; 28 | % %L = m*abs(sin(t))+n*abs(cos(t)); 29 | % %offset = ceil((D-L)/2); 30 | % fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 31 | % for kn = 1:N 32 | % [x,y] = ind2sub(sz,kn); 33 | % xy_rot = R*([x;y]-rc)+D/2+0.5; 34 | % idx = round(xy_rot(2));%# 35 | % %corresponding indice in W and p matrix 36 | % ixM = D*(kp-1)+idx; 37 | % W(ixM,kn) = 1; 38 | % %W(D*(kp-1)+idx,kn) = 1; 39 | % %W(cnt+idx-offset,kn) = 1; 40 | % %p(cnt+idx-offset) = projmat(kp,idx);%# 41 | % if ~ix(ixM) 42 | % p(ixM) = projmat(kp,idx); 43 | % ix(ixM) = true; 44 | % end 45 | % %p(D*(kp-1)+idx) = projmat(kp,idx); 46 | % %proj_info(cnt+idx-offset,:) = [kp,idx]; 47 | % end 48 | % %cnt = cnt+(D-2*offset); 49 | % %fprintf('%d equations built.\r',D-2*offset) 50 | % end 51 | % % 52 | [x,y] = ind2sub(sz,1:N); 53 | %xy = [x;y]; %x,y coordinate 54 | %x,y coordinate with respect to rotation center 55 | xy_c = [x;y]-repmat(rc,1,N); 56 | for kp = 1:n_proj 57 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 58 | pvec = sum(im_rot,1); 59 | t = -angles(kp)/180*pi; 60 | R = [cos(t) -sin(t);sin(t) cos(t)]; 61 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 62 | xy_rot = R*xy_c+D/2+0.5; 63 | idx = round(xy_rot(2,:)); 64 | ixM = D*(kp-1)+idx; %corresponding indice in W and p matrix 65 | for kn = 1:N 66 | W(ixM(kn),kn) = 1; 67 | if ~ix(ixM(kn)) 68 | p(ixM(kn)) = pvec(idx(kn)); 69 | ix(ixM(kn)) = true; 70 | end 71 | end 72 | end 73 | % % 74 | W = sparse(W); 75 | 76 | %Delete all-zero rows in W 77 | fprintf('\nDelete all-zero rows in W...\r') 78 | %ix = sum(W,2)==0; 79 | W(~ix,:) = []; 80 | p(~ix) = []; 81 | n_eq = size(W,1); 82 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 83 | figure('name','Sparsity pattern of matrix W'); 84 | spy(W) 85 | t_e = toc; 86 | t = t_e-t_s; 87 | fprintf('\nTotal: %f seconds\n\r',t); 88 | end 89 | -------------------------------------------------------------------------------- /test/build8.m: -------------------------------------------------------------------------------- 1 | function [W, p, t] = build8(im, angles) 2 | t_s = toc; 3 | %Pad image 4 | [im_pad,D] = impad(im,'diag',0); 5 | sz = size(im); 6 | m = sz(1); %rows 7 | n = sz(2); %cols 8 | n_proj = length(angles); 9 | N = m*n; %number of unknown variables 10 | M = D*n_proj; %number of equations 11 | %Weighting Factor Matrix 12 | try 13 | W = zeros(M,N); 14 | catch expr 15 | fprintf([expr.message '\nGenerates a sparse.\r']) 16 | W = sparse(M,N); 17 | end 18 | %proj_info = zeros(M,2); %[theta No.] 19 | p = zeros(M,1); %projection vector 20 | ix = false(M,1); 21 | rc = [m;n]/2+0.5; 22 | fprintf('Building Weight Matrix...\r') 23 | [x,y] = ind2sub(sz,1:N); 24 | %xy = [x;y]; %x,y coordinate 25 | %x,y coordinate with respect to rotation center 26 | xy_c = [x;y]-repmat(rc,1,N); 27 | d = 1; %side length of square 28 | A0 = d^2; 29 | H = @heaviside; 30 | try 31 | A = zeros(D,N); 32 | catch expr 33 | fprintf(['Building A...\n' ... 34 | expr.message '\nGenerating a sparse...\r']) 35 | A = sparse(D,N); 36 | end 37 | for kp = 1:n_proj 38 | A(:) = 0; %# 39 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 40 | pvec = sum(im_rot,1); 41 | t = -angles(kp)/180*pi; 42 | R = [cos(t) -sin(t);sin(t) cos(t)]; 43 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 44 | xy_rot = R*xy_c+D/2+0.5; 45 | idx = round(xy_rot(2,:)); 46 | ixM = D*(kp-1)+idx; %corresponding indice in W and p matrix 47 | %Calculate Area 48 | x1 = d*min(abs([sin(t) cos(t)]))+eps; 49 | x2 = d*max(abs([sin(t) cos(t)]))-eps; 50 | l = x1+x2; 51 | h = d^2/x2; 52 | %A0 = d^2; 53 | A1 = x1*h/2; 54 | G1 = @(x) H(x)-H(x-x1); 55 | G2 = @(x) H(x-x1)-H(x-x2); 56 | G3 = @(x) H(x-x2)-H(x-l); 57 | f_A = @(x) (x/x1).^2*A1.*G1(x)+... 58 | (A1+h*(x-x1)).*G2(x)+... 59 | (A0-((l-x)/x1).^2.*A1).*G3(x)+... 60 | A0*H(x-l); 61 | x0 = xy_rot(2,:)-l/2; 62 | S1 = f_A((idx-0.5)-x0)/A0; 63 | S2 = f_A((idx+0.5)-x0)/A0; 64 | for kn = 1:N 65 | if idx(kn)-1 >= 1 66 | A(idx(kn)-1,kn) = S1(kn); 67 | end 68 | A(idx(kn) ,kn) = S2(kn)-S1(kn); 69 | if idx(kn)+1 <= D 70 | A(idx(kn)+1,kn) = 1-S2(kn); 71 | end 72 | p(ixM(kn)) = pvec(idx(kn)); 73 | ix(ixM(kn)) = true; 74 | end 75 | W((D*(kp-1)+(1:D)),:) = A; %# 76 | end 77 | % % 78 | %Delete all-zero rows in W 79 | fprintf('\nDelete all-zero rows in W...\r') 80 | %ix = sum(W,2)==0; 81 | try 82 | W(~ix,:) = []; 83 | catch expr 84 | fprintf([expr.message '\nConverting to sparse...\r']) 85 | W = sparse(W); 86 | W(~ix,:) = []; 87 | end 88 | p(~ix) = []; 89 | n_eq = size(W,1); 90 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 91 | figure('name','Sparsity pattern of matrix W'); 92 | spy(W) 93 | t_e = toc; 94 | t = t_e-t_s; 95 | fprintf('\nTotal: %f seconds\n\r',t); 96 | end 97 | -------------------------------------------------------------------------------- /test/build6.m: -------------------------------------------------------------------------------- 1 | function [W, p, t] = build6(im, angles) 2 | t_s = toc; 3 | %Pad image 4 | [im_pad,D] = impad(im,'diag',0); 5 | sz = size(im); 6 | m = sz(1); %rows 7 | n = sz(2); %cols 8 | n_proj = length(angles); 9 | N = m*n; %number of unknown variables 10 | M = D*n_proj; %number of equations 11 | %Weighting Factor Matrix 12 | try 13 | W = zeros(M,N); 14 | method = 'matrix'; 15 | catch expr 16 | fprintf([expr.message '\nGenerating a sparse...\r']) 17 | W = sparse(M,N); 18 | method = 'sparse'; 19 | end 20 | %proj_info = zeros(M,2); %[theta No.] 21 | p = zeros(M,1); %projection vector 22 | ix = false(M,1); 23 | rc = [m;n]/2+0.5; 24 | %cnt = 0; 25 | fprintf('Building Weight Matrix...\r') 26 | [x,y] = ind2sub(sz,1:N); 27 | %xy = [x;y]; %x,y coordinate 28 | %x,y coordinate with respect to rotation center 29 | xy_c = [x;y]-repmat(rc,1,N); 30 | if strcmp(method,'sparse') 31 | try 32 | A = zeros(D,N); 33 | catch expr 34 | fprintf(['Building A...\n' ... 35 | expr.message '\nGenerating a sparse...\r']) 36 | A = sparse(D,N); 37 | end 38 | for kp = 1:n_proj 39 | A(:) = 0; %# 40 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 41 | pvec = sum(im_rot,1); 42 | t = -angles(kp)/180*pi; 43 | R = [cos(t) -sin(t);sin(t) cos(t)]; 44 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 45 | xy_rot = R*xy_c+D/2+0.5; 46 | idx = round(xy_rot(2,:)); 47 | ixM = D*(kp-1)+idx; %corresponding indice in W and p matrix 48 | for kn = 1:N 49 | %W(ixM(kn),kn) = 1; 50 | A(idx(kn),kn) = 1; 51 | p(ixM(kn)) = pvec(idx(kn)); 52 | ix(ixM(kn)) = true; 53 | end 54 | W((D*(kp-1)+(1:D)),:) = A; %# 55 | end 56 | else 57 | for kp = 1:n_proj 58 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 59 | pvec = sum(im_rot,1); 60 | t = -angles(kp)/180*pi; 61 | R = [cos(t) -sin(t);sin(t) cos(t)]; 62 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 63 | xy_rot = R*xy_c+D/2+0.5; 64 | idx = round(xy_rot(2,:)); 65 | ixM = D*(kp-1)+idx; %corresponding indice in W and p matrix 66 | for kn = 1:N 67 | W(ixM(kn),kn) = 1; 68 | p(ixM(kn)) = pvec(idx(kn)); 69 | ix(ixM(kn)) = true; 70 | end 71 | end 72 | end 73 | % % 74 | %Delete all-zero rows in W 75 | fprintf('\nDelete all-zero rows in W...\r') 76 | %ix = sum(W,2)==0; 77 | try 78 | W(~ix,:) = []; 79 | catch expr 80 | fprintf([expr.message '\nConverting to sparse...\r']) 81 | W = sparse(W); 82 | W(~ix,:) = []; 83 | end 84 | p(~ix) = []; 85 | n_eq = size(W,1); 86 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 87 | figure('name','Sparsity pattern of matrix W'); 88 | spy(W) 89 | t_e = toc; 90 | t = t_e-t_s; 91 | fprintf('\nTotal: %f seconds\n\r',t); 92 | end 93 | -------------------------------------------------------------------------------- /test/build4.m: -------------------------------------------------------------------------------- 1 | function [W, p, t] = build4(im, angles) 2 | t_s = toc; 3 | %Pad image 4 | [im_pad,D] = impad(im,'diag',0); 5 | sz = size(im); 6 | m = sz(1); %rows 7 | n = sz(2); %cols 8 | n_proj = length(angles); 9 | N = m*n; %number of unknown variables 10 | M = D*n_proj; %number of equations 11 | W_c = cell(n_proj,1); 12 | %proj_info = zeros(M,2); %[theta No.] 13 | p = zeros(M,1); %projection vector 14 | ix = false(M,1); 15 | rc = [m;n]/2+0.5; 16 | %cnt = 0; 17 | fprintf('Building Weight Matrix...\r') 18 | % for kp = 1:n_proj 19 | % im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 20 | % projmat(kp,:) = sum(im_rot,1); 21 | % t = -angles(kp)/180*pi; 22 | % R = [cos(t) -sin(t);sin(t) cos(t)]; 23 | % %L = m*abs(sin(t))+n*abs(cos(t)); 24 | % %offset = ceil((D-L)/2); 25 | % fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 26 | % for kn = 1:N 27 | % [x,y] = ind2sub(sz,kn); 28 | % xy_rot = R*([x;y]-rc)+D/2+0.5; 29 | % idx = round(xy_rot(2));%# 30 | % %corresponding indice in W and p matrix 31 | % ixM = D*(kp-1)+idx; 32 | % W(ixM,kn) = 1; 33 | % %W(D*(kp-1)+idx,kn) = 1; 34 | % %W(cnt+idx-offset,kn) = 1; 35 | % %p(cnt+idx-offset) = projmat(kp,idx);%# 36 | % if ~ix(ixM) 37 | % p(ixM) = projmat(kp,idx); 38 | % ix(ixM) = true; 39 | % end 40 | % %p(D*(kp-1)+idx) = projmat(kp,idx); 41 | % %proj_info(cnt+idx-offset,:) = [kp,idx]; 42 | % end 43 | % %cnt = cnt+(D-2*offset); 44 | % %fprintf('%d equations built.\r',D-2*offset) 45 | % end 46 | % % 47 | [x,y] = ind2sub(sz,1:N); 48 | %xy = [x;y]; %x,y coordinate 49 | %x,y coordinate with respect to rotation center 50 | xy_c = [x;y]-repmat(rc,1,N); 51 | for kp = 1:n_proj 52 | try 53 | W_c{kp} = zeros(D,N); %weighting factor matrix 54 | catch expr 55 | fprintf(['Build W{%d}...\n' expr.message '\nGenerate a sparse.\r'],kp) 56 | W_c{kp} = sparse(D,N); 57 | end 58 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 59 | pvec = sum(im_rot,1); 60 | t = -angles(kp)/180*pi; 61 | R = [cos(t) -sin(t);sin(t) cos(t)]; 62 | fprintf('\nAngle No.%d(%d Degree)\r',kp,angles(kp)) 63 | xy_rot = R*xy_c+D/2+0.5; 64 | idx = round(xy_rot(2,:)); 65 | ixM = D*(kp-1)+idx; %corresponding indice in W and p matrix 66 | for kn = 1:N 67 | W_c{kp}(idx(kn),kn) = 1; 68 | p(ixM(kn)) = pvec(idx(kn)); 69 | ix(ixM(kn)) = true; 70 | end 71 | end 72 | % % 73 | %Convert cell to matrix 74 | % try 75 | % W = zeros(M,N); 76 | % catch expr 77 | % fprintf([expr.message '\nGenerate a sparse.\r']) 78 | % W = sparse(M,N); 79 | % end 80 | W = sparse(M,N); 81 | for kp = 1:n_proj 82 | W((D*(kp-1)+(1:D)),:) = W_c{kp}; 83 | end 84 | 85 | %Delete all-zero rows in W 86 | fprintf('\nDelete all-zero rows in W...\r') 87 | %ix = sum(W,2)==0; 88 | W(~ix,:) = []; 89 | p(~ix) = []; 90 | n_eq = size(W,1); 91 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 92 | figure('name','Sparsity pattern of matrix W'); 93 | spy(W) 94 | t_e = toc; 95 | t = t_e-t_s; 96 | fprintf('\nTotal: %f seconds\n\r',t); 97 | end 98 | -------------------------------------------------------------------------------- /imtool/imtest.m: -------------------------------------------------------------------------------- 1 | function im = imtest(im_type,m,n) 2 | %IMTEST Generate test image\ 3 | % Testing Images: 4 | % 5 | % 6 | % 08-Aug-2013 19:18:05 7 | % hanligong@gmail.com 8 | 9 | switch nargin 10 | case 0 11 | im_type = 'phan'; 12 | m = 256; 13 | n = 256; 14 | case 1 15 | if ~ischar(im_type) 16 | switch im_type 17 | case 0 18 | im_type = 'c'; 19 | case 1 20 | im_type = 'c1'; 21 | case 2 22 | im_type = 'c2'; 23 | case 3 24 | im_type = 'c3'; 25 | case 4 26 | im_type = 'ring'; 27 | case 5 28 | im_type = 's'; 29 | case 6 30 | im_type = 's2'; 31 | case 7 32 | im_type = 's4'; 33 | case 8 34 | im_type = 'sq'; 35 | case 9 36 | im_type = 'phantom'; 37 | case 10 38 | im_type = 'lena'; 39 | case 12 40 | im_type = 'cat'; 41 | case 13 42 | im_type = 'head'; 43 | case 14 44 | im_type = 'gauss'; 45 | case 15 46 | im_type = 'noise'; 47 | otherwise 48 | im_type = 'non'; 49 | end 50 | end 51 | m = 256; 52 | n = 256; 53 | case 2 54 | if length(m) ~= 2 55 | n = m; 56 | else 57 | n = m(2); 58 | m = m(1); 59 | end 60 | end 61 | M = 256; 62 | N = 256; 63 | [x,y] = meshgrid(1:M,1:N); 64 | x = x'; 65 | y = y'; 66 | if strcmpi(im_type,'c') 67 | im = zeros(M,N); 68 | im((x-N/2).^2+(y-M/2).^2 <= 32^2) = 255; 69 | elseif strcmpi(im_type,'c1') 70 | im = zeros(M,N); 71 | im((x-N/4).^2+(y-M/4).^2 <= 32^2) = 255; 72 | elseif strcmpi(im_type,'c2') 73 | im = zeros(M,N); 74 | im((x- N/4).^2+(y-M/2).^2 <= 48^2) = 255; 75 | im((x-3*N/4).^2+(y-M/2).^2 <= 16^2) = 255; 76 | elseif strcmpi(im_type,'c3') 77 | im = zeros(M,N); 78 | im((x- N/2).^2+(y-3*M/4).^2 <= 16^2) = 255; 79 | im((x-3*N/4).^2+(y- M/4).^2 <= 16^2) = 255; 80 | im((x- N/4).^2+(y- M/4).^2 <= 16^2) = 255; 81 | elseif strcmpi(im_type,'ring') 82 | im = zeros(M,N); 83 | im((x-N/2).^2+(y-M/2).^2<=102^2 & (x-N/2).^2+(y-M/2).^2>=90^2) = 255; 84 | elseif strcmpi(im_type,'s') 85 | im = zeros(M,N); 86 | l = 32; 87 | for k1 = 0:M/l-1 88 | im(k1*l+1:k1*l+l/2,:) = 255; 89 | end 90 | elseif strcmpi(im_type,'s2') 91 | im = zeros(M,N); 92 | l = 32; 93 | for k1 = 0:M/l-1 94 | im(k1*l+1:k1*l+l/2,1:N/2) = 255; 95 | end 96 | for k2 = 0:(N/2)/l-1 97 | im(:,N/2+(k2*l+1:k2*l+l/2)) = 255; 98 | end 99 | elseif strcmpi(im_type,'s4') 100 | im = zeros(M,N); 101 | l = 32; 102 | for k1 = 0:(M/2)/l-1 103 | im((k1*l+1:k1*l+l/2),1:N/2) = 255; 104 | end 105 | for k2 = 0:(N/2)/l-1 106 | im(M/2+1:M,(k2*l+1:k2*l+l/2)) = 255; 107 | end 108 | for k1 = 0:(M/2)/l-1 109 | im(M/2+(k1*l+1:k1*l+l/2),N/2+1:N) = 255; 110 | end 111 | for k2 = 0:(N/2)/l-1 112 | im(1:M/2,N/2+(k2*l+1:k2*l+l/2)) = 255; 113 | end 114 | elseif strcmpi(im_type,'sq') 115 | im = zeros(M,N); 116 | l = 64; 117 | im((x-(M/2-l/2)).*(x-(M/2+l/2))<=0 & ... 118 | (y-(N/2-sqrt(3)*l/2)).*(y-(N/2+sqrt(3)*l/2))<=0) = 255; 119 | elseif strcmpi(im_type,'phantom') 120 | im = phantom(256); 121 | im = im*255; 122 | elseif strcmpi(im_type,'lena') 123 | S = load('lena.mat'); 124 | im = S.im; 125 | im = imresize(im,[M,N]); 126 | elseif strcmpi(im_type,'cat') 127 | S = load('cat.mat'); 128 | im = S.im; 129 | im = imresize(im,[M,N]); 130 | elseif strcmpi(im_type,'head') 131 | S = load('head.mat'); 132 | im = S.im; 133 | im = imresize(im,[M,N]); 134 | elseif strcmpi(im_type,'gauss') 135 | im = 255*exp(-((x-M/2).^2+(y-N/2).^2)/64^2); 136 | elseif strcmpi(im_type,'noise') 137 | im = 255*rand(M,N); 138 | else 139 | im = zeros(M,N); 140 | end 141 | if m~=M || n~=N 142 | im = imresize(im,[m,n]); 143 | end 144 | im = uint8(im); 145 | if nargout == 0 146 | imshow(im); 147 | end 148 | end -------------------------------------------------------------------------------- /test/test_07_Compare_simple_area_weightmat.m: -------------------------------------------------------------------------------- 1 | %TEST 07: Simple v.s. Area (two methods of building weight matrix) 2 | clear 3 | clc 4 | pic = 'phan'; 5 | fprintf('TEST 07: Simple v.s. Area (two methods of building weight matrix)\r'); 6 | im = imtest(pic,128); 7 | figure('name',['Original Image: ' pic]) 8 | imshow(im) 9 | siz = size(im); 10 | % angles = 0:2.5:179; 11 | % [W1,p1] = buildWeightMatrixSimple(im,angles); 12 | % [W2,p2] = buildWeightMatrixArea(im,angles); 13 | % %Simple 14 | % fprintf('Reconstruct from simple-W:\r') 15 | % im_rec1 = tomo_recon_lsqr(W1, p1, siz, 1e-3, 3500); 16 | % %im_rec1 = tomo_recon_myart(W1, p1, siz, 500); 17 | % im_rec1 = uint8(imscale(im_rec1)); 18 | % figure('name','Simple') 19 | % imshow(im_rec1); 20 | % %Area 21 | % fprintf('Reconstruct from area-W:\r') 22 | % im_rec2 = tomo_recon_lsqr(W2, p2, siz, 1e-3, 3500); 23 | % %im_rec2 = tomo_recon_myart(W2, p2, siz, 500); 24 | % im_rec2 = uint8(imscale(im_rec2)); 25 | % figure('name','Area') 26 | % imshow(im_rec2); 27 | 28 | % angles = 0:6:179; 29 | % [W,p] = buildWeightMatrixArea(im,angles); 30 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 31 | % im_rec = uint8(imscale(im_rec)); 32 | % figure('name','180') 33 | % imshow(im_rec); 34 | % angles = 0:4.5:134; 35 | % [W,p] = buildWeightMatrixArea(im,angles); 36 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 37 | % im_rec = uint8(imscale(im_rec)); 38 | % figure('name','135') 39 | % imshow(im_rec); 40 | % angles = 0:3:79; 41 | % [W,p] = buildWeightMatrixArea(im,angles); 42 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 43 | % im_rec = uint8(imscale(im_rec)); 44 | % figure('name','90') 45 | % imshow(im_rec); 46 | % angles = 0:1.5:44; 47 | % [W,p] = buildWeightMatrixArea(im,angles); 48 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 49 | % im_rec = uint8(imscale(im_rec)); 50 | % figure('name','45') 51 | % imshow(im_rec); 52 | 53 | % [M,N] = size(im); 54 | % D = ceil(sqrt(M^2+N^2)); 55 | % M_pad = ceil((D-M)/2)+1; 56 | % N_pad = ceil((D-N)/2)+1; 57 | % angles = 0:2:179; 58 | % [W,p] = buildWeightMatrixArea(im,angles); 59 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 60 | % im_rec = uint8(imscale(im_rec)); 61 | % figure('name','lsqr180') 62 | % imshow(im_rec); 63 | % [pm,~] = tomoproj2d(im,angles); 64 | % im_rec = tomo_recon_fbp(pm,angles); 65 | % im_rec = im_rec(M_pad:D-M_pad,N_pad:D-N_pad); 66 | % im_rec = uint8(imscale(im_rec)); 67 | % figure('name','fbp180') 68 | % imshow(im_rec); 69 | % angles = 0:1:89; 70 | % [W,p] = buildWeightMatrixArea(im,angles); 71 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 72 | % im_rec = uint8(imscale(im_rec)); 73 | % figure('name','lsqr90') 74 | % imshow(im_rec); 75 | % [pm,~] = tomoproj2d(im,angles); 76 | % im_rec = tomo_recon_fbp(pm,angles); 77 | % im_rec = im_rec(M_pad:D-M_pad,N_pad:D-N_pad); 78 | % im_rec = uint8(imscale(im_rec)); 79 | % figure('name','fbp90') 80 | % imshow(im_rec); 81 | 82 | % angles = 0:3:179; 83 | % [W,p] = buildWeightMatrixArea(im,angles); 84 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 2000); 85 | % im_rec = uint8(imscale(im_rec)); 86 | % figure('name','lsqr') 87 | % imshow(im_rec); 88 | % im_rec = tomo_recon_myart(W, p, siz, 2000); 89 | % im_rec = uint8(imscale(im_rec)); 90 | % figure('name','myart') 91 | % imshow(im_rec); 92 | 93 | % angles = 0:6:89; 94 | % [W,p] = buildWeightMatrixArea(im,angles); 95 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 96 | % im_rec = uint8(imscale(im_rec)); 97 | % figure('name','0:6:90') 98 | % imshow(im_rec); 99 | % angles = 0:4:89; 100 | % [W,p] = buildWeightMatrixArea(im,angles); 101 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 102 | % im_rec = uint8(imscale(im_rec)); 103 | % figure('name','0:4:90') 104 | % imshow(im_rec); 105 | % angles = 0:2:89; 106 | % [W,p] = buildWeightMatrixArea(im,angles); 107 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 108 | % im_rec = uint8(imscale(im_rec)); 109 | % figure('name','0:2:90') 110 | % imshow(im_rec); 111 | % angles = 0:1:89; 112 | % [W,p] = buildWeightMatrixArea(im,angles); 113 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 114 | % im_rec = uint8(imscale(im_rec)); 115 | % figure('name','0:1:90') 116 | % imshow(im_rec); 117 | % angles = 0:0.5:89; 118 | % [W,p] = buildWeightMatrixArea(im,angles); 119 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 120 | % im_rec = uint8(imscale(im_rec)); 121 | % figure('name','0:0.5:90') 122 | % imshow(im_rec); 123 | % angles = 0:0.25:89; 124 | % [W,p] = buildWeightMatrixArea(im,angles); 125 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 126 | % im_rec = uint8(imscale(im_rec)); 127 | % figure('name','0:0.25:90') 128 | % imshow(im_rec); 129 | % angles = 0:0.1:89; 130 | % [W,p] = buildWeightMatrixArea(im,angles); 131 | % im_rec = tomo_recon_lsqr(W, p, siz, 1e-3, 3500); 132 | % im_rec = uint8(imscale(im_rec)); 133 | % figure('name','0:0.1:90') 134 | % imshow(im_rec); 135 | % save('phan_0..0.1..90.mat','W','p','angles') 136 | -------------------------------------------------------------------------------- /test/demo_tomo.m: -------------------------------------------------------------------------------- 1 | %% Structure and Arrangement of this toolbox 2 | % There are two main methods for tomographic reconstruction: one is based 3 | % on Radon transform and its inverse transform such as filtered 4 | % back-projection (FBP), another is based on solving linear algebra 5 | % equations such as algebraic reconstruction technique (ART). 6 | % 7 | % This toolbox includes tools for creating projections and reconstructing 8 | % the image from projections: 9 | % tomo_projection_2d, computes projections of a given image; 10 | % build_weight_matrix, builds weighting factor matrix used for algebraic 11 | % methods; 12 | % tomo_reconstruction_bp, reconstructs the image from its projections using 13 | % BP method; 14 | % tomo_reconstruction_fbp, reconstructs the image from its projections 15 | % using FBP method; 16 | % tomo_reconstruction_art, reconstructs the image from its projections 17 | % using ART method; 18 | % tomo_reconstruction_sart, reconstructs the image from its projections 19 | % using SART method; 20 | % tomo_reconstruction_lsqr, reconstructs the image from its projections, 21 | % equations are solved by calling the build-in lsqr function; 22 | % 23 | %% References: 24 | % http://www.sv.vt.edu/xray_ct/parallel/Parallel_CT.html 25 | % http://www.dspguide.com/ch25/5.htm 26 | % http://www.owlnet.rice.edu/~elec539/Projects97/cult/node2.html 27 | % http://www.owlnet.rice.edu/~elec539/Projects97/cult/node3.html#SECTION00021000000000000000 28 | % http://www.owlnet.rice.edu/~elec539/Projects97/cult/node4.html#SECTION00022000000000000000 29 | % http://en.wikipedia.org/wiki/Radon_transform 30 | % http://en.wikipedia.org/wiki/Deconvolution 31 | % http://www.tnw.tudelft.nl/index.php?id=33826&binary=/doc/mvanginkel_radonandhough_tr2004.pdf 32 | % 33 | % Demos 34 | % %% Show test images 35 | % fprintf(1,'Demo 00: Show test images.\r'); 36 | % for k = 0:15 37 | % figure 38 | % imshow(imtest(k)) 39 | % end 40 | %% Test filters 41 | load bone01; 42 | n_it = 15; 43 | tao = 0.1; 44 | lambda = 30; 45 | gn = 1; 46 | fprintf('Demo 01: Compare Anisotropic Filter and Bilateral Filter\r'); 47 | fprintf('Parameters:\n\nAnisoDiff:\niteration %d\ntao %f\nlambda %f\r',n_it,tao,lambda); 48 | fprintf('Bilateral:\ninteration %d\nmask size %dx%d\nlambda_s AUTO\nlambda_r AUTO\r',n_it,2*gn+1,2*gn+1); 49 | im0 = rgb2gray(im0); 50 | im1 = anisodiff(im0,n_it,tao,lambda,1); 51 | im2 = bilateral_filter(im0,n_it,gn); 52 | figure('name','Original Image') 53 | imshow(uint8(im0)); 54 | figure('name','Anisotropic Filter') 55 | imshow(uint8(im1)); 56 | figure('name','Bilateral Filter') 57 | imshow(uint8(im2)); 58 | % Compute projections 59 | fprintf(1,'Demo 02: Compute projections\r'); 60 | im = imtest('phantom'); 61 | ang = 0:1:179.99; 62 | [projmat, ~] = tomo_projection_2d(im,ang); 63 | figure('name','Sinogram') 64 | imshow(uint8(imscale(projmat))); 65 | %% Build weight matrix 66 | fprintf(1,'Demo 03: Build weight matrix\r'); 67 | sz = [128,128]; 68 | ang = 0:2:179.99; 69 | [W, ~] = build_weight_matrix(sz, ang, 0, 'area'); 70 | figure('name','Weight Matrix'); 71 | spy(W); 72 | %% Filtered back-projection reconstruction 73 | fprintf(1,'Demo 04: Filtered back-projection\r'); 74 | im = imtest('phantom'); 75 | ang = 0:1:179.99; 76 | [projmat, ~] = tomo_projection_2d(im,ang); 77 | tomo_reconstruction_fbp(projmat,ang,true); 78 | %% Algebraic methods 79 | fprintf(1,'Demo 05: Algebraic methods\r'); 80 | clear; 81 | clc; 82 | im = imtest('phantom'); 83 | sz = size(im); 84 | ang = 0:3:179.99; 85 | [W, p, ~, ~] = build_weight_matrix(im, ang, 1, 'area'); 86 | fprintf('LSQR solver\r') 87 | im_rec = tomo_reconstruction_lsqr(W, p, sz, 0.5*1e-3, 200); 88 | im_rec = uint8(imscale(im_rec)); 89 | figure('name','LSQR') 90 | imshow(im_rec); 91 | %% ART 92 | fprintf(1,'Demo 06_01: ART methods\r'); 93 | clear; 94 | clc; 95 | im = imtest('phantom',[128,128]); 96 | sz = size(im); 97 | ang = 0:3:179.99; 98 | [W, p, ~, ~] = build_weight_matrix(im, ang, 1, 'area'); 99 | fprintf('ART solver\r') 100 | im_rec = tomo_reconstruction_art(W, p, sz); 101 | im_rec = uint8(imscale(im_rec)); 102 | figure('name','ART') 103 | imshow(im_rec); 104 | 105 | fprintf(1,'Demo 06_02: ART methods\r'); 106 | clear; 107 | clc; 108 | im = imtest('phantom',[256,256]); 109 | sz = size(im); 110 | ang = 0:12:179.99; 111 | [W, p, ~, ~] = build_weight_matrix(im, ang, 1, 'area'); 112 | fprintf('ART solver\r') 113 | im_rec = tomo_reconstruction_art(W, p, sz); 114 | im_rec = uint8(imscale(im_rec)); 115 | figure('name','ART') 116 | imshow(im_rec); 117 | %% Two ways of calling function 'build_weight_matrix' 118 | clear; 119 | clc; 120 | im = imtest('phantom',[128,128]); 121 | sz = size(im); 122 | ang = 0:12:179.99; 123 | % Method 1 124 | fprintf('The first way to call ''build_weight_matrix''\r') 125 | [proj_mat,D] = tomo_projection_2d(im,ang); 126 | [W, ix] = build_weight_matrix(im, ang, false, 'area'); 127 | p = proj_mat'; 128 | p = p(:); 129 | W = sparse(W); 130 | W(~ix,:) = []; 131 | p(~ix) = []; 132 | im_rec = tomo_reconstruction_lsqr(W, p, sz, 0.5*1e-3, 200); 133 | im_rec = uint8(imscale(im_rec)); 134 | figure('name','The first method') 135 | imshow(im_rec); 136 | % Method 2 137 | fprintf('The second way to call ''build_weight_matrix''\r') 138 | [W, p, ~, ~] = build_weight_matrix(im, ang, true, 'area'); 139 | im_rec = tomo_reconstruction_lsqr(W, p, sz, 0.5*1e-3, 200); 140 | im_rec = uint8(imscale(im_rec)); 141 | figure('name','The second method') 142 | imshow(im_rec); 143 | -------------------------------------------------------------------------------- /demo.m: -------------------------------------------------------------------------------- 1 | %% Structure and Arrangement of this toolbox 2 | % There are two main methods for tomographic reconstruction: one is based 3 | % on Radon transform and its inverse transform such as filtered 4 | % back-projection (FBP), another is based on solving linear algebra 5 | % equations such as algebraic reconstruction technique (ART). 6 | % 7 | % This toolbox includes tools for creating projections and reconstructing 8 | % the image from projections: 9 | % tomo_projection_2d, computes projections of a given image; 10 | % build_weight_matrix, builds weighting factor matrix used for algebraic 11 | % methods; 12 | % tomo_reconstruction_bp, reconstructs the image from its projections using 13 | % BP method; 14 | % tomo_reconstruction_fbp, reconstructs the image from its projections 15 | % using FBP method; 16 | % tomo_reconstruction_art, reconstructs the image from its projections 17 | % using ART method; 18 | % tomo_reconstruction_sart, reconstructs the image from its projections 19 | % using SART method; 20 | % tomo_reconstruction_lsqr, reconstructs the image from its projections, 21 | % equations are solved by calling the build-in lsqr function; 22 | % 23 | %% References: 24 | % http://www.sv.vt.edu/xray_ct/parallel/Parallel_CT.html 25 | % http://www.dspguide.com/ch25/5.htm 26 | % http://www.owlnet.rice.edu/~elec539/Projects97/cult/node2.html 27 | % http://www.owlnet.rice.edu/~elec539/Projects97/cult/node3.html#SECTION00021000000000000000 28 | % http://www.owlnet.rice.edu/~elec539/Projects97/cult/node4.html#SECTION00022000000000000000 29 | % http://en.wikipedia.org/wiki/Radon_transform 30 | % http://en.wikipedia.org/wiki/Deconvolution 31 | % http://www.tnw.tudelft.nl/index.php?id=33826&binary=/doc/mvanginkel_radonandhough_tr2004.pdf 32 | % 33 | %% Demos 34 | 35 | %% Show test images 36 | fprintf(1,'Demo 00: Show test images.\r'); 37 | for k = 0:15 38 | figure 39 | imshow(imtest(k)) 40 | end 41 | 42 | %% Test filters 43 | load bone01; 44 | n_it = 15; 45 | tao = 0.1; 46 | lambda = 30; 47 | gn = 1; 48 | fprintf('Demo 01: Compare Anisotropic Filter and Bilateral Filter\r'); 49 | fprintf('Parameters:\n\nAnisoDiff:\niteration %d\ntao %f\nlambda %f\r',n_it,tao,lambda); 50 | fprintf('Bilateral:\ninteration %d\nmask size %dx%d\nlambda_s AUTO\nlambda_r AUTO\r',n_it,2*gn+1,2*gn+1); 51 | im0 = rgb2gray(im0); 52 | im1 = anisodiff(im0,n_it,tao,lambda,1); 53 | im2 = bilateral_filter(im0,n_it,gn); 54 | figure('name','Original Image') 55 | imshow(uint8(im0)); 56 | figure('name','Anisotropic Filter') 57 | imshow(uint8(im1)); 58 | figure('name','Bilateral Filter') 59 | imshow(uint8(im2)); 60 | 61 | %% Compute projections 62 | fprintf(1,'Demo 02: Compute projections\r'); 63 | im = imtest('phantom'); 64 | ang = 0:1:179.99; 65 | [projmat, ~] = tomo_projection_2d(im,ang); 66 | improj = uint8(imscale(projmat)); 67 | figure('name','Projections'); 68 | imshow(improj); 69 | 70 | %% Build weight matrix 71 | fprintf(1,'Demo 03: Build weight matrix\r'); 72 | sz = [128,128]; 73 | ang = 0:2:179.99; 74 | [W, ~] = build_weight_matrix(sz, ang, 0, 'area'); 75 | figure('name','Weight Matrix'); 76 | spy(W); 77 | 78 | %% Filtered back-projection reconstruction 79 | fprintf(1,'Demo 04: Filtered back-projection\r'); 80 | im = imtest('phantom'); 81 | ang = 0:1:179.99; 82 | [projmat, ~] = tomo_projection_2d(im,ang); 83 | tomo_reconstruction_fbp(projmat,ang,true); 84 | 85 | %% Algebraic methods 86 | fprintf(1,'Demo 05: Algebraic methods\r'); 87 | clear; 88 | clc; 89 | im = imtest('phantom'); 90 | sz = size(im); 91 | ang = 0:3:179.99; 92 | [W, p, ~, ~] = build_weight_matrix(im, ang, 1, 'area'); 93 | fprintf('LSQR solver\r') 94 | im_rec = tomo_reconstruction_lsqr(W, p, sz, 0.5*1e-3, 200); 95 | im_rec = uint8(imscale(im_rec)); 96 | figure('name','LSQR') 97 | imshow(im_rec); 98 | 99 | %% ART 100 | fprintf(1,'Demo 06_01: ART methods\r'); 101 | clear; 102 | clc; 103 | im = imtest('phantom',[128,128]); 104 | sz = size(im); 105 | ang = 0:3:179.99; 106 | [W, p, ~, ~] = build_weight_matrix(im, ang, 1, 'area'); 107 | fprintf('ART solver\r') 108 | im_rec = tomo_reconstruction_art(W, p, sz); 109 | im_rec = uint8(imscale(im_rec)); 110 | figure('name','ART') 111 | imshow(im_rec); 112 | 113 | fprintf(1,'Demo 06_02: ART methods\r'); 114 | clear; 115 | clc; 116 | im = imtest('phantom',[256,256]); 117 | sz = size(im); 118 | ang = 0:12:179.99; 119 | [W, p, ~, ~] = build_weight_matrix(im, ang, 1, 'area'); 120 | fprintf('ART solver\r') 121 | im_rec = tomo_reconstruction_art(W, p, sz); 122 | im_rec = uint8(imscale(im_rec)); 123 | figure('name','ART') 124 | imshow(im_rec); 125 | 126 | %% Two ways of calling function 'build_weight_matrix' 127 | clear; 128 | clc; 129 | im = imtest('phantom',[128,128]); 130 | sz = size(im); 131 | ang = 0:12:179.99; 132 | % Method 1 133 | fprintf('The first way to call ''build_weight_matrix''\r') 134 | [proj_mat,D] = tomo_projection_2d(im,ang); 135 | [W, ix] = build_weight_matrix(im, ang, false, 'area'); 136 | p = proj_mat'; 137 | p = p(:); 138 | W = sparse(W); 139 | W(~ix,:) = []; 140 | p(~ix) = []; 141 | im_rec = tomo_reconstruction_lsqr(W, p, sz, 0.5*1e-3, 200); 142 | im_rec = uint8(imscale(im_rec)); 143 | figure('name','The first method') 144 | imshow(im_rec); 145 | % Method 2 146 | fprintf('The second way to call ''build_weight_matrix''\r') 147 | [W, p, ~, ~] = build_weight_matrix(im, ang, true, 'area'); 148 | im_rec = tomo_reconstruction_lsqr(W, p, sz, 0.5*1e-3, 200); 149 | im_rec = uint8(imscale(im_rec)); 150 | figure('name','The second method') 151 | imshow(im_rec); 152 | 153 | %% Crop reconstructed images 154 | im = imtest('lena',512); 155 | angles = 0:1:179; 156 | im = double(im); 157 | % Compute projection matrix 158 | [projmat,D] = tomo_projection_2d(im,angles); 159 | % Reconstruct image 160 | im_rec = tomo_reconstruction_fbp(projmat,angles); 161 | % Crop reconstructed image 162 | [m,n] = size(im); 163 | m_pad = floor((D-m)/2); 164 | n_pad = floor((D-n)/2); 165 | im_crop = im_rec(m_pad+1:m_pad+m,n_pad+1:n_pad+n); 166 | im_crop = uint8(imscale(im_crop)); 167 | figure 168 | imshow(im_crop) 169 | % Crop reconstructed image by calling function 'imcrop_tomo' 170 | im_crop2 = imcrop_tomo(im_rec,[m,n]); 171 | im_crop2 = uint8(imscale(im_crop2)); 172 | figure 173 | imshow(im_crop2) 174 | 175 | %% An animation illustrates how to compute weighting factor matrix 176 | test_buildweight 177 | -------------------------------------------------------------------------------- /test/test_buildweight.m: -------------------------------------------------------------------------------- 1 | function test_buildweight 2 | % This function is wrote for the purpose of debugging. 3 | 4 | %Pad image 5 | im = ones(4,5); 6 | %disp(im) 7 | angles = [23 67]; 8 | [im_pad,D] = impad(im,'diag',1); %avoid <=1 or >=D indices 9 | %disp(im_pad) 10 | sz = size(im); 11 | m = sz(1); %rows 12 | n = sz(2); %cols 13 | n_p = length(angles); 14 | N = m*n; %number of unknown variables 15 | M = D*n_p; %number of equations 16 | %Weighting Factor Matrix 17 | W = zeros(M,N); 18 | %proj_info = zeros(M,2); %[theta No.] 19 | projmat = zeros(n_p,D); %projection matrix 20 | p = zeros(M,1); %projection vector 21 | ix = false(M,1); 22 | %=========================% 23 | rc = repmat([m;n]/2+0.5,1,N); 24 | rcy = repmat(n/2+0.5,1,N); 25 | %xypad = repmat(floor([(D-m)/2;(D-n)/2]),1,N); 26 | ypad = repmat(floor((D-n)/2),1,N); 27 | %=========================% 28 | %cnt = 0; 29 | fprintf('Building Weight Matrix...\r') 30 | [x,y] = ind2sub(sz,1:N); 31 | %xy = [x;y]; %x,y coordinate 32 | %x,y coordinate with respect to rotation center 33 | xy_c = [x;y]-rc; 34 | %Parameter for accounting area ratio 35 | d = 1; %side length of square 36 | A0 = d^2; 37 | H = @heaviside; 38 | %% lines 39 | % % 40 | figure 41 | axes('xlim',[0 D+1],'ylim',[0 D+1],'dataaspectratio',[1 1 1],... 42 | 'xtick',1:D,'ytick',1:D,'box','on') 43 | %padded image 44 | for k = 0:D 45 | line([k+0.5 k+0.5],[0.5 D+0.5],'linestyle',':','color',[0 0 0]); 46 | line([0.5 D+0.5],[k+0.5 k+0.5],'linestyle',':','color',[0 0 0]); 47 | end 48 | %original image 49 | xp = floor((D-m)/2); 50 | yp = floor((D-n)/2); 51 | lxh = zeros(1,m+1); 52 | lyh = zeros(1,n+1); 53 | %gx = xp+0.5:1:xp+m+0.5; 54 | %gy = yp+0.5:1:yp+n+0.5; 55 | gx = 0.5:1:m+0.5; 56 | gy = 0.5:1:n+0.5; 57 | for k = 1:m+1 58 | %lxh(k) = line([gx(k) gx(k)],[yp+0.5 yp+n+0.5],'color',[0 0 1]); 59 | lxh(k) = line([gx(k) gx(k)]+xp,[yp+0.5 yp+n+0.5],'color',[0 0 1]); 60 | end 61 | for k = 1:n+1 62 | %lyh(k) = line([xp+0.5 xp+m+0.5],[gy(k) gy(k)],'color',[0 0 1]); 63 | lyh(k) = line([xp+0.5 xp+m+0.5],[gy(k) gy(k)]+yp,'color',[0 0 1]); 64 | end 65 | txth = zeros(D,D); 66 | for ii = 1:D 67 | for jj = 1:D 68 | txth(ii,jj) = text(ii,jj,num2str(im_pad(ii,jj)),... 69 | 'HorizontalAlignment','center'); 70 | end 71 | end 72 | pth = line(NaN,NaN,'marker','o','markeredgecolor',[1 0 0]); 73 | lphc = line([0 D+1],[NaN NaN],'linestyle',':','color',[1 0 0]); 74 | lphu = line([0 D+1],[NaN NaN],'color',[1 0 0]); 75 | lphd = line([0 D+1],[NaN NaN],'color',[1 0 0]); 76 | ly0h = line([0 D+1],[NaN NaN],'color',[0 1 0]); 77 | % % 78 | %% Compute 79 | for kp = 1:n_p 80 | im_rot = imrotate(im_pad,-angles(kp),'bilinear','crop'); 81 | pvec = sum(im_rot,1); 82 | projmat(kp,:) = pvec; 83 | t = -angles(kp)/180*pi; 84 | RR = [cos(t) -sin(t);sin(t) cos(t)]; 85 | R = [sin(t) cos(t)]; 86 | % % 87 | for k = 1:m+1 88 | %lxy1 = RR*([gx(k);yp+0.5]-[D/2+0.5;D/2+0.5])+[D/2+0.5;D/2+0.5]; 89 | %lxy2 = RR*([gx(k);yp+n+0.5]-[D/2+0.5;D/2+0.5])+[D/2+0.5;D/2+0.5]; 90 | lxy1 = RR*([gx(k);0.5]-[m/2+0.5;n/2+0.5])+[m/2+0.5;n/2+0.5]+[xp;yp]; 91 | lxy2 = RR*([gx(k);n+0.5]-[m/2+0.5;n/2+0.5])+[m/2+0.5;n/2+0.5]+[xp;yp]; 92 | set(lxh(k),'xdata',[lxy1(1) lxy2(1)],'ydata',[lxy1(2) lxy2(2)]); 93 | end 94 | for k = 1:n+1 95 | %lxy1 = RR*([xp+0.5;gy(k)]-[D/2+0.5;D/2+0.5])+[D/2+0.5;D/2+0.5]; 96 | %lxy2 = RR*([xp+m+0.5;gy(k)]-[D/2+0.5;D/2+0.5])+[D/2+0.5;D/2+0.5]; 97 | lxy1 = RR*([0.5;gy(k)]-[m/2+0.5;n/2+0.5])+[m/2+0.5;n/2+0.5]+[xp;yp]; 98 | lxy2 = RR*([m+0.5;gy(k)]-[m/2+0.5;n/2+0.5])+[m/2+0.5;n/2+0.5]+[xp;yp]; 99 | set(lyh(k),'xdata',[lxy1(1) lxy2(1)],'ydata',[lxy1(2) lxy2(2)]); 100 | end 101 | updateImpad 102 | % % 103 | fprintf('\nAngle %d(%d Degree)\n%.1f%% completed.\r',... 104 | kp,angles(kp),100*kp/n_p) 105 | %=========================% 106 | xy_rot = R*xy_c+rcy+ypad; %!!! 107 | %=========================% 108 | %idx = round(xy_rot(2,:)); 109 | idx = round(xy_rot); 110 | %corresponding indice in W and p matrix 111 | ixM = D*(kp-1)+idx; 112 | %Calculate Area 113 | x1 = d*min(abs([sin(t) cos(t)]))+eps; 114 | x2 = d*max(abs([sin(t) cos(t)]))-eps; 115 | l = x1+x2; 116 | h = d^2/x2; 117 | %A0 = d^2; 118 | A1 = x1*h/2; 119 | G1 = @(x) H(x)-H(x-x1); 120 | G2 = @(x) H(x-x1)-H(x-x2); 121 | G3 = @(x) H(x-x2)-H(x-l); 122 | f_A = @(x) (x/x1).^2*A1.*G1(x)+... 123 | (A1+h*(x-x1)).*G2(x)+... 124 | (A0-((l-x)/x1).^2.*A1).*G3(x)+... 125 | A0*H(x-l); 126 | %x0 = xy_rot(2,:)-l/2; 127 | x0 = xy_rot-l/2; 128 | S1 = f_A((idx-0.5)-x0)/A0; 129 | S2 = f_A((idx+0.5)-x0)/A0; 130 | for kn = 1:N 131 | W(ixM(kn)-1,kn) = S1(kn); 132 | W(ixM(kn) ,kn) = S2(kn)-S1(kn); 133 | W(ixM(kn)+1,kn) = 1-S2(kn); 134 | % % 135 | [xx,yy] = ind2sub(sz,kn); 136 | xxyy = RR*([xx;yy]-[m/2+0.5;n/2+0.5])+[m/2+0.5;n/2+0.5]+[xp;yp]; 137 | set(pth,'xdata',xxyy(1),'ydata',xxyy(2)); 138 | disp(xxyy(2)) 139 | disp(idx(kn)) 140 | set(lphc,'ydata',[idx(kn) idx(kn)]); 141 | set(lphu,'ydata',[idx(kn) idx(kn)]+0.5); 142 | set(lphd,'ydata',[idx(kn) idx(kn)]-0.5); 143 | set(ly0h,'ydata',[x0(kn) x0(kn)]); 144 | pause(0.5) %# 145 | fprintf('\narea1: %.2f\n',S1(kn)); 146 | fprintf('\narea2: %.2f\n',S2(kn)-S1(kn)); 147 | fprintf('\narea3: %.2f\n',1-S2(kn)); 148 | % % 149 | %if ~ix(ixM(kn)) 150 | % p(ixM(kn)) = pvec(idx(kn)); 151 | % ix(ixM(kn)) = true; 152 | %end 153 | %=========================% 154 | %p(ixM(kn)) = projmat(kp,idx(kn)); 155 | p(ixM(kn)) = pvec(idx(kn)); 156 | % % 157 | disp(W((kp-1)*D+idx(kn),kn)); 158 | disp(pvec(idx(kn))); 159 | % % 160 | %=========================% 161 | ix(ixM(kn)) = true; 162 | end 163 | end 164 | %% Delete all-zero rows in W 165 | fprintf('\nDelete all-zero rows in W...\r') 166 | %ix = sum(W,2)==0; 167 | try 168 | W(~ix,:) = []; 169 | catch expr 170 | fprintf([expr.message '\nConverting to sparse...\r']) 171 | W = sparse(W); 172 | W(~ix,:) = []; 173 | end 174 | p(~ix) = []; 175 | n_eq = size(W,1); 176 | fprintf('\nFinish building A. \n%d equations in total.\r',n_eq); 177 | figure('name','Sparsity pattern of matrix W','numbertitle','off'); 178 | spy(W) 179 | e = W*im(:)-p; 180 | disp(e) 181 | 182 | function updateImpad 183 | for fii = 1:D 184 | for fjj = 1:D 185 | set(txth(fii,fjj),'string',num2str(im_rot(fii,fjj),'%.1f'),... 186 | 'HorizontalAlignment','center'); 187 | end 188 | end 189 | end 190 | end 191 | -------------------------------------------------------------------------------- /backproj.m: -------------------------------------------------------------------------------- 1 | function [img,H] = backproj(varargin) 2 | %BACKPROJ Compute inverse Radon transform. 3 | % I = BACKPROJ(R,THETA) reconstructs the image I from projection 4 | % data in the 2-D array R. The columns of R are parallel beam 5 | % projection data. IRADON assumes that the center of rotation 6 | % is the center point of the projections, which is defined as 7 | % ceil(size(R,1)/2). 8 | % 9 | % THETA describes the angles (in degrees) at which the projections 10 | % were taken. It can be either a vector containing the angles or 11 | % a scalar specifying D_theta, the incremental angle between 12 | % projections. If THETA is a vector, it must contain angles with 13 | % equal spacing between them. If THETA is a scalar specifying 14 | % D_theta, the projections are taken at angles THETA = m * D_theta; 15 | % m = 0,1,2,...,size(R,2)-1. If the input is the empty matrix 16 | % ([]), D_theta defaults to 180/size(R,2). 17 | % 18 | % BACKPROJ uses the filtered or unfiltered backprojection algorithm to perform 19 | % the inverse Radon transform. The filter is designed directly 20 | % in the frequency domain and then multiplied by the FFT of the 21 | % projections. The projections are zero-padded to a power of 2 22 | % before filtering to prevent spatial domain aliasing and to 23 | % speed up the FFT. 24 | % 25 | % I = BACKPROJ(R,THETA,INTERP,FILTER,D,N) specifies parameters 26 | % to use in the inverse Radon transform. You can specify 27 | % any combination of the last four arguments. IRADON uses 28 | % default values for any of these arguments that you omit. 29 | % 30 | % INTERP specifies the type of interpolation to use in the 31 | % backprojection. The available options are listed in order 32 | % of increasing accuracy and computational complexity: 33 | % 34 | % 'nearest' - nearest neighbor interpolation 35 | % 'linear' - linear interpolation (default) 36 | % 'spline' - spline interpolation 37 | % 38 | % FILTER specifies the filter to use for frequency domain filtering. 39 | % FILTER is a string that specifies any of the following standard 40 | % filters: 41 | % 42 | % 'None' No filter: simple (unfiltered) backprojection. 43 | % 'Ram-Lak' The cropped Ram-Lak or ramp filter (default). The 44 | % frequency response of this filter is |f|. Because 45 | % this filter is sensitive to noise in the projections, 46 | % one of the filters listed below may be preferable. 47 | % These filters multiply the Ram-Lak filter by a 48 | % window that de-emphasizes high frequencies. 49 | % 'Shepp-Logan' The Shepp-Logan filter multiplies the Ram-Lak 50 | % filter by a sinc function. 51 | % 'Cosine' The cosine filter multiplies the Ram-Lak filter 52 | % by a cosine function. 53 | % 'Hamming' The Hamming filter multiplies the Ram-Lak filter 54 | % by a Hamming window. 55 | % 'Hann' The Hann filter multiplies the Ram-Lak filter by 56 | % a Hann window. 57 | % 58 | % D is a scalar in the range (0,1] that modifies the filter by 59 | % rescaling its frequency axis. The default is 1. If D is less 60 | % than 1, the filter is compressed to fit into the frequency range 61 | % [0,D], in normalized frequencies; all frequencies above D are set 62 | % to 0. 63 | % 64 | % N is a scalar that specifies the number of rows and columns in the 65 | % reconstructed image. If N is not specified, the size is determined 66 | % from the length of the projections: 67 | % 68 | % N = 2*floor(size(R,1)/(2*sqrt(2))) 69 | % 70 | % If you specify N, BACKPROJ reconstructs a smaller or larger portion of 71 | % the image, but does not change the scaling of the data. 72 | % 73 | % If the projections were calculated with the RADON function, the 74 | % reconstructed image may not be the same size as the original 75 | % image. 76 | % 77 | % [I,H] = BACKPROJ(...) returns the frequency response of the filter 78 | % in the vector H. 79 | % 80 | % Class Support 81 | % ------------- 82 | % All input arguments must be of class double. The output arguments are 83 | % of class double. 84 | % 85 | % Example 86 | % ------- 87 | % P = phantom(128); 88 | % R = radon(P,0:179); 89 | % I = backproj(R,0:179,'nearest','Hann'); 90 | % imshow(P); figure; imshow(I); 91 | % 92 | % See also IRADON, RADON, PHANTOM. 93 | % 94 | % Oliver Wieben - 23Mar01 95 | % This code is based on the MATLAB-file IRADON with added support 96 | % for unfiltered back-projection. 97 | 98 | % Chris Griffin 6-5-97 99 | % Copyright 1993-1998 The MathWorks, Inc. All Rights Reserved. 100 | % $Revision: 1.4 $ $Date: 1997/11/24 16:18:24 $ 101 | 102 | % References: 103 | % A. C. Kak, Malcolm Slaney, "Principles of Computerized Tomographic 104 | % Imaging", IEEE Press 1988. 105 | 106 | 107 | [p,theta,filter,d,interp,N] = parse_inputs(varargin{:}); 108 | 109 | % Design the filter 110 | len=size(p,1); 111 | H = designFilter(filter, len, d); 112 | p(length(H),1)=0; % Zero pad projections 113 | 114 | % In the code below, I continuously reuse the array p so as to 115 | % save memory. This makes it harder to read, but the comments 116 | % explain what is going on. 117 | 118 | p = fft(p); % p holds fft of projections 119 | 120 | for i = 1:size(p,2) 121 | p(:,i) = p(:,i).*H; % frequency domain filtering 122 | end 123 | 124 | p = real(ifft(p)); % p is the filtered projections 125 | p(len+1:end,:) = []; % Truncate the filtered projections 126 | img = zeros(N); % Allocate memory for the image. 127 | 128 | % Define the x & y axes for the reconstructed image so that the origin 129 | % (center) is in the spot which RADON would choose. 130 | xax = (1:N)-ceil(N/2); 131 | x = repmat(xax, N, 1); % x coordinates, the y coordinates are rot90(x) 132 | y = rot90(x); 133 | 134 | costheta = cos(theta); 135 | sintheta = sin(theta); 136 | ctrIdx = ceil(len/2); % index of the center of the projections 137 | 138 | % Zero pad the projections to size 1+2*ceil(N/sqrt(2)) if this 139 | % quantity is greater than the length of the projections 140 | imgDiag = 2*ceil(N/sqrt(2))+1; % largest distance through image. 141 | if size(p,1) < imgDiag 142 | rz = imgDiag - size(p,1); % how many rows of zeros 143 | p = [zeros(ceil(rz/2),size(p,2)); p; zeros(floor(rz/2),size(p,2))]; 144 | ctrIdx = ctrIdx+ceil(rz/2); 145 | end 146 | 147 | % Backprojection - vectorized in (x,y), looping over theta 148 | if strcmp(interp, 'nearest neighbor') 149 | for i=1:length(theta) 150 | proj = p(:,i); 151 | t = round(x*costheta(i) + y*sintheta(i)); 152 | img = img + proj(t+ctrIdx); 153 | end 154 | elseif strcmp(interp, 'linear') 155 | for i=1:length(theta) 156 | proj = p(:,i); 157 | t = x.*costheta(i) + y.*sintheta(i); 158 | a = floor(t); 159 | img = img + (t-a).*proj(a+1+ctrIdx) + (a+1-t).*proj(a+ctrIdx); 160 | end 161 | elseif strcmp(interp, 'spline') 162 | for i=1:length(theta) 163 | proj = p(:,i); 164 | taxis = (1:size(p,1)) - ctrIdx; 165 | t = x.*costheta(i) + y.*sintheta(i); 166 | projContrib = interp1(taxis,proj,t(:),'*spline'); 167 | img = img + reshape(projContrib,N,N); 168 | end 169 | end 170 | 171 | img = img*pi/(2*length(theta)); 172 | 173 | 174 | 175 | 176 | %%% 177 | %%% Sub-Function: designFilter 178 | %%% 179 | 180 | function filt = designFilter(filter, len, d) 181 | % Returns the Fourier Transform of the filter which will be 182 | % used to filter the projections 183 | % 184 | % INPUT ARGS: filter - either the string specifying the filter 185 | % len - the length of the projections 186 | % d - the fraction of frequencies below the nyquist 187 | % which we want to pass 188 | % 189 | % OUTPUT ARGS: filt - the filter to use on the projections 190 | 191 | 192 | order = max(64,2^nextpow2(2*len)); 193 | 194 | % First create a ramp filter - go up to the next highest 195 | % power of 2. 196 | 197 | filt = 2*( 0:(order/2) )./order; 198 | w = 2*pi*(0:size(filt,2)-1)/order; % frequency axis up to Nyquist 199 | 200 | switch filter 201 | case 'ram-lak' 202 | % Do nothing 203 | case 'shepp-logan' 204 | % be careful not to divide by 0: 205 | filt(2:end) = filt(2:end) .* (sin(w(2:end)/(2*d))./(w(2:end)/(2*d))); 206 | case 'cosine' 207 | filt(2:end) = filt(2:end) .* cos(w(2:end)/(2*d)); 208 | case 'hamming' 209 | filt(2:end) = filt(2:end) .* (.54 + .46 * cos(w(2:end)/d)); 210 | case 'hann' 211 | filt(2:end) = filt(2:end) .*(1+cos(w(2:end)./d)) / 2; 212 | case 'none' 213 | filt(1:end) = 1; 214 | otherwise 215 | error('Invalid filter selected.'); 216 | end 217 | 218 | filt(w>pi*d) = 0; % Crop the frequency response 219 | filt = [filt' ; filt(end-1:-1:2)']; % Symmetry of the filter 220 | 221 | 222 | %%% 223 | %%% Sub-Function: parse_inputs 224 | %%% 225 | 226 | function [p,theta,filter,d,interp,N] = parse_inputs(varargin); 227 | % Parse the input arguments and retun things 228 | % 229 | % Inputs: varargin - Cell array containing all of the actual inputs 230 | % 231 | % Outputs: p - Projection data 232 | % theta - the angles at which the projections were taken 233 | % filter - string specifying filter or the actual filter 234 | % d - a scalar specifying normalized freq. at which to crop 235 | % the frequency response of the filter 236 | % interp - the type of interpolation to use 237 | % N - The size of the reconstructed image 238 | 239 | if nargin<2 240 | error('Invalid input arguments.'); 241 | end 242 | 243 | p = varargin{1}; 244 | theta = pi*varargin{2}/180; 245 | 246 | % Default values 247 | N = 0; % Size of the reconstructed image 248 | d = 1; % Defaults to no cropping of filters frequency response 249 | filter = 'ram-lak'; % The ramp filter is the default 250 | interp = 'linear'; % default interpolation is linear 251 | string_args = {'nearest neighbor', 'linear', 'spline', ... 252 | 'ram-lak','shepp-logan','cosine','hamming', 'hann', 'none'}; 253 | 254 | for i=3:nargin 255 | arg = varargin{i}; 256 | if ischar(arg) 257 | idx = strmatch(lower(arg),string_args); 258 | if isempty(idx) 259 | error(['Unknown input string: ' arg '.']); 260 | elseif prod(size(idx)) > 1 261 | error(['Ambiguous input string: ' arg '.']); 262 | elseif prod(size(idx)) == 1 263 | if idx <= 3 % It is the interpolatio 264 | interp = string_args{idx}; 265 | elseif (idx > 3) & (idx <= 9) 266 | filter = string_args{idx}; 267 | end 268 | end 269 | elseif prod(size(arg))==1 270 | if arg <=1 271 | d = arg; 272 | else 273 | N = arg; 274 | end 275 | else 276 | error('Invalid input parameters'); 277 | end 278 | end 279 | 280 | % If the user didn't specify the size of the reconstruction, so 281 | % deduce it from the length of projections 282 | if N==0 283 | N = 2*floor( size(p,1)/(2*sqrt(2)) ); % This doesn't always jive with RADON 284 | end 285 | 286 | % for empty theta, choose an intelligent default delta-theta 287 | if isempty(theta) 288 | theta = pi / size(p,2); 289 | end 290 | 291 | % If the user passed in delta-theta, build the vector of theta values 292 | if prod(size(theta))==1 293 | theta = (0:(size(p,2)-1))* theta; 294 | end 295 | 296 | if length(theta) ~= size(p,2) 297 | error('THETA does not match the number of projections.'); 298 | end 299 | fprintf('\nBackprojection Parameters:'); 300 | fprintf('\n Filter = %s',filter); 301 | fprintf('\n Interpolation method = %s',interp); 302 | fprintf('\n # of Projections : %d with %d samples each\n\n',size(p,2),size(p,1)); 303 | --------------------------------------------------------------------------------