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.