-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHuffman-Coding_Prog.cpp
More file actions
404 lines (327 loc) · 13.6 KB
/
Huffman-Coding_Prog.cpp
File metadata and controls
404 lines (327 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <map>
#include <queue>
#include <iomanip>
#include<chrono>
//auto start = std::chrono::high_resolution_clock::now();
//auto end = std::chrono::high_resolution_clock::now();
//auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
//cout << "Running time of compression" << duration.count() << " microseconds." << endl;
struct Node {
int value; // Pixel value
int freq; // Frequency of the pixel
Node* left;
Node* right;
Node() : value(-1), left(nullptr), right(nullptr) {}
Node(int value, int freq) : value(value), freq(freq), left(nullptr), right(nullptr) {}
Node(int value) : value(value), freq(0), left(nullptr), right(nullptr) {}
Node(Node* left, Node* right) : value(0), freq(0), left(left), right(right) {}
};
struct Compare {
bool operator()(Node* a, Node* b) {
return a->freq > b->freq; // Min Heap
}
};
// Function to construct Huffman Tree by minHeap
Node* buildHuffmanTree(std::unordered_map<int, int>& frequencyMap) {
std::priority_queue<Node*, std::vector<Node*>, Compare> minHeap;
// Create nodes for each unique pixel and add to min heap
for (const auto& pair : frequencyMap) {
minHeap.push(new Node(pair.first, pair.second));//Sort after pushing into heap
}
// Build Huffman Tree
while (minHeap.size() > 1) {
Node* left = minHeap.top();//Least
minHeap.pop();
Node* right = minHeap.top();//Second least
minHeap.pop();
//建一個new node左右連起加進去
Node* combined = new Node(-1, left->freq + right->freq); // -1 indicates an internal node
combined->left = left;
combined->right = right;
minHeap.push(combined);
}
return minHeap.top(); // Root of Huffman Tree
}
// Function to encode Huffman Tree
void encodeHuffmanTree(Node* root, std::string str, std::unordered_map<int, std::string>& huffmanCode) {
if (root == nullptr) {
return;
}
// Found a leaf node
if (!root->left && !root->right) {
huffmanCode[root->value] = str;
}
encodeHuffmanTree(root->left, str + "0", huffmanCode);
encodeHuffmanTree(root->right, str + "1", huffmanCode);
}
// Function to encode specified Image
std::string encodeImage(const std::vector<std::vector<int>>& image,
const std::unordered_map<int, std::string>& huffmanCode) {
std::string encodedImage;
for (const auto& row : image) {
for (int pixel : row) {
encodedImage += huffmanCode.at(pixel);
}
}
return encodedImage;
}
// Function to turn string(encoded image) into binary
std::vector<char> stringToBinary(const std::string& encodedString) {
std::vector<char> binaryData;
char currentByte = 0;
int bitCount = 0;
for (char bit : encodedString) {
// Set the bit in currentByte
currentByte = (currentByte << 1) | (bit == '1');
bitCount++;
if (bitCount == 8) {
// Byte is filled, add to vector
binaryData.push_back(currentByte);
currentByte = 0;
bitCount = 0;
}
}
// Handle the last byte if it's not full
if (bitCount > 0) {
currentByte <<= (8 - bitCount);
binaryData.push_back(currentByte);
}
return binaryData;
}
// Function to serialize huffman tree for compressing
void serializeTree(Node* root, std::vector<char>& encoding) {
if (root == nullptr) return;
if (!root->left && !root->right) {
encoding.push_back('1');
encoding.push_back(root->value);
}
else {
encoding.push_back('0');
serializeTree(root->left, encoding);
serializeTree(root->right, encoding);
}
}
// Function to reconstruct the Huffman tree
Node* decodeHuffmanTree(std::ifstream& file) {
if (file.eof()) return nullptr;
char marker;
file.get(marker);
if (marker == '1') {
if (file.eof()) return nullptr;
char ch;
file.get(ch);
return new Node(static_cast<unsigned char>(ch));
}
else {
Node* left = decodeHuffmanTree(file);
Node* right = decodeHuffmanTree(file);
return new Node(left, right);
}
}
// Function to decode the encoded data using the Huffman tree
std::vector<int> decodeData(Node* root, const std::string& encodedData) {
std::vector<int> decodedData;
Node* current = root; // For decoding a part of bits
// Tracing tree using VLR to decode only for leaves
for (char byte : encodedData) {
for (int i = 7; i >= 0; --i) {
bool bit = (byte >> i) & 1; // 提取每個位元
if (bit == 0)
current = current->left;
else
current = current->right;
if (current->left == nullptr && current->right == nullptr) {
decodedData.push_back(current->value);
current = root; // 重置到根節點
}
}
}
return decodedData;
}
// Function to draw the TABLE of pixel values and corresponding Huffman code
void printHuffmanTable(const std::unordered_map<int, std::string>& huffmanCode) {
// Sort the map
std::map<int, std::string> orderedHC(huffmanCode.begin(), huffmanCode.end());
// 表格標題
std::cout << std::endl;
std::cout << std::left << std::setw(12) << "Pixel Value" << "Huffman Code" << std::endl;
std::cout << std::string(30, '-') << std::endl; // 分隔線
// 遍歷映射並打印每個項目
for (const auto& pair : orderedHC) {
std::cout << std::left << std::setw(12) << pair.first << pair.second << std::endl;
}
std::cout << std::endl;
}
// Function to draw HISTOGRAM of pixel values and corresponding frequency
void printHistogram(const std::unordered_map<int, int>& frequencyMap) {
// 找出最大頻率
int maxFrequency = 0;
for (const auto& pair : frequencyMap) {
if (pair.second > maxFrequency) {
maxFrequency = pair.second;
}
}
// 定義最大長度和正規化比例
const int maxLength = 100; // 可以自定義最大長度
double normalizationRatio = static_cast<double>(maxLength) / maxFrequency;
std::map<int, int> orderedFmap(frequencyMap.begin(), frequencyMap.end());
std::cout << "Histogram of frequency: (one # indicates " << 1 / normalizationRatio << " times, no # indicates less than the number)" << std::endl;
for (const auto& pair : orderedFmap) {
int pixelValue = pair.first;
int normalizedLength = static_cast<int>(pair.second * normalizationRatio);
std::cout << std::left << std::setw(4) << pixelValue << ": ";
for (int i = 0; i < normalizedLength; ++i) {
std::cout << "#";
}
std::cout << std::endl;
}
}
std::streamsize getFileSize(const std::string& filePath) {
std::ifstream file(filePath, std::ifstream::ate | std::ifstream::binary);
if (!file) {
return -1;
}
// Get size of file
std::streamsize size = file.tellg();
file.close();
return size;
}
int main(int argc, char* argv[]) {
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " -c/-d filename" << std::endl;
return 1;
}
std::string mode = argv[1]; // "-c" or "-d"
std::string filename = argv[2]; // File name, e.g., "test.pgm" or "test.hc"
std::string filename2; // File name, e.g., "tested.pgm"
if (argc >= 4) filename2 = argv[3];
// For encoding
if (mode == "-c") {
// Compression code
std::cout << "Compressing " << filename << std::endl;
auto start = std::chrono::high_resolution_clock::now();
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Error opening file" << std::endl;
return 1;
}
std::string line;
getline(file, line); // Read the magic number (e.g., "P2")
getline(file, line); // Read the next line which might be a comment
if (line[0] == '#') {
getline(file, line); // Read the next line if the previous was a comment
}
std::stringstream ss(line);
int width, height, maxVal;
ss >> width >> height; // Read image dimensions
file >> maxVal; // Read maximum pixel value
std::vector<std::vector<int>> pixels(height, std::vector<int>(width));
std::unordered_map<int, int> frequencyMap;//<index, frequency>
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
file >> pixels[i][j];
++frequencyMap[pixels[i][j]];
}
}
file.close();
// Now, pixels[][] contains the pixel values of the PGM image
// Build huffman tree
Node* root = buildHuffmanTree(frequencyMap);
// Build huffman code and Serialize
std::unordered_map<int, std::string> huffmanCode;//<index, encode number>
encodeHuffmanTree(root, "", huffmanCode);
std::vector<char> treeStructure;
serializeTree(root, treeStructure);
// Encode specified image
std::string encodedImageData = encodeImage(pixels, huffmanCode);
std::vector<char> binaryData = stringToBinary(encodedImageData);
// Open a file stream to write
std::string compressedFile = filename + ".hc";
std::ofstream outputFile(compressedFile, std::ios::binary);
if (outputFile.is_open()) {
outputFile.write(reinterpret_cast<const char*>(&width), sizeof(width));
outputFile.write(reinterpret_cast<const char*>(&height), sizeof(height));
// Output char(byte) by char
outputFile.write(treeStructure.data(), treeStructure.size());
outputFile.write(binaryData.data(), binaryData.size());
outputFile.close();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Running time of compression is " << duration.count() << " milliseconds." << std::endl;
std::cout << "Image successfully encoded and saved to '"<< compressedFile <<"'" << std::endl;
}
else {
std::cerr << "Unable to open file for writing." << std::endl;
return 1;
}
printHuffmanTable(huffmanCode);// Print Table
printHistogram(frequencyMap); // Print Histogram
std::streamsize originalSize, compressedSize;
originalSize = getFileSize(filename);// Print Original size
compressedSize = getFileSize(compressedFile);// Print Compressed size
if (originalSize >= 0) {
std::cout << "Original image size: " << originalSize << " bytes." << std::endl;
}
else {
std::cerr << "Error reading file size." << std::endl;
}
if (compressedSize >= 0) {
std::cout << "Compressed image size: " << compressedSize << " bytes." << std::endl;
}
else {
std::cerr << "Error reading file size." << std::endl;
}
//std::cout << "Compression ratio: " << (originalSize - compressedSize) / originalSize << " %" << std::endl;
}
// For decoding
else if (mode == "-d") {
// Decompression code
std::cout << "Decompressing " << filename << std::endl;
auto start = std::chrono::high_resolution_clock::now();
std::ifstream inputFile(filename, std::ios::binary);
if (!inputFile.is_open()) {
std::cerr << "Unable to open file." << std::endl;
return 1;
}
// read width and height
int width, height;
inputFile.read(reinterpret_cast<char*>(&width), sizeof(width));
inputFile.read(reinterpret_cast<char*>(&height), sizeof(height));
// read tree and reconstruct
Node* root = decodeHuffmanTree(inputFile);
// read rest of encoded data and decode
std::string encodedData(std::istreambuf_iterator<char>(inputFile), {});
std::vector<int> decodedImage = decodeData(root, encodedData);
std::cout << "File .hc successfully inputed" << std::endl;
inputFile.close();
//------------output below---------------
std::ofstream outputFile(filename2);
if (outputFile.is_open()) {
outputFile << "P2" << std::endl
<< width << " " << height << std::endl
<< "255" << std::endl;
for (int value : decodedImage) {
outputFile << value << " " ;
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Running time of depression is " << duration.count() << " milliseconds." << std::endl;
std::cout << "Image successfully decoded and saved to '" << filename2 << "'" << std::endl;
}
else {
std::cerr << "Unable to open file for writing." << std::endl;
return 1;
}
outputFile.close();
}
else {
std::cerr << "Invalid mode. Use -c for compression and -d for decompression." << std::endl;
return 1;
}
return 0;
}