package gdsc.smlm.results;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.math3.util.FastMath;
import org.junit.Assert;
import org.junit.Test;
import gdsc.core.utils.Random;
public class ImageSourceTest
{
@Test
public void canConstructMemoryImageSource()
{
int w = 5;
int h = 3;
int n = 15;
MemoryImageSource source = new MemoryImageSource(w, h, createData(w, h, n));
Assert.assertEquals(w, source.getWidth());
Assert.assertEquals(h, source.getHeight());
Assert.assertEquals(n, source.getFrames());
}
@Test(expected = RuntimeException.class)
public void nullDataThrowsConstructMemoryImageSource()
{
int w = 5;
int h = 3;
float[][] data = null;
new MemoryImageSource(w, h, data);
}
@Test(expected = RuntimeException.class)
public void nullArrayDataThrowsConstructMemoryImageSource()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
data[2] = null;
new MemoryImageSource(w, h, data);
}
@Test(expected = RuntimeException.class)
public void invalidLengthArrayDataThrowsConstructMemoryImageSource()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
data[2] = new float[w * h + 1];
new MemoryImageSource(w, h, data);
}
@Test
public void invalidWidthThrowsConstructMemoryImageSource()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
int ok = 0;
new MemoryImageSource(w, h, data);
if (canConstruct(w + 1, h, data))
ok++;
if (canConstruct(w - 1, h, data))
ok++;
if (canConstruct(-1, h, data))
ok++;
if (canConstruct(0, h, data))
ok++;
Assert.assertEquals(0, ok);
}
@Test
public void invalidHeightThrowsConstructMemoryImageSource()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
int ok = 0;
new MemoryImageSource(w, h, data);
if (canConstruct(w, h + 1, data))
ok++;
if (canConstruct(w, h - 1, data))
ok++;
if (canConstruct(w, -1, data))
ok++;
if (canConstruct(w, 0, data))
ok++;
Assert.assertEquals(0, ok);
}
private boolean canConstruct(int w, int h, float[][] data)
{
try
{
new MemoryImageSource(w, h, data);
return true;
}
catch (RuntimeException e)
{
return false;
}
}
@Test
public void memoryImageSourceCanReturnDataWithNext()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
MemoryImageSource source = new MemoryImageSource(w, h, data);
int i = 0;
float[] d = null;
Assert.assertTrue(source.open());
while ((d = source.next()) != null)
{
Assert.assertArrayEquals(data[i++], d, 0);
}
Assert.assertEquals(i, data.length);
}
@Test
public void memoryImageSourceCanReturnDataWithGet()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
MemoryImageSource source = new MemoryImageSource(w, h, data);
int[] frames = new int[data.length];
for (int i = 0; i < data.length; i++)
frames[i] = i + 1;
Random rand = new Random();
rand.shuffle(frames);
Assert.assertTrue(source.open());
for (int i = 0; i < data.length; i++)
{
int frame = frames[i];
Assert.assertTrue(source.isValid(frame));
Assert.assertArrayEquals(data[frame - 1], source.get(frame), 0);
}
Assert.assertFalse(source.isValid(0));
Assert.assertFalse(source.isValid(data.length + 1));
}
@Test
public void memoryImageSourceCanReturnCroppedDataWithNext()
{
int w = 5;
int h = 3;
Rectangle bounds = new Rectangle(2, 1, 3, 1);
float[][] data = createData(w, h, 15);
MemoryImageSource source = new MemoryImageSource(w, h, data);
int i = 0;
float[] d = null;
Assert.assertTrue(source.open());
while ((d = source.next(bounds)) != null)
{
Assert.assertEquals(bounds.width * bounds.height, d.length);
Assert.assertArrayEquals(crop(data[i], w, bounds), d, 0);
i++;
}
Assert.assertEquals(i, data.length);
}
@Test
public void memoryImageSourceCanReturnCroppedDataWithGet()
{
int w = 5;
int h = 3;
Rectangle bounds = new Rectangle(2, 1, 3, 1);
float[][] data = createData(w, h, 15);
MemoryImageSource source = new MemoryImageSource(w, h, data);
int[] frames = new int[data.length];
for (int i = 0; i < data.length; i++)
frames[i] = i + 1;
Random rand = new Random();
rand.shuffle(frames);
Assert.assertTrue(source.open());
for (int i = 0; i < data.length; i++)
{
int frame = frames[i];
Assert.assertTrue(source.isValid(frame));
float[] d = source.get(frame, bounds);
Assert.assertEquals(bounds.width * bounds.height, d.length);
Assert.assertArrayEquals(crop(data[frame - 1], w, bounds), d, 0);
}
Assert.assertFalse(source.isValid(0));
Assert.assertFalse(source.isValid(data.length + 1));
}
@Test(expected = RuntimeException.class)
public void memoryImageSourceThrowsWithInvalidBounds()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
data[2] = new float[w * h + 1];
MemoryImageSource source = new MemoryImageSource(w, h, data);
source.next(new Rectangle(-1, 0, 1, 1));
}
@Test
public void canConstructInterlacedImageSource()
{
int w = 5;
int h = 3;
int n = 15;
int start = 4;
int size = 2;
int skip = 1;
MemoryImageSource source = new MemoryImageSource(w, h, createData(w, h, n));
InterlacedImageSource iSource = new InterlacedImageSource(source, start, size, skip);
Assert.assertEquals(w, iSource.getWidth());
Assert.assertEquals(h, iSource.getHeight());
Assert.assertEquals(12 * 2 / 3, iSource.getFrames());
Assert.assertEquals(start, iSource.getStart());
Assert.assertEquals(size, iSource.getSize());
Assert.assertEquals(skip, iSource.getSkip());
}
@Test(expected = RuntimeException.class)
public void nullImageSourceThrowsConstructInterlacedImageSource()
{
int start = 4;
int size = 2;
int skip = 1;
new InterlacedImageSource(null, start, size, skip);
}
@Test(expected = RuntimeException.class)
public void invalidStartThrowsConstructInterlacedImageSource()
{
int w = 5;
int h = 3;
int n = 15;
int start = 0;
int size = 2;
int skip = 1;
new InterlacedImageSource(new MemoryImageSource(w, h, createData(w, h, n)), start, size, skip);
}
@Test(expected = RuntimeException.class)
public void invalidSizeThrowsConstructInterlacedImageSource()
{
int w = 5;
int h = 3;
int n = 15;
int start = 4;
int size = 0;
int skip = 1;
new InterlacedImageSource(new MemoryImageSource(w, h, createData(w, h, n)), start, size, skip);
}
@Test(expected = RuntimeException.class)
public void invalidSkipThrowsConstructInterlacedImageSource()
{
int w = 5;
int h = 3;
int n = 15;
int start = 4;
int size = 2;
int skip = -1;
new InterlacedImageSource(new MemoryImageSource(w, h, createData(w, h, n)), start, size, skip);
}
@Test
public void interlacedImageSourceCanReturnDataWithNext()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
int start = 4;
int size = 2;
int skip = 1;
ImageSource source = new InterlacedImageSource(new MemoryImageSource(w, h, data), start, size, skip);
Assert.assertTrue(source.open());
int[] expected = new int[] { 4, 5, 7, 8, 10, 11, 13, 14 };
int i = 0;
float[] d = null;
while ((d = source.next()) != null)
{
int startFrame = source.getStartFrameNumber();
int endFrame = source.getEndFrameNumber();
//System.out.printf("Read %d - %d\n", startFrame, endFrame);
Assert.assertEquals("Start and end frames do not match", startFrame, endFrame);
Assert.assertEquals(expected[i], startFrame);
Assert.assertArrayEquals(data[startFrame - 1], d, 0);
i++;
}
Assert.assertEquals(i, source.getFrames());
}
@Test
public void interlacedImageSourceCanReturnDataWithGet()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
int start = 4;
int size = 2;
int skip = 1;
ImageSource source = new InterlacedImageSource(new MemoryImageSource(w, h, data), start, size, skip);
int[] frames = new int[data.length];
for (int i = 0; i < data.length; i++)
frames[i] = i + 1;
Random rand = new Random();
rand.shuffle(frames);
int[] expected = new int[] { 4, 5, 7, 8, 10, 11, 13, 14 };
Assert.assertTrue(source.open());
for (int i = 0; i < data.length; i++)
{
int frame = frames[i];
Assert.assertTrue(source.isValid(frame));
float[] d = source.get(frame);
if (isExpected(frame, expected))
Assert.assertArrayEquals(data[frame - 1], d, 0);
else
Assert.assertNull(d);
}
Assert.assertFalse(source.isValid(0));
Assert.assertFalse(source.isValid(data.length + 1));
}
@Test
public void interlacedImageSourceCanReturnCroppedDataWithNext()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
int start = 4;
int size = 2;
int skip = 1;
Rectangle bounds = new Rectangle(2, 1, 3, 1);
ImageSource source = new InterlacedImageSource(new MemoryImageSource(w, h, data), start, size, skip);
Assert.assertTrue(source.open());
int[] expected = new int[] { 4, 5, 7, 8, 10, 11, 13, 14 };
int i = 0;
float[] d = null;
while ((d = source.next(bounds)) != null)
{
int startFrame = source.getStartFrameNumber();
int endFrame = source.getEndFrameNumber();
Assert.assertEquals("Start and end frames do not match", startFrame, endFrame);
Assert.assertEquals(expected[i], startFrame);
Assert.assertEquals(bounds.width * bounds.height, d.length);
Assert.assertArrayEquals(crop(data[startFrame - 1], w, bounds), d, 0);
i++;
}
Assert.assertEquals(i, source.getFrames());
}
@Test
public void interlacedImageSourceCanReturnCroppedDataWithGet()
{
int w = 5;
int h = 3;
float[][] data = createData(w, h, 15);
int start = 4;
int size = 2;
int skip = 1;
Rectangle bounds = new Rectangle(2, 1, 3, 1);
ImageSource source = new InterlacedImageSource(new MemoryImageSource(w, h, data), start, size, skip);
int[] frames = new int[data.length];
for (int i = 0; i < data.length; i++)
frames[i] = i + 1;
Random rand = new Random();
rand.shuffle(frames);
int[] expected = new int[] { 4, 5, 7, 8, 10, 11, 13, 14 };
Assert.assertTrue(source.open());
for (int i = 0; i < data.length; i++)
{
int frame = frames[i];
Assert.assertTrue(source.isValid(frame));
float[] d = source.get(frame, bounds);
if (isExpected(frame, expected))
{
Assert.assertEquals(bounds.width * bounds.height, d.length);
Assert.assertArrayEquals(crop(data[frame - 1], w, bounds), d, 0);
}
else
Assert.assertNull(d);
}
Assert.assertFalse(source.isValid(0));
Assert.assertFalse(source.isValid(data.length + 1));
}
@Test
public void canConstructAggregatedImageSource()
{
int w = 5;
int h = 3;
int n = 15;
int aggregate = 3;
MemoryImageSource source = new MemoryImageSource(w, h, createData(w, h, n));
AggregatedImageSource iSource = new AggregatedImageSource(source, aggregate);
Assert.assertEquals(w, iSource.getWidth());
Assert.assertEquals(h, iSource.getHeight());
Assert.assertEquals(n / aggregate, iSource.getFrames());
Assert.assertEquals(aggregate, iSource.getAggregate());
}
@Test(expected = RuntimeException.class)
public void nullImageSourceThrowsConstructAggregatedImageSource()
{
int aggregate = 3;
new AggregatedImageSource(null, aggregate);
}
@Test(expected = RuntimeException.class)
public void invalidAggregateThrowsConstructAggregatedImageSource()
{
int w = 5;
int h = 3;
int n = 15;
int aggregate = 1;
new AggregatedImageSource(new MemoryImageSource(w, h, createData(w, h, n)), aggregate);
}
@Test
public void aggregatedImageSourceCanReturnDataWithNext()
{
int w = 5;
int h = 3;
int aggregate = 3;
float[][] data = createData(w, h, 15);
ImageSource source = new AggregatedImageSource(new MemoryImageSource(w, h, data), aggregate);
Assert.assertTrue(source.open());
int i = 1;
int ii = 0;
float[] d = null;
while ((d = source.next()) != null)
{
ii++;
Assert.assertEquals(i, source.getStartFrameNumber());
Assert.assertEquals(i + 2, source.getEndFrameNumber());
float[] all = combine(data[i - 1], data[i], data[i + 1]);
Assert.assertArrayEquals(all, d, 0);
i += 3;
}
Assert.assertEquals(ii, source.getFrames());
}
@Test
public void aggregatedImageSourceCanReturnDataWithGet()
{
int w = 5;
int h = 3;
int aggregate = 3;
float[][] data = createData(w, h, 15);
ImageSource source = new AggregatedImageSource(new MemoryImageSource(w, h, data), aggregate);
int[] frames = new int[data.length / 3];
for (int i = 0, frame = 1; i < frames.length; i++, frame += 3)
frames[i] = frame;
Random rand = new Random();
rand.shuffle(frames);
Assert.assertTrue(source.open());
for (int i = 0; i < frames.length; i++)
{
int frame = frames[i];
Assert.assertTrue("Invalid frame " + frame, source.isValid(frame));
float[] d = source.get(frame);
Assert.assertEquals(frame, source.getStartFrameNumber());
Assert.assertEquals(frame + 2, source.getEndFrameNumber());
float[] all = combine(data[frame - 1], data[frame], data[frame + 1]);
Assert.assertArrayEquals("Invalid frame data " + frame, all, d, 0);
}
Assert.assertFalse(source.isValid(0));
Assert.assertFalse(source.isValid(data.length + 1));
}
@Test
public void aggregatedImageSourceCanReturnCroppedDataWithNext()
{
int w = 5;
int h = 3;
int aggregate = 3;
float[][] data = createData(w, h, 15);
Rectangle bounds = new Rectangle(2, 1, 3, 1);
ImageSource source = new AggregatedImageSource(new MemoryImageSource(w, h, data), aggregate);
Assert.assertTrue(source.open());
int i = 1;
int ii = 0;
float[] d = null;
while ((d = source.next(bounds)) != null)
{
ii++;
Assert.assertEquals(i, source.getStartFrameNumber());
Assert.assertEquals(i + 2, source.getEndFrameNumber());
float[] all = combine(crop(data[i - 1], w, bounds), crop(data[i], w, bounds), crop(data[i + 1], w, bounds));
Assert.assertArrayEquals(all, d, 0);
i += 3;
}
Assert.assertEquals(ii, source.getFrames());
}
@Test
public void aggregatedImageSourceCanReturnCroppedDataWithGet()
{
int w = 5;
int h = 3;
int aggregate = 3;
float[][] data = createData(w, h, 15);
Rectangle bounds = new Rectangle(2, 1, 3, 1);
ImageSource source = new AggregatedImageSource(new MemoryImageSource(w, h, data), aggregate);
int[] frames = new int[data.length / 3];
for (int i = 0, frame = 1; i < frames.length; i++, frame += 3)
frames[i] = frame;
Random rand = new Random();
rand.shuffle(frames);
Assert.assertTrue(source.open());
for (int i = 0; i < frames.length; i++)
{
int frame = frames[i];
Assert.assertTrue(source.isValid(frame));
float[] d = source.get(frame, bounds);
Assert.assertEquals(frame, source.getStartFrameNumber());
Assert.assertEquals(frame + 2, source.getEndFrameNumber());
float[] all = combine(crop(data[frame - 1], w, bounds), crop(data[frame], w, bounds),
crop(data[frame + 1], w, bounds));
Assert.assertArrayEquals("Invalid frame data " + frame, all, d, 0);
}
Assert.assertFalse(source.isValid(0));
Assert.assertFalse(source.isValid(data.length + 1));
}
@Test
public void canConstructAggregatedInterlacedImageSource()
{
int w = 5;
int h = 3;
int n = 15;
float[][] data = createData(w, h, n);
int aggregate = 3;
int start = 4;
int size = 2;
int skip = 1;
ImageSource source = new InterlacedImageSource(new MemoryImageSource(w, h, data), start, size, skip);
AggregatedImageSource iSource = new AggregatedImageSource(source, aggregate);
Assert.assertEquals(w, iSource.getWidth());
Assert.assertEquals(h, iSource.getHeight());
Assert.assertEquals(12 * 2 / 3, source.getFrames());
Assert.assertEquals((int) Math.ceil((double) source.getFrames() / aggregate), iSource.getFrames());
Assert.assertEquals(aggregate, iSource.getAggregate());
}
@Test(expected = RuntimeException.class)
public void constructInterlacedAggregatedImageSourceThrows()
{
int w = 5;
int h = 3;
int n = 15;
float[][] data = createData(w, h, n);
int aggregate = 3;
int start = 4;
int size = 2;
int skip = 1;
ImageSource source = new AggregatedImageSource(new MemoryImageSource(w, h, data), aggregate);
new InterlacedImageSource(source, start, size, skip);
}
@Test
public void aggregatedInterlacedImageSourceCanReturnDataWithNext()
{
int w = 5;
int h = 3;
int n = 15;
float[][] data = createData(w, h, n);
int aggregate = 3;
int start = 4;
int size = 2;
int skip = 1;
ImageSource source = new AggregatedImageSource(new InterlacedImageSource(new MemoryImageSource(w, h, data),
start, size, skip), aggregate);
Assert.assertTrue(source.open());
// Set the expected frames returned by the interlacing
int[] expected = new int[] { 4, 5, 7, 8, 10, 11, 13, 14 };
int i = 0, ii = 0;
float[] d = null;
while ((d = source.next()) != null)
{
// Get the range for the data
int endE = FastMath.min(i + 2, expected.length - 1);
int startFrame = expected[i];
int endFrame = expected[endE];
// Check the correct range is returned
Assert.assertEquals(startFrame, source.getStartFrameNumber());
Assert.assertEquals(endFrame, source.getEndFrameNumber());
// Check the data is collated correctly
float[] all = new float[data[0].length];
for (int e = i; e <= endE; e++)
{
int frame = expected[e] - 1;
for (int j = 0; j < all.length; j++)
all[j] += data[frame][j];
}
Assert.assertArrayEquals(all, d, 0);
i += 3;
ii++;
}
Assert.assertEquals(ii, source.getFrames());
}
@Test
public void aggregatedInterlacedImageSourceCanReturnDataWithGet()
{
int w = 5;
int h = 3;
int n = 15;
float[][] data = createData(w, h, n);
int aggregate = 3;
int start = 4;
int size = 2;
int skip = 1;
ImageSource source = new AggregatedImageSource(new InterlacedImageSource(new MemoryImageSource(w, h, data),
start, size, skip), aggregate);
// Set the expected frames returned by the interlacing
int[] expected = new int[] { 4, 5, 7, 8, 10, 11, 13, 14 };
// Randomly pick points from the positions used by next()
int[] frames = new int[source.getFrames()];
for (int i = 0, ii = 0; ii < expected.length; i++, ii += 3)
frames[i] = ii;
Random rand = new Random();
rand.shuffle(frames);
Assert.assertTrue(source.open());
for (int i = 0; i < frames.length; i++)
{
// Get the range for the data
int e = frames[i];
int endE = FastMath.min(e + 2, expected.length - 1);
int startFrame = expected[e];
int endFrame = expected[endE];
// Get the data
float[] d = source.get(startFrame);
// Check the correct range is returned
Assert.assertEquals(startFrame, source.getStartFrameNumber());
Assert.assertEquals(endFrame, source.getEndFrameNumber());
// Check the data is collated correctly
float[] all = new float[data[0].length];
for (; e <= endE; e++)
{
int frame = expected[e] - 1;
for (int j = 0; j < all.length; j++)
all[j] += data[frame][j];
}
Assert.assertArrayEquals(all, d, 0);
}
// Check all the data is valid but skipped interlaced points return null
for (int i = 0; i < data.length; i++)
{
int frame = i+1;
Assert.assertTrue(source.isValid(frame));
if (!isExpected(frame, expected))
Assert.assertNull(source.get(frame));
}
Assert.assertFalse(source.isValid(0));
Assert.assertFalse(source.isValid(data.length + 1));
}
@Test
public void canSerialiseMemoryImageSource()
{
int w = 5;
int h = 3;
int n = 15;
String name = "canSerialiseMemoryImageSource";
MemoryImageSource source = new MemoryImageSource(w, h, createData(w, h, n));
source.setName(name);
source.setFreeMemoryOnClose(true);
String xml = source.toXML();
//System.out.println(xml);
MemoryImageSource source2 = (MemoryImageSource) ImageSource.fromXML(xml);
Assert.assertEquals(w, source2.getWidth());
Assert.assertEquals(h, source2.getHeight());
Assert.assertEquals(n, source2.getFrames());
Assert.assertEquals(name, source2.getName());
Assert.assertEquals(true, source2.isFreeMemoryOnClose());
float[] data;
while ((data = source.next()) != null)
{
float[] data2 = source2.next();
Assert.assertArrayEquals(data, data2, 1e-6f);
}
}
/**
* Create data using the specified dimensions and the number of frames. Each frame will have a different base
* number and each index will be unique in the frame.
*
* @param w
* width
* @param h
* height
* @param n
* The number of frames
* @return The data
*/
private float[][] createData(int w, int h, int n)
{
List<float[]> data = new ArrayList<float[]>(n);
float f = 1;
for (int i = 0; i < n; i++)
{
data.add(createData(w, h, f));
f *= 2;
}
return data.toArray(new float[0][0]);
}
/**
* Create data using the specified dimensions and the base number. Each index will be unique in the frame.
*
* @param w
* width
* @param h
* height
* @param f
* the base number
* @return The data
*/
private float[] createData(int w, int h, float f)
{
float[] data = new float[w * h];
Arrays.fill(data, f);
for (int i = 0; i < data.length; i++)
data[i] += (double) i / data.length;
return data;
}
/**
* Check if the frame is contained in the expected frames array
*
* @param frame
* @param expected
* @return true if within the array
*/
private boolean isExpected(int frame, int[] expected)
{
for (int e : expected)
if (e == frame)
return true;
return false;
}
/**
* Crop the data to the specified bounds
*
* @param data
* @param width
* @param bounds
* @return
*/
float[] crop(float[] data, int width, Rectangle bounds)
{
float[] newData = new float[bounds.width * bounds.height];
for (int y = bounds.y, ii = 0; y < bounds.y + bounds.height; y++)
{
for (int x = bounds.x; x < bounds.x + bounds.width; x++, ii++)
{
int index = y * width + x;
newData[ii] = data[index];
}
}
return newData;
}
/**
* Sum all the input arrays
*
* @param f
* @return The summed array
*/
private float[] combine(float[]... f)
{
float[] all = Arrays.copyOf(f[0], f[0].length);
for (int i = 1; i < f.length; i++)
for (int j = 0; j < all.length; j++)
all[j] += f[i][j];
return all;
}
}