Почему я не могу получить рабочий двухмерный БПФ в Visual Studio 2013 с помощью FFTW или AMPFFT?

Я работал с 2D-БПФ в своем проекте и не смог получить правильные результаты, используя две разные библиотеки БПФ. Сначала я предположил, что использую их неправильно, но после сравнения с эталонными реализациями MATLAB и Linux GCC теперь кажется, что с моим компилятором происходит что-то зловещее (MSVC 2013 Express).

Мой тестовый пример выглядит следующим образом: комплекс 256x256 для реального IFFT, с одним бином на 255 (0,255 для обозначения X, Y), установленным на 10000.

Используя AMPFFT, я получаю следующее 2D-преобразование:

Пример неработающего AMPFFT

И с FFTW я получаю следующее 2D-преобразование:

Неработающий пример FFTW

Как видите, версия AMPFFT "почти" правильная, но в ней есть странная полоса каждого семпла, а версия FFTW просто повсюду и до обеда.

Я взял результаты двух разных тестовых версий и сравнил их с MATLAB (технически октавным, который использует FFTW под капотом). Я также провел тот же тест для FFTW под Linux с GCC. Вот фрагмент из этого набора тестов 127-й строки (номер строки технически не имеет значения, так как при моем выборе бинов все строки должны быть одинаковыми):

Сравнения БПФ

В этом примере реализации для октавы и Linux представляют правильный результат и следуют красной линии (октава была нанесена черным цветом, Linux — красным, и она полностью совпадала с октавой). FFTW в MSVC отображается синим цветом, а результат AMP FFT отображается пурпурным цветом. Как видите, снова версия AMPFFT кажется почти близкой, но в ней есть эта странная высокочастотная пульсация, а FFTW под MSVC — просто беспорядок с этим странным «пакетным» видом.

На данном этапе я могу только указать пальцем на Visual Studio, но понятия не имею, что происходит и как это исправить.

Вот две мои тестовые программы:

Тест FFTW:

//fftwtest.cpp
//2 dimensional complex-to-real inverse FFT test.
//Produces a 256 x 256 real-valued matrix that is loadable by octave/MATLAB
#include <fstream>
#include <iostream>
#include <complex>
#include <fftw3.h>

int main(int argc, char** argv)
{
    int FFTSIZE = 256;

    std::complex<double>* cpxArray;
    std::complex<double>* fftOut;

   // cpxArray = new std::complex<double>[FFTSIZE * FFTSIZE];
    //fftOut = new double[FFTSIZE * FFTSIZE];
    fftOut = (std::complex<double>*)fftw_alloc_complex(FFTSIZE*FFTSIZE);
    cpxArray = (std::complex<double>*)fftw_alloc_complex(FFTSIZE * FFTSIZE);

    for(int i = 0; i < FFTSIZE * FFTSIZE; i++) cpxArray[i] = 0;
    cpxArray[255] = std::complex<double>(10000, 0);
    fftw_plan p = fftw_plan_dft_2d(FFTSIZE, FFTSIZE, (fftw_complex*)cpxArray, (fftw_complex*)fftOut, FFTW_BACKWARD, FFTW_DESTROY_INPUT | FFTW_ESTIMATE);
    fftw_execute(p);

    std::ofstream debugDump("debugdumpFFTW.txt");
    for(int j = 0; j < FFTSIZE; j++)
    {
        for(int i = 0; i < FFTSIZE; i++)
        {
            debugDump << " " << fftOut[j * FFTSIZE + i].real();
        }
        debugDump << std::endl;
    }
    debugDump.close();

}

Тест AMPFFT:

//ampffttest.cpp
//2 dimensional complex-to-real inverse FFT test.
//Produces a 256 x 256 real-valued matrix that is loadable by octave/MATLAB
#include <amp_fft.h>
#include <fstream>
#include <iostream>



