MAJOR update: Added functionality to handle pre-computed OD images in a separate location and plot images or run analysis on it.

This commit is contained in:
Karthik 2025-08-28 16:19:13 +02:00
parent cf577bfcde
commit e64665af39
8 changed files with 465 additions and 176 deletions

View File

@ -34,11 +34,14 @@ function [results, scan_parameter_values] = performAnalysis(options)
options.skipSaveFigures (1,1) logical
options.skipSaveData (1,1) logical
options.skipSaveOD (1,1) logical
options.skipFullODImagesFolderUse (1,1) logical
options.showProgressBar (1,1) logical
options.measurementName (1,:) char
options.selectedPath (1,:) char
options.folderPath (1,:) char
options.baseDataFolder (1,:) char
options.saveDirectory (1,:) char
options.FullODImagesFolder (1,:) char
options.titleString (1,:) char
options.font (1,:) char
options.SAVE_TO_WORKSPACE (1,1) logical

View File

@ -8,84 +8,225 @@ function results_all = batchAnalyze(dataSources, options)
if ~isfield(options, 'baseDataFolder')
options.baseDataFolder = '//DyLabNAS/Data';
end
% Default skip flag
if ~isfield(options, 'skipFullODImagesFolderUse')
options.skipFullODImagesFolderUse = false;
end
% Determine whether to use FullODImagesFolder or raw baseDataFolder
useFullOD = false;
if isfield(options, 'FullODImagesFolder') && isfolder(options.FullODImagesFolder)
if ~isfolder(options.baseDataFolder)
if ~options.skipFullODImagesFolderUse
% Case 1a: raw folder missing, fallback to FullODImagesFolder
useFullOD = true;
fprintf('\n[INFO] Raw data folder not found but found full OD Images folder.\n');
fprintf('\n[INFO] Using full OD Images folder: %s\n', options.FullODImagesFolder);
else
% Case 1b: raw folder missing, fallback to
% FullODImagesFolder but user overrides
error('Raw data folder not found, found full OD Images folder which cannot be used (set skipFullODImagesFolderUse=false to override). Aborting.\n');
end
elseif ~options.skipFullODImagesFolderUse
% Case 2: both exist, prioritize FullODImagesFolder unless skipped
useFullOD = true;
fprintf('\n[INFO] Both raw data folder (%s) and full OD Images folder (%s) found.\n', ...
options.baseDataFolder, options.FullODImagesFolder);
fprintf('\n[INFO] Prioritizing full OD Images folder (set skipFullODImagesFolderUse=true to override).\n');
else
% Case 3: both exist, prioritize raw data folder because of overrride
fprintf('\n[INFO] Both raw data folder (%s) and full OD Images folder (%s) found.\n', ...
options.baseDataFolder, options.FullODImagesFolder);
fprintf('\n[INFO] Prioritizing raw data folder (set skipFullODImagesFolderUse=false to override).\n');
end
elseif isfolder(options.baseDataFolder)
% 🚨 Raw data folder exists, full OD images does not
fprintf('\n[INFO] Full OD Images folder not found but found raw data folder.\n');
fprintf('\n[INFO] Using raw data folder: %s\n', options.baseDataFolder);
end
% 🚨 Sanity check if neither folder exists
if ~useFullOD && ~isfolder(options.baseDataFolder)
warning('\n[ERROR] Neither raw data folder (%s) nor a valid full OD Images folder were found. Exiting.', ...
options.baseDataFolder);
results_all = {};
return;
end
% ===== Estimate dataset memory and get per-run estimates =====
[options.SAVE_TO_WORKSPACE, runMemoryGB] = Helper.estimateDatasetMemory(dataSources, options);
[options.SAVE_TO_WORKSPACE, ~] = Helper.estimateDatasetMemory(dataSources, options);
results_all = {}; % one element per folder
for i = 1:numel(dataSources)
ds = dataSources{i};
% Use per-sequence baseFolder if present, otherwise default from options
if isfield(ds, 'baseFolder') && ~isempty(ds.baseFolder)
baseFolder = fullfile(ds.baseFolder, ds.sequence, ds.date);
else
baseFolder = fullfile(options.baseDataFolder, ds.sequence, ds.date);
% --- FULL OD MODE ---
if useFullOD
% --- List available FullODImages_* folders ---
fullODFolders = dir(fullfile(options.FullODImagesFolder, 'FullODImages_*'));
fullODFolders = fullODFolders([fullODFolders.isdir]);
ds = dataSources{1}; % only one dataSources struct
% Ensure sequences, dates, runs are cell arrays for uniform processing
sequences = ds.sequence; if ischar(sequences), sequences = {sequences}; end
dates = ds.date; if ischar(dates), dates = {dates}; end
runs = ds.runs; if isnumeric(runs), runs = num2cell(runs); end
if isstring(runs), runs = cellstr(runs); end
% Loop over all combinations of sequence × date × run
for seqIdx = 1:numel(sequences)
for dateIdx = 1:numel(dates)
for runIdx = 1:numel(runs)
targetSequence = sequences{seqIdx};
targetDate = dates{dateIdx};
targetRun = runs{runIdx};
matched = false;
for i = 1:numel(fullODFolders)
selectedPath = fullfile(fullODFolders(i).folder, fullODFolders(i).name);
% Load metadata for run info
metaFile = fullfile(selectedPath, 'metadata.mat');
if ~isfile(metaFile)
warning('[WARNING] No metadata.mat in %s, skipping.', selectedPath);
continue;
end
S = load(metaFile, 'metadata');
% Reconstruct sequence/date/run from stored folderPath
dataSourceMeta = makeDataSourceStruct(S.metadata.options.folderPath);
% Check for match: measurementName and run info
if isfield(S.metadata.options, 'measurementName') && ...
isfield(options, 'measurementName') && ...
strcmp(S.metadata.options.measurementName, options.measurementName) && ...
strcmp(dataSourceMeta{1}.sequence, targetSequence) && ...
strcmp(dataSourceMeta{1}.date, targetDate) && ...
dataSourceMeta{1}.runs == targetRun
fprintf('\n[INFO] Found matching full OD images subfolder: %s\n', fullODFolders(i).name);
options.selectedPath = selectedPath;
options.folderPath = S.metadata.options.folderPath;
matched = true;
break;
end
end
if ~matched
warning('[WARNING] No matching full OD images subfolder found for sequence %s, date %s, run %s, measurementName %s.', ...
targetSequence, targetDate, targetRun, options.measurementName);
continue; % skip this run but continue to next combination
end
% Proceed to analysis for this combination
try
args = [fieldnames(options), struct2cell(options)]';
args = args(:)';
[analysisResults, scan_parameter_values] = Analyzer.performAnalysis(args{:});
result = struct();
result.sequence = targetSequence;
result.date = targetDate;
result.run = targetRun;
result.path = options.folderPath;
result.options = options;
result.results = analysisResults;
result.scan_parameter_values = scan_parameter_values;
% Save dataset as MAT
if ~isfield(options, 'skipSaveData') || ~options.skipSaveData
saveResultStruct(result, options.saveDirectory);
end
results_all{end+1,1} = result;
catch ME
warning("Error processing %s: %s", options.folderPath, ME.message);
end
end
end
end
return; % handled all in FullOD mode
end
for j = 1:numel(ds.runs)
runItem = ds.runs(j);
% Convert numeric or char arrays to a string with leading zeros if needed
if isnumeric(runItem)
runID = sprintf('%04d', runItem); % adjust padding as needed
elseif isstring(runItem)
runID = runItem;
elseif ischar(runItem)
runID = string(runItem);
elseif iscell(runItem)
runID = string(runItem{1}); % handles cell of char
else
error('Unsupported type for run entry: %s', class(runItem));
end
% --- Check per-run memory before processing ---
if runMemoryGB(j) > 0.5 * getAvailableRAM()
fprintf('[WARNING] Skipping run %s/%s due to high memory requirement (%.2f GB)\n', ...
ds.sequence, runID, runMemoryGB(j));
continue; % skip this run
end
% Build folder path
folderPath = fullfile(baseFolder, runID);
options.folderPath = folderPath;
try
% Convert struct -> name-value args
args = [fieldnames(options), struct2cell(options)]';
args = args(:)';
% Perform analysis (now returns 2 outputs)
[analysisResults, scan_parameter_values] = Analyzer.performAnalysis(args{:});
% Store flat struct with metadata + results + options
result = struct();
result.sequence = ds.sequence;
result.date = ds.date;
result.run = runID;
result.path = folderPath;
result.options = options;
result.results = analysisResults;
result.scan_parameter_values = scan_parameter_values;
% Save each dataset as its own MAT file
if ~isfield(options, 'skipSaveData') || ~options.skipSaveData
saveResultStruct(result, options.saveDirectory);
% --- RAW MODE (default) ---
ds = dataSources{1}; % single struct
% Ensure sequences, dates, and runs are cell arrays
sequences = ds.sequence; if ischar(sequences), sequences = {sequences}; end
dates = ds.date; if ischar(dates), dates = {dates}; end
runs = ds.runs; if isnumeric(runs), runs = num2cell(runs); end
if isstring(runs), runs = cellstr(runs); end
% Loop over all combinations of sequence × date × run
for seqIdx = 1:numel(sequences)
for dateIdx = 1:numel(dates)
for runIdx = 1:numel(runs)
targetSequence = sequences{seqIdx};
targetDate = dates{dateIdx};
runItem = runs{runIdx};
% Convert runItem to string with leading zeros if numeric
if isnumeric(runItem)
runID = sprintf('%04d', runItem);
elseif isstring(runItem)
runID = runItem;
elseif ischar(runItem)
runID = string(runItem);
elseif iscell(runItem)
runID = string(runItem{1});
else
error('Unsupported type for run entry: %s', class(runItem));
end
% Determine base folder
if isfield(ds, 'baseFolder') && ~isempty(ds.baseFolder)
baseFolder = fullfile(ds.baseFolder, targetSequence, targetDate);
else
baseFolder = fullfile(options.baseDataFolder, targetSequence, targetDate);
end
% Append to output
results_all{end+1,1} = result;
catch ME
warning("Error processing %s/%s/%s: %s", ...
ds.sequence, ds.date, runID, ME.message);
% Build folder path
folderPath = fullfile(baseFolder, runID);
options.folderPath = folderPath;
try
% Convert struct -> name-value args
args = [fieldnames(options), struct2cell(options)]';
args = args(:)';
% Perform analysis
[analysisResults, scan_parameter_values] = Analyzer.performAnalysis(args{:});
% Store results
result = struct();
result.sequence = targetSequence;
result.date = targetDate;
result.run = runID;
result.path = folderPath;
result.options = options;
result.results = analysisResults;
result.scan_parameter_values = scan_parameter_values;
% Save dataset
if ~isfield(options, 'skipSaveData') || ~options.skipSaveData
saveResultStruct(result, options.saveDirectory);
end
% Append to output
results_all{end+1,1} = result;
catch ME
warning("Error processing %s/%s/%s: %s", ...
targetSequence, targetDate, runID, ME.message);
end
end
end
end
end
%% ---- Local function for saving results ----
%% --- Local helper functions ---
function saveResultStruct(result, saveDirectory)
% Define results folder
resultsFolder = fullfile(saveDirectory, "Results", "SavedData");
@ -117,12 +258,33 @@ function saveResultStruct(result, saveDirectory)
save(indexFile, "nextIdx");
end
%% ---- Local function to get available RAM ----
function availableRAM = getAvailableRAM()
if ispc
[~, sys] = memory;
availableRAM = sys.PhysicalMemory.Available;
else
availableRAM = 16e9; % fallback: 16 GB if not Windows
end
function dataSource = makeDataSourceStruct(folderPath)
% Split by file separators (handles / or \)
parts = regexp(folderPath, '[\\/]', 'split');
% Remove empty parts caused by leading slashes
parts = parts(~cellfun('isempty', parts));
% Extract sequence, date, and run number
% Now the indices are correct:
% parts = {'DyLabNAS', 'Data', 'StructuralPhaseTransition', '2025', '08', '13', '0062'}
sequence = parts{3}; % "StructuralPhaseTransition"
year = parts{4}; % "2025"
month = parts{5}; % "08"
day = parts{6}; % "13"
runStr = parts{7}; % "0062"
% Build date string
dateStr = sprintf('%s/%s/%s', year, month, day);
% Convert run string to number
runNum = str2double(runStr);
% Construct struct inside a cell array
dataSource = {
struct('sequence', sequence, ...
'date', dateStr, ...
'runs', runNum)
};
end

