【问题标题】:Reliable and fast FFT in Java [closed]Java中可靠且快速的FFT [关闭]
【发布时间】:2011-03-18 06:45:50
【问题描述】:

因为我不想自己做,所以我正在为 java 寻找一个好的 FFT 实现。首先,我在这里使用了这个FFT Princeton,但它使用了对象,我的分析器告诉我,由于这个事实,它并不是很快。所以我再次搜索并找到了这个:FFT Columbia,它更快。也许你们中的一个人知道另一种 FFT 实现?我想要一个“最好的”,因为我的应用必须处理大量的声音数据,而且用户不喜欢等待...... ;-)

问候。

【问题讨论】:

    标签: java fft


    【解决方案1】:

    FFTW 是“西方最快的傅立叶变换”,并且有一些 Java 包装器:

    http://www.fftw.org/download.html

    希望有帮助!

    【讨论】:

    • 看起来很有趣,我稍后会检查它。 :)
    • 我已经接受了你的回答,虽然我不使用它,但是很多人参考了这个库。
    • 请注意,FFTW 受 GPL 许可保护。 (非免费版本提供较少限制的许可证)
    • apache-commons 的 FastFourierTransformer 类怎么样?
    【解决方案2】:

    迟到 - 这里是纯 Java 解决方案,适用于无法选择 JNI 的用户。JTransforms

    【讨论】:

    • JTransforms 的 API 不如 Apache Commons FastFourierTransformer 好,但速度更快。
    【解决方案3】:

    我用 Java 写了一个 FFT 函数:http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29

    我已在公共领域发布它,因此您可以在任何地方使用这些功能(个人或商业项目也一样)。只需在学分中引用我,然后给我发送一个指向您工作的链接,就可以了。

    这是完全可靠的。我已经对照 Mathematica 的 FFT 检查了它的输出,直到第 15 位十进制数字它们总是正确的。我认为这是一个非常好的 Java 的 FFT 实现。我是在J2SE 1.6版本上写的,在J2SE 1.5-1.6版本上测试过。

    如果您计算指令的数量(它比完美的计算复杂度函数估计要简单得多),您可以清楚地看到这个版本非常棒,即使它根本没有优化。如果有足够的请求,我打算发布优化版本。

    让我知道它是否有用,并告诉我你喜欢的任何 cmets。

    我在这里共享相同的代码:

    /**
    * @author Orlando Selenu
    * Originally written in the Summer of 2008
    * Based on the algorithms originally published by E. Oran Brigham "The Fast Fourier Transform" 1973, in ALGOL60 and FORTRAN
    */
    public class FFTbase {
    /**
     * The Fast Fourier Transform (generic version, with NO optimizations).
     *
     * @param inputReal
     *            an array of length n, the real part
     * @param inputImag
     *            an array of length n, the imaginary part
     * @param DIRECT
     *            TRUE = direct transform, FALSE = inverse transform
     * @return a new array of length 2n
     */
    public static double[] fft(final double[] inputReal, double[] inputImag,
                               boolean DIRECT) {
        // - n is the dimension of the problem
        // - nu is its logarithm in base e
        int n = inputReal.length;
    
        // If n is a power of 2, then ld is an integer (_without_ decimals)
        double ld = Math.log(n) / Math.log(2.0);
    
        // Here I check if n is a power of 2. If exist decimals in ld, I quit
        // from the function returning null.
        if (((int) ld) - ld != 0) {
            System.out.println("The number of elements is not a power of 2.");
            return null;
        }
    
        // Declaration and initialization of the variables
        // ld should be an integer, actually, so I don't lose any information in
        // the cast
        int nu = (int) ld;
        int n2 = n / 2;
        int nu1 = nu - 1;
        double[] xReal = new double[n];
        double[] xImag = new double[n];
        double tReal, tImag, p, arg, c, s;
    
        // Here I check if I'm going to do the direct transform or the inverse
        // transform.
        double constant;
        if (DIRECT)
            constant = -2 * Math.PI;
        else
            constant = 2 * Math.PI;
    
        // I don't want to overwrite the input arrays, so here I copy them. This
        // choice adds \Theta(2n) to the complexity.
        for (int i = 0; i < n; i++) {
            xReal[i] = inputReal[i];
            xImag[i] = inputImag[i];
        }
    
        // First phase - calculation
        int k = 0;
        for (int l = 1; l <= nu; l++) {
            while (k < n) {
                for (int i = 1; i <= n2; i++) {
                    p = bitreverseReference(k >> nu1, nu);
                    // direct FFT or inverse FFT
                    arg = constant * p / n;
                    c = Math.cos(arg);
                    s = Math.sin(arg);
                    tReal = xReal[k + n2] * c + xImag[k + n2] * s;
                    tImag = xImag[k + n2] * c - xReal[k + n2] * s;
                    xReal[k + n2] = xReal[k] - tReal;
                    xImag[k + n2] = xImag[k] - tImag;
                    xReal[k] += tReal;
                    xImag[k] += tImag;
                    k++;
                }
                k += n2;
            }
            k = 0;
            nu1--;
            n2 /= 2;
        }
    
        // Second phase - recombination
        k = 0;
        int r;
        while (k < n) {
            r = bitreverseReference(k, nu);
            if (r > k) {
                tReal = xReal[k];
                tImag = xImag[k];
                xReal[k] = xReal[r];
                xImag[k] = xImag[r];
                xReal[r] = tReal;
                xImag[r] = tImag;
            }
            k++;
        }
    
        // Here I have to mix xReal and xImag to have an array (yes, it should
        // be possible to do this stuff in the earlier parts of the code, but
        // it's here to readibility).
        double[] newArray = new double[xReal.length * 2];
        double radice = 1 / Math.sqrt(n);
        for (int i = 0; i < newArray.length; i += 2) {
            int i2 = i / 2;
            // I used Stephen Wolfram's Mathematica as a reference so I'm going
            // to normalize the output while I'm copying the elements.
            newArray[i] = xReal[i2] * radice;
            newArray[i + 1] = xImag[i2] * radice;
        }
        return newArray;
    }
    
    /**
     * The reference bit reverse function.
     */
    private static int bitreverseReference(int j, int nu) {
        int j2;
        int j1 = j;
        int k = 0;
        for (int i = 1; i <= nu; i++) {
            j2 = j1 / 2;
            k = 2 * k + j1 - 2 * j2;
            j1 = j2;
        }
        return k;
      }
    }
    

    【讨论】:

    • 能否请您在网页上说明许可证?另外,请说明您希望如何被引用。
    • 嗨@stackoverflowuser2010,许可证位于wikijava.org/wiki/WikiJava:GFDL,所以只需链接到代码,并写下我的名字(Orlando Selenu):)你的项目是什么?
    • 谢谢。在 Android 上工作并需要 FFT 实现。
    • 出于好奇,您是否已经实现了优化版本?
    • 嗨 ravemir,实际上是的。它针对一些小的输入长度(8 和 32)进行了优化,但您可以轻松扩展它。我会尽快在wikijava中发布。
    【解决方案4】:

    我想这取决于您正在处理的内容。如果您在很长一段时间内计算 FFT,您可能会发现它确实需要一段时间,具体取决于您想要多少频点。但是,在大多数情况下,对于音频,它被认为是非平稳的(即信号均值和方差随时间变化很大),因此采用较大的 FFT(Periodogram PSD 估计)并不是一种准确的表示。或者,您可以使用短时傅立叶变换,将信号分解成更小的帧并计算 FFT。帧大小取决于统计数据变化的速度,对于语音,通常为 20-40 毫秒,对于音乐,我认为它会稍高一些。

    如果您从麦克风采样,这种方法很好,因为它允许您一次缓冲每一帧,计算 fft 并给出用户感觉的“实时”交互。因为 20 毫秒很快,因为我们无法真正感知到那么小的时间差。

    我开发了一个小型基准测试来测试 FFTW 和 KissFFT c 库在语音信号上的差异。是的,FFTW 进行了高度优化,但是当您仅采用短帧、为用户更新数据并仅使用较小的 fft 大小时,它们都非常相似。这是一个关于如何通过 badlogic 游戏使用 LibGdx 实现KissFFT libraries in Android 的示例。我在几个月前开发的名为 Speech Enhancement for Android 的 Android 应用中使用重叠帧实现了这个库。

    【讨论】:

      【解决方案5】:

      我正在研究在 Java 中将 SSTJ 用于 FFT。如果库可用,它可以通过 JNI 重定向到 FFTW,否则将使用纯 Java 实现。

      【讨论】:

      • SSTJ 链接已过期...您的意思是 Java 中的 Shared Scientific Toolbox,现在托管在 carsomyr.github.io
      • @JasonS 我更正了他的链接
      猜你喜欢
      • 1970-01-01
      • 2020-08-06
      • 2015-12-15
      • 2011-09-16
      • 2011-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-22
      相关资源
      最近更新 更多