/src/libjxl/lib/jxl/enc_gaborish.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/enc_gaborish.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <hwy/base.h> |
11 | | |
12 | | #include "lib/jxl/base/data_parallel.h" |
13 | | #include "lib/jxl/base/rect.h" |
14 | | #include "lib/jxl/base/status.h" |
15 | | #include "lib/jxl/convolve.h" |
16 | | #include "lib/jxl/image.h" |
17 | | #include "lib/jxl/image_ops.h" |
18 | | |
19 | | namespace jxl { |
20 | | |
21 | | Status GaborishInverse(Image3F* in_out, const Rect& rect, const float mul[3], |
22 | 186 | ThreadPool* pool) { |
23 | 186 | JxlMemoryManager* memory_manager = in_out->memory_manager(); |
24 | 186 | WeightsSymmetric5 weights[3]; |
25 | | // Only an approximation. One or even two 3x3, and rank-1 (separable) 5x5 |
26 | | // are insufficient. The numbers here have been obtained by butteraugli |
27 | | // based optimizing the whole system and the errors produced are likely |
28 | | // more favorable for good rate-distortion compromises rather than |
29 | | // just using mathematical optimization to find the inverse. |
30 | 186 | static const float kGaborish[5] = { |
31 | 186 | -0.09495815671340026, -0.041031725066768575, 0.013710004822696948, |
32 | 186 | 0.006510206083837737, -0.0014789063378272242, |
33 | 186 | }; |
34 | 744 | for (int i = 0; i < 3; ++i) { |
35 | 558 | double sum = 1.0 + mul[i] * 4 * |
36 | 558 | (kGaborish[0] + kGaborish[1] + kGaborish[2] + |
37 | 558 | kGaborish[4] + 2 * kGaborish[3]); |
38 | 558 | if (sum < 1e-5) { |
39 | 0 | sum = 1e-5; |
40 | 0 | } |
41 | 558 | const float normalize = static_cast<float>(1.0 / sum); |
42 | 558 | const float normalize_mul = mul[i] * normalize; |
43 | 558 | weights[i] = WeightsSymmetric5{{HWY_REP4(normalize)}, |
44 | 558 | {HWY_REP4(normalize_mul * kGaborish[0])}, |
45 | 558 | {HWY_REP4(normalize_mul * kGaborish[2])}, |
46 | 558 | {HWY_REP4(normalize_mul * kGaborish[1])}, |
47 | 558 | {HWY_REP4(normalize_mul * kGaborish[4])}, |
48 | 558 | {HWY_REP4(normalize_mul * kGaborish[3])}}; |
49 | 558 | } |
50 | | // Reduce memory footprint by only allocating a single plane and swapping it |
51 | | // into the output Image3F. Better still would be tiling. |
52 | | // Note that we cannot *allocate* a plane, as doing so might cause Image3F to |
53 | | // have planes of different stride. Instead, we copy one plane in a temporary |
54 | | // image and reuse the existing planes of the in/out image. |
55 | 186 | ImageF temp; |
56 | 186 | JXL_ASSIGN_OR_RETURN(temp, |
57 | 186 | ImageF::Create(memory_manager, in_out->Plane(2).xsize(), |
58 | 186 | in_out->Plane(2).ysize())); |
59 | 186 | JXL_RETURN_IF_ERROR(CopyImageTo(in_out->Plane(2), &temp)); |
60 | 186 | Rect xrect = rect.Extend(3, Rect(*in_out)); |
61 | 186 | JXL_RETURN_IF_ERROR(Symmetric5(in_out->Plane(0), xrect, weights[0], pool, |
62 | 186 | &in_out->Plane(2), xrect)); |
63 | 186 | JXL_RETURN_IF_ERROR(Symmetric5(in_out->Plane(1), xrect, weights[1], pool, |
64 | 186 | &in_out->Plane(0), xrect)); |
65 | 186 | JXL_RETURN_IF_ERROR( |
66 | 186 | Symmetric5(temp, xrect, weights[2], pool, &in_out->Plane(1), xrect)); |
67 | | // Now planes are 1, 2, 0. |
68 | 186 | in_out->Plane(0).Swap(in_out->Plane(1)); |
69 | | // 2 1 0 |
70 | 186 | in_out->Plane(0).Swap(in_out->Plane(2)); |
71 | 186 | return true; |
72 | 186 | } |
73 | | |
74 | | } // namespace jxl |