Major update: Added functionality to auto-detect scan parameter, generalized to any parameter, single or multiple and other bugfixes.
This commit is contained in:
parent
2091ad85f0
commit
652a38ab9f
@ -25,6 +25,7 @@ function [results, scan_parameter_values] = performAnalysis(options)
|
||||
options.k_max (1,1) double
|
||||
options.skipFringeRemoval (1,1) logical
|
||||
options.skipUnshuffling (1,1) logical
|
||||
options.flipSortOrder (1,1) logical
|
||||
options.skipPreprocessing (1,1) logical
|
||||
options.skipMasking (1,1) logical
|
||||
options.skipIntensityThresholding (1,1) logical
|
||||
|
||||
@ -68,13 +68,8 @@ function runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list,
|
||||
shortName = [fname, ext];
|
||||
|
||||
% Update figure title with shot + filename
|
||||
if strcmp(options.scan_parameter, 'ps_rot_mag_fin_pol_angle')
|
||||
hFig.Name = sprintf('Shot %d | %s', idx, shortName);
|
||||
txtHandle.String = sprintf('%.1f^\\circ', scan_parameter_values(idx));
|
||||
else
|
||||
hFig.Name = sprintf('Shot %d | %s', idx, shortName);
|
||||
txtHandle.String = sprintf('%.2f G', scan_parameter_values(idx));
|
||||
end
|
||||
hFig.Name = sprintf('Shot %d | %s', idx, shortName);
|
||||
txtHandle.String = sprintf('%.2f', scan_parameter_values(idx));
|
||||
|
||||
sliderHandle.Value = idx;
|
||||
drawnow;
|
||||
@ -92,5 +87,4 @@ function runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list,
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@ -85,10 +85,9 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
||||
|
||||
else
|
||||
matched = false;
|
||||
full_od_image_subfolder = [];
|
||||
useFullODFolders = ~isfield(options,'skipFullODImagesFolderUse') || ~options.skipFullODImagesFolderUse;
|
||||
|
||||
% --- If user provided selectedPath and it is a folder, disambiguate its meaning ---
|
||||
% --- If user provided a selected path and it is a folder, disambiguate its meaning ---
|
||||
if isfield(options,'selectedPath') && isfolder(options.selectedPath)
|
||||
selPath = options.selectedPath;
|
||||
|
||||
@ -100,8 +99,7 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
||||
% exact match to any discovered fullodimage_folders entry
|
||||
if isfolder(full_od_image_parent_folder) && ~isempty(full_od_image_parent_folder)
|
||||
for r = 1:numel(full_od_image_parent_folder)
|
||||
cand = fullfile(full_od_image_parent_folder(r).folder, full_od_image_parent_folder(r).name);
|
||||
if strcmp(cand, selPath)
|
||||
if strcmp(full_od_image_parent_folder, selPath)
|
||||
selIsFullOD = true;
|
||||
break;
|
||||
end
|
||||
@ -121,19 +119,19 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
||||
|
||||
% If user forces recompute -> recompute from selected raw path
|
||||
if isfield(options,'skipFullODImagesFolderUse') && options.skipFullODImagesFolderUse
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = recomputeODImages(options, selPath);
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list, options.scan_parameter_names, options.scan_reference_values] = recomputeODImages(options, selPath);
|
||||
if ~options.SAVE_TO_WORKSPACE
|
||||
% --- Determine parent folder for FullODImages ---
|
||||
if isfield(options, 'FullODImagesFolder') && ...
|
||||
isfolder(options.FullODImagesFolder) && ...
|
||||
~isempty(options.FullODImagesFolder)
|
||||
parentFolder = dir(fullfile(options.FullODImagesFolder, 'FullODImages_*'));
|
||||
elseif isfield(options, 'saveDirectory') && isfolder(options.saveDirectory)
|
||||
parentFolder = dir(fullfile(options.saveDirectory, 'FullODImages_*'));
|
||||
parentFolder = options.FullODImagesFolder;
|
||||
else
|
||||
parentFolder = options.saveDirectory;
|
||||
end
|
||||
dataSource = makeDataSourceStruct(options.folderPath);
|
||||
fullodimagesFolder = createFullODImagesFolderPath(parentFolder, dataSource);
|
||||
[mat_files, ~, ~, nFiles] = prepareFromOnDiskData(fullodimagesFolder);
|
||||
full_od_image_subfolder = createFullODImagesFolderPath(parentFolder, dataSource);
|
||||
useFullODFolders = true;
|
||||
else
|
||||
nFiles = numel(raw_file_list);
|
||||
end
|
||||
@ -163,20 +161,20 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
||||
|
||||
% If no matching full-OD folder found, recompute from the selected raw path
|
||||
if ~found
|
||||
fprintf('\n[INFO] Forcing recompute from raw data as no matching full OD images subfolder found for selected raw path: %s\n', full_od_image_parent_folder(r).name);
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = recomputeODImages(options, selPath);
|
||||
fprintf('\n[INFO] Forcing recompute from raw data as no matching full OD images subfolder found (Use skipFullODImagesFolderUse=true to skip directly to computing from raw data).\n');
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list, options.scan_parameter_names, options.scan_reference_values] = recomputeODImages(options, selPath);
|
||||
if ~options.SAVE_TO_WORKSPACE
|
||||
% --- Determine parent folder for FullODImages ---
|
||||
if isfield(options, 'FullODImagesFolder') && ...
|
||||
isfolder(options.FullODImagesFolder) && ...
|
||||
~isempty(options.FullODImagesFolder)
|
||||
parentFolder = dir(fullfile(options.FullODImagesFolder, 'FullODImages_*'));
|
||||
parentFolder = options.FullODImagesFolder;
|
||||
elseif isfield(options, 'saveDirectory') && isfolder(options.saveDirectory)
|
||||
parentFolder = dir(fullfile(options.saveDirectory, 'FullODImages_*'));
|
||||
parentFolder = options.saveDirectory;
|
||||
end
|
||||
dataSource = makeDataSourceStruct(options.folderPath);
|
||||
fullodimagesFolder = createFullODImagesFolderPath(parentFolder, dataSource);
|
||||
[mat_files, ~, ~, nFiles] = prepareFromOnDiskData(fullodimagesFolder);
|
||||
full_od_image_subfolder = createFullODImagesFolderPath(parentFolder, dataSource);
|
||||
useFullODFolders = true;
|
||||
else
|
||||
nFiles = numel(raw_file_list);
|
||||
end
|
||||
@ -188,19 +186,19 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
||||
else
|
||||
% --- No selected path: either force recompute or search among fullodimage_folders ---
|
||||
if isfield(options,'skipFullODImagesFolderUse') && options.skipFullODImagesFolderUse
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = recomputeODImages(options, options.baseDataFolder);
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list, options.scan_parameter_names, options.scan_reference_values] = recomputeODImages(options, options.baseDataFolder);
|
||||
if ~options.SAVE_TO_WORKSPACE
|
||||
% --- Determine parent folder for FullODImages ---
|
||||
if isfield(options, 'FullODImagesFolder') && ...
|
||||
isfolder(options.FullODImagesFolder) && ...
|
||||
~isempty(options.FullODImagesFolder)
|
||||
parentFolder = dir(fullfile(options.FullODImagesFolder, 'FullODImages_*'));
|
||||
parentFolder = options.FullODImagesFolder;
|
||||
elseif isfield(options, 'saveDirectory') && isfolder(options.saveDirectory)
|
||||
parentFolder = dir(fullfile(options.saveDirectory, 'FullODImages_*'));
|
||||
parentFolder = options.saveDirectory;
|
||||
end
|
||||
dataSource = makeDataSourceStruct(options.folderPath);
|
||||
fullodimagesFolder = createFullODImagesFolderPath(parentFolder, dataSource);
|
||||
[mat_files, ~, ~, nFiles] = prepareFromOnDiskData(fullodimagesFolder);
|
||||
full_od_image_subfolder = createFullODImagesFolderPath(parentFolder, dataSource);
|
||||
useFullODFolders = true;
|
||||
else
|
||||
nFiles = numel(raw_file_list);
|
||||
end
|
||||
@ -236,19 +234,19 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
||||
elseif isfolder(full_od_image_parent_folder) && useFullODFolders
|
||||
fprintf('\n[INFO] No matching full OD images subfolder found. Will recompute from raw data.\n');
|
||||
end
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = recomputeODImages(options, options.baseDataFolder);
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list, options.scan_parameter_names, options.scan_reference_values] = recomputeODImages(options, options.baseDataFolder);
|
||||
if ~options.SAVE_TO_WORKSPACE
|
||||
% --- Determine parent folder for FullODImages ---
|
||||
if isfield(options, 'FullODImagesFolder') && ...
|
||||
isfolder(options.FullODImagesFolder) && ...
|
||||
~isempty(options.FullODImagesFolder)
|
||||
parentFolder = dir(fullfile(options.FullODImagesFolder, 'FullODImages_*'));
|
||||
parentFolder = options.FullODImagesFolder;
|
||||
elseif isfield(options, 'saveDirectory') && isfolder(options.saveDirectory)
|
||||
parentFolder = dir(fullfile(options.saveDirectory, 'FullODImages_*'));
|
||||
parentFolder = options.saveDirectory;
|
||||
end
|
||||
dataSource = makeDataSourceStruct(options.folderPath);
|
||||
fullodimagesFolder = createFullODImagesFolderPath(parentFolder, dataSource);
|
||||
[mat_files, ~, ~, nFiles] = prepareFromOnDiskData(fullodimagesFolder);
|
||||
full_od_image_subfolder = createFullODImagesFolderPath(parentFolder, dataSource);
|
||||
useFullODFolders = true;
|
||||
else
|
||||
nFiles = numel(raw_file_list);
|
||||
end
|
||||
@ -328,38 +326,87 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
||||
if isfield(options, 'skipUnshuffling') && ~options.skipUnshuffling
|
||||
fprintf('\n[INFO] Reordering images...\n');
|
||||
|
||||
n_total = numel(raw_scan_parameter_values);
|
||||
n_values = numel(options.scan_reference_values);
|
||||
n_reps = n_total / n_values;
|
||||
tol = 1e-6; % tolerance for floating-point comparisons
|
||||
|
||||
idx_mat = nan(n_reps, n_values);
|
||||
|
||||
for j = 1:n_values
|
||||
% find all matches within tolerance
|
||||
idx_all = find(abs(raw_scan_parameter_values - options.scan_reference_values(j)) < tol);
|
||||
if numel(idx_all) ~= n_reps
|
||||
error('Reference value %.6g occurs %d times; expected %d', ...
|
||||
options.scan_reference_values(j), numel(idx_all), n_reps);
|
||||
% --- Determine scan reference values ---
|
||||
if (~isfield(options,'scan_reference_values') || isempty(options.scan_reference_values))
|
||||
if isvector(raw_scan_parameter_values)
|
||||
% Single parameter case
|
||||
scan_reference_values = unique(raw_scan_parameter_values(:), 'stable');
|
||||
scan_reference_values = scan_reference_values(:).'; % row vector
|
||||
n_total = numel(raw_scan_parameter_values);
|
||||
n_values = numel(scan_reference_values);
|
||||
else
|
||||
% Multi-parameter case: unique rows
|
||||
scan_reference_values = unique(raw_scan_parameter_values, 'rows', 'stable');
|
||||
n_total = size(raw_scan_parameter_values, 1);
|
||||
n_values = size(scan_reference_values, 1);
|
||||
end
|
||||
else
|
||||
scan_reference_values = options.scan_reference_values;
|
||||
if isvector(raw_scan_parameter_values)
|
||||
n_total = numel(raw_scan_parameter_values);
|
||||
n_values = numel(scan_reference_values);
|
||||
else
|
||||
n_total = size(raw_scan_parameter_values, 1);
|
||||
n_values = size(scan_reference_values, 1);
|
||||
end
|
||||
idx_mat(:, j) = idx_all(:); % column = all occurrences of this reference value
|
||||
end
|
||||
|
||||
% Reordered indices
|
||||
% --- Determine sort order based on options.flipSortOrder ---
|
||||
if isfield(options, 'flipSortOrder') && options.flipSortOrder
|
||||
sort_order = 'descend';
|
||||
else
|
||||
sort_order = 'ascend'; % default
|
||||
end
|
||||
|
||||
if isvector(scan_reference_values)
|
||||
[scan_reference_values, ~] = sort(scan_reference_values, sort_order);
|
||||
else
|
||||
scan_reference_values = sortrows(scan_reference_values, sort_order);
|
||||
end
|
||||
|
||||
% --- Reorder images according to scan reference values ---
|
||||
n_reps = n_total / n_values;
|
||||
tol = 1e-6; % tolerance for floating-point comparisons
|
||||
idx_mat = nan(n_reps, n_values);
|
||||
|
||||
for j = 1:n_values
|
||||
if isvector(raw_scan_parameter_values)
|
||||
ref_val = scan_reference_values(j); % scalar for single parameter
|
||||
idx_all = find(abs(raw_scan_parameter_values - ref_val) < tol);
|
||||
else
|
||||
ref_val = scan_reference_values(j,:); % row vector
|
||||
diffs = abs(raw_scan_parameter_values - ref_val);
|
||||
idx_all = find(all(diffs < tol, 2));
|
||||
end
|
||||
|
||||
if numel(idx_all) ~= n_reps
|
||||
error('Reference value(s) %s occurs %d times; expected %d', ...
|
||||
mat2str(ref_val,6), numel(idx_all), n_reps);
|
||||
end
|
||||
|
||||
idx_mat(:, j) = idx_all(:);
|
||||
end
|
||||
|
||||
% --- Reordered indices ---
|
||||
ordered_idx = reshape(idx_mat.', 1, []);
|
||||
|
||||
% Apply reorder
|
||||
od_imgs = od_imgs(ordered_idx);
|
||||
scan_parameter_values = raw_scan_parameter_values(ordered_idx);
|
||||
file_list = raw_file_list(ordered_idx);
|
||||
% --- Apply reorder ---
|
||||
od_imgs = od_imgs(ordered_idx);
|
||||
if isvector(raw_scan_parameter_values)
|
||||
scan_parameter_values = raw_scan_parameter_values(ordered_idx).';
|
||||
else
|
||||
scan_parameter_values = raw_scan_parameter_values(ordered_idx,:);
|
||||
end
|
||||
file_list = raw_file_list(ordered_idx);
|
||||
|
||||
fprintf('\n[INFO] Image reordering completed.\n');
|
||||
|
||||
else
|
||||
scan_parameter_values = raw_scan_parameter_values;
|
||||
file_list = raw_file_list;
|
||||
end
|
||||
|
||||
% --- Save processed dataset and options to workspace for reuse ---
|
||||
% --- Save processed dataset and options to workspace ---
|
||||
assignin('base', 'od_imgs', od_imgs);
|
||||
assignin('base', 'scan_parameter_values', scan_parameter_values);
|
||||
assignin('base', 'file_list', file_list);
|
||||
@ -369,7 +416,7 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
|
||||
if ~options.skipSaveProcessedOD
|
||||
saveProcessedOD(od_imgs, options);
|
||||
end
|
||||
|
||||
|
||||
fprintf('\n[INFO] OD image dataset ready for further analysis.\n');
|
||||
end
|
||||
|
||||
@ -493,7 +540,7 @@ function dataSource = makeDataSourceStruct(folderPath)
|
||||
};
|
||||
end
|
||||
|
||||
function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = recomputeODImages(options, selectedPath)
|
||||
function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list, scan_parameter_names, scan_reference_values] = recomputeODImages(options, selectedPath)
|
||||
% recomputeODImages: central recompute routine
|
||||
% optional argument selectedPath (if provided) will be used as options.folderPath for processing
|
||||
|
||||
@ -508,14 +555,13 @@ function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list]
|
||||
opts.folderPath = selectedPath;
|
||||
end
|
||||
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = Helper.processRawData(opts);
|
||||
[full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list, scan_parameter_names, scan_reference_values] = Helper.processRawData(opts);
|
||||
|
||||
if opts.SAVE_TO_WORKSPACE
|
||||
fprintf('\n[INFO] Completed computing OD images. Stored in workspace for reuse.\n');
|
||||
else
|
||||
fprintf('\n[INFO] Completed computing OD images. Stored on disk for reuse.\n');
|
||||
end
|
||||
fprintf('\n[INFO] Cropping and subtracting background from images...\n');
|
||||
end
|
||||
|
||||
function fullodimagesFolder = createFullODImagesFolderPath(parentFolder, dataSourcesStruct)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = processRawData(options)
|
||||
function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list, scan_parameter_names, scan_reference_values] = processRawData(options)
|
||||
%% Reads HDF5 files, computes OD images, supports disk-backed storage in blocks
|
||||
|
||||
fprintf('\n[INFO] Processing raw data files at %s ...\n', options.folderPath);
|
||||
@ -21,14 +21,27 @@ function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list]
|
||||
|
||||
% Determine image size
|
||||
testFile = fullfile(files(1).folder, files(1).name);
|
||||
atm_test = double(imrotate(h5read(testFile, append(groupList(options.cam), "/atoms")), options.angle, 'bilinear', 'crop'));
|
||||
[ny, nx] = size(atm_test);
|
||||
cameraGroup = groupList(options.cam);
|
||||
try
|
||||
info = h5info(testFile, cameraGroup); % Check if group exists
|
||||
catch
|
||||
error('Group "%s" not found in file "%s". Aborting.', cameraGroup, testFile);
|
||||
end
|
||||
|
||||
datasetNames = {info.Datasets.Name};
|
||||
if ~ismember('atoms', datasetNames)
|
||||
error('Dataset "%s/atoms" not found in file "%s". Aborting.', cameraGroup, testFile);
|
||||
end
|
||||
% If we reach here, the dataset exists
|
||||
atm_test = double(imrotate(h5read(testFile, append(cameraGroup, "/atoms")), ...
|
||||
options.angle, 'bilinear', 'crop'));
|
||||
[ny, nx] = size(atm_test);
|
||||
|
||||
full_od_imgs = [];
|
||||
full_bkg_imgs = [];
|
||||
raw_scan_parameter_values = zeros(1, nFiles);
|
||||
raw_scan_parameter_values = [];
|
||||
raw_file_list = string(zeros(1,nFiles)); % always string array
|
||||
|
||||
|
||||
if options.SAVE_TO_WORKSPACE
|
||||
fprintf('\n[INFO] Creating in-memory arrays of raw data...\n');
|
||||
full_od_imgs = nan(ny, nx, nFiles, 'single');
|
||||
@ -40,25 +53,23 @@ function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list]
|
||||
dataSource{1}.sequence, ...
|
||||
strrep(dataSource{1}.date,'/','-'), ...
|
||||
dataSource{1}.runs);
|
||||
|
||||
% --- Determine parent folder for FullODImages ---
|
||||
|
||||
if isfield(options, 'FullODImagesFolder') && ...
|
||||
~isempty(options.FullODImagesFolder) && ...
|
||||
isfolder(options.FullODImagesFolder)
|
||||
isfolder(options.FullODImagesFolder) && ...
|
||||
~isempty(options.FullODImagesFolder)
|
||||
parentFolder = options.FullODImagesFolder;
|
||||
else
|
||||
parentFolder = options.saveDirectory;
|
||||
end
|
||||
|
||||
% --- Create uniquely identified full OD image folder ---
|
||||
|
||||
fullODImageFolder = fullfile(parentFolder, ['FullODImages_' runID]);
|
||||
if ~exist(fullODImageFolder,'dir'), mkdir(fullODImageFolder); end
|
||||
fprintf('\n[INFO] Creating folder of full OD images on disk: %s\n', fullODImageFolder);
|
||||
|
||||
% --- Save metadata for this run ---
|
||||
metadata.options = options;
|
||||
metadata.timestamp = datetime; % still record analysis time
|
||||
metadata.runID = runID; % traceable to experiment run
|
||||
metadata.timestamp = datetime;
|
||||
metadata.runID = runID;
|
||||
metadata.imageSize = [ny, nx];
|
||||
metadata.fileList = string(arrayfun(@(f) fullfile(f.folder, f.name), files, 'UniformOutput', false));
|
||||
save(fullfile(fullODImageFolder,'metadata.mat'),'metadata','-v7.3');
|
||||
@ -67,29 +78,48 @@ function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list]
|
||||
% --- Prepare file names ---
|
||||
fullFileNames = string(arrayfun(@(f) fullfile(f.folder, f.name), files, 'UniformOutput', false));
|
||||
|
||||
% --- Use specified scan parameter, auto-detect if not specified ---
|
||||
if isfield(options,'scan_parameter') && ~isempty(options.scan_parameter)
|
||||
fprintf('\n[INFO] Using user-specified scan parameter(s): ');
|
||||
if iscell(options.scan_parameter)
|
||||
fprintf('%s\n', strjoin(options.scan_parameter, ', '));
|
||||
scan_parameter_names = options.scan_parameter;
|
||||
nParams = numel(scan_parameter_names);
|
||||
else
|
||||
fprintf('%s\n', options.scan_parameter);
|
||||
scan_parameter_names = options.scan_parameter;
|
||||
nParams = 1;
|
||||
end
|
||||
else
|
||||
[scan_parameter_names, nParams] = detectScanParametersFromFiles(fullFileNames);
|
||||
end
|
||||
|
||||
if ~exist('nParams','var')
|
||||
nParams = 1;
|
||||
end
|
||||
|
||||
% --- Preallocate temp scan values for parfor ---
|
||||
temp_scan_vals = cell(nFiles,1);
|
||||
|
||||
% --- Check for Parallel Computing Toolbox ---
|
||||
useParallel = license('test','Distrib_Computing_Toolbox') && ~options.SAVE_TO_WORKSPACE;
|
||||
|
||||
if useParallel
|
||||
fprintf('\n[INFO] Parallel Computing Toolbox detected. Using parallelization for raw data processing...\n');
|
||||
raw_file_list = fullFileNames;
|
||||
|
||||
% --- Preallocate scan parameters and file list for parallel mode ---
|
||||
raw_scan_parameter_values = nan(1, nFiles, 'single');
|
||||
raw_file_list = fullFileNames;
|
||||
|
||||
% --- Parallel loop without progress bar ---
|
||||
parfor k = 1:nFiles
|
||||
[od_img, bkg_img, val] = readAndComputeOD(fullFileNames(k), options, groupList, ny, nx);
|
||||
[od_img, bkg_img, val] = readAndComputeOD(fullFileNames(k), options, groupList, ny, nx, scan_parameter_names);
|
||||
temp_scan_vals{k} = val(:).';
|
||||
|
||||
if options.SAVE_TO_WORKSPACE
|
||||
full_od_imgs(:,:,k) = single(od_img);
|
||||
full_bkg_imgs(:,:,k) = single(bkg_img);
|
||||
else
|
||||
writeFullODImagesToDisk(fullODImageFolder, od_img, bkg_img, val, fullFileNames(k), k);
|
||||
end
|
||||
raw_scan_parameter_values(k) = val;
|
||||
end
|
||||
else
|
||||
% --- Standard for-loop with your progress bar ---
|
||||
showPB = isfield(options,'showProgressBar') && options.showProgressBar;
|
||||
if showPB && options.SAVE_TO_WORKSPACE
|
||||
pb = Helper.ProgressBar();
|
||||
@ -97,7 +127,8 @@ function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list]
|
||||
end
|
||||
|
||||
for k = 1:nFiles
|
||||
[od_img, bkg_img, val] = readAndComputeOD(fullFileNames(k), options, groupList, ny, nx);
|
||||
[od_img, bkg_img, val] = readAndComputeOD(fullFileNames(k), options, groupList, ny, nx, scan_parameter_names);
|
||||
temp_scan_vals{k} = val(:).';
|
||||
|
||||
if options.SAVE_TO_WORKSPACE
|
||||
full_od_imgs(:,:,k) = single(od_img);
|
||||
@ -105,8 +136,7 @@ function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list]
|
||||
else
|
||||
writeFullODImagesToDisk(fullODImageFolder, od_img, bkg_img, val, fullFileNames(k), k);
|
||||
end
|
||||
raw_scan_parameter_values(k) = val;
|
||||
raw_file_list(k) = fullFileNames(k);
|
||||
raw_file_list(k) = fullFileNames(k);
|
||||
|
||||
if showPB && options.SAVE_TO_WORKSPACE
|
||||
progressPercent = round(k/nFiles*100);
|
||||
@ -118,10 +148,31 @@ function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list]
|
||||
pb.run(' Done!');
|
||||
end
|
||||
end
|
||||
|
||||
% --- Convert cell array to numeric matrix after parfor ---
|
||||
if nParams == 1
|
||||
raw_scan_parameter_values = cellfun(@(c)c, temp_scan_vals);
|
||||
raw_scan_parameter_values = reshape(raw_scan_parameter_values, 1, []); % Ensure it's a row vector
|
||||
else
|
||||
raw_scan_parameter_values = vertcat(temp_scan_vals{:}); % rows = files, cols = parameters
|
||||
end
|
||||
|
||||
if (~isfield(options,'scan_reference_values') || isempty(options.scan_reference_values))
|
||||
if isvector(raw_scan_parameter_values)
|
||||
% Single parameter case
|
||||
scan_reference_values = unique(raw_scan_parameter_values(:), 'stable');
|
||||
scan_reference_values = scan_reference_values(:).'; % row vector
|
||||
else
|
||||
% Multi-parameter case: unique rows
|
||||
scan_reference_values = unique(raw_scan_parameter_values, 'rows', 'stable');
|
||||
end
|
||||
else
|
||||
scan_reference_values = options.scan_reference_values;
|
||||
end
|
||||
end
|
||||
|
||||
%% --- Local helper functions ---
|
||||
function [od_img, bkg_img, val] = readAndComputeOD(fullFileName, options, groupList, ny, nx)
|
||||
function [od_img, bkg_img, val] = readAndComputeOD(fullFileName, options, groupList, ny, nx, scanParamNames)
|
||||
try
|
||||
atm_img = double(imrotate(h5read(fullFileName, append(groupList(options.cam), "/atoms")), options.angle, 'bilinear', 'crop'));
|
||||
bkg_img = double(imrotate(h5read(fullFileName, append(groupList(options.cam), "/background")), options.angle, 'bilinear', 'crop'));
|
||||
@ -133,31 +184,127 @@ function [od_img, bkg_img, val] = readAndComputeOD(fullFileName, options, groupL
|
||||
bkg_img = nan(ny, nx);
|
||||
end
|
||||
|
||||
% --- Extract scan parameter safely ---
|
||||
% --- Read scan parameter(s) for this file ---
|
||||
try
|
||||
info = h5info(fullFileName, '/globals');
|
||||
attrNames = string({info.Attributes.Name});
|
||||
if ismember(options.scan_parameter, attrNames)
|
||||
val = h5readatt(fullFileName, '/globals', options.scan_parameter);
|
||||
if strcmp(options.scan_parameter,'ps_rot_mag_fin_pol_angle')
|
||||
val = 180 - val;
|
||||
if iscell(scanParamNames)
|
||||
val = NaN(1,numel(scanParamNames));
|
||||
for j = 1:numel(scanParamNames)
|
||||
val(j) = h5readatt(fullFileName, '/globals', scanParamNames{j});
|
||||
end
|
||||
else
|
||||
val = NaN;
|
||||
val = h5readatt(fullFileName, '/globals', scanParamNames);
|
||||
end
|
||||
catch
|
||||
val = NaN;
|
||||
end
|
||||
end
|
||||
|
||||
function writeFullODImagesToDisk(fullODImageFolder, od_img, bkg_img, scan_val, file_name, idx)
|
||||
% Writes a single OD/BKG image + scan parameter to a MAT file
|
||||
function [scanParamNames, nParams] = detectScanParametersFromFiles(fileNames, minFilesToCheck)
|
||||
% Detect scan parameter(s) by checking which numeric attributes vary across multiple files
|
||||
%
|
||||
% Inputs:
|
||||
% fileNames - string array or cell array of full HDF5 file paths
|
||||
% minFilesToCheck - minimum number of files to compare (default: 3)
|
||||
%
|
||||
% Outputs:
|
||||
% scanParamNames - char array (single) or cell array of char arrays (multiple)
|
||||
% nParams - number of detected scan parameters
|
||||
|
||||
if nargin < 2
|
||||
minFilesToCheck = 3;
|
||||
end
|
||||
|
||||
% Ensure fileNames is a string array
|
||||
if iscell(fileNames)
|
||||
fileNames = string(fileNames);
|
||||
end
|
||||
|
||||
nFiles = numel(fileNames);
|
||||
nCheck = min(minFilesToCheck, nFiles);
|
||||
fprintf('\n[INFO] Detecting scan parameter(s)...\n');
|
||||
|
||||
% Read attribute names from first file
|
||||
info = h5info(fileNames(1));
|
||||
if any(strcmp({info.Groups.Name}, '/globals'))
|
||||
globalsGroup = info.Groups(strcmp({info.Groups.Name}, '/globals'));
|
||||
else
|
||||
warning('\n[WARNING] /globals group not found in first file.');
|
||||
scanParamNames = NaN;
|
||||
nParams = 0;
|
||||
return;
|
||||
end
|
||||
|
||||
attrNames = string({globalsGroup.Attributes.Name});
|
||||
numericAttrNames = strings(0,1);
|
||||
|
||||
% Identify numeric attributes
|
||||
for j = 1:numel(attrNames)
|
||||
if attrNames(j) == "runs"
|
||||
continue;
|
||||
end
|
||||
val = h5readatt(fileNames(1), '/globals', attrNames(j));
|
||||
if isnumeric(val)
|
||||
numericAttrNames(end+1) = attrNames(j); %#ok<AGROW>
|
||||
end
|
||||
end
|
||||
|
||||
if isempty(numericAttrNames)
|
||||
fprintf('\n[WARNING] No numeric attributes in /globals.\n');
|
||||
scanParamNames = NaN;
|
||||
nParams = 0;
|
||||
return;
|
||||
end
|
||||
|
||||
% --- Check which numeric attributes vary across first few files ---
|
||||
varyingParams = strings(0,1);
|
||||
for j = 1:numel(numericAttrNames)
|
||||
attrName = numericAttrNames(j);
|
||||
values = NaN(1, nCheck);
|
||||
for k = 1:nCheck
|
||||
try
|
||||
val = h5readatt(fileNames(k), '/globals', attrName);
|
||||
if isnumeric(val)
|
||||
values(k) = val(1); % use first element if array
|
||||
end
|
||||
catch
|
||||
values(k) = NaN;
|
||||
end
|
||||
end
|
||||
% Ignore NaNs when checking for variation
|
||||
if numel(unique(values(~isnan(values)))) > 1
|
||||
varyingParams(end+1) = attrName; %#ok<AGROW>
|
||||
end
|
||||
end
|
||||
|
||||
% --- Return result ---
|
||||
nParams = numel(varyingParams);
|
||||
if nParams == 0
|
||||
fprintf('\n[INFO] No varying scan parameters detected.\n');
|
||||
scanParamNames = NaN;
|
||||
elseif nParams == 1
|
||||
scanParamNames = char(varyingParams); % single char array
|
||||
fprintf('\n[INFO] Single scan parameter detected: %s\n', scanParamNames);
|
||||
else
|
||||
scanParamNames = cellstr(varyingParams); % cell array of char arrays
|
||||
fprintf('\n[INFO] Multiple scan parameters detected: %s\n', strjoin(varyingParams, ', '));
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function writeFullODImagesToDisk(fullODImageFolder, od_img, bkg_img, scan_vals, file_name, idx)
|
||||
% Writes OD/BKG image + scan parameter(s) to a MAT file
|
||||
matFilePath = fullfile(fullODImageFolder, sprintf('Image_%04d.mat', idx));
|
||||
OD = single(od_img);
|
||||
BKG = single(bkg_img);
|
||||
Scan = single(scan_val);
|
||||
File = string(file_name);
|
||||
save(matFilePath, 'OD','BKG','Scan','File','-v7.3');
|
||||
|
||||
if isscalar(scan_vals)
|
||||
Scan = single(scan_vals);
|
||||
save(matFilePath, 'OD','BKG','Scan','File','-v7.3');
|
||||
else
|
||||
Scan = single(scan_vals); %#ok<NASGU>
|
||||
save(matFilePath, 'OD','BKG','Scan','File','-v7.3');
|
||||
end
|
||||
end
|
||||
|
||||
function dataSource = makeDataSourceStruct(folderPath)
|
||||
|
||||
@ -11,6 +11,15 @@ function [selectedPath, folderPath] = selectDataSourcePath(dataSources, options)
|
||||
|
||||
allPaths = {}; % initialize candidate paths
|
||||
lookup = struct('rawPath', {}, 'fullODPath', {});
|
||||
|
||||
% --- General path to full od image folders ---
|
||||
if isfield(options, 'FullODImagesFolder')
|
||||
full_od_image_parent_folder = options.FullODImagesFolder;
|
||||
elseif isfield(options, 'saveDirectory')
|
||||
full_od_image_parent_folder = options.saveDirectory;
|
||||
else
|
||||
full_od_image_parent_folder = '';
|
||||
end
|
||||
|
||||
% --- Gather candidate raw data paths ---
|
||||
for i = 1:numel(dataSources)
|
||||
@ -55,8 +64,8 @@ function [selectedPath, folderPath] = selectDataSourcePath(dataSources, options)
|
||||
% Build matching FullOD path
|
||||
expectedName = sprintf('FullODImages_%s_%s_Run%s', ...
|
||||
targetSequence, strrep(targetDate,'/','-'), runID);
|
||||
if isfield(options,'FullODImagesFolder') && isfolder(options.FullODImagesFolder) && ~isempty(options.FullODImagesFolder)
|
||||
fullODPath = fullfile(options.FullODImagesFolder, expectedName);
|
||||
if isfolder(full_od_image_parent_folder) && ~isempty(full_od_image_parent_folder)
|
||||
fullODPath = fullfile(full_od_image_parent_folder, expectedName);
|
||||
else
|
||||
fullODPath = '';
|
||||
end
|
||||
@ -69,15 +78,6 @@ function [selectedPath, folderPath] = selectDataSourcePath(dataSources, options)
|
||||
end
|
||||
end
|
||||
|
||||
% --- General path to full od image folders ---
|
||||
if isfield(options, 'FullODImagesFolder')
|
||||
full_od_image_parent_folder = options.FullODImagesFolder;
|
||||
elseif isfield(options, 'saveDirectory')
|
||||
full_od_image_parent_folder = options.saveDirectory;
|
||||
else
|
||||
full_od_image_parent_folder = '';
|
||||
end
|
||||
|
||||
% --- Determine whether FullODImagesFolder should be used ---
|
||||
useFullOD = false;
|
||||
if ~isempty(allPaths)
|
||||
|
||||
@ -11,7 +11,7 @@ options = struct();
|
||||
|
||||
% File paths
|
||||
options.baseDataFolder = '//DyLabNAS/Data';
|
||||
options.FullODImagesFolder = 'D:/Data - Experiment/FullODImages/202508';
|
||||
% options.FullODImagesFolder = 'D:/Data - Experiment/FullODImages/202508';
|
||||
options.measurementName = 'DropletsToStripes';
|
||||
scriptFullPath = mfilename('fullpath');
|
||||
options.saveDirectory = fileparts(scriptFullPath);
|
||||
@ -42,24 +42,6 @@ options.Angular_Sigma = 2;
|
||||
options.Angular_WindowSize = 5;
|
||||
options.zoom_size = 50;
|
||||
|
||||
% Scan parameter
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.scan_reference_values = [2.40, 2.39, 2.38, 2.37, 2.35, 2.34, 2.32, 2.30, 2.28, 2.26, 2.24, 2.22, 2.2, 2.15, 2.10, 2.05, 2, 1.95, 1.90, 1.85, 1.8];
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.scan_reference_values = [2.45, 2.44, 2.43, 2.42, 2.4, 2.39, 2.38, 2.37, 2.36, 2.35, 2.34, 2.32, 2.3, 2.28, 2.25, 2.2, 2.15, 2.10, 2.0, 1.90, 1.8];
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.scan_reference_values = [0, 5, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40];
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.scan_reference_values = fliplr([0, 5, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40]);
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
% Flags
|
||||
options.skipUnshuffling = false;
|
||||
options.skipNormalization = false;
|
||||
@ -79,6 +61,20 @@ options.showProgressBar = true;
|
||||
|
||||
% Extras
|
||||
options.font = 'Bahnschrift';
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.flipSortOrder = true;
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.flipSortOrder = true;
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.flipSortOrder = true;
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.flipSortOrder = false;
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
%% ===== Collect Images and Launch Viewer =====
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user