【问题标题】:Problems when compressing and decompressing a small .png file using Huffman Coding (Java)使用霍夫曼编码 (Java) 压缩和解压缩小型 .png 文件时出现的问题
【发布时间】:2023-01-08 09:13:43
【问题描述】:

所以我有一个实现 Hufmman Coding 的 Java 类,我想用它来压缩和解压缩任何类型的文件。

这是我的代码:

import java.io.*;
import java.util.*;

public class HuffmanCoding {

    public static void main(String[] args) throws IOException {

        String inputFilePath = "C:\\Users\\MAJ\\eclipse-workspace\\ProjectTwo\\src\\inputFile.png";
        String encodedOutputFilePath = "C:\\Users\\MAJ\\eclipse-workspace\\ProjectTwo\\src\\encodedOutputFile.txt";
        // get the frequencies of all the bytes in the file
        byte[] data = fileToByteArray(inputFilePath);
        Map<Byte, Integer> frequencyTable = getByteFrequencies(data);

        // create a Huffman coding tree
        Node root = createHuffmanTree(frequencyTable);

        // create the table of encodings for each byte
        Map<Byte, String> encodings = createEncodings(root);

        // encode the input file and write the encoded output to the output file
        encodeFile(data, encodings, encodedOutputFilePath);
        String inputFileExtension = inputFilePath.substring(inputFilePath.lastIndexOf('.'));
        String decompressedOutputFilePath = "C:\\Users\\MAJ\\eclipse-workspace\\ProjectTwo\\src\\decompressedOutputFile" + inputFileExtension;
        decodeFile(encodedOutputFilePath, decompressedOutputFilePath, root);
    }

    public static byte[] fileToByteArray(String filePath) throws IOException {
        // read the file
        BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(filePath));
        byte[] data = inputStream.readAllBytes();
        inputStream.close();

        return data;
    }


    public static Map<Byte, Integer> getByteFrequencies(byte[] data) {
        // map for storing the frequencies of the bytes
        Map<Byte, Integer> frequencyTable = new HashMap<>();

        // count the frequencies of the bytes
        for (byte b : data) {
            frequencyTable.put(b, frequencyTable.getOrDefault(b, 0) + 1);
        }

        return frequencyTable;
    }

    public static Node createHuffmanTree(Map<Byte, Integer> frequencyTable) {
        // create a priority queue to store the nodes of the tree
        PriorityQueue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(n -> n.frequency));

        // create a leaf node for each byte and add it to the priority queue
        for (Map.Entry<Byte, Integer> entry : frequencyTable.entrySet()) {
            queue.add(new Node(entry.getKey(), entry.getValue()));
        }

        // create the Huffman tree
        while (queue.size() > 1) {
            // remove the two nodes with the lowest frequency from the queue
            Node left = queue.poll();
            Node right = queue.poll();

            // create a new internal node with these two nodes as children and the sum of their frequencies as the frequency
            assert right != null;
            Node parent = new Node(left.frequency + right.frequency, left, right);

            // add the new internal node to the queue
            queue.add(parent);
        }

        // the root node is the node remaining in the queue
        return queue.poll();

    }


    // node class for the Huffman tree
    static class Node {
        int frequency;
        byte character;
        Node left;
        Node right;

        Node(int frequency, Node left, Node right) {
            this.frequency = frequency;
            this.left = left;
            this.right = right;
        }

        Node(byte character, int frequency) {
            this.character = character;
            this.frequency = frequency;
        }
    }

    public static Map<Byte, String> createEncodings(Node root) {
        // map for storing the encodings of the bytes
        Map<Byte, String> encodings = new HashMap<>();

        // create the encodings
        createEncodings(root, "", encodings);

        return encodings;
    }

    private static void createEncodings(Node node, String encoding, Map<Byte, String> encodings) {
        if (node == null) {
            return;
        }
        if (node.character != 0) {
            // this is a leaf node, so add the encoding to the map
            encodings.put(node.character, encoding);
        } else {
            // this is an internal node, so recurse on the left and right children
            createEncodings(node.left, encoding + "0", encodings);
            createEncodings(node.right, encoding + "1", encodings);
        }
    }



    public static void encodeFile(byte[] data, Map<Byte, String> encodings, String outputFilePath) throws IOException {
        BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath));

        // create a string builder for building the encoded string
        StringBuilder sb = new StringBuilder();

        // encode the data and add the encoded string to the string builder
        for (byte b : data) {
            String str = encodings.get(b);
            if (str == null) {
                str = "";
            }
            sb.append(str);
        }

        // write the encoded string to the output file
        writer.write(sb.toString());

        writer.close();
    }




    public static void decodeFile(String inputFilePath, String outputFilePath, Node root) throws IOException {
        // read the encoded data from the input file
        BufferedReader reader = new BufferedReader(new FileReader(inputFilePath));
        String encodedData = reader.readLine();
        reader.close();

        // create the output file
        BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFilePath));

        // decode the data and write it to the output file
        Node current = root;
        for (int i = 0; i < encodedData.length(); i++) {
            current = encodedData.charAt(i) == '0' ? current.left : current.right;
            assert current != null;
            if (current.left == null && current.right == null) {
                outputStream.write(current.character);
                current = root;
            }
        }
        outputStream.close();
    }




}

压缩和解压缩 .txt 文件时,一切正常,但是当压缩和解压缩大小为 5 KB 的小 .png 图像时,输出的解压缩文件应该是与原始图像相同的 .png 图像,具有正确的大小但是当我尝试使用任何类型的图像查看器打开它时,它不会加载,而且我似乎无法弄清楚问题是什么,我假设任何其他类型的文件都会出现同样的问题( .mp4、.mp3、.jpeg、.exe 等)。如果可以,请帮帮我!

【问题讨论】:

  • 我将 BufferedReader 和 BufferedWriter 都更改为 BufferedInputStream 和 BufferedOutputStream,但这并没有解决任何问题
  • 对不起。我删除了那些 cmet 作为树被序列化为文本(afaics)。可能部分算法是错误的,因为结果相差不远,但当然足以破坏二进制文件
  • 天啊。你确实意识到你是戏剧性的扩大您的数据,而不是压缩它,对吗?你正在写一个 ASCII 字节,八位,一个“0”或一个“1”,每一位!每一点都应该是一点。
  • 而你在作弊。您正在将编码器生成的霍夫曼树传递给解码器。您需要在代码之前对文件中的霍夫曼代码进行编码。

标签: java compression huffman-code image-compression


【解决方案1】:

如果您希望能够对所有可能的字节进行编码,则不能使用“特殊”字符。你也不需要一个。叶子已经由空指针标识。如果你改变:

if (node.character != 0) {

到:

if (node.left == null) {

然后就可以了。

【讨论】:

    猜你喜欢
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多