-
Notifications
You must be signed in to change notification settings - Fork 0
/
ImageBase.cpp
336 lines (289 loc) · 13.6 KB
/
ImageBase.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
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
#include "ImageBase.hpp"
#include "utils.hpp"
#include "Logger.hpp"
#include "Block.hpp"
#include "Huffman.hpp"
static const std::string NO_VALUE("");
/**
* @brief Default ctor
*
* @param source_file
* Path to a raw image file.
* @param width
* The width in pixels for the image.
* @param height
* The height in pixels for the image.
*/
dc::ImageBase::ImageBase(const std::string &source_file, const uint16_t &width, const uint16_t &height)
: width(width), height(height)
{
try {
this->raw = util::readBinaryFile(source_file);
} catch (Exceptions::FileReadException const& e) {
util::Logger::WriteLn(e.getMessage());
exit(-1);
}
this->reader = util::allocVar<util::BitStreamReader>(this->raw->data(), this->raw->size());
}
/**
* @brief Ctor when source stream is known (video).
*
* @param raw
* Pointer to start of image bytes.
* @param width
* The width in pixels for the image.
* @param height
* The height in pixels for the image.
*/
dc::ImageBase::ImageBase(uint8_t * const raw, const uint16_t &width, const uint16_t &height)
: width(width), height(height)
, raw(nullptr)
, reader(util::allocVar<util::BitStreamReader>(raw, width * height))
{
// Empty
}
/**
* @brief Default dtor
*/
dc::ImageBase::~ImageBase(void) {
util::deallocVar(this->raw);
util::deallocVar(this->reader);
}
////////////////////////////////////////////////////////////////////////////////////
/**
* @brief Default ctor for 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
* The width in pixels for the image.
* @param height
* The height in pixels for the image.
* @param use_rle
* Whether to use Run-Length Encoding.
* Currently enabling this will drop trailing zeroes in an encoded block.
* @param quant_m
* The quantization matrix to use.
*/
dc::ImageProcessor::ImageProcessor(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)
: ImageBase(source_file, width, height),
use_rle(use_rle), quant_m(quant_m),
dest_file(dest_file),
blocks(util::allocVar<std::vector<Block<>*>>())
{
// Empty
}
/**
* @brief Default ctor for decoder, settings need to be determined from the reader stream.
*
* @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).
*/
dc::ImageProcessor::ImageProcessor(const std::string &source_file, const std::string &dest_file)
: ImageBase(source_file, 0u, 0u) ///< Create stream
, dest_file(dest_file)
, blocks(util::allocVar<std::vector<Block<>*>>())
{
// Assume input is encoded image and settings should be determined from the bytestream
// Perform Huffman decompress (if used, first bit is '1' else '0')
algo::Huffman<> hm;
util::BitStreamReader *hm_output = hm.decode(*this->reader);
util::Logger::WriteLn("", false);
#ifdef LOG_LOCAL
util::Logger::WriteLn("\n", false);
hm.printDict();
util::Logger::WriteLn("\n", false);
#endif
// Replace reader with result from decompressed Huffman stream
if (hm_output != nullptr) {
util::deallocVar(this->reader);
this->reader = hm_output;
}
// Read Matrix
this->quant_m = dc::MatrixReader<>::fromBitstream(*this->reader);
// Read other settings in same order as they were presumably written to the encoded stream
this->use_rle = this->reader->get(dc::ImageProcessor::RLE_BITS);
this->width = uint16_t(this->reader->get(dc::ImageProcessor::DIM_BITS));
this->height = uint16_t(this->reader->get(dc::ImageProcessor::DIM_BITS));
}
/**
* @brief Ctor for video processor Frame.
*
* @param raw
* Pointer to vidio strteam buffer where a frame starts.
* @param width
* The width in pixels for the image.
* @param height
* The height in pixels for the image.
* @param use_rle
* Whether to use Run-Length Encoding.
* Currently enabling this will drop trailing zeroes in an encoded block.
* @param quant_m
* The quantization matrix to use.
*/
dc::ImageProcessor::ImageProcessor(uint8_t * const raw,
const uint16_t &width, const uint16_t &height,
const bool &use_rle, MatrixReader<> &quant_m)
: ImageBase(raw, width, height)
, use_rle(use_rle), quant_m(quant_m)
, dest_file(NO_VALUE)
, blocks(util::allocVar<std::vector<Block<>*>>())
, macroblocks(util::allocVar<std::vector<MacroBlock*>>())
{
// Empty
}
/**
* @brief Default dtor
*/
dc::ImageProcessor::~ImageProcessor(void) {
util::deallocVector(this->blocks);
util::deallocVector(this->macroblocks);
util::deallocVar(this->writer);
}
/**
* @brief Start processing by creating blocks for the given stream.
*
* @param source_block_buffer
* The source strean te create blocks from.
* Usually the reader stream when encoding, and the writer stream when decoding.
* @return Returns true on success.
*/
bool dc::ImageProcessor::process(uint8_t * const source_block_buffer) {
util::Logger::WriteLn("[ImageProcessor] Creating blocks...");
uint8_t *block_starts[dc::BlockSize] = { nullptr };
constexpr size_t block_size = dc::BlockSize * dc::BlockSize; ///< Total values inside 1 Block
const size_t blockx = this->width / dc::BlockSize; ///< Amount of Blocks on a row
const size_t blocky = this->height / dc::BlockSize; ///< Amount of Blocks in a column
// Reserve space for blocks
this->blocks->reserve(blockx * blocky);
// Get only pointers to start of each block row and save to Block in this->blocks
for (size_t b_y = 0; b_y < blocky; b_y++) { ///< Block y coord
for (size_t b_x = 0; b_x < blockx; b_x++) { ///< Block x coord
for (size_t y = 0; y < dc::BlockSize; y++) { ///< Row inside block
block_starts[y] = (source_block_buffer + // Buffer start
( (b_y * block_size * blockx) // Block row start
+ (b_x * dc::BlockSize) // Block column start
+ (y * this->width) // Row within block
+ (0))); // Column withing block
}
// Create new block with the found row offsets
this->blocks->push_back(util::allocVar<dc::Block<>>(block_starts));
}
}
// Create zig-zag-pattern LUT
Block<dc::BlockSize>::CreateZigZagLUT();
return true;
}
bool dc::ImageProcessor::processMacroBlocks(uint8_t * const source_block_buffer) {
util::Logger::WriteLn("[ImageProcessor] Creating macro blocks...");
uint8_t *block_starts[dc::MacroBlockSize] = { nullptr };
constexpr size_t block_size = dc::MacroBlockSize * dc::MacroBlockSize; ///< Total values inside 1 Block
const size_t blockx = this->width / dc::MacroBlockSize; ///< Amount of Blocks on a row
const size_t blocky = this->height / dc::MacroBlockSize; ///< Amount of Blocks in a column
// Reserve space for blocks
this->macroblocks->reserve(blockx * blocky);
// Get only pointers to start of each block row and save to Block in this->blocks
for (size_t b_y = 0; b_y < blocky; b_y++) { ///< Block y coord
for (size_t b_x = 0; b_x < blockx; b_x++) { ///< Block x coord
for (size_t y = 0; y < dc::MacroBlockSize; y++) { ///< Row inside block
block_starts[y] = (source_block_buffer + // Buffer start
( (b_y * block_size * blockx) // Block row start
+ (b_x * dc::MacroBlockSize) // Block column start
+ (y * this->width) // Row within block
+ (0))); // Column withing block
}
// Create new block with the found row offsets
this->macroblocks->push_back(util::allocVar<dc::MacroBlock>(block_starts,
b_x * dc::MacroBlockSize,
b_y * dc::MacroBlockSize));
}
}
// Create zig-zag-pattern LUT
Block<dc::BlockSize>::CreateZigZagLUT();
return true;
}
dc::MacroBlock* dc::ImageProcessor::getBlockAtCoord(int16_t x, int16_t y) const {
uint8_t *block_starts[dc::MacroBlockSize] = { nullptr };
// Since Blocks only take pointer to their starting column for each row,
// no blocks can be made outside of the boundaries.
// This could be solved bycreating a duplicate of the reader buffer
// but with (this->width + 2 * dc::MacroBlockSize) * (this->height + 2 * dc::MacroBlockSize)
// in size, so referencing of coords outside of the normal frame is possible.
// FIXME For now, just clamp requested coord within frame...
const int16_t b_x = std::clamp(x, int16_t(0), int16_t(this->width - dc::MacroBlockSize));
const int16_t b_y = std::clamp(y, int16_t(0), int16_t(this->height - dc::MacroBlockSize));
for (int16_t row = 0; row < dc::MacroBlockSize; row++) { ///< Row inside block
block_starts[row] = (this->reader->get_buffer() + // Buffer start
( ((b_y + row) * this->width) // Block row start + Row within block
+ (b_x) // Block column start
)); // Column withing block
}
return util::allocVar<dc::MacroBlock>(block_starts, b_x, b_y);
}
void dc::ImageProcessor::copyMacroblockToMatchingMicroblocks(dc::MacroBlock& mb) {
constexpr size_t mblock_size = dc::MacroBlockSize * dc::MacroBlockSize; ///< Macro px size
constexpr size_t block_size = dc::BlockSize * dc::BlockSize; ///< Micro px size
// For 4 micro and 16 macro size, 16 microblocks in macro, or 4x4 grid
constexpr size_t micro_per_macro_row = (mblock_size / block_size) / dc::BlockSize;
const size_t blockx = this->width / dc::BlockSize; ///< Amount of Micro on a row
const size_t mb_x = size_t(mb.getCoord().x0) / dc::MacroBlockSize; ///< Macro x idx
const size_t mb_y = size_t(mb.getCoord().y0) / dc::MacroBlockSize; ///< Macro y idx
// Micro at (x, y) = this->blocks[y * blockx + x]
// Micro in first col of Macro at (mb_x, mb_y):
// this->blocks[mb_y * mblocky + mb_x]
for (size_t y = 0; y < micro_per_macro_row; y++) {
// For each row of Micros in Macro
dc::MicroBlock **row_start = this->blocks->data() // Buffer start
+ (mb_y * blockx * micro_per_macro_row) // Start of Micro row at Macro
+ (mb_x * micro_per_macro_row) // Start of Micro col at Macro
+ (y * blockx + 0); // First Micro in Macro row
for (size_t x = 0; x < micro_per_macro_row; x++) {
// For each Micro in Macro row
for (size_t row = 0; row < dc::BlockSize; row++) {
// Copy the correct 4x4 piece of Macro to Micro, from expanded to expanded
std::copy_n(mb.getExpandedRow( y * dc::BlockSize + row ) + x * dc::BlockSize,
dc::BlockSize,
row_start[x]->getExpandedRow(row));
}
// Encode MicroBlock
row_start[x]->processDCTDivQ(this->quant_m.getData());
row_start[x]->createRLESequence();
// Decode MicroBlock so next p-frame can use this as new diff
row_start[x]->processIDCTMulQ(this->quant_m.getData());
}
}
}
/**
* @brief Save the writer stream to this->dest_file,
* and give some compression stats.
*
* @param encoded
* Whether this was called after encoding (true) or decoding (false).
*/
void dc::ImageProcessor::saveResult(bool encoded) const {
const size_t total_length = this->writer->get_last_byte_position(); // Total final write length in bytes
#if 1
// This will only write whole bytes; if pos % 8 != 0, last byte is skipped
// Edit: Padding is now added after settings, so only whole bytes are in buffer.
util::writeBinaryFile(this->dest_file,
this->writer->get_buffer(),
total_length);
#else
// Will take last byte into account
std::ofstream file(this->dest_file, std::ofstream::binary);
util::write(file, *this->writer);
#endif
util::Logger::WriteLn(std::string_format("[ImageProcessor] Original file size: %8d bytes", this->raw->size()));
util::Logger::WriteLn(std::string_format("[ImageProcessor] %scoded size: %8d bytes => Ratio: %.2f%%",
(encoded ? "En" : "De"),
total_length,
(float(total_length) / this->raw->size() * 100)));
util::Logger::WriteLn("[ImageProcessor] Saved file at: " + this->dest_file);
}