-
Notifications
You must be signed in to change notification settings - Fork 0
/
ImageEncoder.cpp
182 lines (149 loc) · 5.93 KB
/
ImageEncoder.cpp
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
#include "ImageEncoder.hpp"
#include "main.hpp"
#include "Logger.hpp"
#include "utils.hpp"
#include "Huffman.hpp"
#include <cassert>
/**
* @brief dc::Encoder::Encoder
*
* @param source_file
* Path to a raw image file.
* @param dest_file
* Path to the destination file (path needs to exist, file will be overwritten).
* @param width
* @param height
* @param use_rle
* @param quant_m
*/
dc::ImageEncoder::ImageEncoder(const std::string &source_file, const std::string &dest_file,
const uint16_t &width, const uint16_t &height, const bool &use_rle,
MatrixReader<> &quant_m)
: ImageProcessor(source_file, dest_file, width, height, use_rle, quant_m)
{
assert(this->width % dc::BlockSize == 0);
assert(this->height % dc::BlockSize == 0);
assert(this->reader->get_size() == size_t(this->width * this->height));
}
/**
* @brief Default dtor
*/
dc::ImageEncoder::~ImageEncoder(void) {
// Empty
}
/**
* @brief Process the raw image for encoding.
*
* 1. Create Blocks
* 2. Determine header length
* 3. Estimate final stream length (header + size for each Block)
* 4. Write header (encoding settings)
* For each Block:
* 5. Perform DCT and divide with the quant_matrix
* 6. Create the RLE sequence
* 7. Stream the results to the byte stream, ignoring trailing zeroes if use_rle == true
*
* @return Returns true on success.
*/
bool dc::ImageEncoder::process(void) {
bool success = true;
util::Logger::WriteLn("[ImageEncoder] Processing image...");
// Pre-process image
success = ImageProcessor::process(this->reader->get_buffer());
// Write setting header
util::Logger::WriteLn("[ImageEncoder] Creating settings header...");
size_t output_length;
const uint8_t quant_bit_len = this->quant_m.getMaxBitLength();
output_length = dc::ImageProcessor::RLE_BITS // Bit for RLE setting
+ dc::ImageProcessor::DIM_BITS * 2u // 2 times bits for image dimension
+ dc::MatrixReader<>::SIZE_LEN_BITS // Bits to signify size of quant_matrix contents
+ (quant_bit_len // Size of quantmatrix
* dc::BlockSize * dc::BlockSize);
util::Logger::WriteLn(std::string_format("[ImageEncoder] Settings header length: %.1f bytes.",
float(output_length) / 8.f));
output_length += this->blocks->size() * this->blocks->front()->streamSize();
#ifndef ENABLE_HUFFMAN
output_length++; // Add one bit to signal Huffman is not enabled.
#endif
output_length = util::round_to_byte(output_length); // Padding to next whole byte
this->writer = util::allocVar<util::BitStreamWriter>(output_length);
#ifndef ENABLE_HUFFMAN
this->writer->put_bit(0); // '0': No Huffman sequence present.
#endif
// Write matrix data first
this->quant_m.write(*this->writer);
// Write other settings
this->writer->put(dc::ImageProcessor::RLE_BITS, uint32_t(this->use_rle));
this->writer->put(dc::ImageProcessor::DIM_BITS, this->width);
this->writer->put(dc::ImageProcessor::DIM_BITS, this->height);
const size_t block_count = this->blocks->size();
size_t blockid = 0u;
util::Logger::WriteLn("[ImageEncoder] Processing Blocks...");
util::Logger::WriteProgress(0, block_count);
#ifdef LOG_LOCAL
for (Block<>* b : *this->blocks) {
util::Logger::WriteLn(std::string_format("Block % 3d:", blockid++));
b->printExpanded();
util::Logger::WriteLn("", false);
util::Logger::WriteLn("After DCT and quantization:");
b->processDCTDivQ(this->quant_m.getData());
b->printExpanded();
util::Logger::WriteLn("", false);
b->printZigzag();
b->createRLESequence();
b->printRLE();
b->streamEncoded(*this->writer, this->use_rle);
util::Logger::WriteLn("", false);
}
#else
#ifdef ENABLE_OPENMP
#pragma omp parallel for shared(blockid) schedule(dynamic)
for (auto it = this->blocks->begin(); it < this->blocks->end(); it++) {
Block<> *b = *it;
b->processDCTDivQ(this->quant_m.getData());
b->createRLESequence();
#pragma omp atomic
++blockid;
#pragma omp critical
util::Logger::WriteProgress(blockid, block_count);
}
// Writing results must happen in sequence
for (Block<>* b : *this->blocks) {
b->streamEncoded(*this->writer, this->use_rle);
}
#else
for (Block<>* b : *this->blocks) {
b->processDCTDivQ(this->quant_m.getData());
b->createRLESequence();
b->streamEncoded(*this->writer, this->use_rle);
util::Logger::WriteProgress(++blockid, block_count);
}
#endif
#endif
util::Logger::WriteLn("", false);
#ifdef ENABLE_HUFFMAN
util::BitStreamReader hm_input(this->writer->get_buffer(),
this->writer->get_last_byte_position());
algo::Huffman<> hm;
util::BitStreamWriter *hm_output = hm.encode(hm_input);
#ifdef LOG_LOCAL
util::Logger::WriteLn("\n", false);
hm.printDict();
// util::Logger::WriteLn("\n", false);
// hm.printTree();
util::Logger::WriteLn("\n", false);
#endif
if (hm_output != nullptr) {
util::deallocVar(this->writer);
this->writer = hm_output;
}
util::Logger::WriteLn("", false);
#endif
return success;
}
/**
* @brief Save the resulting stream to the destination.
*/
void dc::ImageEncoder::saveResult(void) const {
ImageProcessor::saveResult(true);
}