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