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

153 lines
5.8 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.
% 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