Files
2026-08-07 15:56:42 +09:00

457 lines
17 KiB
Matlab
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
% Call function without arguments for instructions on how to use it
% Filename: $RCSfile: prep_integ_masks.m,v $
%
% $Revision: 1.9 $ $Date: 2016/01/21 14:51:57 $
% $Author: $
% $Tag: $
%
% Description:
% prepare masks for the radial integration of SAXS patterns
%
% Note:
% Call without arguments for a brief help text.
%
% Dependencies:
% - image_read
% - pilatus_valid_pixel_roi
%
% history:
%
% September 4th 2009:
% correct in help text one of the RadiusFrom to RadiusTo
%
% May 9th 2008: 1st documented version
%
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 [ integ_masks ] = prep_integ_masks(filename, center_xy, varargin)
import beamline.pilatus_valid_pixel_roi
import io.image_read
import plotting.display_valid_mask
import utils.pixel_to_q
% set number of radii
no_of_radii = 0;
% number of angular segments per radius
no_of_segments = 1;
% pixel size in mm
pixel_size_mm = [];%.172;
% detector distance in mm
det_dist_mm = [];%2000;
calculate_q=0; %only calculate q if exact detector distance is given
% wavelength (unit inconsequential, will be reflected in q)
lambda = [];%1;
% output directory for the masks
out_dir = '~/Data10/analysis/data/';
filename_valid_mask = [ out_dir 'pilatus_valid_mask.mat' ];
filename_integ_masks = [ out_dir 'pilatus_integration_masks.mat' ];
% output figure number
fig_no = 240;
% save integration masks
save_data = 1;
% display valid pixel mask
display_valid_mask_flag = 1;
% detector number
det_no= 1;
% angular range to be excluded to cut out the beam stop
bs_angle_from = 0;
bs_angle_to = 0;
% check minimum number of input arguments
if (nargin < 2)
fprintf('\nUsage:\n');
fprintf('[integ_masks]=%s( filename, center_xy [[,<name>,<value>]...]);\n',mfilename);
fprintf('Prepare the masks for an efficient radial integration.\n');
fprintf('The optional angular range in degree can be used to cut out a beam stop.\n');
fprintf('Angle 0 is horizontally to the left, positive in counterclockwise direction.\n');
fprintf('The specified data file is loaded and some of the integration masks are plotted into that frame.\n');
fprintf('\n');
fprintf('The optional <name>,<value> pairs are:\n');
fprintf('''NormalXY'',[x y] pixel coordinates, from where the detector normal points\n');
fprintf(' toward the sample. Default is equal to center_xy\n');
fprintf('''PixelSize_mm'',<double> pixel size in mm, default is %.3f\n',pixel_size_mm);
fprintf('''DetDist_mm'',<double> detector distance in mm, default is %.1f\n',det_dist_mm);
fprintf('''Wavelength'',<double> wavelength. The units chosen here will determine the units of q\n');
fprintf(' The defaults is %.1f\n',lambda);
fprintf('''NoOfRadii'',<integer> radial integration start radius, default is %d\n',no_of_radii);
fprintf(' or ,<vector>, defining the limits of radius bins\n');
fprintf('''NoOfSegments'',<integer> Number of angular segments. If an integer, this number of equally wide azimuthal\n')
fprintf(' bins over 360 degrees are created. default is %d\n',no_of_segments);
fprintf(' or ,<vector>, defining the limits of angular bins\n');
fprintf('''SaveData'',<0-no,1-yes> save the integration masks, default is %d\n',save_data);
fprintf('''FilenameValidMask'',<path and filename> Matlab file with the valid pixel indices ind_valid,\n');
fprintf(' default is %s\n',filename_valid_mask);
fprintf('''FilenameIntegMasks'',<path and filename> output file name for the structure integ_masks,\n');
fprintf(' default is %s\n',filename_integ_masks);
fprintf('''FigNo'',<integer> number of the figure in which the result is displayed\n');
fprintf('''DetNo'',<integer> number of detector 1 for SAXS and 2 for WAXS\n');
fprintf(' Default is 1 (SAXS)\n');
fprintf('''BeamstopAngleFrom'',<float> exclude an angular region from the integration, default for the start value is %d\n',...
bs_angle_from);
fprintf('''BeamstopAngleTo'',<float> exclude an angular region from the integration, default for the end value is %d\n',...
bs_angle_to);
fprintf('\n');
fprintf('\n');
fprintf('The file name should be the name of a single file without wildcards\n');
fprintf('that is displayed as an example.\n');
fprintf('The image file has no other function beyond being displayed as example.\n');
fprintf('Example:\n');
fprintf('[integ_masks]=%s(''~/Data10/pilatus/image.cbf'',[512 512]);\n',...
mfilename);
error('At least the filename and the beam center have to be specified as input parameter.');
end
% check number of center coordinates
if (length(center_xy) ~= 2)
error('The beam center needs to be specified as a two component vector [cen_x cen_y].\n');
end
center_x = center_xy(1);
center_y = center_xy(2);
norm_x = center_x;
norm_y = center_y;
% accept cell array with name/value pairs as well
no_of_in_arg = nargin;
if (nargin == 3)
if (isempty(varargin))
% ignore empty cell array
no_of_in_arg = no_of_in_arg -1;
else
if (iscell(varargin{1}))
% use a filled one given as first and only variable parameter
varargin = varargin{1};
no_of_in_arg = 2 + length(varargin);
end
end
end
% check number of input arguments
if (rem(no_of_in_arg,2) ~= 0)
error('The optional parameters have to be specified as ''name'',''value'' pairs');
end
% parse the variable input arguments
vararg = cell(0,0);
for ind = 1:2:length(varargin)
name = varargin{ind};
value = varargin{ind+1};
switch name
case 'NormalXY'
if (numel(value)==2)
norm_x = value(1);
norm_y = value(2);
end
case 'PixelSize_mm'
pixel_size_mm = value;
case 'DetDist_mm'
det_dist_mm = value;
calculate_q=1;
case 'Wavelength_nm'
lambda = value;
case 'Wavelength'
lambda = value/10;
case 'NoOfRadii'
no_of_radii = value;
case 'NoOfSegments'
no_of_segments = value;
case 'FilenameValidMask'
filename_valid_mask = value;
case 'FilenameIntegMasks'
filename_integ_masks = value;
case 'SaveData'
save_data = value;
case 'DisplayValidMask'
display_valid_mask_flag = value;
case 'FigNo'
fig_no = value;
case 'DetNo'
det_no = value;
case 'BeamstopAngleFrom'
bs_angle_from = value;
case 'BeamstopAngleTo'
bs_angle_to = value;
otherwise
vararg{end+1} = name;
vararg{end+1} = value;
end
end
% MGS - The fact that the detector number is dictating whether to have a
% radius variable or q is not ideal. Additional arguments should be given
% for this such as calculate_q or save_radius_var.
% initialize return arguments
if (det_no == 1)||(det_no == 3)
calculate_radius = true;
elseif (det_no == 2)
calculate_radius = false;
else
error('Only det_no 1, 2, and 3 are recognized')
end
if ( calculate_radius ) && (calculate_q)
integ_masks = struct('radius',[], 'indices',[], 'norm_sum', [],'q',[]);
elseif (calculate_radius) && (calculate_q == 0)
integ_masks = struct('radius',[], 'indices',[], 'norm_sum', []);
elseif (~calculate_radius)
integ_masks = struct('indices',[], 'norm_sum', [],'q',[]);
end
% check radius
%if (exist('r_from','var'))
if (size(no_of_radii)>1)
if (no_of_radii(1) < 1)
%if (r_from < 1)
error('The minimum radius is 1, %d is invalid.',r_from);
end
%end
%if (exist('r_from','var') && exist('r_to','var'))
if (no_of_radii(end) < no_of_radii(1))
%if ((r_to ~= 0) && (r_to < r_from))
error('The maximum radius must be greater than the minimum one, %d is invalid.\n',no_of_radii(end));
end
end
% check number of angular segments
if (no_of_segments < 1)
error('The number of angular segments must be at least 1');
end
if (numel(no_of_segments)>1)
angular_segments = no_of_segments;
no_of_segments = numel(no_of_segments)-1;
else
angular_segments = 360/no_of_segments * (0:no_of_segments);
end
angular_segments = mod(angular_segments, 360);
% check beamstop region
if ((bs_angle_from < 0.0) || (bs_angle_to > 360.0))
error('The angular range for the beam stop region is 0 to 360 degree.');
end
if (bs_angle_to < bs_angle_from)
error('The maximum beam stop angle must be less than or equal to the minimum one.\n');
end
% load the indices of valid pixels
fprintf('loading the valid pixel mask %s\n',filename_valid_mask);
load(filename_valid_mask);
dim_x = valid_mask.framesize(2);
dim_y = valid_mask.framesize(1);
if (~isempty(filename))
% load test frame
frame = image_read(filename,vararg);
% select the first frame for display
frame.data = frame.data(:,:,1);
% in case of less than full detector readout cut out the right part of
% the valid pixel mask
valid_mask = pilatus_valid_pixel_roi(valid_mask,'RoiSize',size(frame.data));
dim_x = size(frame.data,2);
dim_y = size(frame.data,1);
end
% plot valid pixel mask
if (display_valid_mask_flag)
figure(fig_no);
vpm = zeros(dim_y,dim_x);
vpm(valid_mask.indices) = 1;
imagesc(vpm);
axis xy;
axis equal;
axis tight;
title('valid pixels');
set(gcf,'Name','valid pixels');
drawnow;
end
if calculate_radius
%if (exist('r_to','var'))
% choose maximum radius, if specified via r_to=0
if (no_of_radii < 1)
no_of_radii = max( [ sqrt(center_x^2+center_y^2) ...
sqrt((dim_x-center_x)^2+center_y^2) ...
sqrt(center_x^2+(dim_y-center_y)^2) ...
sqrt((dim_x-center_x)^2+(dim_y-center_y)^2) ] );
end
if size(no_of_radii)==1
no_of_radii=1:1:no_of_radii;
end
end
fprintf('preparing the integration masks ...\n');
% create an array of the (x,y) coordinates relative to the beam center and
% convert it to polar coordinates
%
if calculate_radius % For SAXS detector - MGS, should be fixed, why is it neded different calculation for different detectors?
% angular range to be excluded to cut out the beam stop
[ x, y ] = meshgrid( (1:dim_x)-center_x, (1:dim_y)-center_y );
[ theta, rho ] = cart2pol( x, y );
% convert angular range from -pi/pi to 0/360
theta = (theta/pi +1) * 180.0;
% prepare circular masks of the integer width r_step (in pixel)
integ_masks.radius = no_of_radii;
r_step=no_of_radii(2)-no_of_radii(1);
if calculate_q
integ_masks.q = pixel_to_q(no_of_radii,pixel_size_mm,det_dist_mm, 12.39852/lambda);
end
no_of_radii = length(integ_masks.radius);
integ_masks.indices = cell( no_of_radii, no_of_segments );
integ_masks.norm_sum = zeros( no_of_radii, no_of_segments );
seg_inds = cell(no_of_segments,1);
for ind_seg = 1:no_of_segments
seg_from = angular_segments(ind_seg);
seg_to = angular_segments(ind_seg+1);
if (seg_from >= seg_to)
ind_curr = find( ((theta > seg_from) | (theta <= seg_to) ) & ...
((theta <= bs_angle_from) | (theta >= bs_angle_to)) );
else
ind_curr = find( ((theta > seg_from) & (theta <= seg_to) ) & ...
((theta <= bs_angle_from) | (theta >= bs_angle_to)) );
end
% only take valid pixels into account
ind_curr = intersect(ind_curr, valid_mask.indices);
seg_inds{ind_seg} = ind_curr;
end
for ind_r=1:no_of_radii
if (rem(ind_r,100) == 0)
fprintf('%4d / %d',ind_r,no_of_radii);
if (ind_r <= no_of_radii-100)
fprintf(', ');
end
end
r_inds = find( (rho >= integ_masks.radius(ind_r)) & ...
(rho < integ_masks.radius(ind_r)+r_step) );
for ind_seg = 1:no_of_segments
integ_masks.indices{ind_r, ind_seg} = intersect( r_inds, seg_inds{ind_seg} );
% calculate the normalization value (sum of the pixels within the mask)
integ_masks.norm_sum(ind_r, ind_seg) = ...
length( integ_masks.indices{ind_r, ind_seg} );
end
end
fprintf('\n');
else
[ x, y ] = meshgrid( (1:dim_x)-norm_x, (1:dim_y)-norm_y );
if (norm_x == center_x && norm_y == center_y)
[ theta, rho ] = cart2pol( x, y );
q = 4*pi/lambda*sin(atan2(rho,det_dist_mm/pixel_size_mm)/2);
% convert angular range from -pi/pi to 0/360
theta = theta/pi*180.0;
else
if (norm_x ~= center_x)
angle = atan((norm_x - center_x) / (det_dist_mm/pixel_size_mm));
z = -x*sin(angle) + det_dist_mm/pixel_size_mm*cos(angle);
x = x*cos(angle) + det_dist_mm/pixel_size_mm*sin(angle);
else
fprintf('not implemented yet!!!\n');
exit
end
q = 4*pi/lambda*sin(atan2(sqrt(x.^2 + y.^2),z)/2);
theta = atan2(y,x)/pi*180;
end
t_1d = reshape(theta(valid_mask.indices),1,[]);
q_1d = reshape(q(valid_mask.indices),1,[]);
t_ed = linspace( -180, 180,1e0+1);
integ_masks.theta = t_ed(1:end-1);
integ_masks.theta_end = t_ed(end);
q_ed = linspace(min(q_1d),max(q_1d),1e3+1);
integ_masks.q = q_ed(1:end-1);
integ_masks.q_end = q_ed(end);
[~,t_bin] = histc(t_1d,t_ed);
[~,q_bin] = histc(q_1d,q_ed);
integ_masks.indices = cell(numel(q_ed)-1,numel(t_ed)-1);
integ_masks.norm_sum = zeros(size(integ_masks.indices));
for q_i=1:numel(q_ed)-1
for t_i=1:numel(t_ed)-1
integ_masks.indices{q_i,t_i} = ...
valid_mask.indices(and(q_bin==q_i,t_bin==t_i));
integ_masks.norm_sum(q_i,t_i) = numel(integ_masks.indices{q_i,t_i});
end
end
end
% save integration masks
if (save_data)
fprintf('Saving center_xy, no_of_segments, integ_masks to %s\n',...
filename_integ_masks);
if angular_segments(end) == 0
angular_segments(end) = 360;
end
phi_det = (angular_segments(2:end) + angular_segments(1:end-1))/2; %% Center of the angular sector in degrees
save(filename_integ_masks,'center_xy','no_of_segments','integ_masks','angular_segments','phi_det');
end
% display some integration circles
if (~isempty(filename))
figure(fig_no+1);
hold off;
clf;
frame_plot = double(frame.data);
frame_plot( frame_plot < 1 ) = 1;
plot_step = round(length(integ_masks.indices)/50);
if (plot_step < 2)
plot_step = 2;
end
for (ind_r = 1:plot_step:size(integ_masks.indices,1))
for (ind_seg = 1:2:no_of_segments)
frame_plot(integ_masks.indices{ind_r,ind_seg}) = 10^(6*ind_seg/no_of_segments);
end
end
imagesc(log10(frame_plot));
axis xy;
axis equal;
axis tight;
colorbar;
title([ 'integration segment test plot for ' strrep(filename,'_','\_') ]);
set(gcf,'Name','integration masks');
end