/*
* 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.transform.wavelet.impl;
import boofcv.alg.misc.GImageMiscOps;
import boofcv.core.image.GeneralizedImageOps;
import boofcv.core.image.border.BorderIndex1D;
import boofcv.core.image.border.BorderIndex1D_Reflect;
import boofcv.core.image.border.BorderIndex1D_Wrap;
import boofcv.core.image.border.BorderType;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.ImageGray;
import boofcv.struct.wavelet.*;
import boofcv.testing.BoofTesting;
import java.util.Random;
/**
* Generalized wavelet test which allows one algorithm to be compared against another one across
* a wide variety of image shapes, wavelet sizes, and sub-images.
*
* @author Peter Abeles
*/
public abstract class PermuteWaveletCompare {
Random rand = new Random(234);
Class inputType;
Class outputType;
protected PermuteWaveletCompare(Class inputType, Class outputType) {
this.inputType = inputType;
this.outputType = outputType;
}
public void runTests( boolean swapSizes ) {
// normal image sizes for a single level
runTest(20,30,20,30,swapSizes);
runTest(19,29,20,30,swapSizes);
// now try expanded borders
runTest(20,30,22,32,swapSizes);
runTest(20,30,24,34,swapSizes);
runTest(20,30,24,30,swapSizes);
runTest(19,29,22,32,swapSizes);
runTest(19,29,24,34,swapSizes);
runTest(19,29,24,30,swapSizes);
}
private void runTest( int widthIn , int heightIn , int widthOut , int heightOut , boolean swapSize ) {
if( swapSize ) {
int t = widthIn;
widthIn = widthOut;
widthOut = t;
t = heightIn;
heightIn = heightOut;
heightOut = t;
}
ImageGray input = GeneralizedImageOps.createSingleBand(inputType, widthIn, heightIn);
ImageGray found = GeneralizedImageOps.createSingleBand(outputType, widthOut, heightOut);
ImageGray expected = GeneralizedImageOps.createSingleBand(outputType, widthOut, heightOut);
GImageMiscOps.fillUniform(input, rand, 0, 50);
// System.out.println("In [ "+widthIn+" , "+heightIn+" ] Out [ "+widthOut+" , "+heightOut+" ]");
// test different descriptions lengths and offsets, and borders
for( BorderType type : BorderType.values() ) {
for( int o = 0; o <= 2; o++ ) {
for( int l = 2+o; l <= 5; l++ ) {
// System.out.println("type "+type+" o = "+o+" l = "+l);
GImageMiscOps.fill(found, 0);
GImageMiscOps.fill(expected,0);
// create a random wavelet. does not have to be a real once
// since it just is checking that two functions produce the same output
WaveletDescription<?> desc = createDesc(-o,l,type);
applyValidation(desc,input,expected);
// make sure it works on sub-images
BoofTesting.checkSubImage(this,"innerTest",false,input,found, expected, desc);
}
}
}
}
public void innerTest(ImageGray input, ImageGray found, ImageGray expected,
WaveletDescription<?> desc) {
applyTransform(desc,input,found);
// BoofTesting.printDiff(found,expected);
compareResults(desc, input , expected , found);
}
public abstract void applyValidation(WaveletDescription<?> desc , ImageGray input , ImageGray output );
public abstract void applyTransform(WaveletDescription<?> desc , ImageGray input , ImageGray output );
public abstract void compareResults(WaveletDescription<?> desc, ImageGray input , ImageGray expected, ImageGray found );
private WaveletDescription<?> createDesc(int offset, int length, BorderType type ) {
if( inputType == GrayF32.class ) {
return createDesc_F32(offset,length,type);
} else {
return createDesc_I32(offset,length,type);
}
}
private WaveletDescription<WlCoef_F32> createDesc_F32(int offset, int length, BorderType type ) {
WlCoef_F32 forward = createRandomCoef_F32(offset, length);
WlBorderCoef<WlCoef_F32> inverse;
BorderIndex1D border;
if( type == BorderType.WRAP ) {
inverse = new WlBorderCoefStandard<>(forward);
border = new BorderIndex1D_Wrap();
} else {
inverse = createFixedCoef_F32(forward);
border = new BorderIndex1D_Reflect();
}
return new WaveletDescription<>(border, forward, inverse);
}
private WlCoef_F32 createRandomCoef_F32(int offset, int length) {
WlCoef_F32 forward = new WlCoef_F32();
forward.offsetScaling = offset;
forward.offsetWavelet = offset;
forward.scaling = new float[length];
forward.wavelet = new float[length];
for( int i = 0; i < length; i++ ) {
forward.scaling[i] = (float)rand.nextGaussian();
forward.wavelet[i] = (float)rand.nextGaussian();
}
return forward;
}
private WlBorderCoef<WlCoef_F32> createFixedCoef_F32(WlCoef_F32 forward) {
int l = Math.max(forward.getScalingLength()+forward.offsetScaling,forward.getWaveletLength()+forward.offsetWavelet);
int numLower = -Math.max(forward.offsetScaling,forward.offsetWavelet);
int numUpper = Math.max(0,l-2);
numLower = (numLower + numLower%2)/2;
numUpper = (numUpper + numUpper%2)/2;
WlBorderCoefFixed<WlCoef_F32> ret = new WlBorderCoefFixed<>(numLower, numUpper);
ret.setInnerCoef(forward);
for( int i = 0; i < numLower; i++ ) {
ret.setLower(i,createRandomCoef_F32(forward.offsetScaling, forward.getScalingLength()));
}
for( int j = 0; j < numUpper; j++ ) {
ret.setUpper(j,createRandomCoef_F32(forward.offsetScaling, forward.getScalingLength()));
}
return ret;
}
private WaveletDescription<WlCoef_I32> createDesc_I32(int offset, int length, BorderType type ) {
WlCoef_I32 forward = createRandomCoef_I32(offset, length);
BorderIndex1D border;
WlBorderCoef<WlCoef_I32> inverse;
if( type == BorderType.WRAP ) {
inverse = new WlBorderCoefStandard<>(forward);
border = new BorderIndex1D_Wrap();
} else {
inverse = createFixedCoef_I32(forward);
border = new BorderIndex1D_Reflect();
}
return new WaveletDescription<>(border, forward, inverse);
}
private WlCoef_I32 createRandomCoef_I32(int offset, int length) {
WlCoef_I32 forward = new WlCoef_I32();
forward.offsetScaling = offset;
forward.offsetWavelet = offset;
forward.scaling = new int[length];
forward.wavelet = new int[length];
forward.denominatorScaling = 2;
forward.denominatorWavelet = 3;
for( int i = 0; i < length; i++ ) {
forward.scaling[i] = rand.nextInt(8)-3;
forward.wavelet[i] = rand.nextInt(8)-3;
// it would never be zero in practice
if( forward.scaling[i] == 0 )
forward.scaling[i] = 1;
if( forward.wavelet[i] == 0 )
forward.wavelet[i] = 1;
}
return forward;
}
private WlBorderCoef<WlCoef_I32> createFixedCoef_I32(WlCoef_I32 forward) {
int l = Math.max(forward.getScalingLength()+forward.offsetScaling,forward.getWaveletLength()+forward.offsetWavelet);
int numLower = -Math.max(forward.offsetScaling,forward.offsetWavelet);
int numUpper = Math.max(0,l-2);
numLower = (numLower + numLower%2)/2;
numUpper = (numUpper + numUpper%2)/2;
WlBorderCoefFixed<WlCoef_I32> ret = new WlBorderCoefFixed<>(numLower, numUpper);
ret.setInnerCoef(forward);
for( int i = 0; i < numLower; i++ ) {
ret.setLower(i,createRandomCoef_I32(forward.offsetScaling, forward.getScalingLength()));
}
for( int j = 0; j < numUpper; j++ ) {
ret.setUpper(j,createRandomCoef_I32(forward.offsetScaling, forward.getScalingLength()));
}
return ret;
}
}