実現したいこと
Huffman符号化で画像データを圧縮をし、ファイルポインタを使って読み込みと書き込み
今度は圧縮したデータをファイルポインタで圧縮したデータを画像データに戻す
前提
C言語でHuffman符号化の圧縮と圧縮したHuffman符号化の復号の実装を行ってます。
圧縮したHuffman符号は.exeで画像データの圧縮は出来てますが復号したHuffman符号は.exeで圧縮をしたデータを元の画像に戻すことが出来ませんでした
発生している問題・エラーメッセージ
エラーメッセージ
エラー表示は出てませんが、復号したデータを.exeで元の画像に戻す実装が出来ませんでした
該当のソースコード
C++ Huffman_d.cpp //===================================== // 復号(Huffman符号化) //===================================== #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <tchar.h> // Huffman符号ツリーのノード構造体 struct HuffmanNode { unsigned char data; // ノードが表すデータ(葉ノードの場合) struct HuffmanNode* left; // 左の子ノード struct HuffmanNode* right; // 右の子ノード }; // ファイルヘッダにツリー情報を保存するための構造体 struct HuffmanHeader { int treeSize; // ツリーのサイズなど、必要な情報を含む int numLeafNodes; // 葉ノードの数 }; // ファイルヘッダにHuffman符号ツリー情報を保存する関数 void WriteHuffmanHeader(FILE* fp_w, struct HuffmanHeader* header) { fwrite(header, sizeof(struct HuffmanHeader), 1, fp_w); } // ファイルヘッダからHuffman符号ツリー情報を読み取る関数 void ReadHuffmanHeader(FILE* fp_r, struct HuffmanHeader* header) { fread(header, sizeof(struct HuffmanHeader), 1, fp_r); } // ツリーの情報を使ってHuffman符号ツリーを再構築する関数 struct HuffmanNode* RebuildHuffmanTree(FILE* fp_r) { struct HuffmanHeader header; ReadHuffmanHeader(fp_r, &header); // ここでHuffman符号ツリーを再構築するロジックを実装する // headerに含まれる情報を使用してツリーを構築する struct HuffmanNode* root = NULL; int i; for (i = 0; i < header.numLeafNodes; i++) { // ノードの種類を読み取る(葉ノードか、ノードか) int nodeType; fread(&nodeType, sizeof(int), 1, fp_r); if (nodeType == 0) // 葉ノードの場合 { // ノードのデータ値を読み取る unsigned char data; fread(&data, sizeof(unsigned char), 1, fp_r); // 新しい葉ノードを作成してツリーに追加するロジックを実装する struct HuffmanNode* newLeafNode = (struct HuffmanNode*)malloc(sizeof(struct HuffmanNode)); newLeafNode->data = data; newLeafNode->left = NULL; newLeafNode->right = NULL; } else // ノードの場合 { // ノードの出現頻度を読み取る int frequency; fread(&frequency, sizeof(int), 1, fp_r); // 新しいノードを作成してツリーに追加するロジックを実装する struct HuffmanNode* newNode = (struct HuffmanNode*)malloc(sizeof(struct HuffmanNode)); newNode->data = 0; // ノードのデータ値はここでは使わない newNode->left = NULL; newNode->right = NULL; } } return root; } // Huffman符号デコード関数 void DecodeHuffman(FILE* fp_r, FILE* fp_w) { // Huffman符号ツリーを構築する struct HuffmanNode* huffmanTree = RebuildHuffmanTree(fp_r); // デコード用の変数を初期化 struct HuffmanNode* currentNode = huffmanTree; int bitBuffer = 0; // ビットバッファ int bitCount = 0; // ビットバッファ内のビット数 // データの読み取りとデコード while (1) { int bit; // ビットを1つ読み取る if (bitCount == 0) { bitBuffer = fgetc(fp_r); if (bitBuffer == EOF) { break; // データの終わり } bitCount = 8; } bit = (bitBuffer >> (bitCount - 1)) & 1; bitCount--; // ビットを使ってHuffman符号ツリーを下りながらデコード if (bit == 0) { currentNode = currentNode->left; } else { currentNode = currentNode->right; } // 葉ノードに到達したらデータを書き込む if (currentNode->left == NULL && currentNode->right == NULL) { fputc(currentNode->data, fp_w); currentNode = huffmanTree; // ツリーのルートに戻る } } // デコードが終了したら、ツリーのメモリを解放する(必要に応じて) // ここでメモリ解放のコードを追加する } int main(int argc, char** argv) { if (argc < 2) { printf("usage: HuffmanDecode filename\n"); return 1; } char opath[_MAX_PATH]; char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; _splitpath(argv[1], drive, dir, fname, ext); if (strcmp(ext, ".huff")) { printf("ファイル(%s)が違います。\n", argv[1]); return 1; } _makepath(opath, drive, dir, fname, NULL); FILE* fp_r = fopen(argv[1], "rb"); if (!fp_r) { printf("ファイル(%s)が見つかりません。\n", argv[1]); return 1; } FILE* fp_w = fopen(opath, "wb"); if (!fp_w) { fclose(fp_r); printf("ファイル(%s)が作成できません。\n", opath); return 1; } DecodeHuffman(fp_r, fp_w); fclose(fp_w); fclose(fp_r); return 0; }
C++ Huffman.cpp //===================================== // 圧縮(Huffman符号化) //===================================== #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <queue> using namespace std; // Huffmanツリーのノード定義 struct HuffmanNode { char data; int frequency; HuffmanNode* left; HuffmanNode* right; }; // Huffmanツリーのノード比較関数 struct CompareNodes { bool operator()(HuffmanNode* lhs, HuffmanNode* rhs) const { return lhs->frequency > rhs->frequency; } }; // 各文字の出現頻度をカウントする関数 void CountFrequencies(FILE* fp, int frequencies[256]) { // frequencies配列を初期化 for (int i = 0; i < 256; ++i) { frequencies[i] = 0; } // ファイルからデータを読み取り、各文字の出現頻度をカウント int c; while ((c = getc(fp)) != EOF) { if (c >= 0 && c < 256) { frequencies[c]++; } } } // Huffmanツリーを構築する関数 HuffmanNode* BuildHuffmanTree(int frequencies[256]) { // 優先度キューを用意 priority_queue<HuffmanNode*, vector<HuffmanNode*>, CompareNodes> minHeap; // 各文字の出現頻度が0でない場合、ノードを優先度キューに追加 for (int i = 0; i < 256; ++i) { if (frequencies[i] > 0) { HuffmanNode* node = new HuffmanNode; node->data = (char)i; node->frequency = frequencies[i]; node->left = nullptr; node->right = nullptr; minHeap.push(node); } } // ツリーを構築 while (minHeap.size() > 1) { HuffmanNode* left = minHeap.top(); minHeap.pop(); HuffmanNode* right = minHeap.top(); minHeap.pop(); // 新しいノードを生成し、子ノードを設定 HuffmanNode* newNode = new HuffmanNode; newNode->data = '\0'; // ダミーデータ newNode->frequency = left->frequency + right->frequency; newNode->left = left; newNode->right = right; // 新しいノードを優先度キューに追加 minHeap.push(newNode); } // 最終的なツリーのルートを返す return minHeap.top(); } // Huffman符号を生成する関数 void GenerateHuffmanCodes(HuffmanNode* root, string code, string codes[256]) { if (root == nullptr) { return; } // ノードが葉ノード(文字を持つノード)であるかどうかを確認 if (root->data != '\0') { // 葉ノードなら、その文字のHuffman符号を保存 codes[(unsigned char)root->data] = code; } // 左側に移動し、"0"を追加して再帰呼び出し GenerateHuffmanCodes(root->left, code + "0", codes); // 右側に移動し、"1"を追加して再帰呼び出し GenerateHuffmanCodes(root->right, code + "1", codes); } // メインのHuffmanエンコード関数 void EncodeHuffman(FILE* fp_r, FILE* fp_w) { int frequencies[256] = { 0 }; // 各文字の出現頻度を格納する配列 string codes[256]; // 各文字に対するHuffman符号を格納する配列 // ファイルからデータを読み取り、各文字の出現頻度をカウントする処理を追加 CountFrequencies(fp_r, frequencies); // Huffmanツリーを構築 HuffmanNode* root = BuildHuffmanTree(frequencies); // Huffman符号を生成 GenerateHuffmanCodes(root, "", codes); // ファイルポインタをファイルの先頭に戻す rewind(fp_r); int bitBuffer = 0; // ビットバッファ int bitCount = 0; // ビットカウント // ファイルからデータを読み取り、Huffman符号に変換し、ビットバッファに書き込み int c; while ((c = getc(fp_r)) != EOF) { string huffmanCode = codes[(unsigned char)c]; for (size_t i = 0; i < huffmanCode.length(); ++i) { bitBuffer <<= 1; // ビットバッファを左に1ビットシフト if (huffmanCode[i] == '1') { bitBuffer |= 1; // 1ビットをセット } bitCount++; // ビットバッファが8ビットになったら、1バイトを書き込み if (bitCount == 8) { putc(bitBuffer, fp_w); bitBuffer = 0; bitCount = 0; } } } // ビットバッファに残っているビットを書き込み if (bitCount > 0) { bitBuffer <<= (8 - bitCount); putc(bitBuffer, fp_w); } } // メイン int main(int argc, char** argv) { if (argc < 2) { printf("usage: HuffmanEncode filename\n"); return 1; } FILE* fp_r = fopen(argv[1], "rb"); if (!fp_r) { printf("ファイル(%s)が見つかりません。\n", argv[1]); return 1; } char opath[260]; strcpy(opath, argv[1]); strcat(opath, ".huffman"); FILE* fp_w = fopen(opath, "wb"); if (!fp_w) { fclose(fp_r); printf("ファイル(%s)が作成できません。\n", opath); return 1; } EncodeHuffman(fp_r, fp_w); fclose(fp_w); fclose(fp_r); return 0; }
試したこと
特にHuffman_d.cppにヘッダーのツリー情報を追加などをして
DecodeHuffman関数を修正したりしてやってみましたがうまくいかず…
補足情報(FW/ツールのバージョンなど)
Visual Studio2017でプログラムコードを打ってます
2019以降は設定を変えるなどをしておいてほしいです
文字コードはUnicodeで行ってます
画像データに関しては.bmpで行っていますので.jpgや.pngは出来ませんので注意してください

0 コメント