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