use of org.apache.commons.lang3.concurrent.ConcurrentRuntimeException in project GDSC-SMLM by aherbert.
the class BenchmarkSpotFilter method getSimulationCoordinates.
/**
* Gets the coordinates for the current simulation. This extract all the results in memory into a
* list per frame and is cached for the simulation Id.
*
* @return the coordinates
*/
private TIntObjectHashMap<PsfSpot[]> getSimulationCoordinates() {
Pair<Integer, TIntObjectHashMap<PsfSpot[]>> coords = coordinateCache.get();
if (coords.getKey() != simulationParameters.id) {
// Always use float coordinates.
// The Worker adds a pixel offset for the spot coordinates.
final TIntObjectHashMap<List<Coordinate>> coordinates = ResultsMatchCalculator.getCoordinates(results, false);
// Spot PSFs may overlap so we must determine the amount of signal overlap and amplitude
// effect for each spot...
final int nThreads = Prefs.getThreads();
final BlockingQueue<Integer> jobs = new ArrayBlockingQueue<>(nThreads * 2);
final List<OverlapWorker> workers = new LinkedList<>();
final List<Thread> threads = new LinkedList<>();
final Ticker overlapTicker = ImageJUtils.createTicker(coordinates.size(), nThreads, "Computing PSF overlap ...");
for (int i = 0; i < nThreads; i++) {
final OverlapWorker worker = new OverlapWorker(jobs, coordinates, overlapTicker);
final Thread t = new Thread(worker);
workers.add(worker);
threads.add(t);
t.start();
}
// Process the frames
coordinates.forEachKey(value -> {
put(jobs, value);
return true;
});
// Finish all the worker threads by passing in a null job
for (int i = 0; i < threads.size(); i++) {
put(jobs, -1);
}
// Wait for all to finish
final TIntObjectHashMap<PsfSpot[]> actualCoordinates = new TIntObjectHashMap<>();
for (int i = 0; i < threads.size(); i++) {
try {
threads.get(i).join();
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new ConcurrentRuntimeException("Unexpected interrupt", ex);
}
actualCoordinates.putAll(workers.get(i).coordinates);
}
threads.clear();
// For testing
final SimpleRegression regression = new SimpleRegression(false);
for (final PsfSpot[] spots : actualCoordinates.valueCollection()) {
for (final PsfSpot spot : spots) {
regression.addData(spot.getAmplitude(), calculator.getAmplitude(spot.getPeakResult().getParameters()));
}
}
ImageJUtils.log("PixelAmplitude vs Amplitude = %f, slope=%f, n=%d", regression.getR(), regression.getSlope(), regression.getN());
ImageJUtils.finished();
coords = Pair.of(simulationParameters.id, actualCoordinates);
coordinateCache.set(coords);
}
return coords.getRight();
}
use of org.apache.commons.lang3.concurrent.ConcurrentRuntimeException in project GDSC-SMLM by aherbert.
the class DoubletAnalysis method runFitting.
private void runFitting() {
referenceResults.set(null);
final ImageStack stack = imp.getImageStack();
// Get the coordinates per frame
final TIntObjectHashMap<List<Coordinate>> actualCoordinates = ResultsMatchCalculator.getCoordinates(results, false);
final long[] sumCount = new long[1];
actualCoordinates.forEachValue(list -> {
sumCount[0] += list.size();
return true;
});
final double density = 1e6 * sumCount[0] / (simulationParameters.pixelPitch * simulationParameters.pixelPitch * results.getBounds().getWidth() * results.getBounds().getHeight() * actualCoordinates.size());
// Create a pool of workers
final int nThreads = Prefs.getThreads();
final BlockingQueue<Integer> jobs = new ArrayBlockingQueue<>(nThreads * 2);
final Ticker ticker = ImageJUtils.createTicker(actualCoordinates.size(), nThreads, "Computing results ...");
final List<Worker> workers = new LinkedList<>();
final List<Thread> threads = new LinkedList<>();
final Overlay overlay = (settings.showOverlay) ? new Overlay() : null;
for (int i = 0; i < nThreads; i++) {
final Worker worker = new Worker(jobs, stack, actualCoordinates, config, overlay, ticker);
final Thread t = new Thread(worker);
workers.add(worker);
threads.add(t);
t.start();
}
// Fit the frames
final long startTime = System.nanoTime();
actualCoordinates.forEachKey(frame -> {
put(jobs, frame);
return true;
});
// Finish all the worker threads by passing in a null job
for (int i = 0; i < threads.size(); i++) {
put(jobs, -1);
}
// Wait for all to finish
for (int i = 0; i < threads.size(); i++) {
try {
threads.get(i).join();
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new ConcurrentRuntimeException(ex);
}
}
threads.clear();
ImageJUtils.finished("Collecting results ...");
final long runTime = System.nanoTime() - startTime;
// Collect the results
int cic = 0;
int daic = 0;
int dbic = 0;
ArrayList<DoubletResult> results = null;
int maxH = 0;
int maxH2 = 0;
int maxH3 = 0;
for (final Worker worker : workers) {
if (results == null) {
results = worker.results;
} else {
results.addAll(worker.results);
}
cic += worker.cic;
daic += worker.daic;
dbic += worker.dbic;
maxH = MathUtils.max(maxH, worker.spotHistogram.length);
for (int k = 0; k < 3; k++) {
maxH2 = MathUtils.max(maxH2, worker.neighbourHistogram[k].length);
maxH3 = MathUtils.max(maxH3, worker.almostNeighbourHistogram[k].length);
}
}
if (cic > 0) {
ImageJUtils.log("Difference AIC %d, BIC %d, Total %d", daic, dbic, cic);
}
if (settings.showHistograms) {
final double[] spotHistogram = new double[maxH];
final double[] resultHistogram = new double[maxH];
final double[][] neighbourHistogram = new double[3][maxH2];
final double[][] almostNeighbourHistogram = new double[3][maxH3];
for (final Worker worker : workers) {
final int[] h1a = worker.spotHistogram;
final int[] h1b = worker.resultHistogram;
for (int j = 0; j < h1a.length; j++) {
spotHistogram[j] += h1a[j];
resultHistogram[j] += h1b[j];
}
final int[][] h2 = worker.neighbourHistogram;
final int[][] h3 = worker.almostNeighbourHistogram;
for (int k = 0; k < 3; k++) {
for (int j = 0; j < h2[k].length; j++) {
neighbourHistogram[k][j] += h2[k][j];
}
for (int j = 0; j < h3[k].length; j++) {
almostNeighbourHistogram[k][j] += h3[k][j];
}
}
}
showHistogram(0, spotHistogram);
showHistogram(1, resultHistogram);
showHistogram(2, neighbourHistogram[0]);
showHistogram(3, neighbourHistogram[1]);
showHistogram(4, neighbourHistogram[2]);
showHistogram(5, almostNeighbourHistogram[0]);
showHistogram(6, almostNeighbourHistogram[1]);
showHistogram(7, almostNeighbourHistogram[2]);
}
workers.clear();
if (overlay != null) {
imp.setOverlay(overlay);
}
MemoryUtils.runGarbageCollector();
Collections.sort(results, DoubletResult::compare);
summariseResults(results, density, runTime);
windowOrganiser.tile();
IJ.showStatus("");
}
use of org.apache.commons.lang3.concurrent.ConcurrentRuntimeException in project GDSC-SMLM by aherbert.
the class FitWorker method run.
@Override
public void run() {
try {
while (!finished) {
final FitJob fitjob = jobs.take();
if (fitjob == null || fitjob.data == null || finished) {
break;
}
run(fitjob);
}
} catch (final InterruptedException ex) {
if (!finished) {
Logger.getLogger(FitWorker.class.getName()).log(Level.WARNING, () -> "Interrupted: " + ex.toString());
Thread.currentThread().interrupt();
throw new ConcurrentRuntimeException(ex);
}
} finally {
finished = true;
}
}
use of org.apache.commons.lang3.concurrent.ConcurrentRuntimeException in project GDSC-SMLM by aherbert.
the class BenchmarkFilterAnalysis method scoreFilters.
/**
* Score filters.
*
* @param points the points (must be sorted by duplicate distance)
* @param createTextResult set to true to create the text result
* @return the score results
*/
@Nullable
private ParameterScoreResult[] scoreFilters(double[][] points, boolean createTextResult) {
if (points == null || points.length == 0) {
return null;
}
gaResultsListToScore = gaResultsList;
gaSubset = false;
ParameterScoreResult[] scoreResults = new ParameterScoreResult[points.length];
if (scoreResults.length == 1) {
// No need to multi-thread this
final int failCount = (int) Math.round(points[0][0]);
final double residualsThreshold = points[0][1];
final double duplicateDistance = points[0][2];
scoreResults[0] = scoreFilter(searchScoreFilter, defaultMinimalFilter, failCount, residualsThreshold, duplicateDistance, createCoordinateStore(duplicateDistance), createTextResult);
} else {
// Multi-thread score all the result
final int nThreads = getThreads(scoreResults.length);
final BlockingQueue<ParameterScoreJob> jobs = new ArrayBlockingQueue<>(nThreads * 2);
final List<Thread> threads = new LinkedList<>();
final Ticker ticker = ImageJUtils.createTicker(scoreResults.length, nThreads, "Scoring Filters");
for (int i = 0; i < nThreads; i++) {
final ParameterScoreWorker worker = new ParameterScoreWorker(jobs, scoreResults, createTextResult, ticker);
final Thread t = new Thread(worker);
threads.add(t);
t.start();
}
ticker.start();
for (int i = 0; i < points.length; i++) {
if (IJ.escapePressed()) {
break;
}
put(jobs, new ParameterScoreJob(points[i], i));
}
// Finish all the worker threads by passing in a null job
for (int i = 0; i < threads.size(); i++) {
put(jobs, new ParameterScoreJob(null, -1));
}
// Wait for all to finish
for (int i = 0; i < threads.size(); i++) {
try {
threads.get(i).join();
} catch (final InterruptedException ex) {
Logger.getLogger(BenchmarkFilterAnalysis.class.getName()).log(Level.WARNING, "Interrupted!", ex);
Thread.currentThread().interrupt();
throw new ConcurrentRuntimeException("Unexpected interruption", ex);
}
}
threads.clear();
ImageJUtils.finished();
// In case the threads were interrupted
if (ImageJUtils.isInterrupted()) {
scoreResults = null;
}
}
finishScoring();
return scoreResults;
}
use of org.apache.commons.lang3.concurrent.ConcurrentRuntimeException 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;
}
Aggregations