Trasformazione rapida del coseno tramite FFT


15

Voglio implementare la Fast Cosine Transform. Ho letto su Wikipedia , che esiste una versione veloce del DCT che è calcolata in modo simile alla FFT. Ho provato a leggere il citato documento Makhoul *, per le implementazioni FTPACK e FFTW utilizzate anche in Scipy , ma non sono riuscito a estrarre l'algoritmo effettivamente. Questo è quello che ho finora:

Codice FFT:

def fft(x):
    if x.size ==1:
        return x
    N = x.size
    x0 = my_fft(x[0:N:2])
    x1 = my_fft(x[0+1:N:2])
    k = numpy.arange(N/2)
    e = numpy.exp(-2j*numpy.pi*k/N)
    l = x0 + x1 * e
    r = x0 - x1 * e  
    return numpy.hstack([l,r])

Codice DCT:

def dct(x):
    k = 0
    N = x.size
    xk = numpy.zeros(N)
    for k in range(N):     
        for n in range(N):
            xn = x[n]
            xk[k] += xn*numpy.cos(numpy.pi/N*(n+1/2.0)*k)
    return xk 

Prova FCT:

def my_fct(x):
    if x.size ==1:
        return x
    N = x.size
    x0 = my_fct(x[0:N:2]) # have to be set to zero?
    x1 = my_fct(x[0+1:N:2])
    k = numpy.arange(N/2)
    n = # ???
    c = numpy.cos(numpy.pi/N*(n+1/2.0)*k)
    l = x0 #???
    r = x0 #???
    return numpy.hstack([l,r])

* J. Makhoul, "Una rapida trasformazione del coseno in una e due dimensioni", IEEE Trans. Acoust. Discorso Sig. Proc. 28 (1), 27-34 (1980).


2
Stai chiedendo se il tuo codice DCT è corretto o qualcosa del genere?
Jim Clay,

Grazie per i vostri commenti. Ho aggiunto un'altra frase all'inizio. Il mio obiettivo è implementare la FCT sulla base della FFT.
Framester,

Risposte:


18

Ho letto su questo e ci sono molti modi per farlo, usando diverse dimensioni N. Il mio Matlab è arrugginito, quindi eccoli in Python ( Nè la lunghezza del segnale di input x, kè arange(N)= ):[0,1,2,...,N-1]

Tipo 2 DCT utilizzando 4N FFT e senza turni

Il segnale [a, b, c, d]diventa

[0, a, 0, b, 0, c, 0, d, 0, d, 0, c, 0, b, 0, a].

Quindi prendere la FFT per ottenere lo spettro

[A, B, C, D, 0, -D, -C, -B, -A, -B, -C, -D, 0, D, C, B]

poi butta via tutto tranne il primo [A, B, C, D]e il gioco è fatto:

u = zeros(4 * N)
u[1:2*N:2] = x
u[2*N+1::2] = x[::-1]

U = fft(u)[:N]
return U.real

Tipo 2 DCT utilizzando 2N FFT con mirroring (Makhoul)

[a, b, c, d]diventa [a, b, c, d, d, c, b, a]. Prendi la FFT di quello per ottenere [A, B, C, D, 0, D*, C*, B*], poi butta via tutto ma [A, B, C, D]e moltiplicalo per ( spostamento di mezzo campione ) per ottenere il DCT:e-jπK2N

y = empty(2*N)
y[:N] = x
y[N:] = x[::-1]

Y = fft(y)[:N]

Y *= exp(-1j*pi*k/(2*N))
return Y.real

Tipo 2 DCT con imbottitura FFT 2N (Makhoul)

[a, b, c, d]diventa [a, b, c, d, 0, 0, 0, 0]. Prendi la FFT di quello per ottenere [A, B, C, D, E, D*, C*, B*], poi butta via tutto ma [A, B, C, D]e moltiplicalo per per ottenere il DCT:2e-jπK2N

y = zeros(2*N)
y[:N] = x

Y = fft(y)[:N]

Y *= 2 * exp(-1j*pi*k/(2*N))
return Y.real

Tipo 2 DCT usando N FFT (Makhoul)

