/*
* 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 "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 dqcoeff 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
};
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
Messung V0.5 in Prozent C=94 H=97 G=95
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-10-11)
¤
*© Formatika GbR, Deutschland