mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-17 23:59:11 +09:00
278 lines
9.5 KiB
Matlab
278 lines
9.5 KiB
Matlab
% [U,S,V,rec_all] = SART_SVD(sinogram, theta, Npix, blocks, par)
|
||
% perform temporal SVD analysis and SART based reconstruction to
|
||
% estimate changes of the sample during reconstruction
|
||
% Inputs:
|
||
% **sinogram unwrapped sinogram
|
||
% **theta tomography angles
|
||
% **Npix size of the reconstructed volume
|
||
% **blocks cell list containing indices for each subtomogram
|
||
% Outputs:
|
||
% ++U,S,V singular vectors
|
||
% ++rec_all SVD filterd reconstruction for each subtomogram
|
||
% Example of use:
|
||
% subtomo_ind = [1, find(abs(diff(theta))> 170), length(theta)];
|
||
% for ii = 1:length(subtomo_ind)-1
|
||
% ind{ii} = subtomo_ind(ii):subtomo_ind(ii+1);
|
||
% end
|
||
% [U,S,V] = nonrigid.SART_SVD(sinogram, theta, Npix, ind);
|
||
|
||
|
||
%*-----------------------------------------------------------------------*
|
||
%| |
|
||
%| 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) 2018 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] = SART_SVD(sinogram, theta, Npix, blocks, varargin)
|
||
|
||
|
||
p = inputParser;
|
||
p.addOptional('split', 1)
|
||
p.addParameter('valid_angles', [])
|
||
p.addParameter('SART_grouping', 25 ) % size of blocks in SART, ART=1, SIRT=Nangles
|
||
p.addParameter('GPU', []) % list of GPUs to be used in reconstruction
|
||
p.addParameter('verbose', 1) % verbose = 0 : quiet, verbose : standard info , verbose = 2: debug
|
||
p.addParameter('N_SVD_modes', 2) % number of recovered SVD modes, 2 is usually enough
|
||
p.addParameter('Niter_SVD', 3) % number of iter of the SVD SART
|
||
p.addParameter('Niter_SART', 5) % number of internal iterations in each SART loops
|
||
p.addParameter('output_folder', '') % path where the results should be stored
|
||
p.addParameter('mask', []) % mask applied on the reconstruction
|
||
|
||
|
||
|
||
p.parse(varargin{:})
|
||
res = p.Results;
|
||
|
||
|
||
utils.verbose(1,'Using FBP for initial guess')
|
||
|
||
Nblocks = length(blocks);
|
||
|
||
tomogram = cell(Nblocks,1);
|
||
for ii = 1:Nblocks
|
||
utils.progressbar(ii,Nblocks)
|
||
|
||
% choose projections to process
|
||
rec_ind = setdiff(blocks{ii}, res.valid_angles);
|
||
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
||
[Nlayers,width_sinogram,~] = size(sinogram);
|
||
[cfg, vectors] = astra.ASTRA_initialize([Npix,Npix, Nlayers],[Nlayers,width_sinogram],theta);
|
||
% find optimal split of the dataset for given GPU
|
||
split = astra.ASTRA_find_optimal_split(cfg, length(res.GPU), 1);
|
||
|
||
% new FBP code
|
||
subtomogram = tomo.FBP_zsplit(sinogram, cfg, vectors, split,'valid_angles',rec_ind,...
|
||
'determine_weights', true, ...
|
||
'GPU', res.GPU ,'filter','ram-lak', 'filter_value',1, 'verbose',-1);
|
||
|
||
num_proj_all(ii) = length(rec_ind);
|
||
|
||
% get full reconstruction (for FBP is sum already final tomogram)
|
||
% calculate complex refractive index
|
||
tomogram{ii} = gather(subtomogram);
|
||
|
||
end
|
||
|
||
|
||
if isempty(res.mask)
|
||
constraint_fnct= @(x)x;
|
||
else
|
||
constraint_fnct = @(x)(abs(x).*res.mask);
|
||
end
|
||
|
||
|
||
for ii = 1:Nblocks
|
||
tomogram{ii} = constraint_fnct(tomogram{ii});
|
||
end
|
||
|
||
gpu = gpuDevice;
|
||
|
||
for iter = 1:res.Niter_SVD
|
||
utils.verbose(1,' ====== Iteration %i/%i ==== ', iter,res.Niter_SVD)
|
||
rec_all = cat(4, tomogram{:});
|
||
|
||
utils.verbose(2,'Available GPU memory = %3.1fGB', gpu.AvailableMemory/1e9)
|
||
|
||
rec_all = reshape(rec_all, [], Nblocks);
|
||
|
||
%% %%%%%%%%%%%%%%%%%% APPLY SVD CONSTRAINT %%%%%
|
||
utils.verbose(0,'Calculating SVD ... ')
|
||
Nmodes = min(iter, res.N_SVD_modes);
|
||
[U,S,V] = math.fsvd(rec_all, Nmodes);
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
||
if Nmodes == res.N_SVD_modes
|
||
err_total(iter,:) = gather(sqrt(sum((U*S*V'-rec_all).^2)));
|
||
|
||
%% plot convergence progress
|
||
plotting.smart_figure(3)
|
||
loglog(mean(err_total'))
|
||
axis tight
|
||
grid on
|
||
title('SVD SART - Convergence evolution')
|
||
xlabel('Iteration')
|
||
ylabel('Residuum between SVD model and reconstruction')
|
||
drawnow
|
||
|
||
end
|
||
|
||
|
||
utils.verbose(0,'Calculating SART ... ')
|
||
|
||
% apply SART refinement
|
||
for ii = 1:Nblocks
|
||
utils.progressbar(ii,Nblocks)
|
||
|
||
|
||
% choose projections to process
|
||
rec_ind = setdiff(blocks{ii}, res.valid_angles);
|
||
|
||
if isempty(rec_ind); continue; end
|
||
|
||
[cache_SART,cfg_SART] = tomo.SART_prepare(cfg, vectors(rec_ind,:), res.SART_grouping, 'keep_on_GPU', true, 'verbose', 0);
|
||
|
||
rec = U*S*V(ii,:)';
|
||
rec = reshape(rec,size(tomogram{1}));
|
||
|
||
% get full reconstruction (for FBP is sum already final tomogram)
|
||
% calculate complex refractive index
|
||
|
||
rec = utils.Garray(rec);
|
||
sino = utils.Garray(sinogram(:,:,rec_ind));
|
||
clear err
|
||
|
||
for jj = 1:res.Niter_SART
|
||
[rec,err(jj,:)] = tomo.SART(rec, sino, cfg_SART, ...
|
||
vectors(rec_ind,:),cache_SART, 'relax', 0, 'constraint', constraint_fnct, 'verbose', 0);
|
||
end
|
||
% apply some weak total variation to help againts undersampling
|
||
% artefacts
|
||
%rec = regularization.local_TV3D_chambolle(rec, 1e-6, 10);
|
||
|
||
tomogram{ii} = gather(rec);
|
||
|
||
end
|
||
clear rec sino cache_SART
|
||
|
||
|
||
|
||
end
|
||
|
||
|
||
%% plot SVD evolution
|
||
|
||
rec_all = reshape(U*S*V', Npix, Npix,size(sinogram,1), Nblocks);
|
||
rec_all = reshape(rec_all, [size(tomogram{1}), Nblocks]);
|
||
|
||
V_sign = sign(mean(V));
|
||
|
||
U(:,1) = U(:,1).*V_sign(1);
|
||
V(:,1) = V(:,1).*V_sign(1);
|
||
|
||
screensize = get( 0, 'Screensize' );
|
||
|
||
|
||
plotting.smart_figure(11)
|
||
subplot(1,2,1)
|
||
plotting.imagesc3D(squeeze(rec_all(:,:,ceil(end/2),:)))
|
||
axis image off
|
||
colormap bone
|
||
caxis(gather(math.sp_quantile(rec_all, [0.001, 0.995], 10)));
|
||
plotting.suptitle('Tomogram evolution in each subtomogram (central slice)')
|
||
subplot(1,2,2)
|
||
plot(V, '-o')
|
||
title('Principal components evolution')
|
||
axis tight
|
||
grid on
|
||
xlabel('Block')
|
||
ylabel('S*V''')
|
||
Energy = diag(S);
|
||
Energy = Energy / sum(Energy);
|
||
for kk = 1:res.N_SVD_modes
|
||
legend_txt{kk} = sprintf('E=%3.2g%%', Energy(kk)*100);
|
||
end
|
||
legend(legend_txt ,'location','best')
|
||
set(gcf,'Outerposition',[1 screensize(4)-500 800 500]);
|
||
|
||
if ~isempty(res.output_folder) && ~debug()
|
||
try
|
||
savefig(fullfile(res.output_folder, 'SVD_filtered_evolution.fig'))
|
||
catch err
|
||
warning('Saving of SVD_filtered_evolution failed with error: %s', err.message)
|
||
end
|
||
end
|
||
|
||
U = reshape(U, [size(tomogram{1}), res.N_SVD_modes]);
|
||
|
||
U_plot = U(:,:,2:end-1,:); % it seems that first and last layer are not well estimated
|
||
U_plot = U_plot - median(quantile(min(U_plot,[],1),0.01,2),3);
|
||
U_plot = U_plot ./ median(quantile(max(U_plot,[],1),0.99,2),3);
|
||
|
||
%
|
||
U_plot = cat(2, U_plot(:,:,:,1), U_plot(:,:,:,2));
|
||
|
||
|
||
|
||
plotting.smart_figure(10)
|
||
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('Principal components (left is 1th PC , right is 2nd PC)')
|
||
subplot(2,1,2)
|
||
plotting.imagesc3D(U_plot, 'init_frame', size(U_plot,1)/2, 'slider_axis',1)
|
||
axis image off
|
||
colormap bone
|
||
title('Principal components (left is 1th PC , right is 2nd PC)')
|
||
caxis(gather(math.sp_quantile(U_plot, [0.001, 0.995], 10)));
|
||
|
||
%%%suptitle('Principal vectors showing tomogram evolution (slide to see layers of the sample)')
|
||
set(gcf,'Outerposition',[1 screensize(4)-1250 1200 700]);
|
||
if ~isempty(res.output_folder) && ~debug()
|
||
print('-f10','-dpng','-r300',[res.output_folder, '/SVD_modes_scaled.png']);
|
||
end
|
||
|
||
|
||
|
||
%% get reconstructions to RAM
|
||
U = gather(U);
|
||
S = gather(S);
|
||
V = gather(V);
|
||
rec_all = gather(rec_all);
|
||
|
||
U = reshape(U, [Npix, Npix,size(sinogram,1),res.N_SVD_modes]);
|
||
|
||
|
||
end |