Implementing JPEG Algorithm in Java

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)
 
*/

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.