boofcv.alg.transform.fft.DiscreteFourierTransformOps Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of boofcv-ip Show documentation
Show all versions of boofcv-ip Show documentation
BoofCV is an open source Java library for real-time computer vision and robotics applications.
/*
* Copyright (c) 2011-2017, 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.abst.transform.fft.DiscreteFourierTransform;
import boofcv.abst.transform.fft.GeneralFft_to_DiscreteFourierTransform_F32;
import boofcv.abst.transform.fft.GeneralFft_to_DiscreteFourierTransform_F64;
import boofcv.alg.InputSanityCheck;
import boofcv.struct.image.*;
/**
* Various functions related to {@link DiscreteFourierTransform}.
*
* @author Peter Abeles
*/
public class DiscreteFourierTransformOps {
/**
* Creates a {@link DiscreteFourierTransform} for images of type {@link GrayF32}.
*
* @see GeneralPurposeFFT_F32_2D
*
* @return {@link DiscreteFourierTransform}
*/
public static DiscreteFourierTransform createTransformF32() {
return new GeneralFft_to_DiscreteFourierTransform_F32();
}
/**
* Creates a {@link DiscreteFourierTransform} for images of type {@link GrayF64}.
*
* @see GeneralPurposeFFT_F64_2D
*
* @return {@link DiscreteFourierTransform}
*/
public static DiscreteFourierTransform createTransformF64() {
return new GeneralFft_to_DiscreteFourierTransform_F64();
}
/**
* true if the number provided is a power of two
* @param x number
* @return true if it is a power of two
*/
public static boolean isPowerOf2(int x) {
if (x <= 1)
return false;
else
return (x & (x - 1)) == 0;
}
/**
* Returns the closest power-of-two number greater than or equal to x.
*
* @param x
* @return the closest power-of-two number greater than or equal to x
*/
public static int nextPow2(int x) {
if (x < 1)
throw new IllegalArgumentException("x must be greater or equal 1");
if ((x & (x - 1)) == 0) {
if( x == 1 )
return 2;
return x; // x is already a power-of-two number
}
x |= (x >>> 1);
x |= (x >>> 2);
x |= (x >>> 4);
x |= (x >>> 8);
x |= (x >>> 16);
x |= (x >>> 32);
return x + 1;
}
/**
* Checks to see if the image and its transform are appropriate sizes . The transform should have
* twice the width and twice the height as the image.
*
* @param image Storage for an image
* @param transform Storage for a Fourier Transform
*/
public static void checkImageArguments( ImageBase image , ImageInterleaved transform ) {
InputSanityCheck.checkSameShape(image,transform);
if( 2 != transform.getNumBands() )
throw new IllegalArgumentException("The transform must have two bands");
}
/**
* Moves the zero-frequency component into the image center (width/2,height/2). This function can
* be called to undo the transform.
*
* @param transform the DFT which is to be shifted.
* @param forward If true then it does the shift in the forward direction. If false then it undoes the transforms.
*/
public static void shiftZeroFrequency(InterleavedF32 transform, boolean forward ) {
int hw = transform.width/2;
int hh = transform.height/2;
if( transform.width%2 == 0 && transform.height%2 == 0 ) {
// if both sides are even then a simple transform can be done
for( int y = 0; y < hh; y++ ) {
for( int x = 0; x < hw; x++ ) {
float ra = transform.getBand(x,y,0);
float ia = transform.getBand(x,y,1);
float rb = transform.getBand(x+hw,y+hh,0);
float ib = transform.getBand(x+hw,y+hh,1);
transform.setBand(x,y,0,rb);
transform.setBand(x,y,1,ib);
transform.setBand(x+hw,y+hh,0,ra);
transform.setBand(x+hw,y+hh,1,ia);
ra = transform.getBand(x+hw,y,0);
ia = transform.getBand(x+hw,y,1);
rb = transform.getBand(x,y+hh,0);
ib = transform.getBand(x,y+hh,1);
transform.setBand(x+hw,y,0,rb);
transform.setBand(x+hw,y,1,ib);
transform.setBand(x,y+hh,0,ra);
transform.setBand(x,y+hh,1,ia);
}
}
} else {
// with odd images, the easiest way to do it is by copying the regions
int w = transform.width;
int h = transform.height;
int hw1 = hw + w%2;
int hh1 = hh + h%2;
if( forward ) {
InterleavedF32 storageTL = new InterleavedF32(hw1,hh1,2);
storageTL.setTo(transform.subimage(0, 0, hw1, hh1, null));
InterleavedF32 storageTR = new InterleavedF32(hw,hh1,2);
storageTR.setTo(transform.subimage(hw1, 0, w, hh1, null));
transform.subimage(0,0,hw,hh, null).setTo(transform.subimage(hw1,hh1,w,h, null));
transform.subimage(hw,0,w, hh, null).setTo(transform.subimage(0,hh1,hw1,h, null));
transform.subimage(hw,hh,w,h, null).setTo(storageTL);
transform.subimage(0,hh,hw,h, null).setTo(storageTR);
} else {
InterleavedF32 storageBL = new InterleavedF32(hw,hh1,2);
storageBL.setTo(transform.subimage(0, hh, hw, h, null));
InterleavedF32 storageBR = new InterleavedF32(hw1,hh1,2);
storageBR.setTo(transform.subimage(hw, hh, w, h, null));
transform.subimage(hw1,hh1,w,h, null).setTo(transform.subimage(0,0,hw,hh, null));
transform.subimage(0,hh1,hw1,h, null).setTo(transform.subimage(hw,0,w, hh, null));
transform.subimage(hw1,0,w,hh1, null).setTo(storageBL);
transform.subimage(0,0,hw1,hh1, null).setTo(storageBR);
}
}
}
/**
* Moves the zero-frequency component into the image center (width/2,height/2). This function can
* be called to undo the transform.
*
* @param transform the DFT which is to be shifted.
* @param forward If true then it does the shift in the forward direction. If false then it undoes the transforms.
*/
public static void shiftZeroFrequency(InterleavedF64 transform, boolean forward ) {
int hw = transform.width/2;
int hh = transform.height/2;
if( transform.width%2 == 0 && transform.height%2 == 0 ) {
// if both sides are even then a simple transform can be done
for( int y = 0; y < hh; y++ ) {
for( int x = 0; x < hw; x++ ) {
double ra = transform.getBand(x,y,0);
double ia = transform.getBand(x,y,1);
double rb = transform.getBand(x+hw,y+hh,0);
double ib = transform.getBand(x+hw,y+hh,1);
transform.setBand(x,y,0,rb);
transform.setBand(x,y,1,ib);
transform.setBand(x+hw,y+hh,0,ra);
transform.setBand(x+hw,y+hh,1,ia);
ra = transform.getBand(x+hw,y,0);
ia = transform.getBand(x+hw,y,1);
rb = transform.getBand(x,y+hh,0);
ib = transform.getBand(x,y+hh,1);
transform.setBand(x+hw,y,0,rb);
transform.setBand(x+hw,y,1,ib);
transform.setBand(x,y+hh,0,ra);
transform.setBand(x,y+hh,1,ia);
}
}
} else {
// with odd images, the easiest way to do it is by copying the regions
int w = transform.width;
int h = transform.height;
int hw1 = hw + w%2;
int hh1 = hh + h%2;
if( forward ) {
InterleavedF64 storageTL = new InterleavedF64(hw1,hh1,2);
storageTL.setTo(transform.subimage(0, 0, hw1, hh1, null));
InterleavedF64 storageTR = new InterleavedF64(hw,hh1,2);
storageTR.setTo(transform.subimage(hw1, 0, w, hh1, null));
transform.subimage(0,0,hw,hh, null).setTo(transform.subimage(hw1,hh1,w,h, null));
transform.subimage(hw,0,w, hh, null).setTo(transform.subimage(0,hh1,hw1,h, null));
transform.subimage(hw,hh,w,h, null).setTo(storageTL);
transform.subimage(0,hh,hw,h, null).setTo(storageTR);
} else {
InterleavedF64 storageBL = new InterleavedF64(hw,hh1,2);
storageBL.setTo(transform.subimage(0, hh, hw, h, null));
InterleavedF64 storageBR = new InterleavedF64(hw1,hh1,2);
storageBR.setTo(transform.subimage(hw, hh, w, h, null));
transform.subimage(hw1,hh1,w,h, null).setTo(transform.subimage(0,0,hw,hh, null));
transform.subimage(0,hh1,hw1,h, null).setTo(transform.subimage(hw,0,w, hh, null));
transform.subimage(hw1,0,w,hh1, null).setTo(storageBL);
transform.subimage(0,0,hw1,hh1, null).setTo(storageBR);
}
}
}
/**
* Computes the magnitude of the complex image:
* magnitude = sqrt( real2 + imaginary2 )
* @param transform (Input) Complex interleaved image
* @param magnitude (Output) Magnitude of image
*/
public static void magnitude( InterleavedF32 transform , GrayF32 magnitude ) {
checkImageArguments(magnitude,transform);
for( int y = 0; y < transform.height; y++ ) {
int indexTran = transform.startIndex + y*transform.stride;
int indexMag = magnitude.startIndex + y*magnitude.stride;
for( int x = 0; x < transform.width; x++, indexTran += 2 ) {
float real = transform.data[indexTran];
float img = transform.data[indexTran+1];
magnitude.data[indexMag++] = (float)Math.sqrt(real * real + img * img);
}
}
}
/**
* Computes the magnitude of the complex image:
* magnitude = sqrt( real2 + imaginary2 )
* @param transform (Input) Complex interleaved image
* @param magnitude (Output) Magnitude of image
*/
public static void magnitude( InterleavedF64 transform , GrayF64 magnitude ) {
checkImageArguments(magnitude,transform);
for( int y = 0; y < transform.height; y++ ) {
int indexTran = transform.startIndex + y*transform.stride;
int indexMag = magnitude.startIndex + y*magnitude.stride;
for( int x = 0; x < transform.width; x++, indexTran += 2 ) {
double real = transform.data[indexTran];
double img = transform.data[indexTran+1];
magnitude.data[indexMag++] = Math.sqrt(real * real + img * img);
}
}
}
/**
* Computes the phase of the complex image:
* phase = atan2( imaginary , real )
* @param transform (Input) Complex interleaved image
* @param phase (output) Phase of image
*/
public static void phase( InterleavedF32 transform , GrayF32 phase ) {
checkImageArguments(phase,transform);
for( int y = 0; y < transform.height; y++ ) {
int indexTran = transform.startIndex + y*transform.stride;
int indexPhase = phase.startIndex + y*phase.stride;
for( int x = 0; x < transform.width; x++, indexTran += 2 ) {
float real = transform.data[indexTran];
float img = transform.data[indexTran+1];
phase.data[indexPhase++] = (float)Math.atan2(img, real);
}
}
}
/**
* Computes the phase of the complex image:
* phase = atan2( imaginary , real )
* @param transform (Input) Complex interleaved image
* @param phase (output) Phase of image
*/
public static void phase( InterleavedF64 transform , GrayF64 phase ) {
checkImageArguments(phase,transform);
for( int y = 0; y < transform.height; y++ ) {
int indexTran = transform.startIndex + y*transform.stride;
int indexPhase = phase.startIndex + y*phase.stride;
for( int x = 0; x < transform.width; x++, indexTran += 2 ) {
double real = transform.data[indexTran];
double img = transform.data[indexTran+1];
phase.data[indexPhase++] = Math.atan2(img, real);
}
}
}
/**
* Converts a regular image into a complex interleaved image with the imaginary component set to zero.
*
* @param real (Input) Regular image.
* @param complex (Output) Equivalent complex image.
*/
public static void realToComplex(GrayF32 real , InterleavedF32 complex ) {
checkImageArguments(real,complex);
for( int y = 0; y < complex.height; y++ ) {
int indexReal = real.startIndex + y*real.stride;
int indexComplex = complex.startIndex + y*complex.stride;
for( int x = 0; x < real.width; x++, indexReal++ , indexComplex += 2 ) {
complex.data[indexComplex] = real.data[indexReal];
complex.data[indexComplex+1] = 0;
}
}
}
/**
* Converts a regular image into a complex interleaved image with the imaginary component set to zero.
*
* @param real (Input) Regular image.
* @param complex (Output) Equivalent complex image.
*/
public static void realToComplex(GrayF64 real , InterleavedF64 complex ) {
checkImageArguments(real,complex);
for( int y = 0; y < complex.height; y++ ) {
int indexReal = real.startIndex + y*real.stride;
int indexComplex = complex.startIndex + y*complex.stride;
for( int x = 0; x < real.width; x++, indexReal++ , indexComplex += 2 ) {
complex.data[indexComplex] = real.data[indexReal];
complex.data[indexComplex+1] = 0;
}
}
}
/**
* Performs element-wise complex multiplication between a real image and a complex image.
*
* @param realA (Input) Regular image
* @param complexB (Input) Complex image
* @param complexC (Output) Complex image
*/
public static void multiplyRealComplex( GrayF32 realA ,
InterleavedF32 complexB , InterleavedF32 complexC ) {
checkImageArguments(realA,complexB);
InputSanityCheck.checkSameShape( complexB,complexC);
for( int y = 0; y < realA.height; y++ ) {
int indexA = realA.startIndex + y*realA.stride;
int indexB = complexB.startIndex + y*complexB.stride;
int indexC = complexC.startIndex + y*complexC.stride;
for( int x = 0; x < realA.width; x++, indexA++ , indexB += 2 ,indexC += 2 ) {
float real = realA.data[indexA];
float realB = complexB.data[indexB];
float imgB = complexB.data[indexB+1];
complexC.data[indexC] = real*realB;
complexC.data[indexC+1] = real*imgB;
}
}
}
/**
* Performs element-wise complex multiplication between a real image and a complex image.
*
* @param realA (Input) Regular image
* @param complexB (Input) Complex image
* @param complexC (Output) Complex image
*/
public static void multiplyRealComplex( GrayF64 realA ,
InterleavedF64 complexB , InterleavedF64 complexC ) {
checkImageArguments(realA,complexB);
InputSanityCheck.checkSameShape( complexB,complexC);
for( int y = 0; y < realA.height; y++ ) {
int indexA = realA.startIndex + y*realA.stride;
int indexB = complexB.startIndex + y*complexB.stride;
int indexC = complexC.startIndex + y*complexC.stride;
for( int x = 0; x < realA.width; x++, indexA++ , indexB += 2 ,indexC += 2 ) {
double real = realA.data[indexA];
double realB = complexB.data[indexB];
double imgB = complexB.data[indexB+1];
complexC.data[indexC] = real*realB;
complexC.data[indexC+1] = real*imgB;
}
}
}
/**
* Performs element-wise complex multiplication between two complex images.
*
* @param complexA (Input) Complex image
* @param complexB (Input) Complex image
* @param complexC (Output) Complex image
*/
public static void multiplyComplex( InterleavedF32 complexA , InterleavedF32 complexB , InterleavedF32 complexC ) {
InputSanityCheck.checkSameShape(complexA, complexB,complexC);
for( int y = 0; y < complexA.height; y++ ) {
int indexA = complexA.startIndex + y*complexA.stride;
int indexB = complexB.startIndex + y*complexB.stride;
int indexC = complexC.startIndex + y*complexC.stride;
for( int x = 0; x < complexA.width; x++, indexA += 2 , indexB += 2 ,indexC += 2 ) {
float realA = complexA.data[indexA];
float imgA = complexA.data[indexA+1];
float realB = complexB.data[indexB];
float imgB = complexB.data[indexB+1];
complexC.data[indexC] = realA*realB - imgA*imgB;
complexC.data[indexC+1] = realA*imgB + imgA*realB;
}
}
}
/**
* Performs element-wise complex multiplication between two complex images.
*
* @param complexA (Input) Complex image
* @param complexB (Input) Complex image
* @param complexC (Output) Complex image
*/
public static void multiplyComplex( InterleavedF64 complexA , InterleavedF64 complexB , InterleavedF64 complexC ) {
InputSanityCheck.checkSameShape(complexA, complexB,complexC);
for( int y = 0; y < complexA.height; y++ ) {
int indexA = complexA.startIndex + y*complexA.stride;
int indexB = complexB.startIndex + y*complexB.stride;
int indexC = complexC.startIndex + y*complexC.stride;
for( int x = 0; x < complexA.width; x++, indexA += 2 , indexB += 2 ,indexC += 2 ) {
double realA = complexA.data[indexA];
double imgA = complexA.data[indexA+1];
double realB = complexB.data[indexB];
double imgB = complexB.data[indexB+1];
complexC.data[indexC] = realA*realB - imgA*imgB;
complexC.data[indexC+1] = realA*imgB + imgA*realB;
}
}
}
}