AAN Discrete Cosine Transform [Paper Implementation]
Implementing the Ara Agui Nakajima DCT compression algorithm
Quick intro
LeetArxiv is Leetcode for implementing Arxiv papers. We offer weekly, hands-on, step-by-step coding guides to programmers who want to transition into careers in research.
*We provide a C implementation that can be translated to other languages.
1.0 Introduction
The paper, A Summary of the AAN Discrete Cosine Transform, summarizes the Discrete Cosine Transform optimizations introduced in the 1981 paper, A fast DCT-SQ Scheme for Images.
We suggest opening the paper link in a separate tab while reading the walkthrough.
The summary is 2 pages long. Page 1 introduces the basic Type-II Discrete Cosine Transform in Natarajan and Rao(1974)1. Page 2 introduces optimizations to the DCT in Ara Agui, A., and Nakajima, M. (1981)2.
2.0 The Basic Discrete Cosine Transform
This section introduces the type-II DCT used for JPEG image compression. The author states the DCT’s desirable properties:
Energy Compaction : Most of the signal energy is concentrated in the first few coefficients, making it ideal for compression.
Symmetry : The cosine basis functions exhibit symmetry, which simplifies computation.
Real-valued Transform : Unlike Fourier Transforms, the DCT-II does not involve complex numbers, reducing computational overhead.
2.1 The Original DCT-II Equation
The author further introduces the DCT-II equation :
where
𝛼 (𝑘) is a scaling factor that ensures the transform is orthogonal and energy preserving.
In C, the equation can be written as:
#include <stdio.h>
#include <math.h>
#define N 8 // Define the size of the DCT (can be changed)
void dct_ii(double x[], double X[])
{
double factor = sqrt(2.0 / N);
for (int k = 0; k < N; k++)
{
double sum = 0.0;
for (int n = 0; n < N; n++) {
sum += x[n] * cos(M_PI / N * (n + 0.5) * k);
}
X[k] = factor * sum;
if (k == 0) {
X[k] /= sqrt(2.0); // Scaling for k = 0
}
}
}
void print_array(double arr[], int size)
{
for (int i = 0; i < size; i++) {
printf("%f ", arr[i]);
}
printf("\n");
}
int main()
{
double x[N] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}; // Example input
double X[N];
dct_ii(x, X);
printf("DCT-II Coefficients:\n");
print_array(X, N);
return 0;
}
2.2. The AAN DCT-II Equation Optimization
On page 2, the author introduces the optimizations performed by Ara Agui and Nakajima on the 8-point DCT. This is a version of the Discrete Cosine Transform that takes 8 inputs.
The flowgraph above showcases these DCT optimizations :
Removing expensive cosine calculations.
Exploiting Symmetry: Symmetrical properties of the cosine basis functions reduce the need for repetitive calculations.
Precomputed Constants: The AAN method uses precomputed constants, simplifying the flowgraph and further reducing complexity.
Parallelism: Flowgraphs highlight opportunities for parallel computation, improving performance in hardware implementations.
2.2.1. Interpreting the AAN Flowgraph
Dots (○ and ●):
Represent the data points at different stages of the transformation.
The leftmost dots (f(n)) are the input values.
The rightmost dots (F(k)) are the DCT coefficients (output values).
Arrows (→ and ↘):
Indicate the flow of data between stages. The forward step goes from left to right. The Inverse DCT is from right to left.
Some connections represent direct passthrough, while others involve combinations of values.
Boxes (a₁, a₂, etc.):
Represent multiplication constants used to scale certain values during computation.
Coding the 1-D AAN DCT
This code is from the Unix4Lyfe blogpost on the AAN DCT3.
// https://unix4lyfe.org/dct-1d/
#include <cmath>
void aan_dct(const double i[], double o[]) {
#if 1
const double a1 = sqrt(.5);
const double a2 = sqrt(2.) * cos(3./16. * 2 * M_PI);
const double a3 = a1;
const double a4 = sqrt(2.) * cos(1./16. * 2 * M_PI);
const double a5 = cos(3./16. * 2 * M_PI);
#else
const double a1 = 0.707;
const double a2 = 0.541;
const double a3 = 0.707;
const double a4 = 1.307;
const double a5 = 0.383;
#endif
double b0 = i[0] + i[7];
double b1 = i[1] + i[6];
double b2 = i[2] + i[5];
double b3 = i[3] + i[4];
double b4 =-i[4] + i[3];
double b5 =-i[5] + i[2];
double b6 =-i[6] + i[1];
double b7 =-i[7] + i[0];
double c0 = b0 + b3;
double c1 = b1 + b2;
double c2 =-b2 + b1;
double c3 =-b3 + b0;
double c4 =-b4 - b5;
double c5 = b5 + b6;
double c6 = b6 + b7;
double c7 = b7;
double d0 = c0 + c1;
double d1 =-c1 + c0;
double d2 = c2 + c3;
double d3 = c3;
double d4 = c4;
double d5 = c5;
double d6 = c6;
double d7 = c7;
double d8 = (d4 + d6) * a5;
double e0 = d0;
double e1 = d1;
double e2 = d2 * a1;
double e3 = d3;
double e4 = -d4 * a2 - d8;
double e5 = d5 * a3;
double e6 = d6 * a4 - d8;
double e7 = d7;
double f0 = e0;
double f1 = e1;
double f2 = e2 + e3;
double f3 = e3 - e2;
double f4 = e4;
double f5 = e5 + e7;
double f6 = e6;
double f7 = e7 - e5;
double g0 = f0;
double g1 = f1;
double g2 = f2;
double g3 = f3;
double g4 = f4 + f7;
double g5 = f5 + f6;
double g6 = -f6 + f5;
double g7 = f7 - f4;
#if 1
const double s0 = (cos(0)*sqrt(.5)/2)/(1 ); // 0.353553
const double s1 = (cos(1.*M_PI/16)/2)/(-a5+a4+1); // 0.254898
const double s2 = (cos(2.*M_PI/16)/2)/(a1+1 ); // 0.270598
const double s3 = (cos(3.*M_PI/16)/2)/(a5+1 ); // 0.300672
const double s4 = s0; // (cos(4.*M_PI/16)/2)/(1 );
const double s5 = (cos(5.*M_PI/16)/2)/(1-a5 ); // 0.449988
const double s6 = (cos(6.*M_PI/16)/2)/(1-a1 ); // 0.653281
const double s7 = (cos(7.*M_PI/16)/2)/(a5-a4+1 ); // 1.281458
#else
const double s0 = 1.;
const double s1 = 1.;
const double s2 = 1.;
const double s3 = 1.;
const double s4 = 1.;
const double s5 = 1.;
const double s6 = 1.;
const double s7 = 1.;
#endif
o[0] = g0 * s0;
o[4] = g1 * s4;
o[2] = g2 * s2;
o[6] = g3 * s6;
o[5] = g4 * s5;
o[1] = g5 * s1;
o[7] = g6 * s7;
o[3] = g7 * s3;
}
Let me know if you want to see a 2D version and it’s inverse.
Citations
Ahmed, N., Natarajan, T., and Rao, K. R. (1974). Discrete Cosine Transform. IEEE Transactions on Computers, 23(1), 90–93. doi:10.1109/T-C.1974.223785.
Ara Agui, A., and Nakajima, M. (1981). A fast DCT-SQ scheme for images. IEEE Transactions on Communications, 29(4), 535–542. doi: 10.1109/TCOM.1981.1095360