How to fix the issue of getting a black square while compressing PNG image using Huffman code?

46 Views Asked by At

I am writing a program that converts PNG image into the text by using Huffman code(It's like a compressed file) and after decompressed this file by using a CodeDictionary and as result I should get the same PNG image. But something going wrong and regardless which image would be chosen I get a black square. I need to use only Huffman code to get a compressed image.

Could anyone please suggest what should I do?

This is the code I have.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STB_IMAGE_IMPLEMENTATION
#include "../stb_image/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "../stb_image/stb_image_write.h"


typedef struct Node {
    unsigned char symbol;
    int frequency;
    struct Node* left;
    struct Node* right;
} Node;

Node *createNode(unsigned char symbol, int frequency) {
    Node *node = (Node *) malloc(sizeof(Node));
    node->symbol = symbol;
    node->frequency = frequency;
    node->left = NULL;
    node->right = NULL;
    return node;
}

Node *buildHuffmanTree(int *frequency) {
    // Создание начального массива узлов-листьев
    Node *leaves[256];
    int leafCount = 0;
    for (int i = 0; i < 256; i++) {
        if (frequency[i] > 0) {
            leaves[leafCount] = createNode(i, frequency[i]);
            leafCount++;
        }
    }

    // Построение дерева Хаффмана
    while (leafCount > 1) {
        // Поиск двух узлов с минимальной частотой
        int min1 = 0;
        int min2 = 1;
        if (leaves[min1]->frequency > leaves[min2]->frequency) {
            int temp = min1;
            min1 = min2;
            min2 = temp;
        }

        for (int i = 2; i < leafCount; i++) {
            if (leaves[i]->frequency < leaves[min1]->frequency) {
                min2 = min1;
                min1 = i;
            } else if (leaves[i]->frequency < leaves[min2]->frequency) {
                min2 = i;
            }
        }

        // Создание нового узла-родителя
        Node *parent = createNode(0, leaves[min1]->frequency + leaves[min2]->frequency);
        parent->left = leaves[min1];
        parent->right = leaves[min2];

        // Удаление использованных узлов и добавление нового узла в массив листьев
        leaves[min1] = parent;
        leaves[min2] = leaves[leafCount - 1];
        leafCount--;
    }

    return leaves[0]; // Возвращение корня дерева
}

void createCodeDictionary(Node* root, unsigned char* code, int depth) {
    if (root->left == NULL && root->right == NULL) {
        printf("Symbol: %c, Code: ", root->symbol);
        for (int i = 0; i < depth; i++) {
            printf("%d", code[i]);
        }
        printf("\n");
    }
    else {
        code[depth] = 0;
        createCodeDictionary(root->left, code, depth + 1);
        code[depth] = 1;
        createCodeDictionary(root->right, code, depth + 1);
    }
}

void compressImage(const unsigned char* symbol_sequence, size_t pixel_count, Node* root, unsigned char* compressed_data, size_t* compressed_size) {
    unsigned char* output = compressed_data;
    size_t output_bit_count = 0;

    for (size_t i = 0; i < pixel_count * 3; i++) {
        Node* current = root;
        unsigned char symbol = symbol_sequence[i];

        while (current->left != NULL && current->right != NULL) {
            int bit = (symbol >> (7 - output_bit_count)) & 1;

            if (bit == 0) {
                current = current->left;
            }
            else {
                current = current->right;
            }

            output_bit_count++;
            if (output_bit_count == 8) {
                *output = 0;
                for (int j = 0; j < 8; j++) {
                    *output |= ((compressed_data[i] >> (7 - j)) & 1) << j;
                }
                output++;
                output_bit_count = 0;
            }
        }
    }

    *compressed_size = output - compressed_data;
}

void decompressImage(unsigned char* compressed_data, size_t compressed_size, Node* root, unsigned char* decompressed_data, size_t pixel_count) {
    unsigned char* output = decompressed_data;
    size_t output_bit_count = 0;
    Node* current = root;

    for (size_t i = 0; i < compressed_size; i++) {
        unsigned char byte = compressed_data[i];

        for (int j = 7; j >= 0; j--) {
            int bit = (byte >> j) & 1;

            if (bit == 0) {
                current = current->left;
            }
            else {
                current = current->right;
            }

            if (current->left == NULL && current->right == NULL) {
                *output = current->symbol;
                output++;
                output_bit_count++;

                if (output_bit_count == pixel_count * 3) {
                    return;
                }

                current = root;
            }
        }
    }
}

void freeHuffmanTree(Node *node) {
    if (node == NULL) {
        return;
    }
    freeHuffmanTree(node->left);
    freeHuffmanTree(node->right);
    free(node);

}



void printRGBFrequency(const int *frequency) {
    printf("Frequency of R, G, B:\n");
    for (int i = 0; i < 256; i += 3) {
        printf("R:%d, G:%d, B:%d\n", frequency[i], frequency[i + 1], frequency[i + 2]);
    }
}



int main(void)
{
    // added block for getting information about the image into the memory
    int height, width, channels;
    unsigned char *image_data = stbi_load("../images/photo.png", &width, &height, &channels, 0);
    if (image_data == NULL){
        printf("Error in loading the image\n");
        exit(1);
    }

    // convertion image data into a sequence of characters (RGB)
    unsigned char *pixels = image_data;
    size_t pixel_count = width * height;
    char *symbol_sequence = malloc(pixel_count * 3);
    size_t i, j;
    for (i = 0, j = 0; i < pixel_count; i++, j += 3) {
        symbol_sequence[j] = pixels[j]; // sequence of R        
        symbol_sequence[j + 1] = pixels[j + 1]; // sequence of G
        symbol_sequence[j + 2] = pixels[j + 2]; // sequence of B
    }

    // counting the frequency of occurence of each symbol
    int frequency[256] = {0}; 
    for (i = 0; i < pixel_count * 3; i++) 
        frequency[symbol_sequence[i]]++;
    
    printRGBFrequency(frequency);

    // start of making Haffman Tree

    Node* root = buildHuffmanTree(frequency);

    unsigned char code[256];
    createCodeDictionary(root, code, 0);

    // Compress image

    size_t compressed_data_size = (pixel_count * 3 * 8) / 8 + 1; // Assuming each symbol is represented by 8 bits
    unsigned char* compressed_data = malloc(compressed_data_size);
    compressImage(symbol_sequence, pixel_count, root, compressed_data, &compressed_data_size);

    // Save to txt

    FILE* file = fopen("../images/compressed_data.txt", "w");
    if (file == NULL) {
        printf("Error in opening the file\n");
        exit(1);
    }
    fwrite(compressed_data, 1, compressed_data_size, file);
    fclose(file);

    // Read txt 

    file = fopen("../images/compressed_data.txt", "r");
    if (file == NULL) {
        printf("Error in opening the file\n");
        exit(1);
    }
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    rewind(file);
    unsigned char* read_compressed_data = malloc(file_size);
    fread(read_compressed_data, 1, file_size, file);
    fclose(file);

    // Decompression of image

    unsigned char* decompressed_data = malloc(pixel_count * 3);
    decompressImage(read_compressed_data, file_size, root, decompressed_data, pixel_count);

    // Write image

    stbi_write_png("../images/decompressed_image.png", width, height, channels, decompressed_data, width * channels);


    // Free memory
    
    free(symbol_sequence);
    freeHuffmanTree(root);
    free(compressed_data);
    free(read_compressed_data);
    free(decompressed_data);
    stbi_image_free(image_data);






    return 0;
}

I have tried to rewrite the code several times by using other libraries to read the image, but got the same result.

0

There are 0 best solutions below