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
+151
View File
@@ -0,0 +1,151 @@
% [delta_stack_prealign, obj_interf_pos_x, obj_interf_pos_y ] = get_auto_tomo(param_autotomo,surface_calib_file, omnyposfile)
%
% Description:
%
% The function (1) loads the omnyposfile file and determine the scanning
% positions if get_auto_calibration or auto_alignment is 1 and
% (2) loads the surface_calib_file to give an initial guess for
% the alignemnt array (deltastack) if auto_alignment is 1
%
% Input:
%
% par. auto_alignment: 0 or 1 (default)
% par. get_auto_calibration: 0 or 1 (default)
% surface_calib_file (mandatory if auto_alignment=1)
% omnyposfile (mandatory if auto_alignment=1 or get_auto_calibration=1)
%
% Output:
%
% delta_stack_prealign: used as initial guess for the alignment
% obj_interf_pos_x and obj_interf_pos_y: Object maximum position based on interferometry
%
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [delta_stack_prealign, obj_interf_pos_x, obj_interf_pos_y ] = ...
get_auto_tomo(par,surface_calib_file, omnyposfile, theta, scanstomo)
import beamline.read_omny_pos
import utils.*
import ptycho.*
import beamline.*
obj_interf_pos_x = [];
obj_interf_pos_y = [];
flag_plot = 1;
delta_stack_prealign = [];
%%% To improve: Shifts of the probe are not yet considered here, see
%%% /cSAXS_sxdm_2013_06_omny/matlab/tomo/autotomo_calibration_porous_S00506_S00930.m
if par.auto_alignment ||par.get_auto_calibration
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Determine position of first pixel in the reconstructions %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('Loading omny_pos for autoalignment')
for ii = 1:max(size(scanstomo))
progressbar(ii, max(size(scanstomo)))
out_orch = read_omny_pos(sprintf(omnyposfile,scanstomo(ii)));
positions_real = [out_orch.Average_y_st_fzp*1e-6 out_orch.Average_x_st_fzp*1e-6];
% clear positions
% positions = positions_real./par.pixel_size;
%
% % Change from object to probe positions
% positions = -positions;
%
% positions(:,1) = positions(:,1) - min(positions(:,1));
% positions(:,2) = positions(:,2) - min(positions(:,2));
% positions = round(positions);
%%% Object maximum position based on interferometry - sample motion
%%% Corresponds to pos to coordinates of (1,1) pixel
%%% increasing number means the sample was higher
obj_interf_pos_y(ii) = max(positions_real(:,1));
obj_interf_pos_x(ii) = max(positions_real(:,2));
end
end
if par.auto_alignment && exist(surface_calib_file, 'file')
%%% Read calibration file and interpolate correction to these angles
pos_cal = load(surface_calib_file);
delta_stack_corr_y_filt = spline(pos_cal.thetasort,pos_cal.delta_stack_corr_y_filt,theta);
delta_stack_corr_x_filt = spline(pos_cal.thetasort,pos_cal.delta_stack_corr_x_filt,theta);
%%% Interferometer alignment with mirror surface corrections
delta_stack_prealign(1,:) = delta_stack_corr_y_filt+obj_interf_pos_y;
delta_stack_prealign(2,:) = delta_stack_corr_x_filt+obj_interf_pos_x;
%%% Remove constant term from y alignment
delta_stack_prealign(1,:) = delta_stack_prealign(1,:)-mean(delta_stack_prealign(1,:));
%%% Remove sin term from correction in x
[~,indsort] = sort(theta);
auxfunc = delta_stack_prealign(2,indsort);
auxfunc = [auxfunc -auxfunc+auxfunc(end)+auxfunc(1)];
auxfuncft = fft(auxfunc);
auxfuncft(3:end-1) = 0;
auxfunc2 = ifft(auxfuncft);
auxfunc3 = auxfunc2(1:end/2);
delta_stack_prealign(2,indsort) = delta_stack_prealign(2,indsort) - auxfunc3;
delta_stack_prealign = delta_stack_prealign/par.pixel_size;
if flag_plot
figure(1);
clf;
subplot(2,1,1)
plot(theta,obj_interf_pos_y,'.')
title('Interferometer y position [microns]')
subplot(2,1,2)
plot(theta,obj_interf_pos_x,'.')
title('Interferometer x position [microns]')
figure(2);
clf;
subplot(2,1,1)
plot(theta,delta_stack_prealign(1,:),'.')
title('Correction in y [pixels]')
subplot(2,1,2)
plot(theta,delta_stack_prealign(2,:),'.')
title('Correction in x [pixels]')
end
elseif ~exist(surface_calib_file, 'file')
warning('Missing surface calibration file %s', surface_calib_file)
end
end
+285
View File
@@ -0,0 +1,285 @@
% INITIALIZE_TOMO basic initialization steps of tomography -> check validity of the inputs,
% load first projection and store its parameters, check angles, create output folders
%
% [par, angles_check, object] = initialize_tomo(par, scans, use_gpu, object_preprocess_fun)
%
% Inputs:
% **par - basic parameters defined in template
% **scans - list of the scans to be loaded
% **use_gpu - (bool), dont use GPU if use_gpu == 0, (default = true )
% **object_preprocess_fun - user defined preprocessing function applied on the loaded projections, e.g. in laminography it can be rotation, default = @(x)x
%
% *returns*
% ++par updated basic parameters
% ++angles_check
% ++object example of one loaded projection
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [par, angles_check, object] = initialize_tomo(par, scans, use_gpu, object_preprocess_fun)
import ptycho.*
import io.*
utils.verbose(struct('prefix', 'initialize'))
%% initial checks
if verLessThan('matlab', '9.3')
warning on
warning('Only Matlab versions >= 2018a are tested and supported, \nYour Matlab version is %s', version)
pause(5)
end
if nargin < 3
use_gpu = true;
end
if gpuDeviceCount == 0 && use_gpu
warning('Using CUDA enabled GPU is strongly recommended')
pause(5)
use_gpu = false;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% CHECK GPU AVAILIBILITY %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if use_gpu
if gpuDeviceCount == 0
error('Code needs CUDA enabled GPU, suppress by setting input parameter "use_gpu=false" ')
end
if any(par.GPU_list > gpuDeviceCount)
error('Selected GPU in GPU_list is not available')
end
gpu = gpuDevice(par.GPU_list(1));
if ~verLessThan('matlab', '9.4') && gpu.ToolkitVersion < 9
error('Code needs CUDA 9.0 to work with Matlab 2018a and newer')
elseif verLessThan('matlab', '9.4') && gpu.ToolkitVersion < 8
error('Code needs at least CUDA 8.0 to work with Matlab 2017b')
end
fprintf('=================================================== \n')
fprintf('=== Available memory for GPU %i : %2.1fGB / %2.1fGB === \n', gpu.Index, gpu.AvailableMemory/1e9, gpu.TotalMemory/1e9)
fprintf('=================================================== \n')
% check that more than 3GB of GPU mem is free and that 90% of total
% memory is available -> make sure that this template is the only
% process using the selected GPU
reset(gpu)
if ~debug() && (gpu.AvailableMemory < gpu.TotalMemory * 0.9 || gpu.AvailableMemory < 3e9)
utils.verbose(0,'\n\n=============== GPU report ================')
!nvidia-smi
warning on
warning off backtrace
if gpu.AvailableMemory < gpu.TotalMemory * 0.9
warning(['Memory in GPU %i (Nvidia id:%i) is probably used by other user,'...
'try to manunally choose GPU or kill other processes'], gpu.Index, gpu.Index-1)
else
warning(['Memory in GPU %i (Nvidia id:%i) is less than recommended 3GB,'...
'try to manunally choose GPU or kill other processes'], gpu.Index, gpu.Index-1)
end
warning on
% check who is using the GPU
%utils.report_GPU_usage(gpu.Index);
if ~debug() && ~par.online_tomo
if ~strcmpi(input('Do you want to continue [y/N]', 's'), 'y')
error('Set other GPU to use by par.GPU_list parameter')
end
end
% this is only recommende value, the code should run even with
% less, but then it gets less efficient.
elseif (gpu.AvailableMemory < gpu.TotalMemory * 0.9 || gpu.AvailableMemory < 3e9)
utils.report_GPU_usage
end
end
if nargin < 4
object_preprocess_fun = []; % no preprocessing function
end
par.use_GPU = use_gpu; % store user preferences in using GPU
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% initial values - LOAD ONE FRAME FOR DEFINING PTYCHO SCAN VALUES %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
file = [];
ii = 1;
while isempty(file) && ii <= length(scans)
file = find_projection_files_names(par, scans(ii));
if isempty(file)
warning(['Out of luck - Reconstruction not found']);
ii = ii+1;
else
break
end
end
if isempty(file)
error('No reconstructions found, check that analysis folder path contains scans %i-%i', min(scans), max(scans))
end
%%% Read first projection to check size and reconstruction parameters
display(['Reading file: ' file])
[object, probe, p] = load_ptycho_recons(file);
probe = single(probe(:,:,1)); % keep only the first mode
par.asize = p.asize; % probe size
par.dims_ob_loaded = [size(object,1), size(object,2)]; % load the sizes directly from the object, note that "object_preprocess_fun" can crop/rotate the image !!
if isfield(p, 'scanindexrange')
p.scanidxs{1} = p.scanindexrange(1):p.scanindexrange(2);
positions = int32(p.positions(p.scanidxs{1},:));
indices = int32(1:length(p.scanidxs{1}));
% get at least some estimation of the illumination intensity for different regions in the
% projection
par.illum_sum = utils.add_to_3D_projection(abs(probe).^2,zeros(max(p.object_size,[],1),'single'),positions,indices, true);
else
% if nto availible, get et least a crude guess
par.illum_sum = ones(par.dims_ob_loaded-par.asize);
end
par.illum_sum = utils.crop_pad(par.illum_sum,par.dims_ob_loaded);
par.illum_sum = par.illum_sum ./ quantile(par.illum_sum(:), 0.9); % normalize the values to keep maximum around 1
% in case of unequal pixel size
if p.dx_spec(1) ~= p.dx_spec(2)
% upsample the data in the dimennsion with lower resolution (-> at least relax issues in tomography interpolation)
pixel_scale = p.dx_spec ./ min(p.dx_spec) ;
dims_ob_new = round(par.dims_ob_loaded .* pixel_scale);
par.illum_sum = max(0,real(utils.interpolateFT(par.illum_sum, dims_ob_new)));
object = utils.interpolateFT(par.illum_sum, dims_ob_new);
par.asize = round(par.asize .* pixel_scale);
probe = utils.interpolateFT(probe, par.asize);
p.dx_spec(:) = min(p.dx_spec);
end
if ~isempty(object_preprocess_fun)
% apply custom preprocessing, e.g. rotation and flipping for
% laminography setup
object = object_preprocess_fun(object);
par.illum_sum = max(0, object_preprocess_fun(par.illum_sum));
end
par.dims_ob = [size(object,1), size(object,2)]; % object size after object_preprocess_fun
par.probe = probe;
par.lambda =p.lambda; % wavelength [m]
par.pixel_size=p.dx_spec(1) * 2^par.downsample_projections; % reconstructed pixel size [m]
if p.dx_spec(1)~=p.dx_spec(2)
warning('Pixel size not symmetric - This code cannot handle')
end
par.factor=par.lambda/(2*pi*par.pixel_size);
par.factor_edensity = 1e-30*2*pi/(par.lambda^2*2.81794e-15);
%%% Check angles %%%
if par.checkangles
[par.scans_check, angles_check] = tomo_angles(projections, subtomograms, ...
scan_num, subs_to_do); % ignores the repeated 180deg scan.
else
angles_check = [];
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% GENERATE SCAN STRING FOR FILES DESCRIPTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
par.scans_string = {};
if isfield(par, 'output_folder_prefix') && ~isempty(par.output_folder_prefix)
par.scans_string{end+1} = par.output_folder_prefix;
end
if ~isempty(par.tomo_id)
auxstr = repmat('%i+',1,length(par.tomo_id));
par.scans_string{end+1} = sprintf(['id_',auxstr(1:end-1)], par.tomo_id);
elseif par.online_tomo
par.scans_string{end+1} = sprintf('S%05d',scans(1));
end
% load sample name if provided
if ~isfield(p, 'samplename')
par.samplename = '';
else
par.samplename = p.samplename;
end
if ~isempty(par.samplename)
par.scans_string{end+1} = par.samplename;
end
if ~par.online_tomo
par.scans_string{end+1}= sprintf('S%05d_to_S%05d',scans(1),scans(end));
end
par.scans_string = join(par.scans_string, '_');
par.scans_string = par.scans_string{1};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Output folder
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
par.output_folder= {fullfile(par.output_path, 'tomo'), par.scans_string, par.filesuffix, par.fileprefix};
if par.online_tomo
par.output_folder{end+1}= 'online';
end
par.output_folder = join(par.output_folder, '_');
par.output_folder = par.output_folder{1};
if ~debug()
utils.verbose('Output folder: %s', par.output_folder)
if ~exist(par.output_folder,'dir')
mkdir(par.output_folder);
end
[~,attr] = fileattrib(par.output_folder);
if ~(attr.UserWrite || attr.GroupWrite)
error('Output path %s is not writable', par.output_folder)
end
% For website
subdir_online = fullfile(par.base_path,'analysis/online/tomo/');
if ~exist(subdir_online,'dir')
mkdir(subdir_online);
end
par.online_tomo_path = sprintf('%sonline_tomo_S%05d', subdir_online, min(par.scanstomo));
end
end
+276
View File
@@ -0,0 +1,276 @@
% INITIALIZE_TOMO_APS basic initialization steps of tomography -> check validity of the inputs,
% load first projection and store its parameters, check angles, create output folders
% Created by YJ Based on PSI's function
% [par, angles_check, object] = initialize_tomo_aps(par, scans, use_gpu, object_preprocess_fun)
%
% Inputs:
% **par - basic parameters defined in template
% **scans - list of the scans to be loaded
% **use_gpu - (bool), dont use GPU if use_gpu == 0, (default = true )
% **object_preprocess_fun - user defined preprocessing function applied on the loaded projections, e.g. in laminography it can be rotation, default = @(x)x
%
% *returns*
% ++par updated basic parameters
% ++angles_check
% ++object example of one loaded projection
function [par, angles_check, object] = initialize_tomo_aps(par, scans, use_gpu, object_preprocess_fun)
import ptycho.*
import io.*
utils.verbose(struct('prefix', 'initialize'))
%% initial checks
if verLessThan('matlab', '9.3')
warning on
warning('Only Matlab versions >= 2018a are tested and supported, \nYour Matlab version is %s', version)
pause(5)
end
if nargin < 3
use_gpu = true;
end
if gpuDeviceCount == 0 && use_gpu
warning('Using CUDA enabled GPU is strongly recommended')
pause(5)
use_gpu = false;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% CHECK GPU AVAILIBILITY %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if use_gpu
if gpuDeviceCount == 0
error('Code needs CUDA enabled GPU, suppress by setting input parameter "use_gpu=false" ')
end
if any(par.GPU_list > gpuDeviceCount)
error('Selected GPU in GPU_list is not available')
end
gpu = gpuDevice(par.GPU_list(1));
if ~verLessThan('matlab', '9.4') && gpu.ToolkitVersion < 9
error('Code needs CUDA 9.0 to work with Matlab 2018a and newer')
elseif verLessThan('matlab', '9.4') && gpu.ToolkitVersion < 8
error('Code needs at least CUDA 8.0 to work with Matlab 2017b')
end
fprintf('=================================================== \n')
fprintf('=== Available memory for GPU %i : %2.1fGB / %2.1fGB === \n', gpu.Index, gpu.AvailableMemory/1e9, gpu.TotalMemory/1e9)
fprintf('=================================================== \n')
% check that more than 3GB of GPU mem is free and that 90% of total
% memory is available -> make sure that this template is the only
% process using the selected GPU
reset(gpu)
if ~debug() && (gpu.AvailableMemory < gpu.TotalMemory * par.check_gpu_percentage || gpu.AvailableMemory < 3e9)
utils.verbose(0,'\n\n=============== GPU report ================')
!nvidia-smi
warning on
warning off backtrace
if gpu.AvailableMemory < gpu.TotalMemory * 0.9
warning(['Memory in GPU %i (Nvidia id:%i) is probably used by other user,'...
'try to manunally choose GPU or kill other processes'], gpu.Index, gpu.Index-1)
else
warning(['Memory in GPU %i (Nvidia id:%i) is less than recommended 3GB,'...
'try to manunally choose GPU or kill other processes'], gpu.Index, gpu.Index-1)
end
warning on
% check who is using the GPU
utils.report_GPU_usage(gpu.Index);
if ~debug() && ~par.online_tomo
if ~strcmpi(input('Do you want to continue [y/N]', 's'), 'y')
error('Set other GPU to use by par.GPU_list parameter')
end
end
% this is only recommende value, the code should run even with
% less, but then it gets less efficient.
elseif (gpu.AvailableMemory < gpu.TotalMemory * 0.9 || gpu.AvailableMemory < 3e9)
%utils.report_GPU_usage
end
end
if nargin < 4
object_preprocess_fun = []; % no preprocessing function
end
par.use_GPU = use_gpu; % store user preferences in using GPU
%{
%% First check if reconstructions exist
proj_file_names = {};
hasRecon = zeros(length(scans),1);
for ii=1:length(scans)
progressbar(ii, length(scans))
file = find_ML_recon_files_names(par, scans(ii)); %find ML recon outputs
if ~isempty(file)
hasRecon(ii) = 1;
end
end
%}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% initial values - LOAD ONE FRAME FOR DEFINING PTYCHO SCAN VALUES %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
file = [];
ii = 1;
while isempty(file) && ii <= length(scans)
%file = find_projection_files_names_aps(par, scans(ii));
file = find_ML_recon_files_names(par, scans(ii)); %find ML recon outputs
if isempty(file)
%warning(['Out of luck - Reconstruction not found']);
disp(['Out of luck - Reconstruction not found']);
ii = ii+1;
else
break
end
end
if isempty(file)
error('No reconstructions found, check that analysis folder path contains scans %i-%i', min(scans), max(scans))
end
disp(file)
%%% Read first projection to check size and reconstruction parameters
display(['Reading file: ' file])
[object, probe, dx_spec] = load_aps_ML_recons(file);
if iscell(probe)
probe = single(probe{1}(:,:,1)); % keep only the first mode
else
probe = single(probe(:,:,1)); % keep only the first mode
end
par.asize = size(probe); % probe size
par.dims_ob_loaded = [size(object,1), size(object,2)]; % load the sizes directly from the object, note that "object_preprocess_fun" can crop/rotate the image !!
%{
if isfield(p, 'scanindexrange')
p.scanidxs{1} = p.scanindexrange(1):p.scanindexrange(2);
positions = int32(p.positions(p.scanidxs{1},:));
indices = int32(1:length(p.scanidxs{1}));
% get at least some estimation of the illumination intensity for different regions in the
% projection
par.illum_sum = utils.add_to_3D_projection(abs(probe).^2,zeros(max(p.object_size,[],1),'single'),positions,indices, true);
else
% if nto availible, get et least a crude guess
par.illum_sum = ones(par.dims_ob_loaded-par.asize);
end
%}
par.illum_sum = ones(par.dims_ob_loaded-par.asize);
par.illum_sum = utils.crop_pad(par.illum_sum,par.dims_ob_loaded);
par.illum_sum = par.illum_sum ./ quantile(par.illum_sum(:), 0.9); % normalize the values to keep maximum around 1
% in case of unequal pixel size
if dx_spec(1) ~= dx_spec(2)
% upsample the data in the dimennsion with lower resolution (-> at least relax issues in tomography interpolation)
pixel_scale = dx_spec ./ min(dx_spec) ;
dims_ob_new = round(par.dims_ob_loaded .* pixel_scale);
par.illum_sum = max(0,real(utils.interpolateFT(par.illum_sum, dims_ob_new)));
object = utils.interpolateFT(par.illum_sum, dims_ob_new);
par.asize = round(par.asize .* pixel_scale);
probe = utils.interpolateFT(probe, par.asize);
dx_spec(:) = min(dx_spec);
end
if ~isempty(object_preprocess_fun)
% apply custom preprocessing, e.g. rotation and flipping for
% laminography setup
object = object_preprocess_fun(object);
par.illum_sum = max(0, object_preprocess_fun(par.illum_sum));
end
par.dims_ob = [size(object,1), size(object,2)]; % object size after object_preprocess_fun
par.probe = probe;
if ~isfield(par, 'lambda')
par.lambda = p.lambda; % wavelength [m]
end
par.pixel_size=dx_spec(1) * 2^par.downsample_projections; % reconstructed pixel size [m]
if dx_spec(1)~=dx_spec(2)
warning('Pixel size not symmetric - This code cannot handle')
end
par.factor=par.lambda/(2*pi*par.pixel_size);
par.factor_edensity = 1e-30*2*pi/(par.lambda^2*2.81794e-15);
%{
%%% Check angles %%%
if par.checkangles
[par.scans_check, angles_check] = tomo_angles(projections, subtomograms, ...
scan_num, subs_to_do); % ignores the repeated 180deg scan.
else
angles_check = [];
end
%}
angles_check = [];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% GENERATE SCAN STRING FOR FILES DESCRIPTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
par.scans_string = {};
if isfield(par, 'output_folder_prefix') && ~isempty(par.output_folder_prefix)
par.scans_string{end+1} = par.output_folder_prefix;
end
if ~isempty(par.tomo_id)
auxstr = repmat('%i+',1,length(par.tomo_id));
par.scans_string{end+1} = sprintf(['id_',auxstr(1:end-1)], par.tomo_id);
elseif par.online_tomo
par.scans_string{end+1} = sprintf('S%05d',scans(1));
end
%{
% load sample name if provided
if ~isfield(p, 'samplename')
par.samplename = '';
else
par.samplename = p.samplename;
end
if ~isempty(par.samplename)
par.scans_string{end+1} = par.samplename;
end
%}
if ~par.online_tomo
par.scans_string{end+1}= sprintf('S%05d_to_S%05d',scans(1),scans(end));
end
par.scans_string = join(par.scans_string, '_');
par.scans_string = par.scans_string{1};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Output folder
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
par.output_folder= {fullfile(par.output_path, 'tomo'), par.scans_string, par.filesuffix, par.fileprefix};
if par.online_tomo
par.output_folder{end+1}= 'online';
end
par.output_folder = join(par.output_folder, '_');
par.output_folder = par.output_folder{1};
%{
if ~debug()
utils.verbose('Output folder: %s', par.output_folder)
if ~exist(par.output_folder,'dir')
mkdir(par.output_folder);
end
[~,attr] = fileattrib(par.output_folder);
if ~(attr.UserWrite || attr.GroupWrite)
error('Output path %s is not writable', par.output_folder)
end
% For website
subdir_online = fullfile(par.base_path,'analysis/online/tomo/');
if ~exist(subdir_online,'dir')
mkdir(subdir_online);
end
par.online_tomo_path = sprintf('%sonline_tomo_S%05d', subdir_online, min(par.scanstomo));
end
%}
end
+251
View File
@@ -0,0 +1,251 @@
% INITIALIZE_TOMO_MATLAB basic initialization steps of tomography -> check validity of the inputs,
% load first projection and store its parameters, check angles, create output folders
% Created by YJ Based on PSI's function
% [par, angles_check, object] = initialize_tomo_aps(par, scans, use_gpu, object_preprocess_fun)
%
% Inputs:
% **par - basic parameters defined in template
% **scans - list of the scans to be loaded
% **use_gpu - (bool), dont use GPU if use_gpu == 0, (default = true )
% **object_preprocess_fun - user defined preprocessing function applied on the loaded projections, e.g. in laminography it can be rotation, default = @(x)x
%
% *returns*
% ++par updated basic parameters
% ++angles_check
% ++object example of one loaded projection
function [par, angles_check, object] = initialize_tomo_matlab(par, scans, use_gpu, object_preprocess_fun)
import ptycho.*
import io.*
utils.verbose(struct('prefix', 'initialize'))
%% initial checks
if verLessThan('matlab', '9.3')
warning on
warning('Only Matlab versions >= 2018a are tested and supported, \nYour Matlab version is %s', version)
pause(5)
end
if nargin < 3
use_gpu = true;
end
if gpuDeviceCount == 0 && use_gpu
warning('Using CUDA enabled GPU is strongly recommended')
pause(5)
use_gpu = false;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% CHECK GPU AVAILIBILITY %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if use_gpu
if gpuDeviceCount == 0
error('Code needs CUDA enabled GPU, suppress by setting input parameter "use_gpu=false" ')
end
if any(par.GPU_list > gpuDeviceCount)
error('Selected GPU in GPU_list is not available')
end
gpu = gpuDevice(par.GPU_list(1));
if ~verLessThan('matlab', '9.4') && gpu.ToolkitVersion < 9
error('Code needs CUDA 9.0 to work with Matlab 2018a and newer')
elseif verLessThan('matlab', '9.4') && gpu.ToolkitVersion < 8
error('Code needs at least CUDA 8.0 to work with Matlab 2017b')
end
fprintf('=================================================== \n')
fprintf('=== Available memory for GPU %i : %2.1fGB / %2.1fGB === \n', gpu.Index, gpu.AvailableMemory/1e9, gpu.TotalMemory/1e9)
fprintf('=================================================== \n')
% check that more than 3GB of GPU mem is free and that 90% of total
% memory is available -> make sure that this template is the only
% process using the selected GPU
reset(gpu)
if ~debug() && (gpu.AvailableMemory < gpu.TotalMemory * par.check_gpu_percentage || gpu.AvailableMemory < 3e9)
utils.verbose(0,'\n\n=============== GPU report ================')
!nvidia-smi
warning on
warning off backtrace
if gpu.AvailableMemory < gpu.TotalMemory * 0.9
warning(['Memory in GPU %i (Nvidia id:%i) is probably used by other user,'...
'try to manunally choose GPU or kill other processes'], gpu.Index, gpu.Index-1)
else
warning(['Memory in GPU %i (Nvidia id:%i) is less than recommended 3GB,'...
'try to manunally choose GPU or kill other processes'], gpu.Index, gpu.Index-1)
end
warning on
% check who is using the GPU
utils.report_GPU_usage(gpu.Index);
if ~debug() && ~par.online_tomo
if ~strcmpi(input('Do you want to continue [y/N]', 's'), 'y')
error('Set other GPU to use by par.GPU_list parameter')
end
end
% this is only recommende value, the code should run even with
% less, but then it gets less efficient.
elseif (gpu.AvailableMemory < gpu.TotalMemory * 0.9 || gpu.AvailableMemory < 3e9)
%utils.report_GPU_usage
end
end
if nargin < 4
object_preprocess_fun = []; % no preprocessing function
end
par.use_GPU = use_gpu; % store user preferences in using GPU
%{
%% First check if reconstructions exist
proj_file_names = {};
hasRecon = zeros(length(scans),1);
for ii=1:length(scans)
progressbar(ii, length(scans))
file = find_ML_recon_files_names(par, scans(ii)); %find ML recon outputs
if ~isempty(file)
hasRecon(ii) = 1;
end
end
%}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% initial values - LOAD ONE FRAME FOR DEFINING PTYCHO SCAN VALUES %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
file = [];
ii = 1;
while isempty(file) && ii <= length(scans)
%file = find_projection_files_names_aps(par, scans(ii));
file = find_ML_recon_files_names(par, scans(ii)); %find ML recon outputs
if isempty(file)
%warning(['Out of luck - Reconstruction not found']);
disp(['Out of luck - Reconstruction not found']);
ii = ii+1;
else
break
end
end
if isempty(file)
error('No reconstructions found, check that analysis folder path contains scans %i-%i', min(scans), max(scans))
end
%disp(file)
%%% Read first projection to check size and reconstruction parameters
display(['Reading file: ' file])
[object, probe, dx_spec] = load_aps_ML_recons(file);
if iscell(probe)
probe = single(probe{1}(:,:,1)); % keep only the first mode
else
probe = single(probe(:,:,1)); % keep only the first mode
end
par.asize = size(probe); % probe size
par.dims_ob_loaded = [size(object,1), size(object,2)]; % load the sizes directly from the object, note that "object_preprocess_fun" can crop/rotate the image !!
%{
if isfield(p, 'scanindexrange')
p.scanidxs{1} = p.scanindexrange(1):p.scanindexrange(2);
positions = int32(p.positions(p.scanidxs{1},:));
indices = int32(1:length(p.scanidxs{1}));
% get at least some estimation of the illumination intensity for different regions in the
% projection
par.illum_sum = utils.add_to_3D_projection(abs(probe).^2,zeros(max(p.object_size,[],1),'single'),positions,indices, true);
else
% if nto availible, get et least a crude guess
par.illum_sum = ones(par.dims_ob_loaded-par.asize);
end
%}
par.illum_sum = ones(par.dims_ob_loaded-par.asize);
par.illum_sum = utils.crop_pad(par.illum_sum,par.dims_ob_loaded);
par.illum_sum = par.illum_sum ./ quantile(par.illum_sum(:), 0.9); % normalize the values to keep maximum around 1
% in case of unequal pixel size
if dx_spec(1) ~= dx_spec(2)
% upsample the data in the dimennsion with lower resolution (-> at least relax issues in tomography interpolation)
pixel_scale = dx_spec ./ min(dx_spec) ;
dims_ob_new = round(par.dims_ob_loaded .* pixel_scale);
par.illum_sum = max(0,real(utils.interpolateFT(par.illum_sum, dims_ob_new)));
object = utils.interpolateFT(par.illum_sum, dims_ob_new);
par.asize = round(par.asize .* pixel_scale);
probe = utils.interpolateFT(probe, par.asize);
dx_spec(:) = min(dx_spec);
end
if ~isempty(object_preprocess_fun)
% apply custom preprocessing, e.g. rotation and flipping for
% laminography setup
object = object_preprocess_fun(object);
par.illum_sum = max(0, object_preprocess_fun(par.illum_sum));
end
par.dims_ob = [size(object,1), size(object,2)]; % object size after object_preprocess_fun
par.probe = probe;
if ~isfield(par, 'lambda')
par.lambda = p.lambda; % wavelength [m]
end
par.pixel_size=dx_spec(1) * 2^par.downsample_projections; % reconstructed pixel size [m]
if dx_spec(1)~=dx_spec(2)
warning('Pixel size not symmetric - This code cannot handle')
end
par.factor=par.lambda/(2*pi*par.pixel_size);
par.factor_edensity = 1e-30*2*pi/(par.lambda^2*2.81794e-15);
%{
%%% Check angles %%%
if par.checkangles
[par.scans_check, angles_check] = tomo_angles(projections, subtomograms, ...
scan_num, subs_to_do); % ignores the repeated 180deg scan.
else
angles_check = [];
end
%}
angles_check = [];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% GENERATE SCAN STRING FOR FILES DESCRIPTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
par.scans_string = {};
if isfield(par, 'output_folder_prefix') && ~isempty(par.output_folder_prefix)
par.scans_string{end+1} = par.output_folder_prefix;
end
if ~isempty(par.tomo_id)
auxstr = repmat('%i+',1,length(par.tomo_id));
par.scans_string{end+1} = sprintf(['id_',auxstr(1:end-1)], par.tomo_id);
elseif par.online_tomo
par.scans_string{end+1} = sprintf('S%05d',scans(1));
end
%{
% load sample name if provided
if ~isfield(p, 'samplename')
par.samplename = '';
else
par.samplename = p.samplename;
end
if ~isempty(par.samplename)
par.scans_string{end+1} = par.samplename;
end
%}
if ~par.online_tomo
par.scans_string{end+1}= sprintf('S%05d_to_S%05d',scans(1),scans(end));
end
par.scans_string = join(par.scans_string, '_');
par.scans_string = par.scans_string{1};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Output folder
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
par.output_folder= {fullfile(par.output_path, 'tomo'), par.scans_string, par.filesuffix, par.fileprefix};
if par.online_tomo
par.output_folder{end+1}= 'online';
end
par.output_folder = join(par.output_folder, '_');
par.output_folder = par.output_folder{1};
end
+146
View File
@@ -0,0 +1,146 @@
% LOAD_ANGLES load tomopgrahy angles for given scan numbers or tomo_id
%
% [par, angles] = load_angles(par, scans, tomo_id, plot_angles)
% Inputs:
% **par tomo parameter structure
% **scans - list of loaded scan numbers
% **tomo_id - indetification number of the sample, default = []
% **plot_angles - plot loaded angles, default == true
% *returns*
% ++par tomo parameter structure
% ++angles loaded angles
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [par, angles] = load_angles(par, scans, tomo_id, plot_angles)
if nargin < 4
plot_angles = true;
end
warning on
if nargin < 3
tomo_id = [];
end
Nscans = length(scans);
angles = nan(Nscans,1);
if ~par.use_OMNY_file_angles
S = io.spec_read(par.base_path,'ScanNr',scans);
for ii = 1:Nscans
angles(ii)=S{ii}.samroy;
end
else
[S, errflag] = beamline.read_omny_angles(par.OMNY_angle_file,scans, tomo_id);
if errflag
disp(['Not all scans found in ' par.OMNY_angle_file])
disp(['I will remove the angles not found and show you some plots anyway'])
end
angles=S.readout_angle(:).';
scans = S.scan(:).';
subtomos = S.subtomo_num(:).';
if isfield(S,'tomo_id')
if any(S.tomo_id ~= S.tomo_id(1))
warning('tomo_id number is not the same for all scans')
end
par.tomo_id = unique(S.tomo_id);
else
par.tomo_id = [] ;
end
par.sample_name = S.sample_name{1};
end
% remove duplicted scan numbers
[~,ind] = unique(scans, 'last'); % take the !last! occurence of the scan, assume that the second measurement was better
angles = angles(ind); % Angles not repeated in scan
scans = scans(ind);
subtomos = subtomos(ind);
% take only unique angles, measure uniqueness
if par.remove_duplicated_angles
[~,ind] = unique(angles, 'last'); % take the !last! occurence of the angle, assume that the second measurement was better
if length(angles) ~= length(ind)
warning('Removed %i duplicated angles', length(angles) - length(ind))
end
else
[~,ind] = sort(angles);
end
angles = angles(ind); % Angles not repeated in scan
scans = scans(ind);
subtomos = subtomos(ind);
if isfield(par,'angle_offset') && par.angle_offset ~=0
angles = angles + par.angle_offset; % avoid the angles to be too well aligned with pixels, ie avoid exact angles 0, 90, 180, ...
end
par.scanstomo = scans;
par.subtomos = subtomos;
par.num_proj=numel(par.scanstomo);
[anglessort,indsortangle] = sort(angles);
if par.sort_by_angle
angles = angles(indsortangle);
par.scanstomo = par.scanstomo(indsortangle);
par.subtomos = par.subtomos(indsortangle);
else % sort by scan number
[~,indsortscan] = sort( par.scanstomo);
angles = angles(indsortscan);
par.scanstomo = par.scanstomo(indsortscan);
par.subtomos = par.subtomos(indsortscan);
end
if par.verbose_level && plot_angles
plotting.smart_figure(1);
subplot(2,1,1)
plot(par.scanstomo,angles,'ob'); grid on;
%par.scanstomo(1)
%par.scanstomo(end)
xlim(par.scanstomo([1,end]))
legend('Spec angles')
xlabel('Scan #')
subplot(2,1,2)
plot(diff(anglessort))
title('Angular spacing'); grid on;
xlim([1,par.num_proj-1])
if par.windowautopos
screensize = get( groot, 'Screensize' );
win_size = [946 815];
set(gcf,'Outerposition',[139 min(163,screensize(4)-win_size(2)) win_size]); %[left, bottom, width, height]
end
title('Measured angles')
drawnow
end
end
+148
View File
@@ -0,0 +1,148 @@
% LOAD_ANGLES_APS load tomopgrahy angles for given scan numbers
% Directly load from original master .h5 files
% created by YJ based on PSI's function
% [par, angles] = load_angles_aps(par, scans, plot_angles)
% Inputs:
% **par tomo parameter structure
% **scans - list of loaded scan numbers
% **tomo_id - indetification number of the sample, default = []
% **plot_angles - plot loaded angles, default == true
% *returns*
% ++par tomo parameter structure
% ++angles loaded angles
function [par, angles] = load_angles_aps(par, scans, plot_angles)
warning on
Nscans = length(scans);
angles = nan(Nscans,1);
hasAngle = ones(Nscans,1);
if isfield(par,par.angle.filesuffix) && ~isempty(par.angle.filesuffix)
file_suffix = par.angle.filesuffix;
else
file_suffix = '_master.h5'; %default for velociprobe data outputs
end
if isfield(par.angle,'h5path') && ~isempty(par.angle.h5path)
h5path = par.angle.h5path;
else
h5path = '/entry/sample/goniometer/chi_start';
end
%%
wb = waitbar(0,'1','Name','Loading ptycho-tomo projection angles...',...
'CreateCancelBtn','setappdata(gcbf,''canceling'',1)');
setappdata(wb,'canceling',0);
for i=1:Nscans
% Check for clicked Cancel button
if getappdata(wb,'canceling')
break
end
filename = strcat(par.base_path, 'ptycho/',sprintf(par.scan_string_format, scans(i)),'/',sprintf(par.scan_string_format, scans(i)),file_suffix);
if ~isempty(filename)
try
angle_temp = h5read(filename,h5path);
angles(i) = angle_temp(1);
status = [sprintf(par.scan_string_format, scans(i)), ' angle = ',num2str(angles(i))];
catch
disp(['Reading angle failed for ', sprintf(par.scan_string_format, scans(i))]);
disp(strcat('Check angle h5path:',h5path))
disp(filename)
status = ['Reading angle failed for ', sprintf(par.scan_string_format, scans(i))];
end
else
hasAngle(i) = 0;
disp(['No angle found for ',sprintf(par.scan_string_format, scans(i))])
status = ['No angle found for ',sprintf(par.scan_string_format, scans(i))];
end
% Update waitbar and message
%waitbar(i/Nscans,wb,sprintf(par.scan_string_format, scans(i)))
waitbar(i/Nscans,wb,status)
end
delete(wb)
% legacy code - read angles from processed h5 files
%{
for i=1:Nscans
file = find_projection_files_names_aps(par, scans(i));
if ~isempty(file)
angles(i) = h5read(file,'/angle');
else
hasAngle(i) = 0;
disp(strcat('No angle found for scan ',num2str(scans(i))))
end
end
%}
%% process angles
% remove scan without angle
angles = angles(hasAngle==1);
scans = scans(hasAngle==1);
% remove duplicted scan numbers
[~,ind] = unique(scans, 'last'); % take the !last! occurence of the scan, assume that the second measurement was better
angles = angles(ind); % Angles not repeated in scan
scans = scans(ind);
%subtomos = subtomos(ind);
% take only unique angles, measure uniqueness
if par.remove_duplicated_angles
[~,ind] = unique(angles, 'last'); % take the !last! occurence of the angle, assume that the second measurement was better
if length(angles) ~= length(ind)
warning('Removed %i duplicated angles', length(angles) - length(ind))
end
else
[~,ind] = sort(angles);
end
angles = angles(ind); % Angles not repeated in scan
scans = scans(ind);
%subtomos = subtomos(ind);
if isfield(par,'angle_offset') && par.angle_offset ~=0
angles = angles + par.angle_offset; % avoid the angles to be too well aligned with pixels, ie avoid exact angles 0, 90, 180, ...
end
par.scanstomo = scans;
%par.subtomos = subtomos;
par.num_proj=numel(par.scanstomo);
[anglessort,indsortangle] = sort(angles);
if par.sort_by_angle
angles = angles(indsortangle);
par.scanstomo = par.scanstomo(indsortangle);
%par.subtomos = par.subtomos(indsortangle);
else % sort by scan number
[~,indsortscan] = sort( par.scanstomo);
angles = angles(indsortscan);
par.scanstomo = par.scanstomo(indsortscan);
%par.subtomos = par.subtomos(indsortscan);
end
if par.verbose_level && plot_angles
plotting.smart_figure(1);
subplot(2,1,1)
plot(par.scanstomo,angles,'ob'); grid on;
xlim(par.scanstomo([1,end]))
%legend('Tilt angles')
xlabel('Scan #')
ylabel('Tilt angles')
subplot(2,1,2)
plot(diff(anglessort))
ylabel('Angle increment')
%title('Angular spacing');
grid on;
xlim([1,par.num_proj-1])
if par.windowautopos
screensize = get( groot, 'Screensize' );
win_size = [946 815];
set(gcf,'Outerposition',[139 min(163,screensize(4)-win_size(2)) win_size]); %[left, bottom, width, height]
end
title('Measured angles')
drawnow
end
end
+148
View File
@@ -0,0 +1,148 @@
% LOAD_ANGLES_APS load tomopgrahy angles for given scan numbers
% Directly load from original mda files. useful for BNP
% created by YJ based on PSI's function
% [par, angles] = load_angles(par, scans, plot_angles)
% Inputs:
% **par tomo parameter structure
% **scans - list of loaded scan numbers
% **tomo_id - indetification number of the sample, default = []
% **plot_angles - plot loaded angles, default == true
% *returns*
% ++par tomo parameter structure
% ++angles loaded angles
function [par, angles] = load_angles_aps_bnp(par, scans, plot_angles)
warning on
Nscans = length(scans);
angles = nan(Nscans,1);
hasAngle = ones(Nscans,1);
%{
if isfield(par,par.angle.filesuffix) && ~isempty(par.angle.filesuffix)
file_suffix = par.angle.filesuffix;
else
file_suffix = '.mda'; %default for velociprobe data outputs
end
%}
file_suffix = '.mda'; %default for velociprobe data outputs
%%
wb = waitbar(0,'1','Name','Loading ptycho-tomo projection angles...',...
'CreateCancelBtn','setappdata(gcbf,''canceling'',1)');
setappdata(wb,'canceling',0);
for i=1:Nscans
% Check for clicked Cancel button
if getappdata(wb,'canceling')
break
end
scan_string_format = 'bnp_fly%04d';
%disp(scans(i))
filename = strcat(par.base_path, 'mda/',sprintf(scan_string_format, scans(i)),file_suffix);
if ~isempty(filename)
try
%disp(filename)
xx=mdaload(filename);
a=(getfield(getfield(xx,'extra'),'pvs'));
angles(i) = getfield(a(9),'values');
status = [sprintf(par.scan_string_format, scans(i)), ' angle = ',num2str(angles(i))];
catch
disp(['Reading angle failed for ', sprintf(par.scan_string_format, scans(i))]);
%disp(strcat('Check angle h5path:',h5path))
status = ['Reading angle failed for ', sprintf(par.scan_string_format, scans(i))];
end
else
hasAngle(i) = 0;
disp(['No angle found for ',sprintf(par.scan_string_format, scans(i))])
status = ['No angle found for ',sprintf(par.scan_string_format, scans(i))];
end
% Update waitbar and message
%waitbar(i/Nscans,wb,sprintf(par.scan_string_format, scans(i)))
waitbar(i/Nscans,wb,status)
end
delete(wb)
% legacy code - read angles from processed h5 files
%{
for i=1:Nscans
file = find_projection_files_names_aps(par, scans(i));
if ~isempty(file)
angles(i) = h5read(file,'/angle');
else
hasAngle(i) = 0;
disp(strcat('No angle found for scan ',num2str(scans(i))))
end
end
%}
%% process angles
% remove scan without angle
angles = angles(hasAngle==1);
scans = scans(hasAngle==1);
% remove duplicted scan numbers
[~,ind] = unique(scans, 'last'); % take the !last! occurence of the scan, assume that the second measurement was better
angles = angles(ind); % Angles not repeated in scan
scans = scans(ind);
%subtomos = subtomos(ind);
% take only unique angles, measure uniqueness
if par.remove_duplicated_angles
[~,ind] = unique(angles, 'last'); % take the !last! occurence of the angle, assume that the second measurement was better
if length(angles) ~= length(ind)
warning('Removed %i duplicated angles', length(angles) - length(ind))
end
else
[~,ind] = sort(angles);
end
angles = angles(ind); % Angles not repeated in scan
scans = scans(ind);
%subtomos = subtomos(ind);
if isfield(par,'angle_offset') && par.angle_offset ~=0
angles = angles + par.angle_offset; % avoid the angles to be too well aligned with pixels, ie avoid exact angles 0, 90, 180, ...
end
par.scanstomo = scans;
%par.subtomos = subtomos;
par.num_proj=numel(par.scanstomo);
[anglessort,indsortangle] = sort(angles);
if par.sort_by_angle
angles = angles(indsortangle);
par.scanstomo = par.scanstomo(indsortangle);
%par.subtomos = par.subtomos(indsortangle);
else % sort by scan number
[~,indsortscan] = sort( par.scanstomo);
angles = angles(indsortscan);
par.scanstomo = par.scanstomo(indsortscan);
%par.subtomos = par.subtomos(indsortscan);
end
if par.verbose_level && plot_angles
plotting.smart_figure(1);
subplot(2,1,1)
plot(par.scanstomo,angles,'ob'); grid on;
xlim(par.scanstomo([1,end]))
%legend('Tilt angles')
xlabel('Scan #')
ylabel('Tilt angles')
subplot(2,1,2)
plot(diff(anglessort))
ylabel('Angle increment')
%title('Angular spacing');
grid on;
xlim([1,par.num_proj-1])
if par.windowautopos
screensize = get( groot, 'Screensize' );
win_size = [946 815];
set(gcf,'Outerposition',[139 min(163,screensize(4)-win_size(2)) win_size]); %[left, bottom, width, height]
end
title('Measured angles')
drawnow
end
end
+401
View File
@@ -0,0 +1,401 @@
% LOAD_PROJECTIONS load reconstructed projections from disk to RAM
%
% [stack_object, theta,num_proj, par] = load_projections(par, exclude_scans, dims_ob, theta, custom_preprocess_fun)
%
% Inputs:
% **par - parameter structure
% **exclude_scans - list of scans to be excluded from loading, [] = none
% **dims_ob - dimension of the object
% **theta - angles of the scans
% **custom_preprocess_fun - function to be applied on the loaded data, eg cropping , rotation, etc
%
% *returns*
% ++stack_object - loaded complex-valued projections
% ++theta - angles corresponding to the loaded projections, angles for missing projections are removed
% ++num_proj - number of projections
% ++par - updated parameter structure
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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, theta,num_proj, par] = load_projections(par, exclude_scans, dims_ob, theta, custom_preprocess_fun)
import ptycho.*
import utils.*
import io.*
import plotting.*
if nargin < 5
custom_preprocess_fun = [];
end
if ~isempty(custom_preprocess_fun) && ishandle(custom_preprocess_fun) && ~strcmpi(func2str(custom_preprocess_fun), '@(x)x')
custom_preprocess_fun = [] ;
end
scanstomo = par.scanstomo;
% avoid loading scans listed in 'exclude_scans'
if ~isempty(exclude_scans)
ind = ismember(scanstomo, exclude_scans);
scanstomo(ind) = [];
theta(ind) = [];
end
% % plot average vibrations for each of the laoded projections
% disp('Checking stability of the projections')
% poor_projections = prepare.plot_sample_stability(par, scanstomo, ~par.online_tomo, par.pixel_size);
% if sum(poor_projections) && ...
% (par.online_tomo || ~strcmpi(input(sprintf('Remove %i low stability projections: [Y/n]\n',sum(poor_projections)), 's'), 'n') )
% theta(poor_projections) = [];
% scanstomo(poor_projections) = [];
% else
% disp('All projections are fine')
% end
verbose(1,'Checking available files')
missing_scans = [];
for num = 1:length(scanstomo)
progressbar(num, length(scanstomo))
proj_file_names{num} = find_ptycho_filename(par.analysis_path,scanstomo(num),par.fileprefix,par.filesuffix, par.file_extension);
if isempty(proj_file_names{num})
missing_scans(end+1) = scanstomo(num);
end
end
verbose(par.verbose_level); % return to original settings
figure(1)
subplot(2,1,1)
hold on
plot(missing_scans, theta(ismember(scanstomo, missing_scans)), 'rx')
hold off
legend({'Measured angles', 'Missing projections'})
axis tight
if ~isempty(missing_scans)
ind = ismember(scanstomo, missing_scans);
verbose(1,['Scans not found are ' num2str(missing_scans)])
verbose(1,['Projections not found are ' num2str(find(ind))])
scanstomo(ind) = [];
theta(ind) = [];
proj_file_names(ind) = [];
else
verbose(1,'All projections found')
end
num_proj = length(scanstomo);
if isfield(par, 'fp16_precision') && par.fp16_precision
% use uint32 to store half floar precision data
stack_object=zeros(dims_ob(1),dims_ob(2),num_proj, 'like', fp16.set(1i));
else
stack_object=zeros(dims_ob(1),dims_ob(2),num_proj, 'like', single(1i));
end
pixel_scale =zeros(num_proj,2);
energy = zeros(num_proj,1);
tic
if num_proj == 0
verbose(0, 'No new projections loaded')
return
end
which_missing = false(1,num_proj); % Include here INDEX numbers that you want to exclude (bad reconstructions)
%{
%% prepare parpool
% pool = gcp('nocreate');
% if isempty(pool) || pool.NumWorkers < par.Nworkers
% delete(pool);
% pool = parpool(par.Nworkers);
% end
% pool.IdleTimeout = 600; % set idle timeout to 10 hours
%
% load at least 10 frames per worker to use well the resources
block_size = max(1, par.Nworkers)*50;
%% load data, use parfor but process blockwise to avoid lare memory use
for block_id = 1:ceil(num_proj/block_size)
block_inds = 1+(block_id-1)*block_size: min(num_proj, block_id*block_size);
verbose(1,'===== Block %i / %i started ===== ', block_id, ceil(num_proj/block_size))
utils.check_available_memory
stack_object_block = zeros(dims_ob(1),dims_ob(2),length(block_inds), 'like', stack_object);
share_mem = shm(true);
share_mem.allocate(stack_object_block);
share_mem.detach();
% ticBytes(gcp);
%% start a smaller block in parallel
% parfor(num = block_inds,par.Nworkers)
% if parfor fails, try normal loop
for num = block_inds
file = proj_file_names{num};
if ismember(scanstomo(num), exclude_scans)
warning(['Skipping by user request: ' file{1}])
continue % skip the frames that are listed in exclude_scans
end
if ~iscell(file)
file = {file}; % make them all cells
end
object= [];
for jj = length(file):-1:1
disp(['Reading file: ' file{jj}])
% if more than one file is present, try to load the first last one that
% does not fail
try
object = load_ptycho_recons(file{jj}, 'object');
object = single(object.object);
object = prod(object,4); % use only the eDOF object if multiple layers are available
pixel_scale(num,:) = io.HDF.hdf5_load(file{jj}, '/reconstruction/p/dx_spec');
energy(num) = io.HDF.hdf5_load(file{jj}, '/reconstruction/p/energy');
break
end
end
if isempty(object) || all(object(:) == 0 )
which_missing(num) = true;
warning(['Loading failed: ' [file{:}]])
continue
end
if ~isempty(custom_preprocess_fun)
object = custom_preprocess_fun(object);
end
nx = dims_ob(2);
ny = dims_ob(1);
if size(object,2) > nx
object = object(:,1:nx);
elseif size(object,2) < nx
object = padarray(object,[0 nx-size(object,2)],'post');
end
if size(object,1) > ny
if par.auto_alignment|| par.get_auto_calibration
object = object(1:ny,:);
else
shifty = floor((size(object,1)-ny)/2);
object = object([1:ny]+shifty,:);
end
elseif size(object,1) < ny
if par.auto_alignment||par.get_auto_calibration
object = padarray(object,[ny-size(object,1) 0],'post');
else
shifty = (ny-size(object,1))/2;
object = padarray(object,[ny-size(object,1)-floor(shifty) 0],'post');
object = padarray(object,[floor(shifty) 0],'pre');
end
end
% if par.showrecons
% mag=a+bs(object);
% phase=angle(object);
% figure(1); clf
% imagesc(mag); axis xy equal tight ; colormap bone(256); colorbar;
% title(['object magnitude S',sprintf('%05d',ii),', Projection ' ,sprintf('%03d',num) , ', Theta = ' sprintf('%.2f',theta(num)), ' degrees']);drawnow;
% set(gcf,'Outerposition',[601 424 600 600])
% figure(2); imagesc(phase); axis xy equal tight; colormap bone(256); colorbar;
% title(['object phase S',sprintf('%05d',ii),', Projection ' ,sprintf('%03d',num) , ', Theta = ' sprintf('%.2f',theta(num)), ' degrees']);drawnow;
% set(gcf,'Outerposition',[1 424 600 600]) %[left, bottom, width, height
% figure(3); % imagesc3D(probe);
% axis xy equal tight
% set(gcf,'Outerposition',[600 49 375 375]) %[left, bottom, width, height
% figure(4);
% if isfield(p, 'err')
% loglog(p.err);
% elseif isfield(p, 'mlerror')
% loglog(p.mlerror)
% elseif isfield(p, 'error_metric')
% loglog(p.error_metric(2).iteration,p.error_metric(2).value)
% end
% title(sprintf('Error %03d',num))
% set(gcf,'Outerposition',[1 49 600 375]) %[left, bottom, width, height
% drawnow;
% end
if isfield(par, 'fp16_precision') && par.fp16_precision
% convert data to fp16 precision
object = fp16.set(object);
end
% keyboard
% write loaded object to a small block of shared memory, avoid using
% parpool data transfer
share_mem_tmp = share_mem;
[share_mem_tmp, share_mem_object] = share_mem_tmp.attach();
tomo.set_to_array(share_mem_object, object, num - block_inds(1));
share_mem_tmp.detach();
end % enf of parfor
% tocBytes(gcp);
tic
verbose(1,'Writting to shared stack_object')
[share_mem, stack_object_block] = share_mem.attach();
% write loaded block to the full array, avoid memory reallocation
tomo.set_to_array(stack_object, stack_object_block, block_inds-1);
share_mem.free();
toc
end
%}
verbose(1, 'Data loaded')
verbose(1, 'Find residua')
[Nx, Ny, Nprojections] = size(stack_object);
object_ROI = {ceil(1+par.asize(1)/2:Nx-par.asize(1)/2),ceil(1+par.asize(2)/2:Ny-par.asize(2)/2)};
residua = tomo.block_fun(@(x)(squeeze(math.sum2(abs(utils.findresidues(x))>0.1))),stack_object, struct('ROI', {object_ROI}));
max_residua = 100;
poor_projections = (residua(:)' > max_residua) & ~par.is_laminography ; % ignore in the case of laminography
if any(poor_projections)
verbose(1, 'Found %i/%i projections with more than %i residues ', sum(poor_projections), Nprojections, max_residua)
end
if any(which_missing & ~ismember(scanstomo, exclude_scans) )
missing = find(which_missing & ~ismember(scanstomo, exclude_scans));
verbose(1,['Projections not found are ' num2str(missing)])
verbose(1,['Scans not found are ' num2str(scanstomo(missing))])
else
verbose(1,'All projections loaded')
end
toc
% avoid also empty projections
which_wrong = poor_projections | squeeze(math.sum2(stack_object)==0)';
if any(which_wrong & ~ismember(scanstomo, exclude_scans) )
wrong = find(which_wrong & ~ismember(scanstomo, exclude_scans));
verbose(1,['Projections failed are ' num2str(wrong)])
verbose(1,['Scans failed are ' num2str(scanstomo(wrong))])
else
verbose(1,'All loaded projections are OK')
end
%%% Getting rid of missing projections %%%
which_remove = which_missing | which_wrong;
if any(which_remove)
if par.online_tomo || ~strcmpi(input(sprintf('Do you want remove %i missing/wrong projections and keep going (Y/n)?',sum(which_remove)),'s'),'n')
disp('Removing missing/wrong projections. stack_object, scanstomo, theta and num_proj are modified')
stack_object(:,:,which_remove) = [];
scanstomo(which_remove)=[];
theta(which_remove)=[];
pixel_scale(which_remove,:) = [];
energy(which_remove,:) = [];
disp('Done')
else
disp('Keeping empty spaces for missing projections. Problems are expected if you continue.')
end
end
par.scanstomo = scanstomo;
par.num_proj=numel(scanstomo);
pixel_scale = pixel_scale ./ mean(pixel_scale);
assert(par.num_proj > 0, 'No projections loaded')
if all(all(abs(pixel_scale)-1 < 1e-6)) || ~any(isfinite(mean(pixel_scale)))
%if all datasets have the same pixel scale
pixel_scale = [1,1];
else
warning('Datasets do not have equal pixel sizes, auto-rescaling projections')
% use FFT base rescaling -> apply illumination function first to remove
% effect of the noise out of the reconstruction region
rot_fun = @(x,sx,sy)(utils.imrescale_frft(x .* par.illum_sum, sx, sy)) ./ ( max(0,utils.imrescale_frft(par.illum_sum,sx,sy))+1e-2*max(par.illum_sum(:)));
stack_object = tomo.block_fun(rot_fun,stack_object, pixel_scale(:,1),pixel_scale(:,2));
pixel_scale = [1,1];
end
par.pixel_scale = pixel_scale;
par.energy = energy;
%% clip the projections ampltitude by quantile filter
if par.clip_amplitude_quantile < 1
MAX = quantile(reshape(abs(fp16.get(stack_object(1:10:end,1:10:end,:))), [], par.num_proj), par.clip_amplitude_quantile ,1);
MAX = reshape(MAX,1,1,par.num_proj);
clip_fun = @(x,M)(min(abs(x),M) .* x ./ (abs(x) + 1e-5));
stack_object = tomo.block_fun(clip_fun,stack_object, MAX, struct('use_GPU', true));
end
if size(stack_object,3) ~= length(theta) || length(theta) ~= par.num_proj
error('Inconsistency between number of angles and projections')
end
if ~isempty(par.tomo_id) && all(par.tomo_id > 0)
% sanity safety check, all loaded angles correpont to the stored angles
[~,theta_test] = prepare.load_angles(par, par.scanstomo, [], false);
if max(abs(theta - theta_test)) > 180/par.num_proj/2
error('Some angles have angles different from expected')
end
end
end
+568
View File
@@ -0,0 +1,568 @@
% LOAD_PROJECTIONS_APS load reconstructed projections from disk to RAM
% created by YJ based on PSI's function
% [stack_object, theta,num_proj, par] = load_projections(par, exclude_scans, dims_ob, theta, custom_preprocess_fun)
%
% Inputs:
% **par - parameter structure
% **exclude_scans - list of scans to be excluded from loading, [] = none
% **dims_ob - dimension of the object
% **theta - angles of the scans
% **custom_preprocess_fun - function to be applied on the loaded data, eg cropping , rotation, etc
%
% *returns*
% ++stack_object - loaded complex-valued projections
% ++theta - angles corresponding to the loaded projections, angles for missing projections are removed
% ++num_proj - number of projections
% ++par - updated parameter structure
function [stack_object, theta,num_proj, par] = load_projections_aps(par, exclude_scans, dims_ob, theta, custom_preprocess_fun)
import ptycho.*
import utils.*
import io.*
import plotting.*
if nargin < 5
custom_preprocess_fun = [];
end
if ~isempty(custom_preprocess_fun) && ishandle(custom_preprocess_fun) && ~strcmpi(func2str(custom_preprocess_fun), '@(x)x')
custom_preprocess_fun = [] ;
end
scanstomo = par.scanstomo;
% avoid loading scans listed in 'exclude_scans'
if ~isempty(exclude_scans)
ind = ismember(scanstomo, exclude_scans);
scanstomo(ind) = [];
theta(ind) = [];
end
% % plot average vibrations for each of the laoded projections
% disp('Checking stability of the projections')
% poor_projections = prepare.plot_sample_stability(par, scanstomo, ~par.online_tomo, par.pixel_size);
% if sum(poor_projections) && ...
% (par.online_tomo || ~strcmpi(input(sprintf('Remove %i low stability projections: [Y/n]\n',sum(poor_projections)), 's'), 'n') )
% theta(poor_projections) = [];
% scanstomo(poor_projections) = [];
% else
% disp('All projections are fine')
% end
verbose(1,'Checking available files')
missing_scans = [];
proj_file_names = {};
proj_recon_method = {};
proj_roi = {};
proj_scanNo = {};
for num = 1:length(scanstomo)
progressbar(num, length(scanstomo))
%proj_file_names{num} = find_ptycho_filename(par.analysis_path,scanstomo(num),par.fileprefix,par.filesuffix, par.file_extension);
%proj_file_names{num} = find_projection_files_names_aps(par, scanstomo(num));
[proj_file_names{num},proj_recon_method{num},proj_roi{num},proj_scanNo{num}] = find_ML_recon_files_names(par, scanstomo(num));
%disp(proj_file_names{num})
if isempty(proj_file_names{num})
missing_scans(end+1) = scanstomo(num);
end
end
verbose(par.verbose_level); % return to original settings
%{
figure(1)
subplot(2,1,1)
hold on
plot(missing_scans, theta(ismember(scanstomo, missing_scans)), 'rx')
hold off
legend({'Measured angles', 'Missing projections'})
axis tight
%}
if ~isempty(missing_scans)
ind = ismember(scanstomo, missing_scans);
verbose(1,['Scans not found are ' num2str(missing_scans)])
verbose(1,['Projections not found are ' num2str(find(ind))])
scanstomo(ind) = [];
theta(ind) = [];
proj_file_names(ind) = [];
proj_recon_method(ind) = [];
proj_roi(ind) = [];
proj_scanNo(ind) = [];
else
verbose(1,'All projections found')
end
num_proj = length(scanstomo);
object_size_orig = zeros(2,num_proj);
if isfield(par, 'fp16_precision') && par.fp16_precision
% use uint32 to store half floar precision data
stack_object=zeros(dims_ob(1),dims_ob(2),num_proj, 'like', fp16.set(1i));
else
stack_object=zeros(dims_ob(1),dims_ob(2),num_proj, 'like', single(1i));
end
pixel_scale =zeros(num_proj,2);
%energy = zeros(num_proj,1);
tic
if num_proj == 0
verbose(0, 'No new projections loaded')
return
end
which_missing = false(1,num_proj); % Include here INDEX numbers that you want to exclude (bad reconstructions)
utils.check_available_memory
%%
wb = waitbar(0,'1','Name','Loading ptycho-tomo projection...',...
'CreateCancelBtn','setappdata(gcbf,''canceling'',1)');
setappdata(wb,'canceling',0);
%
t0 = tic;
for num=1:num_proj
% Update waitbar and message
status = sprintf(par.scan_string_format, scanstomo(num));
status = strcat(status,' (',num2str(num),'/',num2str(num_proj),') ');
if num>1
timeLeft = (num_proj-num+1)*avgTimePerIter;
if timeLeft>3600
time_status = sprintf(' Time left:%3.3g hour', timeLeft/3600);
elseif timeLeft>60
time_status = sprintf(' Time left:%3.3g min', timeLeft/60);
else
time_status = sprintf(' Time left:%3.3g sec', timeLeft);
end
status = strcat(status,time_status);
end
waitbar(num/num_proj,wb,status)
% Check for clicked Cancel button
if getappdata(wb,'canceling')
break
end
file = proj_file_names{num};
if ismember(scanstomo(num), exclude_scans)
warning(['Skipping by user request: ' file{1}])
continue % skip the frames that are listed in exclude_scans
end
if ~iscell(file)
file = {file}; % make them all cells
end
object= [];
for jj = length(file):-1:1
%disp(['Reading file: ' file{jj}])
% if more than one file is present, try to load the first last one that
% does not fail
%try
%{
object_r = h5read(file{1},'/object_r');
object_i = h5read(file{1},'/object_i');
object = object_r + 1i*object_i;
object = single(object);
%object = prod(object,4); % use only the eDOF object if multiple layers are available
pixel_scale(num,:) = h5read(file{1},'/dx_spec');
%energy(num) = io.HDF.hdf5_load(file{jj}, '/reconstruction/p/energy');
%}
object = load(file{1},'object');
object = single(object.object);
parameter = load(file{1},'p');
pixel_scale(num,:) = parameter.p.dx_spec; %pixel size
break
%end
end
%% for multislice recon - sum layers into a single projection
if size(object,3)>1
if isfield(par.MLrecon,'select_layers') && any(par.MLrecon.select_layers)
object = prod(object(:,:,par.MLrecon.select_layers),3);
else
object = prod(object,3);
end
end
%%
object_size_orig(:,num) = size(object);
if isempty(object) || all(object(:) == 0 )
which_missing(num) = true;
warning(['Loading failed: ' [file{:}]])
continue
end
if isfield(par, 'crop_edge') && par.crop_edge>0
object = object(1+par.crop_edge:end-par.crop_edge,1+par.crop_edge:end-par.crop_edge);
end
if ~isempty(custom_preprocess_fun)
object = custom_preprocess_fun(object);
end
nx = dims_ob(2);
ny = dims_ob(1);
if size(object,2) > nx
object = object(:,1:nx);
elseif size(object,2) < nx
object = padarray(object,[0 nx-size(object,2)],'post');
end
if size(object,1) > ny
if par.auto_alignment|| par.get_auto_calibration
object = object(1:ny,:);
else
shifty = floor((size(object,1)-ny)/2);
object = object([1:ny]+shifty,:);
end
elseif size(object,1) < ny
if par.auto_alignment||par.get_auto_calibration
object = padarray(object,[ny-size(object,1) 0],'post');
else
shifty = (ny-size(object,1))/2;
object = padarray(object,[ny-size(object,1)-floor(shifty) 0],'post');
object = padarray(object,[floor(shifty) 0],'pre');
end
end
stack_object(:,:,num) = object;
% if par.showrecons
% mag=a+bs(object);
% phase=angle(object);
% figure(1); clf
% imagesc(mag); axis xy equal tight ; colormap bone(256); colorbar;
% title(['object magnitude S',sprintf('%05d',ii),', Projection ' ,sprintf('%03d',num) , ', Theta = ' sprintf('%.2f',theta(num)), ' degrees']);drawnow;
% set(gcf,'Outerposition',[601 424 600 600])
% figure(2); imagesc(phase); axis xy equal tight; colormap bone(256); colorbar;
% title(['object phase S',sprintf('%05d',ii),', Projection ' ,sprintf('%03d',num) , ', Theta = ' sprintf('%.2f',theta(num)), ' degrees']);drawnow;
% set(gcf,'Outerposition',[1 424 600 600]) %[left, bottom, width, height
% figure(3); % imagesc3D(probe);
% axis xy equal tight
% set(gcf,'Outerposition',[600 49 375 375]) %[left, bottom, width, height
% figure(4);
% if isfield(p, 'err')
% loglog(p.err);
% elseif isfield(p, 'mlerror')
% loglog(p.mlerror)
% elseif isfield(p, 'error_metric')
% loglog(p.error_metric(2).iteration,p.error_metric(2).value)
% end
% title(sprintf('Error %03d',num))
% set(gcf,'Outerposition',[1 49 600 375]) %[left, bottom, width, height
% drawnow;
% end
avgTimePerIter = toc(t0)/num;
end % enf of parfor
delete(wb)
%store info for ML reconstructions
par.proj_file_names = proj_file_names;
par.proj_recon_method = proj_recon_method;
par.proj_roi = proj_roi;
par.proj_scanNo = proj_scanNo;
par.object_size_orig = object_size_orig;
verbose(1, 'Data loaded')
%% parallel loading -- Not working
%{
%% prepare parpool
% pool = gcp('nocreate');
% if isempty(pool) || pool.NumWorkers < par.Nworkers
% delete(pool);
% pool = parpool(par.Nworkers);
% end
% pool.IdleTimeout = 600; % set idle timeout to 10 hours
%
% load at least 10 frames per worker to use well the resources
block_size = max(1, par.Nworkers)*50;
%% load data, use parfor but process blockwise to avoid lare memory use
for block_id = 1:ceil(num_proj/block_size)
block_inds = 1+(block_id-1)*block_size: min(num_proj, block_id*block_size);
verbose(1,'===== Block %i / %i started ===== ', block_id, ceil(num_proj/block_size))
utils.check_available_memory
stack_object_block = zeros(dims_ob(1),dims_ob(2),length(block_inds), 'like', stack_object);
share_mem = shm(true);
share_mem.allocate(stack_object_block);
share_mem.detach();
% ticBytes(gcp);
%% start a smaller block in parallel
% parfor(num = block_inds,par.Nworkers)
% if parfor fails, try normal loop
for num = block_inds
file = proj_file_names{num};
if ismember(scanstomo(num), exclude_scans)
warning(['Skipping by user request: ' file{1}])
continue % skip the frames that are listed in exclude_scans
end
if ~iscell(file)
file = {file}; % make them all cells
end
object= [];
for jj = length(file):-1:1
disp(['Reading file: ' file{jj}])
% if more than one file is present, try to load the first last one that
% does not fail
%try
object_r = h5read(file{1},'/object_r');
object_i = h5read(file{1},'/object_i');
object = object_r + 1i*object_i;
object = single(object);
%object = prod(object,4); % use only the eDOF object if multiple layers are available
pixel_scale(num,:) = h5read(file{1},'/dx_spec');
%energy(num) = io.HDF.hdf5_load(file{jj}, '/reconstruction/p/energy');
break
%end
end
if isempty(object) || all(object(:) == 0 )
which_missing(num) = true;
warning(['Loading failed: ' [file{:}]])
continue
end
if ~isempty(custom_preprocess_fun)
object = custom_preprocess_fun(object);
end
nx = dims_ob(2);
ny = dims_ob(1);
if size(object,2) > nx
object = object(:,1:nx);
elseif size(object,2) < nx
object = padarray(object,[0 nx-size(object,2)],'post');
end
if size(object,1) > ny
if par.auto_alignment|| par.get_auto_calibration
object = object(1:ny,:);
else
shifty = floor((size(object,1)-ny)/2);
object = object([1:ny]+shifty,:);
end
elseif size(object,1) < ny
if par.auto_alignment||par.get_auto_calibration
object = padarray(object,[ny-size(object,1) 0],'post');
else
shifty = (ny-size(object,1))/2;
object = padarray(object,[ny-size(object,1)-floor(shifty) 0],'post');
object = padarray(object,[floor(shifty) 0],'pre');
end
end
% if par.showrecons
% mag=a+bs(object);
% phase=angle(object);
% figure(1); clf
% imagesc(mag); axis xy equal tight ; colormap bone(256); colorbar;
% title(['object magnitude S',sprintf('%05d',ii),', Projection ' ,sprintf('%03d',num) , ', Theta = ' sprintf('%.2f',theta(num)), ' degrees']);drawnow;
% set(gcf,'Outerposition',[601 424 600 600])
% figure(2); imagesc(phase); axis xy equal tight; colormap bone(256); colorbar;
% title(['object phase S',sprintf('%05d',ii),', Projection ' ,sprintf('%03d',num) , ', Theta = ' sprintf('%.2f',theta(num)), ' degrees']);drawnow;
% set(gcf,'Outerposition',[1 424 600 600]) %[left, bottom, width, height
% figure(3); % imagesc3D(probe);
% axis xy equal tight
% set(gcf,'Outerposition',[600 49 375 375]) %[left, bottom, width, height
% figure(4);
% if isfield(p, 'err')
% loglog(p.err);
% elseif isfield(p, 'mlerror')
% loglog(p.mlerror)
% elseif isfield(p, 'error_metric')
% loglog(p.error_metric(2).iteration,p.error_metric(2).value)
% end
% title(sprintf('Error %03d',num))
% set(gcf,'Outerposition',[1 49 600 375]) %[left, bottom, width, height
% drawnow;
% end
if isfield(par, 'fp16_precision') && par.fp16_precision
% convert data to fp16 precision
object = fp16.set(object);
end
% keyboard
% write loaded object to a small block of shared memory, avoid using
% parpool data transfer
share_mem_tmp = share_mem;
[share_mem_tmp, share_mem_object] = share_mem_tmp.attach();
tomo.set_to_array(share_mem_object, object, num - block_inds(1));
share_mem_tmp.detach();
end % enf of parfor
% tocBytes(gcp);
tic
verbose(1,'Writting to shared stack_object')
[share_mem, stack_object_block] = share_mem.attach();
% write loaded block to the full array, avoid memory reallocation
tomo.set_to_array(stack_object, stack_object_block, block_inds-1);
share_mem.free();
toc
end
verbose(1, 'Data loaded')
%}
%% examine projections
verbose(1, 'Find residua')
[Nx, Ny, Nprojections] = size(stack_object);
object_ROI = {ceil(1+par.asize(1)/2:Nx-par.asize(1)/2),ceil(1+par.asize(2)/2:Ny-par.asize(2)/2)};
residua = tomo.block_fun(@(x)(squeeze(math.sum2(abs(utils.findresidues(x))>0.1))),stack_object, struct('ROI', {object_ROI}));
if isfield(par,'max_residua_limit')
max_residua = par.max_residua_limit;
else
max_residua = 100;
end
poor_projections = (residua(:)' > max_residua) & ~par.is_laminography ; % ignore in the case of laminography
if any(poor_projections)
verbose(1, 'Found %i/%i projections with more than %i residues ', sum(poor_projections), Nprojections, max_residua)
end
if any(which_missing & ~ismember(scanstomo, exclude_scans) )
missing = find(which_missing & ~ismember(scanstomo, exclude_scans));
verbose(1,['Projections not found are ' num2str(missing)])
verbose(1,['Scans not found are ' num2str(scanstomo(missing))])
else
verbose(1,'All projections loaded')
end
toc
% avoid also empty projections
which_wrong = poor_projections | squeeze(math.sum2(stack_object)==0)';
if any(which_wrong & ~ismember(scanstomo, exclude_scans) )
wrong = find(which_wrong & ~ismember(scanstomo, exclude_scans));
verbose(1,['Projections failed are ' num2str(wrong)])
verbose(1,['Scans failed are ' num2str(scanstomo(wrong))])
else
verbose(1,'All loaded projections are OK')
end
%%% Getting rid of missing projections %%%
which_remove = which_missing | which_wrong;
if any(which_remove)
if par.online_tomo || ~strcmpi(input(sprintf('Do you want remove %i missing/wrong projections and keep going (Y/n)?',sum(which_remove)),'s'),'n')
disp('Removing missing/wrong projections. stack_object, scanstomo, theta and num_proj are modified')
stack_object(:,:,which_remove) = [];
scanstomo(which_remove)=[];
theta(which_remove)=[];
pixel_scale(which_remove,:) = [];
%energy(which_remove,:) = [];
disp('Done')
else
disp('Keeping empty spaces for missing projections. Problems are expected if you continue.')
end
end
par.scanstomo = scanstomo;
par.num_proj=numel(scanstomo);
pixel_scale = pixel_scale ./ mean(pixel_scale);
assert(par.num_proj > 0, 'No projections loaded')
if all(all(abs(pixel_scale)-1 < 1e-6)) || ~any(isfinite(mean(pixel_scale)))
%if all datasets have the same pixel scale
pixel_scale = [1,1];
else
warning('Datasets do not have equal pixel sizes, auto-rescaling projections')
% use FFT base rescaling -> apply illumination function first to remove
% effect of the noise out of the reconstruction region
rot_fun = @(x,sx,sy)(utils.imrescale_frft(x .* par.illum_sum, sx, sy)) ./ ( max(0,utils.imrescale_frft(par.illum_sum,sx,sy))+1e-2*max(par.illum_sum(:)));
stack_object = tomo.block_fun(rot_fun,stack_object, pixel_scale(:,1),pixel_scale(:,2));
pixel_scale = [1,1];
end
par.pixel_scale = pixel_scale;
%par.energy = energy;
%% clip the projections ampltitude by quantile filter
if par.clip_amplitude_quantile < 1
MAX = quantile(reshape(abs(fp16.get(stack_object(1:10:end,1:10:end,:))), [], par.num_proj), par.clip_amplitude_quantile ,1);
MAX = reshape(MAX,1,1,par.num_proj);
clip_fun = @(x,M)(min(abs(x),M) .* x ./ (abs(x) + 1e-5));
stack_object = tomo.block_fun(clip_fun,stack_object, MAX, struct('use_GPU', true));
end
if size(stack_object,3) ~= length(theta) || length(theta) ~= par.num_proj
error('Inconsistency between number of angles and projections')
end
%{
if ~isempty(par.tomo_id) && all(par.tomo_id > 0)
% sanity safety check, all loaded angles correpont to the stored angles
[~,theta_test] = prepare.load_angles(par, par.scanstomo, [], false);
if max(abs(theta - theta_test)) > 180/par.num_proj/2
error('Some angles have angles different from expected')
end
end
%}
%% replot angle
plot_angles = true;
if par.verbose_level && plot_angles
plotting.smart_figure(1);
subplot(2,1,1)
plot(par.scanstomo,theta,'ob'); grid on;
xlim(par.scanstomo([1,end]))
%legend('Tilt angles')
xlabel('Scan #')
ylabel('Tilt angles')
%[anglessort,~] = sort(theta);
subplot(2,1,2)
plot(diff(theta))
ylabel('Angle increment')
%title('Angular spacing');
grid on;
xlim([1,par.num_proj-1])
if par.windowautopos
screensize = get( groot, 'Screensize' );
win_size = [946 815];
set(gcf,'Outerposition',[139 min(163,screensize(4)-win_size(2)) win_size]); %[left, bottom, width, height]
end
title('Measured angles')
drawnow
end
end
+452
View File
@@ -0,0 +1,452 @@
% LOAD_PROJECTIONS_FAST load reconstructed projections from disk to RAM
%
% [stack_object, theta,num_proj, par] = load_projections_fast(par, exclude_scans, dims_ob, theta, custom_preprocess_fun)
%
% Inputs:
% **par - parameter structure
% **exclude_scans - list of scans to be excluded from loading, [] = none
% **dims_ob - dimension of the object
% **theta - angles of the scans
% **custom_preprocess_fun - function to be applied on the loaded data, eg cropping , rotation, etc
%
% *returns*
% ++stack_object - loaded complex-valued projections
% ++theta - angles corresponding to the loaded projections, angles for missing projections are removed
% ++num_proj - number of projections
% ++par - updated parameter structure
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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, theta,num_proj, par] = load_projections_fast(par, exclude_scans, dims_ob, theta, custom_preprocess_fun)
import ptycho.*
import utils.*
import io.*
import plotting.imagesc3D
utils.verbose(struct('prefix', 'loading'))
if nargin < 5
custom_preprocess_fun = [];
end
if ~isempty(custom_preprocess_fun) && ishandle(custom_preprocess_fun) && ~strcmpi(func2str(custom_preprocess_fun), '@(x)x')
custom_preprocess_fun = [] ;
end
scanstomo = par.scanstomo;
% avoid loading scans listed in 'exclude_scans'
if ~isempty(exclude_scans)
ind = ismember(scanstomo, exclude_scans);
%% clear values corresponding to excluded scans
scanstomo(ind) = [];
theta(ind) = [];
par.subtomos(ind) = [];
end
% % plot average vibrations for each of the loaded projections
% utils.verbose(0,'Checking stability of the projections')
% poor_projections = prepare.plot_sample_stability(par, scanstomo, ~par.online_tomo, par.pixel_size);
% if sum(poor_projections) && ...
% (par.online_tomo || ~strcmpi(input(sprintf('Remove %i low stability projections: [Y/n]\n',sum(poor_projections)), 's'), 'n') )
% theta(poor_projections) = [];
% scanstomo(poor_projections) = [];
% else
% utils.verbose(0,'All projections are fine')
% end
verbose(0,'Checking available files')
verbose(0); % make it quiet
missing_scans = [];
proj_file_names = cell(length(scanstomo),1);
for num = 1:length(scanstomo)
progressbar(num, length(scanstomo))
filename = find_projection_files_names(par, scanstomo(num));
if isempty(filename)
missing_scans(end+1) = scanstomo(num);
continue
end
proj_file_names{num} = filename;
end
verbose(par.verbose_level); % return to original settings
if ~isempty(missing_scans)
plotting.smart_figure(1)
subplot(2,1,1)
hold on
plot(missing_scans, theta(ismember(scanstomo, missing_scans)), 'rx', 'Linewidth', 2)
hold off
legend({'Measured angles', 'Missing scans'})
axis tight
drawnow
ind = ismember(scanstomo, missing_scans);
verbose(1,['Scans not found are ' num2str(missing_scans)])
verbose(1,['Projections not found are ' num2str(find(ind))])
%% clear values corresponding to measured but missing scans (not reconstructed)
scanstomo(ind) = [];
theta(ind) = [];
proj_file_names(ind) = [];
par.subtomos(ind) = [];
else
verbose(1,'All projections found')
end
num_proj = length(scanstomo);
if isfield(par, 'fp16_precision') && par.fp16_precision
dtype = uint16(1i); % use uint32 to store half floar precision data
else
dtype= single(1i);
end
downsample = 2^par.downsample_projections; % calculate downsample factor for binning , default par.downsample_projections = 0;
pixel_size =zeros(num_proj,2);
energy = zeros(num_proj,1);
stack_object=zeros(ceil(dims_ob(1) / downsample),ceil(dims_ob(2) / downsample),num_proj, 'like', dtype);
residua = zeros(num_proj,1);
%disp(size(stack_object))
tic
% load at least 10 frames per worker to use well the resources
block_size = max(1, feature('numcores'))*4;
object_ROI = {ceil(1+par.asize(1)/2/downsample):ceil((dims_ob(1)-par.asize(1)/2)/downsample),ceil(1+par.asize(2)/2/downsample):ceil((dims_ob(2)-par.asize(2)/2)/downsample)};
verbose(1,'Loading projections ...')
missing_all = [];
t0 = tic;
%% load data, use parfor but process blockwise to avoid large memory use and also allow user stopping during MEX reading
for block_id = 1:ceil(num_proj/block_size)
block_inds = 1+(block_id-1)*block_size: min(num_proj, block_id*block_size);
utils.progressbar(block_id, ceil(num_proj/block_size))
if strcmpi(par.file_extension, 'h5') && ~verLessThan('matlab', '9.4') && ...
(isfield(par, 'use_mex_loader') && par.use_mex_loader ) % only matlab newer than R2018a is supported
object_block = [];
try
% fast MEX loader, sometimes it tends to fail and needs to
% be run again to load the data corectly
[object_block,missing_tmp] = mex_read(par.dims_ob_loaded, proj_file_names(block_inds), par.Nthreads_mexread);
catch Err
disp('Error in loading using MEX, falling back to matlab reader, try to reduce par.Nthreads_mexread is this warning repeats often')
disp(Err)
end
% if loading was not succeful ..
if isempty(object_block)
[object_block, missing_tmp] = matlab_read(par.dims_ob_loaded, proj_file_names(block_inds));
end
else
% loading using matlab for original MAT file data or old matlab
[object_block, missing_tmp] = matlab_read(par.dims_ob_loaded, proj_file_names(block_inds));
end
missing_all = [missing_all, block_inds(missing_tmp)];
% read additional information
for jj = setdiff(block_inds, block_inds(missing_tmp)) % remove missing projection from loading
if strcmpi(par.file_extension, 'h5')
try
pixel_size(jj,:) = h5read(proj_file_names{jj}, '/reconstruction/p/dx_spec');
energy(jj) = h5read(proj_file_names{jj}, '/reconstruction/p/energy');
catch err
disp(err)
keyboard
end
else
% load it from the matlab file is not supported (it is too slow)
pixel_size(jj,:) = par.pixel_size;
energy(jj) = nan;
end
end
%% apply custom data proprocessing and caculate basic statistics, DO IT ON GPU
[object_block, residua(block_inds,1), projection_value(block_inds)] = ...
tomo.block_fun(@process_projection_block, object_block, custom_preprocess_fun, par, object_ROI,pixel_size(block_inds,:), struct('verbose_level', 0));
% convert data to fp16 precision if requested
if isfield(par, 'fp16_precision') && par.fp16_precision
object_block = fp16.set(object_block);
end
stack_object(:,:,block_inds) = object_block;
end
pixel_size = min(pixel_size,[],2); % projection were already rescaled to provide the same pixel size in each dimension
verbose(1, 'Data loaded in %is', round(toc(t0)))
% downsample the illum_sum if requested
if downsample > 0
par.illum_sum = utils.binning_2D(crop_pad(par.illum_sum, ceil(dims_ob/downsample)*downsample) , downsample);
par.asize = ceil(par.asize / downsample);
end
failed_projections = projection_value < 0.1*median(projection_value) | ismember(1:num_proj, missing_all) | ~isfinite(projection_value);
if any(failed_projections )
verbose(0,['Projections failed are ' num2str(find(failed_projections))])
verbose(0,['Scans failed are ' num2str(scanstomo(failed_projections))])
else
verbose(0,'All loaded projections seems OK')
end
[Nprojections] = size(stack_object,3);
poor_projections = false;
if ~par.is_laminography
% laminography has a more complex definition of field of view ->
% currently not implemented
poor_projections = (residua(:)' > par.max_residua_limit) ; % ignore in the case of laminography
verbose(1, 'Found %i/%i projections with more than %i residues ', sum(poor_projections), Nprojections, par.max_residua_limit)
if any(poor_projections)
verbose(1,['Projections with residua are ' num2str(find(poor_projections))])
verbose(1,['Scans with residua are ' num2str(scanstomo(poor_projections))])
end
end
verbose(1, 'Find residua done')
% avoid also empty projections
which_remove = poor_projections | failed_projections;
%%% Getting rid of missing projections %%%
if any(which_remove)
[Nx,Ny,~] = size(stack_object);
title_extra = {};
for ii = 1:num_proj
if which_remove(ii)
title_extra{end+1} = sprintf(' N residua: %i',residua(ii));
end
end
verbose(1,' %i failed projections shown in figure(1) \n', sum(which_remove))
tomo.show_projections(stack_object(:,:,which_remove), theta(which_remove), par, 'fnct', @angle, ...
'title', 'Projection to be removed','plot_residua', true, 'title_extra', title_extra, ...
'rectangle_pos', [par.asize(2)/2,Ny-par.asize(2)/2, par.asize(1)/2,Nx-par.asize(1)/2], 'figure_id', 1)
if par.online_tomo || debug() || ~strcmpi(input(sprintf('Do you want remove %i failed/wrong projections and keep going (Y/n)?',sum(which_remove)),'s'),'n')
verbose(0,'Removing failed/wrong projections. stack_object, scanstomo, theta and num_proj are modified')
stack_object(:,:,which_remove) = [];
scanstomo(which_remove)=[];
theta(which_remove)=[];
pixel_size(which_remove,:) = [];
energy(which_remove,:) = [];
residua(which_remove)=[];
par.subtomos(which_remove) = [];
verbose(0,'Done')
else
verbose(0,'Keeping empty spaces for failed projections. Problems are expected if you continue.')
end
end
par.scanstomo = scanstomo;
par.num_proj=numel(scanstomo);
% store number of residua for later processing
par.nresidua_per_frame = residua(:)';
pixel_scale = pixel_size ./ min(pixel_size(:)); % just in case that the axis do not have indetical pixel size, NOT TESTED YET
assert(par.num_proj > 0, 'No projections loaded')
if all(all(abs(pixel_scale)-1 < 1e-6)) || ~any(isfinite(mean(pixel_scale)))
%if all datasets have the same pixel scale
pixel_scale = [1,1];
else
warning('Datasets do not have equal pixel sizes, auto-rescaling projections')
% use FFT base rescaling -> apply illumination function first to remove
% effect of the noise out of the reconstruction region
rot_fun = @(x,sx,sy)(utils.imrescale_frft(x .* par.illum_sum, sx, sy)) ./ ( max(0,utils.imrescale_frft(par.illum_sum,sx,sy))+1e-2*max(par.illum_sum(:)));
stack_object = tomo.block_fun(rot_fun,stack_object, pixel_scale(:,1),pixel_scale(:,2));
pixel_scale = [1,1];
end
par.pixel_scale = pixel_scale;
par.energy = energy;
if size(stack_object,3) ~= length(theta) || length(theta) ~= par.num_proj
error('Inconsistency between number of angles and projections')
end
utils.verbose(struct('prefix', 'template'))
end
function [object_block, residua, projection_value] = process_projection_block(object_block, custom_preprocess_fun, par, object_ROI, pixel_size)
% auxiliary function used to apply various preprocessing steps, ie custom_preprocess_fun, binning, clipping and residua calculation on the
% object_block on GPU -> avoid CPU-GPU transfer overhead
% returns:
% object_block - processed complex valued projections
% residua - number of residua in each frame
% projection_value - average amplitude of the projection
% pixel_size in each dimension
% apply additional processing, e.g. rotation
if ~isempty(custom_preprocess_fun)
object_block = custom_preprocess_fun(object_block);
end
if any(pixel_size(:,1) ~= pixel_size(:,2))
% in the case of asymmetric pixel size,
% upsample the data in the dimennsion with lower resolution (-> at least relax issues in tomography interpolation)
pixel_scale = pixel_size ./ min(pixel_size,[],2) ;
assert(all(std(pixel_scale) < 1e-3), 'Variable resolution between projection and asymmetric pixel size is not implemented')
Npix = size(object_block);
dims_ob_new = round(Npix(1:2) .* pixel_scale(1,:));
object_block = utils.interpolateFT(object_block, dims_ob_new);
end
Npix = size(object_block);
downsample = 2^par.downsample_projections;
% downsample the data if requested
if downsample > 1
object_block = utils.binning_2D(utils.crop_pad(object_block, ceil(Npix/downsample)*downsample) , downsample);
end
%% clip the projections amplitude by quantile filter
if par.clip_amplitude_quantile > 0 && par.clip_amplitude_quantile < 1
MAX = quantile(reshape(abs(object_block(1:10:end,1:10:end,:)), [], Npix(3)), par.clip_amplitude_quantile ,1);
MAX = reshape(MAX,1,1,[]);
clip_fun = @(x,M)(min(abs(x),M) .* x ./ (abs(x) + 1e-5));
object_block = clip_fun(object_block, MAX);
end
residua = squeeze(math.sum2(abs(utils.findresidues(object_block(object_ROI{:},:)))>0.1));
projection_value = squeeze(math.sum2(abs(object_block)));
end
function [object_block, missing] = matlab_read(dims_ob, proj_file_names)
% projection loading using matlab
% Inputs:
% dims_ob - projection size
% proj_file_names - cell of filenames to be loaded
% Outputs:
% object_block - loaded projection
% missing - list of missing (failed) projections
object_block = zeros([dims_ob,length(proj_file_names)], 'like', single(1i));
loaded = false(length(proj_file_names),1);
for jj = 1:length(proj_file_names)
utils.verbose(2,['Reading file: ' proj_file_names{jj}])
try
object = io.load_ptycho_recons(proj_file_names{jj}, 'object');
object = single(object.object);
object = prod(object,4); % use only the eDOF object if multiple layers are available
object_block(:,:,jj) = utils.crop_pad(object, dims_ob);
loaded(jj) = true;
catch
utils.verbose(-1,'Loading of file %s failed', proj_file_names{jj})
end
end
missing = find(~loaded);
end
function [object_block, missing] = mex_read(dims_ob, proj_file_names, Nthreads)
% fast projection loader by MEX with paralelization
% Inputs:
% dims_ob - projection size
% proj_file_names - cell of filenames to be loaded
% Nthreads - number of threads used to load the projections in parallel
% Outputs:
% object_block - loaded projection
% missing - list of missing (failed) projections
Nthreads = min(length(proj_file_names),Nthreads );
proj_file_names = reshape(proj_file_names, 1,[]);
% load complex-valued projections using parallel MEX
try
[object_block, missing] = io.ptycho_read(Nthreads, 'single', dims_ob, '/reconstruction/object', proj_file_names);
catch err
if strcmpi(err.identifier, 'ptycho:read:failed')
Nthreads = 5;
[object_block, missing] = io.ptycho_read(Nthreads, 'single', dims_ob, '/reconstruction/object', proj_file_names);
warning off backtrace
warning('===================================================================================================================================')
warning('Loading of projections failed due to too high multithreading, if this warning repeats, consider lowering par.Nthreads_mexread value')
warning('===================================================================================================================================')
warning on backtrace
else
rethrow(err)
end
end
assert(ndims(object_block) <= 5, 'Unexpected dimensionality of inputs')
object_block = permute(object_block, [2,1,3,4,5]); % transpose loaded reconstructions
object_block = prod(object_block,4) ; % get one eDoF frame if ML reconstruction is used
object_block = squeeze(object_block); % get rid of extra dimensions
assert(ndims(object_block) == 3, 'Unexpected dimensionality of inputs')
end
+400
View File
@@ -0,0 +1,400 @@
% LOAD_PROJECTIONS_MATLAB load reconstructed projections from disk to RAM
% created by YJ based on PSI's function
% [stack_object, theta,num_proj, par] = load_projections_matlab(par, exclude_scans, dims_ob, theta, custom_preprocess_fun)
%
% Inputs:
% **par - parameter structure
% **exclude_scans - list of scans to be excluded from loading, [] = none
% **dims_ob - dimension of the object
% **theta - angles of the scans
% **custom_preprocess_fun - function to be applied on the loaded data, eg cropping , rotation, etc
%
% *returns*
% ++stack_object - loaded complex-valued projections
% ++theta - angles corresponding to the loaded projections, angles for missing projections are removed
% ++num_proj - number of projections
% ++par - updated parameter structure
function [stack_object, theta,num_proj, par] = load_projections_matlab(par, exclude_scans, dims_ob, theta, custom_preprocess_fun)
import ptycho.*
import utils.*
import io.*
import plotting.*
if nargin < 5
custom_preprocess_fun = [];
end
if ~isempty(custom_preprocess_fun) && ishandle(custom_preprocess_fun) && ~strcmpi(func2str(custom_preprocess_fun), '@(x)x')
custom_preprocess_fun = [] ;
end
scanstomo = par.scanstomo;
if isfield(par,'energy')
energy = par.energy;
else
energy = zeros(length(theta),1);
end
% avoid loading scans listed in 'exclude_scans'
if ~isempty(exclude_scans)
ind = ismember(scanstomo, exclude_scans);
scanstomo(ind) = [];
theta(ind) = [];
energy(ind) = [];
end
% % plot average vibrations for each of the laoded projections
% disp('Checking stability of the projections')
% poor_projections = prepare.plot_sample_stability(par, scanstomo, ~par.online_tomo, par.pixel_size);
% if sum(poor_projections) && ...
% (par.online_tomo || ~strcmpi(input(sprintf('Remove %i low stability projections: [Y/n]\n',sum(poor_projections)), 's'), 'n') )
% theta(poor_projections) = [];
% scanstomo(poor_projections) = [];
% else
% disp('All projections are fine')
% end
verbose(1,'Checking available files')
missing_scans = [];
proj_file_names = {};
proj_recon_method = {};
proj_roi = {};
proj_scanNo = {};
for num = 1:length(scanstomo)
progressbar(num, length(scanstomo))
%proj_file_names{num} = find_ptycho_filename(par.analysis_path,scanstomo(num),par.fileprefix,par.filesuffix, par.file_extension);
%proj_file_names{num} = find_projection_files_names_aps(par, scanstomo(num));
[proj_file_names{num},proj_recon_method{num},proj_roi{num},proj_scanNo{num}] = find_ML_recon_files_names(par, scanstomo(num));
%disp(proj_file_names{num})
if isempty(proj_file_names{num})
missing_scans(end+1) = scanstomo(num);
end
end
verbose(par.verbose_level); % return to original settings
%{
figure(1)
subplot(2,1,1)
hold on
plot(missing_scans, theta(ismember(scanstomo, missing_scans)), 'rx')
hold off
legend({'Measured angles', 'Missing projections'})
axis tight
%}
if ~isempty(missing_scans)
ind = ismember(scanstomo, missing_scans);
verbose(1,['Scans not found are ' num2str(missing_scans)])
verbose(1,['Projections not found are ' num2str(find(ind))])
scanstomo(ind) = [];
theta(ind) = [];
proj_file_names(ind) = [];
proj_recon_method(ind) = [];
proj_roi(ind) = [];
proj_scanNo(ind) = [];
energy(ind) = [];
else
verbose(1,'All projections found')
end
num_proj = length(scanstomo);
object_size_orig = zeros(2,num_proj);
if isfield(par, 'fp16_precision') && par.fp16_precision
% use uint32 to store half floar precision data
stack_object=zeros(dims_ob(1),dims_ob(2),num_proj, 'like', fp16.set(1i));
else
stack_object=zeros(dims_ob(1),dims_ob(2),num_proj, 'like', single(1i));
end
pixel_size =zeros(num_proj,2);
tic
if num_proj == 0
verbose(0, 'No new projections loaded')
return
end
which_missing = false(1,num_proj); % Include here INDEX numbers that you want to exclude (bad reconstructions)
utils.check_available_memory
%%
wb = waitbar(0,'1','Name','Loading ptycho-tomo projection...',...
'CreateCancelBtn','setappdata(gcbf,''canceling'',1)');
setappdata(wb,'canceling',0);
%
t0 = tic;
for num=1:num_proj
% Update waitbar and message
status = sprintf(par.scan_string_format, scanstomo(num));
status = strcat(status,' (',num2str(num),'/',num2str(num_proj),') ');
if num>1
timeLeft = (num_proj-num+1)*avgTimePerIter;
if timeLeft>3600
time_status = sprintf(' Time left:%3.3g hour', timeLeft/3600);
elseif timeLeft>60
time_status = sprintf(' Time left:%3.3g min', timeLeft/60);
else
time_status = sprintf(' Time left:%3.3g sec', timeLeft);
end
status = strcat(status,time_status);
end
waitbar(num/num_proj,wb,status)
% Check for clicked Cancel button
if getappdata(wb,'canceling')
break
end
file = proj_file_names{num};
if ismember(scanstomo(num), exclude_scans)
warning(['Skipping by user request: ' file{1}])
continue % skip the frames that are listed in exclude_scans
end
if ~iscell(file)
file = {file}; % make them all cells
end
object= [];
for jj = length(file):-1:1
for ii=1:3
try
object = load(file{1},'object');
object = single(object.object);
parameter = load(file{1},'p');
pixel_size(num,:) = parameter.p.dx_spec; %pixel size
break
catch
%warning(['Loading failed: ' [file{1}]])
end
end
end
if isempty(object) || all(object(:) == 0 )
which_missing(num) = true;
warning(['Loading failed: ' [file{:}]])
continue
end
% for multislice recon - sum layers into a single projection
if size(object,3)>1
if isfield(par.MLrecon,'select_layers') && any(par.MLrecon.select_layers)
object = prod(object(:,:,par.MLrecon.select_layers),3);
else
object = prod(object,3);
end
end
%%
object_size_orig(:,num) = size(object);
if isfield(par, 'crop_edge') && par.crop_edge>0
object = object(1+par.crop_edge:end-par.crop_edge,1+par.crop_edge:end-par.crop_edge);
end
if ~isempty(custom_preprocess_fun)
object = custom_preprocess_fun(object);
end
nx = dims_ob(2);
ny = dims_ob(1);
if size(object,2) > nx
object = object(:,1:nx);
elseif size(object,2) < nx
object = padarray(object,[0 nx-size(object,2)],'post');
end
if size(object,1) > ny
if par.auto_alignment|| par.get_auto_calibration
object = object(1:ny,:);
else
shifty = floor((size(object,1)-ny)/2);
object = object([1:ny]+shifty,:);
end
elseif size(object,1) < ny
if par.auto_alignment||par.get_auto_calibration
object = padarray(object,[ny-size(object,1) 0],'post');
else
shifty = (ny-size(object,1))/2;
object = padarray(object,[ny-size(object,1)-floor(shifty) 0],'post');
object = padarray(object,[floor(shifty) 0],'pre');
end
end
stack_object(:,:,num) = object;
% if par.showrecons
% mag=a+bs(object);
% phase=angle(object);
% figure(1); clf
% imagesc(mag); axis xy equal tight ; colormap bone(256); colorbar;
% title(['object magnitude S',sprintf('%05d',ii),', Projection ' ,sprintf('%03d',num) , ', Theta = ' sprintf('%.2f',theta(num)), ' degrees']);drawnow;
% set(gcf,'Outerposition',[601 424 600 600])
% figure(2); imagesc(phase); axis xy equal tight; colormap bone(256); colorbar;
% title(['object phase S',sprintf('%05d',ii),', Projection ' ,sprintf('%03d',num) , ', Theta = ' sprintf('%.2f',theta(num)), ' degrees']);drawnow;
% set(gcf,'Outerposition',[1 424 600 600]) %[left, bottom, width, height
% figure(3); % imagesc3D(probe);
% axis xy equal tight
% set(gcf,'Outerposition',[600 49 375 375]) %[left, bottom, width, height
% figure(4);
% if isfield(p, 'err')
% loglog(p.err);
% elseif isfield(p, 'mlerror')
% loglog(p.mlerror)
% elseif isfield(p, 'error_metric')
% loglog(p.error_metric(2).iteration,p.error_metric(2).value)
% end
% title(sprintf('Error %03d',num))
% set(gcf,'Outerposition',[1 49 600 375]) %[left, bottom, width, height
% drawnow;
% end
avgTimePerIter = toc(t0)/num;
end % enf of parfor
delete(wb)
%store info for ML reconstructions
par.proj_file_names = proj_file_names;
par.proj_recon_method = proj_recon_method;
par.proj_roi = proj_roi;
par.proj_scanNo = proj_scanNo;
par.object_size_orig = object_size_orig;
verbose(1, 'Data loaded')
%% examine projections
verbose(1, 'Find residua')
[Nx, Ny, Nprojections] = size(stack_object);
object_ROI = {ceil(1+par.asize(1)/2:Nx-par.asize(1)/2),ceil(1+par.asize(2)/2:Ny-par.asize(2)/2)};
residua = tomo.block_fun(@(x)(squeeze(math.sum2(abs(utils.findresidues(x))>0.1))),stack_object, struct('ROI', {object_ROI}));
if isfield(par,'max_residua_limit')
max_residua = par.max_residua_limit;
else
max_residua = 100;
end
poor_projections = (residua(:)' > max_residua) & ~par.is_laminography ; % ignore in the case of laminography
if any(poor_projections)
verbose(1, 'Found %i/%i projections with more than %i residues ', sum(poor_projections), Nprojections, max_residua)
end
if any(which_missing & ~ismember(scanstomo, exclude_scans) )
missing = find(which_missing & ~ismember(scanstomo, exclude_scans));
verbose(1,['Projections not found are ' num2str(missing)])
verbose(1,['Scans not found are ' num2str(scanstomo(missing))])
else
verbose(1,'All projections loaded')
end
toc
% avoid also empty projections
which_wrong = poor_projections | squeeze(math.sum2(stack_object)==0)';
if any(which_wrong & ~ismember(scanstomo, exclude_scans) )
wrong = find(which_wrong & ~ismember(scanstomo, exclude_scans));
verbose(1,['Projections failed are ' num2str(wrong)])
verbose(1,['Scans failed are ' num2str(scanstomo(wrong))])
else
verbose(1,'All loaded projections are OK')
end
%%% Getting rid of missing projections %%%
which_remove = which_missing | which_wrong;
if any(which_remove)
if par.online_tomo || ~strcmpi(input(sprintf('Do you want remove %i missing/wrong projections and keep going (Y/n)?',sum(which_remove)),'s'),'n')
disp('Removing missing/wrong projections. stack_object, scanstomo, theta and num_proj are modified')
stack_object(:,:,which_remove) = [];
scanstomo(which_remove)=[];
theta(which_remove)=[];
pixel_size(which_remove,:) = [];
energy(which_remove,:) = [];
disp('Done')
else
disp('Keeping empty spaces for missing projections. Problems are expected if you continue.')
end
end
par.scanstomo = scanstomo;
par.num_proj=numel(scanstomo);
pixel_scale = pixel_size ./ mean(pixel_size);
assert(par.num_proj > 0, 'No projections loaded')
if all(all(abs(pixel_scale)-1 < 1e-6)) || ~any(isfinite(mean(pixel_scale)))
%if all datasets have the same pixel scale
pixel_scale = [1,1];
else
warning('Datasets do not have equal pixel sizes!')
%warning('Datasets do not have equal pixel sizes, auto-rescaling projections')
% use FFT base rescaling -> apply illumination function first to remove
% effect of the noise out of the reconstruction region
%rot_fun = @(x,sx,sy)(utils.imrescale_frft(x .* par.illum_sum, sx, sy)) ./ ( max(0,utils.imrescale_frft(par.illum_sum,sx,sy))+1e-2*max(par.illum_sum(:)));
%stack_object = tomo.block_fun(rot_fun,stack_object, pixel_scale(:,1),pixel_scale(:,2));
%pixel_scale = [1,1];
end
par.pixel_scale = pixel_scale;
par.pixel_size = pixel_size;
par.energy = energy;
%% clip the projections ampltitude by quantile filter
if par.clip_amplitude_quantile < 1
MAX = quantile(reshape(abs(fp16.get(stack_object(1:10:end,1:10:end,:))), [], par.num_proj), par.clip_amplitude_quantile ,1);
MAX = reshape(MAX,1,1,par.num_proj);
clip_fun = @(x,M)(min(abs(x),M) .* x ./ (abs(x) + 1e-5));
stack_object = tomo.block_fun(clip_fun,stack_object, MAX, struct('use_GPU', true));
end
if size(stack_object,3) ~= length(theta) || length(theta) ~= par.num_proj
error('Inconsistency between number of angles and projections')
end
%{
if ~isempty(par.tomo_id) && all(par.tomo_id > 0)
% sanity safety check, all loaded angles correpont to the stored angles
[~,theta_test] = prepare.load_angles(par, par.scanstomo, [], false);
if max(abs(theta - theta_test)) > 180/par.num_proj/2
error('Some angles have angles different from expected')
end
end
%}
%% replot angle
plot_angles = true;
if par.verbose_level && plot_angles
plotting.smart_figure(1);
subplot(2,1,1)
plot(par.scanstomo,theta,'ob'); grid on;
xlim(par.scanstomo([1,end]))
%legend('Tilt angles')
xlabel('Scan #')
ylabel('Tilt angles')
%[anglessort,~] = sort(theta);
subplot(2,1,2)
plot(diff(theta))
ylabel('Angle increment')
%title('Angular spacing');
grid on;
xlim([1,par.num_proj-1])
if par.windowautopos
screensize = get( groot, 'Screensize' );
win_size = [946 815];
set(gcf,'Outerposition',[139 min(163,screensize(4)-win_size(2)) win_size]); %[left, bottom, width, height]
end
title('Measured angles')
drawnow
end
end
+68
View File
@@ -0,0 +1,68 @@
% LOAD_PROJECTIONS_NUM_APS load reconstructed projections numbers
% created by YJ based on PSI's function
% Only read scan numbers, usedful for debugging
% Inputs:
% **par - parameter structure
% **exclude_scans - list of scans to be excluded from loading, [] = none
% **theta - angles of the scans
function [scanstomo] = load_projections_num_aps(par, exclude_scans, theta)
import ptycho.*
import utils.*
import io.*
import plotting.*
scanstomo = par.scanstomo;
% avoid loading scans listed in 'exclude_scans'
if ~isempty(exclude_scans)
ind = ismember(scanstomo, exclude_scans);
scanstomo(ind) = [];
theta(ind) = [];
end
verbose(1,'Checking available files')
missing_scans = [];
proj_file_names = {};
proj_recon_method = {};
proj_roi = {};
proj_scanNo = {};
for num = 1:length(scanstomo)
progressbar(num, length(scanstomo))
%proj_file_names{num} = find_ptycho_filename(par.analysis_path,scanstomo(num),par.fileprefix,par.filesuffix, par.file_extension);
%proj_file_names{num} = find_projection_files_names_aps(par, scanstomo(num));
[proj_file_names{num},proj_recon_method{num},proj_roi{num},proj_scanNo{num}] = find_ML_recon_files_names(par, scanstomo(num));
%disp(proj_file_names{num})
if isempty(proj_file_names{num})
missing_scans(end+1) = scanstomo(num);
end
end
verbose(par.verbose_level); % return to original settings
if ~isempty(missing_scans)
ind = ismember(scanstomo, missing_scans);
verbose(1,['Scans not found are ' num2str(missing_scans)])
verbose(1,['Projections not found are ' num2str(find(ind))])
scanstomo(ind) = [];
theta(ind) = [];
proj_file_names(ind) = [];
proj_recon_method(ind) = [];
proj_roi(ind) = [];
proj_scanNo(ind) = [];
else
verbose(1,'All projections found')
end
num_proj = length(scanstomo);
tic
if num_proj == 0
verbose(0, 'No new projections loaded')
return
end
end
@@ -0,0 +1,87 @@
% MAKE_SYNTHETIC_PROJECTIONS creates projections that can be used as
% initial guess for ptychography reconstruction in order to solver
% iterativelly the ptychotomo task
%
% merged = make_synthetic_projections(stack_object, sinogram_abs, sinogram_phase,total_shift,object_ROI)
%
% Inputs:
% **stack_object - measured projections
% **sinogram_abs - aligned absorbtion sinogram
% **sinogram_phase - aligned phase sinogram
% **total_shift - reconstructed shifts oft the measured projections
% **object_ROI - reliable region of the projections
% Outputs:
% merged - merged projections using stack_object, sinogram_abs and sinogram_phase
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 merged = make_synthetic_projections(stack_object, sinogram_abs, sinogram_phase,total_shift,object_ROI)
[Nlayers,Nw,~] = size(sinogram_abs);
Npx_proj = [size(stack_object,1),size(stack_object,2)];
assert(length(object_ROI{1}) == Nlayers && length(object_ROI{2}) == Nw, 'Reconstructed tomograms have to contain the full field of view, ie vert_range = object_ROI{1}' )
if isreal(sinogram_abs) && isreal(sinogram_phase)
sinogram_abs = exp(-sinogram_abs);
sinogram_phase = exp(-1i*sinogram_phase);
else
sinogram_phase = sinogram_abs ./ (abs(sinogram_abs)+1e-3);
sinogram_abs = abs(sinogram_abs);
end
% find reliability region
win = tukeywin(Nw, 0.2)'.*tukeywin(Nlayers, 0.2);
win = utils.crop_pad(win, Npx_proj);
% find amplitude correction
corr = math.sum2(abs(stack_object(object_ROI{:},:)) .* sinogram_abs) ./ math.sum2(sinogram_abs.^2);
% merge phase and amplitude from tomo and measurements
merged = win.*utils.crop_pad(sinogram_phase,Npx_proj) + (1-win).*stack_object ./ (abs(stack_object)+1e-2);
% enforce phasor
merged = merged ./ (abs(merged) + 1e-2);
% apply amplitude
merged = merged .* ((win .* corr .* utils.crop_pad(sinogram_abs,Npx_proj) + (1-win).* abs(stack_object)));
clear sinogram_phase sinogram_abs
if ~isempty(total_shift)
% apply shift to match the original data
merged = utils.imshift_fft(merged, -total_shift);
end
end
+114
View File
@@ -0,0 +1,114 @@
% PLOT_SAMPLE_STABILITY plot time evolution of the sample stability estimated from the
% OMNY intererometers, useful to detect unexpected behaviour / setup vibrations
%
% poor_projections = plot_sample_stability(par, scanstomo,plot_stability, vibrations_threshold)
%
% Inputs:
% ++scanstomo - number of the scans that will be checked
% ++plot_stability - (bool, default = true)
% ++vibrations_threshold - (default = pixel_size), acceptable level of vibrations in nanometers
% *returns*
% **poor_projections - (bool array), true if the projection stability is worse than vibrations_threshold
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 poor_projections = plot_sample_stability(par, scanstomo, plot_stability, vibrations_threshold)
if nargin < 3
plot_stability = true;
end
if nargin < 4
vibrations_threshold = par.pixel_scale;
end
num_proj = length(scanstomo);
if num_proj == 0
poor_projections = [];
return
end
for ii = 1:num_proj
utils.progressbar(ii,num_proj)
out = beamline.read_omny_pos(sprintf(par.omnyposfile, scanstomo(ii)));
std_err(ii,1) = quantile(out.Stdev_x_st_fzp,0.8);
std_err(ii,2) = quantile(out.Stdev_y_st_fzp,0.8);
d = dir(sprintf(par.omnyposfile, scanstomo(ii)));
scan_time(ii) = d.datenum;
end
if plot_stability
plotting.smart_figure(5457)
% Create the first axes
hax1 = axes();
% Plot something here
line(scan_time,std_err*1e3,'color', 'white');
datetick('x','HH:MM')
xlim([min(scan_time), max(scan_time)])
grid on
ylabel('Average vibrations [nm]')
xlabel('Scan time')
% Create a transparent axes on top of the first one with it's xaxis on top
% and no ytick marks (or labels)
hax2 = axes('Position', get(hax1, 'Position'), ... % Copy position
'XAxisLocation', 'top', ... % Put the x axis on top
'YAxisLocation', 'right', ... % Doesn't really matter
'xlim', [scanstomo(1),scanstomo(end)], ... % Set XLims to fit our data
'Color', 'none', ... % Make it transparent
'YTick', []); % Don't show markers on y axis
% Plot data with a different x-range here
hplot2 = line(scanstomo,std_err*1e3, 'Parent', hax2);
legend({'Horizontal','Vertical'}, 'Location', 'Best')
xlabel(hax2, 'Scan number')
% Link the y limits and position together
linkprop([hax1, hax2], {'ylim', 'Position'});
plotting.hline(vibrations_threshold)
end
% estimate poor projections
poor_projections = any(std_err > 1e6*vibrations_threshold,2);
end
@@ -0,0 +1,22 @@
% Normally the tomography angles can be read from an angles file from OMNY,
% flOMNI, or LaMNI. However in one case this did not work and we had to
% extract the angle from the header of the ptycho positions file. This is a
% script that allows just that.
% Usage example:
% strpatt = '~/Data10/specES1/scan_positions/scan_%05d.dat';
% scannums = [271:277];
% angles = prepare.read_angles_from_position_files(strpatt,scannums)
function angles = read_angles_from_position_files(strpatt,scannums)
filenames = cell(numel(scannums),1);
angles = nan(numel(scannums),1);
for ii = 1:numel(scannums)
filenames{ii} = sprintf(strpatt,scannums(ii));
try
aux = beamline.read_omny_pos(filenames{ii});
angles(ii) = aux.lsamrot_encoder;
catch
fprintf('Scan %i is missing \n', scannums(ii))
end
end
+137
View File
@@ -0,0 +1,137 @@
% SAVE_MERGED_PROJECTIONS save projections generated from tomography to be loaded as initial guess in
% ptychography , projections are saved to the same path from where they were loaded
% just with a differenent suffix
%
% save_merged_projections(par,stack_object, volData_c, theta, total_shift, name_sufix)
%
% Inputs:
% **par - tomography parameters structure
% **stack_object - array of complex valued projections
% **volData_c - complex valued sample reconstruction
% **theta - measured angles
% **total_shift - reconstructed shift of the projections
% **name_sufix - extra sufix added to the saved projections
% *returns*
% ++prepared_objects - prepared lsit of structures for 3D ptychotomo
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 prepared_objects = save_merged_projections(par,stack_object, theta, total_shift, name_sufix)
import ptycho.*
import utils.*
import io.*
import plotting.imagesc3D
scanstomo = par.scanstomo;
num_proj = length(scanstomo);
verbose(0,'Checking available files')
verbose(0); % make it quiet
missing_projections = [];
proj_file_names = cell(length(scanstomo),1);
for num = 1:length(scanstomo)
progressbar(num, length(scanstomo))
path = find_projection_files_names(par, scanstomo(num));
if isempty(path)
missing_projections(end+1) = num;
continue
end
% take the last file fitting the constraints
proj_file_names{num} = path;
end
verbose(par.verbose_level); % return to original settings
if ~isempty(missing_projections)
verbose(1,['Did not find following scans:', num2str(missing_projections)])
end
[Nx,Ny,~] = size(stack_object);
verbose(1,'Preparing projections')
for num = 1:num_proj
progressbar(num, num_proj)
[filepath,name, ext] = fileparts(proj_file_names{num});
out_name = [name,'_',name_sufix];
fullpath = [filepath, '/', out_name,'.mat'];
% load original data
d = io.load_ptycho_recons(proj_file_names{num}, 'recon');
positions = h5read(proj_file_names{num}, '/reconstruction/p/positions')';
obj_size = size(d.object);
object = zeros(obj_size,'like',stack_object);
object(1:min(end,Nx),1:min(end,Ny)) = ...
stack_object(1:min(end,obj_size(1)),1:min(end,obj_size(2)),num);
% load the complex projections from fp16 precision if used
object = fp16.get(object);
%% prepare structure for ptychtomo solver
prepared_objects{num}.object = object;
prepared_objects{num}.positions = positions;
prepared_objects{num}.illum_sum = [];
prepared_objects{num}.weight = [];
prepared_objects{num}.angle = theta(num);
prepared_objects{num}.position_offset = round(total_shift(num,:));
% testme
prepared_objects{num}.probe = utils.imshift_fft(d.probe, total_shift(num,:) -round(total_shift(num,:))) ;
prepared_objects{num}.scan_id = scanstomo(num);
prepared_objects{num}.proj_id = num;
% phase removal in probe
[~,~, gamma_x, gamma_y] = utils.stabilize_phase(d.object,object, 'binning', 8);
% remove ramp from probe as well
xramp = pi*(linspace(-1,1,par.asize(1)))';
yramp = pi*(linspace(-1,1,par.asize(2)));
c_offset = xramp.*gamma_x*par.asize(1) + yramp.*gamma_y*par.asize(2);
prepared_objects{num}.probe = prepared_objects{num}.probe .* exp(1i*c_offset);
% plotting.imagesc3D( angle(d.object .* conj(object(1:4:end,1:4:end)) ))
%drawnow
end
end