use of uk.ac.sussex.gdsc.core.match.PointPair in project GDSC-SMLM by aherbert.
the class ResultsMatchCalculator method showResults.
@SuppressWarnings("null")
private void showResults(MemoryPeakResults results1, MemoryPeakResults results2, final List<PointPair> allMatches, int n1, int n2, final boolean doIdAnalysis1, final boolean doIdAnalysis2, TextWindow resultsWindow) {
if (!settings.showTable) {
return;
}
// Output the results
Consumer<String> output;
if (resultsWindow != null) {
output = resultsWindow::append;
} else {
// Headless mode
output = IJ::log;
if (writeHeader.get()) {
writeHeader.set(false);
IJ.log(createResultsHeader(settings.idAnalysis));
}
}
// We have the results for the largest distance.
// Now reduce the distance threshold and recalculate the results
final double[] distanceThresholds = getDistances(settings.distanceThreshold, settings.increments, settings.delta);
final double[] pairDistances = getPairDistances(allMatches);
// Re-use storage for the ID analysis
TIntHashSet id1 = null;
TIntHashSet id2 = null;
TIntHashSet matchId1 = null;
TIntHashSet matchId2 = null;
final boolean doIdAnalysis = doIdAnalysis1 || doIdAnalysis2;
if (doIdAnalysis) {
if (doIdAnalysis1) {
id1 = getIds(results1);
matchId1 = new TIntHashSet(id1.size());
}
if (doIdAnalysis2) {
id2 = getIds(results2);
matchId2 = new TIntHashSet(id2.size());
}
}
final StringBuilder sb = new StringBuilder();
for (final double distanceThreshold : distanceThresholds) {
double rms = 0;
int tp2 = 0;
final double d2 = distanceThreshold * distanceThreshold;
for (final double d : pairDistances) {
if (d <= d2) {
rms += d;
tp2++;
}
}
// All non-true positives must be added to the false totals.
final int fp2 = n2 - tp2;
final int fn2 = n1 - tp2;
// RMSD to be the root mean square deviation in a single dimension so divide by 2.
// (This assumes 2D Euclidean distances.)
final MatchResult result = new MatchResult(tp2, fp2, fn2, Math.sqrt(MathUtils.div0(rms / 2, tp2)));
MatchResult idResult1 = null;
MatchResult idResult2 = null;
if (doIdAnalysis) {
if (doIdAnalysis1) {
matchId1.clear();
}
if (doIdAnalysis2) {
matchId2.clear();
}
int index = 0;
for (final PointPair pair : allMatches) {
if (pairDistances[index++] <= d2) {
if (doIdAnalysis1) {
matchId1.add(((PeakResultPoint) pair.getPoint1()).getPeakResult().getId());
}
if (doIdAnalysis2) {
matchId2.add(((PeakResultPoint) pair.getPoint2()).getPeakResult().getId());
}
}
}
// => Only the recall will be valid: tp / (tp + fn)
if (doIdAnalysis1) {
idResult1 = new MatchResult(matchId1.size(), 0, id1.size() - matchId1.size(), 0);
}
if (doIdAnalysis2) {
idResult2 = new MatchResult(matchId2.size(), 0, id2.size() - matchId2.size(), 0);
}
}
addResult(sb, settings.inputOption1, settings.inputOption2, distanceThreshold, result, idResult1, idResult2);
output.accept(sb.toString());
}
}
use of uk.ac.sussex.gdsc.core.match.PointPair in project GDSC-SMLM by aherbert.
the class TraceMatchCalculator method sort.
private List<? extends PointPair> sort(List<PointPair> pairs) {
if (settings.sortIndex == 1) {
// Sort by time
final ArrayList<TimeComparablePointPair> newPairs = new ArrayList<>(pairs.size());
for (final PointPair pair : pairs) {
newPairs.add(new TimeComparablePointPair(pair));
}
Collections.sort(newPairs, TimeComparablePointPair::compare);
return newPairs;
}
// Already sorted by score
return pairs;
}
use of uk.ac.sussex.gdsc.core.match.PointPair in project GDSC-SMLM by aherbert.
the class ResultsMatchCalculator method runCompareCoordinates.
@SuppressWarnings("null")
private void runCompareCoordinates(MemoryPeakResults results1, MemoryPeakResults results2) {
final boolean requirePairs = settings.showPairs || settings.isSaveClassifications();
final boolean saveMatched = settings.isSaveMatched();
final boolean saveUnmatched = settings.isSaveUnmatched();
final TextFilePeakResults fileResults = createFilePeakResults(results2);
final List<PointPair> allMatches = new LinkedList<>();
final List<PointPair> pairs = (requirePairs) ? new LinkedList<>() : null;
final double maxDistance = settings.distanceThreshold + settings.increments * settings.delta;
// Divide the results into time points
final TIntObjectHashMap<List<Coordinate>> actualCoordinates = getCoordinates(results1, settings.coordinateMethod1);
final TIntObjectHashMap<List<Coordinate>> predictedCoordinates = getCoordinates(results2, settings.coordinateMethod2);
int n1 = 0;
int n2 = 0;
// Process each time point
for (final int t : getTimepoints(actualCoordinates, predictedCoordinates)) {
final Coordinate[] actual = getCoordinates(actualCoordinates, t);
final Coordinate[] predicted = getCoordinates(predictedCoordinates, t);
final List<Coordinate> tp = null;
List<Coordinate> fp = null;
List<Coordinate> fn = null;
final List<PointPair> matches = new LinkedList<>();
if (requirePairs) {
fp = new LinkedList<>();
fn = new LinkedList<>();
}
MatchCalculator.analyseResults2D(actual, predicted, maxDistance, tp, fp, fn, matches);
// Aggregate
n1 += actual.length;
n2 += predicted.length;
allMatches.addAll(matches);
if (settings.showPairs) {
pairs.addAll(matches);
for (final Coordinate c : fn) {
pairs.add(new PointPair(c, null));
}
for (final Coordinate c : fp) {
pairs.add(new PointPair(null, c));
}
}
if (fileResults != null) {
// Matches are marked in the original value with 1 for true, 0 for false
if (saveMatched) {
for (final PointPair pair : matches) {
PeakResult result = ((PeakResultPoint) pair.getPoint2()).getPeakResult();
result = result.copy();
result.setOrigValue(1);
fileResults.add(result);
}
}
if (saveUnmatched) {
for (final Coordinate c : fp) {
PeakResult result = ((PeakResultPoint) c).getPeakResult();
result = result.copy();
result.setOrigValue(0);
fileResults.add(result);
}
}
}
}
if (fileResults != null) {
fileResults.end();
}
final boolean doIdAnalysis1 = settings.idAnalysis && haveIds(results1);
final boolean doIdAnalysis2 = settings.idAnalysis && haveIds(results2);
// Create output.
// This supports headless mode with just the results table
// or graphical mode with a results table and pairs window.
final boolean headless = java.awt.GraphicsEnvironment.isHeadless();
TextWindow resultsWindow = null;
if (!headless) {
resultsWindow = (settings.showTable) ? createResultsWindow(doIdAnalysis1 || doIdAnalysis2) : null;
showPairs(results1, pairs, resultsWindow);
}
showResults(results1, results2, allMatches, n1, n2, doIdAnalysis1, doIdAnalysis2, resultsWindow);
savePairs(results1, results2, allMatches);
}
use of uk.ac.sussex.gdsc.core.match.PointPair in project GDSC-SMLM by aherbert.
the class ClassificationMatchCalculator method runCompareClassifications.
private void runCompareClassifications(MemoryPeakResults results1, MemoryPeakResults results2) {
final List<PointPair> allMatches = new LinkedList<>();
// Optionally exclude results which do not have an id and/or category
Predicate<PeakResult> test = settings.useId == ClassAnalysis.IGNORE_ZERO ? r -> r.getId() != 0 : null;
if (settings.useCategory == ClassAnalysis.IGNORE_ZERO) {
final Predicate<PeakResult> test2 = r -> r.getCategory() != 0;
test = test == null ? test2 : test.and(test2);
} else if (test == null) {
test = r -> true;
}
// Divide the results into time points
final TIntObjectHashMap<List<PeakResultPoint>> coordinates1 = getCoordinates(results1, test);
final TIntObjectHashMap<List<PeakResultPoint>> coordinates2 = getCoordinates(results2, test);
// Process each time point
int n1 = 0;
int n2 = 0;
for (final int t : getTimepoints(coordinates1, coordinates2)) {
final Coordinate[] c1 = getCoordinates(coordinates1, t);
final Coordinate[] c2 = getCoordinates(coordinates2, t);
n1 += c1.length;
n2 += c2.length;
final List<PointPair> matches = new LinkedList<>();
MatchCalculator.analyseResults3D(c1, c2, settings.matchDistance, null, null, null, matches);
allMatches.addAll(matches);
}
if (allMatches.isEmpty()) {
IJ.error(TITLE, "No localisation matches between the two results sets");
return;
}
// Get the unique Ids and Categories in the matches.
final Mapper ids = getMapper(allMatches, PeakResult::getId, settings.useId);
final Mapper cats = getMapper(allMatches, PeakResult::getCategory, settings.useCategory);
// Map id/category to an index = stride * cat + id
final int stride = ids.size();
// Any integer is allowed as an index
if ((long) stride * cats.size() > 1L << 32) {
IJ.error(TITLE, "Too many combinations of id and category to assigne unique labels");
return;
}
// Extract indices
final int[] set1 = new int[allMatches.size()];
final int[] set2 = new int[allMatches.size()];
int i = 0;
for (final PointPair r : allMatches) {
set1[i] = toIndex(stride, ids, cats, ((PeakResultPoint) r.getPoint1()).getPeakResult());
set2[i] = toIndex(stride, ids, cats, ((PeakResultPoint) r.getPoint2()).getPeakResult());
i++;
}
final Resequencer re = new Resequencer();
re.setCacheMap(true);
re.renumber(set1);
re.renumber(set2);
// Compare
final RandIndex r = new RandIndex().compute(set1, set2);
final TextWindow resultsWindow = ImageJUtils.refresh(resultsWindowRef, () -> new TextWindow(TITLE + " Results", "Results1\tResults2\tID\tCategory\tn1\tc1\tn2\tc2\tMatched\tRand Index\tAdjusted RI", "", 900, 300));
try (BufferedTextWindow bw = new BufferedTextWindow(resultsWindow)) {
final StringBuilder sb = new StringBuilder(2048);
sb.append(results1.getName()).append('\t');
sb.append(results2.getName()).append('\t');
sb.append(ANALYSIS_OPTION[settings.useId.ordinal()]).append('\t');
sb.append(ANALYSIS_OPTION[settings.useCategory.ordinal()]).append('\t');
sb.append(n1).append('\t');
sb.append(MathUtils.max(set1) + 1).append('\t');
sb.append(n2).append('\t');
sb.append(MathUtils.max(set2) + 1).append('\t');
sb.append(set1.length).append('\t');
sb.append(MathUtils.rounded(r.getRandIndex())).append('\t');
sb.append(MathUtils.rounded(r.getAdjustedRandIndex())).append('\t');
bw.append(sb.toString());
}
}
use of uk.ac.sussex.gdsc.core.match.PointPair in project GDSC-SMLM by aherbert.
the class ClassificationMatchCalculator method getMapper.
/**
* Gets the mapper that can create a value from a natural sequence starting from 0 for each unique
* key in the results. If the analysis is set to ignore then a single mapping to zero is created.
*
* @param allMatches the all matches
* @param fun the function to get the key value
* @param analysis the type of analysis
* @return the mapper
*/
private static Mapper getMapper(List<PointPair> allMatches, ToIntFunction<PeakResult> fun, ClassAnalysis analysis) {
if (analysis == ClassAnalysis.IGNORE) {
return Mapper.single();
}
// Find the unique values
final TIntHashSet set = new TIntHashSet();
for (final PointPair r : allMatches) {
set.add(fun.applyAsInt(((PeakResultPoint) r.getPoint1()).getPeakResult()));
set.add(fun.applyAsInt(((PeakResultPoint) r.getPoint2()).getPeakResult()));
}
// Edge case of 1 value
if (set.size() == 1) {
return Mapper.single();
}
// Map to a natural sequence from zero
final int[] keys = set.toArray();
Arrays.sort(keys);
// Check if a discrete sequence already
if (keys[keys.length - 1] - keys[0] == set.size() - 1) {
return Mapper.offset(set.size(), keys[0]);
}
// Map each key to a value starting from 0
final TIntIntHashMap map = new TIntIntHashMap(keys.length);
for (final int k : keys) {
map.put(k, map.size());
}
return new Mapper() {
@Override
public int size() {
return map.size();
}
@Override
public int map(int key) {
return map.get(key);
}
};
}
Aggregations