204 lines
7.8 KiB
Matlab
204 lines
7.8 KiB
Matlab
classdef TwoGaussian2DModel < handle
|
|
%% TwoGaussian2DModel
|
|
% Author: Jianshun Gao
|
|
% Date: 2025-09-12
|
|
% Version: 1.0
|
|
%
|
|
% Description:
|
|
% This class provides methods to model, fit, and analyze 2D datasets using
|
|
% a sum of two Gaussian distributions. It supports:
|
|
% - Initial parameter estimation from 2D data.
|
|
% - Nonlinear least-squares fitting to extract Gaussian parameters.
|
|
% - Access and modification of parameter values and bounds.
|
|
%
|
|
% Properties:
|
|
% - params: Structure storing parameter values and bounds.
|
|
% - fitResult: MATLAB fit object after fitting.
|
|
% - gof: Goodness-of-fit structure from the fit.
|
|
%
|
|
% Methods:
|
|
% - guess(data, x, y): Estimate initial parameters from 2D data.
|
|
% - fit(data, x, y): Fit the two-Gaussian model to data.
|
|
% - getParamValue(paramName): Retrieve the current value of a parameter.
|
|
% - setParamValue(paramName, value): Update the value of a parameter.
|
|
% - setParamBounds(paramName, minVal, maxVal): Set parameter bounds.
|
|
%
|
|
% Usage:
|
|
% obj = TwoGaussian2DModel(); % Create object
|
|
% params = obj.guess(data, x, y); % Estimate parameters
|
|
% [fitResult, gof] = obj.fit(data, x, y); % Perform fitting
|
|
% ampA = obj.getParamValue('A_amplitude'); % Access parameter
|
|
% obj.setParamValue('B_sigmax', 5.0); % Modify parameter
|
|
% obj.setParamBounds('A_sigmay', 1.0, 10.0); % Set bounds
|
|
%
|
|
% Notes:
|
|
% - All parameter names must match those defined in the params structure.
|
|
% - This class is intended for 2D datasets where two Gaussian peaks are present.
|
|
|
|
properties
|
|
params; % Structure storing parameter values, bounds, etc.
|
|
fitResult; % MATLAB fit object after fitting
|
|
gof; % Goodness-of-fit metrics
|
|
end
|
|
|
|
methods
|
|
function obj = TwoGaussian2DModel()
|
|
% Constructor (empty)
|
|
end
|
|
|
|
function params = guess(obj, data, x, y, varargin)
|
|
% Estimate initial parameters for two Gaussian peaks
|
|
% Usage: obj.guess(data, x, y)
|
|
% Optional parameter:
|
|
% 'negative' - boolean, allow negative amplitude (default: false)
|
|
|
|
p = inputParser;
|
|
addParameter(p, 'negative', false);
|
|
parse(p, varargin{:});
|
|
|
|
% Simple peak estimation
|
|
[maxVal, maxIdx] = max(data(:));
|
|
[row, col] = ind2sub(size(data), maxIdx);
|
|
|
|
% Create parameter structure with values, min, and max
|
|
params = struct();
|
|
|
|
% Amplitude parameters
|
|
params.A_amplitude.value = maxVal;
|
|
params.A_amplitude.min = 0;
|
|
params.A_amplitude.max = 2 * maxVal;
|
|
|
|
params.B_amplitude.value = maxVal / 2;
|
|
params.B_amplitude.min = 0;
|
|
params.B_amplitude.max = 2 * maxVal;
|
|
|
|
% Center positions
|
|
params.A_centerx.value = x(col);
|
|
params.A_centerx.min = min(x);
|
|
params.A_centerx.max = max(x);
|
|
|
|
params.A_centery.value = y(row);
|
|
params.A_centery.min = min(y);
|
|
params.A_centery.max = max(y);
|
|
|
|
params.B_centerx.value = x(round(end/2));
|
|
params.B_centerx.min = min(x);
|
|
params.B_centerx.max = max(x);
|
|
|
|
params.B_centery.value = y(round(end/2));
|
|
params.B_centery.min = min(y);
|
|
params.B_centery.max = max(y);
|
|
|
|
% Standard deviations
|
|
sigmax_range = max(x) - min(x);
|
|
sigmay_range = max(y) - min(y);
|
|
|
|
params.A_sigmax.value = sigmax_range / 10;
|
|
params.A_sigmax.min = sigmax_range / 100;
|
|
params.A_sigmax.max = sigmax_range / 2;
|
|
|
|
params.A_sigmay.value = sigmay_range / 10;
|
|
params.A_sigmay.min = sigmay_range / 100;
|
|
params.A_sigmay.max = sigmay_range / 2;
|
|
|
|
params.B_sigmax.value = sigmax_range / 5;
|
|
params.B_sigmax.min = sigmax_range / 100;
|
|
params.B_sigmax.max = sigmax_range / 2;
|
|
|
|
params.B_sigmay.value = sigmay_range / 5;
|
|
params.B_sigmay.min = sigmay_range / 100;
|
|
params.B_sigmay.max = sigmay_range / 2;
|
|
|
|
obj.params = params;
|
|
end
|
|
|
|
function [fitResult, gof] = fit(obj, data, x, y, varargin)
|
|
% Fit the data to a sum of two 2D Gaussian distributions
|
|
% Usage: [fitResult, gof] = obj.fit(data, x, y)
|
|
% Optional parameter:
|
|
% 'params' - struct with initial guesses and bounds
|
|
|
|
p = inputParser;
|
|
addParameter(p, 'params', []);
|
|
parse(p, varargin{:});
|
|
|
|
if isempty(p.Results.params) && isempty(obj.params)
|
|
obj.guess(data, x, y, varargin{:});
|
|
elseif ~isempty(p.Results.params)
|
|
obj.params = p.Results.params;
|
|
end
|
|
|
|
% Prepare fitting data
|
|
[X, Y] = meshgrid(x, y);
|
|
xData = X(:);
|
|
yData = Y(:);
|
|
zData = data(:);
|
|
|
|
% Define two-Gaussian function
|
|
twoGauss = @(A_amplitude, B_amplitude, A_centerx, A_centery, ...
|
|
B_centerx, B_centery, A_sigmax, A_sigmay, B_sigmax, B_sigmay, x, y) ...
|
|
A_amplitude * exp(-((x-A_centerx).^2/(2*A_sigmax^2) + (y-A_centery).^2/(2*A_sigmay^2))) + ...
|
|
B_amplitude * exp(-((x-B_centerx).^2/(2*B_sigmax^2) + (y-B_centery).^2/(2*B_sigmay^2)));
|
|
|
|
% Create MATLAB fittype
|
|
ft = fittype(twoGauss, 'independent', {'x','y'}, 'dependent', 'z');
|
|
|
|
% Fit options
|
|
options = fitoptions(ft);
|
|
|
|
% Parameter order
|
|
paramOrder = {'A_amplitude', 'B_amplitude', 'A_centerx', 'A_centery', ...
|
|
'B_centerx', 'B_centery', 'A_sigmax', 'A_sigmay', 'B_sigmax', 'B_sigmay'};
|
|
|
|
% Build StartPoint, Lower, Upper
|
|
startPoint = zeros(1, length(paramOrder));
|
|
lowerBounds = zeros(1, length(paramOrder));
|
|
upperBounds = zeros(1, length(paramOrder));
|
|
for i = 1:length(paramOrder)
|
|
paramName = paramOrder{i};
|
|
startPoint(i) = obj.params.(paramName).value;
|
|
lowerBounds(i) = obj.params.(paramName).min;
|
|
upperBounds(i) = obj.params.(paramName).max;
|
|
end
|
|
|
|
options.StartPoint = startPoint;
|
|
options.Lower = lowerBounds;
|
|
options.Upper = upperBounds;
|
|
|
|
% Perform the fit
|
|
[obj.fitResult, obj.gof] = fit([xData, yData], zData, ft, options);
|
|
fitResult = obj.fitResult;
|
|
gof = obj.gof;
|
|
end
|
|
|
|
function paramValue = getParamValue(obj, paramName)
|
|
% Get the value of a specified parameter
|
|
if isfield(obj.params, paramName)
|
|
paramValue = obj.params.(paramName).value;
|
|
else
|
|
error('Parameter %s does not exist', paramName);
|
|
end
|
|
end
|
|
|
|
function setParamValue(obj, paramName, value)
|
|
% Set the value of a specified parameter
|
|
if isfield(obj.params, paramName)
|
|
obj.params.(paramName).value = value;
|
|
else
|
|
error('Parameter %s does not exist', paramName);
|
|
end
|
|
end
|
|
|
|
function setParamBounds(obj, paramName, minVal, maxVal)
|
|
% Set the bounds of a specified parameter
|
|
if isfield(obj.params, paramName)
|
|
obj.params.(paramName).min = minVal;
|
|
obj.params.(paramName).max = maxVal;
|
|
else
|
|
error('Parameter %s does not exist', paramName);
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|