129 lines
3.8 KiB
Matlab
129 lines
3.8 KiB
Matlab
function objective = evaluateFeatureDetectionPipeline(x, img, gtMask, params, doPlot)
|
|
%% evaluateFeatureDetectionPipeline
|
|
% Author: Karthik
|
|
% Date: 2025-09-12
|
|
% Version: 1.0
|
|
%
|
|
% Description:
|
|
% Objective wrapper for Bayesian optimization.
|
|
% Runs the detection + shape extraction pipeline for a candidate
|
|
% parameter set and returns a score relative to ground truth.
|
|
%
|
|
% Inputs:
|
|
% x - struct or table from bayesopt with tunable params
|
|
% img - 2D image (double or uint)
|
|
% gtMask - logical mask of ground truth region(s)
|
|
% params - base parameter struct (fixed params)
|
|
% doPlot - optional boolean flag to display results (default=false)
|
|
%
|
|
% Output:
|
|
% objective - scalar (to be MINIMIZED by bayesopt)
|
|
%
|
|
% Notes:
|
|
% Optional notes, references.
|
|
|
|
if nargin < 5
|
|
doPlot = false;
|
|
end
|
|
|
|
% --- Copy params and overwrite tunable fields ---
|
|
p = params;
|
|
|
|
% Clip / enforce constraints
|
|
p.dogGaussianSmallSigma = min(max(x.dogGaussianSmallSigma,0.01), x.dogGaussianLargeSigma-0.01);
|
|
p.dogGaussianLargeSigma = max(x.dogGaussianLargeSigma, p.dogGaussianSmallSigma+0.01);
|
|
p.adaptiveSensitivity = min(max(x.adaptiveSensitivity,0),1);
|
|
|
|
% Use nearestOdd helper for neighborhood size
|
|
p.adaptiveNeighborhoodSize = nearestOdd(x.adaptiveNeighborhoodSize, 3);
|
|
|
|
p.minPeakFraction = min(max(x.minPeakFraction,0),1);
|
|
p.minimumPatchArea = max(round(x.minimumPatchArea),1);
|
|
p.intensityThreshFraction = min(max(x.intensityThreshFraction,0),1);
|
|
p.edgeSigma = max(x.edgeSigma,0.01);
|
|
|
|
lowT = min(max(x.edgeThresholdLow,0),1);
|
|
highT = min(max(x.edgeThresholdHigh,0),1);
|
|
if highT <= lowT, highT = min(lowT+0.05,1); end
|
|
p.edgeThresholdLow = lowT;
|
|
p.edgeThresholdHigh = highT;
|
|
|
|
% --- Run detectPatches safely ---
|
|
try
|
|
[patchProps, ~, imgCropped, xStart, yStart] = Analyzer.detectPatches(img, p);
|
|
catch ME
|
|
warning('detectPatches failed: %s', ME.message);
|
|
objective = 0;
|
|
return;
|
|
end
|
|
|
|
% --- Run extractAndClassifyShapes safely ---
|
|
try
|
|
results = Analyzer.extractAndClassifyShapes(imgCropped, patchProps, xStart, yStart, p);
|
|
catch ME
|
|
warning('extractAndClassifyShapes failed: %s', ME.message);
|
|
objective = 0;
|
|
return;
|
|
end
|
|
|
|
% --- Build predicted mask ---
|
|
predictedMask = false(size(img));
|
|
for k = 1:numel(results)
|
|
BW = results(k).BW;
|
|
if isempty(BW), continue; end
|
|
[h,w] = size(BW);
|
|
yIdx = (yStart:yStart+h-1);
|
|
xIdx = (xStart:xStart+w-1);
|
|
predictedMask(yIdx, xIdx) = predictedMask(yIdx, xIdx) | BW;
|
|
end
|
|
|
|
% --- Compute Dice score ---
|
|
if any(gtMask(:))
|
|
score = dice(predictedMask, gtMask);
|
|
else
|
|
score = 0;
|
|
end
|
|
|
|
% --- Optional plotting ---
|
|
if doPlot
|
|
p.gtMask = gtMask; % overlay
|
|
Plotter.plotDetectedPatches(img, patchProps, results, p, xStart, yStart, 'Optimization Candidate');
|
|
drawnow;
|
|
end
|
|
|
|
% bayesopt minimizes → return negative Dice
|
|
objective = -score;
|
|
end
|
|
|
|
function val = nearestOdd(x, minVal)
|
|
%NEARESTODD Round to the nearest odd integer, enforcing a minimum.
|
|
%
|
|
% val = NEARESTODD(x) rounds x to the nearest odd integer.
|
|
% val = NEARESTODD(x, minVal) enforces that the result is at least minVal.
|
|
|
|
if nargin < 2
|
|
minVal = 1;
|
|
end
|
|
|
|
% Round to nearest integer
|
|
n = round(x);
|
|
|
|
if mod(n,2) == 0
|
|
lowerOdd = n - 1;
|
|
upperOdd = n + 1;
|
|
|
|
% Choose whichever odd is closer to original x
|
|
if abs(lowerOdd - x) <= abs(upperOdd - x)
|
|
n = lowerOdd;
|
|
else
|
|
n = upperOdd;
|
|
end
|
|
end
|
|
|
|
% Enforce minimum
|
|
val = max(n, minVal);
|
|
if mod(val,2) == 0
|
|
val = val + 1; % bump to next odd
|
|
end
|
|
end
|