|  | /* | 
|  | * Copyright (c) 2021, Alliance for Open Media. All rights reserved | 
|  | * | 
|  | * This source code is subject to the terms of the BSD 2 Clause License and | 
|  | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | 
|  | * was not distributed with this source code in the LICENSE file, you can | 
|  | * obtain it at www.aomedia.org/license/software. If the Alliance for Open | 
|  | * Media Patent License 1.0 was not distributed with this source code in the | 
|  | * PATENTS file, you can obtain it at www.aomedia.org/license/patent. | 
|  | */ | 
|  |  | 
|  | #include <stdint.h> | 
|  | #include <vector> | 
|  |  | 
|  | #include "third_party/googletest/src/googletest/include/gtest/gtest.h" | 
|  |  | 
|  | #include "av1/encoder/block.h" | 
|  | #include "av1/encoder/encodemb.h" | 
|  | #include "av1/common/scan.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Reorders 'qcoeff_lexico', which is in lexicographic order (row by row), into | 
|  | // scan order (zigzag) in 'qcoeff_scan'. | 
|  | void ToScanOrder(TX_SIZE tx_size, TX_TYPE tx_type, tran_low_t *qcoeff_lexico, | 
|  | tran_low_t *qcoeff_scan) { | 
|  | const int max_eob = av1_get_max_eob(tx_size); | 
|  | const SCAN_ORDER *const scan_order = get_scan(tx_size, tx_type); | 
|  | for (int i = 0; i < max_eob; ++i) { | 
|  | qcoeff_scan[i] = qcoeff_lexico[scan_order->scan[i]]; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Reorders 'qcoeff_scan', which is in scan order (zigzag), into lexicographic | 
|  | // order (row by row) in 'qcoeff_lexico'. | 
|  | void ToLexicoOrder(TX_SIZE tx_size, TX_TYPE tx_type, tran_low_t *qcoeff_scan, | 
|  | tran_low_t *qcoeff_lexico) { | 
|  | const int max_eob = av1_get_max_eob(tx_size); | 
|  | const SCAN_ORDER *const scan_order = get_scan(tx_size, tx_type); | 
|  | for (int i = 0; i < max_eob; ++i) { | 
|  | qcoeff_lexico[scan_order->scan[i]] = qcoeff_scan[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Runs coefficient dropout on 'qcoeff_scan'. | 
|  | void Dropout(TX_SIZE tx_size, TX_TYPE tx_type, int dropout_num_before, | 
|  | int dropout_num_after, tran_low_t *qcoeff_scan) { | 
|  | tran_low_t qcoeff[MAX_TX_SQUARE]; | 
|  | // qcoeff_scan is assumed to be in scan order, since tests are easier to | 
|  | // understand this way, but av1_dropout_qcoeff expects coeffs in lexico order | 
|  | // so we convert to lexico then back to scan afterwards. | 
|  | ToLexicoOrder(tx_size, tx_type, qcoeff_scan, qcoeff); | 
|  |  | 
|  | const int max_eob = av1_get_max_eob(tx_size); | 
|  | const int kDequantFactor = 10; | 
|  | tran_low_t dqcoeff[MAX_TX_SQUARE]; | 
|  | for (int i = 0; i < max_eob; ++i) { | 
|  | dqcoeff[i] = qcoeff[i] * kDequantFactor; | 
|  | } | 
|  |  | 
|  | uint16_t eob = max_eob; | 
|  | while (eob > 0 && qcoeff_scan[eob - 1] == 0) --eob; | 
|  |  | 
|  | MACROBLOCK mb; | 
|  | const int kPlane = 0; | 
|  | const int kBlock = 0; | 
|  | memset(&mb, 0, sizeof(mb)); | 
|  | uint16_t eobs[] = { eob }; | 
|  | mb.plane[kPlane].eobs = eobs; | 
|  | mb.plane[kPlane].qcoeff = qcoeff; | 
|  | mb.plane[kPlane].dqcoeff = dqcoeff; | 
|  | uint8_t txb_entropy_ctx[1]; | 
|  | mb.plane[kPlane].txb_entropy_ctx = txb_entropy_ctx; | 
|  |  | 
|  | av1_dropout_qcoeff_num(&mb, kPlane, kBlock, tx_size, tx_type, | 
|  | dropout_num_before, dropout_num_after); | 
|  |  | 
|  | ToScanOrder(tx_size, tx_type, qcoeff, qcoeff_scan); | 
|  |  | 
|  | // Check updated eob value is valid. | 
|  | uint16_t new_eob = max_eob; | 
|  | while (new_eob > 0 && qcoeff_scan[new_eob - 1] == 0) --new_eob; | 
|  | EXPECT_EQ(new_eob, mb.plane[kPlane].eobs[0]); | 
|  |  | 
|  | // Check qqcoeff is still valid. | 
|  | for (int i = 0; i < max_eob; ++i) { | 
|  | EXPECT_EQ(qcoeff[i] * kDequantFactor, dqcoeff[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ExpectArrayEq(tran_low_t *actual, std::vector<tran_low_t> expected) { | 
|  | for (size_t i = 0; i < expected.size(); ++i) { | 
|  | EXPECT_EQ(expected[i], actual[i]) << "Arrays differ at index " << i; | 
|  | } | 
|  | } | 
|  |  | 
|  | static constexpr TX_TYPE kTxType = DCT_DCT; | 
|  |  | 
|  | TEST(DropoutTest, KeepsLargeCoeffs) { | 
|  | const TX_SIZE tx_size = TX_8X4; | 
|  | const uint32_t dropout_num_before = 4; | 
|  | const uint32_t dropout_num_after = 6; | 
|  | // Large isolated coeffs should be preserved. | 
|  | tran_low_t qcoeff_scan[] = { 0, 0, 0, 0, 0, 0, 42, 0,    // should be kept | 
|  | 0, 0, 0, 0, 0, 0, 0,  0,    // | 
|  | 0, 0, 0, 0, 0, 0, 0,  -30,  // should be kept | 
|  | 0, 0, 0, 0, 0, 0, 0,  0 }; | 
|  | Dropout(tx_size, kTxType, dropout_num_before, dropout_num_after, qcoeff_scan); | 
|  | ExpectArrayEq(qcoeff_scan, { 0, 0, 0, 0, 0, 0, 42, 0,    // | 
|  | 0, 0, 0, 0, 0, 0, 0,  0,    // | 
|  | 0, 0, 0, 0, 0, 0, 0,  -30,  // | 
|  | 0, 0, 0, 0, 0, 0, 0,  0 }); | 
|  | } | 
|  |  | 
|  | TEST(DropoutTest, RemovesSmallIsolatedCoeffs) { | 
|  | const TX_SIZE tx_size = TX_8X4; | 
|  | const uint32_t dropout_num_before = 4; | 
|  | const uint32_t dropout_num_after = 6; | 
|  | // Small isolated coeffs should be removed. | 
|  | tran_low_t qcoeff_scan[] = { 0, 0, 0, 0, 1,  0, 0, 0,  // should be removed | 
|  | 0, 0, 0, 0, 0,  0, 0, 0,  // | 
|  | 0, 0, 0, 0, -2, 0, 0, 0,  // should be removed | 
|  | 0, 0, 0, 0, 0,  0, 0, 0 }; | 
|  | Dropout(tx_size, kTxType, dropout_num_before, dropout_num_after, qcoeff_scan); | 
|  | ExpectArrayEq(qcoeff_scan, { 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0 }); | 
|  | } | 
|  |  | 
|  | TEST(DropoutTest, KeepsSmallCoeffsAmongLargeOnes) { | 
|  | const TX_SIZE tx_size = TX_8X4; | 
|  | const uint32_t dropout_num_before = 4; | 
|  | const uint32_t dropout_num_after = 6; | 
|  | // Small coeffs that are not isolated (not enough zeros before/after should be | 
|  | // kept). | 
|  | tran_low_t qcoeff_scan[] = { | 
|  | 1, 0,  0, 0,  -5, 0, 0, -1,  // should be kept | 
|  | 0, 0,  0, 10, 0,  0, 2, 0,   // should be kept | 
|  | 0, 0,  0, 0,  0,  0, 0, 0,   // | 
|  | 0, -2, 0, 0,  0,  0, 0, 0    // should be removed | 
|  | };                             // should be removed | 
|  | Dropout(tx_size, kTxType, dropout_num_before, dropout_num_after, qcoeff_scan); | 
|  | ExpectArrayEq(qcoeff_scan, { 1, 0, 0, 0,  -5, 0, 0, -1,  // | 
|  | 0, 0, 0, 10, 0,  0, 2, 0,   // | 
|  | 0, 0, 0, 0,  0,  0, 0, 0,   // | 
|  | 0, 0, 0, 0,  0,  0, 0, 0 }); | 
|  | } | 
|  |  | 
|  | TEST(DropoutTest, KeepsSmallCoeffsCloseToStartOrEnd) { | 
|  | const TX_SIZE tx_size = TX_8X4; | 
|  | const uint32_t dropout_num_before = 4; | 
|  | const uint32_t dropout_num_after = 6; | 
|  | // Small coeffs that are too close to the beginning or end of the block | 
|  | // should also be kept (not enough zeroes before/after). | 
|  | tran_low_t qcoeff_scan[] = { 0, 0, -1, 0,  0, 0, 0,  0,  // should be kept | 
|  | 0, 0, 0,  10, 0, 0, 0,  0,  // should be kept | 
|  | 0, 0, 0,  2,  0, 0, 0,  0,  // should be removed | 
|  | 0, 0, 0,  0,  0, 0, -1, 0 };  // should be kept | 
|  | Dropout(tx_size, kTxType, dropout_num_before, dropout_num_after, qcoeff_scan); | 
|  | ExpectArrayEq(qcoeff_scan, { 0, 0, -1, 0,  0, 0, 0,  0,  // | 
|  | 0, 0, 0,  10, 0, 0, 0,  0,  // | 
|  | 0, 0, 0,  0,  0, 0, 0,  0,  // | 
|  | 0, 0, 0,  0,  0, 0, -1, 0 }); | 
|  | } | 
|  |  | 
|  | TEST(DropoutTest, RemovesSmallClusterOfCoeffs) { | 
|  | const TX_SIZE tx_size = TX_8X4; | 
|  | const uint32_t dropout_num_before = 4; | 
|  | const uint32_t dropout_num_after = 6; | 
|  | // Small clusters (<= kDropoutContinuityMax) of small coeffs should be | 
|  | // removed. | 
|  | tran_low_t qcoeff_scan_two[] = { | 
|  | 0, 0, 0, 0, 1, 0, 0, -1,  // should be removed | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,   // | 
|  | 0, 0, 0, 0, 0, 0, 1, 0,   // should be removed | 
|  | 0, 0, 0, 0, 0, 0, 0, 0 | 
|  | }; | 
|  | Dropout(tx_size, kTxType, dropout_num_before, dropout_num_after, | 
|  | qcoeff_scan_two); | 
|  | ExpectArrayEq(qcoeff_scan_two, { 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0 }); | 
|  | } | 
|  |  | 
|  | TEST(DropoutTest, KeepsLargeClusterOfCoeffs) { | 
|  | const TX_SIZE tx_size = TX_8X4; | 
|  | const uint32_t dropout_num_before = 4; | 
|  | const uint32_t dropout_num_after = 6; | 
|  | // Large clusters (> kDropoutContinuityMax) of small coeffs should be kept. | 
|  | tran_low_t qcoeff_scan[] = { 0, 0, 0, 0, 1, 0,  1, -1,  // should be kept | 
|  | 0, 0, 0, 0, 0, 0,  0, 0,   // | 
|  | 0, 0, 0, 0, 0, -2, 0, 0,   // should be removed | 
|  | 0, 0, 0, 0, 0, 0,  0, 0 }; | 
|  | Dropout(tx_size, kTxType, dropout_num_before, dropout_num_after, qcoeff_scan); | 
|  | ExpectArrayEq(qcoeff_scan, { 0, 0, 0, 0, 1, 0, 1, -1,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,   // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,   // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0 }); | 
|  | } | 
|  |  | 
|  | TEST(DropoutTest, NumBeforeLargerThanNumAfter) { | 
|  | const TX_SIZE tx_size = TX_8X4; | 
|  | const uint32_t dropout_num_before = 4; | 
|  | const uint32_t dropout_num_after = 2; | 
|  | // The second coeff (-2) doesn't seem to meet the dropout_num_before | 
|  | // criteria. But since the first coeff (1) will be dropped, it will meet | 
|  | // the criteria and should be dropped too. | 
|  | tran_low_t qcoeff_scan[] = { 0,  0, 0, 0, 1, 0, 0, 0,  // should be removed | 
|  | -2, 0, 0, 0, 0, 0, 0, 0,  // should be removed | 
|  | 0,  0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0,  0, 0, 0, 0, 0, 0, 0 }; | 
|  | Dropout(tx_size, kTxType, dropout_num_before, dropout_num_after, qcoeff_scan); | 
|  | ExpectArrayEq(qcoeff_scan, { 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0,  // | 
|  | 0, 0, 0, 0, 0, 0, 0, 0 }); | 
|  | } | 
|  |  | 
|  | // More complex test combining other test cases. | 
|  | TEST(DropoutTest, ComplexTest) { | 
|  | const TX_SIZE tx_size = TX_8X8; | 
|  | const uint32_t dropout_num_before = 4; | 
|  | const uint32_t dropout_num_after = 2; | 
|  | tran_low_t qcoeff_scan[] = { 1, 12, 0,  0,   0, 0, 1,  0,   // | 
|  | 0, 0,  0,  -12, 0, 0, 0,  1,   // | 
|  | 0, 0,  -2, 0,   1, 0, 0,  1,   // | 
|  | 0, 0,  0,  0,   5, 0, -1, 0,   // | 
|  | 0, 0,  0,  1,   0, 0, 0,  -1,  // | 
|  | 0, 0,  0,  0,   2, 0, 0,  0,   // | 
|  | 0, 1,  0,  0,   0, 5, 0,  0,   // | 
|  | 0, 0,  1,  1,   0, 0, 0,  -2 }; | 
|  | Dropout(tx_size, kTxType, dropout_num_before, dropout_num_after, qcoeff_scan); | 
|  | ExpectArrayEq(qcoeff_scan, { 1, 12, 0,  0,   0, 0, 0,  0,  // | 
|  | 0, 0,  0,  -12, 0, 0, 0,  1,  // | 
|  | 0, 0,  -2, 0,   1, 0, 0,  1,  // | 
|  | 0, 0,  0,  0,   5, 0, -1, 0,  // | 
|  | 0, 0,  0,  0,   0, 0, 0,  0,  // | 
|  | 0, 0,  0,  0,   0, 0, 0,  0,  // | 
|  | 0, 0,  0,  0,   0, 5, 0,  0,  // | 
|  | 0, 0,  0,  0,   0, 0, 0,  -2 }); | 
|  | } | 
|  |  | 
|  | }  // namespace |