This Java code demonstrates the core steps of the JPEG compression algorithm. It implements the Discrete Cosine Transform (DCT), quantization, zigzag scan, and a simplified entropy encoding. These are fundamental processes for compressing digital images, reducing file size by transforming and encoding image data.
This example provides an educational look at the key stages of JPEG compression.
package ajpeg;
import java.io.*;
import java.math.*;
public class Ajpeg {
// Input 8x8 pixel block (initialized with sample data)
static double m[][]={{48,39,40,68,60,38,50,121},
{149,82,79,101,113,106,27,62},
{58,63,77,69,124,107,74,125},
{80,97,74,54,59,71,91,66},
{18,34,33,46,64,61,32,37},
{149,108,80,106,116,61,73,92},
{211,233,159,88,107,158,161,109},
{212,104,40,44,71,136,113,66}};
// Array to store the zigzagged quantized coefficients
static int n[] = new int[64];
// Array to store the entropy encoded data (simplified)
static int e1[] = new int[128];
/*
* Flow control of the JPEG compression process
*/
public static void main(String args[])throws IOException
{
// Placeholder for reading input image data (currently not implemented)
getinput();
System.out.println("\n\n\n Input");
display();
// Discrete Cosine Transform
System.out.println("\n\n\n On DCT");
dct();
display();
// Quantization
System.out.println("\n\n\n On Quant");
quant();
display();
// Zigzag scan
zigzag();
// Entropy encoding (simplified)
se1();
}
/*
* Placeholder for getting initial input (reading image data)
*/
public static void getinput() throws IOException
{
// BufferedReader obj = new BufferedReader (new InputStreamReader(System.in));
// System.out.println("Enter input matrix");
// m = new double[8][8];
// for(int i=0;i<8;i++)
// for(int j=0;j<8;j++)
// {
// m[i][j] = Integer.parseInt(obj.readLine());
// }
}
/*
* Helper function to display the 8x8 matrix
*/
public static void display(){
for(int i=0;i<8;i++)
for(int j=0;j<8;j++)
{
System.out.print(m[i][j] + "\t");
if(j==7){ System.out.println();}
}
}
/*
* Implementation of the 2D Discrete Cosine Transform (DCT)
*/
public static void dct()
{
int i,j,u,v;
double res;
double temp[][] = new double[8][8];
// Iterate through all output DCT coefficients (u, v)
for(u=0;u<8;u++)
{
for(v=0;v<8;v++)
{
res = 0;
// Iterate through all input pixel values (i, j)
for(i=0;i<8;i++)
{
for(j=0;j<8;j++)
{
// DCT formula for each coefficient
res = res + (m[i][j]*Math.cos(Math.PI*(2*i+1)*u/16)*Math.cos(Math.PI*(2*j+1)*v/16));
}
}
// Apply scaling factor
res = res * 1/4;
// Apply scaling for DC component (u=0)
if(u==0)
{
res = res / Math.sqrt(2);
}
// Apply scaling for DC component (v=0)
if(v==0)
{
res = res / Math.sqrt(2);
}
// Store the calculated DCT coefficient in a temporary matrix
temp[u][v] = res;
}
}
// Update the original matrix with the DCT coefficients
m = temp;
}
/*
* Implementation of the Quantization step
*/
public static void quant()
{
// Standard luminance quantization table (can be different for chroma)
double qt[][] = {{16,11,12,16,24,40,51,61},
{12,12,14,19,26,58,60,55},
{14,13,16,24,40,57,69,56},
{14,17,22,29,51,87,80,62},
{18,22,37,56,68,109,103,77},
{24,35,55,64,81,104,113,92},
{49,64,78,87,103,121,120,101},
{72,92,95,98,112,110,103,99}};
// Iterate through each DCT coefficient and perform quantization
for(int i=0;i<8;i++)
for(int j=0;j<8;j++){
// Divide by the corresponding quantization table value and round to the nearest integer
m[i][j]=m[i][j]/qt[i][j];
m[i][j] = Math.round(m[i][j]);
}
}
/*
* Implementation of the Zigzag scan
*/
public static void zigzag()
{
// Manually populate the 1D array 'n' with the quantized coefficients
// in the zigzag order.
n[0] = (int)m[0][0]; n[1] = (int)m[0][1]; n[2]=(int)m[1][0]; n[3]=(int)m[2][0];
n[4] = (int)m[1][1]; n[5] = (int)m[0][2]; n[6]=(int)m[0][3]; n[7]=(int)m[1][2];
n[8] = (int)m[2][1]; n[9]=(int)m[3][0]; n[10]=(int)m[4][0]; n[11]=(int)m[3][1];
n[12]=(int)m[2][2]; n[13]=(int)m[1][3]; n[14]=(int)m[0][4]; n[15]=(int)m[0][5];
n[16]=(int)m[1][4]; n[17]=(int)m[2][3]; n[18]=(int)m[3][2]; n[19]=(int)m[4][1];
n[20]=(int)m[5][0]; n[21]=(int)m[6][0]; n[22]=(int)m[5][1]; n[23]=(int)m[4][2];
n[24]=(int)m[3][3]; n[25]=(int)m[2][4]; n[26]=(int)m[1][5]; n[27]=(int)m[0][6];
n[28]=(int)m[0][7]; n[29]=(int)m[1][6]; n[30]=(int)m[2][5]; n[31]=(int)m[3][4];
n[32]=(int)m[4][3]; n[33]=(int)m[5][2]; n[34]=(int)m[6][1]; n[35]=(int)m[7][0];
n[36]=(int)m[7][1]; n[37]=(int)m[6][2]; n[38]=(int)m[5][3]; n[39]=(int)m[4][4];
n[40]=(int)m[3][5]; n[41]=(int)m[2][6]; n[42]=(int)m[1][7]; n[43]=(int)m[2][7];
n[44]=(int)m[3][6]; n[45]=(int)m[4][5]; n[46]=(int)m[5][4]; n[47]=(int)m[6][3];
n[48]=(int)m[7][2]; n[49]=(int)m[7][3]; n[50]=(int)m[6][4]; n[51]=(int)m[5][5];
n[52]=(int)m[4][6]; n[53]=(int)m[3][7]; n[54]=(int)m[4][7]; n[55]=(int)m[5][6];
n[56]=(int)m[6][5]; n[57]=(int)m[7][4]; n[58]=(int)m[7][5]; n[59]=(int)m[6][6];
n[60]=(int)m[5][7]; n[61]=(int)m[6][7]; n[62]=(int)m[7][6]; n[63]=(int)m[7][7];
int i;
System.out.println("\n\n\n On zigzag");
for(i=0;i<64;i++)
{
// Take the absolute value (as the subsequent entropy encoding might handle signs)
n[i] = Math.abs(n[i]);
System.out.print(n[i]+"\t");
}
}
/*
* Simplified entropy encoding (run-length encoding of zeros and value encoding)
*/
public static void se1()
{
int i,tp;
StringBuffer op = new StringBuffer();
for(i=0;i<64;i++){
int count =0;
tp = i;
// Count consecutive zeros
while(i+1<64 && n[i+1]==0){
count++;
i++;
}
// Encode the run of zeros (in binary)
if(i!=0)
{
op.append(Integer.toBinaryString(count));
}
// Encode the number of bits required for the coefficient value (in binary)
op.append(Integer.toBinaryString(Integer.toBinaryString(n[tp]).length()));
// Encode the coefficient value itself (in binary)
op.append(Integer.toBinaryString(n[tp]));
}
String opt = op.toString();
System.out.println("\n\n\n"+opt);
}
}
Output
/*
run:
Input
48.0 39.0 40.0 68.0 60.0 38.0 50.0 121.0
149.0 82.0 79.0 101.0 113.0 106.0 27.0 62.0
58.0 63.0 77.0 69.0 124.0 107.0 74.0 125.0
80.0 97.0 74.0 54.0 59.0 71.0 91.0 66.0
18.0 34.0 33.0 46.0 64.0 61.0 32.0 37.0
149.0 108.0 80.0 106.0 116.0 61.0 73.0 92.0
211.0 233.0 159.0 88.0 107.0 158.0 161.0 109.0
212.0 104.0 40.0 44.0 71.0 136.0 113.0 66.0
On DCT
699.2499999999999 43.17537783517842 55.2458962644029 72.11192252561607 24.000000000000046 -25.50537751002191 11.211754811037084 -4.1361934819846775
-129.78397283623318 -71.4957835348326 -70.25934416341046 -73.35229687341331 59.432585562295756 -24.0223298746278 22.614760810882366 -2.0506659449800475
85.70740770894031 30.32177947343959 61.776911934581165 44.86638463520262 14.844230984030151 17.348004771994926 15.510407640085607 -13.193489866474044
-40.809000772948515 10.166167106402561 -17.52558849075414 -55.805100649235996 30.50000746240999 -2.279391113997457 -21.002352160175203 -1.256783994741201
-157.49999999999997 -49.39469576644716 13.267753314920942 -1.7801066852396144 -8.749999999999988 22.468610979498212 -8.472261916064841 -9.234812991136668
92.48938318084393 -9.0274872527208 45.72009525326298 -48.1304191876491 -58.505400504935224 -9.012918835424449 -28.5397689145109 10.37937054199913
-53.090043923635406 -62.968866490330015 -3.4895923599144885 -19.62162006305608 56.08886972022844 -2.2530070591154256 -3.2769119345812876 11.912750023395063
-20.538618492594377 -55.90169401863137 -20.586885431597857 -18.190873560968313 -26.57556076417692 -27.068025625951428 8.472705090576747 0.3138030194931717
On Quant
44.0 4.0 5.0 5.0 1.0 -1.0 0.0 0.0
-11.0 -6.0 -5.0 -4.0 2.0 0.0 0.0 0.0
6.0 2.0 4.0 2.0 0.0 0.0 0.0 0.0
-3.0 1.0 -1.0 -2.0 1.0 0.0 0.0 0.0
-9.0 -2.0 0.0 0.0 0.0 0.0 0.0 0.0
4.0 0.0 1.0 -1.0 -1.0 0.0 0.0 0.0
-1.0 -1.0 0.0 0.0 1.0 0.0 0.0 0.0
0.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0
On zigzag
44 4 11 6 6 5 5 5 2 3 9 1 4 4 1 1 2 2 1 2 4 1 0 0 2 0 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
1101011000111000100101101111001111001110101110101110101010010110100100101101110001110001101101010010100110101001110010111101010111011111111111111111110111
BUILD SUCCESSFUL (total time: 0 seconds)
*/