如何在R中执行* Fast * DCT(离散余弦变换)?

时间:2012-06-26 20:07:32

标签: r fft dct

使用Rprof显示dtt包中的dct是一段运行速度非常慢的R代码中的主要攻击者。在stats包中交换它为fft(这不是相同的转换,但应该花费相同的时间来计算)我的运行时间显着改善。事实上,我的Rprof线路中有2/3是先前的dct呼叫,而且在进行切换后仅有3行约600个fft呼叫。

dtt包中的dct实现是不是使用 fast 离散傅立叶变换完成的?如果是这样,是否有一个包有一个? (我知道可以将数据加倍,然后从那些fft系数中提取dct的系数,但是直接的快速dct肯定会更好,并且确实应该在某个地方)。

3 个答案:

答案 0 :(得分:10)

似乎没有快速dct,但是在stats包中有一个fft(快速傅里叶变换),所以这里是你如何使用fft获得快速dct。

使用此风险需要您自担风险。我没有认真检查它。我在几个不同大小的向量上检查了它,它给出了与包dtt中的函数dct相同的结果。如果有人想通过将它与dct的输出进行比较来仔细检查我,那么随意做所以发布你的结果。

取向量并将其扩展到向量的两倍,如下所示:如果向量是v =(1,2,3),则将条目加倍到w =(1,2,3,3,2,1) )。 注意顺序。如果你的向量是v =(1,2,4,9),那么将条目加倍到w =(1,2,4,9,9,4,2,1)

设N为ORIGINAL向量的长度(在你的长度加倍之前)。

然后是前N个系数 .5 * fft(w)/ exp(复数(虚数= pi / 2 / N)*(seq(2 * N)-1)) 应该同意计算 DCT(五) 除了它几乎在所有情况下都要快得多。

速度考虑因素。如果你推理因子N那么计算快速dct所花费的时间就像为每个素数因子做一个缓慢的dct所花费的时间。因此,如果N是2 ^ K,就像在长度为2的向量上进行K个不同的慢dct变换,因此它的真的快。如果N是素数(最坏的情况)那么根本没有加速。最大的加速是在长度为2的幂的向量上。

注意:上面的R代码看起来非常不友好,所以让我说一下发生了什么。以正确的方式加倍长度后,你得到的fft的前N个系数是几乎正确的事情。然而,系数需要稍微调整一下。设P代表e ^(pi * i / 2 / N)。保留第一个系数。将第二个系数除以P,将第三个除以P ^ 2,将第四个除以P ^ 3等...然后将 all 系数除以2(包括第一个)同意规范化R用于dct。

这个应该给出与在dtt包中使用dct函数相同的功能,但几乎在所有情况下都要快得多。

答案 1 :(得分:1)

免责声明:我从未使用dtt包,无法将结果与结果进行比较。我的答案是关于DCT / FFT的通用。

John的想法是正确的,但他在重复矢量时是两次性的,并且必须通过调整系数(e^(pi * i / 2 / N)因子)来补偿它。通过原始矢量的正确扩展,FFT可以直接产生正确的结果(*)。引用Wikipedia

DCT-I与具有均匀对称性的2N-2实数的DFT完全等效(直到总体比例因子为2)。例如,N = 5实数的DCT-I数字abcde完全相当于8个实数abcdedcb(甚至是对称)的DFT,除以2。

即,如果我们有一个向量v = [1, 2, 3, 4, 5]我们希望在其上执行DCT,我们应该构造一个新的向量w = [1, 2, 3, 4, 5, 4, 3, 2]并对其执行FFT。请注意v的第一个和最后一个组件只在w中按原始顺序出现一次!

这是有效的,因为偶函数的傅立叶变换(函数在零附近对称)纯粹由实数(余弦)系数组成。如果我们通过包含整个反转的w来构造向量v,正如约翰所建议的那样,它将围绕-0.5对称。由于这种微小的移位,傅立叶变换也会产生虚数(正弦)系数。

(*)这里的方法产生DCT-I。约翰的方法似乎是针对DCT-II。

答案 2 :(得分:1)

John和Igor的回答都是正确的,但我认为他们缺少一些示例代码。

library(dtt)

par(mfrow=c(3, 1), mar=c(2, 3, 1, 0.1), mgp=c(2, 0.8, 0))

set.seed(1)
N <- 60
v <- sin(seq(0, pi*2*4, l=N))/2 + 
     sin(seq(0, pi*2*7, l=N))/3 + 
     sin(seq(0, pi*2*15, l=N))/2 + 
     runif(N, -1, 1)/40 +
     runif(N, -1, 1)/40

plot(v, type="o")

DCT-I:

v.dct1 <- dct(v, variant=1)

w <- c(v, v[(N - 1):2])
w.dct1 <- 0.5 * Re(fft(w)[1:N])


plot(v.dct1, type="l", col="#00000088")
abline(h=0, col="#00000044")
lines(w.dct1, col=2, lty=3, lwd=2)
legend("topright", c("dtt::dct", "fft"), bty="n", col=1:2, lwd=1)
legend("topleft", "DCT-I", bty="n")

DCT-II:

v.dct2 <- dct(v, variant=2)

P <- exp(complex(imaginary=pi / 2 / N)*(seq(2*N)-1)) 

w <- c(v, v[N:1])
w.dct2 <- 0.5 * Re(fft(w)[1:N]/P)

plot(v.dct2, type="l", col="#00000088")
abline(h=0, col="#00000044")
lines(w.dct2, col=2, lty=3, lwd=2)
legend("topright", c("dtt::dct", "fft"), bty="n", col=1:2, lwd=1)
legend("topleft", "DCT-II", bty="n")

enter image description here