View File

@ -47,7 +47,16 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
end
% --- General path to full od image folders ---
fullodimage_folders = dir(fullfile(options.saveDirectory,'FullODImages_*'));
if isfield(options, 'FullODImagesFolder') && ...
~isempty(options.FullODImagesFolder) && ...
isfolder(options.FullODImagesFolder)
% User-specified parent folder
fullodimage_folders = dir(fullfile(options.FullODImagesFolder, 'FullODImages_*'));
else
% Default to saveDirectory
fullodimage_folders = dir(fullfile(options.saveDirectory, 'FullODImages_*'));
end
% --- Specific sequence, data and run ---
dataSource = makeDataSourceStruct(options.folderPath);
@ -74,24 +83,44 @@ function [od_imgs, scan_parameter_values, file_list] = collectODImages(options)
raw_file_list = evalin('base', 'raw_file_list');
nFiles = size(full_od_imgs,3);
fprintf('\n[INFO] Cropping and subtracting background from images...\n');
elseif ~options.SAVE_TO_WORKSPACE && ~isempty(fullodimage_folders)
matched = false;
for r = 1:numel(fullodimage_folders)
metaPath = fullfile(fullodimage_folders(r).folder, fullodimage_folders(r).name, 'metadata.mat');
if ~isfile(metaPath), continue; end
S = load(metaPath,'metadata');
if isequaln(S.metadata.options, options)
fprintf('\n[INFO] Found matching full OD images folder: %s\n', fullodimage_folders(r).name);
fullodimage_folder = fullfile(fullodimage_folders(r).folder, fullodimage_folders(r).name);
matched = true;
break;
elseif ~options.SAVE_TO_WORKSPACE || (~isempty(fullodimage_folders) || (isfield(options,'selectedPath') && isfolder(options.selectedPath)))
matched = false;
% --- Use selectedPath directly if it exists and full OD usage is enabled ---
if isfield(options, 'selectedPath') && isfolder(options.selectedPath) ...
&& (~isfield(options, 'skipFullODImagesFolderUse') || ~options.skipFullODImagesFolderUse)
fullodimage_folder = options.selectedPath;
matched = true;
fprintf('\n[INFO] Using selected full OD images subfolder: %s\n', fullodimage_folder);
else
% --- Otherwise, search among available full OD image folders ---
for r = 1:numel(fullodimage_folders)
metaPath = fullfile(fullodimage_folders(r).folder, fullodimage_folders(r).name, 'metadata.mat');
if ~isfile(metaPath), continue; end
S = load(metaPath,'metadata');
% Compare only measurementName and folder path via dataSource struct
mdDataSource = makeDataSourceStruct(S.metadata.options.folderPath);
currentDataSource = makeDataSourceStruct(options.folderPath);
if isfield(S.metadata.options,'measurementName') && isfield(options,'measurementName') && ...
strcmp(S.metadata.options.measurementName, options.measurementName) && ...
isequal(mdDataSource, currentDataSource)
fullodimage_folder = fullfile(fullodimage_folders(r).folder, fullodimage_folders(r).name);
matched = true;
fprintf('\n[INFO] Found matching full OD images subfolder: %s\n', fullodimage_folders(r).name);
break;
end
end
end
if ~matched
fprintf('\n[INFO] No matching full OD images folder for this run found.\n');
fprintf('\n[INFO] No matching full OD images subfolder for this run found.\n');
[~, ~, ~, ~] = Helper.processRawData(options);
fprintf('\n[INFO] Completed computing OD images. Images will be stored on disk for reuse.\n');
end
% --- Load mat files from determined folder ---
mat_files = dir(fullfile(fullodimage_folder,'*.mat'));
mat_files = mat_files(~strcmp({mat_files.name}, 'metadata.mat')); % Exclude metadata.mat
nFiles = numel(mat_files);

