From a441b60aa07326898099e41afaa5fd043c14922c Mon Sep 17 00:00:00 2001 From: "Ronald S. Bultje" Date: Mon, 25 Jun 2012 09:34:10 -0700 Subject: [PATCH] Add unit test for intra prediction. Change-Id: I7dadadeb99bee5a51219f46fe11c760fc294c735 --- test/intrapred_test.cc | 313 +++++++++++++++++++++++++++++++++++++++++++++++++ test/test.mk | 1 + 2 files changed, 314 insertions(+) create mode 100755 test/intrapred_test.cc diff --git a/test/intrapred_test.cc b/test/intrapred_test.cc new file mode 100755 index 0000000..0e07b09 --- /dev/null +++ b/test/intrapred_test.cc @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2012 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 "third_party/googletest/src/include/gtest/gtest.h" +extern "C" { +#include "vpx_config.h" +#include "vpx_rtcd.h" +#include "vp8/common/blockd.h" +} + +namespace { + +class IntraPredBase { + protected: + void SetupMacroblock(uint8_t *data, int block_size, int stride, + int num_planes) { + memset(&mb_, 0, sizeof(mb_)); + memset(&mi_, 0, sizeof(mi_)); + mb_.up_available = 1; + mb_.left_available = 1; + mb_.mode_info_context = &mi_; + stride_ = stride; + block_size_ = block_size; + num_planes_ = num_planes; + for (int p = 0; p < num_planes; p++) + data_ptr_[p] = data + stride * (block_size + 1) * p + + stride + block_size; + } + + void FillRandom() { + // Fill edges with random data + for (int p = 0; p < num_planes_; p++) { + for (int x = -1 ; x <= block_size_; x++) + data_ptr_[p][x - stride_] = rand(); + for (int y = 0; y < block_size_; y++) + data_ptr_[p][y * stride_ - 1] = rand(); + } + } + + virtual void Predict(MB_PREDICTION_MODE mode) = 0; + + void SetLeftUnavailable() { + mb_.left_available = 0; + for (int p = 0; p < num_planes_; p++) + for (int i = -1; i < block_size_; ++i) + data_ptr_[p][stride_ * i - 1] = 129; + } + + void SetTopUnavailable() { + mb_.up_available = 0; + for (int p = 0; p < num_planes_; p++) + memset(&data_ptr_[p][-1 - stride_], 127, block_size_ + 2); + } + + void SetTopLeftUnavailable() { + SetLeftUnavailable(); + SetTopUnavailable(); + } + + int BlockSizeLog2Min1() const { + switch (block_size_) { + case 16: + return 3; + case 8: + return 2; + default: + return 0; + } + } + + // check DC prediction output against a reference + void CheckDCPrediction() const { + for (int p = 0; p < num_planes_; p++) { + // calculate expected DC + int expected; + if (mb_.up_available || mb_.left_available) { + int sum = 0, shift = BlockSizeLog2Min1() + mb_.up_available + + mb_.left_available; + if (mb_.up_available) + for (int x = 0; x < block_size_; x++) + sum += data_ptr_[p][x - stride_]; + if (mb_.left_available) + for (int y = 0; y < block_size_; y++) + sum += data_ptr_[p][y * stride_ - 1]; + expected = (sum + (1 << (shift - 1))) >> shift; + } else + expected = 0x80; + + // check that all subsequent lines are equal to the first + for (int y = 1; y < block_size_; ++y) + ASSERT_EQ(0, memcmp(data_ptr_[p], &data_ptr_[p][y * stride_], + block_size_)); + // within the first line, ensure that each pixel has the same value + for (int x = 1; x < block_size_; ++x) + ASSERT_EQ(data_ptr_[p][0], data_ptr_[p][x]); + // now ensure that that pixel has the expected (DC) value + ASSERT_EQ(expected, data_ptr_[p][0]); + } + } + + // check V prediction output against a reference + void CheckVPrediction() const { + // check that all lines equal the top border + for (int p = 0; p < num_planes_; p++) + for (int y = 0; y < block_size_; y++) + ASSERT_EQ(0, memcmp(&data_ptr_[p][-stride_], + &data_ptr_[p][y * stride_], block_size_)); + } + + // check H prediction output against a reference + void CheckHPrediction() const { + // for each line, ensure that each pixel is equal to the left border + for (int p = 0; p < num_planes_; p++) + for (int y = 0; y < block_size_; y++) + for (int x = 0; x < block_size_; x++) + ASSERT_EQ(data_ptr_[p][-1 + y * stride_], + data_ptr_[p][x + y * stride_]); + } + + static int ClipByte(int value) { + if (value > 255) + return 255; + else if (value < 0) + return 0; + return value; + } + + // check TM prediction output against a reference + void CheckTMPrediction() const { + for (int p = 0; p < num_planes_; p++) + for (int y = 0; y < block_size_; y++) + for (int x = 0; x < block_size_; x++) { + const int expected = ClipByte(data_ptr_[p][x - stride_] + + data_ptr_[p][stride_ * y - 1] + - data_ptr_[p][-1 - stride_]); + ASSERT_EQ(expected, data_ptr_[p][y * stride_ + x]); + } + } + + // Actual test + void RunTest() { + { + SCOPED_TRACE("DC_PRED"); + FillRandom(); + Predict(DC_PRED); + CheckDCPrediction(); + } + { + SCOPED_TRACE("DC_PRED LEFT"); + FillRandom(); + SetLeftUnavailable(); + Predict(DC_PRED); + CheckDCPrediction(); + } + { + SCOPED_TRACE("DC_PRED TOP"); + FillRandom(); + SetTopUnavailable(); + Predict(DC_PRED); + CheckDCPrediction(); + } + { + SCOPED_TRACE("DC_PRED TOP_LEFT"); + FillRandom(); + SetTopLeftUnavailable(); + Predict(DC_PRED); + CheckDCPrediction(); + } + { + SCOPED_TRACE("H_PRED"); + FillRandom(); + Predict(H_PRED); + CheckHPrediction(); + } + { + SCOPED_TRACE("V_PRED"); + FillRandom(); + Predict(V_PRED); + CheckVPrediction(); + } + { + SCOPED_TRACE("TM_PRED"); + FillRandom(); + Predict(TM_PRED); + CheckTMPrediction(); + } + } + + MACROBLOCKD mb_; + MODE_INFO mi_; + uint8_t *data_ptr_[2]; // in the case of Y, only [0] is used + int stride_; + int block_size_; + int num_planes_; +}; + +typedef void (*intra_pred_y_fn_t)(MACROBLOCKD *x, + uint8_t *yabove_row, + uint8_t *yleft, + int left_stride, + uint8_t *ypred_ptr, + int y_stride); + +class IntraPredYTest : public ::testing::TestWithParam, + protected IntraPredBase { + protected: + static const int kBlockSize = 16; + static const int kStride = kBlockSize * 3; + + virtual void SetUp() { + pred_fn_ = GetParam(); + SetupMacroblock(data_array_, kBlockSize, kStride, 1); + } + + virtual void Predict(MB_PREDICTION_MODE mode) { + mb_.mode_info_context->mbmi.mode = mode; + pred_fn_(&mb_, data_ptr_[0] - kStride, data_ptr_[0] - 1, kStride, + data_ptr_[0], kStride); + } + + intra_pred_y_fn_t pred_fn_; + // We use 48 so that the data pointer of the first pixel in each row of + // each macroblock is 16-byte aligned, and this gives us access to the + // top-left and top-right corner pixels belonging to the top-left/right + // macroblocks. + // We use 17 lines so we have one line above us for top-prediction. + DECLARE_ALIGNED(16, uint8_t, data_array_[kStride * (kBlockSize + 1)]); +}; + +TEST_P(IntraPredYTest, IntraPredTests) { + RunTest(); +} + +INSTANTIATE_TEST_CASE_P(C, IntraPredYTest, + ::testing::Values( + vp8_build_intra_predictors_mby_s_c)); +#if HAVE_SSE2 +INSTANTIATE_TEST_CASE_P(SSE2, IntraPredYTest, + ::testing::Values( + vp8_build_intra_predictors_mby_s_sse2)); +#endif +#if HAVE_SSSE3 +INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredYTest, + ::testing::Values( + vp8_build_intra_predictors_mby_s_ssse3)); +#endif + +typedef void (*intra_pred_uv_fn_t)(MACROBLOCKD *x, + uint8_t *uabove_row, + uint8_t *vabove_row, + uint8_t *uleft, + uint8_t *vleft, + int left_stride, + uint8_t *upred_ptr, + uint8_t *vpred_ptr, + int pred_stride); + +class IntraPredUVTest : public ::testing::TestWithParam, + protected IntraPredBase { + protected: + static const int kBlockSize = 8; + static const int kStride = kBlockSize * 3; + + virtual void SetUp() { + pred_fn_ = GetParam(); + SetupMacroblock(data_array_, kBlockSize, kStride, 2); + } + + virtual void Predict(MB_PREDICTION_MODE mode) { + mb_.mode_info_context->mbmi.uv_mode = mode; + pred_fn_(&mb_, data_ptr_[0] - kStride, data_ptr_[1] - kStride, + data_ptr_[0] - 1, data_ptr_[1] - 1, kStride, + data_ptr_[0], data_ptr_[1], kStride); + } + + intra_pred_uv_fn_t pred_fn_; + // We use 24 so that the data pointer of the first pixel in each row of + // each macroblock is 8-byte aligned, and this gives us access to the + // top-left and top-right corner pixels belonging to the top-left/right + // macroblocks. + // We use 9 lines so we have one line above us for top-prediction. + // [0] = U, [1] = V + DECLARE_ALIGNED(8, uint8_t, data_array_[2 * kStride * (kBlockSize + 1)]); +}; + +TEST_P(IntraPredUVTest, IntraPredTests) { + RunTest(); +} + +INSTANTIATE_TEST_CASE_P(C, IntraPredUVTest, + ::testing::Values( + vp8_build_intra_predictors_mbuv_s_c)); +#if HAVE_SSE2 +INSTANTIATE_TEST_CASE_P(SSE2, IntraPredUVTest, + ::testing::Values( + vp8_build_intra_predictors_mbuv_s_sse2)); +#endif +#if HAVE_SSSE3 +INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredUVTest, + ::testing::Values( + vp8_build_intra_predictors_mbuv_s_ssse3)); +#endif + +} // namespace diff --git a/test/test.mk b/test/test.mk index 11b05c9..2853f47 100644 --- a/test/test.mk +++ b/test/test.mk @@ -4,6 +4,7 @@ LIBVPX_TEST_SRCS-yes += config_test.cc LIBVPX_TEST_SRCS-yes += encode_test_driver.cc LIBVPX_TEST_SRCS-yes += encode_test_driver.h LIBVPX_TEST_SRCS-yes += idctllm_test.cc +LIBVPX_TEST_SRCS-yes += intrapred_test.cc LIBVPX_TEST_SRCS-yes += keyframe_test.cc LIBVPX_TEST_SRCS-yes += pp_filter_test.cc LIBVPX_TEST_SRCS-yes += resize_test.cc -- 2.7.4