Files
fold_slice/tomo/+nonrigid/SART_SVD.m
T
2026-08-07 15:56:42 +09:00

278 lines
9.5 KiB
Matlab
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
% [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