/src/libjxl/lib/jxl/quantizer.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) the JPEG XL Project Authors. All rights reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style |
4 | | // license that can be found in the LICENSE file. |
5 | | |
6 | | #include "lib/jxl/quantizer.h" |
7 | | |
8 | | #include <stdio.h> |
9 | | #include <string.h> |
10 | | |
11 | | #include <algorithm> |
12 | | |
13 | | #include "lib/jxl/base/compiler_specific.h" |
14 | | #include "lib/jxl/field_encodings.h" |
15 | | #include "lib/jxl/fields.h" |
16 | | #include "lib/jxl/image.h" |
17 | | #include "lib/jxl/image_ops.h" |
18 | | #include "lib/jxl/quant_weights.h" |
19 | | |
20 | | namespace jxl { |
21 | | |
22 | | static const int32_t kDefaultQuant = 64; |
23 | | |
24 | | constexpr int32_t Quantizer::kQuantMax; |
25 | | |
26 | | Quantizer::Quantizer(const DequantMatrices* dequant) |
27 | 10.9k | : Quantizer(dequant, kDefaultQuant, kGlobalScaleDenom / kDefaultQuant) {} |
28 | | |
29 | | Quantizer::Quantizer(const DequantMatrices* dequant, int quant_dc, |
30 | | int global_scale) |
31 | 10.9k | : global_scale_(global_scale), quant_dc_(quant_dc), dequant_(dequant) { |
32 | 10.9k | JXL_ASSERT(dequant_ != nullptr); |
33 | 10.9k | RecomputeFromGlobalScale(); |
34 | 10.9k | inv_quant_dc_ = inv_global_scale_ / quant_dc_; |
35 | | |
36 | 10.9k | memcpy(zero_bias_, kZeroBiasDefault, sizeof(kZeroBiasDefault)); |
37 | 10.9k | } |
38 | | |
39 | | void Quantizer::ComputeGlobalScaleAndQuant(float quant_dc, float quant_median, |
40 | 0 | float quant_median_absd) { |
41 | | // Target value for the median value in the quant field. |
42 | 0 | const float kQuantFieldTarget = 5; |
43 | | // We reduce the median of the quant field by the median absolute deviation: |
44 | | // higher resolution on highly varying quant fields. |
45 | 0 | float scale = kGlobalScaleDenom * (quant_median - quant_median_absd) / |
46 | 0 | kQuantFieldTarget; |
47 | | // Ensure that new_global_scale is positive and no more than 1<<15. |
48 | 0 | if (scale < 1) scale = 1; |
49 | 0 | if (scale > (1 << 15)) scale = 1 << 15; |
50 | 0 | int new_global_scale = static_cast<int>(scale); |
51 | | // Ensure that quant_dc_ will always be at least |
52 | | // 0.625 * kGlobalScaleDenom/kGlobalScaleNumerator = 10. |
53 | 0 | const int scaled_quant_dc = |
54 | 0 | static_cast<int>(quant_dc * kGlobalScaleNumerator * 1.6); |
55 | 0 | if (new_global_scale > scaled_quant_dc) { |
56 | 0 | new_global_scale = scaled_quant_dc; |
57 | 0 | if (new_global_scale <= 0) new_global_scale = 1; |
58 | 0 | } |
59 | 0 | global_scale_ = new_global_scale; |
60 | | // Code below uses inv_global_scale_. |
61 | 0 | RecomputeFromGlobalScale(); |
62 | |
|
63 | 0 | float fval = quant_dc * inv_global_scale_ + 0.5f; |
64 | 0 | fval = std::min<float>(1 << 16, fval); |
65 | 0 | const int new_quant_dc = static_cast<int>(fval); |
66 | 0 | quant_dc_ = new_quant_dc; |
67 | | |
68 | | // quant_dc_ was updated, recompute values. |
69 | 0 | RecomputeFromGlobalScale(); |
70 | 0 | } |
71 | | |
72 | | void Quantizer::SetQuantFieldRect(const ImageF& qf, const Rect& rect, |
73 | 0 | ImageI* JXL_RESTRICT raw_quant_field) const { |
74 | 0 | for (size_t y = 0; y < rect.ysize(); ++y) { |
75 | 0 | const float* JXL_RESTRICT row_qf = rect.ConstRow(qf, y); |
76 | 0 | int32_t* JXL_RESTRICT row_qi = rect.Row(raw_quant_field, y); |
77 | 0 | for (size_t x = 0; x < rect.xsize(); ++x) { |
78 | 0 | int val = ClampVal(row_qf[x] * inv_global_scale_ + 0.5f); |
79 | 0 | row_qi[x] = val; |
80 | 0 | } |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | | void Quantizer::SetQuantField(const float quant_dc, const ImageF& qf, |
85 | 0 | ImageI* JXL_RESTRICT raw_quant_field) { |
86 | 0 | std::vector<float> data(qf.xsize() * qf.ysize()); |
87 | 0 | for (size_t y = 0; y < qf.ysize(); ++y) { |
88 | 0 | const float* JXL_RESTRICT row_qf = qf.Row(y); |
89 | 0 | for (size_t x = 0; x < qf.xsize(); ++x) { |
90 | 0 | float quant = row_qf[x]; |
91 | 0 | data[qf.xsize() * y + x] = quant; |
92 | 0 | } |
93 | 0 | } |
94 | 0 | std::nth_element(data.begin(), data.begin() + data.size() / 2, data.end()); |
95 | 0 | const float quant_median = data[data.size() / 2]; |
96 | 0 | std::vector<float> deviations(data.size()); |
97 | 0 | for (size_t i = 0; i < data.size(); i++) { |
98 | 0 | deviations[i] = fabsf(data[i] - quant_median); |
99 | 0 | } |
100 | 0 | std::nth_element(deviations.begin(), |
101 | 0 | deviations.begin() + deviations.size() / 2, |
102 | 0 | deviations.end()); |
103 | 0 | const float quant_median_absd = deviations[deviations.size() / 2]; |
104 | 0 | ComputeGlobalScaleAndQuant(quant_dc, quant_median, quant_median_absd); |
105 | 0 | if (raw_quant_field) { |
106 | 0 | JXL_CHECK(SameSize(*raw_quant_field, qf)); |
107 | 0 | SetQuantFieldRect(qf, Rect(qf), raw_quant_field); |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | void Quantizer::SetQuant(float quant_dc, float quant_ac, |
112 | 0 | ImageI* JXL_RESTRICT raw_quant_field) { |
113 | 0 | ComputeGlobalScaleAndQuant(quant_dc, quant_ac, 0); |
114 | 0 | int32_t val = ClampVal(quant_ac * inv_global_scale_ + 0.5f); |
115 | 0 | FillImage(val, raw_quant_field); |
116 | 0 | } |
117 | | |
118 | 5.96k | Status QuantizerParams::VisitFields(Visitor* JXL_RESTRICT visitor) { |
119 | 5.96k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32( |
120 | 5.96k | BitsOffset(11, 1), BitsOffset(11, 2049), BitsOffset(12, 4097), |
121 | 5.96k | BitsOffset(16, 8193), 1, &global_scale)); |
122 | 5.96k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(16), BitsOffset(5, 1), |
123 | 5.96k | BitsOffset(8, 1), BitsOffset(16, 1), 1, |
124 | 5.96k | &quant_dc)); |
125 | 5.95k | return true; |
126 | 5.96k | } |
127 | | |
128 | | Status Quantizer::Encode(BitWriter* writer, size_t layer, |
129 | 0 | AuxOut* aux_out) const { |
130 | 0 | QuantizerParams params; |
131 | 0 | params.global_scale = global_scale_; |
132 | 0 | params.quant_dc = quant_dc_; |
133 | 0 | return Bundle::Write(params, writer, layer, aux_out); |
134 | 0 | } |
135 | | |
136 | 2.98k | Status Quantizer::Decode(BitReader* reader) { |
137 | 2.98k | QuantizerParams params; |
138 | 2.98k | JXL_RETURN_IF_ERROR(Bundle::Read(reader, ¶ms)); |
139 | 2.97k | global_scale_ = static_cast<int>(params.global_scale); |
140 | 2.97k | quant_dc_ = static_cast<int>(params.quant_dc); |
141 | 2.97k | RecomputeFromGlobalScale(); |
142 | 2.97k | return true; |
143 | 2.98k | } |
144 | | |
145 | 0 | void Quantizer::DumpQuantizationMap(const ImageI& raw_quant_field) const { |
146 | 0 | printf("Global scale: %d (%.7f)\nDC quant: %d\n", global_scale_, |
147 | 0 | global_scale_ * 1.0 / kGlobalScaleDenom, quant_dc_); |
148 | 0 | printf("AC quantization Map:\n"); |
149 | 0 | for (size_t y = 0; y < raw_quant_field.ysize(); ++y) { |
150 | 0 | for (size_t x = 0; x < raw_quant_field.xsize(); ++x) { |
151 | 0 | printf(" %3d", raw_quant_field.Row(y)[x]); |
152 | 0 | } |
153 | 0 | printf("\n"); |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | } // namespace jxl |