272 lines
12 KiB
Matlab
272 lines
12 KiB
Matlab
function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
|
%% Applies cropping, background subtraction, and optional fringe removal, optional unshuffling on OD image dataset
|
|
% Automatically reuses in-memory full dataset if available;
|
|
% otherwise, reads and processes raw HDF5 data.
|
|
%
|
|
% Inputs:
|
|
% options - structure containing processing options:
|
|
% .folderPath : path to raw HDF5 files
|
|
% .saveDirectory : path to save cache (if needed)
|
|
% .cam, .angle : camera selection and rotation angle
|
|
% .ImagingMode, .PulseDuration : imaging parameters
|
|
% .scan_parameter : name of scan parameter
|
|
% .center, .span : cropping settings
|
|
% .fraction : background subtraction fraction
|
|
% .removeFringes : logical flag for fringe removal
|
|
% .skipUnshuffling : logical flag to skip unshuffling
|
|
% .scan_reference_values: reference values for unshuffling
|
|
%
|
|
% Outputs:
|
|
% od_imgs : cell array of processed OD images
|
|
% scan_parameter_values: vector of scan parameter values
|
|
% file_list : cell array of file names
|
|
|
|
% --- Early exit if processed data already exist AND options match ---
|
|
reuseVarsExist = evalin('base', ...
|
|
'exist(''od_imgs'',''var'') && exist(''scan_parameter_values'',''var'') && exist(''file_list'',''var'') && exist(''prior_options'',''var'')');
|
|
|
|
% --- Respect SAVE_TO_WORKSPACE flag from batchAnalyze ---
|
|
if isfield(options, 'SAVE_TO_WORKSPACE') && ~options.SAVE_TO_WORKSPACE
|
|
% Force reprocessing: skip all workspace reuse
|
|
reuseVarsExist = false;
|
|
end
|
|
|
|
if reuseVarsExist
|
|
prior_options = evalin('base','prior_options');
|
|
|
|
% Define which fields are critical for reuse
|
|
critical_fields = {'folderPath','cam','angle','ImagingMode','PulseDuration','center','span','fraction','removeFringes','skipUnshuffling','scan_reference_values'};
|
|
|
|
if ~haveOptionsChanged(options, prior_options, critical_fields)
|
|
fprintf('\n[INFO] Reusing processed OD images, scan parameters, and file list from memory.\n');
|
|
od_imgs = evalin('base','od_imgs');
|
|
scan_parameter_values = evalin('base','scan_parameter_values');
|
|
file_list = evalin('base','file_list');
|
|
|
|
% --- Ensure figures exist if requested now ---
|
|
if ~options.skipSaveOD
|
|
saveODFigures(od_imgs, options.saveDirectory);
|
|
end
|
|
|
|
return; % ✅ safe to exit now
|
|
else
|
|
fprintf('\n[INFO] Processed-data-related options changed. Reprocessing full OD image dataset...\n');
|
|
end
|
|
end
|
|
|
|
% --- Check if the full OD dataset and scan parameters exist in workspace ---
|
|
fullDataExists = evalin('base', 'exist(''full_od_imgs'', ''var'')') && ...
|
|
evalin('base', 'exist(''full_bkg_imgs'', ''var'')') && ...
|
|
evalin('base', 'exist(''raw_scan_parameter_values'', ''var'')') && ...
|
|
evalin('base', 'exist(''raw_file_list'', ''var'')') && ...
|
|
evalin('base', 'exist(''prior_options'',''var'')');
|
|
|
|
% --- Respect SAVE_TO_WORKSPACE flag ---
|
|
if isfield(options, 'SAVE_TO_WORKSPACE') && ~options.SAVE_TO_WORKSPACE
|
|
fullDataExists = false; % force recompute even if workspace vars exist
|
|
end
|
|
|
|
if fullDataExists
|
|
% Both required datasets exist, check if raw-data options changed
|
|
prior_options = evalin('base','prior_options');
|
|
|
|
% Define critical fields that affect raw-data computation
|
|
critical_raw_fields = {'folderPath','cam','angle','ImagingMode','PulseDuration'};
|
|
|
|
if ~haveOptionsChanged(options, prior_options, critical_raw_fields)
|
|
fprintf('\n[INFO] Reusing full OD image dataset and scan parameters from memory.\n');
|
|
full_od_imgs = evalin('base', 'full_od_imgs');
|
|
full_bkg_imgs = evalin('base', 'full_bkg_imgs');
|
|
raw_scan_parameter_values = evalin('base', 'raw_scan_parameter_values');
|
|
raw_file_list = evalin('base', 'raw_file_list');
|
|
else
|
|
fprintf('\n[INFO] Raw-data-related options changed. Recomputing full OD image dataset...\n');
|
|
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = Helper.processRawData(options);
|
|
|
|
% Save raw full dataset for reuse
|
|
if options.SAVE_TO_WORKSPACE
|
|
assignin('base', 'full_od_imgs', full_od_imgs);
|
|
assignin('base', 'full_bkg_imgs', full_bkg_imgs);
|
|
assignin('base', 'raw_scan_parameter_values', raw_scan_parameter_values);
|
|
assignin('base', 'raw_file_list', raw_file_list);
|
|
end
|
|
fprintf('\n[INFO] Completed recomputing OD images. Stored in workspace for reuse.\n');
|
|
end
|
|
else
|
|
% Either dataset is missing, process raw HDF5 files completely
|
|
fprintf('\n[INFO] Full OD image dataset or scan parameters not found in memory.\n');
|
|
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = Helper.processRawData(options);
|
|
|
|
% Save raw full dataset for reuse
|
|
if options.SAVE_TO_WORKSPACE
|
|
assignin('base', 'full_od_imgs', full_od_imgs);
|
|
assignin('base', 'full_bkg_imgs', full_bkg_imgs);
|
|
assignin('base', 'raw_scan_parameter_values', raw_scan_parameter_values);
|
|
assignin('base', 'raw_file_list', raw_file_list);
|
|
end
|
|
fprintf('\n[INFO] Completed computing OD images. Images will be stored in workspace for reuse.\n');
|
|
end
|
|
|
|
fprintf('\n[INFO] Cropping and subtracting background from images...\n');
|
|
nFiles = size(full_od_imgs, 3);
|
|
|
|
% --- Preallocate arrays for processed images ---
|
|
absimages = zeros(options.span(1)+1, options.span(2)+1, nFiles, 'single');
|
|
refimages = zeros(options.span(1)+1, options.span(2)+1, nFiles, 'single');
|
|
|
|
% --- Progress bar ---
|
|
if isfield(options, 'showProgressBar') && options.showProgressBar
|
|
pb = Helper.ProgressBar();
|
|
pb.run('Progress: ');
|
|
end
|
|
|
|
% --- Process each image: crop and subtract background ---
|
|
for k = 1:nFiles
|
|
full_od_img = full_od_imgs(:,:,k); % original full OD image, never modified
|
|
full_bkg_img = full_bkg_imgs(:,:,k); % original full background image, never modified
|
|
if any(isnan(full_od_img(:)))
|
|
absimages(:,:,k) = nan(options.span(1)+1, options.span(2)+1, 'single');
|
|
continue
|
|
end
|
|
if any(isnan(full_bkg_img(:)))
|
|
refimages(:,:,k) = nan(options.span(1)+1, options.span(2)+1, 'single');
|
|
continue
|
|
end
|
|
|
|
% Crop image around the region of interest
|
|
cropped_absimage = Helper.cropODImage(full_od_img, options.center, options.span);
|
|
cropped_refimage = Helper.cropODImage(full_bkg_img, options.center, options.span);
|
|
|
|
% Subtract background offset based on fraction
|
|
processed_absimage = Helper.subtractBackgroundOffset(cropped_absimage, options.fraction);
|
|
processed_refimage = Helper.subtractBackgroundOffset(cropped_refimage, options.fraction);
|
|
|
|
% Store processed image (transpose to match orientation)
|
|
absimages(:,:,k) = processed_absimage';
|
|
refimages(:,:,k) = processed_refimage';
|
|
|
|
% Update progress bar
|
|
if isfield(options, 'showProgressBar') && options.showProgressBar
|
|
progressPercent = round(k / nFiles * 100);
|
|
pb.run(progressPercent);
|
|
end
|
|
end
|
|
|
|
% Finish progress bar
|
|
if isfield(options, 'showProgressBar') && options.showProgressBar
|
|
pb.run(' Done!');
|
|
end
|
|
|
|
% --- Optional fringe removal ---
|
|
if isfield(options, 'removeFringes') && options.removeFringes
|
|
fprintf('\n[INFO] Applying fringe removal to processed images...\n');
|
|
optrefimages = Helper.removeFringesInImage(absimages, refimages);
|
|
absimages_fringe_removed = absimages - optrefimages;
|
|
od_imgs = arrayfun(@(i) absimages_fringe_removed(:,:,i), 1:nFiles, 'UniformOutput', false);
|
|
fprintf('\n[INFO] Fringe removal completed.\n');
|
|
else
|
|
od_imgs = arrayfun(@(i) absimages(:,:,i), 1:nFiles, 'UniformOutput', false);
|
|
end
|
|
|
|
% --- Optional unshuffling based on scan reference values ---
|
|
if isfield(options, 'skipUnshuffling') && ~options.skipUnshuffling
|
|
fprintf('\n[INFO] Reordering images according to scan parameter reference values...\n');
|
|
|
|
n_values = length(options.scan_reference_values);
|
|
n_total = length(raw_scan_parameter_values);
|
|
n_reps = n_total / n_values;
|
|
|
|
ordered_scan_parameter_values = zeros(1, n_total);
|
|
ordered_od_imgs = cell(1, n_total);
|
|
ordered_file_list = cell(1, n_total);
|
|
counter = 1;
|
|
|
|
temp_scan_values = raw_scan_parameter_values;
|
|
temp_od_imgs = od_imgs;
|
|
temp_file_list = raw_file_list;
|
|
|
|
for rep = 1:n_reps
|
|
for val = options.scan_reference_values
|
|
idx = find(temp_scan_values == val, 1, 'first');
|
|
if isempty(idx), continue; end
|
|
ordered_scan_parameter_values(counter) = temp_scan_values(idx);
|
|
ordered_od_imgs{counter} = temp_od_imgs{idx};
|
|
ordered_file_list{counter} = temp_file_list{idx};
|
|
temp_scan_values(idx) = NaN;
|
|
temp_od_imgs{idx} = [];
|
|
temp_file_list{idx} = [];
|
|
counter = counter + 1;
|
|
end
|
|
end
|
|
od_imgs = ordered_od_imgs;
|
|
scan_parameter_values = ordered_scan_parameter_values;
|
|
file_list = ordered_file_list;
|
|
fprintf('\n[INFO] Image reordering completed.\n');
|
|
else
|
|
% No unshuffling: keep original order
|
|
scan_parameter_values = raw_scan_parameter_values;
|
|
file_list = raw_file_list;
|
|
end
|
|
|
|
% --- Save processed dataset and options for reuse ---
|
|
assignin('base', 'od_imgs', od_imgs);
|
|
assignin('base', 'scan_parameter_values', scan_parameter_values);
|
|
assignin('base', 'file_list', file_list);
|
|
assignin('base', 'prior_options', options);
|
|
|
|
% --- Save OD images as figures if requested ---
|
|
if ~options.skipSaveOD
|
|
saveODFigures(od_imgs, options.saveDirectory);
|
|
end
|
|
|
|
fprintf('\n[INFO] OD image dataset ready for further analysis.\n');
|
|
|
|
end
|
|
|
|
%% --- Local helper functions ---
|
|
|
|
function changed = haveOptionsChanged(options, prior_options, critical_fields)
|
|
changed = false;
|
|
for f = critical_fields
|
|
fname = f{1};
|
|
if isfield(options, fname) && isfield(prior_options, fname)
|
|
if ~isequal(options.(fname), prior_options.(fname))
|
|
changed = true; return
|
|
end
|
|
elseif xor(isfield(options, fname), isfield(prior_options, fname))
|
|
changed = true; return
|
|
end
|
|
end
|
|
end
|
|
|
|
function saveODFigures(od_imgs, saveDirectory)
|
|
odFolder = fullfile(saveDirectory, "Results", "ODImages");
|
|
if ~exist(odFolder, 'dir')
|
|
mkdir(odFolder);
|
|
end
|
|
|
|
nImgs = length(od_imgs);
|
|
filesExist = all(arrayfun(@(k) isfile(fullfile(odFolder, sprintf('OD_img_%03d.fig', k))), 1:nImgs));
|
|
|
|
if filesExist
|
|
fprintf('\n[INFO] OD figures already exist in %s. Skipping save.\n', odFolder);
|
|
return;
|
|
end
|
|
|
|
fprintf('\n[INFO] Saving OD figures to %s ...\n', odFolder);
|
|
for k = 1:nImgs
|
|
img = od_imgs{k};
|
|
|
|
% Create invisible figure for saving
|
|
hFig = figure('Visible','off');
|
|
imagesc(img);
|
|
axis image off; colormap gray;
|
|
fileName = fullfile(odFolder, sprintf('OD_img_%03d.fig', k));
|
|
|
|
% Save as .fig
|
|
savefig(hFig, fileName);
|
|
close(hFig);
|
|
end
|
|
fprintf('[INFO] OD figures saved successfully.\n');
|
|
end
|