int main(int argc, char** argv)
{
    int FFTSIZE = 256;

    std::complex<float>* cpxArray;
    float* fftOut;

    cpxArray = new std::complex<float>[FFTSIZE * FFTSIZE];
    fftOut = new float[FFTSIZE * FFTSIZE];

    for(size_t i = 0; i < FFTSIZE * FFTSIZE; i++) cpxArray[i] = 0;
    cpxArray[255] = std::complex<float>(10000, 0);

    concurrency::extent<2> e(FFTSIZE, FFTSIZE);
    std::cout << "E[0]: " << e[0] << " E[1]: " << e[1] << std::endl;

    fft<float, 2> m_fft(e);
    concurrency::array<float, 2> outpArray(concurrency::extent<2>(FFTSIZE, FFTSIZE));
    concurrency::array<std::complex<float>, 2> inpArray(concurrency::extent<2>(FFTSIZE, FFTSIZE), cpxArray);


    m_fft.inverse_transform(inpArray, outpArray);
    std::vector<float> outVec = outpArray;

    std::copy(outVec.begin(), outVec.end(), fftOut);

    std::ofstream debugDump("debugdump.txt");
    for(int j = 0; j < FFTSIZE; j++)
    {
        for(int i = 0; i < FFTSIZE; i++)
        {
            debugDump << " " << fftOut[j * FFTSIZE + i];
        }
        debugDump << std::endl;
    }
}

Оба они были скомпилированы со стандартными настройками MSVC 2013 для консольного приложения win32, а тест FFTW также выполнялся под Centos 6.4 с GCC 4.4.7. В обоих тестах FFTW используется версия FFTW 3.3.4, кроме того, были протестированы планы от сложного к реальному и от сложного к сложному (с идентичными результатами).

Есть ли у кого-нибудь хоть малейшее представление о настройках компилятора Visual Studio, я мог бы попытаться решить эту проблему?


person stix    schedule 12.06.2014    source источник
comment
Не знаю, связано ли это, но жонглирование типами fftw_complex и std::complex<double> выглядит сомнительно. Вы не используете какие-либо функции std::complex. Лучше придерживайтесь родного типа вашей библиотеки.   -  person n. 1.8e9-where's-my-share m.    schedule 12.06.2014
comment
В вашем коде AMP я заметил, что предполагаемый буфер вывода (fftOut) используется для инициализации источника ввода (inpArray) для обратного преобразования, как в следующем определении: concurrency::array<std::complex<float>, 2> inpArray(concurrency::extent<2>(FFTSIZE, FFTSIZE), fftOut); это приведет к неопределенному поведению, поскольку буфер fftOut не заполнен допустимыми значениями и его размер составляет только половину inpArray. Я думаю, что cpxArray следует использовать для инициализации inpArray. Можете ли вы попробовать это и посмотреть, решит ли это вашу проблему?   -  person Lukasz Mendakiewicz    schedule 12.06.2014
comment
Упс! Да, это сломано, но это не решает проблему. Я случайно загрузил старую версию теста с ошибкой; Я упрощал код проекта, где я вижу проблему, и случайно изменил имя переменной inpArray на неправильный массив. Я исправил тест AMPSFFT, чтобы использовать правильный буфер.   -  person stix    schedule 12.06.2014


Ответы (1)


Глядя на выходные данные MSVC FFTW, выделенные синим цветом, кажется, что это число, умноженное на синусы. Другими словами, есть синус с периодом 64 или около того, и синус с периодом 4, и, возможно, еще один с такой же частотой для создания биений.

В основном это означает, что версия MSVC имела как минимум два ненулевых входа. Я подозреваю, что причиной является приведение типов, когда вы пишете объект fftw_complex через тип std::complex<double>.

person MSalters    schedule 12.06.2014
comment
Как упоминалось в этом сообщении: stackoverflow .com/questions/4214400/ Тип fftw_complex предполагается совместим по битам с std::complex. Если бы версия MSVC имела два ненулевых входа, выход был бы комбинацией двух базовых функций, но на графике нет никаких свидетельств правильной базисной функции (та, что в ячейке 255). Это также не объясняет, что происходит с тестовым примером AMPFFT, который предположительно должен быть наиболее совместимым с MSVC, и почему случай FFTW работает в GCC в Linux. - person stix; 12.06.2014
comment
@stix: Похоже, что MSVC действительно имеет 2 бина, для которых неправильно установлены ненулевые значения, и один (255) неправильно установлен для нуля. Ясно, что запись идет не туда, куда, по вашему мнению, она должна идти. Я виню указатели в неудачах. В вашем примере AMPFFT есть еще одна проблема: cpxArray доступен только для записи. - person MSalters; 12.06.2014