Calculations/Data-Analyzer/+Analyzer/conductCorrelationAnalysis.m

211 lines
7.8 KiB
Matlab

function results = conductCorrelationAnalysis(od_imgs, scan_parameter_values, options)
%% conductCorrelationAnalysis
% Author: Karthik
% Date: 2025-09-14
% Version: 1.0
%
% Description:
% Computes 2D autocorrelation g²(Δx,Δy) on OD images.
% Extracts radial and angular distributions using Calculator functions.
% Optionally plots results and saves figures.
%
% Inputs:
% od_imgs - cell array of OD images
% scan_parameter_values - array of scan parameter values
% options - struct with fields:
% saveDirectory - base directory to save results
% skipSaveFigures - skip saving plots
% skipLivePlot - skip live plotting
% pixel_size - physical pixel size of camera sensor (m)
% magnification - imaging magnification
% maximumShift - maximum pixel shift for g²
% font - font name for plots
%
% Outputs:
% results - struct containing g² maps, radial and angular distributions
%
% Notes:
% Optional notes, references.
%% ===== Unpack struct arguments =====
pixel_size = options.pixel_size;
magnification = options.magnification;
skipLivePlot = options.skipLivePlot;
skipSaveFigures = options.skipSaveFigures;
saveDirectory = options.saveDirectory;
font = options.font;
radial_theta = options.Radial_Theta;
radial_window_size = options.Radial_WindowSize;
N_angular_bins = options.N_angular_bins;
r_min = options.Radial_Minimum;
r_max = options.Radial_Maximum;
if isfield(options, 'maximumShift') && ~isempty(options.maximumShift)
maximumShift = options.maximumShift;
else
maximumShift = 5; % [µm]
end
% --- Handle units ---
if ischar(options.scanParameterUnits) || isstring(options.scanParameterUnits)
unitList = {char(options.scanParameterUnits)};
else
unitList = options.scanParameterUnits;
end
%% ===== Initialization =====
N_shots = numel(od_imgs);
g2_matrices = cell(1, N_shots);
g2_radial = cell(1, N_shots);
g2_angular = cell(1, N_shots);
r_vals = cell(1, N_shots);
theta_vals = cell(1, N_shots);
dx_phys_ind = cell(1, N_shots);
dy_phys_ind = cell(1, N_shots);
dx = pixel_size / magnification;
shifts = -maximumShift:maximumShift;
if ~skipSaveFigures
saveFolder = fullfile(saveDirectory, 'Results', 'SavedFigures', 'AutocorrAnalysis');
if ~exist(saveFolder, 'dir')
mkdir(saveFolder);
end
end
%% ===== Loop over images =====
for k = 1:N_shots
IMG = od_imgs{k};
% Compute g² in physical units
[g2_matrix, dx_phys, dy_phys] = Calculator.compute2DAutocorrelation( ...
IMG, maximumShift, pixel_size, magnification);
g2_matrices{k} = g2_matrix;
dx_phys_ind{k} = dx_phys;
dy_phys_ind{k} = dy_phys;
% Extract radial profile (within angular window)
[r_vals{k}, g2_radial{k}] = Calculator.computeRadialCorrelation( ...
g2_matrix, dx_phys, dy_phys, radial_theta);
% Extract angular profile (within radial band)
[theta_vals{k}, g2_angular{k}] = Calculator.computeAngularCorrelation( ...
g2_matrix, dx_phys, dy_phys, r_min, r_max, N_angular_bins);
% Smooth radial profile
g2_radial_smoothed = movmean(g2_radial{k}, radial_window_size);
%% ===== Plotting =====
if ~skipLivePlot
figure(1); clf
set(gcf,'Position',[500 100 1000 800])
tiledlayout(2,2,'TileSpacing','compact','Padding','compact');
% OD image
ax1 = nexttile;
imagesc(IMG);
axis equal tight;
set(gca,'FontSize',14,'YDir','normal');
colormap(ax1, Colormaps.inferno());
hcb = colorbar;
ylabel(hcb,'Optical Density','Rotation',-90,'FontSize',14,'FontName',font);
xlabel('x [px]','FontSize',14,'FontName',font);
ylabel('y [px]','FontSize',14,'FontName',font);
title('OD Image','FontSize',16,'FontWeight','bold','FontName',font);
% Annotate scan parameter
if iscell(scan_parameter_values)
param_row = scan_parameter_values{k};
else
param_row = scan_parameter_values(k,:);
end
if numel(unitList) < numel(param_row)
unitList(end+1:numel(param_row)) = {''};
end
xPos = 0.975; yPos = 0.975; yStep = 0.075;
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',txtInterpreter,'Units','normalized', ...
'HorizontalAlignment','right','VerticalAlignment','top');
end
% g² map
ax2 = nexttile;
imagesc(shifts, shifts, g2_matrix);
axis equal tight;
set(gca,'FontSize',14,'YDir','normal');
colormap(ax2, Colormaps.coolwarm());
colorbar;
xlabel('\Deltax (\mum)','FontSize',14,'FontName',font);
ylabel('\Deltay (\mum)','FontSize',14,'FontName',font);
title('Autocorrelation g_2(\Deltax,\Deltay)','FontSize',16,'FontWeight','bold','FontName',font);
% Radial distribution
ax3 = nexttile;
plot(r_vals{k}, g2_radial_smoothed, 'LineWidth',2);
set(gca,'FontSize',14);
set(gca, 'FontSize', 14, 'XLim', [min(r_vals{k}), max(r_vals{k})], 'YLim', [0, 1]);
xlabel('r [\mum]','Interpreter','tex','FontSize',14,'FontName',font);
ylabel('g_2(r)','FontSize',14,'FontName',font);
title('Radial Distribution','FontSize',16,'FontWeight','bold','FontName',font);
grid on;
% Angular distribution
ax4 = nexttile;
plot(theta_vals{k}/pi, g2_angular{k}, 'LineWidth',2);
set(gca,'FontSize', 14, 'YLim', [0, 1]);
xlabel('\theta/\pi [rad]','Interpreter','tex','FontSize',14,'FontName',font);
ylabel('g_2(\theta)','FontSize',14,'FontName',font);
title('Angular Distribution','FontSize',16,'FontWeight','bold','FontName',font);
grid on;
ax = gca;
ax.MinorGridLineStyle = ':';
ax.MinorGridColor = [0.7 0.7 0.7];
ax.MinorGridAlpha = 0.5;
ax.XMinorGrid = 'on';
ax.YMinorGrid = 'on';
end
%% ===== Save figures =====
if ~skipSaveFigures && ~skipLivePlot
fileNamePNG = fullfile(saveFolder, sprintf('g2_analysis_img_%03d.png', k));
print(gcf, fileNamePNG, '-dpng','-r100');
elseif ~skipLivePlot
pause(0.5);
end
end
%% ===== Package results =====
results = struct();
results.g2_matrices = g2_matrices;
results.g2_radial = g2_radial;
results.g2_angular = g2_angular;
results.r_vals = r_vals;
results.theta_vals = theta_vals;
results.dx_phys = dx_phys_ind;
results.dy_phys = dy_phys_ind;
results.scan_parameter_values = unique(scan_parameter_values);
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