Kamila Przychodzeń

Portfolio

Kompresor obrazów w oparciu o algorytm JPEG

Jest to implementacja algorytmu JPEG. starałam się wykonać w pełni poszczególne kroki z algorytmu. Poniżej zamieszczone są kluczowe fragmenty kodu.

  • Wczytanie pliku
  • Do wczytywania pliku wykorzystuję klasy File oraz BufferedImage.

    public BufferedImage open() throws IOException {
            JFileChooser jf = new JFileChooser();
            jf.showOpenDialog(null);
            File file = jf.getSelectedFile();
            BufferedImage img = ImageIO.read(file);
            return img;
        }
    
  • Podział na kanały i zamiana RGB na YCrCb
  • private void channels(BufferedImage img) {
            this.Cb = new int[img.getWidth()][img.getHeight()];
            this.Cr = new int[img.getWidth()][img.getHeight()];
            this.Y = new int[img.getWidth()][img.getHeight()];
            this.alfa = new int[img.getWidth()][img.getHeight()];
            //   System.out.println("Pobiera kolory");
            for (int i = 0; i < this.img.getWidth(); i++) {
                for (int j = 0; j < this.img.getHeight(); j++) {
                    alfa[i][j] = (img.getRGB(i, j) >> 24) & 0xff;
                    double pixel_r = (img.getRGB(i, j) >> 16) & 0xff;
                    double pixel_g = (img.getRGB(i, j) >> 8) & 0xff;
                    double pixel_b = (img.getRGB(i, j)) & 0xff;
                    Y[i][j] = (int) (0.299 * (double) pixel_r + 0.587 * (double) pixel_g + 0.114 * (double) pixel_b);
                    Cb[i][j] = (int) (128 - 0.168736 * (double) pixel_r - 0.331264 * (double) pixel_g + 0.5 * (double) pixel_b);
                    Cr[i][j] = (int) (128 + 0.5 * (double) pixel_r - 0.418688 * (double) pixel_g - 0.081312 * (double) pixel_b);
    
                }
            }
        }
    
  • Kwantyzacja
  • private void quant(int q, int[][] blok) {
            double wsp = (double) q;
            if (q > 50) {
                for (int y = 0; y < 8; y++) {
                    for (int x = 0; x < 8; x++) {
                        blok[y][x] = (int) Math.floor((blok[y][x]) / (((100 - wsp) / 50) * quantization[y][x]));
                    }
                }
            } else if (q < 50) {
                for (int y = 0; y < 8; y++) {
                    for (int x = 0; x < 8; x++) {
                        blok[y][x] = (int) Math.floor((blok[y][x]) / ((50 / wsp) * quantization[y][x]));
                    }
                }
            } else {
                for (int y = 0; y < 8; y++) {
                    for (int x = 0; x < 8; x++) {
                        blok[y][x] = (int) Math.floor(blok[y][x] / quantization[y][x]);
                    }
                }
            }
        }
    
  • Transformata Cosinusowa
  • private int[][] dct2d(int[][] blok) {
            int[][] tmpImg = new int[8][8];
            double Cu, Cv;
    
            for (int u = 0; u < 8; u++) {
                for (int v = 0; v < 8; v++) {
                    double z = 0.0, pixel = 0.0;
                    if (u == 0) {
                        Cu = 0.353553391;
                    } else {
                        Cu = 0.25;
                    }
                    if (v == 0) {
                        Cv = 0.353553391;
                    } else {
                        Cv = 0.25;
                    }
                    for (int x = 0; x < 8; x++) {
                        for (int y = 0; y < 8; y++) {
                            pixel = blok[x][y];
                            z += pixel * Math.cos((double) (2 * x + 1) * (double) u * Math.PI / 16.0) * Math.cos((double) (2 * y + 1) * (double) v * Math.PI / 16.0);
                        }
                    }
                    tmpImg[u][v] = (int) (z * Cu * Cv);
                }
            }
    
            return tmpImg;
        }
    

    Transformata oraz Zig-Zag wykonywane są na kolejnych blokach o rozmiarze 8x8, na każdym kanale oddzielnie.

  • Zig-Zag
  • public int[] Zig_Zag(int data[][]) {
            int i = 1;
            int j = 1;
            int[] tab = new int[64];
            for (int element = 0; element < 64; element++) {
                tab[element] = data[i - 1][j - 1];
                if ((i + j) % 2 == 0) {
                    if (j < 8) {
                        j++;
                    } else {
                        i += 2;
                    }
                    if (i > 1) {
                        i--;
                    }
                } else {
                    if (i < 8) {
                        i++;
                    } else {
                        j += 2;
                    }
                    if (j > 1) {
                        j--;
                    }
                }
            }
            return tab;
        }
    
  • Zapis do pliku i kodowanie
  • Kodowanie odbywa się podczas zapisu pliku, przy użyciu klasy Deflater. Sam zapis odbywa się przy użyciu File, FileOutputStream, DataOutputStream oraz ByteArrayOutputStream.

    Dekoder przebiega w następujących etapach:
  • Zdekodowanie odczytanych danych z pliku
  • Cofnięcie Zig-zag-a
  • Odwrotna transformata
  • Dekwantyzacja
  • Zmiana kanałów YCrCb na RGB
  • Połączanie obrazka w całość

Zastosowana kompresja pozwala na znaczne zmniejszenie rozmiaru obrazu, jednak wyraźnie widać stratę jakości.

created by kama