use of uk.ac.sussex.gdsc.smlm.engine.FitEngineConfiguration in project GDSC-SMLM by aherbert.
the class FailCountManager method createData.
/**
* Creates the fail count data by running fitting on the current image.
*/
private void createData() {
final ImagePlus imp = WindowManager.getCurrentImage();
if (imp == null) {
IJ.error(TITLE, "No image for fitting");
return;
}
if (!showCreateDataDialog(imp)) {
return;
}
// Get the current fit configuration
final Configuration c = new Configuration();
if (!c.showDialog(false)) {
return;
}
final FitEngineConfiguration fitConfig = c.getFitEngineConfiguration();
// Update stopping criteria.
fitConfig.resetFailCounter();
fitConfig.setFailuresLimit(settings.getFailCountLimit());
final ImageSource source = new IJImageSource(imp);
final PeakFit peakFit = new PeakFit(fitConfig, ResultsSettings.getDefaultInstance());
peakFit.setResultsSuffix("(FailCountAnalysis)");
if (!peakFit.initialise(source, null, false)) {
IJ.error(TITLE, "Failed to initialise the fit engine");
return;
}
final FitEngine engine = peakFit.createFitEngine();
final Rectangle bounds = new Rectangle(source.getWidth(), source.getHeight());
// Run
final int totalFrames = Math.min(source.getFrames(), settings.getMaxFrames());
final int step = ImageJUtils.getProgressInterval(totalFrames);
IJ.showProgress(0);
boolean shutdown = false;
int slice = 0;
final LocalList<ParameterisedFitJob> jobs = new LocalList<>(totalFrames);
while (!shutdown && slice < totalFrames) {
final float[] data = source.next();
if (data == null) {
break;
}
if (slice++ % step == 0) {
final int frames = slice;
if (ImageJUtils.showStatus(() -> "Fitting slice: " + frames + " / " + totalFrames)) {
IJ.showProgress(slice, totalFrames);
}
}
final ParameterisedFitJob job = createJob(source.getStartFrameNumber(), data, bounds);
jobs.push(job);
engine.run(job);
shutdown = escapePressed();
}
ImageJUtils.showStatus("Extracting fail count data");
engine.end(shutdown);
IJ.showProgress(1);
source.close();
// Extract the fail count data
final LocalList<FailCountData> failCountData = new LocalList<>(jobs.size());
for (int i = 0; i < jobs.size(); i++) {
final ParameterisedFitJob job = jobs.unsafeGet(i);
if (job.getStatus() == Status.FINISHED) {
final FitParameters fitParams = job.getFitParameters();
// Find the last success
boolean[] results = fitParams.pass;
int end = results.length - 1;
while (end > 0 && !results[end]) {
end--;
}
// Add on the configured fail count limit
end = Math.min(end + 1 + settings.getFailCountLimit(), results.length);
results = Arrays.copyOf(results, end);
failCountData.add(new FailCountData(job.getSlice(), results));
}
}
failCountDataRef.set(failCountData);
ImageJUtils.showStatus("");
// Save for the future
if (settings.getSaveAfterFitting()) {
saveData();
}
}
use of uk.ac.sussex.gdsc.smlm.engine.FitEngineConfiguration in project GDSC-SMLM by aherbert.
the class BenchmarkFilterAnalysis method showDialog.
private boolean showDialog(int optimiseParameters) {
final ExtendedGenericDialog gd = new ExtendedGenericDialog(TITLE);
final boolean showOptimiseFilter = (optimiseParameters & FLAG_OPTIMISE_FILTER) != 0;
final boolean showOptimiseParams = (optimiseParameters & FLAG_OPTIMISE_PARAMS) != 0;
final String helpKey = showOptimiseParams ? "benchmark-filter-parameters" : "benchmark-filter-analysis";
addSimulationData(gd);
// TODO - Make minimal filter configurable?
gd.addSlider("Fail_count", 0, 20, settings.failCount);
if (showOptimiseParams) {
gd.addNumericField("Min_fail_count", settings.minFailCount, 0);
gd.addNumericField("Max_fail_count", settings.maxFailCount, 0);
}
if (computeDoublets) {
gd.addSlider("Residuals_threshold", 0.01, 1, settings.residualsThreshold);
if (showOptimiseParams) {
gd.addNumericField("Min_residuals_threshold", settings.minResidualsThreshold, 2);
gd.addNumericField("Max_residuals_threshold", settings.maxResidualsThreshold, 2);
}
}
final FitEngineConfiguration tmp = new FitEngineConfiguration();
tmp.setDuplicateDistance(settings.duplicateDistance);
tmp.setDuplicateDistanceAbsolute(settings.duplicateDistanceAbsolute);
PeakFit.addDuplicateDistanceOptions(gd, new PeakFit.SimpleFitEngineConfigurationProvider(tmp));
if (showOptimiseParams) {
gd.addNumericField("Min_duplicate_distance", settings.minDuplicateDistance, 2);
gd.addNumericField("Max_duplicate_distance", settings.maxDuplicateDistance, 2);
}
gd.addCheckbox("Reset", settings.reset);
gd.addCheckbox("Show_table", settings.showResultsTable);
gd.addCheckbox("Show_summary", settings.showSummaryTable);
gd.addCheckbox("Clear_tables", settings.clearTables);
gd.addSlider("Summary_top_n", 0, 20, settings.summaryTopN);
gd.addNumericField("Summary_depth (nm)", settings.summaryDepth, 0);
gd.addSlider("Plot_top_n", 0, 20, settings.plotTopN);
gd.addCheckbox("Save_best_filter", settings.saveBestFilter);
gd.addCheckbox("Save_template", settings.saveTemplate);
gd.addCheckbox("Calculate_sensitivity", settings.calculateSensitivity);
gd.addSlider("Delta", 0.01, 1, settings.delta);
gd.addMessage("Match scoring");
gd.addChoice("Criteria", Settings.COLUMNS, settings.criteriaIndex);
gd.addNumericField("Criteria_limit", settings.criteriaLimit, 4);
gd.addChoice("Score", Settings.COLUMNS, settings.scoreIndex);
ImageJUtils.addMessage(gd, "Fitting match distance = %s nm; signal factor = %s", MathUtils.rounded(spotFitResults.distanceInPixels * simulationParameters.pixelPitch), MathUtils.rounded(fitSignalFactor));
gd.addSlider("Upper_match_distance (%)", 0, 100, settings.upperMatchDistance);
gd.addSlider("Partial_match_distance (%)", 0, 100, settings.partialMatchDistance);
gd.addSlider("Upper_signal_factor (%)", 0, 100, settings.upperSignalFactor);
gd.addSlider("Partial_signal_factor (%)", 0, 100, settings.partialSignalFactor);
if (!simulationParameters.fixedDepth) {
gd.addCheckbox("Depth_recall_analysis", settings.depthRecallAnalysis);
}
gd.addCheckbox("Score_analysis", settings.scoreAnalysis);
gd.addChoice("Component_analysis", Settings.COMPONENT_ANALYSIS_OPTIONS, settings.componentAnalysis);
if (showOptimiseFilter) {
gd.addChoice("Evolve", Settings.EVOLVE_OPTIONS, settings.evolve);
gd.addCheckbox("Repeat_evolve", settings.repeatEvolve);
}
if (showOptimiseParams) {
gd.addChoice("Search", Settings.SEARCH_OPTIONS, settings.searchParam);
gd.addCheckbox("Repeat_search", settings.repeatSearch);
}
gd.addStringField("Title", settings.resultsTitle, 20);
final String[] labels = { "Show_TP", "Show_FP", "Show_FN" };
gd.addCheckboxGroup(1, 3, labels, new boolean[] { settings.showTP, settings.showFP, settings.showFN });
gd.addHelp(HelpUrls.getUrl(helpKey));
gd.showDialog();
if (gd.wasCanceled() || !readDialog(gd, optimiseParameters, tmp)) {
return false;
}
if (!selectTableColumns()) {
return false;
}
// We may have to read the results again if the ranking option has changed.
// Also we must read the results with the maximum duplicate distance we may encounter.
final double dd = settings.duplicateDistance;
if (showOptimiseParams) {
settings.duplicateDistance = settings.maxDuplicateDistance;
}
readResults();
settings.duplicateDistance = dd;
return true;
}
use of uk.ac.sussex.gdsc.smlm.engine.FitEngineConfiguration 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.smlm.engine.FitEngineConfiguration in project GDSC-SMLM by aherbert.
the class BenchmarkFilterAnalysis method createResults.
/**
* Create peak results.
*
* @param filterResults The results from running the filter (or null)
* @param filter the filter
*/
private MemoryPeakResults createResults(PreprocessedPeakResult[] filterResults, DirectFilter filter, boolean withBorder) {
if (filterResults == null) {
final MultiPathFilter multiPathFilter = createMpf(filter, defaultMinimalFilter);
filterResults = filterResults(multiPathFilter);
}
final MemoryPeakResults newResults = new MemoryPeakResults();
newResults.copySettings(this.results);
newResults.setName(TITLE);
if (withBorder) {
// To produce the same results as the PeakFit plugin we must implement the border
// functionality used in the FitWorker. This respects the border of the spot filter.
final FitEngineConfiguration config = new FitEngineConfiguration();
updateAllConfiguration(config);
final MaximaSpotFilter spotFilter = config.createSpotFilter();
final int border = spotFilter.getBorder();
final Rectangle bounds = getBounds();
final int borderLimitX = bounds.x + bounds.width - border;
final int borderLimitY = bounds.y + bounds.height - border;
for (final PreprocessedPeakResult spot : filterResults) {
if (spot.getX() > border && spot.getX() < borderLimitX && spot.getY() > border && spot.getY() < borderLimitY) {
final double[] p = spot.toGaussian2DParameters();
final float[] params = new float[p.length];
for (int j = 0; j < p.length; j++) {
params[j] = (float) p[j];
}
final int frame = spot.getFrame();
final int origX = (int) p[Gaussian2DFunction.X_POSITION];
final int origY = (int) p[Gaussian2DFunction.Y_POSITION];
newResults.add(frame, origX, origY, 0, 0, spot.getNoise(), spot.getMeanSignal(), params, null);
}
}
} else {
for (final PreprocessedPeakResult spot : filterResults) {
final double[] p = spot.toGaussian2DParameters();
final float[] params = new float[p.length];
for (int j = 0; j < p.length; j++) {
params[j] = (float) p[j];
}
final int frame = spot.getFrame();
final int origX = (int) p[Gaussian2DFunction.X_POSITION];
final int origY = (int) p[Gaussian2DFunction.Y_POSITION];
newResults.add(frame, origX, origY, 0, 0, spot.getNoise(), spot.getMeanSignal(), params, null);
}
}
return newResults;
}
use of uk.ac.sussex.gdsc.smlm.engine.FitEngineConfiguration in project GDSC-SMLM by aherbert.
the class AstigmatismModelManager method loadConfiguration.
private boolean loadConfiguration() {
// We have a different fit configuration just for the PSF Creator.
// This allows it to be saved and not effect PeakFit settings.
config = new FitEngineConfiguration(pluginSettings.getFitEngineSettings(), pluginSettings.getCalibration(), pluginSettings.getPsf());
if (!showConfigurationDialog()) {
IJ.error(TITLE, "No fit configuration loaded");
return false;
}
writeAstigmatismModelManagerSettings(pluginSettings);
if (fitConfig.getPsfType() != PSFType.TWO_AXIS_GAUSSIAN_2D) {
IJ.error(TITLE, "PSF must be " + PsfProtosHelper.getName(PSFType.TWO_AXIS_GAUSSIAN_2D));
return false;
}
// Simple data filter. This is just used to get the initial estimate of amplitude.
config.setDataFilterType(DataFilterType.SINGLE);
config.setDataFilter(DataFilterMethod.GAUSSIAN, 1, 0);
config.setIncludeNeighbours(false);
config.configureOutputUnits();
config.setResidualsThreshold(1);
config.setDuplicateDistance(0);
pluginSettings.setFitEngineSettings(config.getFitEngineSettings());
pluginSettings.setCalibration(fitConfig.getCalibration());
pluginSettings.setPsf(fitConfig.getPsf());
writeAstigmatismModelManagerSettings(pluginSettings);
return true;
}
Aggregations