View File

@ -1,5 +1,7 @@
function [SAVE_TO_WORKSPACE, runMemoryGB] = estimateDatasetMemory(dataSources, options)
% Estimate per-run memory and decide whether to save dataset to workspace
%
% Supports both raw data folders and a preselected run folder.
% --- Measured memory per image (bytes) ---
bytesPerFullImage = 37.75 * 1e6; % full OD image
@ -16,55 +18,61 @@ function [SAVE_TO_WORKSPACE, runMemoryGB] = estimateDatasetMemory(dataSources, o
SAVE_TO_WORKSPACE = true; % default, may change per run
runMemoryGB = []; % store per-run memory
% --- Loop over all data sources and runs ---
for i_ds = 1:numel(dataSources)
ds = dataSources{i_ds};
% --- Case 1: selectedPath exists ---
if isfield(options, 'selectedPath') && isfolder(options.selectedPath)
runFolder = options.selectedPath;
files = dir(fullfile(runFolder, '*.h5'));
nFiles = numel(files);
if isfield(ds, 'baseFolder') && ~isempty(ds.baseFolder)
baseFolder = fullfile(ds.baseFolder, ds.sequence, ds.date);
else
baseFolder = fullfile(options.baseDataFolder, ds.sequence, ds.date);
if nFiles > 0
runBytes = nFiles * (bytesPerFullImage + bytesPerCroppedImage);
runMemoryGB(end+1,1) = runBytes/1e9;
if runBytes > 0.75 * availableRAM
SAVE_TO_WORKSPACE = false;
fprintf('[INFO] Selected run %s estimated size %.2f GB exceeds 75%% of available RAM. Saving to disk.\n', ...
runFolder, runBytes/1e9);
else
fprintf('[INFO] Selected run %s estimated size %.2f GB fits in memory.\n', ...
runFolder, runBytes/1e9);
end
end
for j_run = 1:numel(ds.runs)
runItem = ds.runs(j_run);
% Convert runItem to string runID
if isnumeric(runItem)
runID = sprintf('%04d', runItem);
elseif isstring(runItem)
runID = runItem;
elseif ischar(runItem)
runID = string(runItem);
elseif iscell(runItem)
runID = string(runItem{1});
% --- Case 2: fallback to raw data folders ---
else
for i_ds = 1:numel(dataSources)
ds = dataSources{i_ds};
if isfield(ds, 'baseFolder') && ~isempty(ds.baseFolder)
baseFolder = fullfile(ds.baseFolder, ds.sequence, ds.date);
else
error('Unsupported run type');
baseFolder = fullfile(options.baseDataFolder, ds.sequence, ds.date);
end
runFolder = fullfile(baseFolder, runID);
for j_run = 1:numel(ds.runs)
runItem = ds.runs(j_run);
runID = sprintf('%04d', runItem);
runFolder = fullfile(baseFolder, runID);
if isfolder(runFolder)
files = dir(fullfile(runFolder, '*.h5'));
nFiles = numel(files);
if nFiles == 0
continue;
end
if isfolder(runFolder)
files = dir(fullfile(runFolder, '*.h5'));
nFiles = numel(files);
if nFiles == 0
continue;
end
% Memory estimate for this run (full + cropped)
runBytes = nFiles * (bytesPerFullImage + bytesPerCroppedImage);
runMemoryGB(end+1,1) = runBytes/1e9;
runBytes = nFiles * (bytesPerFullImage + bytesPerCroppedImage);
runMemoryGB(end+1,1) = runBytes/1e9;
% Decide workspace flag per run by comparing with 50% of available RAM
if runBytes > 0.75 * availableRAM
SAVE_TO_WORKSPACE = false;
fprintf('\n[INFO] Estimated size on memory of Run %s/%s too large (%.2f GB). Will save full OD images to disk and partially to workspace if not done so already.\n', ...
ds.sequence, runID, runBytes/1e9);
else
fprintf('\n[INFO] Estimated size on memory of Run %s/%s = %.2f GB. Will save full OD images data completely to workspace if not done so already.\n', ...
ds.sequence, runID, runBytes/1e9);
if runBytes > 0.75 * availableRAM
SAVE_TO_WORKSPACE = false;
fprintf('[INFO] Run %s/%s estimated size %.2f GB exceeds 75%% of available RAM. Saving to disk.\n', ...
ds.sequence, runID, runBytes/1e9);
else
fprintf('[INFO] Run %s/%s estimated size %.2f GB fits in memory.\n', ...
ds.sequence, runID, runBytes/1e9);
end
end
end
end
end
end
end

View File

@ -40,10 +40,20 @@ function [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list]
strrep(dataSource{1}.date,'/','-'), ...
dataSource{1}.runs);
fullODImageFolder = fullfile(options.saveDirectory, ['FullODImages_' runID]);
% --- Determine parent folder for FullODImages ---
if isfield(options, 'FullODImagesFoldersPath') && ...
~isempty(options.FullODImagesFoldersPath) && ...
isfolder(options.FullODImagesFoldersPath)
parentFolder = options.FullODImagesFoldersPath;
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

