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:
parent
cf577bfcde
commit
e64665af39
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
102
Data-Analyzer/+Helper/selectDataSourcePath.m
Normal file
102
Data-Analyzer/+Helper/selectDataSourcePath.m
Normal 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
|
@ -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);
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user