enc_gaborish.cc (3220B)
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 ThreadPool* pool) { 23 JxlMemoryManager* memory_manager = in_out->memory_manager(); 24 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 static const float kGaborish[5] = { 31 -0.09495815671340026, -0.041031725066768575, 0.013710004822696948, 32 0.006510206083837737, -0.0014789063378272242, 33 }; 34 for (int i = 0; i < 3; ++i) { 35 double sum = 1.0 + mul[i] * 4 * 36 (kGaborish[0] + kGaborish[1] + kGaborish[2] + 37 kGaborish[4] + 2 * kGaborish[3]); 38 if (sum < 1e-5) { 39 sum = 1e-5; 40 } 41 const float normalize = static_cast<float>(1.0 / sum); 42 const float normalize_mul = mul[i] * normalize; 43 weights[i] = WeightsSymmetric5{{HWY_REP4(normalize)}, 44 {HWY_REP4(normalize_mul * kGaborish[0])}, 45 {HWY_REP4(normalize_mul * kGaborish[2])}, 46 {HWY_REP4(normalize_mul * kGaborish[1])}, 47 {HWY_REP4(normalize_mul * kGaborish[4])}, 48 {HWY_REP4(normalize_mul * kGaborish[3])}}; 49 } 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 ImageF temp; 56 JXL_ASSIGN_OR_RETURN(temp, 57 ImageF::Create(memory_manager, in_out->Plane(2).xsize(), 58 in_out->Plane(2).ysize())); 59 JXL_RETURN_IF_ERROR(CopyImageTo(in_out->Plane(2), &temp)); 60 Rect xrect = rect.Extend(3, Rect(*in_out)); 61 JXL_RETURN_IF_ERROR(Symmetric5(in_out->Plane(0), xrect, weights[0], pool, 62 &in_out->Plane(2), xrect)); 63 JXL_RETURN_IF_ERROR(Symmetric5(in_out->Plane(1), xrect, weights[1], pool, 64 &in_out->Plane(0), xrect)); 65 JXL_RETURN_IF_ERROR( 66 Symmetric5(temp, xrect, weights[2], pool, &in_out->Plane(1), xrect)); 67 // Now planes are 1, 2, 0. 68 in_out->Plane(0).Swap(in_out->Plane(1)); 69 // 2 1 0 70 in_out->Plane(0).Swap(in_out->Plane(2)); 71 return true; 72 } 73 74 } // namespace jxl