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