使用霍夫曼编码压缩PNG图像出现黑块问题的排查与修复
问题根源与修复方案
你的代码核心问题在于压缩逻辑完全错误,同时缺少关键的编码字典存储、文件读写模式错误等问题,导致解压后数据全黑。以下是具体修复步骤:
1. 修复压缩函数的核心逻辑
当前compressImage函数完全搞反了霍夫曼编码的流程:它没有根据符号查找对应的霍夫曼编码并写入比特流,而是用符号本身的二进制位去遍历霍夫曼树,这根本不是压缩。
解决步骤:
- 首先构建字典,存储每个符号对应的霍夫曼编码(比特序列和长度)
- 压缩时遍历每个符号,查字典得到编码,将编码的比特依次写入压缩数据
第一步:添加编码字典结构
在代码开头添加:
typedef struct { unsigned int code; // 存储霍夫曼编码(整数保存,高位在前) int length; // 编码的比特长度 } HuffmanCode; HuffmanCode codeDict[256]; // 编码字典
第二步:修改createCodeDictionary函数,填充编码字典
void createCodeDictionary(Node* root, unsigned int code, int depth) { if (root->left == NULL && root->right == NULL) { codeDict[root->symbol].code = code; codeDict[root->symbol].length = depth; // 调试用打印 printf("Symbol: 0x%02X, Code: ", root->symbol); for (int i = depth-1; i >= 0; i--) { printf("%d", (code >> i) & 1); } printf("\n"); return; } createCodeDictionary(root->left, code << 1, depth + 1); createCodeDictionary(root->right, (code << 1) | 1, depth + 1); }
调用时改为:createCodeDictionary(root, 0, 0);
第三步:重写compressImage函数
void compressImage(const unsigned char* symbol_sequence, size_t total_symbols, HuffmanCode* codeDict, unsigned char* compressed_data, size_t* compressed_size) { unsigned char* output = compressed_data; unsigned char current_byte = 0; int bit_pos = 0; // 当前字节的比特位置(0为最高位) for (size_t i = 0; i < total_symbols; i++) { unsigned char symbol = symbol_sequence[i]; HuffmanCode code = codeDict[symbol]; unsigned int huff_code = code.code; int code_len = code.length; // 将编码的每一位写入当前字节 for (int j = code_len - 1; j >= 0; j--) { int bit = (huff_code >> j) & 1; current_byte |= (bit << (7 - bit_pos)); bit_pos++; // 字节满8位,写入输出 if (bit_pos == 8) { *output = current_byte; output++; current_byte = 0; bit_pos = 0; } } } // 处理剩余未填满的字节 if (bit_pos > 0) { *output = current_byte; output++; } *compressed_size = output - compressed_data; }
调用时改为:compressImage(symbol_sequence, pixel_count * 3, codeDict, compressed_data, &compressed_data_size);
2. 修复文件读写的二进制模式
压缩数据是二进制流,不能用文本模式读写,否则会损坏数据(如Windows下0x0A会被转换为0x0D0A):
// 保存压缩数据用二进制写模式 FILE* file = fopen("../images/compressed_data.bin", "wb"); // 读取压缩数据用二进制读模式 file = fopen("../images/compressed_data.bin", "rb");
3. 修复符号序列的类型错误
symbol_sequence用char*存储unsigned char数据会导致符号扩展问题,改为:
unsigned char *symbol_sequence = malloc(pixel_count * 3);
4. 修复频率打印函数的逻辑
原printRGBFrequency遍历方式错误,改为正确的频率打印:
void printFrequency(const int *frequency) { printf("Symbol frequencies:\n"); for (int i = 0; i < 256; i++) { if (frequency[i] > 0) { printf("0x%02X: %d\n", i, frequency[i]); } } }
5. 补充压缩数据的必要元信息
当前代码未保存霍夫曼树/编码字典到压缩文件,实际解压时需要这些信息。可以将频率数组写入文件开头:
// 保存时先写入频率数组 fwrite(frequency, sizeof(int), 256, file); // 再写入压缩数据 fwrite(compressed_data, 1, compressed_data_size, file);
解压时先读取频率数组重构霍夫曼树:
int read_frequency[256]; fread(read_frequency, sizeof(int), 256, file); Node* root = buildHuffmanTree(read_frequency);
完成以上修改后,程序应该能正确压缩和解压图像,不会再出现全黑的情况。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

