8.12.2. MINFFT-02 — DCT Basics
The Discrete Cosine Transform (DCT-II) concentrates a signal’s energy into a few low-frequency coefficients. Lossy codecs exploit this: keep the big low coefficients, throw away the small high ones. This tutorial covers the 1D DCT; the next one builds JPEG on top of it.
8.12.2.1. Plans
A plan holds the twiddle tables for a given length. Build it once and reuse it
for every transform of that length — building allocates, so reuse matters in a
loop. The same plan drives both dct (forward, DCT-II) and idct
(inverse, DCT-III). Free it with delete (it is a managed handle):
var plan = make_dct_plan_1d(8)
// ... dct / idct calls ...
unsafe { delete plan }
A plan holds internal scratch state, so it is not thread-safe — build one plan per thread (or serialize access) if you transform in parallel.
8.12.2.2. Forward DCT and energy compaction
A smooth signal collapses to a dominant DC coefficient plus a few small ones:
let signal <- [for (i in range(8)); float(i)] // a ramp 0..7
var coeff : array<float>
dct(signal, coeff, plan)
// coefficients: 56 -25 0 -2 0 0 0 0 -> the DC term dominates
8.12.2.3. Inverse DCT and the 2N factor
Like the FFT, minfft’s DCT is unnormalized: idct(dct(x)) == 2N*x for a
length-N signal. Divide by 2N to recover the original:
var back : array<float>
idct(coeff, back, plan)
// back[i] / float(2 * N) == signal[i]
8.12.2.4. Lossy truncation
Energy compaction in action — zero every coefficient past the first K, then
reconstruct. Because the discarded coefficients were tiny, the error stays
small. This is the essence of lossy coding:
for (k in range(keep, n)) {
trunc[k] = 0.0f
}
idct(trunc, back, plan)
// keep 8/32 coefficients -> max error ~0.16
See also
Full source: tutorials/dasMinfft/02_dct_basics.das
Next tutorial: MINFFT-03 — 2D DCT and JPEG Compression