web-dev-qa-db-fra.com

FFT fiable et rapide en Java

comme je ne veux pas le faire par moi-même, je recherche une bonne implémentation FFT pour Java. J'ai d'abord utilisé celui-ci ici FFT Princeton mais il utilise des objets et mon profileur m'a dit que ce n'était pas vraiment rapide à cause de ce fait. J'ai donc googlé à nouveau et trouvé celui-ci: FFT Columbia qui est plus rapide. Peut-être que l'un de vous connaît une autre implémentation FFT? J'aimerais avoir le "meilleur" parce que mon application doit traiter une énorme quantité de données sonores, et les utilisateurs n'aiment pas attendre ... ;-)

Cordialement.

51
InsertNickHere

FFTW est la "transformée de Fourier la plus rapide de l'ouest", et a quelques Java wrappers:

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

J'espère que ça t'as aidé!

31
Kieren Johnstone

Tard dans la fête - ici comme une pure Java pour ceux quand JNI n'est pas une option . JTransforms

18
basszero

J'ai écrit une fonction pour la FFT en Java: http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29

Il est dans le domaine public, vous pouvez donc utiliser ces fonctions partout (projets personnels ou professionnels également). Citez-moi simplement dans les crédits et envoyez-moi juste un lien de votre travail, et vous êtes ok.

C'est totalement fiable. J'ai vérifié sa sortie par rapport à la FFT de Mathematica et ils étaient toujours corrects jusqu'au 15ème chiffre décimal. Je pense que c'est une très bonne implémentation FFT pour Java. Je l'ai écrit sur la version J2SE 1.6 et l'ai testé sur la version J2SE 1.5-1.6.

Si vous comptez le nombre d'instructions (c'est beaucoup plus simple qu'une estimation de fonction de complexité de calcul parfaite), vous pouvez clairement voir que cette version est excellente même si elle n'est pas du tout optimisée. Je prévois de publier la version optimisée s'il y a suffisamment de demandes.

Faites-moi savoir si cela a été utile et dites-moi tout commentaire que vous aimez.

Je partage le même code ici:

/**
* @author Orlando Selenu
*
*/
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 bitreverse 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;
  }
}
11
alcor

Je suppose que cela dépend de ce que vous traitez. Si vous calculez la FFT sur une longue durée, il se peut que cela prenne un certain temps en fonction du nombre de points de fréquence que vous souhaitez. Cependant, dans la plupart des cas pour l'audio, il est considéré comme non stationnaire (c'est-à-dire que la moyenne des signaux et la variance varient beaucoup au fil du temps), donc prendre une grande FFT ( Periodogram PSD estimation) n'est pas une représentation précise . Vous pouvez également utiliser la transformée de Fourier à court terme, par laquelle vous divisez le signal en plus petites trames et calculez la FFT. La taille de l'image varie en fonction de la vitesse à laquelle les statistiques changent, pour la parole, elle est généralement de 20 à 40 ms, pour la musique, je suppose qu'elle est légèrement plus élevée.

Cette méthode est bonne si vous échantillonnez à partir du microphone, car elle vous permet de mettre en mémoire tampon chaque image à la fois, de calculer le fft et de donner à l'utilisateur une interaction "en temps réel". Parce que 20 ms est rapide, car nous ne pouvons pas vraiment percevoir une différence de temps aussi petite.

J'ai développé une petite référence pour tester la différence entre les c-bibliothèques FFTW et KissFFT sur un signal vocal. Oui, FFTW est hautement optimisé, mais lorsque vous ne prenez que des images courtes, mettez à jour les données pour l'utilisateur et n'utilisez qu'une petite taille fft, elles sont toutes deux très similaires. Voici un exemple sur la façon d'implémenter les bibliothèques KissFFT dans Android en utilisant LibGdx par les jeux badlogic. J'ai implémenté cette bibliothèque en utilisant des cadres qui se chevauchent dans une application Android que j'ai développée il y a quelques mois et appelée Speech Enhancement for Android .

4
digiphd

Je cherche à utiliser SSTJ pour les FFT en Java. Il peut rediriger via JNI vers FFTW si la bibliothèque est disponible ou utilisera une implémentation pure Java sinon.

4
Jay R.