/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 <cstring> |
10 | | |
11 | | #include "lib/jxl/base/compiler_specific.h" |
12 | | #include "lib/jxl/base/rect.h" |
13 | | #include "lib/jxl/field_encodings.h" |
14 | | #include "lib/jxl/fields.h" |
15 | | #include "lib/jxl/image.h" |
16 | | #include "lib/jxl/image_ops.h" |
17 | | #include "lib/jxl/quant_weights.h" |
18 | | |
19 | | namespace jxl { |
20 | | |
21 | | static const int32_t kDefaultQuant = 64; |
22 | | |
23 | | #if JXL_CXX_LANG < JXL_CXX_17 |
24 | | constexpr int32_t Quantizer::kQuantMax; |
25 | | #endif |
26 | | |
27 | | Quantizer::Quantizer(const DequantMatrices& dequant) |
28 | 33.6k | : Quantizer(dequant, kDefaultQuant, kGlobalScaleDenom / kDefaultQuant) {} |
29 | | |
30 | | Quantizer::Quantizer(const DequantMatrices& dequant, int quant_dc, |
31 | | int global_scale) |
32 | 33.6k | : global_scale_(global_scale), quant_dc_(quant_dc), dequant_(&dequant) { |
33 | 33.6k | RecomputeFromGlobalScale(); |
34 | 33.6k | inv_quant_dc_ = inv_global_scale_ / quant_dc_; |
35 | | |
36 | 33.6k | memcpy(zero_bias_, kZeroBiasDefault, sizeof(kZeroBiasDefault)); |
37 | 33.6k | } |
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 | | Status 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_ENSURE(SameSize(*raw_quant_field, qf)); |
107 | 0 | SetQuantFieldRect(qf, Rect(qf), raw_quant_field); |
108 | 0 | } |
109 | 0 | return true; |
110 | 0 | } |
111 | | |
112 | | void Quantizer::SetQuant(float quant_dc, float quant_ac, |
113 | 0 | ImageI* JXL_RESTRICT raw_quant_field) { |
114 | 0 | ComputeGlobalScaleAndQuant(quant_dc, quant_ac, 0); |
115 | 0 | int32_t val = ClampVal(quant_ac * inv_global_scale_ + 0.5f); |
116 | 0 | FillImage(val, raw_quant_field); |
117 | 0 | } |
118 | | |
119 | 24.5k | Status QuantizerParams::VisitFields(Visitor* JXL_RESTRICT visitor) { |
120 | 24.5k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32( |
121 | 24.5k | BitsOffset(11, 1), BitsOffset(11, 2049), BitsOffset(12, 4097), |
122 | 24.5k | BitsOffset(16, 8193), 1, &global_scale)); |
123 | 24.5k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(16), BitsOffset(5, 1), |
124 | 24.5k | BitsOffset(8, 1), BitsOffset(16, 1), 1, |
125 | 24.5k | &quant_dc)); |
126 | 24.5k | return true; |
127 | 24.5k | } |
128 | | |
129 | 0 | QuantizerParams Quantizer::GetParams() const { |
130 | 0 | QuantizerParams params; |
131 | 0 | params.global_scale = global_scale_; |
132 | 0 | params.quant_dc = quant_dc_; |
133 | 0 | return params; |
134 | 0 | } |
135 | | |
136 | 12.2k | Status Quantizer::Decode(BitReader* reader) { |
137 | 12.2k | QuantizerParams params; |
138 | 12.2k | JXL_RETURN_IF_ERROR(Bundle::Read(reader, ¶ms)); |
139 | 12.2k | global_scale_ = static_cast<int>(params.global_scale); |
140 | 12.2k | quant_dc_ = static_cast<int>(params.quant_dc); |
141 | 12.2k | RecomputeFromGlobalScale(); |
142 | 12.2k | return true; |
143 | 12.2k | } |
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 |