sjpeg 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,290 @@
1
+ // Copyright 2018 Google Inc.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // Dichotomy loop and default search implementation
16
+ //
17
+ // Author: Skal (pascal.massimino@gmail.com)
18
+
19
+ #include <math.h>
20
+ #include <stdio.h>
21
+ #include <stdlib.h>
22
+ #include <stdint.h>
23
+
24
+ #include "sjpegi.h"
25
+
26
+ using namespace sjpeg;
27
+
28
+ ////////////////////////////////////////////////////////////////////////////////
29
+ // dichotomy
30
+
31
+ #define DBG_PRINT 0
32
+
33
+ // convergence is considered reached if |dq| < kdQLimit.
34
+ static const float kdQLimit = 0.15;
35
+
36
+ static float Clamp(float v, float min, float max) {
37
+ return (v < min) ? min : (v > max) ? max : v;
38
+ }
39
+
40
+ bool SearchHook::Setup(const EncoderParam& param) {
41
+ for_size = (param.target_mode == EncoderParam::TARGET_SIZE);
42
+ target = param.target_value;
43
+ tolerance = param.tolerance / 100.;
44
+ qmin = (param.qmin < 0) ? 0 : param.qmin;
45
+ qmax = (param.qmax > 100) ? 100 :
46
+ (param.qmax < param.qmin) ? param.qmin : param.qmax;
47
+ q = Clamp(SjpegEstimateQuality(param.GetQuantMatrix(0), false), qmin, qmax);
48
+ value = 0; // undefined for at this point
49
+ pass = 0;
50
+ return true;
51
+ }
52
+
53
+ bool SearchHook::Update(float result) {
54
+ value = result;
55
+ bool done = (fabs(value - target) < tolerance * target);
56
+ if (done) return true;
57
+
58
+ if (value > target) {
59
+ qmax = q;
60
+ } else {
61
+ qmin = q;
62
+ }
63
+ const float last_q = q;
64
+ q = (qmin + qmax) / 2.;
65
+ done = (fabs(q - last_q) < kdQLimit);
66
+ if (DBG_PRINT) {
67
+ printf(" -> next-q=%.2f\n", last_q);
68
+ }
69
+ return done;
70
+ }
71
+
72
+ void SearchHook::NextMatrix(int idx, uint8_t dst[64]) {
73
+ SetQuantMatrix(kDefaultMatrices[idx], GetQFactor(q), dst);
74
+ }
75
+
76
+ ////////////////////////////////////////////////////////////////////////////////
77
+ // Helpers for the search
78
+
79
+ void Encoder::StoreRunLevels(DCTCoeffs* coeffs) {
80
+ assert(use_extra_memory_);
81
+ assert(reuse_run_levels_);
82
+
83
+ const QuantizeBlockFunc quantize_block = use_trellis_ ? TrellisQuantizeBlock
84
+ : quantize_block_;
85
+ if (use_trellis_) InitCodes(true);
86
+
87
+ ResetDCs();
88
+ nb_run_levels_ = 0;
89
+ int16_t* in = in_blocks_;
90
+ for (int n = 0; n < mb_w_ * mb_h_; ++n) {
91
+ CheckBuffers();
92
+ for (int c = 0; c < nb_comps_; ++c) {
93
+ for (int i = 0; i < nb_blocks_[c]; ++i) {
94
+ RunLevel* const run_levels = all_run_levels_ + nb_run_levels_;
95
+ const int dc = quantize_block(in, c, &quants_[quant_idx_[c]],
96
+ coeffs, run_levels);
97
+ coeffs->dc_code_ = GenerateDCDiffCode(dc, &DCs_[c]);
98
+ nb_run_levels_ += coeffs->nb_coeffs_;
99
+ ++coeffs;
100
+ in += 64;
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ void Encoder::LoopScan() {
107
+ assert(use_extra_memory_);
108
+ assert(reuse_run_levels_);
109
+
110
+ if (use_adaptive_quant_) {
111
+ CollectHistograms();
112
+ } else {
113
+ CollectCoeffs(); // we just need the coeffs
114
+ }
115
+
116
+ const size_t nb_mbs = mb_w_ * mb_h_ * mcu_blocks_;
117
+ DCTCoeffs* const base_coeffs = Alloc<DCTCoeffs>(nb_mbs);
118
+ if (base_coeffs == nullptr) return;
119
+
120
+ uint8_t opt_quants[2][64];
121
+
122
+ // Dichotomy passes
123
+ float best = 0.; // best distance
124
+ float best_q = 0.; // informative value to return to the user
125
+ float best_result = 0.;
126
+ for (int p = 0; p < passes_; ++p) {
127
+ search_hook_->pass = p;
128
+ // set new matrices to evaluate
129
+ for (int c = 0; c < 2; ++c) {
130
+ search_hook_->NextMatrix(c, quants_[c].quant_);
131
+ FinalizeQuantMatrix(&quants_[c], q_bias_);
132
+ }
133
+ if (use_adaptive_quant_) {
134
+ AnalyseHisto(); // adjust quant_[] matrices
135
+ }
136
+
137
+ float result;
138
+ if (search_hook_->for_size) {
139
+ // compute pass to store coeffs / runs / dc_code_
140
+ StoreRunLevels(base_coeffs);
141
+ if (optimize_size_) {
142
+ StoreOptimalHuffmanTables(nb_mbs, base_coeffs);
143
+ if (use_trellis_) InitCodes(true);
144
+ }
145
+ result = ComputeSize(base_coeffs);
146
+ } else {
147
+ // if we're just targeting PSNR, we don't need to compute the
148
+ // run/levels within the loop. We just need to quantize the coeffs
149
+ // and measure the distortion.
150
+ result = ComputePSNR();
151
+ }
152
+ if (DBG_PRINT) printf("pass #%d: q=%.2f value:%.2f ",
153
+ search_hook_->pass, search_hook_->q, result);
154
+
155
+ if (p == 0 || fabs(result - search_hook_->target) < best) {
156
+ // save the matrices for later, if they are better
157
+ for (int c = 0; c < 2; ++c) {
158
+ CopyQuantMatrix(quants_[c].quant_, opt_quants[c]);
159
+ }
160
+ best = fabs(result - search_hook_->target);
161
+ best_q = search_hook_->q;
162
+ best_result = result;
163
+ }
164
+ if (search_hook_->Update(result)) break;
165
+ }
166
+ // transfer back the final matrices
167
+ SetQuantMatrices(opt_quants);
168
+ for (int c = 0; c < 2; ++c) FinalizeQuantMatrix(&quants_[c], q_bias_);
169
+
170
+ // return informative values to the user
171
+ search_hook_->q = best_q;
172
+ search_hook_->value = best_result;
173
+
174
+ // optimize Huffman table now, if we haven't already during the search
175
+ if (!search_hook_->for_size) {
176
+ StoreRunLevels(base_coeffs);
177
+ if (optimize_size_) {
178
+ StoreOptimalHuffmanTables(nb_mbs, base_coeffs);
179
+ }
180
+ }
181
+
182
+ // finish bitstream
183
+ WriteDQT();
184
+ WriteSOF();
185
+ WriteDHT();
186
+ WriteSOS();
187
+ FinalPassScan(nb_mbs, base_coeffs);
188
+
189
+ Free(base_coeffs);
190
+ }
191
+
192
+ ////////////////////////////////////////////////////////////////////////////////
193
+ // Size & PSNR computation, mostly for dichotomy
194
+
195
+ size_t Encoder::HeaderSize() const {
196
+ size_t size = 0;
197
+ size += 20; // APP0
198
+ size += app_markers_.size();
199
+ if (exif_.size() > 0) {
200
+ size += 8 + exif_.size();
201
+ }
202
+ if (iccp_.size() > 0) {
203
+ const size_t chunk_size_max = 0xffff - 12 - 4;
204
+ const size_t num_chunks = (iccp_.size() - 1) / chunk_size_max + 1;
205
+ size += num_chunks * (12 + 4 + 2);
206
+ size += iccp_.size();
207
+ }
208
+ if (xmp_.size() > 0) {
209
+ size += 2 + 2 + 29 + xmp_.size();
210
+ }
211
+ size += 2 * 65 + 2 + 2; // DQT
212
+ size += 8 + 3 * nb_comps_ + 2; // SOF
213
+ size += 6 + 2 * nb_comps_ + 2; // SOS
214
+ size += 2; // EOI
215
+ // DHT:
216
+ for (int c = 0; c < (nb_comps_ == 1 ? 1 : 2); ++c) { // luma, chroma
217
+ for (int type = 0; type <= 1; ++type) { // dc, ac
218
+ const HuffmanTable* const h = Huffman_tables_[type * 2 + c];
219
+ size += 2 + 3 + 16 + h->nb_syms_;
220
+ }
221
+ }
222
+ return size * 8;
223
+ }
224
+
225
+ void Encoder::BlocksSize(int nb_mbs, const DCTCoeffs* coeffs,
226
+ const RunLevel* rl,
227
+ BitCounter* const bc) const {
228
+ for (int n = 0; n < nb_mbs; ++n) {
229
+ const DCTCoeffs& c = coeffs[n];
230
+ const int idx = c.idx_;
231
+ const int q_idx = quant_idx_[idx];
232
+
233
+ // DC
234
+ const int dc_len = c.dc_code_ & 0x0f;
235
+ const uint32_t code = dc_codes_[q_idx][dc_len];
236
+ bc->AddPackedCode(code);
237
+ if (dc_len) bc->AddBits(c.dc_code_ >> 4, dc_len);
238
+
239
+ // AC
240
+ const uint32_t* const codes = ac_codes_[q_idx];
241
+ for (int i = 0; i < c.nb_coeffs_; ++i) {
242
+ int run = rl[i].run_;
243
+ while (run & ~15) { // escapes
244
+ bc->AddPackedCode(codes[0xf0]);
245
+ run -= 16;
246
+ }
247
+ const uint32_t suffix = rl[i].level_;
248
+ const size_t nbits = suffix & 0x0f;
249
+ const int sym = (run << 4) | nbits;
250
+ bc->AddPackedCode(codes[sym]);
251
+ bc->AddBits(suffix >> 4, nbits);
252
+ }
253
+ if (c.last_ < 63) bc->AddPackedCode(codes[0x00]); // EOB
254
+ rl += c.nb_coeffs_;
255
+ }
256
+ }
257
+
258
+ float Encoder::ComputeSize(const DCTCoeffs* coeffs) {
259
+ InitCodes(false);
260
+ size_t size = HeaderSize();
261
+ BitCounter bc;
262
+ BlocksSize(mb_w_ * mb_h_ * mcu_blocks_, coeffs, all_run_levels_, &bc);
263
+ size += bc.Size();
264
+ return size / 8.f;
265
+ }
266
+
267
+ ////////////////////////////////////////////////////////////////////////////////
268
+
269
+ static float GetPSNR(uint64_t err, uint64_t size) {
270
+ // This expression is written such that it gives the same result on ARM
271
+ // and x86 (for large values of err/size in particular). Don't change it!
272
+ return (err > 0 && size > 0) ? 4.3429448f * log(size / (err / 255. / 255.))
273
+ : 99.f;
274
+ }
275
+
276
+ float Encoder::ComputePSNR() const {
277
+ uint64_t error = 0;
278
+ const int16_t* in = in_blocks_;
279
+ const size_t nb_mbs = mb_w_ * mb_h_;
280
+ for (size_t n = 0; n < nb_mbs; ++n) {
281
+ for (int c = 0; c < nb_comps_; ++c) {
282
+ const Quantizer* const Q = &quants_[quant_idx_[c]];
283
+ for (int i = 0; i < nb_blocks_[c]; ++i) {
284
+ error += quantize_error_(in, Q);
285
+ in += 64;
286
+ }
287
+ }
288
+ }
289
+ return GetPSNR(error, 64ull * nb_mbs * mcu_blocks_);
290
+ }