package gdsc.smlm.results;
import java.awt.Rectangle;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.junit.internal.ArrayComparisonFailure;
import gdsc.core.utils.NotImplementedException;
import gdsc.core.utils.Random;
import gdsc.smlm.function.gaussian.Gaussian2DFunction;
import gdsc.smlm.ij.results.ResultsFileFormat;
public class PeakResultsReaderTest
{
private gdsc.core.utils.Random rand = new Random();
// TODO - Add tests to compare writing to a IJTablePeakResults, saving the TextPanel contents to file and then reading.
// -=-=-=-=-
@Test
public void writeTextMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, false, false, false, false, false);
}
@Test
public void writeSequentialTextMatchesRead()
{
writeMatchesRead(true, ResultsFileFormat.GDSC_TEXT, false, false, false, false, false);
}
@Test
public void writeTextWithDeviationsMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, true, false, false, false, false);
}
@Test
public void writeTextWithEndFrameMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, false, true, false, false, false);
}
@Test
public void writeTextWithIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, false, false, true, false, false);
}
@Test
public void writeTextWithDeviationsAndEndFrameMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, true, true, false, false, false);
}
@Test
public void writeTextWithDeviationsAndIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, true, false, true, false, false);
}
@Test
public void writeTextWithDeviationsAndEndFrameAndIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, true, true, true, false, false);
}
// -=-=-=-=-
@Test
public void writeBinaryMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, false, false, false, false, true);
}
@Test
public void writeSequentialBinaryMatchesRead()
{
writeMatchesRead(true, ResultsFileFormat.GDSC_BINARY, false, false, false, false, true);
}
@Test
public void writeBinaryWithDeviationsMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, true, false, false, false, true);
}
@Test
public void writeBinaryWithEndFrameMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, false, true, false, false, true);
}
@Test
public void writeBinaryWithIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, false, false, true, false, true);
}
@Test
public void writeBinaryWithDeviationsAndEndFrameMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, true, true, false, false, true);
}
@Test
public void writeBinaryWithDeviationsAndIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, true, false, true, false, true);
}
@Test
public void writeBinaryWithDeviationsAndEndFrameAndIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, true, true, true, false, true);
}
// -=-=-=-=-
@Test
public void writeTextWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, false, false, false, true, false);
}
@Test
public void writeSequentialTextWithSortMatchesRead()
{
writeMatchesRead(true, ResultsFileFormat.GDSC_TEXT, false, false, false, true, false);
}
@Test
public void writeTextWithDeviationsWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, true, false, false, true, false);
}
@Test
public void writeTextWithEndFrameWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, false, true, false, true, false);
}
@Test
public void writeTextWithIdWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, false, false, true, true, false);
}
@Test
public void writeTextWithDeviationsAndEndFrameWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, true, true, false, true, false);
}
@Test
public void writeTextWithDeviationsAndIdWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, true, false, true, true, false);
}
@Test
public void writeTextWithDeviationsAndEndFrameAndIdWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_TEXT, true, true, true, true, false);
}
// -=-=-=-=-
@Test
public void writeBinaryWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, false, false, false, true, true);
}
@Test
public void writeSequentialBinaryWithSortMatchesRead()
{
writeMatchesRead(true, ResultsFileFormat.GDSC_BINARY, false, false, false, true, true);
}
@Test
public void writeBinaryWithDeviationsWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, true, false, false, true, true);
}
@Test
public void writeBinaryWithEndFrameWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, false, true, false, true, true);
}
@Test
public void writeBinaryWithIdWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, false, false, true, true, true);
}
@Test
public void writeBinaryWithDeviationsAndEndFrameWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, true, true, false, true, true);
}
@Test
public void writeBinaryWithDeviationsAndIdWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, true, false, true, true, true);
}
@Test
public void writeBinaryWithDeviationsAndEndFrameAndIdWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.GDSC_BINARY, true, true, true, true, true);
}
// -=-=-=-=-
// Note: For MALK we cannot do all the tests as the format only contains X,Y,T,I
@Test
public void writeMALKMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.MALK, false, false, false, false, false);
}
@Test
public void writeSequentialMALKMatchesRead()
{
writeMatchesRead(true, ResultsFileFormat.MALK, false, false, false, false, false);
}
@Test
public void writeMALKWithSortMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.MALK, false, false, false, true, false);
}
@Test
public void writeSequentialMALKWithSortMatchesRead()
{
writeMatchesRead(true, ResultsFileFormat.MALK, false, false, false, true, false);
}
// -=-=-=-=-
// Note: For TSF we cannot specify as binary because the widths are converted into a
// different format and then back again.
@Test
public void writeTSFMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.TSF, false, false, false, false, false);
}
@Test
public void writeSequentialTSFMatchesRead()
{
writeMatchesRead(true, ResultsFileFormat.TSF, false, false, false, false, false);
}
@Test
public void writeTSFWithDeviationsMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.TSF, true, false, false, false, false);
}
@Test
public void writeTSFWithEndFrameMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.TSF, false, true, false, false, false);
}
@Test
public void writeTSFWithIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.TSF, false, false, true, false, false);
}
@Test
public void writeTSFWithDeviationsAndEndFrameMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.TSF, true, true, false, false, false);
}
@Test
public void writeTSFWithDeviationsAndIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.TSF, true, false, true, false, false);
}
@Test
public void writeTSFWithDeviationsAndEndFrameAndIdMatchesRead()
{
writeMatchesRead(false, ResultsFileFormat.TSF, true, true, true, false, false);
}
// -=-=-=-=-
@Test
public void readWithScannerMatchesNonScanner()
{
readWithScannerMatchesNonScanner(false, false, false, false, false);
}
@Test
public void readWithScannerMatchesNonScannerWithDeviations()
{
readWithScannerMatchesNonScanner(true, false, false, false, false);
}
@Test
public void readWithScannerMatchesNonScannerWithEndFrame()
{
readWithScannerMatchesNonScanner(false, true, false, false, false);
}
@Test
public void readWithScannerMatchesNonScannerWithId()
{
readWithScannerMatchesNonScanner(false, false, true, false, false);
}
@Test
public void readWithScannerMatchesNonScannerWithDeviationsWithEndFrame()
{
readWithScannerMatchesNonScanner(true, true, false, false, false);
}
@Test
public void readWithScannerMatchesNonScannerWithDeviationsWithId()
{
readWithScannerMatchesNonScanner(true, false, true, false, false);
}
@Test
public void readWithScannerMatchesNonScannerWithDeviationsWithEndFrameWithId()
{
readWithScannerMatchesNonScanner(true, true, true, false, false);
}
// -=-=-=-=-
@Test
public void readWithScannerMatchesNonScannerWithSort()
{
readWithScannerMatchesNonScanner(false, false, false, true, false);
}
@Test
public void readWithScannerMatchesNonScannerWithDeviationsWithSort()
{
readWithScannerMatchesNonScanner(true, false, false, true, false);
}
@Test
public void readWithScannerMatchesNonScannerWithEndFrameWithSort()
{
readWithScannerMatchesNonScanner(false, true, false, true, false);
}
@Test
public void readWithScannerMatchesNonScannerWithIdWithSort()
{
readWithScannerMatchesNonScanner(false, false, true, true, false);
}
@Test
public void readWithScannerMatchesNonScannerWithDeviationsWithEndFrameWithSort()
{
readWithScannerMatchesNonScanner(true, true, false, true, false);
}
@Test
public void readWithScannerMatchesNonScannerWithDeviationsWithIdWithSort()
{
readWithScannerMatchesNonScanner(true, false, true, true, false);
}
@Test
public void readWithScannerMatchesNonScannerWithDeviationsWithEndFrameWithIdWithSort()
{
readWithScannerMatchesNonScanner(true, true, true, true, false);
}
// -=-=-=-=-
@Test
public void readTextWithNonScannerIsFasterThanScanner()
{
readWith2IsFasterThan1(false, false, false, ResultsFileFormat.GDSC_TEXT, true, ResultsFileFormat.GDSC_TEXT,
false, 1);
}
@Test
public void readTextWithNonScannerIsFasterThanScannerWithDeviationsWithEndFrameWithId()
{
readWith2IsFasterThan1(true, true, true, ResultsFileFormat.GDSC_TEXT, true, ResultsFileFormat.GDSC_TEXT, false,
1);
}
@Test
public void readWithMALKIsFasterThanText()
{
readWith2IsFasterThan1(false, false, false, ResultsFileFormat.GDSC_TEXT, false, ResultsFileFormat.MALK, false,
2);
}
@Test
public void readWithBinaryIsFasterThanText()
{
readWith2IsFasterThan1(false, false, false, ResultsFileFormat.GDSC_TEXT, false, ResultsFileFormat.GDSC_BINARY,
false, 2);
}
@Test
public void readWithBinaryIsFasterThanTSF()
{
readWith2IsFasterThan1(false, false, false, ResultsFileFormat.TSF, false, ResultsFileFormat.GDSC_BINARY, false,
20);
}
private void readWith2IsFasterThan1(boolean showDeviations, boolean showEndFrame, boolean showId,
ResultsFileFormat f1, boolean useScanner1, ResultsFileFormat f2, boolean useScanner2, int loops)
{
MemoryPeakResults out = createResults(20000, showDeviations, showEndFrame, showId);
String filename = createFile();
writeFile(false, f1, showDeviations, showEndFrame, showId, false, out, filename);
long time1 = getReadTime(filename, useScanner1, loops);
writeFile(false, f2, showDeviations, showEndFrame, showId, false, out, filename);
long time2 = getReadTime(filename, useScanner2, loops);
if (useScanner1 != useScanner2)
System.out.printf("%s (scan=%b) is %.2fx faster than %s (scan=%b)\n", f2, useScanner2,
(double) time1 / time2, f1, useScanner1);
else
System.out.printf("%s is %.2fx faster than %s\n", f2, (double) time1 / time2, f1);
Assert.assertTrue(String.format(f1 + " is slower (%d > %d) than " + f2, time2, time1), time2 < time1);
}
// -=-=-=-=-
private void writeMatchesRead(boolean sequential, ResultsFileFormat fileFormat, boolean showDeviations,
boolean showEndFrame, boolean showId, boolean sort, boolean binary)
{
MemoryPeakResults out = createResults(200, showDeviations, showEndFrame, showId);
String filename = createFile();
writeFile(sequential, fileFormat, showDeviations, showEndFrame, showId, sort, out, filename);
MemoryPeakResults in = readFile(filename, false);
checkEqual(fileFormat, showDeviations, showEndFrame, showId, sort, binary, out, in);
}
private void readWithScannerMatchesNonScanner(boolean showDeviations, boolean showEndFrame, boolean showId,
boolean sort, boolean binary)
{
MemoryPeakResults out = createResults(1000, showDeviations, showEndFrame, showId);
String filename = createFile();
ResultsFileFormat fileFormat = ResultsFileFormat.GDSC_TEXT;
writeFile(false, fileFormat, showDeviations, showEndFrame, showId, sort, out, filename);
MemoryPeakResults in = readFile(filename, false);
MemoryPeakResults in2 = readFile(filename, true);
checkEqual(fileFormat, showDeviations, showEndFrame, showId, sort, binary, in, in2);
}
private void checkEqual(ResultsFileFormat fileFormat, boolean showDeviations, boolean showEndFrame, boolean showId,
boolean sort, boolean binary, MemoryPeakResults expectedResults, MemoryPeakResults actualResults)
throws ArrayComparisonFailure
{
Assert.assertNotNull("Input results are null", actualResults);
Assert.assertEquals("Size differ", expectedResults.size(), actualResults.size());
final float delta = (binary) ? 0 : 1e-6f;
List<PeakResult> expected = expectedResults.getResults();
List<PeakResult> actual = actualResults.getResults();
if (sort)
{
// Results should be sorted by time
Collections.sort(expected, new Comparator<PeakResult>()
{
public int compare(PeakResult o1, PeakResult o2)
{
return o1.getFrame() - o2.getFrame();
}
});
}
// TSF requires the bias be subtracted
// double bias = expectedResults.getCalibration().getBias();
for (int i = 0; i < actualResults.size(); i++)
{
PeakResult p1 = expected.get(i);
PeakResult p2 = actual.get(i);
Assert.assertEquals("Peak mismatch @ " + i, p1.getFrame(), p2.getFrame());
if (fileFormat == ResultsFileFormat.MALK)
{
Assert.assertEquals("X @ " + i, p1.getXPosition(), p2.getXPosition(), delta);
Assert.assertEquals("Y @ " + i, p1.getYPosition(), p2.getYPosition(), delta);
Assert.assertEquals("Signal @ " + i, p1.getSignal(), p2.getSignal(), delta);
continue;
}
Assert.assertEquals("Orig X mismatch @ " + i, p1.origX, p2.origX);
Assert.assertEquals("Orig Y mismatch @ " + i, p1.origY, p2.origY);
Assert.assertEquals("Orig value mismatch @ " + i, p1.origValue, p2.origValue, delta);
Assert.assertEquals("Error mismatch @ " + i, p1.error, p2.error, 1e-6);
Assert.assertEquals("Noise mismatch @ " + i, p1.noise, p2.noise, delta);
Assert.assertNotNull("Params is null @ " + i, p2.params);
Assert.assertArrayEquals("Params mismatch @ " + i, p1.params, p2.params, delta);
if (showDeviations)
{
Assert.assertNotNull(p2.paramsStdDev);
Assert.assertArrayEquals("Params StdDev mismatch @ " + i, p1.paramsStdDev, p2.paramsStdDev, delta);
}
if (showEndFrame)
{
Assert.assertEquals("End frame mismatch @ " + i, p1.getEndFrame(), p2.getEndFrame());
}
if (showId)
{
Assert.assertEquals("ID mismatch @ " + i, p1.getId(), p2.getId());
}
}
// Check the header information
Assert.assertEquals("Name", expectedResults.getName(), actualResults.getName());
Assert.assertEquals("Configuration", expectedResults.getConfiguration(), actualResults.getConfiguration());
Rectangle r1 = expectedResults.getBounds();
Rectangle r2 = actualResults.getBounds();
if (r1 != null)
{
Assert.assertNotNull("Bounds", r2);
Assert.assertEquals("Bounds x", r1.x, r2.x);
Assert.assertEquals("Bounds y", r1.y, r2.y);
Assert.assertEquals("Bounds width", r1.width, r2.width);
Assert.assertEquals("Bounds height", r1.height, r2.height);
}
else
{
Assert.assertNull("Bounds", r2);
}
Calibration c1 = expectedResults.getCalibration();
Calibration c2 = actualResults.getCalibration();
if (c1 != null)
{
Assert.assertNotNull("Calibration", c2);
Assert.assertEquals("Calibration nmPerPixel", c1.getNmPerPixel(), c2.getNmPerPixel(), 1e-6);
Assert.assertEquals("Calibration gain", c1.getGain(), c2.getGain(), 1e-6);
Assert.assertEquals("Calibration exposureTime", c1.getExposureTime(), c2.getExposureTime(), 1e-6);
Assert.assertEquals("Calibration readNoise", c1.getReadNoise(), c2.getReadNoise(), 1e-6);
Assert.assertEquals("Calibration bias", c1.getBias(), c2.getBias(), 1e-6);
Assert.assertEquals("Calibration emCCD", c1.isEmCCD(), c2.isEmCCD());
Assert.assertEquals("Calibration amplification", c1.getAmplification(), c2.getAmplification(), 1e-6);
}
else
{
Assert.assertNull("Calibration", c2);
}
}
private MemoryPeakResults createResults(int i, boolean showDeviations, boolean showEndFrame, boolean showId)
{
double bias = rand.next();
MemoryPeakResults results = new MemoryPeakResults();
while (i-- > 0)
{
int startFrame = (int) (i * rand.next());
int origX = (int) (i * rand.next());
int origY = (int) (i * rand.next());
float origValue = rand.next();
double error = rand.next();
float noise = rand.next();
float[] params = createData();
params[Gaussian2DFunction.BACKGROUND] += bias;
float[] paramsStdDev = (showDeviations) ? createData() : null;
if (showEndFrame || showId)
results.add(new ExtendedPeakResult(startFrame, origX, origY, origValue, error, noise, params,
paramsStdDev, startFrame + (int) (10 * rand.next()), i + 1));
else
results.addf(startFrame, origX, origY, origValue, error, noise, params, paramsStdDev);
}
results.setName(Float.toString(rand.next()) + Float.toString(rand.next()));
results.setConfiguration(Float.toString(rand.next()) + Float.toString(rand.next()));
results.setBounds(new Rectangle((int) (10 * rand.next()), (int) (10 * rand.next()), (int) (100 * rand.next()),
(int) (100 * rand.next())));
Calibration cal = new Calibration();
cal.setNmPerPixel(rand.next());
cal.setGain(rand.next());
cal.setExposureTime(rand.next());
cal.setReadNoise(rand.next());
cal.setBias(bias);
cal.setEmCCD(rand.next() < 0.5f);
cal.setAmplification(rand.next());
results.setCalibration(cal);
return results;
}
private float[] createData()
{
float[] data = new float[7];
for (int i = 0; i < data.length; i++)
data[i] = rand.next();
return data;
}
private String createFile()
{
File file;
try
{
file = File.createTempFile("test", null);
file.deleteOnExit();
String filename = file.getPath();
return filename;
}
catch (IOException e)
{
Assert.fail("Cannot create temp files for IO testing");
}
return null; // Allow compilation but the assert will stop the code
}
private void writeFile(boolean sequential, ResultsFileFormat fileFormat, boolean showDeviations,
boolean showEndFrame, boolean showId, boolean sort, MemoryPeakResults results, String filename)
{
PeakResults out;
switch (fileFormat)
{
case GDSC_BINARY:
out = new BinaryFilePeakResults(filename, showDeviations, showEndFrame, showId);
break;
case GDSC_TEXT:
out = new FilePeakResults(filename, showDeviations, showEndFrame, showId);
break;
case TSF:
out = new TSFPeakResultsWriter(filename);
break;
case MALK:
out = new MALKFilePeakResults(filename);
break;
default:
throw new NotImplementedException("Unsupported file format: " + fileFormat);
}
out.copySettings(results);
if (sort && out instanceof FilePeakResults)
{
((FilePeakResults) out).setSortAfterEnd(sort);
}
out.begin();
// TODO - option to test adding using:
// add(peak, origX, origY, origValue, chiSquared, noise, params, paramsStdDev);
if (sequential)
{
for (PeakResult peak : results)
{
out.add(peak.getFrame(), peak.origX, peak.origY, peak.origValue, peak.error, peak.noise, peak.params,
peak.paramsStdDev);
}
}
else
{
out.addAll(results.getResults());
}
out.end();
}
private MemoryPeakResults readFile(String filename, boolean useScanner)
{
PeakResultsReader reader = new PeakResultsReader(filename);
reader.setUseScanner(useScanner);
MemoryPeakResults in = reader.getResults();
return in;
}
private long getReadTime(String filename, final boolean useScanner, final int loops)
{
// Initialise reading code
readFile(filename, useScanner);
long start = System.nanoTime();
for (int i = loops; i-- > 0;)
readFile(filename, useScanner);
long time = System.nanoTime() - start;
return time;
}
}