/src/libjxl/lib/jxl/epf.cc
Line | Count | Source |
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 | | // Edge-preserving smoothing: weighted average based on L1 patch similarity. |
7 | | |
8 | | #include "lib/jxl/epf.h" |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <cstring> |
14 | | |
15 | | #include "lib/jxl/ac_strategy.h" |
16 | | #include "lib/jxl/base/compiler_specific.h" |
17 | | #include "lib/jxl/base/rect.h" |
18 | | #include "lib/jxl/base/status.h" |
19 | | #include "lib/jxl/dec_cache.h" |
20 | | #include "lib/jxl/loop_filter.h" |
21 | | #include "lib/jxl/quantizer.h" |
22 | | |
23 | | namespace jxl { |
24 | | |
25 | | // Mirror n floats starting at *p and store them before p. |
26 | 195k | JXL_INLINE void LeftMirror(float* p, size_t n) { |
27 | 391k | for (size_t i = 0; i < n; i++) { |
28 | 195k | *(p - 1 - i) = p[i]; |
29 | 195k | } |
30 | 195k | } |
31 | | |
32 | | // Mirror n floats starting at *(p - n) and store them at *p. |
33 | 195k | JXL_INLINE void RightMirror(float* p, size_t n) { |
34 | 391k | for (size_t i = 0; i < n; i++) { |
35 | 195k | p[i] = *(p - 1 - i); |
36 | 195k | } |
37 | 195k | } |
38 | | |
39 | | Status ComputeSigma(const LoopFilter& lf, const Rect& block_rect, |
40 | 16.2k | PassesDecoderState* state) { |
41 | 16.2k | JXL_ENSURE(lf.epf_iters > 0); |
42 | 16.2k | const AcStrategyImage& ac_strategy = state->shared->ac_strategy; |
43 | 16.2k | const float quant_scale = state->shared->quantizer.Scale(); |
44 | | |
45 | 16.2k | const size_t sigma_stride = state->sigma.PixelsPerRow(); |
46 | 16.2k | const size_t sharpness_stride = state->shared->epf_sharpness.PixelsPerRow(); |
47 | | |
48 | 362k | for (size_t by = 0; by < block_rect.ysize(); ++by) { |
49 | 346k | float* JXL_RESTRICT sigma_row = block_rect.Row(&state->sigma, by); |
50 | 346k | const uint8_t* JXL_RESTRICT sharpness_row = |
51 | 346k | block_rect.ConstRow(state->shared->epf_sharpness, by); |
52 | 346k | AcStrategyRow acs_row = ac_strategy.ConstRow(block_rect, by); |
53 | 346k | const int32_t* const JXL_RESTRICT row_quant = |
54 | 346k | block_rect.ConstRow(state->shared->raw_quant_field, by); |
55 | | |
56 | 9.16M | for (size_t bx = 0; bx < block_rect.xsize(); bx++) { |
57 | 8.81M | AcStrategy acs = acs_row[bx]; |
58 | 8.81M | size_t llf_x = acs.covered_blocks_x(); |
59 | 8.81M | if (!acs.IsFirstBlock()) continue; |
60 | | // quant_scale is smaller for low quality. |
61 | | // quant_scale is roughly 0.08 / butteraugli score. |
62 | | // |
63 | | // row_quant is smaller for low quality. |
64 | | // row_quant is a quantization multiplier of form 1.0 / |
65 | | // row_quant[bx] |
66 | | // |
67 | | // lf.epf_quant_mul is a parameter in the format |
68 | | // kInvSigmaNum is a constant |
69 | 4.89M | float sigma_quant = |
70 | 4.89M | lf.epf_quant_mul / (quant_scale * row_quant[bx] * kInvSigmaNum); |
71 | 10.6M | for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) { |
72 | 14.5M | for (size_t ix = 0; ix < acs.covered_blocks_x(); ix++) { |
73 | 8.81M | float sigma = |
74 | 8.81M | sigma_quant * |
75 | 8.81M | lf.epf_sharp_lut[sharpness_row[bx + ix + iy * sharpness_stride]]; |
76 | | // Avoid infinities. |
77 | 8.81M | sigma = std::min(-1e-4f, sigma); // TODO(veluca): remove this. |
78 | 8.81M | sigma_row[bx + ix + kSigmaPadding + |
79 | 8.81M | (iy + kSigmaPadding) * sigma_stride] = 1.0f / sigma; |
80 | 8.81M | } |
81 | 5.77M | } |
82 | | // TODO(veluca): remove this padding. |
83 | | // Left padding with mirroring. |
84 | 4.89M | if (bx + block_rect.x0() == 0) { |
85 | 321k | for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) { |
86 | 195k | LeftMirror( |
87 | 195k | sigma_row + kSigmaPadding + (iy + kSigmaPadding) * sigma_stride, |
88 | 195k | kSigmaBorder); |
89 | 195k | } |
90 | 125k | } |
91 | | // Right padding with mirroring. |
92 | 4.89M | if (bx + block_rect.x0() + llf_x == |
93 | 4.89M | state->shared->frame_dim.xsize_blocks) { |
94 | 350k | for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) { |
95 | 195k | RightMirror(sigma_row + kSigmaPadding + bx + llf_x + |
96 | 195k | (iy + kSigmaPadding) * sigma_stride, |
97 | 195k | kSigmaBorder); |
98 | 195k | } |
99 | 154k | } |
100 | | // Offsets for row copying, in blocks. |
101 | 4.89M | size_t offset_before = bx + block_rect.x0() == 0 ? 1 : bx + kSigmaPadding; |
102 | 4.89M | size_t offset_after = |
103 | 4.89M | bx + block_rect.x0() + llf_x == state->shared->frame_dim.xsize_blocks |
104 | 4.89M | ? kSigmaPadding + llf_x + bx + kSigmaBorder |
105 | 4.89M | : kSigmaPadding + llf_x + bx; |
106 | 4.89M | size_t num = offset_after - offset_before; |
107 | | // Above |
108 | 4.89M | if (by + block_rect.y0() == 0) { |
109 | 253k | for (size_t iy = 0; iy < kSigmaBorder; iy++) { |
110 | 126k | memcpy( |
111 | 126k | sigma_row + offset_before + |
112 | 126k | (kSigmaPadding - 1 - iy) * sigma_stride, |
113 | 126k | sigma_row + offset_before + (kSigmaPadding + iy) * sigma_stride, |
114 | 126k | num * sizeof(*sigma_row)); |
115 | 126k | } |
116 | 126k | } |
117 | | // Below |
118 | 4.89M | if (by + block_rect.y0() + acs.covered_blocks_y() == |
119 | 4.89M | state->shared->frame_dim.ysize_blocks) { |
120 | 322k | for (size_t iy = 0; iy < kSigmaBorder; iy++) { |
121 | 161k | memcpy( |
122 | 161k | sigma_row + offset_before + |
123 | 161k | sigma_stride * (acs.covered_blocks_y() + kSigmaPadding + iy), |
124 | 161k | sigma_row + offset_before + |
125 | 161k | sigma_stride * |
126 | 161k | (acs.covered_blocks_y() + kSigmaPadding - 1 - iy), |
127 | 161k | num * sizeof(*sigma_row)); |
128 | 161k | } |
129 | 161k | } |
130 | 4.89M | } |
131 | 346k | } |
132 | 16.2k | return true; |
133 | 16.2k | } |
134 | | |
135 | | } // namespace jxl |