View File

@ -0,0 +1,102 @@
function [selectedPath, folderPath] = selectDataSourcePath(dataSources, options)
% Helper function to select a run/folder for analysis
%
% Inputs:
% dataSources - cell array of structs with fields: sequence, date, runs
% options - struct, may contain: baseDataFolder, FullODImagesFolder, skipFullODImagesFolderUse
%
% Outputs:
% selectedPath - actual folder to use (raw or FullOD)
% folderPath - constructed path from dataSources (always raw folder)
allPaths = {}; % initialize candidate paths
% --- Gather candidate raw data paths ---
for i = 1:numel(dataSources)
ds = dataSources{i};
dateParts = strsplit(ds.date,'/');
for run = ds.runs
runStr = sprintf('%04d', run);
rawPath = fullfile(options.baseDataFolder, ds.sequence, dateParts{:}, runStr);
if isfolder(rawPath)
allPaths{end+1} = rawPath;
else
fprintf('[INFO] Raw data folder does not exist: %s\n', rawPath);
end
end
end
% --- Determine whether FullODImagesFolder should be used ---
useFullOD = false;
if ~isempty(allPaths)
if isfield(options,'FullODImagesFolder') && isfolder(options.FullODImagesFolder)
if ~isfield(options,'skipFullODImagesFolderUse') || ~options.skipFullODImagesFolderUse
fprintf('\n[INFO] Both raw data folder (%s) and full OD Images folder (%s) found.\n', ...
options.baseDataFolder, options.FullODImagesFolder);
fprintf('[INFO] Prioritizing full OD Images folder (set skipFullODImagesFolderUse=true to override).\n');
useFullOD = true;
else
fprintf('\n[INFO] Both raw data folder (%s) and full OD Images folder (%s) found.\n', ...
options.baseDataFolder, options.FullODImagesFolder);
fprintf('[INFO] Prioritizing raw data folder (set skipFullODImagesFolderUse=false to override).\n');
end
else
fprintf('\n[INFO] Using raw data folder(s) since full OD images not found or not specified.\n');
end
elseif isfield(options,'FullODImagesFolder') && isfolder(options.FullODImagesFolder)
if ~options.skipFullODImagesFolderUse
useFullOD = true;
fprintf('\n[INFO] Raw data folder(s) not found but found full OD Images folder which will be used.\n');
allPaths{end+1} = options.FullODImagesFolder;
else
error('Raw data folder(s) not found, found full OD Images folder which cannot be used (set skipFullODImagesFolderUse=false to override). Aborting.\n');
end
end
if isempty(allPaths)
error('No valid paths for data found. Aborting.');
end
% --- If using full OD images folder, match the subfolder corresponding to sequence/date/run ---
if useFullOD
matchedPaths = {};
for i = 1:numel(dataSources)
ds = dataSources{i};
for run = ds.runs
expectedName = sprintf('FullODImages_%s_%s_Run%04d', ds.sequence, strrep(ds.date,'/','-'), run);
candidateFolder = fullfile(options.FullODImagesFolder, expectedName);
if isfolder(candidateFolder)
matchedPaths{end+1} = candidateFolder;
end
end
end
if ~isempty(matchedPaths)
allPaths = matchedPaths;
end
end
% --- Determine if user selection is needed ---
if numel(allPaths) > 1
set(0,'DefaultUicontrolFontSize',10);
[selectedIndex, tf] = listdlg('PromptString','Select a run to analyze:', ...
'SelectionMode','single', ...
'ListString', allPaths, ...
'ListSize',[500, 300]);
if ~tf
error('No path selected. Aborting.');
end
selectedPath = allPaths{selectedIndex};
else
% Only one candidate; select automatically
selectedPath = allPaths{1};
end
fprintf('\n[INFO] Selected path: %s\n', selectedPath);
% --- Construct folderPath from first entry in dataSources ---
ds = dataSources{1};
dateParts = strsplit(ds.date,'/');
runStr = sprintf('%04d', ds.runs(1));
folderPath = fullfile(options.baseDataFolder, ds.sequence, dateParts{:}, runStr);
end

