tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

noise_model_test.cc (53858B)


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