Files
fold_slice/+astra/ASTRA_initialize.m
2026-08-07 15:56:42 +09:00

300 lines
12 KiB
Matlab

% ASTRA_INITIALIZE Generate inputs needed for astra MEX wrapper
%
% [cfg, vectors] = ASTRA_initialize(Npix, size_projection,angles,lamino_angle, tilt_angle, pixel_scale, rotation_center)
%
% Inputs for angular geometry (!! all angles are expected in degress !!):
% **Npix - size of tomogram
% **size_projection - size of sinogram (Nlayers, width, Nangles)
% **angles - rotation angles of projections in degrees
% *optional*:
% **lamino_angle - laminography angle / angles in degrees. lamino_angle ==
% 90 is standard tomography , default = 90
% **tilt_angle - tilt of camera with respect to the rotation axis coordinates, in degrees, default = 0
% **pixel_scale - scale of pixels in tomogram compares to the
% projection pixel size, default = 1
% **rotation_center - center of rotation coordinates, default = size_projection/2
% **skewness_angle - distorsion of parallel axis by [1, sind(alpha); 0, 1]
%
% Inputs for rotation matrix geometry:
% **Npix - size of tomogram
% **size_projection - size of sinogram (Nlayers, width, Nangles)
% **rotation_matrix - R is a 3x3xn matrix, for n projections
%
% Outputs:
% ++cfg - config structure for ASTRA mex wrapper
% ++vectors - parameter vector for each angle
%*-----------------------------------------------------------------------*
%| |
%| 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 [cfg, vectors] = ASTRA_initialize(Npix, size_projection, varargin)
use_rotmat = length(varargin) == 1 && size(varargin{1},1) == 3 && size(varargin{1},2) == 3 ;
if use_rotmat
% rotation matrices were provided
rot_mat = varargin{1};
Nangles = size(rot_mat,3);
r.rotation_center = size_projection/2; % only centered geometry is supported when rotation matrix is provided
else
% angles and other parameters were provided
par = inputParser;
par.KeepUnmatched = true;
%% ALL angles are assumed in degrees
par.addRequired('angles') % rotation angles of projections in degrees
par.addOptional('lamino_angle', 90, @isnumeric) % laminography angle / angles in degrees. lamino_angle == 90 is standard tomography , default = 90
par.addOptional('tilt_angle', 0, @isnumeric) % tilt of camera with respect to the rotation axis coordinates, in
par.addOptional('pixel_scale', [1,1], @isnumeric) % scale of pixels in tomogram compares to the projection pixel size, default = 1
par.addOptional('rotation_center', size_projection/2, @isnumeric) % center of rotation cooridinates, default = size_projection/2
par.addOptional('skewness_angle', 0) % distorsion of parallel axis by [1, sind(alpha); 0, 1]
par.addParameter('show_geometry', false) % plot also a geometry for each projection
par.parse(varargin{:})
r = par.Results;
Nangles = length(r.angles);
end
% angles should be sorted in order to maximize performance of the astra
% toolbox -> better use of texture memory
assert(math.isint(Npix), 'Npix is not integer');
assert(math.isint(size_projection), 'size_projection is not integer');
if isscalar(Npix)
Npix(2) = Npix;
end
if length(Npix) == 2 && all(r.lamino_angle == 90)
Npix(3) = size_projection(1); % default behaviour is to have same number of layers in reconstruction and in laminography
elseif length(Npix) == 2 && any(r.lamino_angle ~= 90)
error('All three dimensions of the volume size has to be specified for the laminograhy geometry')
end
cfg.iVolX = Npix(1);
cfg.iVolY = Npix(2);
cfg.iVolZ = Npix(3);
cfg.iProjAngles = Nangles;
cfg.iProjU = size_projection(2);
cfg.iProjV = size_projection(1);
cfg.iRaysPerDet = 1;
cfg.iRaysPerDetDim = 1;
cfg.iRaysPerVoxelDim = 1;
source_distance = 1; % currenlty not implemented in the ASTRA wrapper
if use_rotmat
[vectors] = astra_convert_R_vectors(rot_mat,Nangles);
else
% compatibility with iradonfast
r.angles = r.angles + 90;
cfg.lamino_angle = r.lamino_angle;
cfg.pixel_scale = r.pixel_scale;
cfg.tilt_angle = r.tilt_angle;
cfg.skewness_angle = r.skewness_angle;
vectors = ASTRA_get_geometry(r.angles, r.lamino_angle, r.tilt_angle, source_distance,r.pixel_scale,r.skewness_angle,r.show_geometry);
end
%%%% apply geometry correction to shift reconstruction into center %%%%%%%%%%%%%%%%%%%%%%%%%%
vectors(:,4:6) = vectors(:,4:6) -(vectors(:,10:12).*(r.rotation_center(:,1) )+vectors(:,7:9).*(r.rotation_center(:,2) ));
end
function vectors = ASTRA_get_geometry(angles, lamino_angle, tilt_angle, source_distance,pixel_scale,skewness_angle,show)
Nangles = numel(angles);
% angles should be sorted in order to maximize performance of the astra
% toolbox -> better use of texture memory
angles = deg2rad(angles(:));
lamino_angle = pi/2 - deg2rad(lamino_angle);
tilt_angle = deg2rad(tilt_angle);
skewness_angle = deg2rad(skewness_angle);
if isscalar(lamino_angle)
lamino_angle = lamino_angle .* ones(Nangles,1);
end
if isscalar(tilt_angle)
tilt_angle = tilt_angle .* ones(Nangles,1);
end
if isscalar(skewness_angle)
skewness_angle = skewness_angle .* ones(Nangles,1);
end
if isscalar(pixel_scale) || numel(pixel_scale) == 2
pixel_scale = bsxfun(@times, pixel_scale , ones(Nangles,2));
end
% We generate the same geometry as the circular one above.
vectors = zeros(Nangles, 12);
% ray direction
vectors(:,1) = sin(angles).*cos(lamino_angle);
vectors(:,2) = -cos(angles).*cos(lamino_angle);
vectors(:,3) = sin(lamino_angle);
vectors(:,1:3) = vectors(:,1:3) .*source_distance;
% center of detector
vectors(:,4:6) = 0;
% vector from detector pixel (0,0) to (0,1)
vectors(:,7) = cos(angles)./pixel_scale(:,1);
vectors(:,8) = sin(angles)./pixel_scale(:,1);
vectors(:,9) = 0/pixel_scale(:,1);
% vector from detector pixel (0,0) to (1,0)
% cross(vectors(i,1:3), vectors(i,7:9))
% dot(vectors(i,1:3), vectors(i,7:9))
vectors(:,10) = - sin(lamino_angle).*sin(angles)./pixel_scale(:,2);
vectors(:,11) = sin(lamino_angle).*cos(angles)./pixel_scale(:,2);
vectors(:,12) = cos(lamino_angle)./pixel_scale(:,2);
% Rodrigues' rotation formula - rotate detector in plane
% perpendicular to the beam axis
if any(tilt_angle ~= 0)
for i = 1:Nangles
vectors(i,7:9)=vectors(i,7:9).*cos(tilt_angle(i)) + ...
cross(vectors(i,1:3), vectors(i,7:9)).*sin(tilt_angle(i)) + ...
(vectors(i,1:3)*dot(vectors(i,1:3),vectors(i,7:9))).*(1-cos(tilt_angle(i)));
vectors(i,10:12)=vectors(i,10:12).*cos(tilt_angle(i)) + ...
cross(vectors(i,1:3), vectors(i,10:12)).*sin(tilt_angle(i)) + ...
(vectors(i,1:3).*dot(vectors(i,1:3),vectors(i,10:12))).*(1-cos(tilt_angle(i)));
end
end
% search also for skewness => the same as rotation, but rotate
% only one axis of the detector !!
if any(skewness_angle ~= 0)
for i = 1:Nangles
vectors(i,10:12)=vectors(i,10:12).*cos(skewness_angle(i)/2) + ...
cross(vectors(i,1:3), vectors(i,10:12)).*sin(skewness_angle(i)/2) + ...
(vectors(i,1:3).*dot(vectors(i,1:3),vectors(i,10:12))).*(1-cos(skewness_angle(i)/2));
end
end
%% PLOT THE CURRENT SETUP
if show
for i = 1:Nangles
draw_projection_geometry(vectors(i,:))
end
end
end
function [vectors] = astra_convert_R_vectors(rot_mat,Nangles)
% R is defined as in the arbitrary projection code by Manuel
% This integrates along z (2nd index) and so this code follows this
% convention.
pixel_scale(1:Nangles,1:2) = [1];
convert_matrix = [0 0 1
0 1 0
1 0 0];
for ii=1:size(rot_mat,3)
rot_mat(:,:,ii)=rot_mat(:,:,ii)*convert_matrix;
end
vectors = zeros(Nangles, 12);
for i = 1:Nangles
% before starting to mess around: works with a correction matrix
% with the magnetic contrast, but not for the laminography
vectors(i,1) = -rot_mat(3,1,i);
vectors(i,2) = -rot_mat(3,3,i);
vectors(i,3) = -rot_mat(3,2,i);
vectors(i,4:6) = 0;
vectors(i,7) = rot_mat(1,1,i)/pixel_scale(i,1);
vectors(i,8) = rot_mat(1,3,i)/pixel_scale(i,1);
vectors(i,9) = rot_mat(1,2,i)/pixel_scale(i,1);
vectors(i,10) = rot_mat(2,1,i)/pixel_scale(i,2);
vectors(i,11) = rot_mat(2,3,i)/pixel_scale(i,2);
vectors(i,12) = rot_mat(2,2,i)/pixel_scale(i,2);
end
end
function draw_projection_geometry(vectors)
% show geometry saved in the "vectors" matrix
ray = vectors(1:3);
c_center = vectors(4:6)-ray;
c_origin = c_center - vectors( 7:9)/2-vectors( 10:12)/2;
k = 6;
n = 2^k-1;
[x,y,z] = sphere(n);
c = hadamard(2^k);
s = 0.5;
figure(15)
surf(vectors(4)+s*x,vectors(5)+s*y,vectors(6)+s*z,c);
shading flat
colormap([1 1 0; 0 1 1])
hold all
plot3d_vec(c_origin, vectors( 7:9), 'r');
plot3d_vec(c_origin, vectors( 10:12), 'r');
plot3d_vec(c_origin+vectors( 7:9), vectors( 10:12), 'r');
plot3d_vec(c_origin+vectors( 10:12), vectors( 7:9), 'r');
% draw "pixels" on a 10x10 grid
for x = linspace(0,1,10)
plot3d_vec(c_origin+x*vectors( 10:12), vectors( 7:9), 'r:');
plot3d_vec(c_origin+x*vectors( 7:9),vectors( 10:12), 'r:');
end
plotting.mArrow3(c_center+ray*2,c_center, 'color', 'blue', 'stemWidth',0.02,'facealpha',0.5);
hold off
axis([-1,1,-1,1,-1,1])
drawnow
end
function h_out = plot3d_vec(x0, vec, varargin)
h = plot3(x0(1)+[0,vec(1)], ...
x0(2)+[0,vec(2)], ...
x0(3)+[0,vec(3)], varargin{:});
if nargout > 1
h_out = h;
end
end