% 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