mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-18 01:49:09 +09:00
318 lines
11 KiB
Matlab
318 lines
11 KiB
Matlab
% [U,S,V,rec_all, rec_all_0] = SVD_regularization(sinogram, theta, Niter_SVD, Npix, reconstruct_ind,max_projections,par)
|
||
% perform spectral SVD analysis and SART based reconstruction
|
||
% Inputs:
|
||
% sinogram - unwrapped sinogram
|
||
% theta - projection angles
|
||
% Niter_SVD - number of iteration of the SVD optimization
|
||
% Npix - (3x1 int), size of the output volume
|
||
% reconstruct_ind - (int array) list of angles that will be considered for reconstruction
|
||
% max_projections - maximal number of projections selected for each
|
||
% energy step. Set "inf" to ignore this limit. It is useful to
|
||
% equialize the weight for each energy step when the distribution is
|
||
% highly unequal
|
||
% par - tomogrpahy paramter structure
|
||
% Outputs:
|
||
% U,S,V - SVD vectors
|
||
% rec_all - regularized SART reconstructions for each energy
|
||
% rec_all_0 - oriignal FBP reocnstructions before SVD regularization
|
||
|
||
|
||
%*-----------------------------------------------------------------------*
|
||
%| |
|
||
%| Except where otherwise noted, this work is licensed under a |
|
||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||
%| International (CC BY-NC-SA 4.0) license. |
|
||
%| |
|
||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||
%| |
|
||
%| Author: CXS group, PSI |
|
||
%*-----------------------------------------------------------------------*
|
||
% You may use this code with the following provisions:
|
||
%
|
||
% If the code is fully or partially redistributed, or rewritten in another
|
||
% computing language this notice should be included in the redistribution.
|
||
%
|
||
% If this code, or subfunctions or parts of it, is used for research in a
|
||
% publication or if it is fully or partially rewritten for another
|
||
% computing language the authors and institution should be acknowledged
|
||
% in written form in the publication: “Data processing was carried out
|
||
% using the “cSAXS matlab package” developed by the CXS group,
|
||
% Paul Scherrer Institut, Switzerland.”
|
||
% Variations on the latter text can be incorporated upon discussion with
|
||
% the CXS group if needed to more specifically reflect the use of the package
|
||
% for the published work.
|
||
%
|
||
% A publication that focuses on describing features, or parameters, that
|
||
% are already existing in the code should be first discussed with the
|
||
% authors.
|
||
%
|
||
% This code and subroutines are part of a continuous development, they
|
||
% are provided “as they are” without guarantees or liability on part
|
||
% of PSI or the authors. It is the user responsibility to ensure its
|
||
% proper use and the correctness of the results.
|
||
|
||
|
||
function [U,S,V,rec_all, rec_all_0] = SVD_regularization(sinogram, theta, Niter_SVD, Npix, reconstruct_ind,max_projections,par)
|
||
|
||
% internal parameters
|
||
SART_grouping = 25;
|
||
Niter_SART = 10;
|
||
Nmodes = 2;
|
||
Nangles = length(theta);
|
||
|
||
E_all = unique(par.energy(ismember(1:Nangles, reconstruct_ind) ));
|
||
Nenergy = length(E_all);
|
||
tomogram_edensity = cell(Nenergy,1);
|
||
|
||
for ii = 1:Nenergy
|
||
utils.progressbar(ii,Nenergy)
|
||
|
||
% choose projections to process
|
||
rec_ind = find(par.energy == E_all(ii) & ismember(1:Nangles,reconstruct_ind)'); % use only some angles
|
||
% downsample to the requested "max_projections"
|
||
rec_ind = rec_ind(1:max(1,ceil(length(rec_ind)/max_projections)):end);
|
||
|
||
num_proj_all(ii) = length(rec_ind);
|
||
if isempty(rec_ind)
|
||
tomogram_edensity{ii} = zeros(Npix,Npix,size(sinogram,1),'single');
|
||
continue;
|
||
end
|
||
|
||
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
||
[Nlayers,width_sinogram,~] = size(sinogram);
|
||
CoR = [Nlayers,width_sinogram]/2; % there is 0.5px shift between fft_1d/fft_2d vs none
|
||
[cfg, vectors] = astra.ASTRA_initialize([Npix,Npix, Nlayers],[Nlayers,width_sinogram],theta,par.lamino_angle,par.tilt_angle,1,CoR);
|
||
% find optimal split of the dataset for given GPU
|
||
split = astra.ASTRA_find_optimal_split(cfg, length(par.GPU_list), 1);
|
||
|
||
% new FBP code
|
||
tomogram = tomo.FBP_zsplit(sinogram, cfg, vectors, split,'valid_angles',rec_ind,...
|
||
'determine_weights', true, ...
|
||
'GPU', par.GPU_list,'filter',par.filter_type, 'filter_value',par.freq_scale, 'verbose',0);
|
||
|
||
|
||
% Caclulate delta tomogram
|
||
|
||
par.lambda = 1.234e-9 / E_all(ii);
|
||
|
||
par.factor=par.lambda/(2*pi*par.pixel_size);
|
||
par.factor_edensity = 1e-30*2*pi/(par.lambda^2*2.81794e-15);
|
||
|
||
% get full reconstruction (for FBP is sum already final tomogram)
|
||
% calculate complex refractive index
|
||
tomogram_edensity{ii} = gather(tomogram*par.factor*par.factor_edensity);
|
||
|
||
end
|
||
|
||
% quantity = min(max_projections, hist(par.energy, E_all));
|
||
|
||
rec_all_0 = gather(cat(4, tomogram_edensity{:}));
|
||
|
||
% get a weighted average
|
||
mrec = max(0,mean(rec_all_0 .* reshape(num_proj_all,1,1,1,[]) ,4)) / mean(num_proj_all);
|
||
mask = mrec > graythresh(mrec);
|
||
|
||
%%
|
||
mask = imopen(mask, strel('disk',5));
|
||
mask = imdilate(mask, strel('disk',5));
|
||
mask = imfill(mask, 'holes');
|
||
mask = Garray(mask);
|
||
constraint_fnct = @(x)(max(0,x.*mask));
|
||
|
||
for ii = 1:Nenergy
|
||
tomogram_edensity{ii} = constraint_fnct(tomogram_edensity{ii});
|
||
end
|
||
|
||
gpu = gpuDevice;
|
||
|
||
for iter = 1:Niter_SVD
|
||
utils.verbose(1,' ====== Iteration %i ==== ', iter)
|
||
rec_all = cat(4, tomogram_edensity{:});
|
||
|
||
utils.verbose(2,'Available GPU memory = %3.1fGB', gpu.AvailableMemory/1e9)
|
||
|
||
|
||
plotting.smart_figure(4)
|
||
plotting.imagesc3D(cat(2, squeeze(rec_all(:,:,ceil(end/2),:)), squeeze(rec_all_0(:,:,ceil(end/2),:))));
|
||
axis off image, colormap bone
|
||
caxis(gather(math.sp_quantile(rec_all, [0.001, 0.9999], 10)));
|
||
title('Left - SVD refined , Right - original FBP')
|
||
|
||
|
||
|
||
rec_all = reshape(rec_all, [], Nenergy);
|
||
|
||
% add more modes progressivelly for higher iteration number
|
||
Nmodes_tmp = min(floor(iter^(1/3)), Nmodes);
|
||
|
||
|
||
%%%%%%%%%%%%%%%%%%%% APPLY SVD CONSTRAINT %%%%%
|
||
[U,S,V] = math.fsvd(rec_all, Nmodes_tmp);
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
||
%% enforce smoothness of the first V vector
|
||
|
||
|
||
if iter > 1
|
||
% plot convergence progress
|
||
try
|
||
err_total(iter,:) = gather(sqrt(sum((U*S*V'-rec_all).^2)));
|
||
catch
|
||
keyboard
|
||
end
|
||
plotting.smart_figure(3)
|
||
loglog(mean(err_total'))
|
||
axis tight
|
||
grid on
|
||
xlabel('Iteration')
|
||
ylabel('Residuum between SVD model and reconstruction')
|
||
drawnow
|
||
end
|
||
|
||
|
||
|
||
|
||
|
||
% relax = 0.1;
|
||
% V(:,1) = V(:,1) *(1-relax) + relax * polyval(polyfit(1:Nenergy,V(:,1)',1), 1:Nenergy)';
|
||
%
|
||
% if Nmodes_tmp == 3
|
||
% keyboard
|
||
%
|
||
% end
|
||
|
||
|
||
|
||
if iter > 1
|
||
% plot SVD evolution
|
||
|
||
best_angle = fminsearch(@(x)get_rotation_score(x,U,S,V), zeros(3,1));
|
||
R = rotation_matrix_3D(best_angle(1),best_angle(2),best_angle(3));
|
||
R = R(1:Nmodes_tmp,1:Nmodes_tmp); % use only number of modes that is needed
|
||
Vplot = (R*V')'; % apply rotation
|
||
Uplot = U*S*inv(R); % apply inverse rotation
|
||
|
||
% flip sign to for convinince
|
||
sign_U = sign(mean(Uplot));
|
||
Uplot = Uplot .* sign_U;
|
||
Vplot = Vplot .* sign_U;
|
||
rec_all = gather(rec_all);
|
||
|
||
U_plot = Uplot ./ quantile(Uplot(1:32:end,:), 0.99);
|
||
|
||
|
||
U_plot = reshape(U_plot, [size(tomogram_edensity{1}),Nmodes_tmp]);
|
||
U_plot = reshape(permute(U_plot,[1,2,4,3]), [size(U_plot,1),size(U_plot,2)*size(U_plot,4),size(U_plot,3)]);
|
||
|
||
plotting.smart_figure(5)
|
||
subplot(2,1,1)
|
||
|
||
plotting.imagesc3D(U_plot, 'init_frame', size(U_plot,3)/2)
|
||
axis image off
|
||
colormap bone
|
||
caxis(gather(math.sp_quantile(U_plot, [0.001, 0.995], 10)));
|
||
title('Topos SVD vectors')
|
||
subplot(2,1,2)
|
||
plot(E_all,Vplot, '-o')
|
||
hold all
|
||
offset = min(min(Vplot ));
|
||
range = max(max(Vplot )) - offset;
|
||
bar(E_all,num_proj_all / max(num_proj_all)*range*0.2+ offset, 'facecolor', 'none', 'Basevalue', offset)
|
||
hold off
|
||
title('Chronos SVD vectors')
|
||
axis tight
|
||
grid on
|
||
xlabel('Energy [keV]')
|
||
ylabel('S*V''')
|
||
Energy = diag(S);
|
||
Energy = Energy / sum(Energy);
|
||
for kk = 1:Nmodes_tmp
|
||
legend_txt{kk} = sprintf('E=%3.2g%%', Energy(kk)*100);
|
||
end
|
||
legend(legend_txt ,'location','best')
|
||
clear U_plot
|
||
end
|
||
|
||
|
||
% ather from GPU to save memory
|
||
% Uout = gather(Uout);
|
||
|
||
|
||
% apply SART refinement
|
||
for ii = 1:Nenergy
|
||
utils.progressbar(ii,Nenergy)
|
||
|
||
|
||
% choose projections to process
|
||
rec_ind = find(par.energy == E_all(ii) & ismember(1:Nangles,reconstruct_ind)'); % use only some angles
|
||
% downsample to the requested "max_projections"
|
||
rec_ind = rec_ind(1:ceil(length(rec_ind)/max_projections):end);
|
||
|
||
if isempty(rec_ind); continue; end
|
||
|
||
|
||
[cache_SART,cfg_SART] = tomo.SART_prepare(cfg, vectors(rec_ind,:), SART_grouping, 'keep_on_GPU', true);
|
||
|
||
|
||
rec = U*S*V(ii,:)';
|
||
rec = reshape(rec,size(tomogram_edensity{1}));
|
||
|
||
par.lambda = 1.234e-9 / E_all(ii);
|
||
par.factor=par.lambda/(2*pi*par.pixel_size);
|
||
par.factor_edensity = 1e-30*2*pi/(par.lambda^2*2.81794e-15);
|
||
|
||
% get full reconstruction (for FBP is sum already final tomogram)
|
||
% calculate complex refractive index
|
||
rec = rec / (par.factor*par.factor_edensity);
|
||
|
||
rec = Garray(rec);
|
||
sino = Garray(sinogram(:,:,rec_ind));
|
||
clear err
|
||
|
||
for jj = 1:Niter_SART
|
||
[rec,err(jj,:)] = tomo.SART(rec, sino, cfg_SART, ...
|
||
vectors(rec_ind,:),cache_SART, 'relax', 0, 'constraint', constraint_fnct);
|
||
end
|
||
% apply some weak total variation to help agsints undersampling
|
||
% artefacts
|
||
rec = regularization.local_TV3D_chambolle(rec, 1e-6, 10);
|
||
|
||
tomogram_edensity{ii} = (rec*par.factor*par.factor_edensity);
|
||
|
||
% plotting.smart_figure(1)
|
||
% subplot(1,3,1)
|
||
% plotting.imagesc3D(tomogram_edensity{ii}); axis off image, colormap bone
|
||
% caxis([0,1])
|
||
% subplot(1,3,2)
|
||
% plot(theta(rec_ind), 'o-')
|
||
% title(['Nangles ', num2str(length(rec_ind)) ])
|
||
% subplot(1,3,3)
|
||
% loglog(mean(err'))
|
||
% drawnow
|
||
end
|
||
clear rec sino cache_SART
|
||
end
|
||
|
||
rec_all = reshape(rec_all, [size(tomogram_edensity{1}), Nenergy]);
|
||
|
||
|
||
|
||
|
||
% get recosntructions to RAM
|
||
U = gather(U);
|
||
S = gather(S);
|
||
V = gather(V);
|
||
rec_all = gather(rec_all);
|
||
|
||
end
|
||
|
||
function score = get_rotation_score(x,U,S,V)
|
||
R = rotation_matrix_3D(x(1),x(2),x(3));
|
||
Nmodes = size(S,1);
|
||
V = (R(1:Nmodes,1:Nmodes)*V')';
|
||
score = gather(norm(V(:,1) - mean(V(:,1))));
|
||
end
|