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.
The newest version!
/*
* Copyright (c) 2021, 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}.
*
* @return {@link DiscreteFourierTransform}
* @see GeneralPurposeFFT_F32_2D
*/
public static DiscreteFourierTransform createTransformF32() {
return new GeneralFft_to_DiscreteFourierTransform_F32();
}
/**
* Creates a {@link DiscreteFourierTransform} for images of type {@link GrayF64}.
*
* @return {@link DiscreteFourierTransform}
* @see GeneralPurposeFFT_F64_2D
*/
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.
*
* @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);
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 - 2024 Weber Informatics LLC | Privacy Policy