仅使用FFTW转换到频域并返回,结果与信号源完全不同

斯特凡·莫诺夫(Stefan Monov)

在我的测试用例中,我正在使用FFTW只是将图像转换到频域并返回到空间域。但是我最终得到的结果与我刚开始的结果截然不同。

要解决一些问题:

  • 我为每次调用fft()创建FFTW计划ifft()而不是缓存它们的原因是为了简化。那不应该是问题
  • 为了简单起见,在将实值数组传递给fftw之前将其转换为复数数组的原因。我发现这种方法比使用真正复杂的DFT函数更容易出错。

我的代码(包括实用程序功能,宏和类)如下。它使用libcinder(其旧版本)来处理矢量类和图形,但是即使您不了解libcinder,它也将是不言而喻的。请注意代码长度,我已经尽力使它最小化,但是某些实用程序(例如Array2D类)占用了相当大的空间。

main.cpp

#include "precompiled.h"
typedef std::complex<float> Complexf;
using namespace ci;

void createConsole()
{
    AllocConsole();
    std::fstream* fs = new std::fstream("CONOUT$");
    std::cout.rdbuf(fs->rdbuf());
}

#define forxy(image) \
    for(Vec2i p(0, 0); p.x < image.w; p.x++) \
        for(p.y = 0; p.y < image.h; p.y++)
template<class T>
class ArrayDeleter
{
public:
    ArrayDeleter(T* arrayPtr)
    {
        refcountPtr = new int(0);
        (*refcountPtr)++;

        this->arrayPtr = arrayPtr;
    }

    ArrayDeleter(ArrayDeleter const& other)
    {
        arrayPtr = other.arrayPtr;
        refcountPtr = other.refcountPtr;
        (*refcountPtr)++;
    }

    ArrayDeleter const& operator=(ArrayDeleter const& other)
    {
        reduceRefcount();

        arrayPtr = other.arrayPtr;
        refcountPtr = other.refcountPtr;
        (*refcountPtr)++;

        return *this;
    }

    ~ArrayDeleter()
    {
        reduceRefcount();
    }

private:
    void reduceRefcount()
    {
        (*refcountPtr)--;
        if(*refcountPtr == 0)
        {
            delete refcountPtr;
            fftwf_free(arrayPtr);
        }
    }

    int* refcountPtr;
    T* arrayPtr;
};

template<class T>
struct Array2D
{
    T* data;
    int area;
    int w, h;
    ci::Vec2i Size() const { return ci::Vec2i(w, h); }
    ArrayDeleter<T> deleter;

    Array2D(Vec2i s) : deleter(Init(s.x, s.y)) { }
    Array2D() : deleter(Init(0, 0)) { }

    T* begin() { return data; }
    T* end() { return data+w*h; }

    T& operator()(Vec2i const& v) { return data[v.y*w+v.x]; }

private:
    T* Init(int w, int h) {
        data = (T*)fftwf_malloc(w * h * sizeof(T));
        area = w * h;
        this->w = w;
        this->h = h;
        return data;
    }
};

Array2D<Complexf> fft(Array2D<float> in)
{
    Array2D<Complexf> in_complex(in.Size());
    forxy(in)
    {
        in_complex(p) = Complexf(in(p));
    }

    Array2D<Complexf> result(in.Size());

    auto plan = fftwf_plan_dft_2d(in.h, in.w,
        (fftwf_complex*)in_complex.data, (fftwf_complex*)result.data, FFTW_FORWARD, FFTW_MEASURE);
    fftwf_execute(plan);
    auto mul = 1.0f / sqrt((float)result.area);
    forxy(result)
    {
        result(p) *= mul;
    }
    return result;
}

Array2D<float> ifft(Array2D<Complexf> in)
{
    Array2D<Complexf> result(in.Size());
    auto plan = fftwf_plan_dft_2d(in.h, in.w,
        (fftwf_complex*)in.data, (fftwf_complex*)result.data, FFTW_BACKWARD, FFTW_MEASURE);
    fftwf_execute(plan);

    Array2D<float> out_real(in.Size());
    forxy(in)
    {
        out_real(p) = result(p).real();
    }
    auto mul = 1.0f / sqrt((float)out_real.area);
    forxy(out_real)
    {
        out_real(p) *= mul;
    }
    return out_real;
}

gl::Texture uploadTex(Array2D<float> a)
{
    gl::Texture tex(a.w, a.h);
    tex.bind();
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, a.w, a.h, GL_LUMINANCE, GL_FLOAT, a.data);
    return tex;
}

struct SApp : ci::app::AppBasic {
    gl::Texture texSource;
    gl::Texture texbackAndForthed;
    void setup()
    {
        createConsole();

        Array2D<float> source(Vec2i(400, 400));
        forxy(source) {
            float dist = p.distance(source.Size() / 2);
            if(dist < 100)
                source(p) = 1.0f;
            else
                source(p) = 0.0f;
        }
        printSum("source", source);
        texSource = uploadTex(source);
        setWindowSize(source.w, source.h);
        auto backAndForthed = backAndForth(source);
        printSum("backAndForthed", backAndForthed);
        //backAndForthed = backAndForth(loaded);
        texbackAndForthed = uploadTex(backAndForthed);
    }
    void printSum(std::string description, Array2D<float> arr) {
        float sum = std::accumulate(arr.begin(), arr.end(), 0.0f);
        std::cout << "array '" << description << "' has sum " << sum << std::endl;
    }
    void draw()
    {
        gl::clear(Color(0, 0, 0));
        gl::draw(texSource, Rectf(0,0, getWindowWidth() / 2, getWindowHeight() /2));
        gl::draw(texbackAndForthed, Rectf(getWindowWidth() / 2, getWindowWidth() / 2, getWindowWidth(), getWindowHeight()));
    }
    Array2D<float> backAndForth(Array2D<float> in) {
        auto inFd = fft(in);
        auto inResult = ifft(inFd);
        return inResult;
    }
};

CINDER_APP_BASIC(SApp, ci::app::RendererGl)

precompiled.h

#include <complex>
#include <cinder/app/AppBasic.h>
#include <cinder/gl/Texture.h>
#include <cinder/gl/gl.h>
#include <cinder/Vector.h>
#include <fftw3.h>
#include <numeric>

控制台输出:

array 'source' has sum 31397
array 'backAndForthed' has sum -0.110077

图形输出:

截屏

如您所见,右下角的圆圈有点暗和渐变。

注意:如果取消注释第二backAndForthed = backAndForth(loaded);行,则结果是正确的(因此,仅在第一次时结果是错误的)。

弗朗西斯

问题在这里:

auto plan = fftwf_plan_dft_2d(in.h, in.w,
    (fftwf_complex*)in_complex.data, (fftwf_complex*)result.data,
     FFTW_FORWARD, FFTW_MEASURE);
fftwf_execute(plan);

那里:

auto plan = fftwf_plan_dft_2d(in.h, in.w,
    (fftwf_complex*)in.data, (fftwf_complex*)result.data,
    FFTW_BACKWARD, FFTW_MEASURE);
fftwf_execute(plan);

使用该标志FFTW_MEASURE意味着FFTW尝试许多DFT算法来选择最快的算法。问题是输入数组in.data在途中被覆盖。因此,之后fftwf_execute(plan);result.data是不是的DFT的in.data,因为它是建立在计划之前。根据FFTW在计划程序标志上的文档

要点:计划者在计划期间将覆盖输入数组,除非已保存的计划(请参见Wisdom)可用于该问题,因此您应在创建计划后初始化输入数据。唯一的例外是FFTW_ESTIMATEandFFTW_WISDOM_ONLY标志,如下所述。

该文档提供了一种解决方案:使用标志FFTW_ESTIMATE可确保在计划期间不会覆盖输入/输出数组。

在这种特定情况下,使用FFTW_ESTIMATE代替FFTW_MEASURE不会触发计算时间的大幅增加。实际上,由于在计划创建期间会计算出许多DFT,因此fftw_plan使用的创建要比使用FFTW_MEASURE的创建慢得多FFTW_ESTIMATE但是,如果要执行许多相同大小的DFT,则必须为所有使用FFTW_MEASURE和存储创建一次计划如有必要,可以应用新数组执行功能

我的猜测是,您已经意识到r2c和c2r转换,旨在将存储空间和计算时间减少近2倍。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

SimpleDateFormat的.format()给出了Java8不同的结果与切换到java11

“ git show”返回的结果与“ git diff”不同

不同版本的sklearn提供了完全不同的训练结果

Buffer.byteLength返回完全不同的字节长度

ResolveMethod()返回完全不同的方法

完全相同的代码在不同设备上获得完全不同的Tensorboard结果

如何使用.replace()和.substring()修复完全不同的输出?

bash中的regex返回的结果与ruby不同

Tensorflow:来自相同初始猜测的完全不同的结果

如何在完全不同的函数中使用函数的返回值

如何使用Random使Java中生成的数字完全不同?

为什么在使用FFTW将(2d)IDFT转换为DFT时,频域中的数据会“镜像”?

为什么此CustomExtract返回的结果与默认的Extract不同?

相同的表达式产生完全不同的结果

Google Geocoding返回的结果与Google Map完全不同

PC通过HDMI连接到A / V接收器-切换接收器上的信号源时出现问题

为什么MATCH AGAINST返回的结果与LIKE不同?

投影机1080p图像的大小取决于信号源

Surface耳机作为A2DP接收器和A2DP信号源

什么是10位显示器,所有信号源都以8位格式保存?

如果没有将任何脉冲插入音频端口,Pulseaudio将无法识别接收器/信号源

从函数返回完全不同的数据类型

PHP 搜索显示与查询完全不同的重复结果

为什么分类器的 score 函数返回的结果与 sklearn 中的 cross_val_score 函数完全不同?

余弦相似性和使用相同来源的完全不同的结果

DOATools.py - 使用我自己的信号源(未生成)

为什么 Octave conv() 给出的结果与两个信号的手动卷积不同?

为什么 count 给出的结果与仅使用 order by 子句的窗口函数中的 row_num 不同?

Mysql 内联查询返回的结果与使用会话变量的查询不同