% Call function without arguments for instructions on how to use it % Filename: $RCSfile: tune_valid_mask.m,v $ % % $Revision: 1.5 $ $Date: 2012/09/02 15:13:40 $ % $Author: bunk $ % $Tag: $ % % Description: % remove outlyers of intensity that deviates from the azimuthal integration % from the valid pixel mask % % Note: % Call without arguments for a brief help text. % % Dependencies: % - image_read % % history: % % May 21st 2010, Oliver Bunk: % 1st 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 [valid_mask] = tune_valid_mask(data_dir, varargin) import beamline.radial_integ import io.image_read import plotting.display_valid_mask import utils.find_files % set default values for the variable input arguments: % directory with the integrated data files indir_integ_data = '~/Data10/analysis/radial_integration/'; % filename of the integrated data, empty to determine it from the raw data % file name filename_integ_data = []; % use all cbf files filename_mask = '*.cbf'; % filename for loading and saving the valid pixel mask filename_valid_mask = '~/Data10/analysis/data/pilatus_valid_mask.mat'; % integration masks filename_integ_masks = '~/Data10/analysis/data/pilatus_integration_masks.mat'; % size of the median filter that is use to smooth the data for identifying % outlyers median_size = 11; % first pixel to start at radius_from = 20; % last pixel to check radius_to = 0; % only intensities above this threshold are considered for being hot threshold_hot = 5; % this value times the standard deviation of the intensity is used as hot pixel % threshold threshold_median = 3.0; % save the updated mask save_data = 0; % display result in this figure fig_no = 201; % matching files to use point_range = []; % check minimum number of input arguments if (nargin < 1) fprintf('\nUsage:\n'); fprintf('[valid_mask]=%s(data_dir [[,,]...]);\n',mfilename); fprintf('Remove outlyers from the valid pixel mask by comparing azimuthally integrated data\n'); fprintf('against the same data median filtered and rejecting pixels with a deviation\n'); fprintf('in intensity specified in multiples of the standard deviation.\n'); fprintf('\n'); fprintf('The optional , pairs are:\n'); fprintf('''FilenameMask'', specify the files to be used from the data directory, empty string for all, default is ''%s''\n',... filename_mask); fprintf('''PointRange'', matching files to use, default is [] for all files\n'); fprintf('''IndirIntegData'', directory with the azimuthally integrated data, default is %s\n',... indir_integ_data); fprintf('''FilenameIntegData'', filename for the azimuthally integrated data, empty to determine the name\n'); fprintf(' from the first raw data file name, default is ''%s''\n',... filename_integ_data); fprintf('''FilenameIntegMasks'', Matlab file containing the integration masks, default is ''%s''\n',filename_integ_masks); fprintf('''RadiusFrom'', no. of the pixel to start with, default is %.0f\n',radius_from); fprintf('''RadiusTo'', no. of the last pixel to check, default is %.0f\n',radius_to); fprintf('''MedianSize'', size of the median filter in pixels, default is %.0f\n',... median_size); fprintf('''ThresholdHot'', pixels above this value are considered for being hot, default is %d\n',... threshold_hot); fprintf('''ThresholdMedian'', pixels outside the range (I+/-threshold_median*sqrt(I))\n'); fprintf(' of the median filtered data are considered to be hot,\n'); fprintf(' default is %.1f\n',... threshold_median); fprintf('''SaveData'',<0-no,1-yes> save the valid pixel mask, default is %d\n',save_data); fprintf('''FilenameValidMask'', Matlab file with the valid pixel indices,\n'); fprintf(' default is %s\n',filename_valid_mask); fprintf('''FigNo'', number of the figure in which the result is displayed, default is %d\n',... fig_no); fprintf('\n'); fprintf('Examples:\n'); fprintf('[valid_mask]=%s(''~/Data10/pilatus/S05000-05999/S05715/e12612_1_05715_00000_00000.cbf'');\n',... mfilename); fprintf('[valid_mask]=%s(''~/Data10/pilatus/S05000-05999/S05715/*.cbf'');\n',... mfilename); error('At least the filename of the raw data has to be specified as input parameter.'); end % accept cell array with name/value pairs as well no_of_in_arg = nargin; if (nargin == 2) 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 = no_of_in_arg -1 + length(varargin); end end end % check number of input arguments if (rem(no_of_in_arg,2) ~= 1) error('The optional parameters have to be specified as ''name'',''value'' pairs'); end % parse the variable input arguments: % initialize the list of unhandled parameters vararg_remain = cell(0,0); for ind = 1:2:length(varargin) name = varargin{ind}; value = varargin{ind+1}; switch name case 'FilenameIntegMasks' filename_integ_masks = value; case 'FilenameMask' filename_mask = value; case 'PointRange' point_range = value; case 'IndirIntegData' indir_integ_data = value; case 'FilenameIntegData' filename_integ_data = value; case 'RadiusFrom', radius_from = round(value); case 'RadiusTo', radius_to = round(value); case 'MedianSize' median_size = round(value); case 'ThresholdMedian' threshold_median = value; case 'ThresholdHot' threshold_hot = value; case 'FilenameValidMask' filename_valid_mask = value; case 'SaveData' save_data = value; case 'FigNo' fig_no = value; otherwise vararg_remain{end+1} = name; %#ok vararg_remain{end+1} = value; %#ok end end vararg_remain{end+1} = 'UnhandledParError'; vararg_remain{end+1} = 0; vararg_remain{end+1} = 'DisplayFilename'; vararg_remain{end+1} = 0; % set some default values for the plot window set(0, 'DefaultAxesfontsize', 12); set(0, 'DefaultAxeslinewidth', 1, 'DefaultAxesfontsize', 12); set(0, 'DefaultLinelinewidth', 1); % get all matching filenames if (data_dir(end) ~= '/') data_dir(end+1) = '/'; end [data_dir,fnames,vararg_remain] = ... find_files( [ data_dir filename_mask ], vararg_remain ); if (length(fnames) < 1) error('No matching files found for %s%s.\n',data_dir,filename_mask); end % load the current valid pixel mask in variable valid_mask fprintf('loading the existing valid mask %s\n',filename_valid_mask); load(filename_valid_mask); framesize = valid_mask.framesize(1) * valid_mask.framesize(2); % load the integration masks in variable integ_masks fprintf('Loading the integration masks from %s\n',filename_integ_masks); load(filename_integ_masks); no_of_radii = length(integ_masks.radius); if ((radius_to < radius_from) || (radius_to > no_of_radii)) radius_to = no_of_radii; end % process the frames ind_hot = []; ind_dark = []; integ_data = []; fprintf('data directory is %s\n',data_dir); if (isempty(point_range)) point_range = 1:length(fnames); else ind = find(point_range <= length(fnames)); if (length(point_range) ~= length(ind)) fprintf('Warning, %d value(s) from the specified point range are out of the range [1,%.0f] and not used.\n',... length(point_range)-length(ind),length(fnames)); point_range = point_range(ind); end end for (point_ind=1:length(point_range)) f_ind = point_range(point_ind); % read the raw data fprintf('%3d/%3d: reading %s%s\n',f_ind,length(point_range),... data_dir,fnames(f_ind).name); filename_raw = [data_dir fnames(f_ind).name ]; [frame] = image_read(filename_raw,vararg_remain); % check that the files have identical dimensions if ((size(frame.data,1) ~= valid_mask.framesize(1)) || ... (size(frame.data,2) ~= valid_mask.framesize(2))) error('The valid pixel mask has %d x %d pixels, this frame has %d x %d pixels',... valid_mask.framesize(1),valid_mask.framesize(2),... size(frame.data,1),size(frame.data,2)); end % read the radially integrated data if (isempty(integ_data)) % determine filename for the integrated data from the first raw % data filename if (isempty(filename_integ_data)) [pathstr, filename_integ_data] = fileparts(fnames(f_ind).name); filename_integ_data = [ filename_integ_data '_integ.mat' ]; %#ok end filename_integ_data = fullfile(indir_integ_data,filename_integ_data); fprintf('Loading the integrated intensities from %s\n',... filename_integ_data); integ_data = load(filename_integ_data); % take the median of all segments with positive intensities, i.e., % skip negative intensities I_all_prev = integ_data.I_all; no_of_segments = size(I_all_prev,2); no_of_points = size(I_all_prev,3); I_all = zeros(no_of_radii,no_of_points); I_std = zeros(no_of_radii,no_of_points); if (no_of_segments > 1) fprintf('Using the median of %d segments.\n',no_of_segments); end for (ind1=1:no_of_radii) for (ind3=1:no_of_points) no_of_el = 0; I_use = zeros(1,no_of_segments); ind_I_use = zeros(1,no_of_segments); for (ind2=1:no_of_segments) if (I_all_prev(ind1,ind2,ind3) >= 0) no_of_el = no_of_el +1; I_use(no_of_el) = I_all_prev(ind1,ind2,ind3); ind_I_use(no_of_el) = ind2; end end if (no_of_el > 1) [I_sorted,ind_sorted] = sort(I_use(1:no_of_el)); ind_median = round(0.5*no_of_el); I_all(ind1,ind3) = I_sorted(ind_median); % get the standard deviation of this intensity I_std(ind1,ind3) = integ_data.I_std(ind1,ind_I_use(ind_sorted(ind_median)),ind3); end end end % print this information once rather than for each file fprintf('Checking radii from %d to %d.\n',radius_from,radius_to); end % get the index to the integrated data ind = 1; ind_max = length(integ_data.filenames_all); while ((ind <= ind_max) && ... (isempty(strfind(integ_data.filenames_all{ind},fnames(f_ind).name)))) ind = ind +1; end if (ind > ind_max) error('Could not find integrated data for raw data file %s in %s.',... filename_raw,filename_integ_data); end data_integ = squeeze(I_all(:,ind)); data_integ_std = squeeze(I_std(:,ind)); % median filtered data for comparison data_integ_med = medfilt1(data_integ,median_size,size(data_integ,1),1); % figure(fig_no+2); % hold off; % clf; % semilogy(data_integ); % hold all; % semilogy(data_integ_med); % semilogy(data_integ_med+data_integ_std*threshold_median); % semilogy(data_integ_med-data_integ_std*threshold_median); frame_cmp = ones(valid_mask.framesize) -2; frame_cmp_std = zeros(valid_mask.framesize); for (ind_r = radius_from:radius_to) for (ind_seg = 1:no_of_segments) if (integ_masks.norm_sum(ind_r,ind_seg) > 0) frame_cmp(integ_masks.indices{ind_r,ind_seg}) = ... data_integ_med(ind_r); frame_cmp_std(integ_masks.indices{ind_r,ind_seg}) = ... data_integ_std(ind_r); end end end % ind_dark = union(ind_dark, ... find((frame.data >= 0) & ... (frame_cmp >= 0) & ... (frame.data < frame_cmp - threshold_median*frame_cmp_std))); % only consider pixels of sufficient intensity for being hot ind_hot = union(ind_hot, ... find((frame.data > threshold_hot) & ... (frame_cmp >= 0) & ... (frame.data > frame_cmp + threshold_median*frame_cmp_std))); end % calculate the complementary masks of the valid pixels valid_mask.indices = intersect(valid_mask.indices,... setdiff(1:framesize,union(ind_dark,ind_hot))); fprintf('In total %d dark and %d hot pixels found.\n',... length(ind_dark),length(ind_hot)); fprintf('%d valid pixels remain.\n',length(valid_mask.indices)); if (save_data) % create a backup of the mask if (exist(filename_valid_mask,'file')) filename_mask_backup = [ filename_valid_mask '.bak' ]; fprintf('Copying the current mask %s to %s\n',filename_valid_mask,... filename_mask_backup); copyfile(filename_valid_mask,filename_mask_backup); end % save the masks fprintf('Saving valid_mask to %s\n',filename_valid_mask); save(filename_valid_mask,'valid_mask'); % plot new valid pixel mask display_valid_mask('FilenameValidMask',filename_valid_mask,... 'NoHelp',1,'FigNo',fig_no); else fprintf('The updated valid pixel mask is NOT saved.\n'); end % plot the additional invalid pixels figure(fig_no+1); % mark the valid pixels as 1, leave the invalid at 0 frame = zeros(valid_mask.framesize); frame(valid_mask.indices) = 1; frame(ind_dark) = -10; frame(ind_hot) = 10; imagesc(frame); caxis([-10 10]); axis xy; axis equal; axis tight; colorbar; title_str = ['valid pixels, ' ... num2str(length(ind_dark)+length(ind_hot),'%d') ... ' update(s) marked with intensity -10/10']; title(title_str); set(gcf,'Name','valid pixels, updates marked');