Coverage Report

Created: 2025-06-16 07:00

/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, &params));
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