傅里叶变换 二维离散傅里叶变换
1、介绍。
DFT:(Discrete Fourier Transform)离散傅里叶变换是傅里叶变换在时域和频域上都呈离散的形式,将信号的时域采样变换为其DTFT的频域采样。在形式上,变换两端(时域和频域上)的序列是有限长的,而实际上这两组序列都应当被认为是离散周期信号的主值序列。即使对有限长的离散信号作DFT,也应当将其看作其周期延拓的变换。
1)、欧拉公式:
,其中i是虚数,即i的平方为-1。
2)、二维离散傅里叶变换DFT公式:
N是二维数组的行数,M是二维数组的列数。u和v是转换后二维数组的位置,F(u,v)是转换后数组中相应位置的值。x和y是原二维数组的位置,f(x,y)是原数组中相应的值。
根据欧拉公式,也可以写成:
3)、二维离散傅里叶逆变换IDFT公式:
N是二维数组的行数,M是二维数组的列数。x和y是转换后二维数组的位置,f(x,y)是转换后数组中相应位置的值。u和v是原二维数组的位置,F(u,v)是原数组中相应的值。
根据欧拉公式,也可以写成:
2、生成随机的二维数组。
//f(x,y)=1+2x+8x^2+2y+3xy^2
private static double[][] getRandom2(int row, int column) {double[][] arrays = new double[row][column];for (int x = 0; x < row; x++) {for (int y = 0; y < column; y++) {double value = 1 + 2 * x + 8 * x * x + 2 * y + 3 * x * y * y;arrays[x][y] = value;}}return arrays;
}
3、复数类。参考文章高数 复数的四则运算
package com.zxj.reptile.utils.number;public class Complex {private double real;//实数private double image;//虚数public Complex() {real = 0;image = 0;}public Complex(double real, double image) {this.real = real;this.image = image;}//加:(a+bi)+(c+di)=(a+c)+(b+d)ipublic Complex add(Complex complex) {double real = complex.getReal();double image = complex.getImage();double newReal = this.real + real;double newImage = this.image + image;return new Complex(newReal, newImage);}//减:(a+bi)-(c+di)=(a-c)+(b-d)ipublic Complex sub(Complex complex) {double real = complex.getReal();double image = complex.getImage();double newReal = this.real - real;double newImage = this.image - image;return new Complex(newReal, newImage);}//乘:(a+bi)(c+di)=(ac-bd)+(bc+ad)ipublic Complex mul(Complex complex) {double real = complex.getReal();double image = complex.getImage();double newReal = this.real * real - this.image * image;double newImage = this.image * real + this.real * image;return new Complex(newReal, newImage);}//乘:a(c+di)=ac+adipublic Complex mul(double multiplier) {return mul(new Complex(multiplier, 0));}//除:(a+bi)/(c+di)=(ac+bd)/(c^2+d^2) +((bc-ad)/(c^2+d^2))ipublic Complex div(Complex complex) {double real = complex.getReal();double image = complex.getImage();double denominator = real * real + image * image;double newReal = (this.real * real + this.image * image) / denominator;double newImage = (this.image * real - this.real * image) / denominator;return new Complex(newReal, newImage);}//欧拉公式 e^(ix)=cosx+isinxpublic static Complex euler(double x) {double newReal = Math.cos(x);double newImage = Math.sin(x);return new Complex(newReal, newImage);}public double getReal() {return real;}public void setReal(double real) {this.real = real;}public double getImage() {return image;}public void setImage(double image) {this.image = image;}@Overridepublic String toString() {String str = "";if (real != 0) {str += real;} else {str += "0";}if (image < 0) {str += image + "i";} else if (image > 0) {str += "+" + image + "i";}return str;}
}
4、二维离散傅里叶变换DFT代码。
public static Complex[][] getDft2(double[][] arrays) {int M = arrays.length;int N = arrays[0].length;Complex[][] complexArrays = new Complex[M][N];double flag = -2 * Math.PI;for (int u = 0; u < M; u++) {for (int v = 0; v < N; v++) {//x-->M和y-->N的累加Complex sum = new Complex();for (int x = 0; x < M; x++) {for (int y = 0; y < N; y++) {//e^(-(2 * PI * (ux / M + vy / N)i)double angle = flag * (u * x * 1.0 / M + v * y * 1.0 / N);Complex complex = Complex.euler(angle);//f(x) * e^(-(2 * PI * (ux / M + vy / N)i)complex = complex.mul(arrays[x][y]);sum = complex.add(sum);}}complexArrays[u][v] = sum;}}return complexArrays;}
5、二维离散傅里叶逆变换IDFT代码。
public static double[][] getInverseDft2(Complex[][] complexArrays) {int M = complexArrays.length;int N = complexArrays[0].length;double[][] arrays = new double[M][N];double flag = 2 * Math.PI;for (int x = 0; x < M; x++) {for (int y = 0; y < N; y++) {//u-->M和v-->N的累加Complex sum = new Complex();for (int u = 0; u < M; u++) {for (int v = 0; v < N; v++) {//e^((2 * PI * (ux / M + vy / N)i)double angle = flag * (u * x * 1.0 / M + v * y * 1.0 / N);Complex complex = Complex.euler(angle);//f(x) * e^((2 * PI * (ux / M + vy / N)i)complex = complex.mul(complexArrays[u][v]);sum = complex.add(sum);}}//f(x) * e^((2 * PI * (ux / M + vy / N)i) / (M * N)sum = sum.mul(1 * 1.0 / (N * M));double realSum = sum.getReal();arrays[x][y] = Math.round(realSum * 100) / 100.0;}}return arrays;}
6、因为java的Arrays类没有提供二维数组是否相等的比较方法,所以自己写一个比较二维数组是否相等的方法。
public static boolean equals(double[][] a, double[][] b) {if (a == null || b == null || a.length != b.length) {return false;}for (int i = 0; i < a.length; i++) {if (!Arrays.equals(a[i], b[i])) {return false;}}return true;
}
7、流程。可以先将随机数组的大小设小点,然后将打印日志的代码开起来,这样子才会对流程有更多的了解。
public static void main(String[] args) {System.out.println("------开始------");long time = System.currentTimeMillis();// testDft();//一维离散傅里叶变换testDft2(10, 10);//二维离散傅里叶变换System.out.println("花费时间 :" + (System.currentTimeMillis() - time));System.out.println("------结束------");}private static void testDft2(int row, int column) {System.out.println(String.format("二维数组共有%d行%d列", row, column));//System.out.println("原数据: ");double[][] arrays = getRandom2(row, column);for (int x = 0; x < row; x++) {for (int y = 0; y < column; y++) {//System.out.print(arrays[x][y] + " ");}//System.out.println();}//System.out.println("离散傅里叶变换DFT: ");Complex[][] dtfArrays = FourierUtils.getDft2(arrays);for (int x = 0; x < row; x++) {for (int y = 0; y < column; y++) {//System.out.print(dtfArrays[x][y] + " ");}//System.out.println();}//System.out.println("逆离散傅里叶变换IDFT: ");double[][] inverseDtfArrays = FourierUtils.getInverseDft2(dtfArrays);for (int x = 0; x < row; x++) {for (int y = 0; y < column; y++) {//System.out.print(inverseDtfArrays[x][y] + " ");}//System.out.println();}System.out.print("二维离散傅里叶变换和逆离散傅里叶变换");if (ArrayUtils.equals(arrays, inverseDtfArrays)) {System.out.println("成功");} else {System.out.println("失败");}}
8、结果。可以将生成随机二维数组的大小进行改变,也可以对生成随机数据的函数进行改变。会发现原数组和经过离散傅里叶变换DFT及逆离散傅里叶变换IDFT之后的数组是一模一样的,就可以验证出算法和公式是对的。我将打印的日志和结果截图一并给出来。
9、结论。
二维离散傅里叶因为涉及到4个嵌套循环,时间复杂度十分的大,所以在实际应用场景上得不到使用,现在的照片小的差不多也要500*500的大小,但是二维离散傅里叶处理大小60*60的时候,就已经显得十分的吃力了。