/*
* Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package boofcv.alg.misc;
import boofcv.core.image.GeneralizedImageOps;
import boofcv.struct.image.ImageBase;
import boofcv.struct.image.ImageGray;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Random;
import static org.junit.Assert.assertEquals;
/**
* @author Peter Abeles
*/
public class TestImageStatistics {
int width = 10;
int height = 15;
int numBands = 2;
Random rand = new Random(234);
@Test
public void checkAll() {
int numExpected = 9*8 + 7*8;
Method methods[] = ImageStatistics.class.getMethods();
// sanity check to make sure the functions are being found
int numFound = 0;
for (Method m : methods) {
if( !isTestMethod(m))
continue;
try {
// System.out.println(m.getName());
if( m.getName().compareTo("min") == 0 ) {
testMin(m);
} else if( m.getName().compareTo("max") == 0 ) {
testMax(m);
} else if( m.getName().compareTo("maxAbs") == 0 ) {
testMaxAbs(m);
} else if( m.getName().compareTo("sum") == 0 ) {
testSum(m);
} else if( m.getName().compareTo("mean") == 0 ) {
testMean(m);
} else if( m.getName().compareTo("variance") == 0 ) {
testVariance(m);
} else if( m.getName().compareTo("meanDiffSq") == 0 ) {
testMeanDiffSq(m);
} else if( m.getName().compareTo("meanDiffAbs") == 0 ) {
testMeanDiffAbs(m);
} else if( m.getName().compareTo("histogram") == 0 ) {
testHistogram(m);
} else {
throw new RuntimeException("Unknown function: "+m.getName());
}
} catch (InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
numFound++;
}
// update this as needed when new functions are added
if(numExpected != numFound)
throw new RuntimeException("Unexpected number of methods: Found "+numFound+" expected "+numExpected);
}
private boolean isTestMethod(Method m ) {
Class param[] = m.getParameterTypes();
if( param.length < 1 )
return false;
return ImageBase.class.isAssignableFrom(param[0]);
}
private void testMaxAbs( Method m ) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageBase input = GeneralizedImageOps.createImage(paramTypes[0], width, height, numBands);
if( input.getImageType().getDataType().isSigned() ) {
GImageMiscOps.fillUniform(input, rand, -20,20);
GeneralizedImageOps.setB(input,0,3,0,-100);
} else {
GImageMiscOps.fillUniform(input, rand, 0,20);
GeneralizedImageOps.setB(input,0,3,0,100);
}
Number o = (Number)m.invoke(null,input);
assertEquals(100,o.doubleValue(),1e-8);
}
private void testMax( Method m ) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageBase input = GeneralizedImageOps.createImage(paramTypes[0], width, height, numBands);
if( input.getImageType().getDataType().isSigned() ) {
GImageMiscOps.fillUniform(input, rand, -20,-5);
GeneralizedImageOps.setB(input,0,3,0,-2);
Number o = (Number)m.invoke(null,input);
assertEquals(-2,o.doubleValue(),1e-8);
} else {
GImageMiscOps.fillUniform(input, rand, 0,200);
double maxValue = input.getImageType().getDataType().getMaxValue();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
double before = GeneralizedImageOps.get(input,x,y,0);
GeneralizedImageOps.setB(input,x,y,0,maxValue);
Number o = (Number)m.invoke(null,input);
assertEquals(maxValue,o.doubleValue(),1e-8);
GeneralizedImageOps.setB(input,x,y,0,before);
}
}
}
}
private void testMin( Method m ) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageBase input = GeneralizedImageOps.createImage(paramTypes[0], width, height, numBands);
if( input.getImageType().getDataType().isSigned() ) {
GImageMiscOps.fillUniform(input, rand, -20,-5);
GeneralizedImageOps.setB(input,0,3,0,-30);
Number o = (Number)m.invoke(null,input);
assertEquals(-30,o.doubleValue(),1e-8);
} else {
double maxValue = input.getImageType().getDataType().getMaxValue();
GImageMiscOps.fillUniform(input, rand, 100,maxValue);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
double before = GeneralizedImageOps.get(input,x,y,0);
GeneralizedImageOps.setB(input,x,y,0,5);
Number o = (Number)m.invoke(null,input);
assertEquals(5,o.doubleValue(),1e-8);
GeneralizedImageOps.setB(input,x,y,0,before);
}
}
}
}
private void testSum( Method m ) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageBase inputA = GeneralizedImageOps.createImage(paramTypes[0], width, height, numBands);
int numBands = inputA.getImageType().getNumBands();
if( inputA.getImageType().getDataType().isSigned() ) {
GImageMiscOps.fillUniform(inputA, rand, -20,20);
} else {
GImageMiscOps.fillUniform(inputA, rand, 0,20);
}
Object result = m.invoke(null,inputA);
double expected = 0;
for( int i = 0; i < height; i++ ) {
for( int j = 0; j < width; j++ ) {
for (int k = 0; k < numBands; k++) {
expected += GeneralizedImageOps.get(inputA,j,i,k);
}
}
}
double found = ((Number)result).doubleValue();
assertEquals(expected,found,1e-3);
}
private void testMean( Method m ) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageBase inputA = GeneralizedImageOps.createImage(paramTypes[0], width, height, numBands);
int numBands = inputA.getImageType().getNumBands();
if( inputA.getImageType().getDataType().isSigned() ) {
GImageMiscOps.fillUniform(inputA, rand, -20,20);
} else {
GImageMiscOps.fillUniform(inputA, rand, 0,20);
}
Object result = m.invoke(null,inputA);
double expected = 0;
for( int i = 0; i < height; i++ ) {
for( int j = 0; j < width; j++ ) {
for (int k = 0; k < numBands; k++) {
expected += GeneralizedImageOps.get(inputA,j,i,k);
}
}
}
expected /= (width*height*numBands);
double found = ((Number)result).doubleValue();
assertEquals(expected,found,1e-3);
}
private void testVariance( Method m ) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageGray inputA = GeneralizedImageOps.createSingleBand(paramTypes[0], width, height);
if( inputA.getDataType().isSigned() ) {
GImageMiscOps.fillUniform(inputA, rand, -20,20);
} else {
GImageMiscOps.fillUniform(inputA, rand, 0,20);
}
double mean = 2.5;
Object result = m.invoke(null,inputA,mean);
double total = 0;
for( int i = 0; i < height; i++ ) {
for( int j = 0; j < width; j++ ) {
double d = GeneralizedImageOps.get(inputA,j,i) - mean;
total += d*d;
}
}
double var = total/(width*height);
assertEquals(var, ((Number) result).doubleValue(), 1e-4);
}
private void testMeanDiffSq(Method m) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageBase inputA = GeneralizedImageOps.createImage(paramTypes[0], width, height, numBands);
ImageBase inputB = GeneralizedImageOps.createImage(paramTypes[1], width, height, numBands);
int numBands = inputA.getImageType().getNumBands();
if( inputA.getImageType().getDataType().isSigned() ) {
GImageMiscOps.fillUniform(inputA, rand, -20,20);
GImageMiscOps.fillUniform(inputB, rand, -20,20);
} else {
GImageMiscOps.fillUniform(inputA, rand, 0,20);
GImageMiscOps.fillUniform(inputB, rand, 0,20);
}
Object result = m.invoke(null,inputA,inputB);
double total = 0;
for( int i = 0; i < height; i++ ) {
for( int j = 0; j < width; j++ ) {
for (int k = 0; k < numBands; k++) {
double a = GeneralizedImageOps.get(inputA,j,i,k);
double b = GeneralizedImageOps.get(inputB,j,i,k);
total += (a-b)*(a-b);
}
}
}
double expected = total/(width*height*numBands);
assertEquals(expected, ((Number) result).doubleValue(), 1e-4);
}
private void testMeanDiffAbs(Method m) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageBase inputA = GeneralizedImageOps.createImage(paramTypes[0], width, height, numBands);
ImageBase inputB = GeneralizedImageOps.createImage(paramTypes[1], width, height, numBands);
int numBands = inputA.getImageType().getNumBands();
if( inputA.getImageType().getDataType().isSigned() ) {
GImageMiscOps.fillUniform(inputA, rand, -20,20);
GImageMiscOps.fillUniform(inputB, rand, -20,20);
} else {
GImageMiscOps.fillUniform(inputA, rand, 0,20);
GImageMiscOps.fillUniform(inputB, rand, 0,20);
}
Object result = m.invoke(null,inputA,inputB);
double total = 0;
for( int i = 0; i < height; i++ ) {
for( int j = 0; j < width; j++ ) {
for (int k = 0; k < numBands; k++) {
double a = GeneralizedImageOps.get(inputA, j, i, k);
double b = GeneralizedImageOps.get(inputB, j, i, k);
total += Math.abs(a - b);
}
}
}
double expected = total/(width*height*numBands);
assertEquals(expected, ((Number) result).doubleValue(), 1e-4);
}
private void testHistogram(Method m) throws InvocationTargetException, IllegalAccessException {
Class paramTypes[] = m.getParameterTypes();
ImageGray inputA = GeneralizedImageOps.createSingleBand(paramTypes[0], width, height);
int histogram[] = new int[ 100 ];
// it should be zeroed
for( int i = 0; i < histogram.length; i++ )
histogram[i] = 100;
int minValue;
if( inputA.getDataType().isSigned() ) {
GImageMiscOps.fillUniform(inputA, rand, -20,20);
m.invoke(null,inputA,-20,histogram);
minValue = -20;
} else {
GImageMiscOps.fillUniform(inputA, rand, 0,40);
m.invoke(null,inputA,histogram);
minValue = 0;
}
// manually compute the histogram
int expected[] = new int[ 100 ];
for( int i = 0; i < height; i++ ) {
for( int j = 0; j < width; j++ ) {
double a = GeneralizedImageOps.get(inputA,j,i);
expected[ -minValue + (int)a ]++;
}
}
for( int i = 0; i < 100; i++ ) {
assertEquals("index "+i,expected[i],histogram[i]);
}
}
}