Il segnale [a, b, c, d, e, f]diventa [a, c, e, f, d, b], quindi prendi la FFT per ottenere [A, B, C, D, C*, B*]. Moltiplicare per e quindi prendere la parte reale:2e-jπK2N

v = empty_like(x)
v[:(N-1)//2+1] = x[::2]

if N % 2: # odd length
    v[(N-1)//2+1:] = x[-2::-2]
else: # even length
    v[(N-1)//2+1:] = x[::-2]

V = fft(v)

V *= 2 * exp(-1j*pi*k/(2*N))
return V.real

Sulla mia macchina, queste hanno all'incirca la stessa velocità, poiché la generazione exp(-1j*pi*k/(2*N))richiede più tempo della FFT. : D

In [99]: timeit dct2_4nfft(a)
10 loops, best of 3: 23.6 ms per loop

In [100]: timeit dct2_2nfft_1(a)
10 loops, best of 3: 20.1 ms per loop

In [101]: timeit dct2_2nfft_2(a)
10 loops, best of 3: 20.8 ms per loop

In [102]: timeit dct2_nfft(a)
100 loops, best of 3: 16.4 ms per loop

In [103]: timeit scipy.fftpack.dct(a, 2)
100 loops, best of 3: 3 ms per loop

2
Ottima risposta, mi ha aiutato molto con la mia implementazione! Nota aggiuntiva: l'ultimo metodo "Tipo 2 DCT utilizzando N FFT" funziona ancora correttamente se la lunghezza del segnale è dispari; l'ultimo elemento si sposta sull'elemento centrale. Ho verificato la matematica e il codice per questo fatto.
Nayuki,

1
@Nayuki Stai generando exp(-1j*pi*k/(2*N))o c'è un collegamento a quel passaggio?
endolith,

Sto generando exp(-1j*pi*k/(2*N))nel mio codice , perché è necessario uno spostamento di un quarto di campione per far funzionare la mappatura da DCT a DFT. Cosa ti fa chiedere?
Nayuki,

Ciao, come funzionerebbe per il DCT di tipo III, per calcolare l'inverso del DCT-II?
Jack H,

8

X(n)

permettere

y(n)={X(n),n=0,1,...,N-1X(2N-1-n),n=N,N+1,...,2N-1

Il DCT viene quindi fornito da

C(K)=Re{e-jπK2NFFT{y(n)}}

2Ny(n)X(n)X(n)

Ecco il codice in MATLAB.

function C = fdct(x)
    N = length(x);
    y = zeros(1,2*N);
    y(1:N) = x;
    y(N+1:2*N) = fliplr(x);
    Y = fft(y);
    k=0:N-1;
    C = real(exp(-j.* pi.*k./(2*N)).*Y(1:N));

Modificare:

Nota: la formula DCT che sta utilizzando è:

C(K)=2Σn=0N-1X(n)cos(πK2N(2n+1))

Esistono diversi modi per ridimensionare la somma, quindi potrebbe non corrispondere esattamente ad altre implementazioni. Ad esempio, MATLAB utilizza:

C(K)=w(K)Σn=0N-1X(n)cos(πK2N(2n+1))

w(0)=1Nw(1 ...N-1)=2N

Puoi renderlo conto ridimensionando correttamente l'output.


1
y (n) dovrebbe essere di lunghezza N, non di lunghezza 2N. Ecco come ottenere la velocità di calcolo 4x calcolando il DCT di lunghezza N dal segnale di lunghezza N anziché il segnale FFT 2N dal segnale 2N. fourier.eng.hmc.edu/e161/lectures/dct/node2.html www-ee.uta.edu/dip/Courses/EE5355/Discrete%20class%201.pdf
endolith

0

Per un vero calcolo scientifico, anche la quantità di memoria utilizzata è importante. Pertanto il punto N FFT è più attraente per me. Ciò è possibile solo a causa della simmetria eremitica del segnale. Il riferimento Makhoul è riportato qui. E in realtà ha l'algoritmo per il calcolo di DCT e IDCT o DCT10 e DCT01.
http://ieeexplore.ieee.org/abstract/document/1163351/

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.