/*-
* Copyright 2015 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.dawnsci.analysis.dataset;
import java.util.Arrays;
import org.eclipse.dawnsci.analysis.dataset.impl.SummedAreaTable;
import org.eclipse.january.asserts.TestUtils;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.DatasetUtils;
import org.eclipse.january.dataset.IDataset;
import org.eclipse.january.dataset.IntegerDataset;
import org.eclipse.january.dataset.Maths;
import org.eclipse.january.dataset.Random;
import org.junit.Ignore;
import org.junit.Test;
public class SummedAreaTableTest {
private enum TestType {
VALUE, MEAN, VARIANCE, FANO;
}
private static final int[] smallShape = new int[] { 10, 10 };
private static final int[] largeShape = new int[] { 256, 256};
@Test
public void testSmallDiagonal() throws Exception {
final Dataset image = Random.rand(smallShape);
final SummedAreaTable sum = new SummedAreaTable(image);
testDiagonal(image, sum, TestType.VALUE);
}
@Test
public void testLargeDiagonal() throws Exception {
long start = System.currentTimeMillis();
final Dataset image = Random.rand(largeShape);
final SummedAreaTable sum = new SummedAreaTable(image);
long end = System.currentTimeMillis();
// Check time
long delta = end - start;
if (delta > 1000)
throw new Exception(
"Unexpected long sum table generation! As a guide, it should take less than 400ms on I7 but took longer than 1000ms");
// Long time, no caching done!
testDiagonal(image, sum, TestType.VALUE);
}
@Test
public void testSmallMeanDiagonal() throws Exception {
final Dataset image = Random.rand(smallShape);
final SummedAreaTable sum = new SummedAreaTable(image);
testDiagonal(image, sum, TestType.MEAN, 3, 3);
}
@Test
public void testLargeMeanDiagonal() throws Exception {
long start = System.currentTimeMillis();
final Dataset image = Random.rand(largeShape);
final SummedAreaTable sum = new SummedAreaTable(image);
long end = System.currentTimeMillis();
// Check time
long delta = end - start;
if (delta > 1000)
throw new Exception(
"Unexpected long sum table generation! As a guide, it should take less than 400ms on I7 but took longer than 1000ms");
// Long time, no caching done!
testDiagonal(image, sum, TestType.MEAN, 5, 5);
}
@Test
public void testSmallVarianceDiagonal() throws Exception {
final Dataset image = Maths.multiply(Random.rand(smallShape), 100);
final SummedAreaTable sum = new SummedAreaTable(image);
testDiagonal(image, sum, TestType.VARIANCE, 3, 3);
}
@Test
public void testLargeVarianceDiagonal() throws Exception {
long start = System.currentTimeMillis();
final Dataset image = Maths.multiply(Random.rand(largeShape), 100);
final SummedAreaTable sum = new SummedAreaTable(image);
long end = System.currentTimeMillis();
// Check time
long delta = end - start;
if (delta > 1000)
throw new Exception(
"Unexpected long sum table generation! As a guide, it should take less than 400ms on I7 but took longer than 1000ms");
// Long time, no caching done!
testDiagonal(image, sum, TestType.VARIANCE, 5, 5);
}
@Test
public void testSmallFanoDiagonal() throws Exception {
final Dataset image = Maths.multiply(Random.rand(smallShape), 100);
final SummedAreaTable sum = new SummedAreaTable(image, true);
testDiagonal(image, sum, TestType.FANO, 3, 3);
}
@Test
public void testLargeFanoDiagonal() throws Exception {
long start = System.currentTimeMillis();
final Dataset image = Maths.multiply(Random.rand(largeShape), 100);
final SummedAreaTable sum = new SummedAreaTable(image, true);
long end = System.currentTimeMillis();
// Check time
long delta = end - start;
if (delta > 1000)
throw new Exception(
"Unexpected long sum table generation! As a guide, it should take less than 400ms on I7 but took longer than 1000ms");
// Long time, no caching done!
testDiagonal(image, sum, TestType.FANO, 5, 5);
}
@Test
public void testSmallFano() throws Exception {
typeLoop(new int[] { 10, 10 });
}
@Test
public void testMediumFano() throws Exception {
typeLoop(new int[] { 378, 517 });
}
@Ignore
@Test
public void test6MillFanoTableTime() throws Exception {
long start = System.currentTimeMillis();
final Dataset image = Random.rand(new int[] { 2000, 3000 });
final SummedAreaTable sum = new SummedAreaTable(image, true);
long postTable = System.currentTimeMillis();
System.out.println("Calculated summed area table of size " + Arrays.toString(new int[] { 2000, 3000 }) + " in "
+ (postTable - start) + "ms");
@SuppressWarnings("unused")
final IDataset fano = sum.getFanoImage(new int[] { 5, 5 });
long end = System.currentTimeMillis();
System.out.println("Fano loop after table creation of size " + Arrays.toString(new int[] { 2000, 3000 })
+ " in " + (end - postTable) + "ms");
System.out.println("Total fano image of size " + Arrays.toString(new int[] { 2000, 3000 }) + " in "
+ (end - start) + "ms");
if ((end - start) > 5000)
throw new Exception("Rather long time take to compute fano factor image!");
}
@Test
public void testNullImage() throws Exception {
try {
new SummedAreaTable(null);
} catch (Exception required) {
return;
}
throw new Exception("Null image worked!");
}
@Test
public void testEmptyImage() throws Exception {
try {
new SummedAreaTable(DatasetFactory.zeros(IntegerDataset.class, new int[] { 0, 0 }));
} catch (Exception required) {
return;
}
throw new Exception("Empty image worked!");
}
@Test
public void testZeroBox() throws Exception {
try {
SummedAreaTable table = new SummedAreaTable(Random.rand(smallShape), true);
table.getFanoImage(0, 0);
} catch (Exception required) {
return;
}
throw new Exception("Empty box worked!");
}
@Test
public void testEvenBox() throws Exception {
try {
SummedAreaTable table = new SummedAreaTable(Random.rand(smallShape), true);
table.getFanoImage(2, 2);
} catch (Exception required) {
return;
}
throw new Exception("Even box worked!");
}
private void typeLoop(int[] size) throws Exception {
for (int i : new int[] { 1, 5, 9 }) {
testFano(DatasetUtils.cast(Maths.multiply(Random.rand(size), 100), Dataset.INT16), i, i);
testFano(DatasetUtils.cast(Maths.multiply(Random.rand(size), 100), Dataset.INT32), i, i);
testFano(DatasetUtils.cast(Maths.multiply(Random.rand(size), 100), Dataset.INT64), i, i);
testFano(DatasetUtils.cast(Maths.multiply(Random.rand(size), 100), Dataset.FLOAT32), i, i);
testFano(DatasetUtils.cast(Maths.multiply(Random.rand(size), 100), Dataset.FLOAT64), i, i);
}
}
private void testFano(Dataset image, int... box) throws Exception {
long start = System.currentTimeMillis();
final SummedAreaTable sum = new SummedAreaTable(image);
final Dataset fano = sum.getFanoImage(box);
long end = System.currentTimeMillis();
if (!Arrays.equals(fano.getShape(), image.getShape()))
throw new Exception("Fano image changed shape!");
TestUtils.verbosePrintf("Calculated fano of size %s with box %s in %dms\n", Arrays.toString(fano.getShape()),
Arrays.toString(box), end - start);
}
private void testDiagonal(IDataset image, SummedAreaTable sum, TestType type, int... box) throws Exception {
if (!Arrays.equals(sum.getShape(), image.getShape()))
throw new Exception("Shape not the same! sum is " + Arrays.toString(sum.getShape()));
int x = 0, y = 0;
while (x < image.getShape()[0] && y < image.getShape()[1]) {
double a = 0d, b = 0d;
if (type == TestType.VALUE) {
a = sum.getDouble(x, y);
b = getSum(image, x, y);
} else if (type == TestType.MEAN) {
a = sum.getBoxMean(new int[] { x, y }, box);
b = getBoxMean(image, new int[] { x, y }, box);
} else if (type == TestType.VARIANCE) {
a = sum.getBoxVariance(new int[] { x, y }, box);
b = getBoxVariance(image, new int[] { x, y }, box);
} else if (type == TestType.FANO) {
a = sum.getBoxFanoFactor(new int[] { x, y }, box);
double mean = getBoxMean(image, new int[] { x, y }, box);
double variance = getBoxVariance(image, new int[] { x, y }, box);
b = variance / mean;
}
TestUtils.assertEquals("a does not equal b", a, b, 1e-6, 1e-6);
x++;
y++;
}
}
/**
* At one point during development the dataset variance was wrong.
*
* @throws Exception
*/
@Test
public void testDatasetVariance() throws Exception {
final Dataset image = Maths.multiply(Random.rand(smallShape), 100);
double mean = ((Number) image.mean()).doubleValue();
Dataset minus = Maths.subtract(image, mean);
Dataset square = Maths.square(minus);
double var = ((Number) square.mean()).doubleValue();
TestUtils.assertEquals("Variance does not equal image variance", var, image.variance(true), 1e-6,
1e-6);
}
private double getBoxVariance(IDataset image, int[] point, int[] box) {
Dataset subsetNoSlice = createDataset(image, point, box);
return subsetNoSlice.variance(true);
}
private double getBoxMean(IDataset image, int[] point, int... box) {
Dataset subsetNoSlice = createDataset(image, point, box);
return ((Number) subsetNoSlice.mean()).doubleValue();
}
private Dataset createDataset(IDataset image, int[] point, int[] box) {
int[] coords = createCoords(image, point, box);
double[] subset = new double[box[0] * box[1]];
int count = 0;
for (int ix = coords[0]; ix <= coords[2]; ix++) {
for (int iy = coords[1]; iy <= coords[3]; iy++) {
subset[count] = image.getDouble(ix, iy);
++count;
}
}
return DatasetFactory.createFromObject(subset, box);
}
private int[] createCoords(IDataset image, int[] point, int[] box) {
int x = point[0];
int y = point[1];
int r1 = (int) Math.floor(box[0] / 2d); // for instance 3->1, 5->2, 7->3
int r2 = (int) Math.floor(box[1] / 2d); // for instance 3->1, 5->2, 7->3
int minx = x - r1;
if (minx < 0)
minx = 0;
int maxx = x + r1;
if (maxx >= image.getShape()[0])
maxx = image.getShape()[0] - 1;
int miny = y - r2;
if (miny < 0)
miny = 0;
int maxy = y + r2;
if (maxy >= image.getShape()[1])
maxy = image.getShape()[1] - 1;
return new int[] { minx, miny, maxx, maxy };
}
/**
* The summed area table is just the sum of all the pixels above and to the
* left of (x, y)
*
* @param image
* @param x
* @param y
* @return The summed area table is just the sum of all the pixels above and
* to the left of (x, y)
*/
private double getSum(IDataset image, int x, int y) {
double sum = 0d;
for (int ix = 0; ix <= x; ix++) {
for (int iy = 0; iy <= y; iy++) {
sum += image.getDouble(ix, iy);
}
}
return sum;
}
}