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
+170
View File
@@ -0,0 +1,170 @@
% FBP_ZSPLIT filtered back projection with simple splitting along vertical direction -> works only for classical tomography, not for laminography
%
% [rec] = FBP_zsplit(sinogram, cfg, vectors, varargin)
%
% Inputs:
% **sino - sinogram (Nlayers x width x Nangles)
% **cfg - config struct from astra.ASTRA_initialize
% **vectors - vectors of projection rotation generated by astra.ASTRA_initialize
% *optional*
% ** split =[1,1,1] - split the solved volume, split(3 is used to split in separated blocks, split(1:2) is used inside Atx_partial to du subplitting for ASTRA
% ** valid_angles = [] - list of valid angles, []==all are valid
% ** filter = 'ram-lak' - name of the FBP filter
% ** filter_value = 1 - fitlering value for the FBP filter
% ** deformation_fields = {} - cell 3x1 of deformation arrays
% ** GPU = [] - list of GPUs to be used in reconstruction
% ** split_sub = [1,1,1] - splitting of the sub block on smaller tasks in the Atx_partial method , 1 == no splitting
% ** verbose = 1 - verbose = 0 : quiet, verbose : standard info , verbose = 2: debug
% ** use_derivative = false - calculate reconstruction from the phase derivative
% ** extra_padding = false - surround the projection by void space to enforce zero around tomogram
% ** keep_on_GPU - if false, move the reconstruction back from GPU before returning
% ** determine_weights = true - reweight projections if the angles are not equidistant
% ** mask = [] - apply mask on reconstruction , inputs is 2D or 3D array
% ** padding = 0 - zero padding is improving standard tomography. 'symmetric' is good for lamino / local tomo
% ** only_filter_sinogram = false - return filtered sinogram, do not backproject
%
% *returns*
% ++rec - reconstructed volume
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  Except where otherwise noted, this work is licensed under a          |
%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            |
%|  International (CC BY-NC-SA 4.0) license.                             |
%|                                                                       |
%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    |
%|                                                                       |
%|      Author: CXS group, PSI  |
%*-----------------------------------------------------------------------*
% You may use this code with the following provisions:
%
% If the code is fully or partially redistributed, or rewritten in another
% computing language this notice should be included in the redistribution.
%
% If this code, or subfunctions or parts of it, is used for research in a
% publication or if it is fully or partially rewritten for another
% computing language the authors and institution should be acknowledged
% in written form in the publication: Data processing was carried out
% using the cSAXS matlab package developed by the CXS group,
% Paul Scherrer Institut, Switzerland.
% Variations on the latter text can be incorporated upon discussion with
% the CXS group if needed to more specifically reflect the use of the package
% for the published work.
%
% A publication that focuses on describing features, or parameters, that
% are already existing in the code should be first discussed with the
% authors.
%
% This code and subroutines are part of a continuous development, they
% are provided as they are without guarantees or liability on part
% of PSI or the authors. It is the user responsibility to ensure its
% proper use and the correctness of the results.
function [rec] = FBP_zsplit(sinogram, cfg0, vectors0, varargin)
par = inputParser;
par.KeepUnmatched = true;
par.addOptional('split', [1,1,1,1]) % split the solved volume, split(3) is used to split in separated blocks, split(1:2) is used inside Atx_partial to du subplitting for ASTRA
par.addOptional('keep_on_GPU', false) % keep results in GPU
par.addOptional('verbose', true) % verbosity level
par.addOptional('mask', []) % apply reconstruction mask, inputs is 2D or 3D array
par.addOptional('GPU', []) % ids of the used GPUs
par.addOptional('use_GPU', true, @islogical) % if false, use CPU based reconstruction
par.parse(varargin{:})
r = par.Results;
[Nlayers,Nw,Nproj] = size(sinogram);
cfg0.iProjAngles = Nproj;
assert(cfg0.iProjU == Nw, 'Wrong sinogram width')
assert(cfg0.iProjV == Nlayers, 'Wrong sinogram height')
assert(mod(Nw,2)==0, 'Only even width of sinogram is supported')
assert(length(r.GPU)<=1, 'For multiGPU use tomo.FBP function')
if r.verbose > 0; fprintf('====== FBP split ==========\n'); end
Nelements = numel(sinogram) ;
if gpuDeviceCount
Nblocks = r.split(3);
r.split(3) = 1;
gpu = gpuDevice;
% empirical condition, may be too pesimistic
Nblocks = max(Nblocks,ceil( (2*8*4* Nelements) / gpu.AvailableMemory)) ;
Nblocks = max(Nblocks, Nelements/ double(intmax('int32')));
Nblocks = max(Nblocks, length(r.GPU));
else
% CPU processing
max_block_size = min(utils.check_available_memory*1e6, 20e9); %% work with 10GB blocks
Nblocks = ceil( (6*8* Nelements) / max_block_size) ;
end
if ~r.use_GPU
rec = tomo.FBP_CPU(sinogram, cfg0, vectors0, varargin{:}, 'verbose', 0);
return
end
if Nblocks == 1 || cfg0.iProjU>4096 || cfg0.iProjV>4096
% for small datatsets process everthing in a single block
if Nblocks == 1
sinogram = utils.Garray(sinogram);
end
rec = tomo.FBP(sinogram, cfg0, vectors0, varargin{:}, 'verbose', 0);
if ~isempty(r.mask)
rec = rec .* r.mask;
end
if ~r.keep_on_GPU
rec = gather(rec);
end
else
%% split tomogram into vertical blocks, it is the most effecient way how to calculate it
% -> upload each block to GPU and keep it there for maximal speed
Nl_small = ceil(Nlayers / Nblocks);
% keep on GPU only if the volume is small enough !!
r.keep_on_GPU = r.keep_on_GPU && (4*cfg0.iVolX*cfg0.iVolY*cfg0.iVolZ) < gpu.AvailableMemory / 10;
if r.keep_on_GPU && gpuDeviceCount
rec = gpuArray.zeros(cfg0.iVolX, cfg0.iVolY, cfg0.iVolZ, 'single');
else
rec = zeros(cfg0.iVolX, cfg0.iVolY, cfg0.iVolZ, 'single');
end
for ii = 1:Nblocks
if r.verbose>0; utils.progressbar(ii, Nblocks); end
ind = 1+Nl_small*(ii-1) : min(Nlayers, Nl_small*ii);
if isempty(ind); continue; end
if isa(sinogram, 'gpuArray')
% if on GPU, use matlab memcpy
sinogram_small = sinogram(ind,:,:);
else
% fast MEX memory copy
pos = [Nl_small*(ii-1),0];
sinogram_small = zeros(length(ind),Nw,Nproj, 'like', sinogram);
sinogram_small = utils.get_from_3D_projection(sinogram_small, sinogram, repmat(pos,Nproj,1), 1:Nproj);
end
cfg = cfg0;
vectors = vectors0;
cfg.iProjV = size(sinogram_small,1);
cfg.iVolZ = cfg.iProjV;
vectors(:,4:6) = vectors(:,4:6) - vectors(:,10:12)*(cfg.iProjV - cfg0.iProjV)/2;
sinogram_small = utils.Garray(fp16.get(sinogram_small));
%% call standard FBP on the data that are on GPU
try
rec_tmp = tomo.FBP(sinogram_small, cfg, vectors,varargin{:}, 'split', r.split, 'verbose', 0, 'keep_on_GPU', r.keep_on_GPU);
catch err
if strcmp(err.identifier, 'parallel:gpu:array:OOM')
warning(err.message)
end
keyboard
end
rec(:,:,ind) = rec_tmp;
end
end
end