Calculations/Data-Analyzer/+Plotter/plotODG2withAnalysis.m

183 lines
7.9 KiB
Matlab

function plotODG2withAnalysis(od_imgs, scan_parameter_values, g2_results, analysis_results, options, varargin)
%% plotODG2withAnalysis
% Author: Karthik
% Date: 2025-09-30
% Version: 1.0
%
% Description:
% For each scan parameter value, produces one or more compact figures.
% Each row contains:
% - Col 1: OD image
% - Col 2: g² correlation matrix
% Overlays from analysis (boundary, ellipse, centroid, anisotropy) can be
% optionally toggled on/off.
%
% Inputs:
% od_imgs : cell array of OD images
% scan_parameter_values: vector/array of scan parameters (one per image)
% g2_results : struct with fields
% - g2_matrices{k}
% - dx_phys{k}, dy_phys{k}
% analysis_results : struct with fields
% - boundary_coords{k}
% - ellipse_params{k}
% - peak_centroid{k}
% - anisotropy_vals(k)
% - roi_params{k} (optional)
% options : struct with imaging calibration
% - pixel_size (in meters)
% - magnification (unitless)
% varargin : name-value pairs
% - 'FontName', 'FontSize', 'SkipLivePlot', 'SkipSaveFigures',
% 'SaveDirectory', 'ShowOverlays', 'RepsPerPage'
%
% Notes:
% Requires conductCorrelationAnalysis and analyzeG2Structures outputs.
% --- Parse optional name-value pairs ---
p = inputParser;
addParameter(p, 'FontName', 'Arial', @ischar);
addParameter(p, 'FontSize', 12, @isnumeric);
addParameter(p, 'SkipLivePlot', false, @islogical);
addParameter(p, 'SkipSaveFigures', true, @islogical);
addParameter(p, 'SaveDirectory', pwd, @ischar);
addParameter(p, 'ShowOverlays', true, @islogical);
addParameter(p, 'RepsPerPage', 10, @isnumeric); % pagination
parse(p, varargin{:});
opts = p.Results;
% --- Setup save directory if needed ---
if ~opts.SkipSaveFigures
saveFolder = fullfile(opts.SaveDirectory, 'Results', 'SavedFigures', 'OD_G2_withAnalysis');
if ~exist(saveFolder, 'dir')
mkdir(saveFolder);
end
end
% --- Group by parameter value ---
param_vals = scan_parameter_values(:);
unique_params = unique(param_vals, 'rows');
N_params = size(unique_params, 1);
for pIdx = 1:N_params
matches = ismember(param_vals, unique_params(pIdx, :), 'rows');
idx_list = find(matches);
N_reps = numel(idx_list);
% --- Pagination over repetitions ---
numPages = ceil(N_reps / opts.RepsPerPage);
for pageIdx = 1:numPages
repStart = (pageIdx-1)*opts.RepsPerPage + 1;
repEnd = min(pageIdx*opts.RepsPerPage, N_reps);
repSubset = idx_list(repStart:repEnd);
N_rows = numel(repSubset);
% --- Create compact figure ---
if ~opts.SkipLivePlot
fig = figure('Color', 'w', 'Position', [100 100 400 800]);
figure(fig); % ensure visible
else
fig = figure('Color', 'w', 'Position', [100 100 400 800], 'Visible','off'); % invisible for direct save
end
t = tiledlayout(fig, N_rows, 2, 'TileSpacing', 'compact', 'Padding', 'compact');
title(t, sprintf('Scan parameter: %s | Page %d/%d', ...
mat2str(unique_params(pIdx, :)), pageIdx, numPages), ...
'FontSize', opts.FontSize + 2, 'FontWeight', 'bold', 'FontName', opts.FontName);
for r = 1:N_rows
k = repSubset(r);
% --- OD image ---
nexttile;
[M, N] = size(od_imgs{k});
x_phys = ((1:N) - ceil(N/2)) * (options.pixel_size / options.magnification * 1e6); % µm
y_phys = ((1:M) - ceil(M/2)) * (options.pixel_size / options.magnification * 1e6); % µm
imagesc(x_phys, y_phys, od_imgs{k});
axis image;
set(gca, 'YDir', 'normal', 'FontName', opts.FontName, 'FontSize', 14);
colormap(gca, Colormaps.inferno());
xlabel('x (\mum)');
ylabel('y (\mum)');
% --- g² correlation matrix ---
nexttile;
dx = g2_results.dx_phys{k};
dy = g2_results.dy_phys{k};
g2_matrix = g2_results.g2_matrices{k};
imagesc(dx, dy, g2_matrix);
axis image;
set(gca, 'YDir', 'normal', 'FontName', opts.FontName, 'FontSize', 14);
colormap(gca, Colormaps.coolwarm()); colorbar;
xlabel('\Deltax (\mum)'); ylabel('\Deltay (\mum)');
hold on;
% --- Optional overlays ---
if opts.ShowOverlays
boundary = analysis_results.boundary_coords{k};
ellipse = analysis_results.ellipse_params{k};
centroid = analysis_results.peak_centroid{k};
anisotropy = analysis_results.anisotropy_vals(k);
theta = analysis_results.ellipse_params{k}(5);
% ROI rectangle (rotated)
if isfield(analysis_results, 'roi_params')
roi = analysis_results.roi_params{k};
if ~isempty(roi) && all(~isnan(roi))
x0 = roi(1); y0 = roi(2);
w = roi(3); h = roi(4);
roi_theta = roi(5);
corners = [ -w/2, -h/2; w/2, -h/2; w/2, h/2; -w/2, h/2];
R = [cos(roi_theta), -sin(roi_theta); sin(roi_theta), cos(roi_theta)];
corners_rot = (R*corners')' + [x0, y0];
corners_rot = [corners_rot; corners_rot(1,:)];
plot(corners_rot(:,1), corners_rot(:,2), 'r--', 'LineWidth',1.5);
end
end
if all(~isnan(boundary(:)))
plot(boundary(:, 1), boundary(:, 2), 'g-', 'LineWidth', 2);
end
if all(~isnan(centroid))
plot(centroid(1), centroid(2), 'mo', 'MarkerSize', 8, 'LineWidth', 2);
x_c = centroid(1); y_c = centroid(2);
else
x_c = NaN; y_c = NaN;
end
if all(~isnan(ellipse)) && ~isnan(anisotropy) && anisotropy ~= 0
a = ellipse(3);
b = ellipse(4);
tEllipse = linspace(0, 2*pi, 200);
ellipse_x = x_c + a*cos(tEllipse)*cos(theta) - b*sin(tEllipse)*sin(theta);
ellipse_y = y_c + a*cos(tEllipse)*sin(theta) + b*sin(tEllipse)*cos(theta);
if anisotropy > 1.0
plot(ellipse_x, ellipse_y, 'y--', 'LineWidth', 2);
else
plot(ellipse_x, ellipse_y, 'y-', 'LineWidth', 2);
end
end
end
end
if ~opts.SkipLivePlot
drawnow; % render figure
end
% --- Save figure ---
if ~opts.SkipSaveFigures
saveFileName = sprintf('OD_G2_analysis_param_%03d_page_%02d.png', pIdx, pageIdx);
Plotter.saveFigure(fig, ...
'SaveFileName', saveFileName, ...
'SaveDirectory', saveFolder, ...
'SkipSaveFigures', opts.SkipSaveFigures);
end
% --- Close invisible figure to free memory ---
if opts.SkipLivePlot
close(fig);
end
end
end
end