Modifications to read and analyse multi-parameter scans - correlation extraction still not modified for this case.

This commit is contained in:
Karthik 2025-09-07 17:33:44 +02:00
parent f41663c801
commit 228a35d22e
9 changed files with 262 additions and 78 deletions

View File

@ -78,6 +78,13 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio
mkdir(saveFolder);
end
end
% --- Handle units: allow single string or cell array ---
if ischar(options.scanParameterUnits) || isstring(options.scanParameterUnits)
unitList = {char(options.scanParameterUnits)}; % wrap single unit in cell
else
unitList = options.scanParameterUnits; % assume cell array
end
%% ===== Main loop over images =====
for k = 1:N_shots
@ -171,18 +178,27 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio
title('OD Image', 'FontSize', 16, 'FontWeight', 'bold', 'Interpreter', 'tex', 'FontName', font);
% Annotate scan parameter
if strcmp(scan_parameter, 'ps_rot_mag_fin_pol_angle')
text(0.975, 0.975, sprintf('%.1f^\\circ', scan_parameter_values(k)), ...
% --- Extract parameter row for this shot ---
param_row = scan_parameter_values(k,:);
% --- Ensure units list is long enough ---
if numel(unitList) < numel(param_row)
unitList(end+1:numel(param_row)) = {''}; % pad with empty units
end
% --- Place one text object per parameter ---
xPos = 0.975; % normalized x-position (right-aligned)
yPos = 0.975; % starting y-position (top)
yStep = 0.05; % vertical spacing between multiple parameters
for j = 1:numel(param_row)
[unitSuffix, txtInterpreter] = getUnitInfo(unitList{j});
text(xPos, yPos - (j-1)*yStep, sprintf('%.2f%s', param_row(j), unitSuffix), ...
'Color', 'white', 'FontWeight', 'bold', 'FontSize', 14, ...
'Interpreter', 'tex', 'Units', 'normalized', ...
'HorizontalAlignment', 'right', 'VerticalAlignment', 'top');
else
text(0.975, 0.975, sprintf('%.2f G', scan_parameter_values(k)), ...
'Color', 'white', 'FontWeight', 'bold', 'FontSize', 14, ...
'Interpreter', 'tex', 'Units', 'normalized', ...
'Interpreter', txtInterpreter, 'Units', 'normalized', ...
'HorizontalAlignment', 'right', 'VerticalAlignment', 'top');
end
% FFT power spectrum
ax2 = nexttile;
imagesc(kx, ky, log(1 + PS_all{k}));
@ -260,3 +276,18 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio
results.angular_spectral_weight = angular_spectral_weight;
end
%% === Local helper function ===
function [unitSuffix, txtInterpreter] = getUnitInfo(u)
switch lower(u)
case {'degrees','deg','°'}
unitSuffix = '^\circ';
txtInterpreter = 'tex';
case {'gauss','g'}
unitSuffix = ' G';
txtInterpreter = 'none';
otherwise
unitSuffix = '';
txtInterpreter = 'none';
end
end

View File

@ -70,22 +70,38 @@ function runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list,
% Update figure title with shot + filename
hFig.Name = sprintf('Shot %d | %s', idx, shortName);
% Determine unit suffix and interpreter
switch lower(options.scanParameterUnits)
case {'degrees', 'deg', '°'}
unitSuffix = '^\circ'; % LaTeX degree symbol
txtInterpreter = 'tex';
case {'gauss', 'g'}
unitSuffix = ' G';
txtInterpreter = 'none';
otherwise
unitSuffix = '';
txtInterpreter = 'none';
%% --- Generalized unit handling ---
% Extract parameter row for this shot
if iscell(scan_parameter_values)
% Multi-parameter scan stored as cell array of row vectors
param_row = scan_parameter_values{idx};
else
% Numeric vector / matrix
param_row = scan_parameter_values(idx,:);
end
% Update overlay text with scan parameter + unit
txtHandle.String = sprintf('%.2f%s', scan_parameter_values(idx), unitSuffix);
txtHandle.Interpreter = txtInterpreter; % switch interpreter dynamically
% Wrap single unit string into a cell if needed
if ischar(options.scanParameterUnits) || isstring(options.scanParameterUnits)
unitList = {char(options.scanParameterUnits)};
else
unitList = options.scanParameterUnits; % assume cell array
end
% Ensure units list is long enough
if numel(unitList) < numel(param_row)
unitList(end+1:numel(param_row)) = {''}; % pad with empty units
end
% Build text lines for each parameter
txtLines = cell(1, numel(param_row));
for j = 1:numel(param_row)
[unitSuffix, txtInterpreter] = getUnitInfo(unitList{j});
txtLines{j} = sprintf('%.2f%s', param_row(j), unitSuffix);
end
% Join multiple parameters with newline
txtHandle.String = strjoin(txtLines, '\n');
txtHandle.Interpreter = txtInterpreter; % use last parameter's interpreter
sliderHandle.Value = idx;
drawnow;
@ -104,3 +120,18 @@ function runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list,
end
end
end
%% --- Helper function ---
function [unitSuffix, txtInterpreter] = getUnitInfo(u)
switch lower(u)
case {'degrees','deg','°'}
unitSuffix = '^\circ';
txtInterpreter = 'tex';
case {'gauss','g'}
unitSuffix = ' G';
txtInterpreter = 'none';
otherwise
unitSuffix = '';
txtInterpreter = 'none';
end
end

View File

@ -34,6 +34,7 @@ function [od_imgs, scan_parameter_values, scan_reference_values, file_list] = co
fprintf('\n[INFO] Reusing processed OD images, scan parameters, and file list from memory.\n');
od_imgs = evalin('base','od_imgs');
scan_parameter_values = evalin('base','scan_parameter_values');
scan_reference_values = evalin('base','scan_reference_values');
file_list = evalin('base','file_list');
% --- Save OD images to disk if requested ---
@ -278,8 +279,30 @@ function [od_imgs, scan_parameter_values, scan_reference_values, file_list] = co
data = load(fullfile(full_od_image_subfolder, mat_files(k).name));
od_mat = data.OD;
bkg_mat = data.BKG;
raw_scan_parameter_names{k} = data.Scan_Param;
raw_scan_parameter_values(k) = data.Scan_Val;
% --- Handle parameter names ---
raw_scan_parameter_names{k} = data.Scan_Param;
% --- Handle parameter values ---
if numel(data.Scan_Val) == 1
% First time through: initialize numeric storage if empty
if isempty(raw_scan_parameter_values)
raw_scan_parameter_values = zeros(1,nFiles);
elseif iscell(raw_scan_parameter_values)
error('Mixed single-parameter and multi-parameter scans detected.');
end
raw_scan_parameter_values(k) = data.Scan_Val;
else
% First time through: initialize cell storage if empty
if isempty(raw_scan_parameter_values)
raw_scan_parameter_values = cell(nFiles,1);
elseif isnumeric(raw_scan_parameter_values)
error('Mixed single-parameter and multi-parameter scans detected.');
end
raw_scan_parameter_values{k} = data.Scan_Val(:).';
end
raw_file_list(k) = data.File;
end
else
@ -328,78 +351,94 @@ function [od_imgs, scan_parameter_values, scan_reference_values, file_list] = co
fprintf('\n[INFO] Reordering images...\n');
% --- Determine scan reference values ---
if (~isfield(options,'scan_reference_values') || isempty(options.scan_reference_values))
if isvector(raw_scan_parameter_values)
% Single parameter case
if ~isfield(options, 'scan_reference_values') || isempty(options.scan_reference_values)
if isnumeric(raw_scan_parameter_values) && isvector(raw_scan_parameter_values)
% --- Single parameter case (numeric vector) ---
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);
elseif iscell(raw_scan_parameter_values)
% --- Multi-parameter case (cell array of row vectors) ---
params = cell2mat(raw_scan_parameter_values); % convert to numeric matrix
scan_reference_values = unique(params, 'rows', 'stable');
n_total = numel(raw_scan_parameter_values);
n_values = size(scan_reference_values, 1);
else
error('Unsupported format for raw scan parameter values.');
end
else
% --- Use reference values from options ---
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);
if isnumeric(raw_scan_parameter_values) && 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);
n_total = size(raw_scan_parameter_values, 1);
n_values = size(scan_reference_values, 1);
end
end
% --- Determine sort order based on options.flipSortOrder ---
% --- Determine sort order ---
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);
% --- Sort reference values ---
if isnumeric(scan_reference_values) && isvector(scan_reference_values)
scan_reference_values = sort(scan_reference_values, sort_order);
else
scan_reference_values = sortrows(scan_reference_values, sort_order);
scan_reference_values = sortrows(scan_reference_values, sort_order);
end
% --- Convert multi-parameter scan_reference_values to cell array ---
if ~isnumeric(scan_reference_values) || size(scan_reference_values,2) > 1
scan_reference_values = mat2cell(scan_reference_values, ...
ones(size(scan_reference_values,1),1), ...
size(scan_reference_values,2));
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
if isnumeric(raw_scan_parameter_values)
% Single parameter
ref_val = scan_reference_values(j); % scalar
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));
elseif iscell(raw_scan_parameter_values)
% Multi-parameter
ref_val = scan_reference_values{j}; % row vector
diffs = cellfun(@(x) all(abs(x - ref_val) < tol), raw_scan_parameter_values);
idx_all = find(diffs);
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(:);
idx_mat(:, j) = idx_all(:);
end
% --- Reordered indices ---
ordered_idx = reshape(idx_mat.', 1, []);
% --- Apply reorder ---
od_imgs = od_imgs(ordered_idx);
if isvector(raw_scan_parameter_values)
if isnumeric(raw_scan_parameter_values)
scan_parameter_values = raw_scan_parameter_values(ordered_idx).';
else
scan_parameter_values = raw_scan_parameter_values(ordered_idx,:);
elseif iscell(raw_scan_parameter_values)
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
@ -607,6 +646,6 @@ function [mat_files, raw_scan_parameter_names, raw_scan_parameter_values, raw_fi
mat_files = mat_files(~strcmp({mat_files.name},'metadata.mat')); % exclude metadata
nFiles = numel(mat_files);
raw_scan_parameter_names = cell(1,nFiles);
raw_scan_parameter_values = zeros(1,nFiles);
raw_scan_parameter_values = [];
raw_file_list = strings(1,nFiles);
end

View File

@ -11,7 +11,7 @@ options = struct();
% File paths
options.baseDataFolder = '//DyLabNAS/Data';
options.FullODImagesFolder = 'F:/Data - Experiment/FullODImages/202508';
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
options.measurementName = 'BECToDroplets';
scriptFullPath = mfilename('fullpath');
options.saveDirectory = fileparts(scriptFullPath);
@ -86,8 +86,8 @@ end
%% ===== Collect Images and Launch Viewer =====
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[od_imgs, scan_parameter_values, file_list] = Helper.collectODImages(options);
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
Analyzer.runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list, options);

View File

@ -11,7 +11,7 @@ options = struct();
% File paths
options.baseDataFolder = '//DyLabNAS/Data';
options.FullODImagesFolder = 'D:/Data - Experiment/FullODImages/202508';
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
options.measurementName = 'DropletsToStripes';
scriptFullPath = mfilename('fullpath');
options.saveDirectory = fileparts(scriptFullPath);
@ -86,8 +86,8 @@ end
%% ===== Collect Images and Launch Viewer =====
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[od_imgs, scan_parameter_values, file_list] = Helper.collectODImages(options);
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
Analyzer.runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list, options);

