Skip to content

Commit 44ef9f3

Browse files
committed
Start of Huffman implementation
+ Added Huffman class to build a dictionary with symbols and further encode an image BitStream.
1 parent 0691fa5 commit 44ef9f3

File tree

9 files changed

+407
-5
lines changed

9 files changed

+407
-5
lines changed

Debug/Debug.bg

3.33 KB
Binary file not shown.

Encoder.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "main.hpp"
33
#include "Logger.hpp"
44
#include "utils.hpp"
5+
#include "Huffman.hpp"
56

67
#include <cassert>
78

@@ -71,7 +72,7 @@ bool dc::Encoder::process(void) {
7172
float(output_length) / 8.f));
7273

7374
output_length += this->blocks->size() * this->blocks->front()->streamSize();
74-
output_length += 8 - (output_length % 8u); // Padding to next whole byte
75+
output_length += (8 - (output_length % 8u)) % 8u; // Padding to next whole byte
7576
output_length /= 8u;
7677

7778
this->writer = util::allocVar<util::BitStreamWriter>(output_length);
@@ -121,5 +122,24 @@ bool dc::Encoder::process(void) {
121122
* @brief Save the resulting stream to the destination.
122123
*/
123124
void dc::Encoder::saveResult(void) const {
125+
126+
util::Logger::WriteLn("\n", false);
127+
128+
util::Logger::WriteLn("[Encoder] Huffman:");
129+
algo::Huffman<> h;
130+
131+
this->writer->flush();
132+
util::BitStreamReader h_input(this->writer->get_buffer(), this->writer->get_position() / 8u);
133+
134+
util::BitStreamWriter* h_stream = h.encode(h_input);
135+
136+
#ifdef LOG_LOCAL
137+
h.printDict();
138+
#endif
139+
140+
delete h_stream;
141+
142+
util::Logger::WriteLn("\n", false);
143+
124144
ImageProcessor::saveResult(true);
125145
}

Huffman.cpp

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
#include "Huffman.hpp"
2+
#include <numeric>
3+
#include <queue>
4+
5+
#include "utils.hpp"
6+
#include "Logger.hpp"
7+
8+
9+
template<class T>
10+
algo::Huffman<T>::Huffman(void) {
11+
12+
}
13+
14+
template<class T>
15+
algo::Huffman<T>::~Huffman(void) {
16+
this->deleteTree(this->tree_root);
17+
}
18+
19+
template<class T>
20+
void algo::Huffman<T>::deleteTree(algo::Node<> *root) {
21+
if (root == nullptr) return;
22+
23+
this->deleteTree(root->left);
24+
this->deleteTree(root->right);
25+
delete root;
26+
}
27+
28+
/**
29+
* @brief Traverse Huffman tree starting from node and
30+
* add codes for leafs to the dictionary.
31+
* @param node
32+
* The starting node.
33+
* @param stream
34+
* The current stream of bits for a path in the tree.
35+
*/
36+
template<class T>
37+
size_t algo::Huffman<T>::buildDict(const algo::Node<> * const node, std::vector<bool> stream) {
38+
if (node == nullptr) {
39+
return 0u;
40+
}
41+
42+
// Check if leaf
43+
if (node->left == nullptr && node->right == nullptr) {
44+
const uint32_t size = uint32_t(stream.size());
45+
46+
this->dict[node->data] = Codeword {
47+
std::accumulate(stream.begin(), stream.end(), uint32_t(0u),
48+
[=](uint32_t x, uint32_t y) { return (x << 1u) | y; }),
49+
size
50+
};
51+
52+
return size;
53+
}
54+
55+
std::vector<bool> lstream(stream);
56+
lstream.push_back(false);
57+
stream.push_back(true);
58+
59+
return std::max(this->buildDict(node->left , lstream),
60+
this->buildDict(node->right, stream));
61+
}
62+
63+
/**
64+
* @brief Traverse Huffman tree starting from node and
65+
* decode symbols according to the dictionary.
66+
* @param node
67+
* The starting node.
68+
* @param reader
69+
* The bytestream to read from.
70+
*/
71+
template<class T>
72+
void algo::Huffman<T>::decode(const algo::Node<> * const node, util::BitStreamReader& reader,util::BitStreamWriter& writer) {
73+
if (node == nullptr) {
74+
return;
75+
}
76+
77+
// Check if leaf
78+
if (node->left == nullptr && node->right == nullptr) {
79+
writer.put(algo::Huffman<>::KEY_BITS, node->data);
80+
return;
81+
}
82+
83+
const uint8_t bit = reader.get_bit();
84+
85+
if (bit) {
86+
this->decode(node->right, reader, writer);
87+
} else {
88+
this->decode(node->left , reader, writer);
89+
}
90+
}
91+
92+
/**
93+
* @brief Encode bits of length sizeof(T) with Huffman encoding and
94+
* write the Huffman dict and the encoded data to an outputstream.
95+
*
96+
* @param reader
97+
* The bytestream to read from.
98+
* @return Returns a new bitstream with the encoded data.
99+
*/
100+
template<class T>
101+
util::BitStreamWriter* algo::Huffman<T>::encode(util::BitStreamReader& reader) {
102+
const size_t length = reader.get_size() * 8u;
103+
104+
// Calculate frequencies
105+
std::unordered_map<T, uint32_t> freqs;
106+
reader.set_position(0);
107+
108+
while(reader.get_position() != length) {
109+
const T word = T(reader.get(algo::Huffman<>::KEY_BITS));
110+
freqs[word]++;
111+
}
112+
113+
// Create priority queue to sort tree with Nodes with data from frequency
114+
std::priority_queue<algo::Node<>*, std::vector<algo::Node<>*>, algo::Node<>::comparator> pq;
115+
116+
for (const auto& pair: freqs) {
117+
pq.push(util::allocVar<algo::Node<>>(pair.first, pair.second));
118+
}
119+
120+
while (pq.size() > 1) {
121+
// Empty out queue and build leaves, starting with lowest freq
122+
// Result is a single Node with references to other Nodes in tree structure.
123+
algo::Node<> *left = pq.top(); pq.pop();
124+
algo::Node<> *right = pq.top(); pq.pop();
125+
126+
pq.push(util::allocVar<algo::Node<>>(-1, left->freq + right->freq, left, right));
127+
}
128+
129+
this->tree_root = pq.top();
130+
131+
const size_t h_table_bits = this->buildDict(this->tree_root, std::vector<bool>());
132+
const size_t h_dict_total_length = (algo::Huffman<>::KEY_BITS + h_table_bits)
133+
* this->dict.size() // Every {key: val} pair
134+
+ algo::Huffman<>::KEY_BITS // Length of table itself
135+
+ algo::Huffman<>::SIZE_BITS; // Bits per value
136+
137+
util::Logger::WriteLn(std::string_format("[Huffman] {key:%d, val:%d} for %d entries + %d hdr bits (%.1f total bytes).",
138+
algo::Huffman<>::KEY_BITS, h_table_bits, this->dict.size(),
139+
(algo::Huffman<>::KEY_BITS + algo::Huffman<>::SIZE_BITS),
140+
float(h_dict_total_length) / 8.0f));
141+
142+
util::BitStreamWriter *writer = util::allocVar<util::BitStreamWriter>((h_dict_total_length + length) / 8 + 1);
143+
144+
writer->put(algo::Huffman<>::KEY_BITS , uint32_t(this->dict.size())); ///< Put table size
145+
writer->put(algo::Huffman<>::SIZE_BITS, uint32_t(h_table_bits)); ///< Put bit length of a table value
146+
147+
for (const auto& pair : this->dict) {
148+
writer->put(algo::Huffman<>::KEY_BITS, pair.first); // Put Key
149+
writer->put(h_table_bits, pair.second.word); // Put Val
150+
}
151+
152+
153+
/*******************************************************************************/
154+
155+
/*ori*/
156+
reader.set_position(0);
157+
while(reader.get_position() != length) {
158+
const T word = T(reader.get(algo::Huffman<>::KEY_BITS));
159+
util::Logger::Write(std::string_format("%X", word), false);
160+
} util::Logger::WriteLn(std::string_format(" (%d bytes)", length/8), false);
161+
162+
/*encoded*/
163+
reader.set_position(0);
164+
while(reader.get_position() != length) {
165+
const T word = T(reader.get(algo::Huffman<>::KEY_BITS));
166+
util::Logger::Write(std::string_format("%X", this->dict[word]), false);
167+
168+
writer->put(this->dict[word].len, this->dict[word].word); //TODO
169+
} util::Logger::WriteLn("", false);
170+
171+
/*decoded*/
172+
util::BitStreamReader enc(writer->get_buffer(), (writer->get_position() / 8) + 1);
173+
size_t table_size = enc.get(algo::Huffman<>::KEY_BITS);
174+
size_t entry_bits = enc.get(algo::Huffman<>::SIZE_BITS);
175+
enc.set_position(enc.get_position() + (algo::Huffman<>::KEY_BITS + entry_bits) * table_size);
176+
177+
util::BitStreamWriter out(length/8);
178+
179+
while (enc.get_position() <= enc.get_size() * 8u) {
180+
this->decode(this->tree_root, enc, out);
181+
} util::Logger::WriteLn("", false);
182+
183+
out.set_position(0);
184+
for (size_t i = 0; i < out.get_size(); i++) {
185+
util::Logger::Write(std::string_format("%X", out.get_buffer()[i]), false);
186+
} util::Logger::WriteLn("", false);
187+
188+
util::Logger::WriteLn("", false);
189+
this->printTree();
190+
util::Logger::WriteLn("", false);
191+
192+
return writer;
193+
}
194+
195+
/**
196+
* @brief Read the Huffman dict from the stream and
197+
* write the decoded data to an outputstream.
198+
*
199+
* @param reader
200+
* The bytestream to read from.
201+
* @return Returns true if current Node has higher frequency.
202+
*/
203+
template<class T>
204+
util::BitStreamWriter* algo::Huffman<T>::decode(util::BitStreamReader& reader) {
205+
const size_t table_size = reader.get(algo::Huffman<>::KEY_BITS); ///< Get table size
206+
const size_t entry_bits = reader.get(algo::Huffman<>::SIZE_BITS); ///< Get entry bit length
207+
const size_t data_bits = reader.get_size() * 8u; ///< Amount of data bits
208+
209+
for (size_t i = 0; i < table_size; i++) {
210+
this->dict[T(reader.get(algo::Huffman<>::KEY_BITS))] = Codeword { reader.get(entry_bits), 0u };
211+
}
212+
213+
214+
// TODO Create tree from dict
215+
// this->deleteTree(this->tree_root);
216+
// this->tree_root = util::allocVar<Node<>>(-1, 0);
217+
218+
219+
// for (const auto& pair : this->dict) {
220+
// // Sink leaf
221+
// Node<> *leaf = util::allocVar<Node<>>(pair.first);
222+
// }
223+
224+
225+
226+
227+
util::BitStreamWriter *writer = util::allocVar<util::BitStreamWriter>(data_bits);
228+
229+
// Consume all other data, bit by bit and traverse Huffman tree to find word in dict
230+
while (reader.get_position() <= data_bits) {
231+
this->decode(this->tree_root, reader, *writer);
232+
}
233+
234+
return writer;
235+
}
236+
237+
template<class T>
238+
void algo::Huffman<T>::printDict(void) {
239+
util::Logger::WriteLn("[Huffman] Dictionary:");
240+
241+
for (const auto& pair : this->dict) {
242+
util::Logger::WriteLn(std::string_format("%02X: %8X (%d bits)", pair.first, pair.second.word,pair.second.len),
243+
false);
244+
}
245+
}
246+
247+
template<class T>
248+
static void printNode(const algo::Node<T> * const node, std::string s) {
249+
if (node == nullptr) {
250+
return;
251+
}
252+
253+
if (node->left == nullptr && node->right == nullptr) {
254+
util::Logger::WriteLn(s + std::string_format(" => %X", node->data), false);
255+
return;
256+
}
257+
258+
printNode(node->left, s + "0");
259+
printNode(node->right, s + "1");
260+
}
261+
262+
template<class T>
263+
void algo::Huffman<T>::printTree(void) {
264+
util::Logger::WriteLn("[Huffman] Tree:");
265+
266+
printNode(this->tree_root, "");
267+
}
268+
269+
template class algo::Node<uint8_t>;
270+
template class algo::Huffman<uint8_t>;

0 commit comments

Comments
 (0)