| /* | |
| * 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 |