/*
* 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.fft;
import boofcv.alg.misc.GImageMiscOps;
import boofcv.core.image.GeneralizedImageOps;
import boofcv.struct.image.*;
import boofcv.testing.BoofTesting;
import org.ejml.data.Complex64F;
import org.ejml.ops.ComplexMath64F;
import org.junit.Test;
import java.util.Random;
import static boofcv.core.image.GeneralizedImageOps.get;
import static org.junit.Assert.*;
/**
* @author Peter Abeles
*/
public class TestDiscreteFourierTransformOps {
Random rand = new Random(234);
Class imageTypes[] = new Class[]{InterleavedF32.class,InterleavedF64.class};
Class imageTypesS[] = new Class[]{GrayF32.class,GrayF64.class};
@Test
public void isPowerOf2() {
assertFalse(DiscreteFourierTransformOps.isPowerOf2(1));
assertTrue(DiscreteFourierTransformOps.isPowerOf2(2));
assertFalse(DiscreteFourierTransformOps.isPowerOf2(3));
assertTrue(DiscreteFourierTransformOps.isPowerOf2(4));
assertFalse(DiscreteFourierTransformOps.isPowerOf2(5));
assertFalse(DiscreteFourierTransformOps.isPowerOf2(6));
assertTrue(DiscreteFourierTransformOps.isPowerOf2(8));
assertFalse(DiscreteFourierTransformOps.isPowerOf2(31));
assertTrue(DiscreteFourierTransformOps.isPowerOf2(32));
assertTrue(DiscreteFourierTransformOps.isPowerOf2(1024));
}
@Test
public void nextPow2() {
assertEquals(2,DiscreteFourierTransformOps.nextPow2(1));
assertEquals(2,DiscreteFourierTransformOps.nextPow2(2));
assertEquals(4,DiscreteFourierTransformOps.nextPow2(3));
assertEquals(4,DiscreteFourierTransformOps.nextPow2(4));
assertEquals(1024,DiscreteFourierTransformOps.nextPow2(767));
assertEquals(1024,DiscreteFourierTransformOps.nextPow2(1024));
}
@Test
public void checkImageArguments() {
DiscreteFourierTransformOps.checkImageArguments(new GrayF64(10,12),new InterleavedF32(10,12,2));
// test negative cases
try {
DiscreteFourierTransformOps.checkImageArguments(new GrayF64(10,12),new InterleavedF32(10,12,1));
fail("Should have thrown an exception");
} catch( IllegalArgumentException ignore ){}
try {
DiscreteFourierTransformOps.checkImageArguments(new GrayF64(10,12),new InterleavedF32(20,12,2));
fail("Should have thrown an exception");
} catch( IllegalArgumentException ignore ){}
try {
DiscreteFourierTransformOps.checkImageArguments(new GrayF64(10,12),new InterleavedF32(10,14,2));
fail("Should have thrown an exception");
} catch( IllegalArgumentException ignore ){}
}
@Test
public void shiftZeroFrequency() {
for( Class type : imageTypes ) {
ImageInterleaved complex;
// test even images first
complex = GeneralizedImageOps.createInterleaved(type,4,6,2);
shiftZeroFrequency(complex);
BoofTesting.checkSubImage(this, "shiftZeroFrequency", false, complex);
// test odd images now
complex = GeneralizedImageOps.createInterleaved(type,5,7,2);
shiftZeroFrequency(complex);
BoofTesting.checkSubImage(this, "shiftZeroFrequency", false, complex);
// check one side being odd or even
complex = GeneralizedImageOps.createInterleaved(type,6,7,2);
shiftZeroFrequency(complex);
BoofTesting.checkSubImage(this, "shiftZeroFrequency", false, complex);
complex = GeneralizedImageOps.createInterleaved(type,5,8,2);
shiftZeroFrequency(complex);
BoofTesting.checkSubImage(this, "shiftZeroFrequency", false, complex);
}
}
public void shiftZeroFrequency( ImageInterleaved input ) {
GImageMiscOps.fillUniform(input, rand, -5, 5);
ImageInterleaved original = (ImageInterleaved)input.clone();
// corners should be at zero now
if( input instanceof InterleavedF32 )
DiscreteFourierTransformOps.shiftZeroFrequency((InterleavedF32)input,true);
else
DiscreteFourierTransformOps.shiftZeroFrequency((InterleavedF64)input,true);
int hw = input.width/2;
int hh = input.height/2;
int w = input.width;
int h = input.height;
assertEquals(get(original, 0, 0, 0), get(input, hw, hh, 0),1e-4);
assertEquals(get(original, 0, 0, 1), get(input, hw, hh, 1),1e-4);
assertEquals(get(original, w - 1, h - 1, 0),get(input, hw - 1, hh - 1, 0),1e-4);
assertEquals(get(original, w - 1, h - 1, 1),get(input, hw - 1, hh - 1, 1),1e-4);
assertEquals(get(original, w - 1, 0, 0),get(input, hw - 1, hh, 0),1e-4);
assertEquals(get(original, w - 1, 0, 1),get(input, hw - 1, hh, 1),1e-4);
assertEquals(get(original, 0, h - 1, 0),get(input, hw, hh - 1, 0),1e-4);
assertEquals(get(original, 0, h - 1, 1),get(input, hw, hh - 1, 1),1e-4);
// undo the transform
if( input instanceof InterleavedF32 )
DiscreteFourierTransformOps.shiftZeroFrequency((InterleavedF32)input,false);
else
DiscreteFourierTransformOps.shiftZeroFrequency((InterleavedF64)input,false);
BoofTesting.assertEquals(original,input,1e-4);
}
@Test
public void magnitude() {
for( int i = 0; i < imageTypes.length; i++ ) {
ImageInterleaved complex = GeneralizedImageOps.createInterleaved(imageTypes[i],10,20,2);
ImageGray output = GeneralizedImageOps.createSingleBand(imageTypesS[i], 10, 20);
magnitude(complex, output);
BoofTesting.checkSubImage(this, "magnitude", false, complex, output);
}
}
public void magnitude( ImageInterleaved transform , ImageGray output ) {
GImageMiscOps.fillUniform(transform,rand,-5,5);
GImageMiscOps.fillUniform(output,rand,-5,5);
if( transform instanceof InterleavedF32 )
DiscreteFourierTransformOps.magnitude((InterleavedF32)transform, (GrayF32)output);
else
DiscreteFourierTransformOps.magnitude((InterleavedF64)transform, (GrayF64)output);
for( int y = 0; y < transform.height; y++ ) {
for( int x = 0; x < transform.width; x++ ) {
double r = get(transform,x,y,0);
double i = get(transform, x, y, 1);
double m = Math.sqrt(r*r + i*i);
assertEquals(m,get(output, x, y),1e-4);
}
}
}
@Test
public void phase() {
for( int i = 0; i < imageTypes.length; i++ ) {
ImageInterleaved complex = GeneralizedImageOps.createInterleaved(imageTypes[i],10,20,2);
ImageGray output = GeneralizedImageOps.createSingleBand(imageTypesS[i],10,20);
phase(complex,output);
BoofTesting.checkSubImage(this, "phase", false, complex, output );
}
}
public void phase( ImageInterleaved transform , ImageGray output ) {
GImageMiscOps.fillUniform(transform,rand,-5,5);
GImageMiscOps.fillUniform(output,rand,-5,5);
if( transform instanceof InterleavedF32 )
DiscreteFourierTransformOps.phase((InterleavedF32) transform, (GrayF32) output);
else
DiscreteFourierTransformOps.phase((InterleavedF64) transform, (GrayF64) output);
for( int y = 0; y < transform.height; y++ ) {
for( int x = 0; x < transform.width; x++ ) {
double r = get(transform, x, y, 0);
double i = get(transform, x, y, 1);
double m = Math.atan2(i, r);
assertEquals(m,get(output, x, y),1e-4);
}
}
}
@Test
public void realToComplex() {
for( int i = 0; i < imageTypes.length; i++ ) {
ImageGray real = GeneralizedImageOps.createSingleBand(imageTypesS[i], 10, 20);
ImageInterleaved complex = GeneralizedImageOps.createInterleaved(imageTypes[i],10,20,2);
realToComplex(real,complex);
BoofTesting.checkSubImage(this, "realToComplex", false, real, complex );
}
}
public void realToComplex(ImageGray real , ImageInterleaved complex ) {
GImageMiscOps.fillUniform(real,rand,-5,5);
GImageMiscOps.fillUniform(complex,rand,-5,5);
if( complex instanceof InterleavedF32 )
DiscreteFourierTransformOps.realToComplex((GrayF32) real, (InterleavedF32) complex);
else
DiscreteFourierTransformOps.realToComplex((GrayF64) real, (InterleavedF64) complex);
for( int y = 0; y < real.height; y++ ) {
for( int x = 0; x < real.width; x++ ) {
assertEquals(get(real,x,y),get(complex, x, y, 0),1e-4);
assertEquals(0,get(complex,x,y,1),1e-4);
}
}
}
@Test
public void multiplyRealComplex() {
for( int i = 0; i < imageTypes.length; i++ ) {
ImageGray realA = GeneralizedImageOps.createSingleBand(imageTypesS[i],10,20);
ImageInterleaved complexB = GeneralizedImageOps.createInterleaved(imageTypes[i],10,20,2);
ImageInterleaved complexC = GeneralizedImageOps.createInterleaved(imageTypes[i],10,20,2);
GImageMiscOps.fillUniform(realA,rand,-5,5);
GImageMiscOps.fillUniform(complexB,rand,-5,5);
multiplyRealComplex(realA,complexB,complexC);
BoofTesting.checkSubImage(this, "multiplyRealComplex", false, realA, complexB, complexC);
}
}
public void multiplyRealComplex(ImageGray realA , ImageInterleaved complexB , ImageInterleaved complexC ) {
if( complexB instanceof InterleavedF32 )
DiscreteFourierTransformOps.multiplyRealComplex((GrayF32)realA, (InterleavedF32)complexB, (InterleavedF32)complexC);
else
DiscreteFourierTransformOps.multiplyRealComplex((GrayF64)realA, (InterleavedF64)complexB, (InterleavedF64)complexC);
Complex64F expected = new Complex64F();
for( int y = 0; y < realA.height; y++ ) {
for( int x = 0; x < realA.width; x++ ) {
Complex64F a = new Complex64F(get(realA,x, y),0);
Complex64F b = new Complex64F(get(complexB,x,y,0),get(complexB, x, y, 1));
ComplexMath64F.multiply(a, b, expected);
assertEquals(expected.getReal(),get(complexC, x, y, 0),1e-4);
assertEquals(expected.getImaginary(),get(complexC, x, y, 1),1e-4);
}
}
}
@Test
public void multiplyComplex() {
for( int i = 0; i < imageTypes.length; i++ ) {
ImageInterleaved complexA = GeneralizedImageOps.createInterleaved(imageTypes[i], 10, 20, 2);
ImageInterleaved complexB = GeneralizedImageOps.createInterleaved(imageTypes[i],10,20,2);
ImageInterleaved complexC = GeneralizedImageOps.createInterleaved(imageTypes[i],10,20,2);
GImageMiscOps.fillUniform(complexA,rand,-5,5);
GImageMiscOps.fillUniform(complexB,rand,-5,5);
multiplyComplex(complexA,complexB,complexC);
BoofTesting.checkSubImage(this,"multiplyComplex",false,complexA,complexB,complexC);
}
}
public void multiplyComplex( ImageInterleaved complexA , ImageInterleaved complexB , ImageInterleaved complexC ) {
if( complexB instanceof InterleavedF32 )
DiscreteFourierTransformOps.multiplyComplex((InterleavedF32) complexA, (InterleavedF32) complexB, (InterleavedF32) complexC);
else
DiscreteFourierTransformOps.multiplyComplex((InterleavedF64) complexA, (InterleavedF64) complexB, (InterleavedF64) complexC);
Complex64F expected = new Complex64F();
for( int y = 0; y < complexA.height; y++ ) {
for( int x = 0; x < complexA.width; x++ ) {
Complex64F a = new Complex64F(get(complexA, x, y, 0),get(complexA, x, y, 1));
Complex64F b = new Complex64F(get(complexB, x, y, 0),get(complexB,x,y,1));
ComplexMath64F.multiply(a, b, expected);
assertEquals(expected.getReal(),get(complexC, x, y, 0),1e-4);
assertEquals(expected.getImaginary(),get(complexC, x, y, 1),1e-4);
}
}
}
}