211 lines
7.8 KiB
Matlab
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
|