/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 | 31.2k | JXL_INLINE void LeftMirror(float* p, size_t n) { |
27 | 62.4k | for (size_t i = 0; i < n; i++) { |
28 | 31.2k | *(p - 1 - i) = p[i]; |
29 | 31.2k | } |
30 | 31.2k | } |
31 | | |
32 | | // Mirror n floats starting at *(p - n) and store them at *p. |
33 | 31.2k | JXL_INLINE void RightMirror(float* p, size_t n) { |
34 | 62.4k | for (size_t i = 0; i < n; i++) { |
35 | 31.2k | p[i] = *(p - 1 - i); |
36 | 31.2k | } |
37 | 31.2k | } |
38 | | |
39 | | Status ComputeSigma(const LoopFilter& lf, const Rect& block_rect, |
40 | 2.41k | PassesDecoderState* state) { |
41 | 2.41k | JXL_ENSURE(lf.epf_iters > 0); |
42 | 2.41k | const AcStrategyImage& ac_strategy = state->shared->ac_strategy; |
43 | 2.41k | const float quant_scale = state->shared->quantizer.Scale(); |
44 | | |
45 | 2.41k | const size_t sigma_stride = state->sigma.PixelsPerRow(); |
46 | 2.41k | const size_t sharpness_stride = state->shared->epf_sharpness.PixelsPerRow(); |
47 | | |
48 | 55.1k | for (size_t by = 0; by < block_rect.ysize(); ++by) { |
49 | 52.7k | float* JXL_RESTRICT sigma_row = block_rect.Row(&state->sigma, by); |
50 | 52.7k | const uint8_t* JXL_RESTRICT sharpness_row = |
51 | 52.7k | block_rect.ConstRow(state->shared->epf_sharpness, by); |
52 | 52.7k | AcStrategyRow acs_row = ac_strategy.ConstRow(block_rect, by); |
53 | 52.7k | const int32_t* const JXL_RESTRICT row_quant = |
54 | 52.7k | block_rect.ConstRow(state->shared->raw_quant_field, by); |
55 | | |
56 | 1.46M | for (size_t bx = 0; bx < block_rect.xsize(); bx++) { |
57 | 1.41M | AcStrategy acs = acs_row[bx]; |
58 | 1.41M | size_t llf_x = acs.covered_blocks_x(); |
59 | 1.41M | 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 | 933k | float sigma_quant = |
70 | 933k | lf.epf_quant_mul / (quant_scale * row_quant[bx] * kInvSigmaNum); |
71 | 1.98M | for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) { |
72 | 2.46M | for (size_t ix = 0; ix < acs.covered_blocks_x(); ix++) { |
73 | 1.41M | float sigma = |
74 | 1.41M | sigma_quant * |
75 | 1.41M | lf.epf_sharp_lut[sharpness_row[bx + ix + iy * sharpness_stride]]; |
76 | | // Avoid infinities. |
77 | 1.41M | sigma = std::min(-1e-4f, sigma); // TODO(veluca): remove this. |
78 | 1.41M | sigma_row[bx + ix + kSigmaPadding + |
79 | 1.41M | (iy + kSigmaPadding) * sigma_stride] = 1.0f / sigma; |
80 | 1.41M | } |
81 | 1.05M | } |
82 | | // TODO(veluca): remove this padding. |
83 | | // Left padding with mirroring. |
84 | 933k | if (bx + block_rect.x0() == 0) { |
85 | 53.5k | for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) { |
86 | 31.2k | LeftMirror( |
87 | 31.2k | sigma_row + kSigmaPadding + (iy + kSigmaPadding) * sigma_stride, |
88 | 31.2k | kSigmaBorder); |
89 | 31.2k | } |
90 | 22.3k | } |
91 | | // Right padding with mirroring. |
92 | 933k | if (bx + block_rect.x0() + llf_x == |
93 | 933k | state->shared->frame_dim.xsize_blocks) { |
94 | 60.4k | for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) { |
95 | 31.2k | RightMirror(sigma_row + kSigmaPadding + bx + llf_x + |
96 | 31.2k | (iy + kSigmaPadding) * sigma_stride, |
97 | 31.2k | kSigmaBorder); |
98 | 31.2k | } |
99 | 29.2k | } |
100 | | // Offsets for row copying, in blocks. |
101 | 933k | size_t offset_before = bx + block_rect.x0() == 0 ? 1 : bx + kSigmaPadding; |
102 | 933k | size_t offset_after = |
103 | 933k | bx + block_rect.x0() + llf_x == state->shared->frame_dim.xsize_blocks |
104 | 933k | ? kSigmaPadding + llf_x + bx + kSigmaBorder |
105 | 933k | : kSigmaPadding + llf_x + bx; |
106 | 933k | size_t num = offset_after - offset_before; |
107 | | // Above |
108 | 933k | if (by + block_rect.y0() == 0) { |
109 | 60.8k | for (size_t iy = 0; iy < kSigmaBorder; iy++) { |
110 | 30.4k | memcpy( |
111 | 30.4k | sigma_row + offset_before + |
112 | 30.4k | (kSigmaPadding - 1 - iy) * sigma_stride, |
113 | 30.4k | sigma_row + offset_before + (kSigmaPadding + iy) * sigma_stride, |
114 | 30.4k | num * sizeof(*sigma_row)); |
115 | 30.4k | } |
116 | 30.4k | } |
117 | | // Below |
118 | 933k | if (by + block_rect.y0() + acs.covered_blocks_y() == |
119 | 933k | state->shared->frame_dim.ysize_blocks) { |
120 | 72.4k | for (size_t iy = 0; iy < kSigmaBorder; iy++) { |
121 | 36.2k | memcpy( |
122 | 36.2k | sigma_row + offset_before + |
123 | 36.2k | sigma_stride * (acs.covered_blocks_y() + kSigmaPadding + iy), |
124 | 36.2k | sigma_row + offset_before + |
125 | 36.2k | sigma_stride * |
126 | 36.2k | (acs.covered_blocks_y() + kSigmaPadding - 1 - iy), |
127 | 36.2k | num * sizeof(*sigma_row)); |
128 | 36.2k | } |
129 | 36.2k | } |
130 | 933k | } |
131 | 52.7k | } |
132 | 2.41k | return true; |
133 | 2.41k | } |
134 | | |
135 | | } // namespace jxl |