All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.alg.transform.fft.DiscreteFourierTransformOps Maven / Gradle / Ivy

Go to download

BoofCV is an open source Java library for real-time computer vision and robotics applications.

There is a newer version: 1.1.6
Show newest version
/*
 * 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; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy