傅立叶变换浮点问题

时间:2013-12-12 18:46:54

标签: c++ math floating-point fft

我正在实现传统(意味着快速),分离图像的傅立叶变换。我知道在浮点中,等间距样本中一个sin或cos周期的总和完全为零,并且这对于常规变换而言比使用快速变换更为严重。

该算法适用于2D双阵列并且是正确的。内部完成反转(在使用非对称公式时通过双符号标记和条件检查),而不是在结合之外。结果几乎100%像预期的那样,所以它是一个关于细节的问题:

当我执行正向变换时,将对数幅度和角度保存到图像,重新加载它们并进行逆变换,我会遇到不同类型的舍入误差和不同类型的实现公式:

  1. F(u,v)= Sum(x = 0-> M-1)Sum(y = 0-> N-1)f(x,y)* e ^( - i * 2 * pi * u * x / M)* e ^( - i * 2 * pi * v * y / N)

    f(x,y)= 1 / M * N *(如上所述)

  2. F(u,v)= 1 / sqrt(M * N)*(如上所述)

    f(x,y)= 1 / sqrt(M * N)*(如上所述)

  3. 所以第一个是非对称变换对,第二个是对称变换对。对于不对称对,舍入误差在图像的亮点中更多(一些像素在值范围外略微舍入(例如256))。对于对称对,误差更多地出现在图像的恒定中间区域( no 超出值范围!)。总的来说,对称对似乎会产生更多的舍入误差。

    然后,它还取决于输入:当存储在[0,255]中的图像时,舍入误差不是[0,1]中的。

    所以我的问题是:如何实现最优,最准确的算法(理论上,没有代码):非对称/对称对? [0,255]或[0,1]中的输入值范围?在将对数的一个保存到文件之前,如何线性升级结果?

    修改

    我的算法只是计算分离的非对称或对称DFT公式。使用欧拉身份将因子分解为实部和虚部,然后将它们分别作为实部和虚部进行扩展和处理:

    sum_re += f_re * cos(-mode*pi*((2.0*v*y)/N)) - // mode = 1 for forward, -1
              f_im * sin(-mode*pi*((2.0*v*y)/N));  // for inverse transform
    // sum_im permutated in the known way and + instead of -
    

    将这个值组合在一起的内部cos和sin应该在我的眼中给出最低的舍入误差(与例如cos(-mode*2*pi*v*y/N)相比),因为没有多次乘法/除数显着错误的舍入超过pi几次,但只有一次。不是吗?

    比例因子1/M*N1/sqrt(M*N)在最内部总和的每次分离后单独应用。内心更好?或者在两次分离结束时完全合并?

    对于更深入的分析,我已退出input->transform->save-to-file->read-from-file->transform^-1->output工作流程,并选择直接以双精度进行比较:input->transform->transform^-1->output

    这里是现实生活704x528 8位图像的结果(delta =输入和输出实部之间的最大绝对差值):

    • 输入[0,1]和不对称公式:delta = 2.6609e-13(对应[0,255]范围的6.785295e-11)。
    • 输入insde [0,1]和对称公式:delta = 2.65232e-13(对应于[0,255]范围的6.763416e-11)。
    • 输入[0,255]和不对称公式:delta = 6.74731e-11。
    • 输入[0,255],对称公式:delta = 6.7871e-11。

    这些并不是真正显着的差异,但是,具有非对称变换的全范围输入表现最佳。我认为16位输入值可能会变差。

    但总的来说,我看到,我经验丰富的问题​​更多是因为在实际转换舍入错误之前,存档前(或反向)舍入错误的缩放比例。

    然而,我很好奇:傅立叶变换最常用的实现是什么:对称还是非对称?通常使用哪个值范围输入:[0,1]或[0,255]?并且通常以对数标度显示的光谱:例如[0,1]输入的非对称变换后的[0,M * N]直接对数缩放到[0,255]或在线性缩放到[0,255 * M * N]之前?

2 个答案:

答案 0 :(得分:0)

缩放会导致舍入错误。因此,解决方案1(其缩放一次)优于解决方案2(其执行两次)。类似地,在求和之后缩放一次比在求和之前缩放所有内容更好。

您从y0或从2*N-N运行+N吗?在数学上它是相同的,但在后一种情况下你有一点额外的精确度。

BTW,modecos(-mode * stuff)做了什么?

答案 1 :(得分:0)

您报告的错误很少,很正常,通常可以忽略。只需缩放结果并将目标间隔之外的任何结果钳制到端点。

在FFT的库实现中(即,编写为通常由不同应用程序使用的FFT例程,而不是为单个应用程序定制的),很少考虑缩放;例程通常简单地返回由算术自然缩放的数据,而不使用额外的乘法运算来调整比例。这是因为比例通常与应用无关(例如,无论比例如何,找到具有最大能量的频率)或者比例可以通过乘法运算分布并且仅执行一次(例如,而不是缩放)在正向变换和逆变换中,应用程序可以通过仅显式缩放一次来获得相同的效果。因此,由于通常不需要缩放,因此将其包含在库例程中是没有意义的。

数据缩放到的目标间隔取决于应用程序。

关于使用什么变换(对数或线性)来显示光谱的问题,我不能建议;我没有使用可视化光谱。