1*77c1e3ccSAndroid Build Coastguard Worker /*
2*77c1e3ccSAndroid Build Coastguard Worker * Copyright (c) 2018, Alliance for Open Media. All rights reserved.
3*77c1e3ccSAndroid Build Coastguard Worker *
4*77c1e3ccSAndroid Build Coastguard Worker * This source code is subject to the terms of the BSD 2 Clause License and
5*77c1e3ccSAndroid Build Coastguard Worker * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6*77c1e3ccSAndroid Build Coastguard Worker * was not distributed with this source code in the LICENSE file, you can
7*77c1e3ccSAndroid Build Coastguard Worker * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8*77c1e3ccSAndroid Build Coastguard Worker * Media Patent License 1.0 was not distributed with this source code in the
9*77c1e3ccSAndroid Build Coastguard Worker * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10*77c1e3ccSAndroid Build Coastguard Worker */
11*77c1e3ccSAndroid Build Coastguard Worker
12*77c1e3ccSAndroid Build Coastguard Worker #include <limits.h>
13*77c1e3ccSAndroid Build Coastguard Worker #include <math.h>
14*77c1e3ccSAndroid Build Coastguard Worker #include <algorithm>
15*77c1e3ccSAndroid Build Coastguard Worker #include <vector>
16*77c1e3ccSAndroid Build Coastguard Worker
17*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/noise_model.h"
18*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/noise_util.h"
19*77c1e3ccSAndroid Build Coastguard Worker #include "config/aom_dsp_rtcd.h"
20*77c1e3ccSAndroid Build Coastguard Worker #include "gtest/gtest.h"
21*77c1e3ccSAndroid Build Coastguard Worker #include "test/acm_random.h"
22*77c1e3ccSAndroid Build Coastguard Worker
23*77c1e3ccSAndroid Build Coastguard Worker namespace {
24*77c1e3ccSAndroid Build Coastguard Worker
25*77c1e3ccSAndroid Build Coastguard Worker // Return normally distrbuted values with standard deviation of sigma.
randn(libaom_test::ACMRandom * random,double sigma)26*77c1e3ccSAndroid Build Coastguard Worker double randn(libaom_test::ACMRandom *random, double sigma) {
27*77c1e3ccSAndroid Build Coastguard Worker while (true) {
28*77c1e3ccSAndroid Build Coastguard Worker const double u = 2.0 * ((double)random->Rand31() /
29*77c1e3ccSAndroid Build Coastguard Worker testing::internal::Random::kMaxRange) -
30*77c1e3ccSAndroid Build Coastguard Worker 1.0;
31*77c1e3ccSAndroid Build Coastguard Worker const double v = 2.0 * ((double)random->Rand31() /
32*77c1e3ccSAndroid Build Coastguard Worker testing::internal::Random::kMaxRange) -
33*77c1e3ccSAndroid Build Coastguard Worker 1.0;
34*77c1e3ccSAndroid Build Coastguard Worker const double s = u * u + v * v;
35*77c1e3ccSAndroid Build Coastguard Worker if (s > 0 && s < 1) {
36*77c1e3ccSAndroid Build Coastguard Worker return sigma * (u * sqrt(-2.0 * log(s) / s));
37*77c1e3ccSAndroid Build Coastguard Worker }
38*77c1e3ccSAndroid Build Coastguard Worker }
39*77c1e3ccSAndroid Build Coastguard Worker }
40*77c1e3ccSAndroid Build Coastguard Worker
41*77c1e3ccSAndroid Build Coastguard Worker // Synthesizes noise using the auto-regressive filter of the given lag,
42*77c1e3ccSAndroid Build Coastguard Worker // with the provided n coefficients sampled at the given coords.
noise_synth(libaom_test::ACMRandom * random,int lag,int n,const int (* coords)[2],const double * coeffs,double * data,int w,int h)43*77c1e3ccSAndroid Build Coastguard Worker void noise_synth(libaom_test::ACMRandom *random, int lag, int n,
44*77c1e3ccSAndroid Build Coastguard Worker const int (*coords)[2], const double *coeffs, double *data,
45*77c1e3ccSAndroid Build Coastguard Worker int w, int h) {
46*77c1e3ccSAndroid Build Coastguard Worker const int pad_size = 3 * lag;
47*77c1e3ccSAndroid Build Coastguard Worker const int padded_w = w + pad_size;
48*77c1e3ccSAndroid Build Coastguard Worker const int padded_h = h + pad_size;
49*77c1e3ccSAndroid Build Coastguard Worker int x = 0, y = 0;
50*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> padded(padded_w * padded_h);
51*77c1e3ccSAndroid Build Coastguard Worker
52*77c1e3ccSAndroid Build Coastguard Worker for (y = 0; y < padded_h; ++y) {
53*77c1e3ccSAndroid Build Coastguard Worker for (x = 0; x < padded_w; ++x) {
54*77c1e3ccSAndroid Build Coastguard Worker padded[y * padded_w + x] = randn(random, 1.0);
55*77c1e3ccSAndroid Build Coastguard Worker }
56*77c1e3ccSAndroid Build Coastguard Worker }
57*77c1e3ccSAndroid Build Coastguard Worker for (y = lag; y < padded_h; ++y) {
58*77c1e3ccSAndroid Build Coastguard Worker for (x = lag; x < padded_w; ++x) {
59*77c1e3ccSAndroid Build Coastguard Worker double sum = 0;
60*77c1e3ccSAndroid Build Coastguard Worker int i = 0;
61*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < n; ++i) {
62*77c1e3ccSAndroid Build Coastguard Worker const int dx = coords[i][0];
63*77c1e3ccSAndroid Build Coastguard Worker const int dy = coords[i][1];
64*77c1e3ccSAndroid Build Coastguard Worker sum += padded[(y + dy) * padded_w + (x + dx)] * coeffs[i];
65*77c1e3ccSAndroid Build Coastguard Worker }
66*77c1e3ccSAndroid Build Coastguard Worker padded[y * padded_w + x] += sum;
67*77c1e3ccSAndroid Build Coastguard Worker }
68*77c1e3ccSAndroid Build Coastguard Worker }
69*77c1e3ccSAndroid Build Coastguard Worker // Copy over the padded rows to the output
70*77c1e3ccSAndroid Build Coastguard Worker for (y = 0; y < h; ++y) {
71*77c1e3ccSAndroid Build Coastguard Worker memcpy(data + y * w, &padded[0] + y * padded_w, sizeof(*data) * w);
72*77c1e3ccSAndroid Build Coastguard Worker }
73*77c1e3ccSAndroid Build Coastguard Worker }
74*77c1e3ccSAndroid Build Coastguard Worker
get_noise_psd(double * noise,int width,int height,int block_size)75*77c1e3ccSAndroid Build Coastguard Worker std::vector<float> get_noise_psd(double *noise, int width, int height,
76*77c1e3ccSAndroid Build Coastguard Worker int block_size) {
77*77c1e3ccSAndroid Build Coastguard Worker float *block =
78*77c1e3ccSAndroid Build Coastguard Worker (float *)aom_memalign(32, block_size * block_size * sizeof(block));
79*77c1e3ccSAndroid Build Coastguard Worker std::vector<float> psd(block_size * block_size);
80*77c1e3ccSAndroid Build Coastguard Worker if (block == nullptr) {
81*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NE(block, nullptr);
82*77c1e3ccSAndroid Build Coastguard Worker return psd;
83*77c1e3ccSAndroid Build Coastguard Worker }
84*77c1e3ccSAndroid Build Coastguard Worker int num_blocks = 0;
85*77c1e3ccSAndroid Build Coastguard Worker struct aom_noise_tx_t *tx = aom_noise_tx_malloc(block_size);
86*77c1e3ccSAndroid Build Coastguard Worker if (tx == nullptr) {
87*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NE(tx, nullptr);
88*77c1e3ccSAndroid Build Coastguard Worker return psd;
89*77c1e3ccSAndroid Build Coastguard Worker }
90*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y <= height - block_size; y += block_size / 2) {
91*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x <= width - block_size; x += block_size / 2) {
92*77c1e3ccSAndroid Build Coastguard Worker for (int yy = 0; yy < block_size; ++yy) {
93*77c1e3ccSAndroid Build Coastguard Worker for (int xx = 0; xx < block_size; ++xx) {
94*77c1e3ccSAndroid Build Coastguard Worker block[yy * block_size + xx] = (float)noise[(y + yy) * width + x + xx];
95*77c1e3ccSAndroid Build Coastguard Worker }
96*77c1e3ccSAndroid Build Coastguard Worker }
97*77c1e3ccSAndroid Build Coastguard Worker aom_noise_tx_forward(tx, &block[0]);
98*77c1e3ccSAndroid Build Coastguard Worker aom_noise_tx_add_energy(tx, &psd[0]);
99*77c1e3ccSAndroid Build Coastguard Worker num_blocks++;
100*77c1e3ccSAndroid Build Coastguard Worker }
101*77c1e3ccSAndroid Build Coastguard Worker }
102*77c1e3ccSAndroid Build Coastguard Worker for (int yy = 0; yy < block_size; ++yy) {
103*77c1e3ccSAndroid Build Coastguard Worker for (int xx = 0; xx <= block_size / 2; ++xx) {
104*77c1e3ccSAndroid Build Coastguard Worker psd[yy * block_size + xx] /= num_blocks;
105*77c1e3ccSAndroid Build Coastguard Worker }
106*77c1e3ccSAndroid Build Coastguard Worker }
107*77c1e3ccSAndroid Build Coastguard Worker // Fill in the data that is missing due to symmetries
108*77c1e3ccSAndroid Build Coastguard Worker for (int xx = 1; xx < block_size / 2; ++xx) {
109*77c1e3ccSAndroid Build Coastguard Worker psd[(block_size - xx)] = psd[xx];
110*77c1e3ccSAndroid Build Coastguard Worker }
111*77c1e3ccSAndroid Build Coastguard Worker for (int yy = 1; yy < block_size; ++yy) {
112*77c1e3ccSAndroid Build Coastguard Worker for (int xx = 1; xx < block_size / 2; ++xx) {
113*77c1e3ccSAndroid Build Coastguard Worker psd[(block_size - yy) * block_size + (block_size - xx)] =
114*77c1e3ccSAndroid Build Coastguard Worker psd[yy * block_size + xx];
115*77c1e3ccSAndroid Build Coastguard Worker }
116*77c1e3ccSAndroid Build Coastguard Worker }
117*77c1e3ccSAndroid Build Coastguard Worker aom_noise_tx_free(tx);
118*77c1e3ccSAndroid Build Coastguard Worker aom_free(block);
119*77c1e3ccSAndroid Build Coastguard Worker return psd;
120*77c1e3ccSAndroid Build Coastguard Worker }
121*77c1e3ccSAndroid Build Coastguard Worker
122*77c1e3ccSAndroid Build Coastguard Worker } // namespace
123*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseStrengthSolver,GetCentersTwoBins)124*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseStrengthSolver, GetCentersTwoBins) {
125*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t solver;
126*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_init(&solver, 2, 8);
127*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, aom_noise_strength_solver_get_center(&solver, 0), 1e-5);
128*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(255, aom_noise_strength_solver_get_center(&solver, 1), 1e-5);
129*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_free(&solver);
130*77c1e3ccSAndroid Build Coastguard Worker }
131*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseStrengthSolver,GetCentersTwoBins10bit)132*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseStrengthSolver, GetCentersTwoBins10bit) {
133*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t solver;
134*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_init(&solver, 2, 10);
135*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, aom_noise_strength_solver_get_center(&solver, 0), 1e-5);
136*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1023, aom_noise_strength_solver_get_center(&solver, 1), 1e-5);
137*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_free(&solver);
138*77c1e3ccSAndroid Build Coastguard Worker }
139*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseStrengthSolver,GetCenters256Bins)140*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseStrengthSolver, GetCenters256Bins) {
141*77c1e3ccSAndroid Build Coastguard Worker const int num_bins = 256;
142*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t solver;
143*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_init(&solver, num_bins, 8);
144*77c1e3ccSAndroid Build Coastguard Worker
145*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < 256; ++i) {
146*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(i, aom_noise_strength_solver_get_center(&solver, i), 1e-5);
147*77c1e3ccSAndroid Build Coastguard Worker }
148*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_free(&solver);
149*77c1e3ccSAndroid Build Coastguard Worker }
150*77c1e3ccSAndroid Build Coastguard Worker
151*77c1e3ccSAndroid Build Coastguard Worker // Tests that the noise strength solver returns the identity transform when
152*77c1e3ccSAndroid Build Coastguard Worker // given identity-like constraints.
TEST(NoiseStrengthSolver,ObserveIdentity)153*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseStrengthSolver, ObserveIdentity) {
154*77c1e3ccSAndroid Build Coastguard Worker const int num_bins = 256;
155*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t solver;
156*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(1, aom_noise_strength_solver_init(&solver, num_bins, 8));
157*77c1e3ccSAndroid Build Coastguard Worker
158*77c1e3ccSAndroid Build Coastguard Worker // We have to add a big more strength to constraints at the boundary to
159*77c1e3ccSAndroid Build Coastguard Worker // overcome any regularization.
160*77c1e3ccSAndroid Build Coastguard Worker for (int j = 0; j < 5; ++j) {
161*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_add_measurement(&solver, 0, 0);
162*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_add_measurement(&solver, 255, 255);
163*77c1e3ccSAndroid Build Coastguard Worker }
164*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < 256; ++i) {
165*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_add_measurement(&solver, i, i);
166*77c1e3ccSAndroid Build Coastguard Worker }
167*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_noise_strength_solver_solve(&solver));
168*77c1e3ccSAndroid Build Coastguard Worker for (int i = 2; i < num_bins - 2; ++i) {
169*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(i, solver.eqns.x[i], 0.1);
170*77c1e3ccSAndroid Build Coastguard Worker }
171*77c1e3ccSAndroid Build Coastguard Worker
172*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t lut;
173*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_noise_strength_solver_fit_piecewise(&solver, 2, &lut));
174*77c1e3ccSAndroid Build Coastguard Worker
175*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(2, lut.num_points);
176*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0.0, lut.points[0][0], 1e-5);
177*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0.0, lut.points[0][1], 0.5);
178*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(255.0, lut.points[1][0], 1e-5);
179*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(255.0, lut.points[1][1], 0.5);
180*77c1e3ccSAndroid Build Coastguard Worker
181*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(&lut);
182*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_free(&solver);
183*77c1e3ccSAndroid Build Coastguard Worker }
184*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseStrengthSolver,SimplifiesCurve)185*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseStrengthSolver, SimplifiesCurve) {
186*77c1e3ccSAndroid Build Coastguard Worker const int num_bins = 256;
187*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t solver;
188*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_noise_strength_solver_init(&solver, num_bins, 8));
189*77c1e3ccSAndroid Build Coastguard Worker
190*77c1e3ccSAndroid Build Coastguard Worker // Create a parabolic input
191*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < 256; ++i) {
192*77c1e3ccSAndroid Build Coastguard Worker const double x = (i - 127.5) / 63.5;
193*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_add_measurement(&solver, i, x * x);
194*77c1e3ccSAndroid Build Coastguard Worker }
195*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_noise_strength_solver_solve(&solver));
196*77c1e3ccSAndroid Build Coastguard Worker
197*77c1e3ccSAndroid Build Coastguard Worker // First try to fit an unconstrained lut
198*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t lut;
199*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_noise_strength_solver_fit_piecewise(&solver, -1, &lut));
200*77c1e3ccSAndroid Build Coastguard Worker ASSERT_LE(20, lut.num_points);
201*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(&lut);
202*77c1e3ccSAndroid Build Coastguard Worker
203*77c1e3ccSAndroid Build Coastguard Worker // Now constrain the maximum number of points
204*77c1e3ccSAndroid Build Coastguard Worker const int kMaxPoints = 9;
205*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1,
206*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_fit_piecewise(&solver, kMaxPoints, &lut));
207*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(kMaxPoints, lut.num_points);
208*77c1e3ccSAndroid Build Coastguard Worker
209*77c1e3ccSAndroid Build Coastguard Worker // Check that the input parabola is still well represented
210*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0.0, lut.points[0][0], 1e-5);
211*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(4.0, lut.points[0][1], 0.1);
212*77c1e3ccSAndroid Build Coastguard Worker for (int i = 1; i < lut.num_points - 1; ++i) {
213*77c1e3ccSAndroid Build Coastguard Worker const double x = (lut.points[i][0] - 128.) / 64.;
214*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(x * x, lut.points[i][1], 0.1);
215*77c1e3ccSAndroid Build Coastguard Worker }
216*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(255.0, lut.points[kMaxPoints - 1][0], 1e-5);
217*77c1e3ccSAndroid Build Coastguard Worker
218*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(4.0, lut.points[kMaxPoints - 1][1], 0.1);
219*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(&lut);
220*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_free(&solver);
221*77c1e3ccSAndroid Build Coastguard Worker }
222*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseStrengthLut,LutInitNegativeOrZeroSize)223*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseStrengthLut, LutInitNegativeOrZeroSize) {
224*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t lut;
225*77c1e3ccSAndroid Build Coastguard Worker ASSERT_FALSE(aom_noise_strength_lut_init(&lut, -1));
226*77c1e3ccSAndroid Build Coastguard Worker ASSERT_FALSE(aom_noise_strength_lut_init(&lut, 0));
227*77c1e3ccSAndroid Build Coastguard Worker }
228*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseStrengthLut,LutEvalSinglePoint)229*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseStrengthLut, LutEvalSinglePoint) {
230*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t lut;
231*77c1e3ccSAndroid Build Coastguard Worker ASSERT_TRUE(aom_noise_strength_lut_init(&lut, 1));
232*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(1, lut.num_points);
233*77c1e3ccSAndroid Build Coastguard Worker lut.points[0][0] = 0;
234*77c1e3ccSAndroid Build Coastguard Worker lut.points[0][1] = 1;
235*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_noise_strength_lut_eval(&lut, -1));
236*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_noise_strength_lut_eval(&lut, 0));
237*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_noise_strength_lut_eval(&lut, 1));
238*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(&lut);
239*77c1e3ccSAndroid Build Coastguard Worker }
240*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseStrengthLut,LutEvalMultiPointInterp)241*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseStrengthLut, LutEvalMultiPointInterp) {
242*77c1e3ccSAndroid Build Coastguard Worker const double kEps = 1e-5;
243*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t lut;
244*77c1e3ccSAndroid Build Coastguard Worker ASSERT_TRUE(aom_noise_strength_lut_init(&lut, 4));
245*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(4, lut.num_points);
246*77c1e3ccSAndroid Build Coastguard Worker
247*77c1e3ccSAndroid Build Coastguard Worker lut.points[0][0] = 0;
248*77c1e3ccSAndroid Build Coastguard Worker lut.points[0][1] = 0;
249*77c1e3ccSAndroid Build Coastguard Worker
250*77c1e3ccSAndroid Build Coastguard Worker lut.points[1][0] = 1;
251*77c1e3ccSAndroid Build Coastguard Worker lut.points[1][1] = 1;
252*77c1e3ccSAndroid Build Coastguard Worker
253*77c1e3ccSAndroid Build Coastguard Worker lut.points[2][0] = 2;
254*77c1e3ccSAndroid Build Coastguard Worker lut.points[2][1] = 1;
255*77c1e3ccSAndroid Build Coastguard Worker
256*77c1e3ccSAndroid Build Coastguard Worker lut.points[3][0] = 100;
257*77c1e3ccSAndroid Build Coastguard Worker lut.points[3][1] = 1001;
258*77c1e3ccSAndroid Build Coastguard Worker
259*77c1e3ccSAndroid Build Coastguard Worker // Test lower boundary
260*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, aom_noise_strength_lut_eval(&lut, -1));
261*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, aom_noise_strength_lut_eval(&lut, 0));
262*77c1e3ccSAndroid Build Coastguard Worker
263*77c1e3ccSAndroid Build Coastguard Worker // Test first part that should be identity
264*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0.25, aom_noise_strength_lut_eval(&lut, 0.25), kEps);
265*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0.75, aom_noise_strength_lut_eval(&lut, 0.75), kEps);
266*77c1e3ccSAndroid Build Coastguard Worker
267*77c1e3ccSAndroid Build Coastguard Worker // This is a constant section (should evaluate to 1)
268*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1.0, aom_noise_strength_lut_eval(&lut, 1.25), kEps);
269*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1.0, aom_noise_strength_lut_eval(&lut, 1.75), kEps);
270*77c1e3ccSAndroid Build Coastguard Worker
271*77c1e3ccSAndroid Build Coastguard Worker // Test interpolation between to non-zero y coords.
272*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1, aom_noise_strength_lut_eval(&lut, 2), kEps);
273*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(251, aom_noise_strength_lut_eval(&lut, 26.5), kEps);
274*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(751, aom_noise_strength_lut_eval(&lut, 75.5), kEps);
275*77c1e3ccSAndroid Build Coastguard Worker
276*77c1e3ccSAndroid Build Coastguard Worker // Test upper boundary
277*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1001, aom_noise_strength_lut_eval(&lut, 100));
278*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1001, aom_noise_strength_lut_eval(&lut, 101));
279*77c1e3ccSAndroid Build Coastguard Worker
280*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(&lut);
281*77c1e3ccSAndroid Build Coastguard Worker }
282*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModel,InitSuccessWithValidSquareShape)283*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModel, InitSuccessWithValidSquareShape) {
284*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 2, 8, 0 };
285*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
286*77c1e3ccSAndroid Build Coastguard Worker
287*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_init(&model, params));
288*77c1e3ccSAndroid Build Coastguard Worker
289*77c1e3ccSAndroid Build Coastguard Worker const int kNumCoords = 12;
290*77c1e3ccSAndroid Build Coastguard Worker const int kCoords[][2] = { { -2, -2 }, { -1, -2 }, { 0, -2 }, { 1, -2 },
291*77c1e3ccSAndroid Build Coastguard Worker { 2, -2 }, { -2, -1 }, { -1, -1 }, { 0, -1 },
292*77c1e3ccSAndroid Build Coastguard Worker { 1, -1 }, { 2, -1 }, { -2, 0 }, { -1, 0 } };
293*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumCoords, model.n);
294*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kNumCoords; ++i) {
295*77c1e3ccSAndroid Build Coastguard Worker const int *coord = kCoords[i];
296*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(coord[0], model.coords[i][0]);
297*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(coord[1], model.coords[i][1]);
298*77c1e3ccSAndroid Build Coastguard Worker }
299*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
300*77c1e3ccSAndroid Build Coastguard Worker }
301*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModel,InitSuccessWithValidDiamondShape)302*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModel, InitSuccessWithValidDiamondShape) {
303*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
304*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_DIAMOND, 2, 8, 0 };
305*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_init(&model, params));
306*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(6, model.n);
307*77c1e3ccSAndroid Build Coastguard Worker const int kNumCoords = 6;
308*77c1e3ccSAndroid Build Coastguard Worker const int kCoords[][2] = { { 0, -2 }, { -1, -1 }, { 0, -1 },
309*77c1e3ccSAndroid Build Coastguard Worker { 1, -1 }, { -2, 0 }, { -1, 0 } };
310*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumCoords, model.n);
311*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kNumCoords; ++i) {
312*77c1e3ccSAndroid Build Coastguard Worker const int *coord = kCoords[i];
313*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(coord[0], model.coords[i][0]);
314*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(coord[1], model.coords[i][1]);
315*77c1e3ccSAndroid Build Coastguard Worker }
316*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
317*77c1e3ccSAndroid Build Coastguard Worker }
318*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModel,InitFailsWithTooLargeLag)319*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModel, InitFailsWithTooLargeLag) {
320*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
321*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 10, 8, 0 };
322*77c1e3ccSAndroid Build Coastguard Worker EXPECT_FALSE(aom_noise_model_init(&model, params));
323*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
324*77c1e3ccSAndroid Build Coastguard Worker }
325*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModel,InitFailsWithTooSmallLag)326*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModel, InitFailsWithTooSmallLag) {
327*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
328*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 0, 8, 0 };
329*77c1e3ccSAndroid Build Coastguard Worker EXPECT_FALSE(aom_noise_model_init(&model, params));
330*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
331*77c1e3ccSAndroid Build Coastguard Worker }
332*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModel,InitFailsWithInvalidShape)333*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModel, InitFailsWithInvalidShape) {
334*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
335*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { aom_noise_shape(100), 3, 8, 0 };
336*77c1e3ccSAndroid Build Coastguard Worker EXPECT_FALSE(aom_noise_model_init(&model, params));
337*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
338*77c1e3ccSAndroid Build Coastguard Worker }
339*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModel,InitFailsWithInvalidBitdepth)340*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModel, InitFailsWithInvalidBitdepth) {
341*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
342*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 2, 8, 0 };
343*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i <= 32; ++i) {
344*77c1e3ccSAndroid Build Coastguard Worker params.bit_depth = i;
345*77c1e3ccSAndroid Build Coastguard Worker if (i == 8 || i == 10 || i == 12) {
346*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_init(&model, params)) << "bit_depth: " << i;
347*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
348*77c1e3ccSAndroid Build Coastguard Worker } else {
349*77c1e3ccSAndroid Build Coastguard Worker EXPECT_FALSE(aom_noise_model_init(&model, params)) << "bit_depth: " << i;
350*77c1e3ccSAndroid Build Coastguard Worker }
351*77c1e3ccSAndroid Build Coastguard Worker }
352*77c1e3ccSAndroid Build Coastguard Worker params.bit_depth = INT_MAX;
353*77c1e3ccSAndroid Build Coastguard Worker EXPECT_FALSE(aom_noise_model_init(&model, params));
354*77c1e3ccSAndroid Build Coastguard Worker }
355*77c1e3ccSAndroid Build Coastguard Worker
356*77c1e3ccSAndroid Build Coastguard Worker // A container template class to hold a data type and extra arguments.
357*77c1e3ccSAndroid Build Coastguard Worker // All of these args are bundled into one struct so that we can use
358*77c1e3ccSAndroid Build Coastguard Worker // parameterized tests on combinations of supported data types
359*77c1e3ccSAndroid Build Coastguard Worker // (uint8_t and uint16_t) and bit depths (8, 10, 12).
360*77c1e3ccSAndroid Build Coastguard Worker template <typename T, int bit_depth, bool use_highbd>
361*77c1e3ccSAndroid Build Coastguard Worker struct BitDepthParams {
362*77c1e3ccSAndroid Build Coastguard Worker typedef T data_type_t;
363*77c1e3ccSAndroid Build Coastguard Worker static const int kBitDepth = bit_depth;
364*77c1e3ccSAndroid Build Coastguard Worker static const bool kUseHighBD = use_highbd;
365*77c1e3ccSAndroid Build Coastguard Worker };
366*77c1e3ccSAndroid Build Coastguard Worker
367*77c1e3ccSAndroid Build Coastguard Worker template <typename T>
368*77c1e3ccSAndroid Build Coastguard Worker class FlatBlockEstimatorTest : public ::testing::Test, public T {
369*77c1e3ccSAndroid Build Coastguard Worker public:
SetUp()370*77c1e3ccSAndroid Build Coastguard Worker void SetUp() override { random_.Reset(171); }
371*77c1e3ccSAndroid Build Coastguard Worker typedef std::vector<typename T::data_type_t> VecType;
372*77c1e3ccSAndroid Build Coastguard Worker VecType data_;
373*77c1e3ccSAndroid Build Coastguard Worker libaom_test::ACMRandom random_;
374*77c1e3ccSAndroid Build Coastguard Worker };
375*77c1e3ccSAndroid Build Coastguard Worker
376*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_SUITE_P(FlatBlockEstimatorTest);
377*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(FlatBlockEstimatorTest,ExtractBlock)378*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(FlatBlockEstimatorTest, ExtractBlock) {
379*77c1e3ccSAndroid Build Coastguard Worker const int kBlockSize = 16;
380*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_t flat_block_finder;
381*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(1, aom_flat_block_finder_init(&flat_block_finder, kBlockSize,
382*77c1e3ccSAndroid Build Coastguard Worker this->kBitDepth, this->kUseHighBD));
383*77c1e3ccSAndroid Build Coastguard Worker const double normalization = flat_block_finder.normalization;
384*77c1e3ccSAndroid Build Coastguard Worker
385*77c1e3ccSAndroid Build Coastguard Worker // Test with an image of more than one block.
386*77c1e3ccSAndroid Build Coastguard Worker const int h = 2 * kBlockSize;
387*77c1e3ccSAndroid Build Coastguard Worker const int w = 2 * kBlockSize;
388*77c1e3ccSAndroid Build Coastguard Worker const int stride = 2 * kBlockSize;
389*77c1e3ccSAndroid Build Coastguard Worker this->data_.resize(h * stride, 128);
390*77c1e3ccSAndroid Build Coastguard Worker
391*77c1e3ccSAndroid Build Coastguard Worker // Set up the (0,0) block to be a plane and the (0,1) block to be a
392*77c1e3ccSAndroid Build Coastguard Worker // checkerboard
393*77c1e3ccSAndroid Build Coastguard Worker const int shift = this->kBitDepth - 8;
394*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < kBlockSize; ++y) {
395*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < kBlockSize; ++x) {
396*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x] = (-y + x + 128) << shift;
397*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + kBlockSize] =
398*77c1e3ccSAndroid Build Coastguard Worker ((x % 2 + y % 2) % 2 ? 128 - 20 : 128 + 20) << shift;
399*77c1e3ccSAndroid Build Coastguard Worker }
400*77c1e3ccSAndroid Build Coastguard Worker }
401*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> block(kBlockSize * kBlockSize, 1);
402*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> plane(kBlockSize * kBlockSize, 1);
403*77c1e3ccSAndroid Build Coastguard Worker
404*77c1e3ccSAndroid Build Coastguard Worker // The block data should be a constant (zero) and the rest of the plane
405*77c1e3ccSAndroid Build Coastguard Worker // trend is covered in the plane data.
406*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_extract_block(&flat_block_finder,
407*77c1e3ccSAndroid Build Coastguard Worker (uint8_t *)&this->data_[0], w, h, stride,
408*77c1e3ccSAndroid Build Coastguard Worker 0, 0, &plane[0], &block[0]);
409*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < kBlockSize; ++y) {
410*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < kBlockSize; ++x) {
411*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, block[y * kBlockSize + x], 1e-5);
412*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR((double)(this->data_[y * stride + x]) / normalization,
413*77c1e3ccSAndroid Build Coastguard Worker plane[y * kBlockSize + x], 1e-5);
414*77c1e3ccSAndroid Build Coastguard Worker }
415*77c1e3ccSAndroid Build Coastguard Worker }
416*77c1e3ccSAndroid Build Coastguard Worker
417*77c1e3ccSAndroid Build Coastguard Worker // The plane trend is a constant, and the block is a zero mean checkerboard.
418*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_extract_block(&flat_block_finder,
419*77c1e3ccSAndroid Build Coastguard Worker (uint8_t *)&this->data_[0], w, h, stride,
420*77c1e3ccSAndroid Build Coastguard Worker kBlockSize, 0, &plane[0], &block[0]);
421*77c1e3ccSAndroid Build Coastguard Worker const int mid = 128 << shift;
422*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < kBlockSize; ++y) {
423*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < kBlockSize; ++x) {
424*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(((double)this->data_[y * stride + x + kBlockSize] - mid) /
425*77c1e3ccSAndroid Build Coastguard Worker normalization,
426*77c1e3ccSAndroid Build Coastguard Worker block[y * kBlockSize + x], 1e-5);
427*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(mid / normalization, plane[y * kBlockSize + x], 1e-5);
428*77c1e3ccSAndroid Build Coastguard Worker }
429*77c1e3ccSAndroid Build Coastguard Worker }
430*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_free(&flat_block_finder);
431*77c1e3ccSAndroid Build Coastguard Worker }
432*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(FlatBlockEstimatorTest,FindFlatBlocks)433*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(FlatBlockEstimatorTest, FindFlatBlocks) {
434*77c1e3ccSAndroid Build Coastguard Worker const int kBlockSize = 32;
435*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_t flat_block_finder;
436*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(1, aom_flat_block_finder_init(&flat_block_finder, kBlockSize,
437*77c1e3ccSAndroid Build Coastguard Worker this->kBitDepth, this->kUseHighBD));
438*77c1e3ccSAndroid Build Coastguard Worker
439*77c1e3ccSAndroid Build Coastguard Worker const int num_blocks_w = 8;
440*77c1e3ccSAndroid Build Coastguard Worker const int h = kBlockSize;
441*77c1e3ccSAndroid Build Coastguard Worker const int w = kBlockSize * num_blocks_w;
442*77c1e3ccSAndroid Build Coastguard Worker const int stride = w;
443*77c1e3ccSAndroid Build Coastguard Worker this->data_.resize(h * stride, 128);
444*77c1e3ccSAndroid Build Coastguard Worker std::vector<uint8_t> flat_blocks(num_blocks_w, 0);
445*77c1e3ccSAndroid Build Coastguard Worker
446*77c1e3ccSAndroid Build Coastguard Worker const int shift = this->kBitDepth - 8;
447*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < kBlockSize; ++y) {
448*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < kBlockSize; ++x) {
449*77c1e3ccSAndroid Build Coastguard Worker // Block 0 (not flat): constant doesn't have enough variance to qualify
450*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + 0 * kBlockSize] = 128 << shift;
451*77c1e3ccSAndroid Build Coastguard Worker
452*77c1e3ccSAndroid Build Coastguard Worker // Block 1 (not flat): too high of variance is hard to validate as flat
453*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + 1 * kBlockSize] =
454*77c1e3ccSAndroid Build Coastguard Worker ((uint8_t)(128 + randn(&this->random_, 5))) << shift;
455*77c1e3ccSAndroid Build Coastguard Worker
456*77c1e3ccSAndroid Build Coastguard Worker // Block 2 (flat): slight checkerboard added to constant
457*77c1e3ccSAndroid Build Coastguard Worker const int check = (x % 2 + y % 2) % 2 ? -2 : 2;
458*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + 2 * kBlockSize] = (128 + check) << shift;
459*77c1e3ccSAndroid Build Coastguard Worker
460*77c1e3ccSAndroid Build Coastguard Worker // Block 3 (flat): planar block with checkerboard pattern is also flat
461*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + 3 * kBlockSize] =
462*77c1e3ccSAndroid Build Coastguard Worker (y * 2 - x / 2 + 128 + check) << shift;
463*77c1e3ccSAndroid Build Coastguard Worker
464*77c1e3ccSAndroid Build Coastguard Worker // Block 4 (flat): gaussian random with standard deviation 1.
465*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + 4 * kBlockSize] =
466*77c1e3ccSAndroid Build Coastguard Worker ((uint8_t)(randn(&this->random_, 1) + x + 128.0)) << shift;
467*77c1e3ccSAndroid Build Coastguard Worker
468*77c1e3ccSAndroid Build Coastguard Worker // Block 5 (flat): gaussian random with standard deviation 2.
469*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + 5 * kBlockSize] =
470*77c1e3ccSAndroid Build Coastguard Worker ((uint8_t)(randn(&this->random_, 2) + y + 128.0)) << shift;
471*77c1e3ccSAndroid Build Coastguard Worker
472*77c1e3ccSAndroid Build Coastguard Worker // Block 6 (not flat): too high of directional gradient.
473*77c1e3ccSAndroid Build Coastguard Worker const int strong_edge = x > kBlockSize / 2 ? 64 : 0;
474*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + 6 * kBlockSize] =
475*77c1e3ccSAndroid Build Coastguard Worker ((uint8_t)(randn(&this->random_, 1) + strong_edge + 128.0)) << shift;
476*77c1e3ccSAndroid Build Coastguard Worker
477*77c1e3ccSAndroid Build Coastguard Worker // Block 7 (not flat): too high gradient.
478*77c1e3ccSAndroid Build Coastguard Worker const int big_check = ((x >> 2) % 2 + (y >> 2) % 2) % 2 ? -16 : 16;
479*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x + 7 * kBlockSize] =
480*77c1e3ccSAndroid Build Coastguard Worker ((uint8_t)(randn(&this->random_, 1) + big_check + 128.0)) << shift;
481*77c1e3ccSAndroid Build Coastguard Worker }
482*77c1e3ccSAndroid Build Coastguard Worker }
483*77c1e3ccSAndroid Build Coastguard Worker
484*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(4, aom_flat_block_finder_run(&flat_block_finder,
485*77c1e3ccSAndroid Build Coastguard Worker (uint8_t *)&this->data_[0], w, h,
486*77c1e3ccSAndroid Build Coastguard Worker stride, &flat_blocks[0]));
487*77c1e3ccSAndroid Build Coastguard Worker
488*77c1e3ccSAndroid Build Coastguard Worker // First two blocks are not flat
489*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[0]);
490*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[1]);
491*77c1e3ccSAndroid Build Coastguard Worker
492*77c1e3ccSAndroid Build Coastguard Worker // Next 4 blocks are flat.
493*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(255, flat_blocks[2]);
494*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(255, flat_blocks[3]);
495*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(255, flat_blocks[4]);
496*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(255, flat_blocks[5]);
497*77c1e3ccSAndroid Build Coastguard Worker
498*77c1e3ccSAndroid Build Coastguard Worker // Last 2 are not flat by threshold
499*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[6]);
500*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[7]);
501*77c1e3ccSAndroid Build Coastguard Worker
502*77c1e3ccSAndroid Build Coastguard Worker // Add the noise from non-flat block 1 to every block.
503*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < kBlockSize; ++y) {
504*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < kBlockSize * num_blocks_w; ++x) {
505*77c1e3ccSAndroid Build Coastguard Worker this->data_[y * stride + x] +=
506*77c1e3ccSAndroid Build Coastguard Worker (this->data_[y * stride + x % kBlockSize + kBlockSize] -
507*77c1e3ccSAndroid Build Coastguard Worker (128 << shift));
508*77c1e3ccSAndroid Build Coastguard Worker }
509*77c1e3ccSAndroid Build Coastguard Worker }
510*77c1e3ccSAndroid Build Coastguard Worker // Now the scored selection will pick the one that is most likely flat (block
511*77c1e3ccSAndroid Build Coastguard Worker // 0)
512*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, aom_flat_block_finder_run(&flat_block_finder,
513*77c1e3ccSAndroid Build Coastguard Worker (uint8_t *)&this->data_[0], w, h,
514*77c1e3ccSAndroid Build Coastguard Worker stride, &flat_blocks[0]));
515*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, flat_blocks[0]);
516*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[1]);
517*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[2]);
518*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[3]);
519*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[4]);
520*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[5]);
521*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[6]);
522*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, flat_blocks[7]);
523*77c1e3ccSAndroid Build Coastguard Worker
524*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_free(&flat_block_finder);
525*77c1e3ccSAndroid Build Coastguard Worker }
526*77c1e3ccSAndroid Build Coastguard Worker
527*77c1e3ccSAndroid Build Coastguard Worker REGISTER_TYPED_TEST_SUITE_P(FlatBlockEstimatorTest, ExtractBlock,
528*77c1e3ccSAndroid Build Coastguard Worker FindFlatBlocks);
529*77c1e3ccSAndroid Build Coastguard Worker
530*77c1e3ccSAndroid Build Coastguard Worker typedef ::testing::Types<BitDepthParams<uint8_t, 8, false>, // lowbd
531*77c1e3ccSAndroid Build Coastguard Worker BitDepthParams<uint16_t, 8, true>, // lowbd in 16-bit
532*77c1e3ccSAndroid Build Coastguard Worker BitDepthParams<uint16_t, 10, true>, // highbd data
533*77c1e3ccSAndroid Build Coastguard Worker BitDepthParams<uint16_t, 12, true> >
534*77c1e3ccSAndroid Build Coastguard Worker AllBitDepthParams;
535*77c1e3ccSAndroid Build Coastguard Worker // Note the empty final argument can be removed if C++20 is made the minimum
536*77c1e3ccSAndroid Build Coastguard Worker // requirement.
537*77c1e3ccSAndroid Build Coastguard Worker INSTANTIATE_TYPED_TEST_SUITE_P(FlatBlockInstatiation, FlatBlockEstimatorTest,
538*77c1e3ccSAndroid Build Coastguard Worker AllBitDepthParams, );
539*77c1e3ccSAndroid Build Coastguard Worker
540*77c1e3ccSAndroid Build Coastguard Worker template <typename T>
541*77c1e3ccSAndroid Build Coastguard Worker class NoiseModelUpdateTest : public ::testing::Test, public T {
542*77c1e3ccSAndroid Build Coastguard Worker public:
543*77c1e3ccSAndroid Build Coastguard Worker static const int kWidth = 128;
544*77c1e3ccSAndroid Build Coastguard Worker static const int kHeight = 128;
545*77c1e3ccSAndroid Build Coastguard Worker static const int kBlockSize = 16;
546*77c1e3ccSAndroid Build Coastguard Worker static const int kNumBlocksX = kWidth / kBlockSize;
547*77c1e3ccSAndroid Build Coastguard Worker static const int kNumBlocksY = kHeight / kBlockSize;
548*77c1e3ccSAndroid Build Coastguard Worker
SetUp()549*77c1e3ccSAndroid Build Coastguard Worker void SetUp() override {
550*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 3,
551*77c1e3ccSAndroid Build Coastguard Worker T::kBitDepth, T::kUseHighBD };
552*77c1e3ccSAndroid Build Coastguard Worker ASSERT_TRUE(aom_noise_model_init(&model_, params));
553*77c1e3ccSAndroid Build Coastguard Worker
554*77c1e3ccSAndroid Build Coastguard Worker random_.Reset(100171);
555*77c1e3ccSAndroid Build Coastguard Worker
556*77c1e3ccSAndroid Build Coastguard Worker data_.resize(kWidth * kHeight * 3);
557*77c1e3ccSAndroid Build Coastguard Worker denoised_.resize(kWidth * kHeight * 3);
558*77c1e3ccSAndroid Build Coastguard Worker noise_.resize(kWidth * kHeight * 3);
559*77c1e3ccSAndroid Build Coastguard Worker renoise_.resize(kWidth * kHeight);
560*77c1e3ccSAndroid Build Coastguard Worker flat_blocks_.resize(kNumBlocksX * kNumBlocksY);
561*77c1e3ccSAndroid Build Coastguard Worker
562*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0, offset = 0; c < 3; ++c, offset += kWidth * kHeight) {
563*77c1e3ccSAndroid Build Coastguard Worker data_ptr_[c] = &data_[offset];
564*77c1e3ccSAndroid Build Coastguard Worker noise_ptr_[c] = &noise_[offset];
565*77c1e3ccSAndroid Build Coastguard Worker denoised_ptr_[c] = &denoised_[offset];
566*77c1e3ccSAndroid Build Coastguard Worker strides_[c] = kWidth;
567*77c1e3ccSAndroid Build Coastguard Worker
568*77c1e3ccSAndroid Build Coastguard Worker data_ptr_raw_[c] = (uint8_t *)&data_[offset];
569*77c1e3ccSAndroid Build Coastguard Worker denoised_ptr_raw_[c] = (uint8_t *)&denoised_[offset];
570*77c1e3ccSAndroid Build Coastguard Worker }
571*77c1e3ccSAndroid Build Coastguard Worker chroma_sub_[0] = 0;
572*77c1e3ccSAndroid Build Coastguard Worker chroma_sub_[1] = 0;
573*77c1e3ccSAndroid Build Coastguard Worker }
574*77c1e3ccSAndroid Build Coastguard Worker
NoiseModelUpdate(int block_size=kBlockSize)575*77c1e3ccSAndroid Build Coastguard Worker int NoiseModelUpdate(int block_size = kBlockSize) {
576*77c1e3ccSAndroid Build Coastguard Worker return aom_noise_model_update(&model_, data_ptr_raw_, denoised_ptr_raw_,
577*77c1e3ccSAndroid Build Coastguard Worker kWidth, kHeight, strides_, chroma_sub_,
578*77c1e3ccSAndroid Build Coastguard Worker &flat_blocks_[0], block_size);
579*77c1e3ccSAndroid Build Coastguard Worker }
580*77c1e3ccSAndroid Build Coastguard Worker
TearDown()581*77c1e3ccSAndroid Build Coastguard Worker void TearDown() override { aom_noise_model_free(&model_); }
582*77c1e3ccSAndroid Build Coastguard Worker
583*77c1e3ccSAndroid Build Coastguard Worker protected:
584*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model_;
585*77c1e3ccSAndroid Build Coastguard Worker std::vector<typename T::data_type_t> data_;
586*77c1e3ccSAndroid Build Coastguard Worker std::vector<typename T::data_type_t> denoised_;
587*77c1e3ccSAndroid Build Coastguard Worker
588*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> noise_;
589*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> renoise_;
590*77c1e3ccSAndroid Build Coastguard Worker std::vector<uint8_t> flat_blocks_;
591*77c1e3ccSAndroid Build Coastguard Worker
592*77c1e3ccSAndroid Build Coastguard Worker typename T::data_type_t *data_ptr_[3];
593*77c1e3ccSAndroid Build Coastguard Worker typename T::data_type_t *denoised_ptr_[3];
594*77c1e3ccSAndroid Build Coastguard Worker
595*77c1e3ccSAndroid Build Coastguard Worker double *noise_ptr_[3];
596*77c1e3ccSAndroid Build Coastguard Worker int strides_[3];
597*77c1e3ccSAndroid Build Coastguard Worker int chroma_sub_[2];
598*77c1e3ccSAndroid Build Coastguard Worker libaom_test::ACMRandom random_;
599*77c1e3ccSAndroid Build Coastguard Worker
600*77c1e3ccSAndroid Build Coastguard Worker private:
601*77c1e3ccSAndroid Build Coastguard Worker uint8_t *data_ptr_raw_[3];
602*77c1e3ccSAndroid Build Coastguard Worker uint8_t *denoised_ptr_raw_[3];
603*77c1e3ccSAndroid Build Coastguard Worker };
604*77c1e3ccSAndroid Build Coastguard Worker
605*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_SUITE_P(NoiseModelUpdateTest);
606*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(NoiseModelUpdateTest,UpdateFailsNoFlatBlocks)607*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(NoiseModelUpdateTest, UpdateFailsNoFlatBlocks) {
608*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_INSUFFICIENT_FLAT_BLOCKS,
609*77c1e3ccSAndroid Build Coastguard Worker this->NoiseModelUpdate());
610*77c1e3ccSAndroid Build Coastguard Worker }
611*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(NoiseModelUpdateTest,UpdateSuccessForZeroNoiseAllFlat)612*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(NoiseModelUpdateTest, UpdateSuccessForZeroNoiseAllFlat) {
613*77c1e3ccSAndroid Build Coastguard Worker this->flat_blocks_.assign(this->flat_blocks_.size(), 1);
614*77c1e3ccSAndroid Build Coastguard Worker this->denoised_.assign(this->denoised_.size(), 128);
615*77c1e3ccSAndroid Build Coastguard Worker this->data_.assign(this->denoised_.size(), 128);
616*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_INTERNAL_ERROR, this->NoiseModelUpdate());
617*77c1e3ccSAndroid Build Coastguard Worker }
618*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(NoiseModelUpdateTest,UpdateFailsBlockSizeTooSmall)619*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(NoiseModelUpdateTest, UpdateFailsBlockSizeTooSmall) {
620*77c1e3ccSAndroid Build Coastguard Worker this->flat_blocks_.assign(this->flat_blocks_.size(), 1);
621*77c1e3ccSAndroid Build Coastguard Worker this->denoised_.assign(this->denoised_.size(), 128);
622*77c1e3ccSAndroid Build Coastguard Worker this->data_.assign(this->denoised_.size(), 128);
623*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_INVALID_ARGUMENT,
624*77c1e3ccSAndroid Build Coastguard Worker this->NoiseModelUpdate(6 /* block_size=6 is too small*/));
625*77c1e3ccSAndroid Build Coastguard Worker }
626*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(NoiseModelUpdateTest,UpdateSuccessForWhiteRandomNoise)627*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(NoiseModelUpdateTest, UpdateSuccessForWhiteRandomNoise) {
628*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t &model = this->model_;
629*77c1e3ccSAndroid Build Coastguard Worker const int width = this->kWidth;
630*77c1e3ccSAndroid Build Coastguard Worker const int height = this->kHeight;
631*77c1e3ccSAndroid Build Coastguard Worker
632*77c1e3ccSAndroid Build Coastguard Worker const int shift = this->kBitDepth - 8;
633*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < height; ++y) {
634*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < width; ++x) {
635*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[0][y * width + x] = int(64 + y + randn(&this->random_, 1))
636*77c1e3ccSAndroid Build Coastguard Worker << shift;
637*77c1e3ccSAndroid Build Coastguard Worker this->denoised_ptr_[0][y * width + x] = (64 + y) << shift;
638*77c1e3ccSAndroid Build Coastguard Worker // Make the chroma planes completely correlated with the Y plane
639*77c1e3ccSAndroid Build Coastguard Worker for (int c = 1; c < 3; ++c) {
640*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[c][y * width + x] = this->data_ptr_[0][y * width + x];
641*77c1e3ccSAndroid Build Coastguard Worker this->denoised_ptr_[c][y * width + x] =
642*77c1e3ccSAndroid Build Coastguard Worker this->denoised_ptr_[0][y * width + x];
643*77c1e3ccSAndroid Build Coastguard Worker }
644*77c1e3ccSAndroid Build Coastguard Worker }
645*77c1e3ccSAndroid Build Coastguard Worker }
646*77c1e3ccSAndroid Build Coastguard Worker this->flat_blocks_.assign(this->flat_blocks_.size(), 1);
647*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_OK, this->NoiseModelUpdate());
648*77c1e3ccSAndroid Build Coastguard Worker
649*77c1e3ccSAndroid Build Coastguard Worker const double kCoeffEps = 0.075;
650*77c1e3ccSAndroid Build Coastguard Worker const int n = model.n;
651*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
652*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
653*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, model.latest_state[c].eqns.x[i], kCoeffEps);
654*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, model.combined_state[c].eqns.x[i], kCoeffEps);
655*77c1e3ccSAndroid Build Coastguard Worker }
656*77c1e3ccSAndroid Build Coastguard Worker // The second and third channels are highly correlated with the first.
657*77c1e3ccSAndroid Build Coastguard Worker if (c > 0) {
658*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(n + 1, model.latest_state[c].eqns.n);
659*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(n + 1, model.combined_state[c].eqns.n);
660*77c1e3ccSAndroid Build Coastguard Worker
661*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1, model.latest_state[c].eqns.x[n], kCoeffEps);
662*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1, model.combined_state[c].eqns.x[n], kCoeffEps);
663*77c1e3ccSAndroid Build Coastguard Worker }
664*77c1e3ccSAndroid Build Coastguard Worker }
665*77c1e3ccSAndroid Build Coastguard Worker
666*77c1e3ccSAndroid Build Coastguard Worker // The fitted noise strength should be close to the standard deviation
667*77c1e3ccSAndroid Build Coastguard Worker // for all intensity bins.
668*77c1e3ccSAndroid Build Coastguard Worker const double kStdEps = 0.1;
669*77c1e3ccSAndroid Build Coastguard Worker const double normalize = 1 << shift;
670*77c1e3ccSAndroid Build Coastguard Worker
671*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < model.latest_state[0].strength_solver.eqns.n; ++i) {
672*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1.0,
673*77c1e3ccSAndroid Build Coastguard Worker model.latest_state[0].strength_solver.eqns.x[i] / normalize,
674*77c1e3ccSAndroid Build Coastguard Worker kStdEps);
675*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1.0,
676*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[0].strength_solver.eqns.x[i] / normalize,
677*77c1e3ccSAndroid Build Coastguard Worker kStdEps);
678*77c1e3ccSAndroid Build Coastguard Worker }
679*77c1e3ccSAndroid Build Coastguard Worker
680*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t lut;
681*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_fit_piecewise(
682*77c1e3ccSAndroid Build Coastguard Worker &model.latest_state[0].strength_solver, -1, &lut);
683*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(2, lut.num_points);
684*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0.0, lut.points[0][0], 1e-5);
685*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1.0, lut.points[0][1] / normalize, kStdEps);
686*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR((1 << this->kBitDepth) - 1, lut.points[1][0], 1e-5);
687*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1.0, lut.points[1][1] / normalize, kStdEps);
688*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(&lut);
689*77c1e3ccSAndroid Build Coastguard Worker }
690*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(NoiseModelUpdateTest,UpdateSuccessForScaledWhiteNoise)691*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(NoiseModelUpdateTest, UpdateSuccessForScaledWhiteNoise) {
692*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t &model = this->model_;
693*77c1e3ccSAndroid Build Coastguard Worker const int width = this->kWidth;
694*77c1e3ccSAndroid Build Coastguard Worker const int height = this->kHeight;
695*77c1e3ccSAndroid Build Coastguard Worker
696*77c1e3ccSAndroid Build Coastguard Worker const double kCoeffEps = 0.055;
697*77c1e3ccSAndroid Build Coastguard Worker const double kLowStd = 1;
698*77c1e3ccSAndroid Build Coastguard Worker const double kHighStd = 4;
699*77c1e3ccSAndroid Build Coastguard Worker const int shift = this->kBitDepth - 8;
700*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < height; ++y) {
701*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < width; ++x) {
702*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
703*77c1e3ccSAndroid Build Coastguard Worker // The image data is bimodal:
704*77c1e3ccSAndroid Build Coastguard Worker // Bottom half has low intensity and low noise strength
705*77c1e3ccSAndroid Build Coastguard Worker // Top half has high intensity and high noise strength
706*77c1e3ccSAndroid Build Coastguard Worker const int avg = (y < height / 2) ? 4 : 245;
707*77c1e3ccSAndroid Build Coastguard Worker const double std = (y < height / 2) ? kLowStd : kHighStd;
708*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[c][y * width + x] =
709*77c1e3ccSAndroid Build Coastguard Worker ((uint8_t)std::min((int)255,
710*77c1e3ccSAndroid Build Coastguard Worker (int)(2 + avg + randn(&this->random_, std))))
711*77c1e3ccSAndroid Build Coastguard Worker << shift;
712*77c1e3ccSAndroid Build Coastguard Worker this->denoised_ptr_[c][y * width + x] = (2 + avg) << shift;
713*77c1e3ccSAndroid Build Coastguard Worker }
714*77c1e3ccSAndroid Build Coastguard Worker }
715*77c1e3ccSAndroid Build Coastguard Worker }
716*77c1e3ccSAndroid Build Coastguard Worker // Label all blocks as flat for the update
717*77c1e3ccSAndroid Build Coastguard Worker this->flat_blocks_.assign(this->flat_blocks_.size(), 1);
718*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_OK, this->NoiseModelUpdate());
719*77c1e3ccSAndroid Build Coastguard Worker
720*77c1e3ccSAndroid Build Coastguard Worker const int n = model.n;
721*77c1e3ccSAndroid Build Coastguard Worker // The noise is uncorrelated spatially and with the y channel.
722*77c1e3ccSAndroid Build Coastguard Worker // All coefficients should be reasonably close to zero.
723*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
724*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
725*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, model.latest_state[c].eqns.x[i], kCoeffEps);
726*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, model.combined_state[c].eqns.x[i], kCoeffEps);
727*77c1e3ccSAndroid Build Coastguard Worker }
728*77c1e3ccSAndroid Build Coastguard Worker if (c > 0) {
729*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(n + 1, model.latest_state[c].eqns.n);
730*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(n + 1, model.combined_state[c].eqns.n);
731*77c1e3ccSAndroid Build Coastguard Worker
732*77c1e3ccSAndroid Build Coastguard Worker // The correlation to the y channel should be low (near zero)
733*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, model.latest_state[c].eqns.x[n], kCoeffEps);
734*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, model.combined_state[c].eqns.x[n], kCoeffEps);
735*77c1e3ccSAndroid Build Coastguard Worker }
736*77c1e3ccSAndroid Build Coastguard Worker }
737*77c1e3ccSAndroid Build Coastguard Worker
738*77c1e3ccSAndroid Build Coastguard Worker // Noise strength should vary between kLowStd and kHighStd.
739*77c1e3ccSAndroid Build Coastguard Worker const double kStdEps = 0.15;
740*77c1e3ccSAndroid Build Coastguard Worker // We have to normalize fitted standard deviation based on bit depth.
741*77c1e3ccSAndroid Build Coastguard Worker const double normalize = (1 << shift);
742*77c1e3ccSAndroid Build Coastguard Worker
743*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(20, model.latest_state[0].strength_solver.eqns.n);
744*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < model.latest_state[0].strength_solver.eqns.n; ++i) {
745*77c1e3ccSAndroid Build Coastguard Worker const double a = i / 19.0;
746*77c1e3ccSAndroid Build Coastguard Worker const double expected = (kLowStd * (1.0 - a) + kHighStd * a);
747*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(expected,
748*77c1e3ccSAndroid Build Coastguard Worker model.latest_state[0].strength_solver.eqns.x[i] / normalize,
749*77c1e3ccSAndroid Build Coastguard Worker kStdEps);
750*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(expected,
751*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[0].strength_solver.eqns.x[i] / normalize,
752*77c1e3ccSAndroid Build Coastguard Worker kStdEps);
753*77c1e3ccSAndroid Build Coastguard Worker }
754*77c1e3ccSAndroid Build Coastguard Worker
755*77c1e3ccSAndroid Build Coastguard Worker // If we fit a piecewise linear model, there should be two points:
756*77c1e3ccSAndroid Build Coastguard Worker // one near kLowStd at 0, and the other near kHighStd and 255.
757*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t lut;
758*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_fit_piecewise(
759*77c1e3ccSAndroid Build Coastguard Worker &model.latest_state[0].strength_solver, 2, &lut);
760*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(2, lut.num_points);
761*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, lut.points[0][0], 1e-4);
762*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(kLowStd, lut.points[0][1] / normalize, kStdEps);
763*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR((1 << this->kBitDepth) - 1, lut.points[1][0], 1e-5);
764*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(kHighStd, lut.points[1][1] / normalize, kStdEps);
765*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(&lut);
766*77c1e3ccSAndroid Build Coastguard Worker }
767*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(NoiseModelUpdateTest,UpdateSuccessForCorrelatedNoise)768*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(NoiseModelUpdateTest, UpdateSuccessForCorrelatedNoise) {
769*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t &model = this->model_;
770*77c1e3ccSAndroid Build Coastguard Worker const int width = this->kWidth;
771*77c1e3ccSAndroid Build Coastguard Worker const int height = this->kHeight;
772*77c1e3ccSAndroid Build Coastguard Worker const int kNumCoeffs = 24;
773*77c1e3ccSAndroid Build Coastguard Worker const double kStd = 4;
774*77c1e3ccSAndroid Build Coastguard Worker const double kStdEps = 0.3;
775*77c1e3ccSAndroid Build Coastguard Worker const double kCoeffEps = 0.065;
776*77c1e3ccSAndroid Build Coastguard Worker // Use different coefficients for each channel
777*77c1e3ccSAndroid Build Coastguard Worker const double kCoeffs[3][24] = {
778*77c1e3ccSAndroid Build Coastguard Worker { 0.02884, -0.03356, 0.00633, 0.01757, 0.02849, -0.04620,
779*77c1e3ccSAndroid Build Coastguard Worker 0.02833, -0.07178, 0.07076, -0.11603, -0.10413, -0.16571,
780*77c1e3ccSAndroid Build Coastguard Worker 0.05158, -0.07969, 0.02640, -0.07191, 0.02530, 0.41968,
781*77c1e3ccSAndroid Build Coastguard Worker 0.21450, -0.00702, -0.01401, -0.03676, -0.08713, 0.44196 },
782*77c1e3ccSAndroid Build Coastguard Worker { 0.00269, -0.01291, -0.01513, 0.07234, 0.03208, 0.00477,
783*77c1e3ccSAndroid Build Coastguard Worker 0.00226, -0.00254, 0.03533, 0.12841, -0.25970, -0.06336,
784*77c1e3ccSAndroid Build Coastguard Worker 0.05238, -0.00845, -0.03118, 0.09043, -0.36558, 0.48903,
785*77c1e3ccSAndroid Build Coastguard Worker 0.00595, -0.11938, 0.02106, 0.095956, -0.350139, 0.59305 },
786*77c1e3ccSAndroid Build Coastguard Worker { -0.00643, -0.01080, -0.01466, 0.06951, 0.03707, -0.00482,
787*77c1e3ccSAndroid Build Coastguard Worker 0.00817, -0.00909, 0.02949, 0.12181, -0.25210, -0.07886,
788*77c1e3ccSAndroid Build Coastguard Worker 0.06083, -0.01210, -0.03108, 0.08944, -0.35875, 0.49150,
789*77c1e3ccSAndroid Build Coastguard Worker 0.00415, -0.12905, 0.02870, 0.09740, -0.34610, 0.58824 },
790*77c1e3ccSAndroid Build Coastguard Worker };
791*77c1e3ccSAndroid Build Coastguard Worker
792*77c1e3ccSAndroid Build Coastguard Worker ASSERT_EQ(model.n, kNumCoeffs);
793*77c1e3ccSAndroid Build Coastguard Worker this->chroma_sub_[0] = this->chroma_sub_[1] = 1;
794*77c1e3ccSAndroid Build Coastguard Worker
795*77c1e3ccSAndroid Build Coastguard Worker this->flat_blocks_.assign(this->flat_blocks_.size(), 1);
796*77c1e3ccSAndroid Build Coastguard Worker
797*77c1e3ccSAndroid Build Coastguard Worker // Add different noise onto each plane
798*77c1e3ccSAndroid Build Coastguard Worker const int shift = this->kBitDepth - 8;
799*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
800*77c1e3ccSAndroid Build Coastguard Worker noise_synth(&this->random_, model.params.lag, model.n, model.coords,
801*77c1e3ccSAndroid Build Coastguard Worker kCoeffs[c], this->noise_ptr_[c], width, height);
802*77c1e3ccSAndroid Build Coastguard Worker const int x_shift = c > 0 ? this->chroma_sub_[0] : 0;
803*77c1e3ccSAndroid Build Coastguard Worker const int y_shift = c > 0 ? this->chroma_sub_[1] : 0;
804*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < (height >> y_shift); ++y) {
805*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < (width >> x_shift); ++x) {
806*77c1e3ccSAndroid Build Coastguard Worker const uint8_t value = 64 + x / 2 + y / 4;
807*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[c][y * width + x] =
808*77c1e3ccSAndroid Build Coastguard Worker (uint8_t(value + this->noise_ptr_[c][y * width + x] * kStd))
809*77c1e3ccSAndroid Build Coastguard Worker << shift;
810*77c1e3ccSAndroid Build Coastguard Worker this->denoised_ptr_[c][y * width + x] = value << shift;
811*77c1e3ccSAndroid Build Coastguard Worker }
812*77c1e3ccSAndroid Build Coastguard Worker }
813*77c1e3ccSAndroid Build Coastguard Worker }
814*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_OK, this->NoiseModelUpdate());
815*77c1e3ccSAndroid Build Coastguard Worker
816*77c1e3ccSAndroid Build Coastguard Worker // For the Y plane, the solved coefficients should be close to the original
817*77c1e3ccSAndroid Build Coastguard Worker const int n = model.n;
818*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
819*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
820*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(kCoeffs[c][i], model.latest_state[c].eqns.x[i], kCoeffEps);
821*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(kCoeffs[c][i], model.combined_state[c].eqns.x[i], kCoeffEps);
822*77c1e3ccSAndroid Build Coastguard Worker }
823*77c1e3ccSAndroid Build Coastguard Worker // The chroma planes should be uncorrelated with the luma plane
824*77c1e3ccSAndroid Build Coastguard Worker if (c > 0) {
825*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, model.latest_state[c].eqns.x[n], kCoeffEps);
826*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(0, model.combined_state[c].eqns.x[n], kCoeffEps);
827*77c1e3ccSAndroid Build Coastguard Worker }
828*77c1e3ccSAndroid Build Coastguard Worker // Correlation between the coefficient vector and the fitted coefficients
829*77c1e3ccSAndroid Build Coastguard Worker // should be close to 1.
830*77c1e3ccSAndroid Build Coastguard Worker EXPECT_LT(0.98, aom_normalized_cross_correlation(
831*77c1e3ccSAndroid Build Coastguard Worker model.latest_state[c].eqns.x, kCoeffs[c], kNumCoeffs));
832*77c1e3ccSAndroid Build Coastguard Worker
833*77c1e3ccSAndroid Build Coastguard Worker noise_synth(&this->random_, model.params.lag, model.n, model.coords,
834*77c1e3ccSAndroid Build Coastguard Worker model.latest_state[c].eqns.x, &this->renoise_[0], width,
835*77c1e3ccSAndroid Build Coastguard Worker height);
836*77c1e3ccSAndroid Build Coastguard Worker
837*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_data_validate(&this->renoise_[0], width, height));
838*77c1e3ccSAndroid Build Coastguard Worker }
839*77c1e3ccSAndroid Build Coastguard Worker
840*77c1e3ccSAndroid Build Coastguard Worker // Check fitted noise strength
841*77c1e3ccSAndroid Build Coastguard Worker const double normalize = 1 << shift;
842*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
843*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < model.latest_state[c].strength_solver.eqns.n; ++i) {
844*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(kStd,
845*77c1e3ccSAndroid Build Coastguard Worker model.latest_state[c].strength_solver.eqns.x[i] / normalize,
846*77c1e3ccSAndroid Build Coastguard Worker kStdEps);
847*77c1e3ccSAndroid Build Coastguard Worker }
848*77c1e3ccSAndroid Build Coastguard Worker }
849*77c1e3ccSAndroid Build Coastguard Worker }
850*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(NoiseModelUpdateTest,NoiseStrengthChangeSignalsDifferentNoiseType)851*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(NoiseModelUpdateTest,
852*77c1e3ccSAndroid Build Coastguard Worker NoiseStrengthChangeSignalsDifferentNoiseType) {
853*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t &model = this->model_;
854*77c1e3ccSAndroid Build Coastguard Worker const int width = this->kWidth;
855*77c1e3ccSAndroid Build Coastguard Worker const int height = this->kHeight;
856*77c1e3ccSAndroid Build Coastguard Worker const int block_size = this->kBlockSize;
857*77c1e3ccSAndroid Build Coastguard Worker // Create a gradient image with std = 2 uncorrelated noise
858*77c1e3ccSAndroid Build Coastguard Worker const double kStd = 2;
859*77c1e3ccSAndroid Build Coastguard Worker const int shift = this->kBitDepth - 8;
860*77c1e3ccSAndroid Build Coastguard Worker
861*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < width * height; ++i) {
862*77c1e3ccSAndroid Build Coastguard Worker const uint8_t val = (i % width) < width / 2 ? 64 : 192;
863*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
864*77c1e3ccSAndroid Build Coastguard Worker this->noise_ptr_[c][i] = randn(&this->random_, 1);
865*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[c][i] = ((uint8_t)(this->noise_ptr_[c][i] * kStd + val))
866*77c1e3ccSAndroid Build Coastguard Worker << shift;
867*77c1e3ccSAndroid Build Coastguard Worker this->denoised_ptr_[c][i] = val << shift;
868*77c1e3ccSAndroid Build Coastguard Worker }
869*77c1e3ccSAndroid Build Coastguard Worker }
870*77c1e3ccSAndroid Build Coastguard Worker this->flat_blocks_.assign(this->flat_blocks_.size(), 1);
871*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_OK, this->NoiseModelUpdate());
872*77c1e3ccSAndroid Build Coastguard Worker
873*77c1e3ccSAndroid Build Coastguard Worker const int kNumBlocks = width * height / block_size / block_size;
874*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.latest_state[0].strength_solver.num_equations);
875*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.latest_state[1].strength_solver.num_equations);
876*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.latest_state[2].strength_solver.num_equations);
877*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.combined_state[0].strength_solver.num_equations);
878*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.combined_state[1].strength_solver.num_equations);
879*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.combined_state[2].strength_solver.num_equations);
880*77c1e3ccSAndroid Build Coastguard Worker
881*77c1e3ccSAndroid Build Coastguard Worker // Bump up noise by an insignificant amount
882*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < width * height; ++i) {
883*77c1e3ccSAndroid Build Coastguard Worker const uint8_t val = (i % width) < width / 2 ? 64 : 192;
884*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[0][i] =
885*77c1e3ccSAndroid Build Coastguard Worker ((uint8_t)(this->noise_ptr_[0][i] * (kStd + 0.085) + val)) << shift;
886*77c1e3ccSAndroid Build Coastguard Worker }
887*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_OK, this->NoiseModelUpdate());
888*77c1e3ccSAndroid Build Coastguard Worker
889*77c1e3ccSAndroid Build Coastguard Worker const double kARGainTolerance = 0.02;
890*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
891*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.latest_state[c].strength_solver.num_equations);
892*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(15250, model.latest_state[c].num_observations);
893*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1, model.latest_state[c].ar_gain, kARGainTolerance);
894*77c1e3ccSAndroid Build Coastguard Worker
895*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(2 * kNumBlocks,
896*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[c].strength_solver.num_equations);
897*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(2 * 15250, model.combined_state[c].num_observations);
898*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1, model.combined_state[c].ar_gain, kARGainTolerance);
899*77c1e3ccSAndroid Build Coastguard Worker }
900*77c1e3ccSAndroid Build Coastguard Worker
901*77c1e3ccSAndroid Build Coastguard Worker // Bump up the noise strength on half the image for one channel by a
902*77c1e3ccSAndroid Build Coastguard Worker // significant amount.
903*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < width * height; ++i) {
904*77c1e3ccSAndroid Build Coastguard Worker const uint8_t val = (i % width) < width / 2 ? 64 : 128;
905*77c1e3ccSAndroid Build Coastguard Worker if (i % width < width / 2) {
906*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[0][i] =
907*77c1e3ccSAndroid Build Coastguard Worker ((uint8_t)(randn(&this->random_, kStd + 0.5) + val)) << shift;
908*77c1e3ccSAndroid Build Coastguard Worker }
909*77c1e3ccSAndroid Build Coastguard Worker }
910*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_DIFFERENT_NOISE_TYPE, this->NoiseModelUpdate());
911*77c1e3ccSAndroid Build Coastguard Worker
912*77c1e3ccSAndroid Build Coastguard Worker // Since we didn't update the combined state, it should still be at 2 *
913*77c1e3ccSAndroid Build Coastguard Worker // num_blocks
914*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.latest_state[0].strength_solver.num_equations);
915*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(2 * kNumBlocks,
916*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[0].strength_solver.num_equations);
917*77c1e3ccSAndroid Build Coastguard Worker
918*77c1e3ccSAndroid Build Coastguard Worker // In normal operation, the "latest" estimate can be saved to the "combined"
919*77c1e3ccSAndroid Build Coastguard Worker // state for continued updates.
920*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_save_latest(&model);
921*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
922*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks, model.latest_state[c].strength_solver.num_equations);
923*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(15250, model.latest_state[c].num_observations);
924*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1, model.latest_state[c].ar_gain, kARGainTolerance);
925*77c1e3ccSAndroid Build Coastguard Worker
926*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumBlocks,
927*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[c].strength_solver.num_equations);
928*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(15250, model.combined_state[c].num_observations);
929*77c1e3ccSAndroid Build Coastguard Worker EXPECT_NEAR(1, model.combined_state[c].ar_gain, kARGainTolerance);
930*77c1e3ccSAndroid Build Coastguard Worker }
931*77c1e3ccSAndroid Build Coastguard Worker }
932*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(NoiseModelUpdateTest,NoiseCoeffsSignalsDifferentNoiseType)933*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(NoiseModelUpdateTest, NoiseCoeffsSignalsDifferentNoiseType) {
934*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t &model = this->model_;
935*77c1e3ccSAndroid Build Coastguard Worker const int width = this->kWidth;
936*77c1e3ccSAndroid Build Coastguard Worker const int height = this->kHeight;
937*77c1e3ccSAndroid Build Coastguard Worker const double kCoeffs[2][24] = {
938*77c1e3ccSAndroid Build Coastguard Worker { 0.02884, -0.03356, 0.00633, 0.01757, 0.02849, -0.04620,
939*77c1e3ccSAndroid Build Coastguard Worker 0.02833, -0.07178, 0.07076, -0.11603, -0.10413, -0.16571,
940*77c1e3ccSAndroid Build Coastguard Worker 0.05158, -0.07969, 0.02640, -0.07191, 0.02530, 0.41968,
941*77c1e3ccSAndroid Build Coastguard Worker 0.21450, -0.00702, -0.01401, -0.03676, -0.08713, 0.44196 },
942*77c1e3ccSAndroid Build Coastguard Worker { 0.00269, -0.01291, -0.01513, 0.07234, 0.03208, 0.00477,
943*77c1e3ccSAndroid Build Coastguard Worker 0.00226, -0.00254, 0.03533, 0.12841, -0.25970, -0.06336,
944*77c1e3ccSAndroid Build Coastguard Worker 0.05238, -0.00845, -0.03118, 0.09043, -0.36558, 0.48903,
945*77c1e3ccSAndroid Build Coastguard Worker 0.00595, -0.11938, 0.02106, 0.095956, -0.350139, 0.59305 }
946*77c1e3ccSAndroid Build Coastguard Worker };
947*77c1e3ccSAndroid Build Coastguard Worker
948*77c1e3ccSAndroid Build Coastguard Worker noise_synth(&this->random_, model.params.lag, model.n, model.coords,
949*77c1e3ccSAndroid Build Coastguard Worker kCoeffs[0], this->noise_ptr_[0], width, height);
950*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < width * height; ++i) {
951*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[0][i] = (uint8_t)(128 + this->noise_ptr_[0][i]);
952*77c1e3ccSAndroid Build Coastguard Worker }
953*77c1e3ccSAndroid Build Coastguard Worker this->flat_blocks_.assign(this->flat_blocks_.size(), 1);
954*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_OK, this->NoiseModelUpdate());
955*77c1e3ccSAndroid Build Coastguard Worker
956*77c1e3ccSAndroid Build Coastguard Worker // Now try with the second set of AR coefficients
957*77c1e3ccSAndroid Build Coastguard Worker noise_synth(&this->random_, model.params.lag, model.n, model.coords,
958*77c1e3ccSAndroid Build Coastguard Worker kCoeffs[1], this->noise_ptr_[0], width, height);
959*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < width * height; ++i) {
960*77c1e3ccSAndroid Build Coastguard Worker this->data_ptr_[0][i] = (uint8_t)(128 + this->noise_ptr_[0][i]);
961*77c1e3ccSAndroid Build Coastguard Worker }
962*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(AOM_NOISE_STATUS_DIFFERENT_NOISE_TYPE, this->NoiseModelUpdate());
963*77c1e3ccSAndroid Build Coastguard Worker }
964*77c1e3ccSAndroid Build Coastguard Worker REGISTER_TYPED_TEST_SUITE_P(NoiseModelUpdateTest, UpdateFailsNoFlatBlocks,
965*77c1e3ccSAndroid Build Coastguard Worker UpdateSuccessForZeroNoiseAllFlat,
966*77c1e3ccSAndroid Build Coastguard Worker UpdateFailsBlockSizeTooSmall,
967*77c1e3ccSAndroid Build Coastguard Worker UpdateSuccessForWhiteRandomNoise,
968*77c1e3ccSAndroid Build Coastguard Worker UpdateSuccessForScaledWhiteNoise,
969*77c1e3ccSAndroid Build Coastguard Worker UpdateSuccessForCorrelatedNoise,
970*77c1e3ccSAndroid Build Coastguard Worker NoiseStrengthChangeSignalsDifferentNoiseType,
971*77c1e3ccSAndroid Build Coastguard Worker NoiseCoeffsSignalsDifferentNoiseType);
972*77c1e3ccSAndroid Build Coastguard Worker
973*77c1e3ccSAndroid Build Coastguard Worker // Note the empty final argument can be removed if C++20 is made the minimum
974*77c1e3ccSAndroid Build Coastguard Worker // requirement.
975*77c1e3ccSAndroid Build Coastguard Worker INSTANTIATE_TYPED_TEST_SUITE_P(NoiseModelUpdateTestInstatiation,
976*77c1e3ccSAndroid Build Coastguard Worker NoiseModelUpdateTest, AllBitDepthParams, );
977*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModelGetGrainParameters,TestLagSize)978*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModelGetGrainParameters, TestLagSize) {
979*77c1e3ccSAndroid Build Coastguard Worker aom_film_grain_t film_grain;
980*77c1e3ccSAndroid Build Coastguard Worker for (int lag = 1; lag <= 3; ++lag) {
981*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, lag, 8, 0 };
982*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
983*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_init(&model, params));
984*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_get_grain_parameters(&model, &film_grain));
985*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(lag, film_grain.ar_coeff_lag);
986*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
987*77c1e3ccSAndroid Build Coastguard Worker }
988*77c1e3ccSAndroid Build Coastguard Worker
989*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 4, 8, 0 };
990*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
991*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_init(&model, params));
992*77c1e3ccSAndroid Build Coastguard Worker EXPECT_FALSE(aom_noise_model_get_grain_parameters(&model, &film_grain));
993*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
994*77c1e3ccSAndroid Build Coastguard Worker }
995*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModelGetGrainParameters,TestARCoeffShiftBounds)996*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModelGetGrainParameters, TestARCoeffShiftBounds) {
997*77c1e3ccSAndroid Build Coastguard Worker struct TestCase {
998*77c1e3ccSAndroid Build Coastguard Worker double max_input_value;
999*77c1e3ccSAndroid Build Coastguard Worker int expected_ar_coeff_shift;
1000*77c1e3ccSAndroid Build Coastguard Worker int expected_value;
1001*77c1e3ccSAndroid Build Coastguard Worker };
1002*77c1e3ccSAndroid Build Coastguard Worker const int lag = 1;
1003*77c1e3ccSAndroid Build Coastguard Worker const int kNumTestCases = 19;
1004*77c1e3ccSAndroid Build Coastguard Worker const TestCase test_cases[] = {
1005*77c1e3ccSAndroid Build Coastguard Worker // Test cases for ar_coeff_shift = 9
1006*77c1e3ccSAndroid Build Coastguard Worker { 0, 9, 0 },
1007*77c1e3ccSAndroid Build Coastguard Worker { 0.125, 9, 64 },
1008*77c1e3ccSAndroid Build Coastguard Worker { -0.125, 9, -64 },
1009*77c1e3ccSAndroid Build Coastguard Worker { 0.2499, 9, 127 },
1010*77c1e3ccSAndroid Build Coastguard Worker { -0.25, 9, -128 },
1011*77c1e3ccSAndroid Build Coastguard Worker // Test cases for ar_coeff_shift = 8
1012*77c1e3ccSAndroid Build Coastguard Worker { 0.25, 8, 64 },
1013*77c1e3ccSAndroid Build Coastguard Worker { -0.2501, 8, -64 },
1014*77c1e3ccSAndroid Build Coastguard Worker { 0.499, 8, 127 },
1015*77c1e3ccSAndroid Build Coastguard Worker { -0.5, 8, -128 },
1016*77c1e3ccSAndroid Build Coastguard Worker // Test cases for ar_coeff_shift = 7
1017*77c1e3ccSAndroid Build Coastguard Worker { 0.5, 7, 64 },
1018*77c1e3ccSAndroid Build Coastguard Worker { -0.5001, 7, -64 },
1019*77c1e3ccSAndroid Build Coastguard Worker { 0.999, 7, 127 },
1020*77c1e3ccSAndroid Build Coastguard Worker { -1, 7, -128 },
1021*77c1e3ccSAndroid Build Coastguard Worker // Test cases for ar_coeff_shift = 6
1022*77c1e3ccSAndroid Build Coastguard Worker { 1.0, 6, 64 },
1023*77c1e3ccSAndroid Build Coastguard Worker { -1.0001, 6, -64 },
1024*77c1e3ccSAndroid Build Coastguard Worker { 2.0, 6, 127 },
1025*77c1e3ccSAndroid Build Coastguard Worker { -2.0, 6, -128 },
1026*77c1e3ccSAndroid Build Coastguard Worker { 4, 6, 127 },
1027*77c1e3ccSAndroid Build Coastguard Worker { -4, 6, -128 },
1028*77c1e3ccSAndroid Build Coastguard Worker };
1029*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, lag, 8, 0 };
1030*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
1031*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_init(&model, params));
1032*77c1e3ccSAndroid Build Coastguard Worker
1033*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kNumTestCases; ++i) {
1034*77c1e3ccSAndroid Build Coastguard Worker const TestCase &test_case = test_cases[i];
1035*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[0].eqns.x[0] = test_case.max_input_value;
1036*77c1e3ccSAndroid Build Coastguard Worker
1037*77c1e3ccSAndroid Build Coastguard Worker aom_film_grain_t film_grain;
1038*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_get_grain_parameters(&model, &film_grain));
1039*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, film_grain.ar_coeff_lag);
1040*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(test_case.expected_ar_coeff_shift, film_grain.ar_coeff_shift);
1041*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(test_case.expected_value, film_grain.ar_coeffs_y[0]);
1042*77c1e3ccSAndroid Build Coastguard Worker }
1043*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
1044*77c1e3ccSAndroid Build Coastguard Worker }
1045*77c1e3ccSAndroid Build Coastguard Worker
TEST(NoiseModelGetGrainParameters,TestNoiseStrengthShiftBounds)1046*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModelGetGrainParameters, TestNoiseStrengthShiftBounds) {
1047*77c1e3ccSAndroid Build Coastguard Worker struct TestCase {
1048*77c1e3ccSAndroid Build Coastguard Worker double max_input_value;
1049*77c1e3ccSAndroid Build Coastguard Worker int expected_scaling_shift;
1050*77c1e3ccSAndroid Build Coastguard Worker int expected_value;
1051*77c1e3ccSAndroid Build Coastguard Worker };
1052*77c1e3ccSAndroid Build Coastguard Worker const int kNumTestCases = 10;
1053*77c1e3ccSAndroid Build Coastguard Worker const TestCase test_cases[] = {
1054*77c1e3ccSAndroid Build Coastguard Worker { 0, 11, 0 }, { 1, 11, 64 }, { 2, 11, 128 }, { 3.99, 11, 255 },
1055*77c1e3ccSAndroid Build Coastguard Worker { 4, 10, 128 }, { 7.99, 10, 255 }, { 8, 9, 128 }, { 16, 8, 128 },
1056*77c1e3ccSAndroid Build Coastguard Worker { 31.99, 8, 255 }, { 64, 8, 255 }, // clipped
1057*77c1e3ccSAndroid Build Coastguard Worker };
1058*77c1e3ccSAndroid Build Coastguard Worker const int lag = 1;
1059*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, lag, 8, 0 };
1060*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
1061*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_init(&model, params));
1062*77c1e3ccSAndroid Build Coastguard Worker
1063*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kNumTestCases; ++i) {
1064*77c1e3ccSAndroid Build Coastguard Worker const TestCase &test_case = test_cases[i];
1065*77c1e3ccSAndroid Build Coastguard Worker aom_equation_system_t &eqns = model.combined_state[0].strength_solver.eqns;
1066*77c1e3ccSAndroid Build Coastguard Worker // Set the fitted scale parameters to be a constant value.
1067*77c1e3ccSAndroid Build Coastguard Worker for (int j = 0; j < eqns.n; ++j) {
1068*77c1e3ccSAndroid Build Coastguard Worker eqns.x[j] = test_case.max_input_value;
1069*77c1e3ccSAndroid Build Coastguard Worker }
1070*77c1e3ccSAndroid Build Coastguard Worker aom_film_grain_t film_grain;
1071*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_get_grain_parameters(&model, &film_grain));
1072*77c1e3ccSAndroid Build Coastguard Worker // We expect a single constant segemnt
1073*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(test_case.expected_scaling_shift, film_grain.scaling_shift);
1074*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(test_case.expected_value, film_grain.scaling_points_y[0][1]);
1075*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(test_case.expected_value, film_grain.scaling_points_y[1][1]);
1076*77c1e3ccSAndroid Build Coastguard Worker }
1077*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
1078*77c1e3ccSAndroid Build Coastguard Worker }
1079*77c1e3ccSAndroid Build Coastguard Worker
1080*77c1e3ccSAndroid Build Coastguard Worker // The AR coefficients are the same inputs used to generate "Test 2" in the test
1081*77c1e3ccSAndroid Build Coastguard Worker // vectors
TEST(NoiseModelGetGrainParameters,GetGrainParametersReal)1082*77c1e3ccSAndroid Build Coastguard Worker TEST(NoiseModelGetGrainParameters, GetGrainParametersReal) {
1083*77c1e3ccSAndroid Build Coastguard Worker const double kInputCoeffsY[] = { 0.0315, 0.0073, 0.0218, 0.00235, 0.00511,
1084*77c1e3ccSAndroid Build Coastguard Worker -0.0222, 0.0627, -0.022, 0.05575, -0.1816,
1085*77c1e3ccSAndroid Build Coastguard Worker 0.0107, -0.1966, 0.00065, -0.0809, 0.04934,
1086*77c1e3ccSAndroid Build Coastguard Worker -0.1349, -0.0352, 0.41772, 0.27973, 0.04207,
1087*77c1e3ccSAndroid Build Coastguard Worker -0.0429, -0.1372, 0.06193, 0.52032 };
1088*77c1e3ccSAndroid Build Coastguard Worker const double kInputCoeffsCB[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1089*77c1e3ccSAndroid Build Coastguard Worker 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5 };
1090*77c1e3ccSAndroid Build Coastguard Worker const double kInputCoeffsCR[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1091*77c1e3ccSAndroid Build Coastguard Worker 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.5 };
1092*77c1e3ccSAndroid Build Coastguard Worker const int kExpectedARCoeffsY[] = { 4, 1, 3, 0, 1, -3, 8, -3,
1093*77c1e3ccSAndroid Build Coastguard Worker 7, -23, 1, -25, 0, -10, 6, -17,
1094*77c1e3ccSAndroid Build Coastguard Worker -5, 53, 36, 5, -5, -18, 8, 67 };
1095*77c1e3ccSAndroid Build Coastguard Worker const int kExpectedARCoeffsCB[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1096*77c1e3ccSAndroid Build Coastguard Worker 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84 };
1097*77c1e3ccSAndroid Build Coastguard Worker const int kExpectedARCoeffsCR[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1098*77c1e3ccSAndroid Build Coastguard Worker 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -126 };
1099*77c1e3ccSAndroid Build Coastguard Worker // Scaling function is initialized analytically with a sqrt function.
1100*77c1e3ccSAndroid Build Coastguard Worker const int kNumScalingPointsY = 12;
1101*77c1e3ccSAndroid Build Coastguard Worker const int kExpectedScalingPointsY[][2] = {
1102*77c1e3ccSAndroid Build Coastguard Worker { 0, 0 }, { 13, 44 }, { 27, 62 }, { 40, 76 },
1103*77c1e3ccSAndroid Build Coastguard Worker { 54, 88 }, { 67, 98 }, { 94, 117 }, { 121, 132 },
1104*77c1e3ccSAndroid Build Coastguard Worker { 148, 146 }, { 174, 159 }, { 201, 171 }, { 255, 192 },
1105*77c1e3ccSAndroid Build Coastguard Worker };
1106*77c1e3ccSAndroid Build Coastguard Worker
1107*77c1e3ccSAndroid Build Coastguard Worker const int lag = 3;
1108*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, lag, 8, 0 };
1109*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t model;
1110*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_init(&model, params));
1111*77c1e3ccSAndroid Build Coastguard Worker
1112*77c1e3ccSAndroid Build Coastguard Worker // Setup the AR coeffs
1113*77c1e3ccSAndroid Build Coastguard Worker memcpy(model.combined_state[0].eqns.x, kInputCoeffsY, sizeof(kInputCoeffsY));
1114*77c1e3ccSAndroid Build Coastguard Worker memcpy(model.combined_state[1].eqns.x, kInputCoeffsCB,
1115*77c1e3ccSAndroid Build Coastguard Worker sizeof(kInputCoeffsCB));
1116*77c1e3ccSAndroid Build Coastguard Worker memcpy(model.combined_state[2].eqns.x, kInputCoeffsCR,
1117*77c1e3ccSAndroid Build Coastguard Worker sizeof(kInputCoeffsCR));
1118*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < model.combined_state[0].strength_solver.num_bins; ++i) {
1119*77c1e3ccSAndroid Build Coastguard Worker const double x =
1120*77c1e3ccSAndroid Build Coastguard Worker ((double)i) / (model.combined_state[0].strength_solver.num_bins - 1.0);
1121*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[0].strength_solver.eqns.x[i] = 6 * sqrt(x);
1122*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[1].strength_solver.eqns.x[i] = 3;
1123*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[2].strength_solver.eqns.x[i] = 2;
1124*77c1e3ccSAndroid Build Coastguard Worker
1125*77c1e3ccSAndroid Build Coastguard Worker // Inject some observations into the strength solver, as during film grain
1126*77c1e3ccSAndroid Build Coastguard Worker // parameter extraction an estimate of the average strength will be used to
1127*77c1e3ccSAndroid Build Coastguard Worker // adjust correlation.
1128*77c1e3ccSAndroid Build Coastguard Worker const int n = model.combined_state[0].strength_solver.num_bins;
1129*77c1e3ccSAndroid Build Coastguard Worker for (int j = 0; j < model.combined_state[0].strength_solver.num_bins; ++j) {
1130*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[0].strength_solver.eqns.A[i * n + j] = 1;
1131*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[1].strength_solver.eqns.A[i * n + j] = 1;
1132*77c1e3ccSAndroid Build Coastguard Worker model.combined_state[2].strength_solver.eqns.A[i * n + j] = 1;
1133*77c1e3ccSAndroid Build Coastguard Worker }
1134*77c1e3ccSAndroid Build Coastguard Worker }
1135*77c1e3ccSAndroid Build Coastguard Worker
1136*77c1e3ccSAndroid Build Coastguard Worker aom_film_grain_t film_grain;
1137*77c1e3ccSAndroid Build Coastguard Worker EXPECT_TRUE(aom_noise_model_get_grain_parameters(&model, &film_grain));
1138*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(lag, film_grain.ar_coeff_lag);
1139*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(3, film_grain.ar_coeff_lag);
1140*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(7, film_grain.ar_coeff_shift);
1141*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(10, film_grain.scaling_shift);
1142*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kNumScalingPointsY, film_grain.num_y_points);
1143*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, film_grain.update_parameters);
1144*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, film_grain.apply_grain);
1145*77c1e3ccSAndroid Build Coastguard Worker
1146*77c1e3ccSAndroid Build Coastguard Worker const int kNumARCoeffs = 24;
1147*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kNumARCoeffs; ++i) {
1148*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kExpectedARCoeffsY[i], film_grain.ar_coeffs_y[i]);
1149*77c1e3ccSAndroid Build Coastguard Worker }
1150*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kNumARCoeffs + 1; ++i) {
1151*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kExpectedARCoeffsCB[i], film_grain.ar_coeffs_cb[i]);
1152*77c1e3ccSAndroid Build Coastguard Worker }
1153*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kNumARCoeffs + 1; ++i) {
1154*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kExpectedARCoeffsCR[i], film_grain.ar_coeffs_cr[i]);
1155*77c1e3ccSAndroid Build Coastguard Worker }
1156*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kNumScalingPointsY; ++i) {
1157*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kExpectedScalingPointsY[i][0], film_grain.scaling_points_y[i][0]);
1158*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(kExpectedScalingPointsY[i][1], film_grain.scaling_points_y[i][1]);
1159*77c1e3ccSAndroid Build Coastguard Worker }
1160*77c1e3ccSAndroid Build Coastguard Worker
1161*77c1e3ccSAndroid Build Coastguard Worker // CB strength should just be a piecewise segment
1162*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(2, film_grain.num_cb_points);
1163*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, film_grain.scaling_points_cb[0][0]);
1164*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(255, film_grain.scaling_points_cb[1][0]);
1165*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(96, film_grain.scaling_points_cb[0][1]);
1166*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(96, film_grain.scaling_points_cb[1][1]);
1167*77c1e3ccSAndroid Build Coastguard Worker
1168*77c1e3ccSAndroid Build Coastguard Worker // CR strength should just be a piecewise segment
1169*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(2, film_grain.num_cr_points);
1170*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, film_grain.scaling_points_cr[0][0]);
1171*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(255, film_grain.scaling_points_cr[1][0]);
1172*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(64, film_grain.scaling_points_cr[0][1]);
1173*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(64, film_grain.scaling_points_cr[1][1]);
1174*77c1e3ccSAndroid Build Coastguard Worker
1175*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(128, film_grain.cb_mult);
1176*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(192, film_grain.cb_luma_mult);
1177*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(256, film_grain.cb_offset);
1178*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(128, film_grain.cr_mult);
1179*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(192, film_grain.cr_luma_mult);
1180*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(256, film_grain.cr_offset);
1181*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, film_grain.chroma_scaling_from_luma);
1182*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, film_grain.grain_scale_shift);
1183*77c1e3ccSAndroid Build Coastguard Worker
1184*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&model);
1185*77c1e3ccSAndroid Build Coastguard Worker }
1186*77c1e3ccSAndroid Build Coastguard Worker
1187*77c1e3ccSAndroid Build Coastguard Worker template <typename T>
1188*77c1e3ccSAndroid Build Coastguard Worker class WienerDenoiseTest : public ::testing::Test, public T {
1189*77c1e3ccSAndroid Build Coastguard Worker public:
SetUpTestSuite()1190*77c1e3ccSAndroid Build Coastguard Worker static void SetUpTestSuite() { aom_dsp_rtcd(); }
1191*77c1e3ccSAndroid Build Coastguard Worker
1192*77c1e3ccSAndroid Build Coastguard Worker protected:
SetUp()1193*77c1e3ccSAndroid Build Coastguard Worker void SetUp() override {
1194*77c1e3ccSAndroid Build Coastguard Worker static const float kNoiseLevel = 5.f;
1195*77c1e3ccSAndroid Build Coastguard Worker static const float kStd = 4.0;
1196*77c1e3ccSAndroid Build Coastguard Worker static const double kMaxValue = (1 << T::kBitDepth) - 1;
1197*77c1e3ccSAndroid Build Coastguard Worker
1198*77c1e3ccSAndroid Build Coastguard Worker chroma_sub_[0] = 1;
1199*77c1e3ccSAndroid Build Coastguard Worker chroma_sub_[1] = 1;
1200*77c1e3ccSAndroid Build Coastguard Worker stride_[0] = kWidth;
1201*77c1e3ccSAndroid Build Coastguard Worker stride_[1] = kWidth / 2;
1202*77c1e3ccSAndroid Build Coastguard Worker stride_[2] = kWidth / 2;
1203*77c1e3ccSAndroid Build Coastguard Worker for (int k = 0; k < 3; ++k) {
1204*77c1e3ccSAndroid Build Coastguard Worker data_[k].resize(kWidth * kHeight);
1205*77c1e3ccSAndroid Build Coastguard Worker denoised_[k].resize(kWidth * kHeight);
1206*77c1e3ccSAndroid Build Coastguard Worker noise_psd_[k].resize(kBlockSize * kBlockSize);
1207*77c1e3ccSAndroid Build Coastguard Worker }
1208*77c1e3ccSAndroid Build Coastguard Worker
1209*77c1e3ccSAndroid Build Coastguard Worker const double kCoeffsY[] = { 0.0406, -0.116, -0.078, -0.152, 0.0033, -0.093,
1210*77c1e3ccSAndroid Build Coastguard Worker 0.048, 0.404, 0.2353, -0.035, -0.093, 0.441 };
1211*77c1e3ccSAndroid Build Coastguard Worker const int kCoords[12][2] = {
1212*77c1e3ccSAndroid Build Coastguard Worker { -2, -2 }, { -1, -2 }, { 0, -2 }, { 1, -2 }, { 2, -2 }, { -2, -1 },
1213*77c1e3ccSAndroid Build Coastguard Worker { -1, -1 }, { 0, -1 }, { 1, -1 }, { 2, -1 }, { -2, 0 }, { -1, 0 }
1214*77c1e3ccSAndroid Build Coastguard Worker };
1215*77c1e3ccSAndroid Build Coastguard Worker const int kLag = 2;
1216*77c1e3ccSAndroid Build Coastguard Worker const int kLength = 12;
1217*77c1e3ccSAndroid Build Coastguard Worker libaom_test::ACMRandom random;
1218*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> noise(kWidth * kHeight);
1219*77c1e3ccSAndroid Build Coastguard Worker noise_synth(&random, kLag, kLength, kCoords, kCoeffsY, &noise[0], kWidth,
1220*77c1e3ccSAndroid Build Coastguard Worker kHeight);
1221*77c1e3ccSAndroid Build Coastguard Worker noise_psd_[0] = get_noise_psd(&noise[0], kWidth, kHeight, kBlockSize);
1222*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kBlockSize * kBlockSize; ++i) {
1223*77c1e3ccSAndroid Build Coastguard Worker noise_psd_[0][i] = (float)(noise_psd_[0][i] * kStd * kStd * kScaleNoise *
1224*77c1e3ccSAndroid Build Coastguard Worker kScaleNoise / (kMaxValue * kMaxValue));
1225*77c1e3ccSAndroid Build Coastguard Worker }
1226*77c1e3ccSAndroid Build Coastguard Worker
1227*77c1e3ccSAndroid Build Coastguard Worker float psd_value =
1228*77c1e3ccSAndroid Build Coastguard Worker aom_noise_psd_get_default_value(kBlockSizeChroma, kNoiseLevel);
1229*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < kBlockSizeChroma * kBlockSizeChroma; ++i) {
1230*77c1e3ccSAndroid Build Coastguard Worker noise_psd_[1][i] = psd_value;
1231*77c1e3ccSAndroid Build Coastguard Worker noise_psd_[2][i] = psd_value;
1232*77c1e3ccSAndroid Build Coastguard Worker }
1233*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < kHeight; ++y) {
1234*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < kWidth; ++x) {
1235*77c1e3ccSAndroid Build Coastguard Worker data_[0][y * stride_[0] + x] = (typename T::data_type_t)fclamp(
1236*77c1e3ccSAndroid Build Coastguard Worker (x + noise[y * stride_[0] + x] * kStd) * kScaleNoise, 0, kMaxValue);
1237*77c1e3ccSAndroid Build Coastguard Worker }
1238*77c1e3ccSAndroid Build Coastguard Worker }
1239*77c1e3ccSAndroid Build Coastguard Worker
1240*77c1e3ccSAndroid Build Coastguard Worker for (int c = 1; c < 3; ++c) {
1241*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < (kHeight >> 1); ++y) {
1242*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < (kWidth >> 1); ++x) {
1243*77c1e3ccSAndroid Build Coastguard Worker data_[c][y * stride_[c] + x] = (typename T::data_type_t)fclamp(
1244*77c1e3ccSAndroid Build Coastguard Worker (x + randn(&random, kStd)) * kScaleNoise, 0, kMaxValue);
1245*77c1e3ccSAndroid Build Coastguard Worker }
1246*77c1e3ccSAndroid Build Coastguard Worker }
1247*77c1e3ccSAndroid Build Coastguard Worker }
1248*77c1e3ccSAndroid Build Coastguard Worker for (int k = 0; k < 3; ++k) {
1249*77c1e3ccSAndroid Build Coastguard Worker noise_psd_ptrs_[k] = &noise_psd_[k][0];
1250*77c1e3ccSAndroid Build Coastguard Worker }
1251*77c1e3ccSAndroid Build Coastguard Worker }
1252*77c1e3ccSAndroid Build Coastguard Worker static const int kBlockSize = 32;
1253*77c1e3ccSAndroid Build Coastguard Worker static const int kBlockSizeChroma = 16;
1254*77c1e3ccSAndroid Build Coastguard Worker static const int kWidth = 256;
1255*77c1e3ccSAndroid Build Coastguard Worker static const int kHeight = 256;
1256*77c1e3ccSAndroid Build Coastguard Worker static const int kScaleNoise = 1 << (T::kBitDepth - 8);
1257*77c1e3ccSAndroid Build Coastguard Worker
1258*77c1e3ccSAndroid Build Coastguard Worker std::vector<typename T::data_type_t> data_[3];
1259*77c1e3ccSAndroid Build Coastguard Worker std::vector<typename T::data_type_t> denoised_[3];
1260*77c1e3ccSAndroid Build Coastguard Worker std::vector<float> noise_psd_[3];
1261*77c1e3ccSAndroid Build Coastguard Worker int chroma_sub_[2];
1262*77c1e3ccSAndroid Build Coastguard Worker float *noise_psd_ptrs_[3];
1263*77c1e3ccSAndroid Build Coastguard Worker int stride_[3];
1264*77c1e3ccSAndroid Build Coastguard Worker };
1265*77c1e3ccSAndroid Build Coastguard Worker
1266*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_SUITE_P(WienerDenoiseTest);
1267*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(WienerDenoiseTest,InvalidBlockSize)1268*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(WienerDenoiseTest, InvalidBlockSize) {
1269*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const data_ptrs[3] = {
1270*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[0][0]),
1271*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[1][0]),
1272*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[2][0]),
1273*77c1e3ccSAndroid Build Coastguard Worker };
1274*77c1e3ccSAndroid Build Coastguard Worker uint8_t *denoised_ptrs[3] = {
1275*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[0][0]),
1276*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[1][0]),
1277*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[2][0]),
1278*77c1e3ccSAndroid Build Coastguard Worker };
1279*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, aom_wiener_denoise_2d(data_ptrs, denoised_ptrs, this->kWidth,
1280*77c1e3ccSAndroid Build Coastguard Worker this->kHeight, this->stride_,
1281*77c1e3ccSAndroid Build Coastguard Worker this->chroma_sub_, this->noise_psd_ptrs_,
1282*77c1e3ccSAndroid Build Coastguard Worker 18, this->kBitDepth, this->kUseHighBD));
1283*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, aom_wiener_denoise_2d(data_ptrs, denoised_ptrs, this->kWidth,
1284*77c1e3ccSAndroid Build Coastguard Worker this->kHeight, this->stride_,
1285*77c1e3ccSAndroid Build Coastguard Worker this->chroma_sub_, this->noise_psd_ptrs_,
1286*77c1e3ccSAndroid Build Coastguard Worker 48, this->kBitDepth, this->kUseHighBD));
1287*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, aom_wiener_denoise_2d(data_ptrs, denoised_ptrs, this->kWidth,
1288*77c1e3ccSAndroid Build Coastguard Worker this->kHeight, this->stride_,
1289*77c1e3ccSAndroid Build Coastguard Worker this->chroma_sub_, this->noise_psd_ptrs_,
1290*77c1e3ccSAndroid Build Coastguard Worker 64, this->kBitDepth, this->kUseHighBD));
1291*77c1e3ccSAndroid Build Coastguard Worker }
1292*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(WienerDenoiseTest,InvalidChromaSubsampling)1293*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(WienerDenoiseTest, InvalidChromaSubsampling) {
1294*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const data_ptrs[3] = {
1295*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[0][0]),
1296*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[1][0]),
1297*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[2][0]),
1298*77c1e3ccSAndroid Build Coastguard Worker };
1299*77c1e3ccSAndroid Build Coastguard Worker uint8_t *denoised_ptrs[3] = {
1300*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[0][0]),
1301*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[1][0]),
1302*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[2][0]),
1303*77c1e3ccSAndroid Build Coastguard Worker };
1304*77c1e3ccSAndroid Build Coastguard Worker int chroma_sub[2] = { 1, 0 };
1305*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, aom_wiener_denoise_2d(data_ptrs, denoised_ptrs, this->kWidth,
1306*77c1e3ccSAndroid Build Coastguard Worker this->kHeight, this->stride_, chroma_sub,
1307*77c1e3ccSAndroid Build Coastguard Worker this->noise_psd_ptrs_, 32, this->kBitDepth,
1308*77c1e3ccSAndroid Build Coastguard Worker this->kUseHighBD));
1309*77c1e3ccSAndroid Build Coastguard Worker
1310*77c1e3ccSAndroid Build Coastguard Worker chroma_sub[0] = 0;
1311*77c1e3ccSAndroid Build Coastguard Worker chroma_sub[1] = 1;
1312*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(0, aom_wiener_denoise_2d(data_ptrs, denoised_ptrs, this->kWidth,
1313*77c1e3ccSAndroid Build Coastguard Worker this->kHeight, this->stride_, chroma_sub,
1314*77c1e3ccSAndroid Build Coastguard Worker this->noise_psd_ptrs_, 32, this->kBitDepth,
1315*77c1e3ccSAndroid Build Coastguard Worker this->kUseHighBD));
1316*77c1e3ccSAndroid Build Coastguard Worker }
1317*77c1e3ccSAndroid Build Coastguard Worker
TYPED_TEST_P(WienerDenoiseTest,GradientTest)1318*77c1e3ccSAndroid Build Coastguard Worker TYPED_TEST_P(WienerDenoiseTest, GradientTest) {
1319*77c1e3ccSAndroid Build Coastguard Worker const int width = this->kWidth;
1320*77c1e3ccSAndroid Build Coastguard Worker const int height = this->kHeight;
1321*77c1e3ccSAndroid Build Coastguard Worker const int block_size = this->kBlockSize;
1322*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const data_ptrs[3] = {
1323*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[0][0]),
1324*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[1][0]),
1325*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->data_[2][0]),
1326*77c1e3ccSAndroid Build Coastguard Worker };
1327*77c1e3ccSAndroid Build Coastguard Worker uint8_t *denoised_ptrs[3] = {
1328*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[0][0]),
1329*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[1][0]),
1330*77c1e3ccSAndroid Build Coastguard Worker reinterpret_cast<uint8_t *>(&this->denoised_[2][0]),
1331*77c1e3ccSAndroid Build Coastguard Worker };
1332*77c1e3ccSAndroid Build Coastguard Worker const int ret = aom_wiener_denoise_2d(
1333*77c1e3ccSAndroid Build Coastguard Worker data_ptrs, denoised_ptrs, width, height, this->stride_, this->chroma_sub_,
1334*77c1e3ccSAndroid Build Coastguard Worker this->noise_psd_ptrs_, block_size, this->kBitDepth, this->kUseHighBD);
1335*77c1e3ccSAndroid Build Coastguard Worker EXPECT_EQ(1, ret);
1336*77c1e3ccSAndroid Build Coastguard Worker
1337*77c1e3ccSAndroid Build Coastguard Worker // Check the noise on the denoised image (from the analytical gradient)
1338*77c1e3ccSAndroid Build Coastguard Worker // and make sure that it is less than what we added.
1339*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
1340*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> measured_noise(width * height);
1341*77c1e3ccSAndroid Build Coastguard Worker
1342*77c1e3ccSAndroid Build Coastguard Worker double var = 0;
1343*77c1e3ccSAndroid Build Coastguard Worker const int shift = (c > 0);
1344*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < (width >> shift); ++x) {
1345*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < (height >> shift); ++y) {
1346*77c1e3ccSAndroid Build Coastguard Worker const double diff = this->denoised_[c][y * this->stride_[c] + x] -
1347*77c1e3ccSAndroid Build Coastguard Worker x * this->kScaleNoise;
1348*77c1e3ccSAndroid Build Coastguard Worker var += diff * diff;
1349*77c1e3ccSAndroid Build Coastguard Worker measured_noise[y * width + x] = diff;
1350*77c1e3ccSAndroid Build Coastguard Worker }
1351*77c1e3ccSAndroid Build Coastguard Worker }
1352*77c1e3ccSAndroid Build Coastguard Worker var /= (width * height);
1353*77c1e3ccSAndroid Build Coastguard Worker const double std = sqrt(std::max(0.0, var));
1354*77c1e3ccSAndroid Build Coastguard Worker EXPECT_LE(std, 1.25f * this->kScaleNoise);
1355*77c1e3ccSAndroid Build Coastguard Worker if (c == 0) {
1356*77c1e3ccSAndroid Build Coastguard Worker std::vector<float> measured_psd =
1357*77c1e3ccSAndroid Build Coastguard Worker get_noise_psd(&measured_noise[0], width, height, block_size);
1358*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> measured_psd_d(block_size * block_size);
1359*77c1e3ccSAndroid Build Coastguard Worker std::vector<double> noise_psd_d(block_size * block_size);
1360*77c1e3ccSAndroid Build Coastguard Worker std::copy(measured_psd.begin(), measured_psd.end(),
1361*77c1e3ccSAndroid Build Coastguard Worker measured_psd_d.begin());
1362*77c1e3ccSAndroid Build Coastguard Worker std::copy(this->noise_psd_[0].begin(), this->noise_psd_[0].end(),
1363*77c1e3ccSAndroid Build Coastguard Worker noise_psd_d.begin());
1364*77c1e3ccSAndroid Build Coastguard Worker EXPECT_LT(
1365*77c1e3ccSAndroid Build Coastguard Worker aom_normalized_cross_correlation(&measured_psd_d[0], &noise_psd_d[0],
1366*77c1e3ccSAndroid Build Coastguard Worker (int)(noise_psd_d.size())),
1367*77c1e3ccSAndroid Build Coastguard Worker 0.35);
1368*77c1e3ccSAndroid Build Coastguard Worker }
1369*77c1e3ccSAndroid Build Coastguard Worker }
1370*77c1e3ccSAndroid Build Coastguard Worker }
1371*77c1e3ccSAndroid Build Coastguard Worker
1372*77c1e3ccSAndroid Build Coastguard Worker REGISTER_TYPED_TEST_SUITE_P(WienerDenoiseTest, InvalidBlockSize,
1373*77c1e3ccSAndroid Build Coastguard Worker InvalidChromaSubsampling, GradientTest);
1374*77c1e3ccSAndroid Build Coastguard Worker
1375*77c1e3ccSAndroid Build Coastguard Worker // Note the empty final argument can be removed if C++20 is made the minimum
1376*77c1e3ccSAndroid Build Coastguard Worker // requirement.
1377*77c1e3ccSAndroid Build Coastguard Worker INSTANTIATE_TYPED_TEST_SUITE_P(WienerDenoiseTestInstatiation, WienerDenoiseTest,
1378*77c1e3ccSAndroid Build Coastguard Worker AllBitDepthParams, );
1379