View File

@ -4,16 +4,17 @@
dataSources = {
struct('sequence', 'StructuralPhaseTransition', ...
'date', '2025/08/16', ...
'runs', [8]) % specify run numbers as a string in "" or just as a numeric value
'runs', [8, 9, 10]) % specify run numbers as a string in "" or just as a numeric value
};
options = struct();
% File / paths
options.baseDataFolder = '//DyLabNAS/Data';
options.measurementName = 'DropletsToStripes';
scriptFullPath = mfilename('fullpath');
options.saveDirectory = fileparts(scriptFullPath);
options.baseDataFolder = '//DyLabNAS/Data';
options.measurementName = 'DropletsToStripes';
scriptFullPath = mfilename('fullpath');
options.saveDirectory = fileparts(scriptFullPath);
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
% Camera / imaging
options.cam = 5;
@ -61,12 +62,15 @@ switch options.measurementName
end
% Flags
options.skipNormalization = false;
options.skipUnshuffling = true;
options.skipNormalization = false;
options.skipPreprocessing = true;
options.skipMasking = true;
options.skipIntensityThresholding = true;
options.skipBinarization = true;
options.skipFullODImagesFolderUse = false;
options.skipSaveFigures = true;
options.skipSaveData = false;
options.skipSaveOD = true;
@ -76,43 +80,10 @@ options.showProgressBar = true;
% Extras
options.font = 'Bahnschrift';
%% ===== Build Paths from Data Sources =====
allPaths = {}; % initialize
for i = 1:length(dataSources)
ds = dataSources{i};
% Split the date string into year/month/day
dateParts = strsplit(ds.date, '/');
for run = ds.runs
runStr = sprintf('%04d', run); % 4-digit run number
fullPath = fullfile(options.baseDataFolder, ds.sequence, dateParts{:}, runStr);
if isfolder(fullPath) % only include valid directories
allPaths{end+1} = fullPath;
else
warning('Path does not exist: %s', fullPath);
end
end
end
% Let user select a single path
set(0,'DefaultUicontrolFontSize',10); % increase default font size
[selectedIndex, tf] = listdlg('PromptString','Select a path to analyze:', ...
'SelectionMode','single', ...
'ListString', allPaths, ...
'ListSize',[400, 300]); % width x height in pixels
if tf
options.folderPath = fullfile(allPaths{selectedIndex}); % store in options
fprintf('Path set to selection: %s\n', options.folderPath);
else
error('No path selected. Aborting.');
end
%% ===== Collect Images and Launch Viewer =====
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[od_imgs, scan_parameter_values, file_list] = Helper.collectODImages(options);
Analyzer.runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list, options);

