【问题标题】:ifft results are different from original signalifft 结果与原始信号不同
【发布时间】:2017-05-21 09:53:39
【问题描述】:

FFT 工作正常,但当我想采用 IFFT 时,我总是从其结果中看到相同的图表。结果很复杂,无论原始信号如何,图形总是相同的。

实部图是一个-sin,周期=帧大小

虚部是同周期的-cos

哪里有问题?

原始信号:

IFFT 实际值(图片仅为帧的一半):

我使用的算法 FFT。

double** FFT(double** f, int s, bool inverse) {
    if (s == 1) return f;
    int sH = s / 2;

    double** fOdd = new double*[sH];
    double** fEven = new double*[sH];
    for (int i = 0; i < sH; i++) {
       int j = 2 * i;
       fOdd[i] = f[j];
       fEven[i] = f[j + 1];
    }

    double** sOdd = FFT(fOdd, sH, inverse);
    double** sEven = FFT(fEven, sH, inverse);

    double**spectr = new double*[s];

    double arg = inverse ? DoublePI / s : -DoublePI / s;
    double*oBase = new double[2]{ cos(arg),sin(arg) };
    double*o = new double[2]{ 1,0 };

    for (int i = 0; i < sH; i++) {
        double* sO1 = Mul(o, sOdd[i]);

        spectr[i] = Sum(sEven[i], sO1);
        spectr[i + sH] = Dif(sEven[i], sO1);

        o = Mul(o, oBase);
    }

    return spectr;
}

【问题讨论】:

  • 旁注:你的代码使用new在堆上分配了很多对象,但是你的代码永远不会调用delete而不在对象离开作用域之前改变它们的所有权——所以你的程序会泄漏记忆。
  • @Dai,谢谢你的回答,我会尝试修复它。但现在这不是我最大的问题。
  • 恕我直言,这是最大的问题。所有这些指针和news 使代码变得不必要地难以阅读和发现像你所拥有的那样的错误
  • 任何人都会认为从未发明过 STL...

标签: c++ fft ifft


【解决方案1】:

“蝴蝶”部分错误地应用了系数:

for (int i = 0; i < sH; i++) {
    double* sO1 = sOdd[i];
    double* sE1 = Mul(o, sEven[i]);

    spectr[i] = Sum(sO1, sE1);
    spectr[i + sH] = Dif(sO1, sE1);

    o = Mul(o, oBase);
}

旁注:

我保留了你的符号,但这会让事情变得混乱:

fOdd 的索引为 0、2、4、6,...所以它应该是 fEven

fEven 有索引 1, 3, 5, 7, ... 所以它应该是fOdd

真的sOdd 应该是sLowersEven 应该是sUpper,因为它们分别对应于频谱的0:s/2s/2:s-1 元素:

sLower = FFT(fEven, sH, inverse); // fEven is 0, 2, 4, ...
sUpper = FFT(fOdd, sH, inverse); // fOdd is 1, 3, 5, ...

那么蝴蝶就变成了:

for (int i = 0; i < sH; i++) {
    double* sL1 = sLower[i];
    double* sU1 = Mul(o, sUpper[i]);

    spectr[i] = Sum(sL1, sU1);
    spectr[i + sH] = Dif(sL1, sU1);

    o = Mul(o, oBase);
}

这样写更容易与pseudocode example on wikipedia比较。

@Dai 是正确的,你会泄漏大量内存

【讨论】:

  • 我优化了内存:.................... for (int i = 0; i &lt; sH; i++) { double* sL1 = sLower[i]; double* sU1 = Mul(o, sUpper[i]); spectr[i] = Sum(sL1, sU1); spectr[i + sH] = Dif(sL1, sU1); double*o1 = Mul(o, oBase); delete[] o; o = o1; delete[] sL1; delete[] sU1; delete[] sUpper[i]; } delete[] sLower; delete[] sUpper; delete[] oBase; delete[] o; return spectr;
  • 您可能仍然有内存问题,因为我猜您的 MulSumDif 函数以及 new 中包含 new fOddfEven。如果您注意操作的顺序,实际上可以进行 FFT 的数学运算。至少对于代码中的每个新内容都应该删除
【解决方案2】:

关于内存,您可以使用std::vector 封装动态分配的数组,并确保在执行离开范围时释放它们。您可以使用 unique_ptr&lt;double[]&gt;,但性能提升并不值得 IMO,而且您失去了 at() 方法的安全性。

(基于@Robb 的回答)

其他一些提示:

  • 避免使用神秘的标识符 - 程序应该是可读的,“f”和“s”这样的名称会使您的程序更难阅读和维护。
  • 基于类型的匈牙利表示法不受欢迎,因为现代编辑器会自动显示类型信息,因此会给标识符名称添加不必要的复杂性。
  • 对索引使用size_t,而不是int
  • STL 是您的朋友,使用它!
  • 通过使用const 防止只读数据的意外突变,提前预防错误。

像这样:

#include <vector>

using namespace std;

vector<double> fastFourierTransform(const vector<double> signal, const bool inverse) {

    if( signal.size() < 2 ) return signal;
    const size_t half = signal.size() / 2;

    vector<double> lower; lower.reserve( half );
    vector<double> upper; upper.reserve( half );

    bool isEven = true;
    for( size_t i = 0; i < signal.size(); i++ ) {
        if( isEven ) lower.push_back( signal.at( i ) );
        else         upper.push_back( signal.at( i ) );

        isEven = !isEven;
    }

    vector<double> lowerFft = fastFourierTransform( lower, inverse );
    vector<double> upperFft = fastFourierTransform( upper, inverse );

    vector<double> result;
    result.reserve( signal.size() );

    double arg = ( inverse ? 1 : -1 ) * ( DoublePI / signal.size() );

    // Ideally these should be local `double` values passed directly into `Mul`.
    unique_ptr<double[]> oBase = make_unique<double[]>( 2 );
    oBase[0] = cos(arg);
    oBase[1] = sin(arg);

    unique_ptr<double[]> o = make_unique<double[]>( 2 );
    o[0] = 0;
    o[1] = 0;

    for( size_t i = 0; i < half; i++ ) {

        double* lower1 = lower.at( i );
        double* upper1 = Mul( o, upper.at( i ) );

        result.at( i        ) = Sum( lower1, upper1 );
        result.at( i + half ) = Dif( lower1, upper1 );

        o = Mul( o, oBase );
    }

    // My knowledge of move-semantics of STL containers is a bit rusty - so there's probably a better way to return the output 'result' vector.
    return result;
}

【讨论】:

  • 我使用 vector> 代替 complex 和 complex 代替 unique_ptr。那么我不需要像 Mul、Sum、Dif 这样的函数。但速度比双倍**慢 4 倍(~1000ms vs ~250ms 或 ~52ms vs ~13ms)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-02
  • 2020-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-01
  • 1970-01-01
相关资源
最近更新 更多