diff --git a/test/test.mk b/test/test.mk index 08e84db39..335ad8939 100644 --- a/test/test.mk +++ b/test/test.mk @@ -190,6 +190,7 @@ LIBVPX_TEST_SRCS-$(CONFIG_VP9_HIGHBITDEPTH) += hbd_metrics_test.cc endif LIBVPX_TEST_SRCS-$(CONFIG_ENCODERS) += sad_test.cc LIBVPX_TEST_SRCS-$(CONFIG_VP10) += vp10_txfm_test.h +LIBVPX_TEST_SRCS-$(CONFIG_VP10) += vp10_txfm_test.cc LIBVPX_TEST_SRCS-$(CONFIG_VP10) += vp10_fwd_txfm1d_test.cc LIBVPX_TEST_SRCS-$(CONFIG_VP10) += vp10_inv_txfm1d_test.cc LIBVPX_TEST_SRCS-$(CONFIG_VP10) += vp10_fwd_txfm2d_test.cc diff --git a/test/vp10_fwd_txfm2d_test.cc b/test/vp10_fwd_txfm2d_test.cc index da8560fe8..945062e79 100644 --- a/test/vp10_fwd_txfm2d_test.cc +++ b/test/vp10_fwd_txfm2d_test.cc @@ -13,6 +13,7 @@ #include #include "test/acm_random.h" +#include "test/util.h" #include "test/vp10_txfm_test.h" #include "vp10/common/vp10_txfm.h" #include "./vp10_rtcd.h" @@ -23,80 +24,127 @@ using libvpx_test::bd; using libvpx_test::compute_avg_abs_error; using libvpx_test::Fwd_Txfm2d_Func; using libvpx_test::TYPE_TXFM; -using libvpx_test::TYPE_DCT; -using libvpx_test::TYPE_ADST; namespace { - #if CONFIG_VP9_HIGHBITDEPTH const Fwd_Txfm2d_Func fwd_txfm_func_ls[TX_SIZES] = { vp10_fwd_txfm2d_4x4_c, vp10_fwd_txfm2d_8x8_c, vp10_fwd_txfm2d_16x16_c, vp10_fwd_txfm2d_32x32_c}; -const TYPE_TXFM type_ls_0[4] = {TYPE_DCT, TYPE_ADST, TYPE_DCT, TYPE_ADST}; -const TYPE_TXFM type_ls_1[4] = {TYPE_DCT, TYPE_DCT, TYPE_ADST, TYPE_ADST}; +// VP10FwdTxfm2dParam argument list: +// tx_type_, tx_size_, max_error_, max_avg_error_ +typedef std::tr1::tuple VP10FwdTxfm2dParam; -TEST(vp10_fwd_txfm2d, accuracy) { - for (int tx_size = 0; tx_size < TX_SIZES; ++tx_size) { - int txfm_size = 1 << (tx_size + 2); - int sqr_txfm_size = txfm_size * txfm_size; - int16_t* input = new int16_t[sqr_txfm_size]; - int32_t* output = new int32_t[sqr_txfm_size]; - double* ref_input = new double[sqr_txfm_size]; - double* ref_output = new double[sqr_txfm_size]; +class VP10FwdTxfm2d : public ::testing::TestWithParam { + public: + virtual void SetUp() { + tx_type_ = GET_PARAM(0); + tx_size_ = GET_PARAM(1); + max_error_ = GET_PARAM(2); + max_avg_error_ = GET_PARAM(3); + count_ = 500; + TXFM_2D_FLIP_CFG fwd_txfm_flip_cfg = + vp10_get_fwd_txfm_cfg(tx_type_, tx_size_); + const TXFM_2D_CFG *fwd_txfm_cfg = fwd_txfm_flip_cfg.cfg; + int amplify_bit = fwd_txfm_cfg->shift[0] + fwd_txfm_cfg->shift[1] + + fwd_txfm_cfg->shift[2]; + amplify_factor_ = + amplify_bit >= 0 ? (1 << amplify_bit) : (1.0 / (1 << -amplify_bit)); - for (int tx_type = 0; tx_type < 4; ++tx_type) { - TXFM_2D_FLIP_CFG fwd_txfm_flip_cfg = - vp10_get_fwd_txfm_cfg(tx_type, tx_size); - const TXFM_2D_CFG *fwd_txfm_cfg = fwd_txfm_flip_cfg.cfg; - if (fwd_txfm_cfg != NULL) { - Fwd_Txfm2d_Func fwd_txfm_func = fwd_txfm_func_ls[tx_size]; - TYPE_TXFM type0 = type_ls_0[tx_type]; - TYPE_TXFM type1 = type_ls_1[tx_type]; - int amplify_bit = fwd_txfm_cfg->shift[0] + fwd_txfm_cfg->shift[1] + - fwd_txfm_cfg->shift[2]; - double amplify_factor = - amplify_bit >= 0 ? (1 << amplify_bit) : (1.0 / (1 << -amplify_bit)); + fwd_txfm_ = fwd_txfm_func_ls[tx_size_]; + txfm1d_size_ = libvpx_test::get_txfm1d_size(tx_size_); + txfm2d_size_ = txfm1d_size_ * txfm1d_size_; + get_txfm1d_type(tx_type_, &type0_, &type1_); + input_ = reinterpret_cast + (vpx_memalign(16, sizeof(int16_t) * txfm2d_size_)); + output_ = reinterpret_cast + (vpx_memalign(16, sizeof(int32_t) * txfm2d_size_)); + ref_input_ = reinterpret_cast + (vpx_memalign(16, sizeof(double) * txfm2d_size_)); + ref_output_ = reinterpret_cast + (vpx_memalign(16, sizeof(double) * txfm2d_size_)); + } - ACMRandom rnd(ACMRandom::DeterministicSeed()); - int count = 500; - double avg_abs_error = 0; - for (int ci = 0; ci < count; ci++) { - for (int ni = 0; ni < sqr_txfm_size; ++ni) { - input[ni] = rnd.Rand16() % input_base; - ref_input[ni] = static_cast(input[ni]); - output[ni] = 0; - ref_output[ni] = 0; - } - - fwd_txfm_func(input, output, txfm_size, tx_type, bd); - reference_hybrid_2d(ref_input, ref_output, txfm_size, type0, type1); - - for (int ni = 0; ni < sqr_txfm_size; ++ni) { - ref_output[ni] = round(ref_output[ni] * amplify_factor); - EXPECT_LE(fabs(output[ni] - ref_output[ni]) / amplify_factor, 70); - } - avg_abs_error += compute_avg_abs_error( - output, ref_output, sqr_txfm_size); - } - - avg_abs_error /= amplify_factor; - avg_abs_error /= count; - // max_abs_avg_error comes from upper bound of avg_abs_error - // printf("type0: %d type1: %d txfm_size: %d accuracy_avg_abs_error: - // %f\n", - // type0, type1, txfm_size, avg_abs_error); - double max_abs_avg_error = 7; - EXPECT_LE(avg_abs_error, max_abs_avg_error); + void RunFwdAccuracyCheck() { + ACMRandom rnd(ACMRandom::DeterministicSeed()); + double avg_abs_error = 0; + for (int ci = 0; ci < count_; ci++) { + for (int ni = 0; ni < txfm2d_size_; ++ni) { + input_[ni] = rnd.Rand16() % input_base; + ref_input_[ni] = static_cast(input_[ni]); + output_[ni] = 0; + ref_output_[ni] = 0; } + + fwd_txfm_(input_, output_, txfm1d_size_, tx_type_, bd); + reference_hybrid_2d(ref_input_, ref_output_, txfm1d_size_, + type0_, type1_); + + for (int ni = 0; ni < txfm2d_size_; ++ni) { + ref_output_[ni] = round(ref_output_[ni] * amplify_factor_); + EXPECT_GE(max_error_, + fabs(output_[ni] - ref_output_[ni]) / amplify_factor_); + } + avg_abs_error += compute_avg_abs_error( + output_, ref_output_, txfm2d_size_); } - delete[] input; - delete[] output; - delete[] ref_input; - delete[] ref_output; + avg_abs_error /= amplify_factor_; + avg_abs_error /= count_; + // max_abs_avg_error comes from upper bound of avg_abs_error + // printf("type0: %d type1: %d txfm_size: %d accuracy_avg_abs_error: + // %f\n", type0_, type1_, txfm1d_size_, avg_abs_error); + EXPECT_GE(max_avg_error_, avg_abs_error); } + + virtual void TearDown() { + vpx_free(input_); + vpx_free(output_); + vpx_free(ref_input_); + vpx_free(ref_output_); + } + + private: + double max_error_; + double max_avg_error_; + int count_; + double amplify_factor_; + TX_TYPE tx_type_; + TX_SIZE tx_size_; + int txfm1d_size_; + int txfm2d_size_; + Fwd_Txfm2d_Func fwd_txfm_; + TYPE_TXFM type0_; + TYPE_TXFM type1_; + int16_t* input_; + int32_t* output_; + double* ref_input_; + double* ref_output_; +}; + +TEST_P(VP10FwdTxfm2d, RunFwdAccuracyCheck) { + RunFwdAccuracyCheck(); } + +INSTANTIATE_TEST_CASE_P( + C, VP10FwdTxfm2d, + ::testing::Values( + VP10FwdTxfm2dParam(DCT_DCT, TX_4X4, 2, 0.2), + VP10FwdTxfm2dParam(ADST_DCT, TX_4X4, 2, 0.2), + VP10FwdTxfm2dParam(DCT_ADST, TX_4X4, 2, 0.2), + VP10FwdTxfm2dParam(ADST_ADST, TX_4X4, 2, 0.2), + VP10FwdTxfm2dParam(DCT_DCT, TX_8X8, 5, 0.6), + VP10FwdTxfm2dParam(ADST_DCT, TX_8X8, 5, 0.6), + VP10FwdTxfm2dParam(DCT_ADST, TX_8X8, 5, 0.6), + VP10FwdTxfm2dParam(ADST_ADST, TX_8X8, 5, 0.6), + VP10FwdTxfm2dParam(DCT_DCT, TX_16X16, 11, 1.5), + VP10FwdTxfm2dParam(ADST_DCT, TX_16X16, 11, 1.5), + VP10FwdTxfm2dParam(DCT_ADST, TX_16X16, 11, 1.5), + VP10FwdTxfm2dParam(ADST_ADST, TX_16X16, 11, 1.5), + VP10FwdTxfm2dParam(DCT_DCT, TX_32X32, 70, 7), + VP10FwdTxfm2dParam(ADST_DCT, TX_32X32, 70, 7), + VP10FwdTxfm2dParam(DCT_ADST, TX_32X32, 70, 7), + VP10FwdTxfm2dParam(ADST_ADST, TX_32X32, 70, 7))); #endif // CONFIG_VP9_HIGHBITDEPTH } // namespace diff --git a/test/vp10_txfm_test.cc b/test/vp10_txfm_test.cc new file mode 100644 index 000000000..a1fef0b6d --- /dev/null +++ b/test/vp10_txfm_test.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2015 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "test/vp10_txfm_test.h" + +namespace libvpx_test { + +int get_txfm1d_size(TX_SIZE tx_size) { + return 1 << (tx_size + 2); +} + +void get_txfm1d_type(TX_TYPE txfm2d_type, TYPE_TXFM* type0, + TYPE_TXFM* type1) { + switch (txfm2d_type) { + case DCT_DCT: + *type0 = TYPE_DCT; + *type1 = TYPE_DCT; + break; + case ADST_DCT: + *type0 = TYPE_ADST; + *type1 = TYPE_DCT; + break; + case DCT_ADST: + *type0 = TYPE_DCT; + *type1 = TYPE_ADST; + break; + case ADST_ADST: + *type0 = TYPE_ADST; + *type1 = TYPE_ADST; + break; + default: + *type0 = TYPE_DCT; + *type1 = TYPE_DCT; + assert(0); + break; + } +} + +double invSqrt2 = 1 / pow(2, 0.5); + +void reference_dct_1d(const double* in, double* out, int size) { + for (int k = 0; k < size; ++k) { + out[k] = 0; + for (int n = 0; n < size; ++n) { + out[k] += in[n] * cos(M_PI * (2 * n + 1) * k / (2 * size)); + } + if (k == 0) out[k] = out[k] * invSqrt2; + } +} + +void reference_adst_1d(const double* in, double* out, int size) { + for (int k = 0; k < size; ++k) { + out[k] = 0; + for (int n = 0; n < size; ++n) { + out[k] += in[n] * sin(M_PI * (2 * n + 1) * (2 * k + 1) / (4 * size)); + } + } +} + +void reference_hybrid_1d(double* in, double* out, int size, int type) { + if (type == TYPE_DCT) + reference_dct_1d(in, out, size); + else + reference_adst_1d(in, out, size); +} + +void reference_hybrid_2d(double* in, double* out, int size, + int type0, int type1) { + double* tempOut = new double[size * size]; + + for (int r = 0; r < size; r++) { + // out ->tempOut + for (int c = 0; c < size; c++) { + tempOut[r * size + c] = in[c * size + r]; + } + } + + // dct each row: in -> out + for (int r = 0; r < size; r++) { + reference_hybrid_1d(tempOut + r * size, out + r * size, size, type0); + } + + for (int r = 0; r < size; r++) { + // out ->tempOut + for (int c = 0; c < size; c++) { + tempOut[r * size + c] = out[c * size + r]; + } + } + + for (int r = 0; r < size; r++) { + reference_hybrid_1d(tempOut + r * size, out + r * size, size, type1); + } + delete[] tempOut; +} +} // namespace libvpx_test diff --git a/test/vp10_txfm_test.h b/test/vp10_txfm_test.h index 4d417adea..3849f5159 100644 --- a/test/vp10_txfm_test.h +++ b/test/vp10_txfm_test.h @@ -33,62 +33,19 @@ typedef enum { TYPE_LAST } TYPE_TXFM; -static double invSqrt2 = 1 / pow(2, 0.5); +int get_txfm1d_size(TX_SIZE tx_size); -static void reference_dct_1d(const double* in, double* out, int size) { - for (int k = 0; k < size; ++k) { - out[k] = 0; - for (int n = 0; n < size; ++n) { - out[k] += in[n] * cos(M_PI * (2 * n + 1) * k / (2 * size)); - } - if (k == 0) out[k] = out[k] * invSqrt2; - } -} +void get_txfm1d_type(TX_TYPE txfm2d_type, TYPE_TXFM* type0, + TYPE_TXFM* type1); -static void reference_adst_1d(const double* in, double* out, int size) { - for (int k = 0; k < size; ++k) { - out[k] = 0; - for (int n = 0; n < size; ++n) { - out[k] += in[n] * sin(M_PI * (2 * n + 1) * (2 * k + 1) / (4 * size)); - } - } -} +void reference_dct_1d(const double* in, double* out, int size); -static void reference_hybrid_1d(double* in, double* out, int size, int type) { - if (type == TYPE_DCT) - reference_dct_1d(in, out, size); - else - reference_adst_1d(in, out, size); -} +void reference_adst_1d(const double* in, double* out, int size); -static INLINE void reference_hybrid_2d(double* in, double* out, int size, - int type0, int type1) { - double* tempOut = new double[size * size]; +void reference_hybrid_1d(double* in, double* out, int size, int type); - for (int r = 0; r < size; r++) { - // out ->tempOut - for (int c = 0; c < size; c++) { - tempOut[r * size + c] = in[c * size + r]; - } - } - - // dct each row: in -> out - for (int r = 0; r < size; r++) { - reference_hybrid_1d(tempOut + r * size, out + r * size, size, type0); - } - - for (int r = 0; r < size; r++) { - // out ->tempOut - for (int c = 0; c < size; c++) { - tempOut[r * size + c] = out[c * size + r]; - } - } - - for (int r = 0; r < size; r++) { - reference_hybrid_1d(tempOut + r * size, out + r * size, size, type1); - } - delete[] tempOut; -} +void reference_hybrid_2d(double* in, double* out, int size, + int type0, int type1); template static double compute_avg_abs_error(const Type1* a, const Type2* b,