View File

@ -10,10 +10,11 @@ dataSources = {
options = struct();
% File / paths
options.baseDataFolder = '//DyLabNAS/Data';
options.measurementName = 'DropletsToStripes';
scriptFullPath = mfilename('fullpath');
options.saveDirectory = fileparts(scriptFullPath);
options.baseDataFolder = '//DyLabNAS/Data';
options.measurementName = 'DropletsToStripes';
scriptFullPath = mfilename('fullpath');
options.saveDirectory = fileparts(scriptFullPath);
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
% Camera / imaging
options.cam = 5;
@ -53,24 +54,27 @@ switch options.measurementName
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.scan_reference_values = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45];
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.scan_reference_values = fliplr([0, 5, 10, 15, 20, 25, 30, 35, 40, 45]);
options.titleString = 'Stripes to Droplets';
end
% Flags
options.skipUnshuffling = true;
options.skipNormalization = false;
options.skipUnshuffling = false;
options.skipPreprocessing = true;
options.skipMasking = true;
options.skipIntensityThresholding = true;
options.skipBinarization = true;
options.skipFullODImagesFolderUse = false;
options.skipSaveFigures = true;
options.skipSaveData = false;
options.skipSaveOD = true;
options.skipLivePlot = true;
options.skipLivePlot = false;
options.showProgressBar = true;
% Extras