/src/libjxl/lib/jxl/quantizer.h
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 | | #ifndef LIB_JXL_QUANTIZER_H_ |
7 | | #define LIB_JXL_QUANTIZER_H_ |
8 | | |
9 | | #include <stddef.h> |
10 | | #include <stdint.h> |
11 | | #include <stdlib.h> |
12 | | |
13 | | #include <algorithm> |
14 | | #include <cmath> |
15 | | #include <utility> |
16 | | #include <vector> |
17 | | |
18 | | #include "lib/jxl/ac_strategy.h" |
19 | | #include "lib/jxl/base/bits.h" |
20 | | #include "lib/jxl/base/compiler_specific.h" |
21 | | #include "lib/jxl/base/status.h" |
22 | | #include "lib/jxl/common.h" |
23 | | #include "lib/jxl/dct_util.h" |
24 | | #include "lib/jxl/dec_bit_reader.h" |
25 | | #include "lib/jxl/fields.h" |
26 | | #include "lib/jxl/image.h" |
27 | | #include "lib/jxl/quant_weights.h" |
28 | | |
29 | | // Quantizes DC and AC coefficients, with separate quantization tables according |
30 | | // to the quant_kind (which is currently computed from the AC strategy and the |
31 | | // block index inside that strategy). |
32 | | |
33 | | namespace jxl { |
34 | | |
35 | | static constexpr int kGlobalScaleDenom = 1 << 16; |
36 | | static constexpr int kGlobalScaleNumerator = 4096; |
37 | | |
38 | | // zero-biases for quantizing channels X, Y, B |
39 | | static constexpr float kZeroBiasDefault[3] = {0.5f, 0.5f, 0.5f}; |
40 | | |
41 | | // Returns adjusted version of a quantized integer, such that its value is |
42 | | // closer to the expected value of the original. |
43 | | // The residuals of AC coefficients that we quantize are not uniformly |
44 | | // distributed. Numerical experiments show that they have a distribution with |
45 | | // the "shape" of 1/(1+x^2) [up to some coefficients]. This means that the |
46 | | // expected value of a coefficient that gets quantized to x will not be x |
47 | | // itself, but (at least with reasonable approximation): |
48 | | // - 0 if x is 0 |
49 | | // - x * biases[c] if x is 1 or -1 |
50 | | // - x - biases[3]/x otherwise |
51 | | // This follows from computing the distribution of the quantization bias, which |
52 | | // can be approximated fairly well by <constant>/x when |x| is at least two. |
53 | | static constexpr float kBiasNumerator = 0.145f; |
54 | | |
55 | | static constexpr float kDefaultQuantBias[4] = { |
56 | | 1.0f - 0.05465007330715401f, |
57 | | 1.0f - 0.07005449891748593f, |
58 | | 1.0f - 0.049935103337343655f, |
59 | | 0.145f, |
60 | | }; |
61 | | |
62 | | struct QuantizerParams; |
63 | | |
64 | | class Quantizer { |
65 | | public: |
66 | | explicit Quantizer(const DequantMatrices* dequant); |
67 | | Quantizer(const DequantMatrices* dequant, int quant_dc, int global_scale); |
68 | | |
69 | | static constexpr int32_t kQuantMax = 256; |
70 | | |
71 | 0 | static JXL_INLINE int32_t ClampVal(float val) { |
72 | 0 | return static_cast<int32_t>( |
73 | 0 | std::max(1.0f, std::min<float>(val, kQuantMax))); |
74 | 0 | } |
75 | | |
76 | 0 | float ScaleGlobalScale(const float scale) { |
77 | 0 | int new_global_scale = static_cast<int>(global_scale_ * scale + 0.5f); |
78 | 0 | float scale_out = new_global_scale * 1.0f / global_scale_; |
79 | 0 | global_scale_ = new_global_scale; |
80 | 0 | RecomputeFromGlobalScale(); |
81 | 0 | return scale_out; |
82 | 0 | } |
83 | | |
84 | | // Recomputes other derived fields after global_scale_ has changed. |
85 | 0 | void RecomputeFromGlobalScale() { |
86 | 0 | global_scale_float_ = global_scale_ * (1.0 / kGlobalScaleDenom); |
87 | 0 | inv_global_scale_ = 1.0 * kGlobalScaleDenom / global_scale_; |
88 | 0 | inv_quant_dc_ = inv_global_scale_ / quant_dc_; |
89 | 0 | for (size_t c = 0; c < 3; c++) { |
90 | 0 | mul_dc_[c] = GetDcStep(c); |
91 | 0 | inv_mul_dc_[c] = GetInvDcStep(c); |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | | // Returns scaling factor such that Scale() * (RawDC() or RawQuantField()) |
96 | | // pixels yields the same float values returned by GetQuantField. |
97 | 0 | JXL_INLINE float Scale() const { return global_scale_float_; } |
98 | | |
99 | | // Reciprocal of Scale(). |
100 | 0 | JXL_INLINE float InvGlobalScale() const { return inv_global_scale_; } |
101 | | |
102 | | void SetQuantFieldRect(const ImageF& qf, const Rect& rect, |
103 | | ImageI* JXL_RESTRICT raw_quant_field) const; |
104 | | |
105 | | void SetQuantField(float quant_dc, const ImageF& qf, |
106 | | ImageI* JXL_RESTRICT raw_quant_field); |
107 | | |
108 | | void SetQuant(float quant_dc, float quant_ac, |
109 | | ImageI* JXL_RESTRICT raw_quant_field); |
110 | | |
111 | | // Returns the DC quantization base value, which is currently global (not |
112 | | // adaptive). The actual scale factor used to dequantize pixels in channel c |
113 | | // is: inv_quant_dc() * dequant_->DCQuant(c). |
114 | 0 | float inv_quant_dc() const { return inv_quant_dc_; } |
115 | | |
116 | | // Dequantize by multiplying with this times dequant_matrix. |
117 | 0 | float inv_quant_ac(int32_t quant) const { return inv_global_scale_ / quant; } |
118 | | |
119 | | QuantizerParams GetParams() const; |
120 | | |
121 | | Status Decode(BitReader* reader); |
122 | | |
123 | | void DumpQuantizationMap(const ImageI& raw_quant_field) const; |
124 | | |
125 | 0 | JXL_INLINE const float* DequantMatrix(size_t quant_kind, size_t c) const { |
126 | 0 | return dequant_->Matrix(quant_kind, c); |
127 | 0 | } |
128 | | |
129 | 0 | JXL_INLINE const float* InvDequantMatrix(size_t quant_kind, size_t c) const { |
130 | 0 | return dequant_->InvMatrix(quant_kind, c); |
131 | 0 | } |
132 | | |
133 | | // Calculates DC quantization step. |
134 | 0 | JXL_INLINE float GetDcStep(size_t c) const { |
135 | 0 | return inv_quant_dc_ * dequant_->DCQuant(c); |
136 | 0 | } |
137 | 0 | JXL_INLINE float GetInvDcStep(size_t c) const { |
138 | 0 | return dequant_->InvDCQuant(c) * (global_scale_float_ * quant_dc_); |
139 | 0 | } |
140 | | |
141 | 0 | JXL_INLINE const float* MulDC() const { return mul_dc_; } |
142 | 0 | JXL_INLINE const float* InvMulDC() const { return inv_mul_dc_; } |
143 | | |
144 | 0 | JXL_INLINE void ClearDCMul() { |
145 | 0 | std::fill(mul_dc_, mul_dc_ + 4, 1.f); |
146 | 0 | std::fill(inv_mul_dc_, inv_mul_dc_ + 4, 1.f); |
147 | 0 | } |
148 | | |
149 | | void ComputeGlobalScaleAndQuant(float quant_dc, float quant_median, |
150 | | float quant_median_absd); |
151 | | |
152 | | private: |
153 | | float mul_dc_[4]; |
154 | | float inv_mul_dc_[4]; |
155 | | |
156 | | // These are serialized: |
157 | | int global_scale_; |
158 | | int quant_dc_; |
159 | | |
160 | | // These are derived from global_scale_: |
161 | | float inv_global_scale_; |
162 | | float global_scale_float_; // reciprocal of inv_global_scale_ |
163 | | float inv_quant_dc_; |
164 | | |
165 | | float zero_bias_[3]; |
166 | | const DequantMatrices* dequant_; |
167 | | }; |
168 | | |
169 | | struct QuantizerParams : public Fields { |
170 | 0 | QuantizerParams() { Bundle::Init(this); } |
171 | | JXL_FIELDS_NAME(QuantizerParams) |
172 | | |
173 | | Status VisitFields(Visitor* JXL_RESTRICT visitor) override; |
174 | | |
175 | | uint32_t global_scale; |
176 | | uint32_t quant_dc; |
177 | | }; |
178 | | |
179 | | } // namespace jxl |
180 | | |
181 | | #endif // LIB_JXL_QUANTIZER_H_ |