297 lines
12 KiB
Matlab
297 lines
12 KiB
Matlab
function [spectral_weight, g2, theta_vals] = conductFourierAnalysis(folder_path, run_index, N_bins, Threshold, Sigma, SuppressPlotFlag)
|
|
|
|
arguments
|
|
folder_path (1,:) char
|
|
run_index (1,:) {mustBeNumeric,mustBeReal}
|
|
N_bins (1,:) {mustBeNumeric,mustBeReal}
|
|
Threshold (1,:) {mustBeNumeric,mustBeReal}
|
|
Sigma (1,:) {mustBeNumeric,mustBeReal}
|
|
SuppressPlotFlag (1,:) logical = true
|
|
end
|
|
|
|
set(0,'defaulttextInterpreter','latex')
|
|
set(groot, 'defaultAxesTickLabelInterpreter','latex'); set(groot, 'defaultLegendInterpreter','latex');
|
|
|
|
% Load data
|
|
Data = load(fullfile(fullfile(folder_path, sprintf('Run_%03i', run_index)), 'psi_gs.mat'), 'psi', 'Transf', 'Observ', 'Params');
|
|
|
|
Params = Data.Params;
|
|
Transf = Data.Transf;
|
|
Observ = Data.Observ;
|
|
|
|
if isgpuarray(Data.psi)
|
|
psi = gather(Data.psi);
|
|
else
|
|
psi = Data.psi;
|
|
end
|
|
if isgpuarray(Data.Observ.residual)
|
|
Observ.residual = gather(Data.Observ.residual);
|
|
else
|
|
Observ.residual = Data.Observ.residual;
|
|
end
|
|
|
|
alpha = Params.theta;
|
|
|
|
% Axes scaling and coordinates in micrometers
|
|
x = Transf.x * Params.l0 * 1e6;
|
|
y = Transf.y * Params.l0 * 1e6;
|
|
z = Transf.z * Params.l0 * 1e6;
|
|
|
|
dz = z(2)-z(1);
|
|
|
|
% Calculate frequency increment (frequency axes)
|
|
Nx = length(x); % grid size along X
|
|
Ny = length(y); % grid size along Y
|
|
dx = mean(diff(x)); % real space increment in the X direction (in micrometers)
|
|
dy = mean(diff(y)); % real space increment in the Y direction (in micrometers)
|
|
dvx = 1 / (Nx * dx); % reciprocal space increment in the X direction (in micrometers^-1)
|
|
dvy = 1 / (Ny * dy); % reciprocal space increment in the Y direction (in micrometers^-1)
|
|
|
|
% Create the frequency axes
|
|
vx = (-Nx/2:Nx/2-1) * dvx; % Frequency axis in X (micrometers^-1)
|
|
vy = (-Ny/2:Ny/2-1) * dvy; % Frequency axis in Y (micrometers^-1)
|
|
|
|
% Calculate maximum frequencies
|
|
% kx_max = pi / dx;
|
|
% ky_max = pi / dy;
|
|
|
|
% Generate reciprocal axes
|
|
% kx = linspace(-kx_max, kx_max * (Nx-2)/Nx, Nx);
|
|
% ky = linspace(-ky_max, ky_max * (Ny-2)/Ny, Ny);
|
|
|
|
% Create the Wavenumber axes
|
|
kx = 2*pi*vx; % Wavenumber axis in X
|
|
ky = 2*pi*vy; % Wavenumber axis in Y
|
|
|
|
% Compute probability density |psi|^2
|
|
n = abs(psi).^2;
|
|
|
|
nxy = squeeze(trapz(n*dz,3));
|
|
|
|
skipPreprocessing = true;
|
|
skipMasking = true;
|
|
skipIntensityThresholding = true;
|
|
skipBinarization = true;
|
|
|
|
%% Extract Spectral Weight and g2
|
|
|
|
IMG = nxy;
|
|
|
|
[IMGFFT, ~] = computeFourierTransform(IMG, skipPreprocessing, skipMasking, skipIntensityThresholding, skipBinarization);
|
|
|
|
[theta_vals, S_theta] = computeNormalizedAngularSpectralDistribution(IMGFFT, 10, 35, N_bins, Threshold, Sigma);
|
|
|
|
spectral_weight = trapz(theta_vals, S_theta);
|
|
|
|
g2 = zeros(1, N_bins); % Preallocate
|
|
|
|
for dtheta = 0:N_bins-1
|
|
profile = S_theta;
|
|
profile_shifted = circshift(profile, -dtheta, 2);
|
|
|
|
num = mean(profile .* profile_shifted);
|
|
denom = mean(profile)^2;
|
|
|
|
g2(dtheta+1) = num / denom - 1;
|
|
end
|
|
|
|
if ~SuppressPlotFlag
|
|
figure(1);
|
|
clf
|
|
set(gcf,'Position',[500 100 1000 800])
|
|
t = tiledlayout(2, 2, 'TileSpacing', 'compact', 'Padding', 'compact'); % 1x4 grid
|
|
font = 'Bahnschrift';
|
|
% Display the cropped OD image
|
|
ax1 = nexttile;
|
|
plotxy = pcolor(x,y,IMG');
|
|
set(plotxy, 'EdgeColor', 'none');
|
|
% Define normalized positions (relative to axis limits)
|
|
x_offset = 0.025; % 5% offset from the edges
|
|
y_offset = 0.025; % 5% offset from the edges
|
|
% Top-right corner (normalized axis coordinates)
|
|
hText = text(1 - x_offset, 1 - y_offset, ...
|
|
['\alpha = ', num2str(rad2deg(alpha), '%.1f'), '^\circ'], ...
|
|
'Color', 'white', 'FontWeight', 'bold', ...
|
|
'Interpreter', 'tex', 'FontSize', 20, ...
|
|
'Units', 'normalized', ...
|
|
'HorizontalAlignment', 'right', ...
|
|
'VerticalAlignment', 'top');
|
|
axis square;
|
|
hcb = colorbar;
|
|
hcb.Label.Interpreter = 'latex';
|
|
colormap(gca, Helper.Colormaps.plasma())
|
|
set(gca, 'FontSize', 14); % For tick labels only
|
|
hL = ylabel(hcb, 'Column Density');
|
|
hXLabel = xlabel('$x$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14);
|
|
hYLabel = ylabel('$y$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14);
|
|
hTitle = title('$|\Psi(x,y)|^2$', 'Interpreter', 'latex', 'FontSize', 14) ;
|
|
set(hText, 'FontName', font)
|
|
set([hXLabel, hYLabel, hL], 'FontSize', 14)
|
|
set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title
|
|
|
|
|
|
% Plot the power spectrum
|
|
nexttile;
|
|
imagesc(kx, ky, log(1 + abs(IMGFFT).^2));
|
|
axis square;
|
|
hcb = colorbar;
|
|
colormap(gca, Helper.Colormaps.plasma())
|
|
set(gca, 'FontSize', 14); % For tick labels only
|
|
set(gca,'YDir','normal')
|
|
hXLabel = xlabel('k_x', 'Interpreter', 'tex');
|
|
hYLabel = ylabel('k_y', 'Interpreter', 'tex');
|
|
hTitle = title('Power Spectrum - S(k_x,k_y)', 'Interpreter', 'tex');
|
|
set([hXLabel, hYLabel], 'FontName', font)
|
|
set([hXLabel, hYLabel], 'FontSize', 14)
|
|
set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title
|
|
|
|
% Plot the angular distribution
|
|
nexttile
|
|
plot(theta_vals/pi, S_theta,'Linewidth',2);
|
|
set(gca, 'FontSize', 14); % For tick labels only
|
|
hXLabel = xlabel('\theta/\pi [rad]', 'Interpreter', 'tex');
|
|
hYLabel = ylabel('Normalized magnitude (a.u.)', 'Interpreter', 'tex');
|
|
hTitle = title('Angular Spectral Distribution - S(\theta)', 'Interpreter', 'tex');
|
|
set([hXLabel, hYLabel], 'FontName', font)
|
|
set([hXLabel, hYLabel], 'FontSize', 14)
|
|
set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title
|
|
grid on
|
|
|
|
nexttile
|
|
plot(theta_vals/pi, g2, 'o-', 'LineWidth', 1.2, 'MarkerSize', 5);
|
|
set(gca, 'FontSize', 14);
|
|
ylim([-1.5 3.0]); % Set y-axis limits here
|
|
hXLabel = xlabel('$\delta\theta / \pi$', 'Interpreter', 'latex');
|
|
hYLabel = ylabel('$g^{(2)}(\delta\theta)$', 'Interpreter', 'latex');
|
|
hTitle = title('Autocorrelation', 'Interpreter', 'tex');
|
|
set([hXLabel, hYLabel], 'FontName', font)
|
|
set([hXLabel, hYLabel], 'FontSize', 14)
|
|
set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title
|
|
grid on;
|
|
end
|
|
end
|
|
|
|
%% Helper Functions
|
|
function [IMGFFT, IMGPR] = computeFourierTransform(I, skipPreprocessing, skipMasking, skipIntensityThresholding, skipBinarization)
|
|
% computeFourierSpectrum - Computes the 2D Fourier power spectrum
|
|
% of binarized and enhanced lattice image features, with optional central mask.
|
|
%
|
|
% Inputs:
|
|
% I - Grayscale or RGB image matrix
|
|
%
|
|
% Output:
|
|
% F_mag - 2D Fourier power spectrum (shifted)
|
|
|
|
if ~skipPreprocessing
|
|
% Preprocessing: Denoise
|
|
filtered = imgaussfilt(I, 10);
|
|
IMGPR = I - filtered; % adjust sigma as needed
|
|
else
|
|
IMGPR = I;
|
|
end
|
|
|
|
if ~skipMasking
|
|
[rows, cols] = size(IMGPR);
|
|
[X, Y] = meshgrid(1:cols, 1:rows);
|
|
% Elliptical mask parameters
|
|
cx = cols / 2;
|
|
cy = rows / 2;
|
|
|
|
% Shifted coordinates
|
|
x = X - cx;
|
|
y = Y - cy;
|
|
|
|
% Ellipse semi-axes
|
|
rx = 0.4 * cols;
|
|
ry = 0.2 * rows;
|
|
|
|
% Rotation angle in degrees -> radians
|
|
theta_deg = 30; % Adjust as needed
|
|
theta = deg2rad(theta_deg);
|
|
|
|
% Rotated ellipse equation
|
|
cos_t = cos(theta);
|
|
sin_t = sin(theta);
|
|
|
|
x_rot = (x * cos_t + y * sin_t);
|
|
y_rot = (-x * sin_t + y * cos_t);
|
|
|
|
ellipseMask = (x_rot.^2) / rx^2 + (y_rot.^2) / ry^2 <= 1;
|
|
|
|
% Apply cutout mask
|
|
IMGPR = IMGPR .* ellipseMask;
|
|
end
|
|
|
|
if ~skipIntensityThresholding
|
|
% Apply global intensity threshold mask
|
|
intensity_thresh = 0.20;
|
|
intensity_mask = IMGPR > intensity_thresh;
|
|
IMGPR = IMGPR .* intensity_mask;
|
|
end
|
|
|
|
if ~skipBinarization
|
|
% Adaptive binarization and cleanup
|
|
IMGPR = imbinarize(IMGPR, 'adaptive', 'Sensitivity', 0.0);
|
|
IMGPR = imdilate(IMGPR, strel('disk', 2));
|
|
IMGPR = imerode(IMGPR, strel('disk', 1));
|
|
IMGPR = imfill(IMGPR, 'holes');
|
|
F = fft2(double(IMGPR)); % Compute 2D Fourier Transform
|
|
IMGFFT = abs(fftshift(F))'; % Shift zero frequency to center
|
|
else
|
|
F = fft2(double(IMGPR)); % Compute 2D Fourier Transform
|
|
IMGFFT = abs(fftshift(F))'; % Shift zero frequency to center
|
|
end
|
|
end
|
|
|
|
function [theta_vals, S_theta] = computeNormalizedAngularSpectralDistribution(IMGFFT, r_min, r_max, num_bins, threshold, sigma)
|
|
% Apply threshold to isolate strong peaks
|
|
IMGFFT(IMGFFT < threshold) = 0;
|
|
|
|
% Prepare polar coordinates
|
|
[ny, nx] = size(IMGFFT);
|
|
[X, Y] = meshgrid(1:nx, 1:ny);
|
|
cx = ceil(nx/2);
|
|
cy = ceil(ny/2);
|
|
R = sqrt((X - cx).^2 + (Y - cy).^2);
|
|
Theta = atan2(Y - cy, X - cx); % range [-pi, pi]
|
|
|
|
% Choose radial band
|
|
radial_mask = (R >= r_min) & (R <= r_max);
|
|
|
|
% Initialize the angular structure factor array
|
|
S_theta = zeros(1, num_bins); % Pre-allocate for 180 angle bins
|
|
% Define the angle values for the x-axis
|
|
theta_vals = linspace(0, pi, num_bins);
|
|
|
|
% Loop through each angle bin
|
|
for i = 1:num_bins
|
|
angle_start = (i-1) * pi / num_bins;
|
|
angle_end = i * pi / num_bins;
|
|
|
|
% Define a mask for the given angle range
|
|
angle_mask = (Theta >= angle_start & Theta < angle_end);
|
|
|
|
bin_mask = radial_mask & angle_mask;
|
|
|
|
% Extract the Fourier components for the given angle
|
|
fft_angle = IMGFFT .* bin_mask;
|
|
|
|
% Integrate the Fourier components over the radius at the angle
|
|
S_theta(i) = sum(sum(abs(fft_angle).^2)); % sum of squared magnitudes
|
|
end
|
|
|
|
% Create a 1D Gaussian kernel
|
|
half_width = ceil(3 * sigma);
|
|
x = -half_width:half_width;
|
|
gauss_kernel = exp(-x.^2 / (2 * sigma^2));
|
|
gauss_kernel = gauss_kernel / sum(gauss_kernel); % normalize
|
|
|
|
% Apply convolution (circular padding to preserve periodicity)
|
|
S_theta = conv([S_theta(end-half_width+1:end), S_theta, S_theta(1:half_width)], gauss_kernel, 'same');
|
|
S_theta = S_theta(half_width+1:end-half_width); % crop back to original size
|
|
|
|
% Normalize to 1
|
|
S_theta = S_theta / max(S_theta);
|
|
end
|
|
|