View File

@ -11,7 +11,7 @@ options = struct();
% File paths
options.baseDataFolder = '//DyLabNAS/Data';
options.FullODImagesFolder = 'F:/Data - Experiment/FullODImages/202508';
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
options.measurementName = 'BECToStripes';
scriptFullPath = mfilename('fullpath');
options.saveDirectory = fileparts(scriptFullPath);
@ -86,8 +86,8 @@ end
%% ===== Collect Images and Launch Viewer =====
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[od_imgs, scan_parameter_values, file_list] = Helper.collectODImages(options);
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
Analyzer.runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list, options);

View File

@ -86,8 +86,8 @@ end
%% ===== Collect Images and Launch Viewer =====
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[od_imgs, scan_parameter_values, file_list] = Helper.collectODImages(options);
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
Analyzer.runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list, options);

View File

@ -0,0 +1,83 @@
%% ===== BEC-Droplets-Stripes Settings =====
% Specify data location to run analysis on
dataSources = {
struct('sequence', 'StructuralPhaseTransition', ...
'date', '2025/08/20', ...
'runs', [2]) % specify run numbers as a string in "" or just as a numeric value
};
options = struct();
% File paths
options.baseDataFolder = '//DyLabNAS/Data';
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
options.measurementName = 'DropletsToStripes';
scriptFullPath = mfilename('fullpath');
options.saveDirectory = fileparts(scriptFullPath);
% Camera / imaging settings
options.cam = 4; % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
options.angle = 0; % angle by which image will be rotated
options.center = [1420, 2050];
options.span = [200, 200];
options.fraction = [0.1, 0.1];
options.pixel_size = 5.86e-6; % in meters
options.magnification = 24.6;
options.ImagingMode = 'HighIntensity';
options.PulseDuration = 5e-6; % in s
% Fourier analysis settings
options.theta_min = deg2rad(0);
options.theta_max = deg2rad(180);
options.N_radial_bins = 500;
options.Radial_Sigma = 2;
options.Radial_WindowSize = 5; % odd number
options.k_min = 1.2771; % μm¹
options.k_max = 2.5541; % μm¹
options.N_angular_bins = 180;
options.Angular_Threshold = 75;
options.Angular_Sigma = 2;
options.Angular_WindowSize = 5;
options.zoom_size = 50;
% Flags
options.skipUnshuffling = false;
options.skipNormalization = false;
options.skipFringeRemoval = true;
options.skipPreprocessing = true;
options.skipMasking = true;
options.skipIntensityThresholding = true;
options.skipBinarization = true;
options.skipFullODImagesFolderUse = false;
options.skipSaveData = false;
options.skipSaveFigures = true;
options.skipSaveProcessedOD = true;
options.skipLivePlot = true;
options.showProgressBar = true;
% Extras
options.font = 'Bahnschrift';
switch options.measurementName
case 'DropletsToStripes'
options.ignore_scan_parameter = {'ps_rot_mag_field'}; % Parameter to IGNORE from these - rot_mag_field, ps_rot_mag_field, ps_rot_mag_fin_pol_angle
options.flipSortOrder = true;
options.scanParameterUnits = {'gauss','degrees'};
options.titleString = 'Droplets to Stripes';
case 'StripesToDroplets'
options.ignore_scan_parameter = {'ps_rot_mag_field'}; % Parameter to IGNORE from these - rot_mag_field, ps_rot_mag_field, ps_rot_mag_fin_pol_angle
options.flipSortOrder = false;
options.scanParameterUnits = {'gauss','degrees'};
options.titleString = 'Stripes to Droplets';
end
%% ===== Collect Images and Launch Viewer =====
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
Analyzer.runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list, options);

