use of uk.ac.sussex.gdsc.core.utils.SettingsList in project GDSC-SMLM by aherbert.
the class BenchmarkSpotFilter method run.
@Override
public void run(String arg) {
SmlmUsageTracker.recordPlugin(this.getClass(), arg);
extraOptions = ImageJUtils.isExtraOptions();
batchMode = "batch".equals(arg);
simulationParameters = CreateData.getSimulationParameters();
if (simulationParameters == null) {
IJ.error(TITLE, "No benchmark spot parameters in memory");
return;
}
imp = CreateData.getImage();
if (imp == null) {
IJ.error(TITLE, "No benchmark image");
return;
}
results = CreateData.getResults();
if (results == null) {
IJ.error(TITLE, "No benchmark results in memory");
return;
}
// Set-up for the simulation
try {
if (results.getCalibration() == null) {
throw new ConfigurationException("Require calibrated results");
}
if (results.getCalibrationReader().getDistanceUnit() != DistanceUnit.PIXEL) {
throw new ConfigurationException("Require results in pixel distance units");
}
if (results.getCalibrationReader().getIntensityUnit() != IntensityUnit.PHOTON) {
throw new ConfigurationException("Require results in photon units");
}
// This plugin is heavily reliant on the results being represented as a
// Gaussian2D function.
final int flags = Gaussian2DPeakResultHelper.AMPLITUDE | Gaussian2DPeakResultHelper.PIXEL_AMPLITUDE;
calculator = Gaussian2DPeakResultHelper.create(results.getPsf(), results.getCalibration(), flags);
cameraModel = CreateData.getCameraModel(simulationParameters);
} catch (final ConfigurationException ex) {
IJ.error(TITLE, "Bad configuration: " + ex.getMessage());
return;
}
if (!showDialog()) {
return;
}
// Get the simulation results into a list per frame
simulationCoords = getSimulationCoordinates();
// Clear old results to free memory
BenchmarkSpotFilterResult localFilterResult;
filterResult.set(null);
// For graphs
windowOrganiser = new WindowOrganiser();
if (batchMode) {
// Clear the cached results if the setting changed
final SettingsList settingList = new SettingsList(simulationParameters.id, settings.filterRelativeDistances, // search, maxSearch, // Ignore search distance for smart caching
settings.border, settings.scoreRelativeDistances, settings.analysisBorder, settings.hardBorder, settings.matchingMethod, settings.upperDistance, settings.lowerDistance, settings.upperSignalFactor, settings.lowerSignalFactor, settings.recallFraction);
final ArrayList<BatchResult[]> cachedResults = getCachedBatchResults(settingList);
// Batch mode to test enumeration of filters
final double sd = simulationParameters.sd / simulationParameters.pixelPitch;
final int limit = (int) Math.floor(3 * sd);
// This should be in integers otherwise we may repeat search box sizes
final int[] searchParam = SimpleArrayUtils.newArray(settings.maxSearch - settings.minSearch + 1, settings.minSearch, 1);
// Continuous parameters
final double[] pEmpty = new double[0];
final double[] mParam = (settings.batchMean) ? getRange(limit, 0.05) : pEmpty;
final double[] gParam = (settings.batchGaussian) ? getRange(limit, 0.05) : pEmpty;
// Less continuous parameters
final double[] cParam = (settings.batchCircular) ? getRange(limit, 0.5) : pEmpty;
// Discrete parameters
final double[] medParam = (settings.batchMedian) ? getRange(limit, 1) : pEmpty;
setupProgress((long) imp.getImageStackSize() * searchParam.length * (mParam.length + gParam.length + cParam.length + medParam.length), "Frame");
ArrayList<BatchResult[]> batchResults = new ArrayList<>(cachedResults.size());
double param2 = 0;
if (settings.differenceFilter && settings.differenceSmooth > 0) {
if (settings.filterRelativeDistances) {
// Convert to absolute for batch run
param2 = MathUtils.roundUsingDecimalPlaces(settings.differenceSmooth * config.getHwhmMin(), 3);
} else {
// Already an absolute value
param2 = settings.differenceSmooth;
}
config.setDataFilterType(DataFilterType.DIFFERENCE);
} else {
config.setDataFilterType(DataFilterType.SINGLE);
}
for (final int search : searchParam) {
// Batch runs use absolute distance
config.setSearch(search, true);
// Allow re-use of these if they are cached to allow quick reanalysis of results.
if (settings.batchMean) {
batchResults.add(getOrCompute(cachedResults, DataFilterMethod.MEAN, mParam, search, param2));
}
if (settings.batchGaussian) {
batchResults.add(getOrCompute(cachedResults, DataFilterMethod.GAUSSIAN, gParam, search, param2));
}
if (settings.batchCircular) {
batchResults.add(getOrCompute(cachedResults, DataFilterMethod.CIRCULAR_MEAN, cParam, search, param2));
}
if (settings.batchMean) {
batchResults.add(getOrCompute(cachedResults, DataFilterMethod.MEDIAN, medParam, search, param2));
}
}
IJ.showProgress(-1);
IJ.showStatus("");
if (ImageJUtils.isInterrupted()) {
return;
}
// Save the results in a cache
setCachedBatchResults(settingList, cachedResults);
// Analysis options
final ExtendedGenericDialog gd = new ExtendedGenericDialog(TITLE);
final boolean haveCached = cachedResults.size() > batchResults.size();
if (haveCached) {
gd.addCheckbox("Use_cached_results", settings.useCached);
}
gd.addMessage("Choose performance plots:");
for (int i = 0; i < settings.batchPlot.length; i++) {
gd.addCheckbox(Settings.batchPlotNames[i], settings.batchPlot[i]);
}
gd.addChoice("Selection", Settings.SELECTION_METHOD, settings.selectionMethod);
gd.addCheckbox("Show_plots", settings.showPlot);
gd.addCheckbox("Plot_rank_by_intensity", settings.rankByIntensity);
gd.addCheckbox("Show_failures_plots", settings.showFailuresPlot);
gd.addCheckbox("Show_TP", settings.showTP);
gd.addCheckbox("Show_FP", settings.showFP);
gd.addCheckbox("Show_FN", settings.showFN);
gd.addHelp(HelpUrls.getUrl("filter-spot-data-batch"));
gd.showDialog();
if (gd.wasCanceled()) {
return;
}
if (haveCached) {
settings.useCached = gd.getNextBoolean();
if (settings.useCached) {
batchResults = cachedResults;
}
}
for (int i = 0; i < settings.batchPlot.length; i++) {
settings.batchPlot[i] = gd.getNextBoolean();
}
settings.selectionMethod = gd.getNextChoiceIndex();
settings.showPlot = gd.getNextBoolean();
settings.rankByIntensity = gd.getNextBoolean();
settings.showFailuresPlot = gd.getNextBoolean();
settings.showTP = gd.getNextBoolean();
settings.showFP = gd.getNextBoolean();
settings.showFN = gd.getNextBoolean();
// Plot charts
for (int i = 0; i < settings.batchPlot.length; i++) {
plot(i, batchResults);
}
// Store in global singleton
localFilterResult = analyse(batchResults);
} else {
// Single filter mode
setupProgress(imp.getImageStackSize(), "Frame");
localFilterResult = runAnalysis(config);
}
ImageJUtils.clearSlowProgress();
IJ.showStatus("");
if (localFilterResult == null) {
return;
}
// Store the latest result
filterResult.set(localFilterResult);
// Debugging the matches
if (settings.debug) {
addSpotsToMemory(localFilterResult.filterResults);
}
if (settings.showFailuresPlot) {
showFailuresPlot(localFilterResult);
}
if (settings.showPlot) {
showPlot(localFilterResult);
}
if (isShowOverlay()) {
showOverlay(imp, localFilterResult);
}
windowOrganiser.tile();
}
use of uk.ac.sussex.gdsc.core.utils.SettingsList in project GDSC-SMLM by aherbert.
the class BenchmarkFilterAnalysis method analyseParameters.
/**
* Run the optimum filter on a set of labelled peak results using various parameter settings
* outputting performance statistics on the success of the filter to an ImageJ table.
*
* @param iterative the iterative
* @param optimum the optimum
* @param rangeReduction the range reduction
* @return the best filter
*/
private ComplexFilterScore analyseParameters(boolean iterative, ComplexFilterScore optimum, double rangeReduction) {
// Non-zero modes are used for the iterative optimisation which require new results
boolean newResults = iterative;
if (!iterative) {
// Interactive run, this may be the first run during iterative optimisation
createResultsWindow();
// Only repeat analysis if necessary
double min = settings.minResidualsThreshold;
double max = settings.maxResidualsThreshold;
if (computeDoublets) {
min = max = 0;
}
final SettingsList settingsList = new SettingsList(optimum, fitResultData.resultsList, settings.failCount, settings.minFailCount, settings.maxFailCount, residualsThreshold, min, max, settings.duplicateDistance, settings.duplicateDistanceAbsolute, settings.minDuplicateDistance, settings.maxDuplicateDistance, settings.summaryDepth, settings.criteriaIndex, settings.criteriaLimit, settings.scoreIndex, settings.searchParam);
if (settings.repeatSearch || !settingsList.equals(filterAnalysisResult.lastAnalyseParametersSettings)) {
newResults = true;
filterAnalysisResult.lastAnalyseParametersSettings = settingsList;
}
}
if (newResults) {
optimum = runParameterAnalysis(iterative, optimum, rangeReduction);
if (optimum == null || ImageJUtils.isInterrupted()) {
return null;
}
}
return reportResults(newResults, optimum);
}
use of uk.ac.sussex.gdsc.core.utils.SettingsList in project GDSC-SMLM by aherbert.
the class BenchmarkFilterAnalysis method readResults.
private MultiPathFitResults[] readResults() {
// Extract all the results in memory into a list per frame. This can be cached
boolean update = false;
Pair<Integer, TIntObjectHashMap<UniqueIdPeakResult[]>> coords = coordinateCache.get();
if (coords.getKey() != simulationParameters.id) {
coords = Pair.of(simulationParameters.id, getCoordinates(results));
coordinateCache.set(coords);
update = true;
}
actualCoordinates = coords.getValue();
spotFitResults = BenchmarkSpotFit.getBenchmarkSpotFitResults();
FitResultData localFitResultData = fitResultDataCache.get();
final SettingsList scoreSettings = new SettingsList(settings.partialMatchDistance, settings.upperMatchDistance, settings.partialSignalFactor, settings.upperSignalFactor);
final boolean equalScoreSettings = scoreSettings.equals(localFitResultData.scoreSettings);
if (update || localFitResultData.fittingId != spotFitResults.id || !equalScoreSettings || localFitResultData.differentSettings(settings)) {
IJ.showStatus("Reading results ...");
if (localFitResultData.fittingId < 0) {
// Copy the settings from the fitter if this is the first run.
// This just starts the plugin with sensible settings.
// Q. Should this be per new simulation or fitting result instead?
final FitEngineConfiguration config = BenchmarkSpotFit.getFitEngineConfiguration();
settings.failCount = config.getFailuresLimit();
settings.duplicateDistance = config.getDuplicateDistance();
settings.duplicateDistanceAbsolute = config.getDuplicateDistanceAbsolute();
settings.residualsThreshold = (BenchmarkSpotFit.getComputeDoublets()) ? BenchmarkSpotFit.getMultiFilter().residualsThreshold : 1;
}
// This functionality is for choosing the optimum filter for the given scoring metric.
if (!equalScoreSettings) {
filterAnalysisResult.scores.clear();
}
localFitResultData = new FitResultData(spotFitResults.id, scoreSettings, settings);
// @formatter:off
// -=-=-=-
// The scoring is designed to find the best fitter+filter combination for the given spot
// candidates. The ideal combination would correctly fit+pick all the candidate positions
// that are close to a localisation.
//
// Use the following scoring scheme for all candidates:
//
// Candidates
// +----------------------------------------+
// | Actual matches |
// | +-----------+ TN |
// | | FN | |
// | | +---------- |
// | | | TP | | Fitted |
// | +-----------+ | spots |
// | | FP | |
// | +---------+ |
// +----------------------------------------+
//
// Candidates = All the spot candidates
// Actual matches = Any spot candidate or fitted spot candidate that matches a localisation
// Fitted spots = Any spot candidate that was successfully fitted
//
// TP = A spot candidate that was fitted and matches a localisation and is accepted
// FP = A spot candidate that was fitted but does not match a localisation and is accepted
// FN = A spot candidate that failed to be fitted but matches a localisation
// = A spot candidate that was fitted and matches a localisation and is rejected
// TN = A spot candidate that failed to be fitted and does not match a localisation
// = A spot candidate that was fitted and does not match a localisation and is rejected
//
// When fitting only produces one result it is possible to compute the TN score.
// Since unfitted candidates can only be TN or FN we could accumulate these scores and cache
// them. This was the old method of benchmarking single spot fitting and allowed more scores
// to be computed.
//
// When fitting produces multiple results then we have to score each fit result against all
// possible actual results and keep a record of the scores. These can then be assessed when
// the specific results have been chosen by result filtering.
//
// Using a distance ramped scoring function the degree of match can be varied from 0 to 1.
// Using a signal-factor ramped scoring function the degree of fitted can be varied from 0
// to 1. When using ramped scoring functions the fractional allocation of scores using the
// above scheme is performed, i.e. candidates are treated as if they both match and unmatch.
// This results in an equivalent to multiple analysis using different thresholds and averaging
// of the scores.
//
// The totals TP+FP+TN+FN must equal the number of spot candidates. This allows different
// fitting methods to be compared since the total number of candidates is the same.
//
// Precision = TP / (TP+FP) : This is always valid as a minimum criteria score
// Recall = TP / (TP+FN) : This is valid between different fitting methods since a
// method that fits more spots will have a potentially lower FN
// Jaccard = TP / (TP+FN+FP) : This is valid between fitting methods
//
// -=-=-=-
// As an alternative scoring system, different fitting methods can be compared using the same
// TP value but calculating FN = localisations - TP and FP as Positives - TP. This creates a
// score against the original number of simulated molecules using everything that was passed
// through the filter (Positives). This score is comparable when a different spot candidate
// filter has been used and the total number of candidates is different, e.g. Mean filtering
// vs. Gaussian filtering
// -=-=-=-
// @formatter:on
final RampedScore distanceScore = RampedScore.of(spotFitResults.distanceInPixels * settings.upperMatchDistance / 100.0, spotFitResults.distanceInPixels * settings.partialMatchDistance / 100.0, false);
localFitResultData.lowerDistanceInPixels = distanceScore.edge1;
localFitResultData.distanceInPixels = distanceScore.edge0;
final double matchDistance = MathUtils.pow2(localFitResultData.distanceInPixels);
localFitResultData.resultsPrefix3 = "\t" + MathUtils.rounded(distanceScore.edge1 * simulationParameters.pixelPitch) + "\t" + MathUtils.rounded(distanceScore.edge0 * simulationParameters.pixelPitch);
localFitResultData.limitRange = ", d=" + MathUtils.rounded(distanceScore.edge1 * simulationParameters.pixelPitch) + "-" + MathUtils.rounded(distanceScore.edge0 * simulationParameters.pixelPitch);
// Signal factor must be greater than 1
final RampedScore signalScore;
final double spotSignalFactor = BenchmarkSpotFit.getSignalFactor();
if (spotSignalFactor > 0 && settings.upperSignalFactor > 0) {
signalScore = RampedScore.of(spotSignalFactor * settings.upperSignalFactor / 100.0, spotSignalFactor * settings.partialSignalFactor / 100.0, false);
localFitResultData.lowerSignalFactor = signalScore.edge1;
localFitResultData.signalFactor = signalScore.edge0;
localFitResultData.resultsPrefix3 += "\t" + MathUtils.rounded(signalScore.edge1) + "\t" + MathUtils.rounded(signalScore.edge0);
localFitResultData.limitRange += ", s=" + MathUtils.rounded(signalScore.edge1) + "-" + MathUtils.rounded(signalScore.edge0);
} else {
signalScore = null;
localFitResultData.resultsPrefix3 += "\t0\t0";
localFitResultData.lowerSignalFactor = localFitResultData.signalFactor = 0;
}
// Store all the results
final ArrayList<MultiPathFitResults> multiPathFitResults = new ArrayList<>(spotFitResults.fitResults.size());
final List<MultiPathFitResults> syncResults = Collections.synchronizedList(multiPathFitResults);
// This could be multi-threaded ...
final int nThreads = getThreads(spotFitResults.fitResults.size());
final BlockingQueue<Job> jobs = new ArrayBlockingQueue<>(nThreads * 2);
final List<FitResultsWorker> workers = new LinkedList<>();
final List<Thread> threads = new LinkedList<>();
final AtomicInteger uniqueId = new AtomicInteger();
final CoordinateStore localCoordinateStore = createCoordinateStore();
final Ticker ticker = ImageJUtils.createTicker(spotFitResults.fitResults.size(), nThreads, null);
for (int i = 0; i < nThreads; i++) {
final FitResultsWorker worker = new FitResultsWorker(jobs, syncResults, matchDistance, distanceScore, signalScore, uniqueId, localCoordinateStore.newInstance(), ticker, actualCoordinates);
final Thread t = new Thread(worker);
workers.add(worker);
threads.add(t);
t.start();
}
spotFitResults.fitResults.forEachEntry((frame, candidates) -> {
put(jobs, new Job(frame, candidates));
return true;
});
// Finish all the worker threads by passing in a null job
for (int i = 0; i < threads.size(); i++) {
put(jobs, new Job(0, null));
}
// Wait for all to finish
for (int i = 0; i < threads.size(); i++) {
try {
threads.get(i).join();
final FitResultsWorker worker = workers.get(i);
localFitResultData.matches += worker.matches;
localFitResultData.fittedResults += worker.included;
localFitResultData.totalResults += worker.total;
localFitResultData.notDuplicateCount += worker.notDuplicateCount;
localFitResultData.newResultCount += worker.newResultCount;
localFitResultData.countActual += worker.includedActual;
if (i == 0) {
localFitResultData.depthStats = worker.depthStats;
localFitResultData.depthFitStats = worker.depthFitStats;
localFitResultData.signalFactorStats = worker.signalFactorStats;
localFitResultData.distanceStats = worker.distanceStats;
} else {
localFitResultData.depthStats.add(worker.depthStats);
localFitResultData.depthFitStats.add(worker.depthFitStats);
localFitResultData.signalFactorStats.add(worker.signalFactorStats);
localFitResultData.distanceStats.add(worker.distanceStats);
}
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new ConcurrentRuntimeException("Unexpected interrupt", ex);
}
}
threads.clear();
ImageJUtils.finished();
localFitResultData.maxUniqueId = uniqueId.get();
localFitResultData.resultsList = multiPathFitResults.toArray(new MultiPathFitResults[0]);
Arrays.sort(localFitResultData.resultsList, (o1, o2) -> Integer.compare(o1.getFrame(), o2.getFrame()));
MultiPathFilter.resetValidationFlag(localFitResultData.resultsList);
fitResultDataCache.set(localFitResultData);
}
fitResultData = localFitResultData;
return localFitResultData.resultsList;
}
use of uk.ac.sussex.gdsc.core.utils.SettingsList in project GDSC-SMLM by aherbert.
the class BenchmarkFilterAnalysis method analyse.
/**
* Run different filtering methods on a set of labelled peak results outputting performance
* statistics on the success of the filter to an ImageJ table.
*
* <p>For each filter set a plot is shown of the score verses the filter value, thus filters
* should be provided in ascending numerical order otherwise they are sorted.
*
* @param filterSets the filter sets
* @param iterative the iterative
* @param optimum the optimum
* @param rangeReduction the range reduction
* @return the best filter
*/
private ComplexFilterScore analyse(List<FilterSet> filterSets, boolean iterative, ComplexFilterScore optimum, double rangeReduction) {
// Non-zero modes are used for the iterative optimisation which require new results
boolean newResults = iterative;
if (optimum != null) {
// Non-interactive re-run when iterating
filterAnalysisResult.scores.clear();
runAnalysis(filterSets, optimum, rangeReduction);
if (ImageJUtils.isInterrupted()) {
return null;
}
} else {
// Interactive run, this may be the first run during iterative optimisation
if (settings.reset) {
filterAnalysisResult.scores.clear();
}
createResultsWindow();
// Only repeat analysis if necessary
double evolveSetting = settings.evolve;
if (settings.evolve == 1) {
// The delta effects the step size for the Genetic Algorithm
evolveSetting *= settings.delta;
}
final SettingsList settingsList = new SettingsList(filterSets, fitResultData.resultsList, settings.failCount, residualsThreshold, settings.duplicateDistance, settings.duplicateDistanceAbsolute, settings.plotTopN, settings.summaryDepth, settings.criteriaIndex, settings.criteriaLimit, settings.scoreIndex, evolveSetting);
final boolean equalSettings = settingsList.equals(filterAnalysisResult.lastAnalyseSettings);
if (!equalSettings || (settings.evolve != 0 && settings.repeatEvolve)) {
newResults = true;
filterAnalysisResult.lastAnalyseSettings = settingsList;
runAnalysis(filterSets);
if (ImageJUtils.isInterrupted()) {
return null;
}
}
}
return reportResults(newResults);
}
Aggregations