一 函数作用说明
svt_av1_cost_coeffs_txb 用于计算变换块(Transform Block, TXB)中量化系数的码率失真成本(Rate - Distortion Cost).
该函数在视频编码 如AV1 的模式决策,阶段被调用,目的是评估当前变换块编码的比特率和失真的权衡,从而选择最佳的模式。
参数说明:
// Note: don't call this function when eob is 0.
uint64_t svt_av1_cost_coeffs_txb(
struct ModeDecisionContext *ctx, //模式决策上下文,包含编码参数,中间计算结果,概率模型,量化参数,以及算法控制标志
uint8_t allow_update_cdf,//是否允许更新上下文自适应二进制算数编码CABAC的概率模型,CDF,通常为0
FRAME_CONTEXT *ec_ctx,//帧级别的熵编码上下文,存储符号的概率分别模型,用于比特率估算。
struct ModeDecisionCandidateBuffer *cand_bf, //候选模式缓冲区,存储当前候选模式的数据 比如量化系数,预测模式等
const TranLow *const qcoeff,//量化后的变换系数数组指针
uint16_t eob,//变换块的非零系数结束为止End of Block 即最后一个非零系数的索引
PlaneType plane_type, //通道类型,Y/UV
TxSize transform_size, //变换村村,
TxType transform_type,//变换类型 DCT, ADST等
int16_t txb_skip_ctx, //变换块是否跳过上下文索引,用于熵编码概率模型的选择
int16_t dc_sign_ctx, //DC系数符号上下文索引,用于熵编码概率模型的选择
Bool reduced_transform_set_flag //是否使用简化的变换集
)
1.1 初始化上下文
根据plane_type 和transform_size 获取对应的熵编码概率模型(CDF表)
确定是否需要跳过当前块的编码 通过txb_skip_ctx 上下文
1.2 计算跳过块的代价
若块中所有系数均为零(eob == 0) ,直接计算跳过块的比特率成本。
1.3 计算非跳过块的代价
DC系数处理,
计算DC系数的符号Sign和幅值 的编码成本。
AC系数处理
逆扫描通过扫描顺序表 遍历所有非零AC系数,计算每一点的
幅值编码成本(包括二进制化,旁路编码)
符号编码成本(仅对非零系数)
EOB 位置编码
编码ROB位置的额外比特开销。
1.4 码率失真
结合量化系数失真(通常预计算并存储在cand_bf中)和比特率成本,生成最终的码率失真代价
RDCost = lambda * rate + Distortion
1.5 更新概率模型
若allow_update_cdf为1,根据实际编码结果更新 CDF 表,以提升后续符号的编码效率。
CDF:表示符号的概率累积分布表,例如,某个二进制符号的概率模型可能初始化为{0.5,0.5} 但是随着编码数据的变化,CDF会逐步更新为{0.3,0.7}等。
二 函数源码分析
uint64_t svt_av1_cost_coeffs_txb(
struct ModeDecisionContext *ctx,
uint8_t allow_update_cdf,
FRAME_CONTEXT *ec_ctx,
struct ModeDecisionCandidateBuffer *cand_bf,
const TranLow *const qcoeff,
uint16_t eob,
PlaneType plane_type,
TxSize transform_size,
TxType transform_type,
int16_t txb_skip_ctx,
int16_t dc_sign_ctx,
Bool reduced_transform_set_flag)
{
//Note: there is a different version of this function in AOM that seems to be efficient as its name is:
//warehouse_efficients_txb
//计算变换尺寸的上下文索引
const TxSize txs_ctx = (TxSize)((txsize_sqr_map[transform_size] + txsize_sqr_up_map[transform_size] + 1) >> 1);
//获取变换类别
const TxClass tx_class = tx_type_to_class[transform_type];
//获取变换块的对数宽度
int32_t cost;
const int32_t bwl = get_txb_bwl_tab[transform_size];
//获取变换块的宽度和高度
const int32_t width = get_txb_wide_tab[transform_size];
const int32_t height = get_txb_high_tab[transform_size];
//获取扫描顺序
const ScanOrder *const scan_order = &av1_scan_orders[transform_size][transform_type]; // get_scan(tx_size, tx_type);//根据变换块尺寸和类型获取扫描顺序
const int16_t *const scan = scan_order->scan;//定义存储系数级别的缓冲区
uint8_t levels_buf[TX_PAD_2D];
uint8_t *const levels = set_levels(levels_buf, width);
//断言txs_ctx 在有效范围内
DECLARE_ALIGNED(16, int8_t, coeff_contexts[MAX_TX_SQUARE]);
assert(txs_ctx < TX_SIZES);
//获取系数成本表
const LvMapCoeffCost *const coeff_costs = &ctx->md_rate_est_ctx->coeff_fac_bits[txs_ctx][plane_type];
//计算eob多尺寸索引
const int32_t eob_multi_size = txsize_log2_minus4[transform_size];
//获取 eob成本表
const LvMapEobCost *const eob_bits = &ctx->md_rate_est_ctx->eob_frac_bits[eob_multi_size][plane_type];
// eob must be greater than 0 here.
//断言eob > 0
assert(eob > 0);
//初始化成本为跳过变换块的成本
cost = coeff_costs->txb_skip_cost[txb_skip_ctx][0];
//如果允许更新CDF
if (allow_update_cdf)
//更新跳过变换块的CDF
update_cdf(ec_ctx->txb_skip_cdf[txs_ctx][txb_skip_ctx], eob == 0, 2);
//如果eob 大雨1,初始化系数级别
if (eob > 1)
svt_av1_txb_init_levels(qcoeff,
width,
height,
levels); // NM - Needs to be optimized - to be combined with the quantisation.
//初始化系数级别,可能需要优化并与量化结合
//判断是否为帧间模式
const Bool is_inter = is_inter_mode(cand_bf->cand->pred_mode);
// Transform type bit estimation
//添加变换类型比特估计
cost += plane_type > PLANE_TYPE_Y ? 0
: av1_transform_type_rate_estimation(ctx,
allow_update_cdf,
ec_ctx,
cand_bf,
is_inter,
transform_size,
transform_type,
reduced_transform_set_flag);
//计算eob成本
// Transform eob bit estimation
int32_t eob_cost = get_eob_cost(eob, eob_bits, coeff_costs, tx_class);
cost += eob_cost;
//如果允许更新CDF,更新eob上下文
if (allow_update_cdf)
svt_av1_update_eob_context(eob, transform_size, tx_class, plane_type, ec_ctx, allow_update_cdf);
//获取非零系数上下文
// Transform non-zero coeff bit estimation
svt_av1_get_nz_map_contexts(levels,
scan,
eob,
transform_size,
tx_class,
coeff_contexts); // NM - Assembly version is available in AOM //获取非零系数上下文
//AOM有汇编版本, 断言eob不超过宽度乘以高度
assert(eob <= width * height);
//如果允许更新CDF
if (allow_update_cdf) {
//遍历系数
for (int c = eob - 1; c >= 0; --c) {
//获取扫描位置
const int pos = scan[c];
//获取系数上下文
const int coeff_ctx = coeff_contexts[pos];
//获取量化系数
const TranLow v = qcoeff[pos];
//获取系数级别
const TranLow level = abs(v);
//如果是最后一个系数
if (c == eob - 1) {
//断言系数上下文在有效范围内
assert(coeff_ctx < 4);
//更新eob 基础CDF update_cdf(ec_ctx->coeff_base_eob_cdf[txs_ctx][plane_type][coeff_ctx], AOMMIN(level, 3) - 1, 3);
} else {
//更新基础CDF update_cdf(ec_ctx->coeff_base_cdf[txs_ctx][plane_type][coeff_ctx], AOMMIN(level, 3), 4);
}
//如果启用熵统计
{
if (c == eob - 1) {
assert(coeff_ctx < 4);
#if CONFIG_ENTROPY_STATS
++td->counts
->coeff_base_eob_multi[cdf_idx][txsize_ctx][plane_type][coeff_ctx][AOMMIN(level, 3) - 1];
} else {
++td->counts->coeff_base_multi[cdf_idx][txsize_ctx][plane_type][coeff_ctx][AOMMIN(level, 3)];
#endif
}
}
//如果级别超过基础级别
if (level > NUM_BASE_LEVELS) {
//计算基础范围
const int base_range = level - 1 - NUM_BASE_LEVELS;
int br_ctx;
//确定br上下文
if (eob == 1)
br_ctx = 0;
else
br_ctx = get_br_ctx(levels, pos, bwl, tx_class);
//更新br CDF
for (int idx = 0; idx < COEFF_BASE_RANGE; idx += BR_CDF_SIZE - 1) {
const int k = AOMMIN(base_range - idx, BR_CDF_SIZE - 1);
update_cdf(ec_ctx->coeff_br_cdf[AOMMIN(txs_ctx, TX_32X32)][plane_type][br_ctx], k, BR_CDF_SIZE);
//处理LPS
for (int lps = 0; lps < BR_CDF_SIZE - 1; lps++) {
#if CONFIG_ENTROPY_STATS
++td->counts->coeff_lps[AOMMIN(txsize_ctx, TX_32X32)][plane_type][lps][br_ctx][lps == k];
#endif // CONFIG_ENTROPY_STATS
if (lps == k)
break;
}
#if CONFIG_ENTROPY_STATS
++td->counts->coeff_lps_multi[cdf_idx][AOMMIN(txsize_ctx, TX_32X32)][plane_type][br_ctx][k];
#endif
if (k < BR_CDF_SIZE - 1)
break;
}
}
}
//如果直流系数不是0,更新直流符号CDF
if (qcoeff[0] != 0)
update_cdf(ec_ctx->dc_sign_cdf[plane_type][dc_sign_ctx], qcoeff[0] < 0, 2);
//检查对于128x128, 需要在每个Txb的基础上更新txb_context (dc_sign + skip_ctx)
//返回0, 表示更新CDF完成
//TODO: CHKN for 128x128 where we need more than one TXb, we need to update the txb_context(dc_sign+skip_ctx) in a Txb basis
return 0;
}
//计算系数成本并返回, 系数已经经过了调整了 ,重新计算一次成本
cost += av1_cost_coeffs_txb_loop_cost_eob(
ctx, eob, scan, qcoeff, coeff_contexts, coeff_costs, dc_sign_ctx, levels, bwl, transform_type);
return cost;
}