View File

@ -4,7 +4,7 @@
dataSources = {
struct('sequence', 'StructuralPhaseTransition', ...
'date', '2025/08/20', ...
'runs', [1]) % specify run numbers as a string in "" or just as a numeric value
'runs', [2]) % specify run numbers as a string in "" or just as a numeric value
};
options = struct();
@ -52,7 +52,7 @@ options.skipMasking = true;
options.skipIntensityThresholding = true;
options.skipBinarization = true;
options.skipFullODImagesFolderUse = true;
options.skipFullODImagesFolderUse = false;
options.skipSaveData = false;
options.skipSaveFigures = true;
options.skipSaveProcessedOD = true;
@ -64,15 +64,15 @@ options.font = 'Bahnschrift';
switch options.measurementName
case 'DropletsToStripes'
options.ignore_scan_parameter = {'ps_rot_mag_field'}; % Parameter to IGNORE from these - rot_mag_field, ps_rot_mag_field, ps_rot_mag_fin_pol_angle
options.flipSortOrder = false;
options.scanParameterUnits = 'degrees';
options.flipSortOrder = true;
options.scanParameterUnits = {'gauss','degrees'};
options.titleString = 'Droplets to Stripes';
case 'StripesToDroplets'
options.ignore_scan_parameter = {'ps_rot_mag_field'}; % Parameter to IGNORE from these - rot_mag_field, ps_rot_mag_field, ps_rot_mag_fin_pol_angle
options.flipSortOrder = false;
options.scanParameterUnits = 'degrees';
options.scanParameterUnits = {'gauss','degrees'};
options.titleString = 'Stripes to Droplets';
end
%% ===== Run Batch Analysis =====
results_all = Helper.batchAnalyze(dataSources, options);
results_all = Helper.batchAnalyze(dataSources, options);