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 <stdint.h>
7 #include <stdio.h>
8
9 #include <algorithm>
10 #include <utility>
11 #include <vector>
12
13 #include "gtest/gtest.h"
14 #include "lib/jxl/ac_strategy.h"
15 #include "lib/jxl/base/compiler_specific.h"
16 #include "lib/jxl/common.h"
17 #include "lib/jxl/dec_reconstruct.h"
18 #include "lib/jxl/epf.h"
19 #include "lib/jxl/image.h"
20 #include "lib/jxl/image_bundle.h"
21 #include "lib/jxl/image_ops.h"
22 #include "lib/jxl/image_test_utils.h"
23 #include "lib/jxl/loop_filter.h"
24 #include "lib/jxl/quant_weights.h"
25 #include "lib/jxl/quantizer.h"
26 #include "lib/jxl/test_utils.h"
27
28 namespace jxl {
29 namespace {
30
31 const size_t xsize = 16;
32 const size_t ysize = 8;
33
GenerateFlat(const float background,const float foreground,std::vector<Image3F> * images)34 void GenerateFlat(const float background, const float foreground,
35 std::vector<Image3F>* images) {
36 for (size_t c = 0; c < Image3F::kNumPlanes; ++c) {
37 Image3F in(xsize, ysize);
38 // Plane c = foreground, all others = background.
39 for (size_t y = 0; y < ysize; ++y) {
40 float* rows[3] = {in.PlaneRow(0, y), in.PlaneRow(1, y),
41 in.PlaneRow(2, y)};
42 for (size_t x = 0; x < xsize; ++x) {
43 rows[0][x] = rows[1][x] = rows[2][x] = background;
44 rows[c][x] = foreground;
45 }
46 }
47 images->push_back(std::move(in));
48 }
49 }
50
51 // Single foreground point at any position in any channel
GeneratePoints(const float background,const float foreground,std::vector<Image3F> * images)52 void GeneratePoints(const float background, const float foreground,
53 std::vector<Image3F>* images) {
54 for (size_t c = 0; c < Image3F::kNumPlanes; ++c) {
55 for (size_t y = 0; y < ysize; ++y) {
56 for (size_t x = 0; x < xsize; ++x) {
57 Image3F in(xsize, ysize);
58 FillImage(background, &in);
59 in.PlaneRow(c, y)[x] = foreground;
60 images->push_back(std::move(in));
61 }
62 }
63 }
64 }
65
GenerateHorzEdges(const float background,const float foreground,std::vector<Image3F> * images)66 void GenerateHorzEdges(const float background, const float foreground,
67 std::vector<Image3F>* images) {
68 for (size_t c = 0; c < Image3F::kNumPlanes; ++c) {
69 // Begin of foreground rows
70 for (size_t y = 1; y < ysize; ++y) {
71 Image3F in(xsize, ysize);
72 FillImage(background, &in);
73 for (size_t iy = y; iy < ysize; ++iy) {
74 std::fill(in.PlaneRow(c, iy), in.PlaneRow(c, iy) + xsize, foreground);
75 }
76 images->push_back(std::move(in));
77 }
78 }
79 }
80
GenerateVertEdges(const float background,const float foreground,std::vector<Image3F> * images)81 void GenerateVertEdges(const float background, const float foreground,
82 std::vector<Image3F>* images) {
83 for (size_t c = 0; c < Image3F::kNumPlanes; ++c) {
84 // Begin of foreground columns
85 for (size_t x = 1; x < xsize; ++x) {
86 Image3F in(xsize, ysize);
87 FillImage(background, &in);
88 for (size_t iy = 0; iy < ysize; ++iy) {
89 float* JXL_RESTRICT row = in.PlaneRow(c, iy);
90 for (size_t ix = x; ix < xsize; ++ix) {
91 row[ix] = foreground;
92 }
93 }
94 images->push_back(std::move(in));
95 }
96 }
97 }
98
DumpTestImage(const char * name,const Image3F & img)99 void DumpTestImage(const char* name, const Image3F& img) {
100 fprintf(stderr, "Image %s:\n", name);
101 for (size_t y = 0; y < img.ysize(); ++y) {
102 const float* row_x = img.ConstPlaneRow(0, y);
103 const float* row_y = img.ConstPlaneRow(1, y);
104 const float* row_b = img.ConstPlaneRow(2, y);
105 for (size_t x = 0; x < img.xsize(); ++x) {
106 fprintf(stderr, "%5.1f|%5.1f|%5.1f ", row_x[x], row_y[x], row_b[x]);
107 }
108 fprintf(stderr, "\n");
109 }
110 fprintf(stderr, "\n");
111 }
112
113 // Ensures input remains unchanged by filter - verifies the edge-preserving
114 // nature of the filter because inputs are piecewise constant.
EnsureUnchanged(const float background,const float foreground,uint32_t epf_iters)115 void EnsureUnchanged(const float background, const float foreground,
116 uint32_t epf_iters) {
117 std::vector<Image3F> images;
118 GenerateFlat(background, foreground, &images);
119 GeneratePoints(background, foreground, &images);
120 GenerateHorzEdges(background, foreground, &images);
121 GenerateVertEdges(background, foreground, &images);
122
123 CodecMetadata metadata;
124 JXL_CHECK(metadata.size.Set(xsize, ysize));
125 metadata.m.xyb_encoded = false;
126 FrameHeader frame_header(&metadata);
127 // Ensure no CT is applied
128 frame_header.color_transform = ColorTransform::kNone;
129 LoopFilter& lf = frame_header.loop_filter;
130 lf.gab = false;
131 lf.epf_iters = epf_iters;
132 FrameDimensions frame_dim = frame_header.ToFrameDimensions();
133
134 jxl::PassesDecoderState state;
135 JXL_CHECK(
136 jxl::InitializePassesSharedState(frame_header, &state.shared_storage));
137 JXL_CHECK(state.Init());
138 state.InitForAC(/*pool=*/nullptr);
139
140 JXL_CHECK(state.filter_weights.Init(lf, frame_dim));
141 FillImage(-0.5f, &state.filter_weights.sigma);
142
143 for (size_t idx_image = 0; idx_image < images.size(); ++idx_image) {
144 const Image3F& in = images[idx_image];
145 state.decoded = CopyImage(in);
146
147 ImageBundle out(&metadata.m);
148 out.SetFromImage(CopyImage(in), ColorEncoding::LinearSRGB());
149 FillImage(-99.f, out.color()); // Initialized with garbage.
150 Image3F padded = PadImageMirror(in, 2 * kBlockDim, 0);
151 // Call with `force_fir` set to true to force to apply filters to all of the
152 // input image.
153 JXL_CHECK(FinalizeFrameDecoding(&out, &state, /*pool=*/nullptr,
154 /*force_fir=*/true,
155 /*skip_blending=*/true));
156
157 #if JXL_HIGH_PRECISION
158 VerifyRelativeError(in, *out.color(), 1E-3, 1E-4);
159 #else
160 VerifyRelativeError(in, *out.color(), 1E-2, 1E-2);
161 #endif
162 if (testing::Test::HasFatalFailure()) {
163 DumpTestImage("in", in);
164 DumpTestImage("out", *out.color());
165 }
166 }
167 }
168
169 } // namespace
170
171 class AdaptiveReconstructionTest : public testing::TestWithParam<uint32_t> {};
172
173 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(EPFItersGroup, AdaptiveReconstructionTest,
174 testing::Values(1, 2, 3),
175 testing::PrintToStringParamName());
176
TEST_P(AdaptiveReconstructionTest,TestBright)177 TEST_P(AdaptiveReconstructionTest, TestBright) {
178 EnsureUnchanged(1.0f, 128.0f, GetParam());
179 }
TEST_P(AdaptiveReconstructionTest,TestDark)180 TEST_P(AdaptiveReconstructionTest, TestDark) {
181 EnsureUnchanged(128.0f, 1.0f, GetParam());
182 }
183
184 } // namespace jxl
185