blob: ebbbd3e6b24500f3e46a4a14c010c8ec28074266 [file] [log] [blame]
Neil Birkbecked25a612017-12-21 13:18:32 -08001#include <algorithm>
2#include <vector>
3
4#include "./aom_dsp/noise_model.h"
5#include "./aom_dsp/noise_util.h"
6#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
7
8extern "C" double aom_randn(double sigma);
9
10TEST(NoiseStrengthSolver, GetCentersTwoBins) {
11 aom_noise_strength_solver_t solver;
12 aom_noise_strength_solver_init(&solver, 2);
13 EXPECT_NEAR(0, aom_noise_strength_solver_get_center(&solver, 0), 1e-5);
14 EXPECT_NEAR(255, aom_noise_strength_solver_get_center(&solver, 1), 1e-5);
15 aom_noise_strength_solver_free(&solver);
16}
17
18TEST(NoiseStrengthSolver, GetCenters256Bins) {
19 const int num_bins = 256;
20 aom_noise_strength_solver_t solver;
21 aom_noise_strength_solver_init(&solver, num_bins);
22
23 for (int i = 0; i < 256; ++i) {
24 EXPECT_NEAR(i, aom_noise_strength_solver_get_center(&solver, i), 1e-5);
25 }
26 aom_noise_strength_solver_free(&solver);
27}
28
29// Tests that the noise strength solver returns the identity transform when
30// given identity-like constraints.
31TEST(NoiseStrengthSolver, ObserveIdentity) {
32 const int num_bins = 256;
33 aom_noise_strength_solver_t solver;
34 EXPECT_EQ(1, aom_noise_strength_solver_init(&solver, num_bins));
35
36 // We have to add a big more strength to constraints at the boundary to
37 // overcome any regularization.
38 for (int j = 0; j < 5; ++j) {
39 aom_noise_strength_solver_add_measurement(&solver, 0, 0);
40 aom_noise_strength_solver_add_measurement(&solver, 255, 255);
41 }
42 for (int i = 0; i < 256; ++i) {
43 aom_noise_strength_solver_add_measurement(&solver, i, i);
44 }
45 EXPECT_EQ(1, aom_noise_strength_solver_solve(&solver));
46 for (int i = 2; i < num_bins - 2; ++i) {
47 EXPECT_NEAR(i, solver.eqns.x[i], 0.1);
48 }
49
50 aom_noise_strength_lut_t lut;
51 EXPECT_EQ(1, aom_noise_strength_solver_fit_piecewise(&solver, &lut));
52
53 ASSERT_EQ(2, lut.num_points);
54 EXPECT_NEAR(0.0, lut.points[0][0], 1e-5);
55 EXPECT_NEAR(0.0, lut.points[0][1], 0.5);
56 EXPECT_NEAR(255.0, lut.points[1][0], 1e-5);
57 EXPECT_NEAR(255.0, lut.points[1][1], 0.5);
58
59 aom_noise_strength_lut_free(&lut);
60 aom_noise_strength_solver_free(&solver);
61}
62
63TEST(NoiseStrengthLut, LutEvalSinglePoint) {
64 aom_noise_strength_lut_t lut;
65 ASSERT_TRUE(aom_noise_strength_lut_init(&lut, 1));
66 ASSERT_EQ(1, lut.num_points);
67 lut.points[0][0] = 0;
68 lut.points[0][1] = 1;
69 EXPECT_EQ(1, aom_noise_strength_lut_eval(&lut, -1));
70 EXPECT_EQ(1, aom_noise_strength_lut_eval(&lut, 0));
71 EXPECT_EQ(1, aom_noise_strength_lut_eval(&lut, 1));
72 aom_noise_strength_lut_free(&lut);
73}
74
75TEST(NoiseStrengthLut, LutEvalMultiPointInterp) {
76 const double kEps = 1e-5;
77 aom_noise_strength_lut_t lut;
78 ASSERT_TRUE(aom_noise_strength_lut_init(&lut, 4));
79 ASSERT_EQ(4, lut.num_points);
80
81 lut.points[0][0] = 0;
82 lut.points[0][1] = 0;
83
84 lut.points[1][0] = 1;
85 lut.points[1][1] = 1;
86
87 lut.points[2][0] = 2;
88 lut.points[2][1] = 1;
89
90 lut.points[3][0] = 100;
91 lut.points[3][1] = 1001;
92
93 // Test lower boundary
94 EXPECT_EQ(0, aom_noise_strength_lut_eval(&lut, -1));
95 EXPECT_EQ(0, aom_noise_strength_lut_eval(&lut, 0));
96
97 // Test first part that should be identity
98 EXPECT_NEAR(0.25, aom_noise_strength_lut_eval(&lut, 0.25), kEps);
99 EXPECT_NEAR(0.75, aom_noise_strength_lut_eval(&lut, 0.75), kEps);
100
101 // This is a constant section (should evaluate to 1)
102 EXPECT_NEAR(1.0, aom_noise_strength_lut_eval(&lut, 1.25), kEps);
103 EXPECT_NEAR(1.0, aom_noise_strength_lut_eval(&lut, 1.75), kEps);
104
105 // Test interpolation between to non-zero y coords.
106 EXPECT_NEAR(1, aom_noise_strength_lut_eval(&lut, 2), kEps);
107 EXPECT_NEAR(251, aom_noise_strength_lut_eval(&lut, 26.5), kEps);
108 EXPECT_NEAR(751, aom_noise_strength_lut_eval(&lut, 75.5), kEps);
109
110 // Test upper boundary
111 EXPECT_EQ(1001, aom_noise_strength_lut_eval(&lut, 100));
112 EXPECT_EQ(1001, aom_noise_strength_lut_eval(&lut, 101));
113
114 aom_noise_strength_lut_free(&lut);
115}
116
117TEST(NoiseModel, InitSuccessWithValidSquareShape) {
Yaowu Xu8ddc7ae2018-01-17 17:09:16 -0800118 aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 2 };
Neil Birkbecked25a612017-12-21 13:18:32 -0800119 aom_noise_model_t model;
120
121 EXPECT_TRUE(aom_noise_model_init(&model, params));
122
123 const int kNumCoords = 12;
124 const int kCoords[][2] = { { -2, -2 }, { -1, -2 }, { 0, -2 }, { 1, -2 },
125 { 2, -2 }, { -2, -1 }, { -1, -1 }, { 0, -1 },
126 { 1, -1 }, { 2, -1 }, { -2, 0 }, { -1, 0 } };
127 EXPECT_EQ(kNumCoords, model.n);
128 for (int i = 0; i < kNumCoords; ++i) {
129 const int *coord = kCoords[i];
130 EXPECT_EQ(coord[0], model.coords[i][0]);
131 EXPECT_EQ(coord[1], model.coords[i][1]);
132 }
133 aom_noise_model_free(&model);
134}
135
136TEST(NoiseModel, InitSuccessWithValidDiamondShape) {
137 aom_noise_model_t model;
Yaowu Xu8ddc7ae2018-01-17 17:09:16 -0800138 aom_noise_model_params_t params = { AOM_NOISE_SHAPE_DIAMOND, 2 };
Neil Birkbecked25a612017-12-21 13:18:32 -0800139 EXPECT_TRUE(aom_noise_model_init(&model, params));
140 EXPECT_EQ(6, model.n);
141 const int kNumCoords = 6;
142 const int kCoords[][2] = { { 0, -2 }, { -1, -1 }, { 0, -1 },
143 { 1, -1 }, { -2, 0 }, { -1, 0 } };
144 EXPECT_EQ(kNumCoords, model.n);
145 for (int i = 0; i < kNumCoords; ++i) {
146 const int *coord = kCoords[i];
147 EXPECT_EQ(coord[0], model.coords[i][0]);
148 EXPECT_EQ(coord[1], model.coords[i][1]);
149 }
150 aom_noise_model_free(&model);
151}
152
153TEST(NoiseModel, InitFailsWithTooLargeLag) {
154 aom_noise_model_t model;
Yaowu Xu8ddc7ae2018-01-17 17:09:16 -0800155 aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 10 };
Neil Birkbecked25a612017-12-21 13:18:32 -0800156 EXPECT_FALSE(aom_noise_model_init(&model, params));
157 aom_noise_model_free(&model);
158}
159
160TEST(NoiseModel, InitFailsWithTooSmallLag) {
161 aom_noise_model_t model;
Yaowu Xu8ddc7ae2018-01-17 17:09:16 -0800162 aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 0 };
Neil Birkbecked25a612017-12-21 13:18:32 -0800163 EXPECT_FALSE(aom_noise_model_init(&model, params));
164 aom_noise_model_free(&model);
165}
166
167TEST(NoiseModel, InitFailsWithInvalidShape) {
168 aom_noise_model_t model;
Yaowu Xu8ddc7ae2018-01-17 17:09:16 -0800169 aom_noise_model_params_t params = { aom_noise_shape(100), 3 };
Neil Birkbecked25a612017-12-21 13:18:32 -0800170 EXPECT_FALSE(aom_noise_model_init(&model, params));
171 aom_noise_model_free(&model);
172}
173
174TEST(FlatBlockEstimator, ExtractBlock) {
175 const int kBlockSize = 16;
176 aom_flat_block_finder_t flat_block_finder;
177 ASSERT_EQ(1, aom_flat_block_finder_init(&flat_block_finder, kBlockSize));
178
179 // Test with an image of more than one block.
180 const int h = 2 * kBlockSize;
181 const int w = 2 * kBlockSize;
182 const int stride = 2 * kBlockSize;
183 std::vector<uint8_t> data(h * stride, 128);
184
185 // Set up the (0,0) block to be a plane and the (0,1) block to be a
186 // checkerboard
187 for (int y = 0; y < kBlockSize; ++y) {
188 for (int x = 0; x < kBlockSize; ++x) {
189 data[y * stride + x] = -y + x + 128;
190 data[y * stride + x + kBlockSize] =
191 (x % 2 + y % 2) % 2 ? 128 - 20 : 128 + 20;
192 }
193 }
194 std::vector<double> block(kBlockSize * kBlockSize, 1);
195 std::vector<double> plane(kBlockSize * kBlockSize, 1);
196
197 // The block data should be a constant (zero) and the rest of the plane
198 // trend is covered in the plane data.
199 aom_flat_block_finder_extract_block(&flat_block_finder, &data[0], w, h,
200 stride, 0, 0, &plane[0], &block[0]);
201 for (int y = 0; y < kBlockSize; ++y) {
202 for (int x = 0; x < kBlockSize; ++x) {
203 EXPECT_NEAR(0, block[y * kBlockSize + x], 1e-5);
204 EXPECT_NEAR((double)(data[y * stride + x]) / 255,
205 plane[y * kBlockSize + x], 1e-5);
206 }
207 }
208
209 // The plane trend is a constant, and the block is a zero mean checkerboard.
210 aom_flat_block_finder_extract_block(&flat_block_finder, &data[0], w, h,
211 stride, kBlockSize, 0, &plane[0],
212 &block[0]);
213 for (int y = 0; y < kBlockSize; ++y) {
214 for (int x = 0; x < kBlockSize; ++x) {
215 EXPECT_NEAR(((double)data[y * stride + x + kBlockSize] - 128.0) / 255,
216 block[y * kBlockSize + x], 1e-5);
217 EXPECT_NEAR(128.0 / 255.0, plane[y * kBlockSize + x], 1e-5);
218 }
219 }
220 aom_flat_block_finder_free(&flat_block_finder);
221}
222
223TEST(FlatBlockEstimator, FindFlatBlocks) {
224 const int kBlockSize = 32;
225 aom_flat_block_finder_t flat_block_finder;
226 ASSERT_EQ(1, aom_flat_block_finder_init(&flat_block_finder, kBlockSize));
227
228 const int num_blocks_w = 8;
229 const int h = kBlockSize;
230 const int w = kBlockSize * num_blocks_w;
231 const int stride = w;
232 std::vector<uint8_t> data(h * stride, 128);
233 std::vector<uint8_t> flat_blocks(num_blocks_w, 0);
234
235 for (int y = 0; y < kBlockSize; ++y) {
236 for (int x = 0; x < kBlockSize; ++x) {
237 // Block 0 (not flat): constant doesn't have enough variance to qualify
238 data[y * stride + x + 0 * kBlockSize] = 128;
239
240 // Block 1 (not flat): too high of variance is hard to validate as flat
241 data[y * stride + x + 1 * kBlockSize] = (uint8_t)(128 + aom_randn(5));
242
243 // Block 2 (flat): slight checkerboard added to constant
244 const int check = (x % 2 + y % 2) % 2 ? -2 : 2;
245 data[y * stride + x + 2 * kBlockSize] = 128 + check;
246
247 // Block 3 (flat): planar block with checkerboard pattern is also flat
248 data[y * stride + x + 3 * kBlockSize] = y * 2 - x / 2 + 128 + check;
249
250 // Block 4 (flat): gaussian random with standard deviation 1.
251 data[y * stride + x + 4 * kBlockSize] =
252 (uint8_t)(aom_randn(1) + x + 128.0);
253
254 // Block 5 (flat): gaussian random with standard deviation 2.
255 data[y * stride + x + 5 * kBlockSize] =
256 (uint8_t)(aom_randn(2) + y + 128.0);
257
258 // Block 6 (not flat): too high of directional gradient.
259 const int strong_edge = x > kBlockSize / 2 ? 64 : 0;
260 data[y * stride + x + 6 * kBlockSize] =
261 (uint8_t)(aom_randn(1) + strong_edge + 128.0);
262
263 // Block 7 (not flat): too high gradient.
264 const int big_check = ((x >> 2) % 2 + (y >> 2) % 2) % 2 ? -16 : 16;
265 data[y * stride + x + 7 * kBlockSize] =
266 (uint8_t)(aom_randn(1) + big_check + 128.0);
267 }
268 }
269
270 EXPECT_EQ(4, aom_flat_block_finder_run(&flat_block_finder, &data[0], w, h,
271 stride, &flat_blocks[0]));
272
273 // First two blocks are not flat
274 EXPECT_EQ(0, flat_blocks[0]);
275 EXPECT_EQ(0, flat_blocks[1]);
276
277 // Next 4 blocks are flat.
278 EXPECT_NE(0, flat_blocks[2]);
279 EXPECT_NE(0, flat_blocks[3]);
280 EXPECT_NE(0, flat_blocks[4]);
281 EXPECT_NE(0, flat_blocks[5]);
282
283 // Last 2 are not.
284 EXPECT_EQ(0, flat_blocks[6]);
285 EXPECT_EQ(0, flat_blocks[7]);
286
287 aom_flat_block_finder_free(&flat_block_finder);
288}
289
290class NoiseModelUpdateTest : public ::testing::Test {
291 public:
292 static const int kWidth = 128;
293 static const int kHeight = 128;
294 static const int kBlockSize = 16;
295 static const int kNumBlocksX = kWidth / kBlockSize;
296 static const int kNumBlocksY = kHeight / kBlockSize;
297
298 void SetUp() {
Yaowu Xu8ddc7ae2018-01-17 17:09:16 -0800299 const aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 3 };
Neil Birkbecked25a612017-12-21 13:18:32 -0800300 ASSERT_TRUE(aom_noise_model_init(&model_, params));
301
302 data_.resize(kWidth * kHeight * 3);
303 denoised_.resize(kWidth * kHeight * 3);
Neil Birkbeck70299482018-02-01 13:55:35 -0800304 noise_.resize(kWidth * kHeight * 3);
Neil Birkbecked25a612017-12-21 13:18:32 -0800305 renoise_.resize(kWidth * kHeight);
306 flat_blocks_.resize(kNumBlocksX * kNumBlocksY);
307
Neil Birkbeck70299482018-02-01 13:55:35 -0800308 for (int c = 0, offset = 0; c < 3; ++c, offset += kWidth * kHeight) {
309 data_ptr_[c] = &data_[offset];
310 noise_ptr_[c] = &noise_[offset];
311 denoised_ptr_[c] = &denoised_[offset];
312 strides_[c] = kWidth;
313 }
Neil Birkbecked25a612017-12-21 13:18:32 -0800314 chroma_sub_[0] = 0;
315 chroma_sub_[1] = 0;
316 }
317
318 void TearDown() { aom_noise_model_free(&model_); }
319
320 protected:
321 aom_noise_model_t model_;
322 std::vector<uint8_t> data_;
323 std::vector<uint8_t> denoised_;
324
325 std::vector<double> noise_;
326 std::vector<double> renoise_;
327 std::vector<uint8_t> flat_blocks_;
328
329 uint8_t *data_ptr_[3];
330 uint8_t *denoised_ptr_[3];
Neil Birkbeck70299482018-02-01 13:55:35 -0800331 double *noise_ptr_[3];
Neil Birkbecked25a612017-12-21 13:18:32 -0800332 int strides_[3];
333 int chroma_sub_[2];
334};
335
336TEST_F(NoiseModelUpdateTest, UpdateFailsNoFlatBlocks) {
337 EXPECT_EQ(AOM_NOISE_STATUS_INSUFFICIENT_FLAT_BLOCKS,
338 aom_noise_model_update(&model_, data_ptr_, denoised_ptr_, kWidth,
339 kHeight, strides_, chroma_sub_,
340 &flat_blocks_[0], kBlockSize));
341}
342
343TEST_F(NoiseModelUpdateTest, UpdateSuccessForZeroNoiseAllFlat) {
344 flat_blocks_.assign(flat_blocks_.size(), 1);
345 denoised_.assign(denoised_.size(), 128);
346 data_.assign(denoised_.size(), 128);
347 EXPECT_EQ(AOM_NOISE_STATUS_INTERNAL_ERROR,
348 aom_noise_model_update(&model_, data_ptr_, denoised_ptr_, kWidth,
349 kHeight, strides_, chroma_sub_,
350 &flat_blocks_[0], kBlockSize));
351}
352
353TEST_F(NoiseModelUpdateTest, UpdateFailsBlockSizeTooSmall) {
354 flat_blocks_.assign(flat_blocks_.size(), 1);
355 denoised_.assign(denoised_.size(), 128);
356 data_.assign(denoised_.size(), 128);
357 EXPECT_EQ(
358 AOM_NOISE_STATUS_INVALID_ARGUMENT,
359 aom_noise_model_update(&model_, data_ptr_, denoised_ptr_, kWidth, kHeight,
360 strides_, chroma_sub_, &flat_blocks_[0],
361 6 /* block_size=2 is too small*/));
362}
363
364TEST_F(NoiseModelUpdateTest, UpdateSuccessForWhiteRandomNoise) {
365 for (int y = 0; y < kHeight; ++y) {
366 for (int x = 0; x < kWidth; ++x) {
367 data_ptr_[0][y * kWidth + x] = int(64 + y + aom_randn(1));
368 denoised_ptr_[0][y * kWidth + x] = 64 + y;
369 // Make the chroma planes completely correlated with the Y plane
370 for (int c = 1; c < 3; ++c) {
371 data_ptr_[c][y * kWidth + x] = data_ptr_[0][y * kWidth + x];
372 denoised_ptr_[c][y * kWidth + x] = denoised_ptr_[0][y * kWidth + x];
373 }
374 }
375 }
376 flat_blocks_.assign(flat_blocks_.size(), 1);
377 EXPECT_EQ(AOM_NOISE_STATUS_OK,
378 aom_noise_model_update(&model_, data_ptr_, denoised_ptr_, kWidth,
379 kHeight, strides_, chroma_sub_,
380 &flat_blocks_[0], kBlockSize));
381
382 const double kCoeffEps = 0.075;
383 const int n = model_.n;
384 for (int c = 0; c < 3; ++c) {
385 for (int i = 0; i < n; ++i) {
386 EXPECT_NEAR(0, model_.latest_state[c].eqns.x[i], kCoeffEps);
387 EXPECT_NEAR(0, model_.combined_state[c].eqns.x[i], kCoeffEps);
388 }
389 // The second and third channels are highly correlated with the first.
390 if (c > 0) {
391 ASSERT_EQ(n + 1, model_.latest_state[c].eqns.n);
392 ASSERT_EQ(n + 1, model_.combined_state[c].eqns.n);
393
394 EXPECT_NEAR(1, model_.latest_state[c].eqns.x[n], kCoeffEps);
395 EXPECT_NEAR(1, model_.combined_state[c].eqns.x[n], kCoeffEps);
396 }
397 }
398
399 // The fitted noise strength should be close to the standard deviation
400 // for all intensity bins.
401 const double kStdEps = 0.1;
402 for (int i = 0; i < model_.latest_state[0].strength_solver.eqns.n; ++i) {
403 EXPECT_NEAR(1.0, model_.latest_state[0].strength_solver.eqns.x[i], kStdEps);
404 EXPECT_NEAR(1.0, model_.combined_state[0].strength_solver.eqns.x[i],
405 kStdEps);
406 }
407
408 aom_noise_strength_lut_t lut;
409 aom_noise_strength_solver_fit_piecewise(
410 &model_.latest_state[0].strength_solver, &lut);
411 ASSERT_EQ(2, lut.num_points);
412 EXPECT_NEAR(0.0, lut.points[0][0], 1e-5);
413 EXPECT_NEAR(1.0, lut.points[0][1], kStdEps);
414 EXPECT_NEAR(255.0, lut.points[1][0], 1e-5);
415 EXPECT_NEAR(1.0, lut.points[1][1], kStdEps);
416 aom_noise_strength_lut_free(&lut);
417}
418
419TEST_F(NoiseModelUpdateTest, UpdateSuccessForScaledWhiteNoise) {
Neil Birkbeck70299482018-02-01 13:55:35 -0800420 const double kCoeffEps = 0.055;
Neil Birkbecked25a612017-12-21 13:18:32 -0800421 const double kLowStd = 1;
422 const double kHighStd = 4;
423 for (int y = 0; y < kHeight; ++y) {
424 for (int x = 0; x < kWidth; ++x) {
425 for (int c = 0; c < 3; ++c) {
426 // The image data is bimodal:
427 // Bottom half has low intensity and low noise strength
428 // Top half has high intensity and high noise strength
429 const int avg = (y < kHeight / 2) ? 4 : 245;
430 const double std = (y < kHeight / 2) ? kLowStd : kHighStd;
431 data_ptr_[c][y * kWidth + x] =
432 (uint8_t)std::min((int)255, (int)(2 + avg + aom_randn(std)));
433 denoised_ptr_[c][y * kWidth + x] = 2 + avg;
434 }
435 }
436 }
437 // Label all blocks as flat for the update
438 flat_blocks_.assign(flat_blocks_.size(), 1);
439 EXPECT_EQ(AOM_NOISE_STATUS_OK,
440 aom_noise_model_update(&model_, data_ptr_, denoised_ptr_, kWidth,
441 kHeight, strides_, chroma_sub_,
442 &flat_blocks_[0], kBlockSize));
443
444 const int n = model_.n;
445 // The noise is uncorrelated spatially and with the y channel.
446 // All coefficients should be reasonably close to zero.
447 for (int c = 0; c < 3; ++c) {
448 for (int i = 0; i < n; ++i) {
449 EXPECT_NEAR(0, model_.latest_state[c].eqns.x[i], kCoeffEps);
450 EXPECT_NEAR(0, model_.combined_state[c].eqns.x[i], kCoeffEps);
451 }
452 if (c > 0) {
453 ASSERT_EQ(n + 1, model_.latest_state[c].eqns.n);
454 ASSERT_EQ(n + 1, model_.combined_state[c].eqns.n);
455
456 // The correlation to the y channel should be low (near zero)
457 EXPECT_NEAR(0, model_.latest_state[c].eqns.x[n], kCoeffEps);
458 EXPECT_NEAR(0, model_.combined_state[c].eqns.x[n], kCoeffEps);
459 }
460 }
461
462 // Noise strength should vary between kLowStd and kHighStd.
463 const double kStdEps = 0.15;
464 ASSERT_EQ(20, model_.latest_state[0].strength_solver.eqns.n);
465 for (int i = 0; i < model_.latest_state[0].strength_solver.eqns.n; ++i) {
466 const double a = i / 19.0;
467 const double expected = (kLowStd * (1.0 - a) + kHighStd * a);
468 EXPECT_NEAR(expected, model_.latest_state[0].strength_solver.eqns.x[i],
469 kStdEps);
470 EXPECT_NEAR(expected, model_.combined_state[0].strength_solver.eqns.x[i],
471 kStdEps);
472 }
473
474 // If we fit a piecewise linear model, there should be two points:
475 // one near kLowStd at 0, and the other near kHighStd and 255.
476 aom_noise_strength_lut_t lut;
477 aom_noise_strength_solver_fit_piecewise(
478 &model_.latest_state[0].strength_solver, &lut);
479 ASSERT_EQ(2, lut.num_points);
480 EXPECT_NEAR(0, lut.points[0][0], 1e-4);
481 EXPECT_NEAR(kLowStd, lut.points[0][1], kStdEps);
482 EXPECT_NEAR(255.0, lut.points[1][0], 1e-5);
483 EXPECT_NEAR(kHighStd, lut.points[1][1], kStdEps);
484 aom_noise_strength_lut_free(&lut);
485}
486
487TEST_F(NoiseModelUpdateTest, UpdateSuccessForCorrelatedNoise) {
488 const int kNumCoeffs = 24;
489 const double kStd = 4;
490 const double kStdEps = 0.3;
491 const int kBlockSize = 16;
Neil Birkbeck70299482018-02-01 13:55:35 -0800492 const double kCoeffEps = 0.06;
493 // Use different coefficients for each channel
494 const double kCoeffs[3][24] = {
495 { 0.02884, -0.03356, 0.00633, 0.01757, 0.02849, -0.04620,
496 0.02833, -0.07178, 0.07076, -0.11603, -0.10413, -0.16571,
497 0.05158, -0.07969, 0.02640, -0.07191, 0.02530, 0.41968,
498 0.21450, -0.00702, -0.01401, -0.03676, -0.08713, 0.44196 },
499 { 0.00269, -0.01291, -0.01513, 0.07234, 0.03208, 0.00477,
500 0.00226, -0.00254, 0.03533, 0.12841, -0.25970, -0.06336,
501 0.05238, -0.00845, -0.03118, 0.09043, -0.36558, 0.48903,
502 0.00595, -0.11938, 0.02106, 0.095956, -0.350139, 0.59305 },
503 { -0.00643, -0.01080, -0.01466, 0.06951, 0.03707, -0.00482,
504 0.00817, -0.00909, 0.02949, 0.12181, -0.25210, -0.07886,
505 0.06083, -0.01210, -0.03108, 0.08944, -0.35875, 0.49150,
506 0.00415, -0.12905, 0.02870, 0.09740, -0.34610, 0.58824 },
Neil Birkbecked25a612017-12-21 13:18:32 -0800507 };
508 ASSERT_EQ(model_.n, kNumCoeffs);
Neil Birkbeck70299482018-02-01 13:55:35 -0800509 chroma_sub_[0] = chroma_sub_[1] = 1;
510
Neil Birkbecked25a612017-12-21 13:18:32 -0800511 flat_blocks_.assign(flat_blocks_.size(), 1);
512
Neil Birkbeck70299482018-02-01 13:55:35 -0800513 // Add different noise onto each plane
514 for (int c = 0; c < 3; ++c) {
515 aom_noise_synth(model_.params.lag, model_.n, model_.coords, kCoeffs[c],
516 noise_ptr_[c], kWidth, kHeight);
517 const int x_shift = c > 0 ? chroma_sub_[0] : 0;
518 const int y_shift = c > 0 ? chroma_sub_[1] : 0;
519 for (int y = 0; y < (kHeight >> y_shift); ++y) {
520 for (int x = 0; x < (kWidth >> x_shift); ++x) {
Neil Birkbecked25a612017-12-21 13:18:32 -0800521 const uint8_t value = 64 + x / 2 + y / 4;
Neil Birkbecked25a612017-12-21 13:18:32 -0800522 data_ptr_[c][y * kWidth + x] =
Neil Birkbeck70299482018-02-01 13:55:35 -0800523 uint8_t(value + noise_ptr_[c][y * strides_[c] + x] * kStd);
524 denoised_ptr_[c][y * strides_[c] + x] = value;
Neil Birkbecked25a612017-12-21 13:18:32 -0800525 }
526 }
527 }
528 EXPECT_EQ(AOM_NOISE_STATUS_OK,
529 aom_noise_model_update(&model_, data_ptr_, denoised_ptr_, kWidth,
530 kHeight, strides_, chroma_sub_,
531 &flat_blocks_[0], kBlockSize));
532
533 // For the Y plane, the solved coefficients should be close to the original
534 const int n = model_.n;
Neil Birkbeck70299482018-02-01 13:55:35 -0800535 for (int c = 0; c < 3; ++c) {
536 for (int i = 0; i < n; ++i) {
537 EXPECT_NEAR(kCoeffs[c][i], model_.latest_state[c].eqns.x[i], kCoeffEps);
538 EXPECT_NEAR(kCoeffs[c][i], model_.combined_state[c].eqns.x[i], kCoeffEps);
Neil Birkbecked25a612017-12-21 13:18:32 -0800539 }
Neil Birkbeck70299482018-02-01 13:55:35 -0800540 // The chroma planes should be uncorrelated with the luma plane
541 if (c > 0) {
542 EXPECT_NEAR(0, model_.latest_state[c].eqns.x[n], kCoeffEps);
543 EXPECT_NEAR(0, model_.combined_state[c].eqns.x[n], kCoeffEps);
544 }
545 // Correlation between the coefficient vector and the fitted coefficients
546 // should be close to 1.
547 EXPECT_LT(0.98, aom_normalized_cross_correlation(
548 model_.latest_state[c].eqns.x, kCoeffs[c], kNumCoeffs));
549
550 aom_noise_synth(model_.params.lag, model_.n, model_.coords,
551 model_.latest_state[c].eqns.x, &renoise_[0], kWidth,
552 kHeight);
553
554 EXPECT_TRUE(aom_noise_data_validate(&renoise_[0], kWidth, kHeight));
Neil Birkbecked25a612017-12-21 13:18:32 -0800555 }
556
Neil Birkbeck70299482018-02-01 13:55:35 -0800557 // Check fitted noise strength
558 for (int c = 0; c < 3; ++c) {
559 for (int i = 0; i < model_.latest_state[c].strength_solver.eqns.n; ++i) {
560 EXPECT_NEAR(kStd, model_.latest_state[c].strength_solver.eqns.x[i],
561 kStdEps);
562 }
Neil Birkbecked25a612017-12-21 13:18:32 -0800563 }
564}