mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-17 19:39:08 +09:00
initial commit
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
% ALIGN_PROJECTIONS_TO_LOWRES_TOMOGRAM align projection from e.g. local tomography to low resolution
|
||||
%
|
||||
% [phase_diff_interior, phase_diff_lres_model, weight, tomogram_lowres] = align_projections_to_lowres_tomogram(stack_object, lres_tomo_path, par)
|
||||
%
|
||||
% estimate alignement for the interior tomogram. Resulting alignment is close to optimal
|
||||
% but it should be further refined using an self-consistent method
|
||||
%
|
||||
% Inputs:
|
||||
% **stack_object - complex valued projection from ptychography
|
||||
% **lres_tomo_path - (string) path to nearfield low resolution
|
||||
% tomogram saved as a .mat file (saved by function tomo.save_tomogram)
|
||||
% The low resolution tomogram assumed to be saved as delta
|
||||
% (real part of refractive index)
|
||||
% and it has to include "par" structure with pixel scale
|
||||
% and a conversion factor from delta to phase
|
||||
% **par - tomography parameter structure
|
||||
%
|
||||
% *returns*:
|
||||
% ++stack_object - aligned complex valued projections of the high resolution sinogram
|
||||
% ++phase_diff_lres_model - phase difference fot the low resolution preview
|
||||
% ++weight - weights between phase_diff_interior and phase_diff_lres_model, weights are provided as uint8 to save memory
|
||||
% ++shifts - shifts that need to be applied on the phase_diff_interior to be aligned with the phase_diff_lres_model
|
||||
% ++resolution_ratio - low resolution pixel size divided by the interior pixel size
|
||||
% ++tomogram_lowres - low resolution tomogram
|
||||
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| 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 [stack_object, tomo_lres, win_small, shift, resolution_ratio, Nw_lres] = ...
|
||||
align_projections_to_lowres_tomogram(stack_object, lres_tomo_path, theta, par)
|
||||
|
||||
import utils.*
|
||||
import math.*
|
||||
utils.verbose(struct('prefix', 'prealign'))
|
||||
|
||||
|
||||
gpu = gpuDevice();
|
||||
if ~isempty(par.GPU_list) && gpu.Index ~= par.GPU_list(1)
|
||||
% switch and !! reset !! GPU
|
||||
gpu = gpuDevice(par.GPU_list(1));
|
||||
end
|
||||
|
||||
|
||||
%% load low resolution tomogram
|
||||
d = load(lres_tomo_path);
|
||||
tomo_lres = -d.tomogram_delta ./ d.par.factor ; % revert delta back to phase
|
||||
T = -graythresh(-tomo_lres(tomo_lres<0));
|
||||
% calculate the maximal diamter of the local tomogram;
|
||||
D = max(sum(radon(max(tomo_lres < T,[],3), 0:180)>0));
|
||||
% calculate resolution ratio between low/ high res tomogram
|
||||
resolution_ratio = d.par.pixel_size / par.pixel_size;
|
||||
clear d
|
||||
|
||||
|
||||
|
||||
[Nx,Ny,Nangles] = size(stack_object);
|
||||
Npix_lres = size(tomo_lres);
|
||||
|
||||
% size of the low resolution projections to be generated
|
||||
Nw_lres = ceil([Npix_lres(3), 1.1*D]); % add 10% extra to the maximal diameter
|
||||
Nw_full = ceil(Nw_lres*resolution_ratio); % get the corresponding size of the low res tomogram would be measured in full resolution
|
||||
|
||||
|
||||
|
||||
%% %%%%%%%% Get computed sinogram
|
||||
|
||||
% get rough and rather emptirical estimation of the reliability of the interior tomograms
|
||||
win = Garray(single(par.illum_sum));
|
||||
win = utils.imgaussfilt2_fft(sqrt(win), par.asize(1)/20);
|
||||
win = 2-2./(1+ win.^2 / max(win(:).^2)); % limit the weights to 0-1 range
|
||||
win = win .* tukeywin(Nx) .* tukeywin(Ny)';
|
||||
|
||||
|
||||
gtomo_lres = Garray(tomo_lres);
|
||||
|
||||
%% center properly the reconstruction
|
||||
for ii = 1:5
|
||||
[x,y,mass] = center(sqrt(max(0,-gtomo_lres))+eps); % abs seems to be more stable than max(0,x) even for missing wedge or laminography
|
||||
% more robust estimation of center
|
||||
rec_center(1) = gather(mean(x.*mass)./mean(mass));
|
||||
rec_center(2) = gather(mean(y.*mass)./mean(mass));
|
||||
|
||||
% avoid drifts of the reconstructed volume
|
||||
gtomo_lres = tomo.block_fun(@imshift_fft, gtomo_lres, -rec_center(1), -rec_center(2));
|
||||
end
|
||||
tomo_lres = gather(gtomo_lres);
|
||||
|
||||
|
||||
verbose(-1,'Aligning projections to a low resolution tomogram')
|
||||
|
||||
% get block size to work roughly with 0.2GB arrays
|
||||
Nblocks = ceil((prod(Nw_full)*Nangles*4*2*12)/gpu.AvailableMemory);
|
||||
|
||||
% prepare the local tomo object downsampled to resolution of the low resolution tomogram
|
||||
win_small = interpolate_linear(win,ceil([Nx,Ny]/resolution_ratio));
|
||||
win_small = gather(uint8(win_small*255));
|
||||
|
||||
[stack_object, shift] = ...
|
||||
tomo.block_fun(@find_alignment,stack_object, gtomo_lres,theta', par, resolution_ratio, win_small, Nw_lres, struct('Nblocks', Nblocks));
|
||||
|
||||
|
||||
verbose(-1,'Pre-alignment done')
|
||||
|
||||
shift = gather(shift);
|
||||
|
||||
%% REPORT ALIGNMENT RESULTS
|
||||
figure()
|
||||
range = [round(Ny-par.asize(2))/2, round(Nx-par.asize(1))/2];
|
||||
range = repmat(range, Nangles,1);
|
||||
subplot(1,2,1)
|
||||
errorbar(theta, shift(:,1)*par.pixel_size*1e6, range(:,1)*par.pixel_size*1e6 , '.')
|
||||
title('Horizontal shift and FOV')
|
||||
axis tight
|
||||
ylabel('Estimated shift [um]')
|
||||
xlabel('Angle [deg]')
|
||||
grid on
|
||||
|
||||
subplot(1,2,2)
|
||||
errorbar(theta, shift(:,2)*par.pixel_size*1e6, range(:,2)*par.pixel_size*1e6 , '.')
|
||||
title('Vertical shift and FOV')
|
||||
axis tight
|
||||
ylabel('Estimated shift [um]')
|
||||
xlabel('Angle [deg]')
|
||||
grid on
|
||||
|
||||
plotting.suptitle('Shifts estimated from initial low-res tomogram')
|
||||
|
||||
drawnow
|
||||
|
||||
utils.verbose(struct('prefix', 'template'))
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
function [stack_object, total_shift] = find_alignment(stack_object, tomo_lres,theta, par, resolution_ratio, win_small, Nw_lres)
|
||||
import utils.*
|
||||
import math.*
|
||||
|
||||
|
||||
Npix_lres = size(tomo_lres);
|
||||
Nw_local = [size(stack_object,1), size(stack_object,2)];
|
||||
|
||||
[cfg_lres, vectors_lres] = astra.ASTRA_initialize(Npix_lres,Nw_lres,theta, par.lamino_angle, 0, 1);
|
||||
|
||||
% find optimal split of the dataset for given GPU
|
||||
split = astra.ASTRA_find_optimal_split(cfg_lres);
|
||||
% forward projection model
|
||||
model = astra.Ax_partial(tomo_lres,cfg_lres, vectors_lres,split,'verbose', 0);
|
||||
model = exp(1i*model);
|
||||
|
||||
% get phase difference
|
||||
diff_model = math.get_phase_gradient_1D(model, 2);
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% FIND RELATIVE SHIFT BETWEEN THE DOWNSCALED MODEL AND THE LOW RES RECONSTRUCTION %%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
win_small = single(win_small)/255;
|
||||
|
||||
object_small = interpolateFT_centered(stack_object,ceil(Nw_local/resolution_ratio), -1);
|
||||
object_small = crop_pad(object_small ./ (abs(object_small) + 1e-3) .* win_small,Nw_lres);
|
||||
|
||||
% get at least some initial phase ramp removal
|
||||
[object_small, gamma_tot, gamma_tot_x, gamma_tot_y] = stabilize_phase(object_small, model, 'weights', abs(object_small));
|
||||
|
||||
% find relative shift of the patch and low resolution model
|
||||
total_shift = 0;
|
||||
|
||||
W = abs(object_small);
|
||||
% perform crosscorrelation between phase derivatives to find
|
||||
% the optimal shift
|
||||
shift = find_shift_fast_2D( W.* diff_model, W.* math.get_phase_gradient_1D(object_small, 2));
|
||||
% shift to the estimated position
|
||||
object_small = imshift_fft(object_small, shift);
|
||||
total_shift = total_shift + shift;
|
||||
|
||||
|
||||
%% remove phase ramp using the low res tomogram
|
||||
for ii = 1:5
|
||||
% refine the phase ramp, we need high precision -> do several iterations
|
||||
[object_small, gamma, gamma_x, gamma_y] = stabilize_phase(object_small, model,'weights', abs(object_small), 'fourier_guess', false);
|
||||
gamma_tot = gamma_tot .* gamma;
|
||||
gamma_tot_x = gamma_tot_x + gamma_x;
|
||||
gamma_tot_y = gamma_tot_y + gamma_y;
|
||||
end
|
||||
% apply results from low resolution to the full resolution object
|
||||
stack_object = apply_ramp(stack_object,gamma_tot, (gamma_tot_x)/resolution_ratio, (gamma_tot_y)/resolution_ratio );
|
||||
total_shift = total_shift * resolution_ratio;
|
||||
|
||||
% weight = uint8(255*win_small);
|
||||
|
||||
% weight = uint8(255*real(abs(object_small)));
|
||||
% weight(weight<10) = 0; % remove interpolation artefacts from regions far from measured array
|
||||
|
||||
end
|
||||
|
||||
|
||||
function object_full = apply_ramp(object_full,gamma, gamma_x, gamma_y )
|
||||
[M,N,~] = size(object_full);
|
||||
xramp = pi*(linspace(-1,1,M))';
|
||||
yramp = pi*(linspace(-1,1,N));
|
||||
if ~isa(object_full, 'gpuArray')
|
||||
object_full = bsxfun(@times,object_full , gamma);
|
||||
object_full = bsxfun(@times,object_full , exp(1i*bsxfun(@times,xramp, M*gamma_x)));
|
||||
object_full = bsxfun(@times,object_full , exp(1i*bsxfun(@times,yramp, N*gamma_y)));
|
||||
else
|
||||
% use inplace GPU calculation
|
||||
object_full = arrayfun(@auxfun, object_full, gamma, M*gamma_x, N*gamma_y, xramp, yramp);
|
||||
end
|
||||
end
|
||||
|
||||
function object = auxfun(object, gamma, gamma_x, gamma_y, xramp, yramp)
|
||||
|
||||
object = object .* gamma;
|
||||
object = object .* exp(1i*xramp*gamma_x);
|
||||
object = object .* exp(1i*yramp*gamma_y);
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
% MERGE_WEIGHTS_INTERIORED_ARRAYS auxiliar function for interior tomography for block processing by tomo.block_fun
|
||||
% that adds up two arrays as X*W + (1-W)*Y, and unwrap resulting phase difference and
|
||||
% return the phase
|
||||
%
|
||||
% phase_diff = merge_weights_interiored_arrays(stack_object, phase_diff_lowres, weights_interior,shift, ROI, param, exterior_weights_interior)
|
||||
%
|
||||
% Inputs:
|
||||
% **stack_object - complex valued projections
|
||||
% **phase_diff_lowres - phase gradient for the low resolution tomogram
|
||||
% **weights_interior - relative weights_interiors of each region of the tomogram (denotes interior region )
|
||||
% **shift - shifts applied to the phase gradient
|
||||
% **ROI - region to be unwrapped
|
||||
% **param - tomography parameter structure
|
||||
% **exterior_weights_interior - scalar, relative weights_interior of the low res region during alignment
|
||||
% Outputs:
|
||||
% ++phase - unwrapped phase of the low + high resolution sinogram together
|
||||
% ++weights_interiors - importance weights_interiors used for alignment, ie gives much smaller
|
||||
% weights_interior to the low resolution region compared to the interion projection part
|
||||
% based on the "exterior_weights_interior" input value
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| 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 [phase_diff, weights_interiors] = merge_and_unwrap_sinograms(stack_object, tomo_lres, weights_interior,shift, theta, ROI, Nw_lres, param)
|
||||
|
||||
|
||||
|
||||
Npix_lres = size(tomo_lres);
|
||||
Npix_full = ceil(Nw_lres*param.resolution_ratio);
|
||||
|
||||
% get phase difference of the interior tomo
|
||||
phase_diff_interior = math.get_phase_gradient_1D(stack_object,2,0);
|
||||
|
||||
avg_shift = round(mean(shift));
|
||||
Npix_partial = [size(stack_object,1), size(stack_object,2)]+2*ceil(max(abs(shift-avg_shift)));
|
||||
Npix_partial = min(Npix_partial, Npix_full);
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%% (1) UPSAMPLE WEIGHTS TO THE FULL RESOLUTION %%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
if isa(weights_interior, 'uint8') || ( isa(weights_interior, 'gpuArray') && strcmpi(classUnderlying(weights_interior),'uint8'))
|
||||
% convert to single precision
|
||||
weights_interior = single(weights_interior-1)/255;
|
||||
end
|
||||
if size(weights_interior,1) ~= size(stack_object,1) || size(weights_interior,2) ~= size(stack_object,2)
|
||||
% assume that the weights_interior are stored downsampled to same memory
|
||||
weights_interior = utils.interpolate_linear(weights_interior, size(stack_object));
|
||||
end
|
||||
|
||||
% pad arrays with enough space for save shifting
|
||||
phase_diff_interior = utils.crop_pad(phase_diff_interior , Npix_partial);
|
||||
weights_interior = utils.crop_pad(weights_interior , Npix_partial);
|
||||
|
||||
|
||||
|
||||
% apply (shifts-avg_shift) on the interior tomogram and its weights_interiors
|
||||
[phase_diff_interior, weights_interior] = shift_interior_sinograms(phase_diff_interior, weights_interior, shift-avg_shift);
|
||||
|
||||
|
||||
|
||||
[cfg_lres, vectors_lres] = astra.ASTRA_initialize(Npix_lres,Nw_lres,theta, 90, 0, 1);
|
||||
|
||||
% find optimal split of the dataset for given GPU
|
||||
split = astra.ASTRA_find_optimal_split(cfg_lres);
|
||||
% forward projection model
|
||||
model = astra.Ax_partial(tomo_lres,cfg_lres, vectors_lres,split,'verbose', 0);
|
||||
|
||||
% get phase difference
|
||||
phase_diff_lowres = math.get_phase_gradient_1D( exp(1i*model), 2, 0);
|
||||
|
||||
clear model
|
||||
% use FFT centred upsampling to keep it accurate
|
||||
%phase_diff_lowres = utils.interpolateFT_centered(phase_diff_lowres / param.resolution_ratio, Npix_full,1);
|
||||
|
||||
% linear interpolation is much faster but it introduces bias
|
||||
bias = 1-1/param.resolution_ratio;
|
||||
phase_diff_lowres = utils.imshift_fft(phase_diff_lowres,-[bias, bias]);
|
||||
phase_diff_lowres = utils.interpolate_linear(phase_diff_lowres / param.resolution_ratio, Npix_full);
|
||||
|
||||
% pad arrays to full size and apply the average shift
|
||||
phase_diff_interior = utils.imshift_fast(phase_diff_interior ,-avg_shift(1),-avg_shift(2), Npix_full);
|
||||
weights_interior = utils.imshift_fast(weights_interior ,-avg_shift(1),-avg_shift(2), Npix_full);
|
||||
|
||||
alpha = 0.05; % avoid effects from weak regions of the weights
|
||||
weights_interior = max(0, weights_interior-alpha)/(1-alpha); % remove artefacts from the FFT shift
|
||||
|
||||
% merge tomogram based on provided weights_interior
|
||||
phase_diff = arrayfun(@merge_arrays,phase_diff_interior, phase_diff_lowres, weights_interior);
|
||||
|
||||
|
||||
if ~isempty(param.vert_range)
|
||||
% find optimal vertical range
|
||||
Nlayers = length(ROI{1});
|
||||
vrange0 = param.vert_range([1,end]);
|
||||
vrange(2) = min(vrange0(2),Nlayers);
|
||||
vrange(1) = max(1,vrange0(1));
|
||||
|
||||
% help with splitting in ASTRA (GPU memory limit)
|
||||
vrange_center = ceil(mean(vrange));
|
||||
estimated_split_factor = 4;
|
||||
Nvert = floor((vrange(2)-vrange(1)+1) / estimated_split_factor)*estimated_split_factor;
|
||||
vrange(1) = ceil(vrange_center - Nvert/2);
|
||||
vrange(2) = floor(vrange_center + Nvert/2-1);
|
||||
vrange = vrange(1):vrange(2);
|
||||
else
|
||||
vrange = ':';
|
||||
end
|
||||
|
||||
|
||||
|
||||
% select only object_ROI -> make it easily splitable to GPU and avoid
|
||||
% edge artefacts
|
||||
phase_diff = phase_diff(ROI{1}(vrange),ROI{2},:);
|
||||
weights_interior = weights_interior(ROI{1}(vrange),ROI{2},:);
|
||||
|
||||
|
||||
% apply binning
|
||||
if param.binning > 1
|
||||
phase_diff = utils.interpolateFT_centered(phase_diff,ceil(size(phase_diff)/param.binning/2)*2, 1); % accurate interpolation using FFT
|
||||
end
|
||||
|
||||
if strcmpi(param.unwrap_data_method, 'fft_1d')
|
||||
phase_diff = math.unwrap2D_fft(phase_diff,2,param.air_gap,0);
|
||||
end
|
||||
|
||||
% provide weights_interiors to be used for alignment
|
||||
weights_interiors = param.exterior_weight + (1-param.exterior_weight)*weights_interior;
|
||||
|
||||
|
||||
weights_interiors = utils.interpolate_linear(weights_interiors, ceil(size(weights_interiors)/10));
|
||||
|
||||
% keep in uint8 to save memory
|
||||
weights_interiors = uint8(weights_interiors*255);
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
function C = merge_arrays(A, B, W)
|
||||
W = max(0, min(1,W));
|
||||
C = A.*W + (1-W).*B;
|
||||
end
|
||||
|
||||
|
||||
% FUNCTION [phase_diff_interior,weights_interior ] = shift_interior_sinograms(phase_diff_interior, weights_interior, shift )
|
||||
% auxiliar function for interior tomography
|
||||
% Inputs:
|
||||
% phase_diff_interior - phase gradient for the interior tomo
|
||||
% weights_interior - relative weights_interiors of each region of the tomogram (denotes interior region )
|
||||
% shift - shifts applied to the phase gradient
|
||||
% Outputs:
|
||||
% phase_diff_interior - phase difference for only the interior part
|
||||
% weights_interior - reliability weights_interiors estimated from the original field of view and shifted accordingly
|
||||
|
||||
function [phase_diff_interior,weights_interior ] = shift_interior_sinograms(phase_diff_interior, weights_interior, shift )
|
||||
|
||||
% shift phase and weights_interiors in parallel to save time and avoid numerical problems at the edges
|
||||
phase_diff_interior = utils.imshift_fft(weights_interior .* exp(1i*phase_diff_interior),shift);
|
||||
|
||||
% get amplitude (weights_interiors)
|
||||
weights_interior = abs(phase_diff_interior);
|
||||
|
||||
% get phase = phase diff
|
||||
phase_diff_interior = angle(phase_diff_interior);
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user