initial commit

This commit is contained in:
2026-08-07 15:56:42 +09:00
commit 91ad25aca9
1012 changed files with 159314 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
% [rec, rec_blocks] = FBP_deform(sinogram, cfg, vectors, varargin)
% FUNCTION filtered back projection
% Inputs:
% sino - sinogram (Nlayers x width x Nangles)
% cfg - config struct from ASTRA_initialize
% vectors - vectors of projection rotation generated by ASTRA_initialize
% varargin - see the code
%*-----------------------------------------------------------------------*
%| |
%| 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 [rec, rec_blocks] = FBP_deform(sinogram, cfg, vectors, block_size, inv_deform_tensors, varargin )
par = inputParser;
par.KeepUnmatched = true;
par.addOptional('valid_angles', [], @isnumeric)
par.addOptional('verbose', 1) % verbose = 0 : quiet, verbose : standard info , verbose = 2: debug
par.parse(varargin{:})
r = par.Results;
Nblocks = length(inv_deform_tensors);
Nangles = cfg.iProjAngles;
if isempty(r.valid_angles)
r.valid_angles = 1:Nangles;
end
rec = 0;
for ll = 1:Nblocks
if r.verbose ; utils.progressbar(ll, Nblocks); end
ids = 1+(ll-1)*block_size:min(Nangles, ll*block_size);
if ~isempty(r.valid_angles)
ids = intersect(ids, r.valid_angles);
end
if isempty(ids)
continue
end
if isempty(inv_deform_tensors{ll})
warning('Empty inv_deform_tensors, skipping %i projections', length(ids))
end
rec_blocks{ll} = tomo.FBP(sinogram, cfg, vectors,'deformation_fields',inv_deform_tensors{ll}, varargin{:}, 'valid_angles', ids, 'verbose', 0);
rec = rec + rec_blocks{ll} * length(ids) / length(r.valid_angles);
if nargout == 1
rec_blocks{ll} = []; % save memory
end
end
end
+278
View File
@@ -0,0 +1,278 @@
% [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
+72
View File
@@ -0,0 +1,72 @@
% SVD_regularize_3D_fields - use SVD to constraint the reconstructed DVF
% and enforce smoothness in the DVF reconstruction
%
% shift_3D_total = SVD_regularize_3D_fields(shift_3D_total, Nsvd, SVD_smoothing)
%
% Inputs:
% **shift_3D_total - (cell of 3D arrays) recovered deformation field
% **Nsvd - (int), maximal number of SVD modes to be recovered (should be less than number of subtomos)
% **SVD_smoothing - (scalar), smoothing constant between 0 to 0.25
% returns:
% ++shift_3D_total - (cell of 3D arrays) regularized deformation field
%*-----------------------------------------------------------------------*
%| |
%| 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 shift_3D_total = SVD_regularize_3D_fields(shift_3D_total, Nsvd, SVD_smoothing)
% SVD regularization
Nblocks= length(shift_3D_total);
Nps = size(shift_3D_total{1}{1});
for kk = 1:3
for ll = 1:Nblocks
shift_3D_mat(:,:,:,kk,ll) = shift_3D_total{ll}{kk};
end
end
shift_3D_mat = reshape(shift_3D_mat,[],Nblocks);
[U,S,V] = fsvd(shift_3D_mat, min(Nsvd, Nblocks));
%% apply a bit of smoothness
kernel = [SVD_smoothing, 1-2*SVD_smoothing, SVD_smoothing]';
V = conv2(V, kernel, 'same') ./ conv2(ones(Nblocks,min(Nsvd, Nblocks)), kernel, 'same') ;
shift_3D_mat = U*S*V';
shift_3D_mat = reshape(shift_3D_mat, [Nps,3,Nblocks]);
for kk = 1:3
for ll = 1:Nblocks
shift_3D_total{ll}{kk} = shift_3D_mat(:,:,:,kk,ll);
end
end
end
+152
View File
@@ -0,0 +1,152 @@
% find_shift_3D_nonrigid - GPU accelerated weighted optical flow method
%
% [shift,err] = find_shift_3D_nonrigid(vol_def, vol_ref, weight, downsample, smooth, regul)
%
% Inputs:
% **vol_def deformed volume
% **vol_ref reference volume
% **weight importance weights for each pixel
% **downsample downscale factor from the volume to DVF size
% **smooth smoothness parameres for the recovered DVF
% **regul regularization preventing empty regions to have too large effect on the DVF estimate
% Outputs:
% ++shift calculated local shift for reference to match deformed volume
% ++err error between reference and the deformed volume
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [shift,err] = find_shift_3D_nonrigid(vol_def, vol_ref, weight, downsample, smooth, regul)
import plotting.*
% calculate error
resid = vol_def-vol_ref;
% apply high pass filtering
resid = resid - utils.imgaussfilt3_fft(resid, 5);
% calculate the error between the volumes
err = weight .* resid.^2;
err = sqrt(mean(err(:)));
% avoid numerical instabilities
weight = weight / mean(abs(resid(:)));
Npix = size(vol_ref);
for i = 1:3
ind_def{i} = gpuArray(linspace(1,Npix(i)/downsample, Npix(i))');
end
[X,Y,Z]= meshgrid(ind_def{:});
for ax = 1:3
% get gradient direction
vol_def_diff = math.get_img_grad_conv( vol_ref,2,ax);
% estimate the optimal step
% GPU kernel merging
[num, denum]= arrayfun(@get_coefs,weight, resid, vol_def_diff);
% bin the volume to make smoothing faster
num = utils.binning_3D(num, downsample);
denum = utils.binning_3D(denum, downsample);
num = padded_3D_smoothing(num, smooth/downsample/2);
denum = padded_3D_smoothing(denum, smooth/downsample/2);
% add some small regularization
denum = bsxfun(@plus, denum , regul*mean2(denum));
shift{ax} = - num ./ denum;
% run simple line search to refined the optimal step, ideal it should be close to 1
shift_full = interp3(shift{ax}, X,Y,Z);
update = shift_full.*vol_def_diff;
Nsteps = 10;
steps = logspace(0,1,Nsteps);
for ii = 1:Nsteps
res = arrayfun(@get_residuum_err, weight, resid,update, steps(ii));
err_tmp(ii) = gather(sum(sum(sum(res))));
if ii > 1 && err_tmp(ii) > err_tmp(ii-1)
break
end
end
%% update the step
shift{ax} = shift{ax} .* steps(math.argmin(err_tmp));
end
end
function [num, denum]= get_coefs(W, resid, grad)
% auxiliary function for fast GPU calculations
agrad = abs(grad);
W = W .* agrad;
% estimate the optimal step
num = W .* real(conj(resid) .* grad);
denum = W .* agrad.^2;
end
function res = get_residuum_err(weight, resid, update, step)
res = weight .* (resid + step.* update).^2;
end
function array = padded_3D_smoothing(array, smooth, split)
% prevent periodic boundary issues for FFT conv smoothing
if nargin < 3
split = 1;
end
Npad = ceil(min(size(array)/2, ceil(smooth/8)*16));
array = padarray(array,[Npad(1),0,0],'symmetric','both');
array = padarray(array,[0,Npad(2),0],'symmetric','both');
array = padarray(array,[0,0,Npad(3)],'symmetric','both');
array = utils.imgaussfilt3_fft(array, smooth, split);
array = array(Npad(1):end-Npad(1)-1, Npad(2):end-Npad(2)-1,Npad(3):end-Npad(3)-1);
end
+115
View File
@@ -0,0 +1,115 @@
% get_deformation_fields - calculate the DVF from the observed deformation
% field arrays bu deconvolution
%
% [deform_tensors_linear, inv_deform_tensors_linear] = ...
% get_deformation_fields(shift_tensors, regularize_lambda, Npix_vol)
%
% Inputs:
% **shift_tensors observed deformation
% **regularize_lambda regularization constant for the deconvolution
% **Npix_vol size of the reconstructed volume
% Outputs:
% ++deform_tensors_linear calculate forward DVF
% ++inv_deform_tensors_linear calculate inverse DVF
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [deform_tensors_linear, inv_deform_tensors_linear] = ...
get_deformation_fields(shift_tensors, regularize_lambda, Npix_vol)
% simple deconvolution of the recovered shift arrays to the object
% defomration arrays for linear deformation model
Nblocks = length(shift_tensors);
if Nblocks == 1
error('Number of blocks (subtomos) has to be > 1')
end
if Nblocks > 1
conv_mat = spdiags(ones(Nblocks,1),0,Nblocks, Nblocks+1) + spdiags(ones(Nblocks,1),1,Nblocks, Nblocks+1);
conv_mat = conv_mat ./ sum(conv_mat,2);
regul_mat = spdiags(2*ones(Nblocks,1), 0, Nblocks, Nblocks+1) - spdiags(ones(Nblocks,1), 1, Nblocks, Nblocks+1)-spdiags(ones(Nblocks,1), -1, Nblocks, Nblocks+1);
regul_mat(1,1:2) = 0;
deform_mat = [];
for block = 1:Nblocks
for ax = 1:3
deform_mat(:,:,:,ax,block) = gather(shift_tensors{block}{ax});
end
end
size_deform_mat = size(deform_mat);
deform_mat = reshape(deform_mat, [], Nblocks);
%% perform Tikhonov based deconvolution
% N = 50;
% lams = logspace(-5,1,N);
% for i = 1:N
deconv_def_mat = ((conv_mat'*conv_mat + regularize_lambda*regul_mat'*regul_mat)\(conv_mat'*deform_mat'))';
% enforce zero for the first deformation
deconv_def_mat = deconv_def_mat - deconv_def_mat(:,1);
% err(i) = math.mean2((conv_mat*deconv_def_mat' - deform_mat').^2);
% end
deconv_def_mat = reshape(deconv_def_mat,[size_deform_mat(1:4), Nblocks+1] );
for block = 1:Nblocks+1
for ax = 1:3
deform_tensors{block}{ax} = single(deconv_def_mat(:,:,:,ax,block));
end
end
else
deform_tensors = shift_tensors;
end
[deform_tensors,inv_deform_tensors] = nonrigid.invert_DVF(deform_tensors, Npix_vol) ;
% join blocks to keep initial and final deform for each block together
% -> linear deformation evolution is assumed in between
if Nblocks > 1
for block = 1:Nblocks
deform_tensors_linear{block} = [deform_tensors{block}; deform_tensors{block+1}];
inv_deform_tensors_linear{block} = [inv_deform_tensors{block}; inv_deform_tensors{block+1}];
end
else % just assume one single deformation
deform_tensors_linear = deform_tensors;
inv_deform_tensors_linear = inv_deform_tensors;
end
end
+93
View File
@@ -0,0 +1,93 @@
% get_mask - estimate support mask for the provided reconstructed volume
%
% [mask, W_rec] = get_mask(rec_0, mask_threshold, mask_dilate, show_mask)
%
% Inputs:
% **rec_0 reconstruction volume
% **mask_threshold relative threshold with respect to the maximum
% **mask_dilate mask dilatation in pixels
% **show_mask true / false if you want to plot the mask
% Outputs:
% ++mask binary mask
% ++W_rec importance weights for the reconstructioin
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [mask, W_rec] = get_mask(rec_0, mask_threshold, mask_dilate, show_mask)
if nargin < 4
show_mask = false;
end
%% get mask
Nlayers = size(rec_0,3);
Npix = size(rec_0,1);
mask_dilate = ceil(mask_dilate);
mask = rec_0 > mask_threshold * quantile(rec_0(:), 0.99);
mask = convn(single(mask), ones(mask_dilate,mask_dilate,mask_dilate, 'single'), 'same') > 1e-3*mask_dilate^3;
%% get importance weighting for difference regions
% importance weighting
W_rec = gpuArray(single(tukeywin(Npix, 0.2) .* tukeywin(Npix, 0.2)' .* reshape(tukeywin(Nlayers, 0.2)',1,1,[]) )); % avoid edge issues
W_rec = W_rec .* mask;
W_rec = utils.imgaussfilt3_fft(W_rec,mask_dilate/2);
W_rec = gather(W_rec);
if show_mask
plotting.smart_figure(212)
plotting.imagesc_tomo(mask)
suptitle('Estimated mask')
drawnow
%% show mask
plotting.smart_figure(46)
subplot(1,2,1)
plotting.imagesc3D(rec_0.* ~mask, 'init_frame', Nlayers/2)
colorbar
axis off image
colormap bone
title('Example of residuum after applied mask')
subplot(1,2,2)
hist(rec_0(1:100:end), 100)
axis tight
drawnow
end
end
+72
View File
@@ -0,0 +1,72 @@
% INVERT_DVF simple iterative method for estimation of the inverse deformation
% field
% Chen, Mingli, et al. "A simple fixedpoint approach to invert a deformation field a." Medical physics 35.1 (2008): 81-88.
%
% [deform_tensors,inv_deform_tensors] = invert_DVF(deform_tensors, Npix_vol)
%
% **deform_tensors cell of 3D arrays containing forward deformation DVF
% **Npix_vol pixel size of the recosntructed volume
% Outputs:
% ++deform_tensors_linear calculate forward DVF
% ++inv_deform_tensors_linear calculate inverse DVF
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [deform_tensors,inv_deform_tensors] = invert_DVF(deform_tensors, Npix_vol)
Niter = 10;
%% find invert transformation
N = length(deform_tensors);
for block = 1:N
% init guess
for ax = 1:3
inv_deform_tensors{block}{ax} = -deform_tensors{block}{ax};
end
for i = 1:Niter
for ax = 1:3
scale = size(deform_tensors{block}{ax}, ax) / Npix_vol(ax) ; % calculate the deformation in deform_tensors grid
inv_deform_tensors{block}{ax} = inv_deform_tensors{block}{ax}*0.5 + 0.5*utils.interp3_gpu(-deform_tensors{block}{ax}, ...
scale*inv_deform_tensors{block}{1},scale*inv_deform_tensors{block}{2},scale*inv_deform_tensors{block}{3});
end
end
% in my code I assume that deform_tensors and inv_deform_tensors
% have the same direction (in the astra the direction is swapped)
for ax = 1:3
inv_deform_tensors{block}{ax} = -gather(inv_deform_tensors{block}{ax});
end
end
end
+80
View File
@@ -0,0 +1,80 @@
% nonrigid_registration - recovered DVF for given full reconstruction and
% subreconstructions using optical flow method
%
%[shift_3D_total, vol_err, img_deform] = nonrigid_registration(volume_deform, volume_reference, weight, par, smooth, Niter, shift_3D_total)
%
% Inputs:
% **volume_deform low quality deformed volume to be matched with reference
% **volume_reference reference volume used for alignment
% **weight importance weights for the 3D volumes
% **par tomography parameter structure
% **smooth constant to smooth the recovered DVF
% **Niter number of iterations for the optical flow method
% **shift_3D_total initial deformation field (zeros)
% Outputs:
% ++shift_3D_all recovered deformation for given full reconstruction and subreconstructions
% ++vol_err error between volume and subvolumes
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [shift_3D_total, vol_err] = nonrigid_registration(volume_deform, volume_reference, weight, par, smooth, Niter, shift_3D_total)
% estimate deformation fields to match two 3D volumes
Npix = size(volume_deform);
if ~exist('shift_3D_total', 'var')
for ii = 1:3
shift_3D_total{ii}= gpuArray.zeros( ceil(Npix/par.downsample_DVF) , 'single');
end
end
img_deform = utils.interp3_gpu(volume_deform, shift_3D_total{:});
for iter = 1:Niter
% core : estimate of the deformation field
[shift_3D,vol_err(iter)] = nonrigid.find_shift_3D_nonrigid(img_deform,volume_reference, weight, par.downsample_DVF, smooth, par.regular);
for ii = 1:3
shift_3D_total{ii} = shift_3D_total{ii}+ par.relax_pos_corr*shift_3D{ii};
end
% apply inverse deformations on the deformated object
img_deform = utils.interp3_gpu(volume_deform, -shift_3D_total{1}, -shift_3D_total{2}, -shift_3D_total{3} );
if iter > 1 && vol_err(end) > vol_err(end-1)
break
end
end
end
@@ -0,0 +1,250 @@
% prepare_artificial_deform_data - prepare deformed phantom for algorithm tests
%
%[dphase, shift_3D_orig, angles, par, rec_ideal, volData_orig] = ...
% prepare_artificial_deform_data(Nangles, Npix, Nlayers, Nblocks, smooth, binning,DVF_amplitude,DVF_period, par_0)
%
% Inputs:
% **Nangles number of angles in the simulated dataset
% **Npix int - pixel size of the phantom
% **Nlayers number of layers in the phatom
% **Nblocks number of subtomograms
% **smooth constant used to estimate ratio between phantom pixel size and DVF pixels size
% **binning simulate binning of the produced sinograms
% **par_0 initial parameters structure that will be merged with the loaded paramters
% Outputs:
% ++dphase phase difference for the complex project
% ++shift_3D_all_0 original deformation = {}
% ++angles angles for each of the projection
% ++par merged parameter structure
% ++rec_ideal ideal construction with known DVF
% ++volData_orig original phantom
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [dphase, shift_3D_orig, angles, par, rec_ideal, volData_orig] = ...
prepare_artificial_deform_data(Nangles, Npix, Nlayers, Nblocks, smooth, binning,DVF_amplitude,DVF_period, par_0)
%% prepare deformated data for provided parameters
import utils.*
import math.*
import plotting.*
%% create data and geometry
angles = pi+[linspace(0, 180, Nangles)];
% create 8 subtomos
angles = reshape(angles, Nblocks,[])';
angles = angles(:);
lamino_angle = 90;
Nw = ceil(Npix*sqrt(2)/16)*16;
try
disp('Loading stored model')
load(['porous_glass_data',num2str(Npix),'.mat']);
disp('Loading done')
catch
try
%% porous_glass phantom
disp('Creating phantom')
rng default
volData_orig = randn(2*[Npix, Npix, Nlayers], 'single');
volData_orig = imgaussfilt3_fft(volData_orig, 6);
porous_glass = imgaussfilt3_fft(volData_orig > 0, 2)>0.01 & volData_orig <= 0;
volData_orig = randn(2*[Npix, Npix, Nlayers], 'single');
volData_orig = imgaussfilt3_fft(volData_orig, 6);
porous_glass = porous_glass | imgaussfilt3_fft(volData_orig > 0, 2)>0.01 & volData_orig <= 0;
[Xq,Yq,Zq] = meshgrid(linspace(-0.5,0.5,2*Npix), linspace(-0.5,0.5,2*Npix), linspace(-0.5,0.5,2*Nlayers));
porous_glass = interp3(single(porous_glass),Xq*Npix*3.5+Npix,Yq*Npix*3.5+Npix,Zq*Nlayers*3.5+Nlayers);
porous_glass = porous_glass(end/4:end*3/4-1,end/4:end*3/4-1,end/4:end*3/4-1);
porous_glass(isnan(porous_glass)) = 0;
% apply circular mask
xgrid = -Npix/2+1 : Npix/2;
[X,Y] = meshgrid(xgrid, xgrid);
porous_glass = porous_glass .* imgaussfilt(single(X.^2+Y.^2 < (Npix/2.2)^2), 3);
porous_glass = porous_glass .* reshape(tukeywin(Nlayers, 0.5), 1,1,[]);
porous_glass = uint8(porous_glass/max(porous_glass(:)) * 255);
savefast_safe(['porous_glass_data',num2str(Npix),'.mat'], 'porous_glass', true);
catch
keyboard
end
end
Bsize = ceil(Nangles/Nblocks);
% load porous_glass_data
volData_orig = single(porous_glass);
volData_orig = volData_orig(:,:,1:Nlayers);
% apply circular mask
xgrid = -Npix/2+1 : Npix/2;
[X,Y] = meshgrid(xgrid, xgrid);
volData_orig = volData_orig .* imgaussfilt2_fft(single(X.^2+Y.^2 < (Npix/2.4)^2), 5);
volData_orig = volData_orig .* reshape(tukeywin(Nlayers, 0.1), 1,1,[]);
Nlayers = size(volData_orig,3);
%% initialize deformation vector fields reconstructions
Nps = ceil([Npix, Npix, Nlayers]/par_0.downsample_DVF);
%% generate deformation field
volData_orig = gather(volData_orig);
%% generate "measured" data
disp('Generating data')
split = 1;
rng default
for ax= 1:3
for j = 1:2
shift_3D{j}{ax} = imgaussfilt3_fft(randn(Nps), DVF_period);
shift_3D{j}{ax} = shift_3D{j}{ax} / max(abs(shift_3D{j}{ax}(:)))*DVF_amplitude;
end
end
for ll = 1:Nblocks+1
% ratio(1) = 1-exp(-3*((ll-1)/(Nblocks+1)));
% ratio(2) = 1-exp(-3*((ll-1)/(Nblocks+1)));
% ratio(3) = 1-exp(-3*((ll-1)/(Nblocks+1)));
ratio(1) = sin(2*pi*(ll-1)/(Nblocks+1));
ratio(2) = sin(2*pi*(ll-1)/(Nblocks+1));
ratio(3) = sin(2*pi*(ll-1)/(Nblocks+1));
for ax= 1:3
shift_3D_orig{ll}{ax} = (ratio(ax)*shift_3D{1}{ax});
end
end
[cfg, vectors] = ...
astra.ASTRA_initialize([Npix, Npix,Nlayers],[Nlayers,Nw],angles,lamino_angle, 0, 1);
% resample the created DVF to reconstruction size of the DVF
for ll = 1:Nblocks+1
for kk = 1:3
Np = size(shift_3D_orig{ll}{kk});
[X,Y,Z] = meshgrid(linspace(1,Np(1),Nps(1)), linspace(1,Np(2),Nps(2)), linspace(1,Np(3),Nps(3)));
shift_3D_orig{ll}{kk} = interp3(shift_3D_orig{ll}{kk},X,Y,Z);
end
end
[deform_tensors,inv_deform_tensors] = nonrigid.invert_DVF(shift_3D_orig, [Npix, Npix,Nlayers]);
% join blocks to keep initial and final deform for each block together
for block = 1:Nblocks
deform_tensors_linear{block} = [deform_tensors{block}; deform_tensors{block+1}];
inv_deform_tensors_linear{block} = [inv_deform_tensors{block}; inv_deform_tensors{block+1}];
end
sinogram = tomo.Ax_sup_partial(volData_orig,cfg, vectors,split);
rec_ideal = tomo.FBP(sinogram , cfg, vectors,split, 'verbose',0);
% generate data
for ll = 1:Nblocks
ids = 1+(ll-1)*Bsize:min(Nangles, ll*Bsize);
cfg.iProjAngles = length(ids);
sinogram(:,:,ids) = tomo.Ax_sup_partial(volData_orig,cfg, vectors(ids,:),split, ...
'deformation_fields', deform_tensors_linear{ll});
end
%create realistic issues
sinogram = binning_2D(sinogram,binning);
% change the change to get phase jumps
sinogram = sinogram / max(sinogram(:)) * 2*pi;
dphase = math.get_phase_gradient_1D(-sinogram, 2);
[cfg, vectors] = ...
astra.ASTRA_initialize([Npix, Npix,Nlayers]/binning,[Nlayers,Nw]/binning,angles,lamino_angle, 0, 1);
rec_0 = tomo.FBP(sinogram , cfg, vectors,split);
rec_corr = 0;
for ll = 1:Nblocks
ids = 1+(ll-1)*Bsize:min(Nangles, ll*Bsize);
cfg.iProjAngles = length(ids);
rec_corr = rec_corr+tomo.FBP(sinogram(:,:,ids) , cfg, vectors(ids,:),split, 'verbose',0,...
'deformation_fields', inv_deform_tensors_linear{ll} )/Nblocks;
end
% if debug()
figure
subplot(1,2,1)
imagesc3D(max(0,rec_0), 'init_frame', Nlayers/2)
axis off image; colormap bone
title('Standard reconstruction')
subplot(1,2,2)
imagesc3D(max(0,rec_corr), 'init_frame', Nlayers/2)
axis off image; colormap bone
title('Ideally corrected reconstruction')
drawnow
% end
% store inputs to par structure
par.binning = binning;
par.valid_angles = 1:Nangles;
par.air_gap = [20,20];
par.factor = 1;
par.output_folder = '';
for field = fields(par_0)'
par.(field{1}) = par_0.(field{1});
end
end
+134
View File
@@ -0,0 +1,134 @@
% prepare_real_deform_data - load already prealigned and fixed projections
%
%[dphase, shift_3D_all_0, angles, par] = ...
% prepare_real_deform_data(path_to_projections, projection_filename, sample_name, par_0)
%
% Inputs:
% **path_to_projections path where are stored preloaded data
% **projection_filename name of the file where are the preloaded data
% **sample_name name of the sample, it used for cache file reparation
% **par_0 initial paramters structure that will be merged with the loaded paramters
% Outputs:
% ++dphase phase difference for the complex project
% ++shift_3D_all_0 original deformation = {}
% ++angles angles for each of the projection
% ++par merged parameter structure
% ++object complex valued projections
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [dphase, shift_3D_all_0, angles, par, object] = ...
prepare_real_deform_data(path_to_projections, projection_filename, sample_name, par_0)
import utils.*
import math.*
import plotting.*
shift_3D_all_0 = {};
if ~exist('cache', 'dir')
mkdir('cache')
end
cached_file = fullfile(path_to_projections, ['cache_nonrigid_tomo', sample_name,'.mat']);
% try to load cached data
verbose(0, 'Loading prepared data from %s', fullfile(path_to_projections, projection_filename))
%% load complex valued projections
d = load(fullfile(path_to_projections, projection_filename));
verbose(0, 'Loading done')
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% create data and geometry
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
if isfield(d, 'dphase')
dphase = d.dphase;
else
object = complex(d.stack_object_r,d.stack_object_i);
object_ROI = d.object_ROI;
object = object(object_ROI{:},:);
object = smooth_edges(object);
dphase = tomo.block_fun(@math.get_phase_gradient_1D,object, 2);
end
if isfield(d, 'angles')
angles = d.angles;
else
angles = d.theta;
end
dphase = smooth_edges(dphase);
try
par = d.par;
catch
par = struct();
end
% rewrite loaded params by some defaults
for field = fields(par_0)'
par.(field{1}) = par_0.(field{1});
end
par.output_folder = path_to_projections;
if debug()
% plot angular blocks
figure
plot(angles, '.-')
for ii = 1:Nblocks
plotting.vline(ii*Bsize)
end
xlabel('Projection #')
ylabel('Angle [deg]')
title('Angular block splitting')
drawnow
end
end
+57
View File
@@ -0,0 +1,57 @@
% REGULARIZE_3D_FIELD - remove rigid motion from the reconstructed DVF
%
% shift_3D_total= regularize_3D_field(shift_3D_total )
%
% Inputs:
% **shift_3D_total reconstructed DVF
% Outputs:
% ++shift_3D_total optimized DVF
%
%*-----------------------------------------------------------------------*
%| |
%| 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 shift_3D_total= regularize_3D_field(shift_3D_total )
Nblocks = length(shift_3D_total);
for kk = 1:3
% remove additional degrees of freedome
shift_3D_avg = 0;
for ll = 1:Nblocks
shift_3D_avg = shift_3D_avg+ shift_3D_total{ll}{kk};
end
for ll = 1:Nblocks
shift_3D_total{ll}{kk} = shift_3D_total{ll}{kk} - shift_3D_avg/Nblocks;
end
end
end
+166
View File
@@ -0,0 +1,166 @@
% show_deformation_field - plot reconstructed deformation vector field
%
% show_deformation_field(rec_avg, deform_tensors, apodize_radial, Nsvd, binning, upscale_arrows, slice_axis, down_DVF)
%
% Inputs:
% **rec_avg optimal reconstuction
% **deform_tensors reconstructed DVF
% **apodize_radial apply radial appodization to crop artefacts around
% **Nsvd number of SVD modes to be plotted
% **binning currenlty used binning (used for scaling)
% **upscale_arrows (scalar) scaling constant for the plotted arrows
% **slice_axis axis along which the reconstruction will be sliced and plotted
% **down_DVF (int) donsample DVF to make the arrows more sparse in the plot
%
%*-----------------------------------------------------------------------*
%| |
%| 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 show_deformation_field(rec_avg, deform_tensors, apodize_radial, Nsvd, binning, upscale_arrows, slice_axis, down_DVF)
% show deformation vector field
Nblocks = length(deform_tensors);
for kk = 1:3
for ll = 1:Nblocks+1
if ll <= Nblocks
shift_3D_mat(:,:,:,kk,ll) = deform_tensors{ll}{1,kk};
else
shift_3D_mat(:,:,:,kk,ll) = deform_tensors{ll-1}{min(end,2),kk};
end
end
end
[~,mask_small] = utils.apply_3D_apodization(deform_tensors{1}{1}, apodize_radial);
% apply mask on the results
shift_3D_mat = shift_3D_mat .* mask_small;
% swap dimension to show the right plane
switch slice_axis
case 1
rec_avg = rot90(permute(rec_avg, [2,3,1]),1);
mask_small = rot90(permute(mask_small, [2,3,1]),1);
shift_3D_mat = rot90(permute(shift_3D_mat, [2,3,1,4,5]),1);
case 2
rec_avg = rot90(permute(rec_avg, [1,3,2]),1);
mask_small = rot90(permute(mask_small, [1,3,2]),1);
shift_3D_mat = rot90(permute(shift_3D_mat, [2,3,1,4,5]),1);
case 3
end
[Nx,Ny,Nlayers] = size(rec_avg);
Nps = size(shift_3D_mat);
mesh_2D = {1:down_DVF:Nps(1),1:down_DVF:Nps(2)};
frame_s = ceil(Nps(3)/2);
frame = ceil(Nlayers/2);
% calculate SVD
shift_3D_mat = reshape(shift_3D_mat,[],Nblocks+1);
[U,S,V] = math.fsvd(shift_3D_mat, Nsvd);
U = reshape(U, [Nps(1:3), 3, Nsvd]);
U = U * sign(mean(V(:,1)));
V = V * sign(mean(V(:,1)));
if slice_axis == 3
mean_amp = sqrt(mean(math.mean2(abs(U).^2 .* mask_small) ./ math.mean2(mask_small),3));
else
mean_amp = sqrt(mean(math.mean2(abs(U).^2)));
end
mean_amp = squeeze(mean_amp(1,1,1,:,1));
mean_amp = mean_amp .* S(1) * V(3,1);
mean_amp = mean_amp .* binning;
fprintf('Mean deformation x:%3.2gpx y:%3.2gpx z:%3.2gpx \n',mean_amp )
[~,S_tmp,~] = math.fsvd(shift_3D_mat, min(size(shift_3D_mat,2),Nsvd+10));
fprintf(['Relative power of the modes:', repmat(' %3.3g%%, ',1,size(S_tmp,1)) , ' \n'], diag(S_tmp ./ sum(S_tmp(:)))*100 )
figure(545)
for mode = 1:Nsvd
ax(mode) = subplot(2,Nsvd,mode);
for kk = 1:3
Q{mode,kk} = U(:,:,:,kk,mode) .* S(mode,mode);
Q{mode,kk} = utils.imgaussfilt3_fft(Q{mode,kk}, down_DVF);
end
ygrid = ((1:Nps(1))-0.5)/Nps(1)*Nx;
xgrid = ((1:Nps(2))-0.5)/Nps(2)*Ny;
[x,y] = meshgrid(xgrid, ygrid);
img = rec_avg(:,:,frame);
img = min(1,img / math.sp_quantile(rec_avg(:), 0.95,5));
imagesc(1-img, [-1,1]);
colormap bone
hold all
quiver(x(mesh_2D{:}),y(mesh_2D{:}),Q{mode,2}(mesh_2D{:},frame_s)*upscale_arrows, ...
Q{mode,1}(mesh_2D{:},frame_s)*upscale_arrows,0,'Linewidth',2);
axis off image
hold off
title(sprintf('%i. PCA of DVF field\n %ix upscaled',mode, upscale_arrows))
subplot(2,Nsvd,Nsvd + mode)
plot(V(:,mode))
grid on
axis tight
xlabel('Interpolation node id')
ylabel('Normalized evolution')
end
linkaxes(ax, 'xy')
plotting.suptitle('Singular value decomposition of the DVF evolution')
% end
figure(45545)
subplot(1,2,1)
plotting.imagesc3D(rec_avg, 'init_frame', size(rec_avg,3)/2)
axis off image ; colormap bone
colorbar
title('Reconstruction example')
subplot(1,2,2)
deform = Q{1,1}*binning;
plotting.imagesc3D(deform, 'init_frame', size(deform,3)/2)
caxis(gather(math.sp_quantile(deform, [0.001, 0.999],5)))
axis off image ; colormap bone
title('Vertical deformation vector field')
plotting.suptitle('1th PCA vector, horizontal cut')
drawnow
end
@@ -0,0 +1,142 @@
% show_reconstruction_quality - compare conventional reconstruction,
% nonrigid reconstruction FBP and SART
%
% [rec_FBP, rec_NCT_FBP, rec_NCT_SART] = show_reconstruction_quality(sinogram, cfg, vectors, shift_3D_total, regularize_deform_evol)
%
% Inputs:
% **sinogram current reconstruction
% **vectors ASTRA configuration vectors
% **cfg ASTRA configuration structure
% **shift_3D_total recovered deformation vector field
% **regularize_deform_evol regularization constant for the deformation field evolution calculation
%
% Outputs:
% ++rec_FBP conventional FBP reconstruction
% ++rec_NCT_FBP nonrigid FBP reconstruction
% ++rec_NCT_SART nonrigid SART reconstruction
%
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [rec_FBP, rec_NCT_FBP, rec_NCT_SART] = show_reconstruction_quality(sinogram, cfg, vectors, shift_3D_total, regularize_deform_evol)
Nangles = cfg.iProjAngles;
reset(gpuDevice)
Nblocks = length(shift_3D_total);
split = astra.ASTRA_find_optimal_split(cfg);
Bsize = ceil(Nangles/Nblocks);
if any(split(1:3)) > 1
warning('Sample volume seems too large, try to reduce the reconstructed volume size')
split(1:3) = 1; % at least try to make it work without splitting, otherwise recontruction will be poor
end
rec_FBP = gather(tomo.FBP(sinogram, cfg, vectors)) ;
% generate deformation tensors from the shift tensors
[deform_tensors, inv_deform_tensors] = nonrigid.get_deformation_fields(shift_3D_total, regularize_deform_evol, size(rec_FBP));
%% FBP
rec_NCT_FBP = nonrigid.FBP_deform(sinogram, cfg, vectors,Bsize, inv_deform_tensors);
[SART_cache, cfg_SART] = tomo.SART_prepare(cfg, vectors, Bsize, split);
SART_cache.R = min(1,SART_cache.R);
%% SART - solve it using all constraints
[~,rec_mask] = utils.apply_3D_apodization(rec_NCT_FBP,0);
rec_NCT_SART = rec_NCT_FBP;
Niter_SART = 10;
clear err_sart
disp('====== SART ==========')
for kk = 1:Niter_SART
utils.progressbar(kk, Niter_SART)
[rec_NCT_SART,err_sart(kk,:)] = tomo.SART(rec_NCT_SART, sinogram, cfg_SART, vectors, SART_cache, split, ...
'relax',0, 'deformation_fields', deform_tensors,'inv_deformation_fields', inv_deform_tensors, ...
'constraint', @(x)(max(0,x.*rec_mask)), 'verbose',0);
% figure(1343)
% subplot(1,2,1)
% plot(err_sart)
% hold all
% plot(mean(err_sart'),'k', 'LineWidth',2)
% hold off
% set(gca, 'xscale', 'log')
% set(gca, 'yscale', 'log')
% grid on
% axis tight
% title('SART error evolution')
% subplot(1,2,2)
% plotting.imagesc3D(rec_NCT_SART, 'init_frame', floor(size(rec_NCT_SART,3)/2))
% axis image
% colormap bone
% axis off image
% drawnow
end
% remove edges
rec_FBP = utils.apply_3D_apodization(rec_FBP, 0);
rec_NCT_FBP = utils.apply_3D_apodization(rec_NCT_FBP,0);
figure(10)
if exist('orig_phantom', 'var') && ~isempty(orig_phantom)
orig_phantom = utils.crop_pad(orig_phantom, [cfg.iVolX,cfg.iVolY]);
orig_phantom = orig_phantom ./ mean(orig_phantom(:)) * mean(rec_FBP(:))*0.9;
rec_all = gather(cat(2, orig_phantom,rec_FBP, rec_NCT_FBP, rec_NCT_SART));
else
rec_all = gather(cat(2, rec_FBP, rec_NCT_FBP, rec_NCT_SART));
end
range = quantile(rec_all(:), [1e-2, 1-1e-2]);
plotting.imagesc3D(rec_all, 'init_frame', size(rec_all,3)/2)
caxis(range);
axis off image; colormap bone
title('Original reconstruction / Deform FBP / Deform SART')
end
+134
View File
@@ -0,0 +1,134 @@
% show_rigid_corrections - plot recovered shifts for each of the projection
%
% show_rigid_corrections(rec, sinogram_shifted, err,shift_all, angles, iter, par)
%
% Inputs:
% **rec current reconstruction
% **sinogram_shifted sinogram with already applied position shifts
% **err projection space (sinogram - model) error
% **shift_all reconstructed projections
% **angles angles of the projections
% **iter current iteration
% **par parameter structure of the nonrigid tomo
%*-----------------------------------------------------------------------*
%| |
%| 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 show_rigid_corrections(rec, sinogram_shifted, err,shift_all, angles, iter, par)
import utils.*
import math.*
[Nlayers,~,~] = size(sinogram_shifted);
[angles, ind_sort] = sort(angles);
sinogram_shifted = sinogram_shifted(:,:,ind_sort);
err = err(:,ind_sort);
shift_all = shift_all(:,ind_sort,:);
verbose(1,'Plotting')
figure(5464)
clf()
subplot(2,3,1)
imagesc(squeeze(sinogram_shifted(ceil(Nlayers/2),:,:))');
axis off
colormap bone
title('Corrected sinogram')
subplot(2,3,2)
if iter > 1
hold on
plot(angles, (shift_all(iter,:,1)-shift_all(iter-1,:,1))*par.binning, 'r')
plot(angles, (shift_all(iter,:,2)-shift_all(iter-1,:,2))*par.binning, 'b')
hold off
legend({'horiz', 'vert'})
end
title('Current position update')
xlim([min(angles), max(angles)])
ylabel('Shift [px]')
xlabel('Angle [deg]')
subplot(2,3,3)
hold on
plot(angles,shift_all(iter,:,1)*par.binning, 'r')
plot(angles,shift_all(iter,:,2)*par.binning, 'b')
hold off
title('Total position update')
legend({'horiz', 'vert'})
ylabel('Shift [px]')
xlim([min(angles), max(angles)])
xlabel('Angle [deg]')
subplot(2,3,4)
Nlayers = size(rec,3);
plotting.imagesc3D(rec, 'init_frame', ceil(Nlayers/2))
caxis(gather(math.sp_quantile(rec(:,:,ceil(Nlayers/2)), [0.01,0.99], 1)));
axis off image
title('Current reconstruction')
colormap bone
subplot(2,3,5)
hold on
plot(err)
plot(mean(err,2), 'k', 'LineWidth', 3);
hold off
grid on
axis tight
xlim([1,iter+1])
set(gca, 'xscale', 'log')
set(gca, 'yscale', 'log')
title('MSE evolution')
xlabel('Iteration')
ylabel('Mean square error')
subplot(2,3,6)
hold on
plot(angles, err(end,:), 'k.')
hold off
if any(~par.valid_angles)
legend({'errors', 'ignored'})
end
title('Current error')
xlim([min(angles), max(angles)])
xlabel('Angle [deg]')
drawnow
end
+111
View File
@@ -0,0 +1,111 @@
% show_sinograms - compare reconstructed and mesaured projections
%
% show_sinograms(rec_avg,dphase, vectors,cfg, angles, shift_3D_total, par, show_derivative = false )
%
% Inputs:
% **rec_avg - optimal NCT reconstruction
% **dphase phase difference calculated from the measured data
% **vectors ASTRA configuration vectors
% **cfg ASTRA configuration structure
% **angles angles of the projections
% **shift_3D_total recovered deformation vector field
% **par parameter structure of the nonrigid tomo
% *optional*
% ++show_derivative if false, show directly the recovered signal otherwise the phase derivative
%*-----------------------------------------------------------------------*
%| |
%| 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 show_sinograms(rec_avg,dphase, vectors,cfg, angles, shift_3D_total, par, show_derivative )
if nargin < 8
show_derivative = false; % show directly the recovered signal
end
Nangles = cfg.iProjAngles;
Nblocks = length(shift_3D_total);
Bsize = ceil(Nangles/Nblocks);
% generate deformation tensors from the shift tensors
deform_tensors = nonrigid.get_deformation_fields(shift_3D_total, par.regularize_deform_evol, size(rec_avg));
% SHOW ANIMATION OF PROJECTIONS
rec_avg = gather(rec_avg);
split = astra.ASTRA_find_optimal_split(cfg,1,Nblocks);
for ll = 1:Nblocks
ids = 1+(ll-1)*Bsize:min(Nangles, ll*Bsize);
sinogram_corr(:,:,ids) = gather(tomo.Ax_sup_partial(rec_avg,cfg, vectors(ids,:),split, 'deformation_fields', deform_tensors{ll}));
end
split = astra.ASTRA_find_optimal_split(cfg);
sinogram_ideal = tomo.Ax_sup_partial(rec_avg,cfg, vectors,split);
if show_derivative
dphase_avg = math.get_phase_gradient_1D(exp(-1i*sinogram_ideal),2);
dphase_corr = math.get_phase_gradient_1D(exp(-1i*sinogram_corr),2);
sino_diff = dphase_corr - math.sum2(dphase_corr .* dphase) ./ math.sum2(dphase.^2) .* dphase ;
% sino_diff = dphase_corr - dphase ;
dsino_range = math.sp_quantile(sino_diff,[0.001, 0.999],10);
sino_diff = max(min(sino_diff, dsino_range(2)), dsino_range(1));
sino_range = math.sp_quantile(dphase_avg,[0.001,0.999],10);
sino_diff = (sino_diff-dsino_range(1))/diff(dsino_range)*sino_range(2);
sino_all= cat(2,dphase_corr, dphase, sino_diff);
else
phase = -math.unwrap2D_fft(dphase, 2, par.air_gap);
sino_diff = sinogram_corr - math.sum2(sinogram_corr .* phase) ./ math.sum2(phase.^2) .* phase ;
sino_range = math.sp_quantile(sinogram_corr,[0.001,0.999],10);
sino_all= cat(2,sinogram_corr,phase, sino_diff+sino_range(2)/2);
end
[~,order] = sort(angles);
figure(45864)
plotting.imagesc3D(sino_all, 'order', order)
axis off image xy
colormap bone
set(gca, 'clim', sino_range)
str = 'Model / Data / Difference';
if show_derivative
str = [str, ' - showing phase-gradient'];
else
str = [str, ' - showing unwrapped phase'];
end
title(str)
drawnow
end