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 ip Show documentation
Show all versions of ip Show documentation
BoofCV is an open source Java library for real-time computer vision and robotics applications.
/*
* 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.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;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy