当前位置: 首页 > news >正文

SVT-AV1源码分析函数 svt_av1_optimize_b

perf svt-av1 编码热点函数

一 函数作用

svt_av1_optimize_b这个函数是SVT-AV1(Scaleable Video Technology for AV1)编码器中的一个很关键的函数,在编码过程中对块进行优化处理,以提高编码效率和视频质量,以下是对该函数的详细解释。

函数参数

ModeDecisionContext *ctx; 模式决策上下文,包含了当前块的编码模式决策信息。

int16_t txb_skip_connect 变换块跳过上下文,用于熵编码时判断是否跳过变换块

int16_t dc_sign_context; 直流系数符号上下文,用于确定直流系数的符号。

const TranLow *coeff_ptr 输入的变换系数指针,指向当前块的变换系数。

const MacroblockPlane *p; 宏块通道信息,包含了当前处理的通道相关的信息

TranLow *qcoeff_ptr; 量化后的变换系数输出指针

TranLow *dqcoeff_ptr; 反量化后的变换系数输出指针

uint16_t *eob; //有消费令系数数量的输出指针,表示在量化后有多少个非零系数。

const QuantParam *qparam; 量化参数,包含了量化步长等信息

TxSize tx_size 变换块的大小

TxType tx_type ;变换类型,制定了使用的变换方法。

Bool is_inter 是否是帧间编码块的标志

uint8_t use_sharpness 是否使用锐化功能的标志

uint8_t delta_q_present 是否使用delta量化参数的标志

uint8_t picture_qp 图片的量化参数

uint32_t lambda 拉格朗日系数,用于码率失真优化

int plane 当前处理的通道

该函数主要功能是对输入的变换系数进行量化,反量化等处理,并计算有效非零系数数量,同时根据不同的编码模式和参数进行优化,具体包括以下几个方面。

1.1 量化处理,根据量化参数对输入的变换系数进行量化,得到量化后的系数存储在qcoeff_ptr中,量化过程会会根据picture_qp, delta_q_present 等参数调整量化步长,以控制编码后的比特率质量。

2 去量化处理,对量化后的系数进行去量化,得到去量化后的系数存储在dqcoeff_ptr中,,反量化后的系数用于后续的重建过程,以生成预测块。

3 有效非零系数计算,通过遍历量化后的系数,统计有效非零系数的数量,将结果存储在eob中,这个值对于熵编码非常重要,因为可以减少需要编码的系数数量,提高编码效率。

4 码率失真优化,根据拉格朗日乘数 lambda 进行码率失真优化,选择最优的量化参数和编码模式,以在保证视频质量的同时最小化码率。

5 上下文处理,处理txb_skip_context和dc_sign_context等上下文信息,为熵编码提供必要的上下文数据,以便更高效的对系数进行编码。

6 锐化处理 如果use_sharpness标志碑设置,函数可能会应用锐化算法,以增强图像的边缘和细节,提高视觉质量。

7 编码模式适配,根据is_inter 标志判断当前块是帧内编码还是帧间编码,相应的调整处理策略,以适应不同的编码需求。

函数的作用和意义

svt_av1_optimize_b在SVT-AV1编码器中扮演着至关重要的角色。它通过对变换系数的优化处理,直接影响了编码后的视频质量和码率。在保证视频质量的前提下,通过量化、去量化、率失真优化等手段,尽可能减少码率,提高编码效率。同时,该函数还为后续的熵编码提供了必要的数据准备,确保整个编码流程的高效性和准确性。

二, 函数源码注释

static void svt_av1_optimize_b(

ModeDecisionContext *ctx, // 模式决策上下文,包含编码参数和中间数据

int16_t txb_skip_context, // 变换块是否跳过的上下文信息

int16_t dc_sign_context, // DC系数符号的上下文信息

const TranLow *coeff_ptr, // 原始变换系数指针

const MacroblockPlane *p, // 宏块通道信息(包含量化参数等)

TranLow *qcoeff_ptr, // 量化后系数指针(输出)

TranLow *dqcoeff_ptr, // 反量化后系数指针(输出)

uint16_t *eob, // 块的非零系数结束位置(End Of Block)

const QuantParam *qparam, // 量化参数

TxSize tx_size, // 变换尺寸(如TX_4x4, TX_8x8等)

TxType tx_type, // 变换类型(如DCT, ADST等)

Bool is_inter, // 是否为inter帧(帧间预测)

uint8_t use_sharpness, // 是否使用锐化量化

uint8_t delta_q_present, // 是否存在delta量化参数

uint8_t picture_qp, // 图像级QP值

uint32_t lambda, // 率失真优化的lambda参数

int plane) // 当前处理的通道(0=Y, 1=U, 2=V)

{

// --------------------------------------------

// 初始化参数和局部变量

// --------------------------------------------

int sharpness = 0; // 锐化级别,0表示不启用

// 判断是否启用快速模式(根据不同的通道和帧类型配置)

int fast_mode = (ctx->rdoq_ctrls.eob_fast_y_inter && is_inter && !plane) ||

(ctx->rdoq_ctrls.eob_fast_y_intra && !is_inter && !plane) ||

(ctx->rdoq_ctrls.eob_fast_uv_inter && is_inter && plane) ||

(ctx->rdoq_ctrls.eob_fast_uv_intra && !is_inter && plane)

? 1 : 0;

// 获取当前变换类型的扫描顺序(如之字形扫描)

const ScanOrder *const scan_order = &av1_scan_orders[tx_size][tx_type];

const int16_t *scan = scan_order->scan; // 具体扫描顺序数组

// 计算量化缩放参数

const int shift = av1_get_tx_scale_tab[tx_size]; // 量化移位值

// 通道类型(Y/UV通道)

const PlaneType plane_type = plane;

// 变换尺寸的熵编码上下文

const TxSize txs_ctx = get_txsize_entropy_ctx_tab[tx_size];

// 变换类型分类(2D/水平/垂直)

const TxClass tx_class = tx_type_to_class[tx_type];

// 变换块的宽度对数(如4x4块bwl=2)

const int bwl = get_txb_bwl_tab[tx_size];

// 变换块的实际宽高

const int width = get_txb_wide_tab[tx_size];

const int height = get_txb_high_tab[tx_size];

// 断言检查变换尺寸参数有效性

assert(width == (1 << bwl)); // 确保宽度是2的幂次

assert(txs_ctx < TX_SIZES); // 确保熵上下文有效

// 获取变换块的系数编码代价表

const LvMapCoeffCost *txb_costs = &ctx->md_rate_est_ctx->coeff_fac_bits[txs_ctx][plane_type];

// EOB分数字节代价表

const int eob_multi_size = txsize_log2_minus4[tx_size];

const LvMapEobCost *txb_eob_costs = &ctx->md_rate_est_ctx->eob_frac_bits[eob_multi_size][plane_type];

// 计算跳过/不跳过当前块的代价

const int non_skip_cost = txb_costs->txb_skip_cost[txb_skip_context][0]; // 不跳过的代价

const int skip_cost = txb_costs->txb_skip_cost[txb_skip_context][1]; // 跳过的代价

// 计算当前EOB位置的代价

const int eob_cost = get_eob_cost(*eob, txb_eob_costs, txb_costs, tx_class);

// --------------------------------------------

// 提前退出判断:如果跳过块的代价更低,则直接返回

// --------------------------------------------

// 根据块尺寸计算提前退出阈值

int sq_size_idx = 7 - (int)svt_log2f(ctx->blk_geom->sq_size);

if (eob_cost < (int)(width height sq_size_idx * ctx->rdoq_ctrls.early_exit_th)) {

// 如果跳过代价更优,直接退出

if (skip_cost < non_skip_cost) {

return;

}

}

// --------------------------------------------

// 快速模式处理:更新EOB并快速返回

// --------------------------------------------

if (fast_mode) {

// 快速更新量化系数和EOB

update_coeff_eob_fast(eob, shift, p->dequant_qtx, scan, coeff_ptr, qcoeff_ptr, dqcoeff_ptr);

// 如果所有系数都是0,直接返回

if (*eob == 0)

return;

}

// --------------------------------------------

// 率失真参数计算

// --------------------------------------------

int rweight = 100; // 率失真权重

const int rshift = 2; // 右移位操作

// 如果启用锐化量化且存在delta QP,调整锐化参数

if (use_sharpness && delta_q_present && plane == 0) {

int diff = ctx->sb_ptr->qindex - quantizer_to_qindex[picture_qp];

if (diff < 0) { // 如果当前QP低于图像QP

sharpness = 1; // 启用锐化

rweight = 0; // 调整权重

}

}

// 计算率失真乘数(综合lambda、通道权重等)

const int64_t rdmult = (((((int64_t)lambda plane_rd_mult[is_inter][plane_type]) rweight) / 100) + 2) >> rshift;

// --------------------------------------------

// 初始化系数层级缓存

// --------------------------------------------

uint8_t levels_buf[TX_PAD_2D]; // 系数层级缓存

uint8_t *const levels = set_levels(levels_buf, width); // 设置层级指针

// 如果EOB>1,初始化层级信息(用于系数扫描)

if (*eob > 1)

svt_av1_txb_init_levels(qcoeff_ptr, width, height, levels);

// --------------------------------------------

// 率失真累积变量初始化

// --------------------------------------------

int accu_rate = eob_cost; // 累积的码率代价

int64_t accu_dist = 0; // 累积的失真代价

// --------------------------------------------

// 逆序扫描处理系数(从最后一个非零系数开始)

// --------------------------------------------

int si = *eob - 1; // 扫描索引,从EOB-1开始

const int ci = scan[si]; // 当前扫描位置的系数索引

const TranLow qc = qcoeff_ptr[ci]; // 当前量化系数值

const TranLow abs_qc = abs(qc); // 绝对值

const int sign = qc < 0; // 符号位(0正1负)

// 非零系数数量和位置记录

const int max_nz_num = 4; // 最大记录的非零系数数

int nz_num = 1; // 当前非零系数计数

int nz_ci[5] = {ci, 0, 0, 0, 0}; // 非零系数位置数组

// 根据系数绝对值大小分情况处理

if (abs_qc >= 2) {

// 处理绝对值>=2的系数(复杂情况)

update_coeff_general(

&accu_rate, &accu_dist, si, *eob, tx_size, tx_class, bwl, height,

rdmult, shift, dc_sign_context, p->dequant_qtx, scan, txb_costs,

coeff_ptr, qcoeff_ptr, dqcoeff_ptr, levels, qparam->iqmatrix);

  • -si; // 移动到前一个系数

} else {

// 处理绝对值=1的系数(简单情况)

assert(abs_qc == 1);

// 获取低层级上下文

const int coeff_ctx = get_lower_levels_ctx_eob(bwl, height, si);

// 计算系数编码代价

accu_rate += get_coeff_cost_eob(

ci, abs_qc, sign, coeff_ctx, dc_sign_context, txb_costs, bwl, tx_class);

// 计算失真差异

const TranLow tqc = coeff_ptr[ci]; // 原始系数

const TranLow dqc = dqcoeff_ptr[ci]; // 反量化系数

const int64_t dist = get_coeff_dist(tqc, dqc, shift); // 量化后的失真

const int64_t dist0 = get_coeff_dist(tqc, 0, shift); // 量化为0的失真

accu_dist += dist - dist0; // 累积失真差

  • -si; // 移动到前一个系数

}

// --------------------------------------------

// 宏定义:根据不同变换类型处理EOB前的系数

// --------------------------------------------

define UPDATE_COEFF_EOB_CASE(tx_class_literal) \

case tx_class_literal: \

for (; si >= 0 && nz_num <= max_nz_num && !fast_mode; --si) { \

update_coeff_eob( \

&accu_rate, &accu_dist, eob, &nz_num, nz_ci, si, \

tx_size, tx_class_literal, bwl, height, \

dc_sign_context, rdmult, shift, p->dequant_qtx, scan, \

txb_eob_costs, txb_costs, coeff_ptr, qcoeff_ptr, \

dqcoeff_ptr, levels, sharpness, qparam->iqmatrix); \

} \

break;

// 根据变换类型分派处理

switch (tx_class) {

UPDATE_COEFF_EOB_CASE(TX_CLASS_2D); // 2D变换

UPDATE_COEFF_EOB_CASE(TX_CLASS_HORIZ); // 水平变换

UPDATE_COEFF_EOB_CASE(TX_CLASS_VERT); // 垂直变换

undef UPDATE_COEFF_EOB_CASE

default: assert(false); // 其他类型断言失败

}

// --------------------------------------------

// 处理所有系数后的跳过决策

// --------------------------------------------

if (si == -1 && nz_num <= max_nz_num) {

// 更新跳过标志和系数

update_skip(&accu_rate, accu_dist, eob, nz_num, nz_ci, rdmult,

skip_cost, non_skip_cost, qcoeff_ptr, dqcoeff_ptr, sharpness);

}

// --------------------------------------------

// 宏定义:处理剩余简单系数(EOB之后)

// --------------------------------------------

define UPDATE_COEFF_SIMPLE_CASE(tx_class_literal) \

case tx_class_literal: \

for (; si >= 1; --si) { \

update_coeff_simple( \

&accu_rate, si, *eob, tx_size, \

tx_class_literal, bwl, rdmult, \

shift, p->dequant_qtx, scan, \

txb_costs, coeff_ptr, qcoeff_ptr, \

dqcoeff_ptr, levels, qparam->iqmatrix); \

} \

break;

// 根据变换类型分派处理

switch (tx_class) {

UPDATE_COEFF_SIMPLE_CASE(TX_CLASS_2D); // 2D变换

UPDATE_COEFF_SIMPLE_CASE(TX_CLASS_HORIZ); // 水平变换

UPDATE_COEFF_SIMPLE_CASE(TX_CLASS_VERT); // 垂直变换

undef UPDATE_COEFF_SIMPLE_CASE

default: assert(false); // 其他类型断言失败

}

// --------------------------------------------

// 处理DC系数(扫描位置0)

// --------------------------------------------

if (si == 0) { // 只剩下DC系数

int64_t dummy_dist = 0; // 临时失真变量(不实际使用)

update_coeff_general(

&accu_rate, &dummy_dist, si, *eob, tx_size, tx_class, bwl, height,

rdmult, shift, dc_sign_context, p->dequant_qtx, scan, txb_costs,

coeff_ptr, qcoeff_ptr, dqcoeff_ptr, levels, qparam->iqmatrix);

}

}


http://www.mrgr.cn/news/94024.html

相关文章:

  • c++ constraints与concepts使用笔记
  • 如何用HTML5 Canvas实现电子签名功能✍️
  • 全网最详解答OSPF基础
  • 我与DeepSeek读《大型网站技术架构》(10)- 维基百科的高性能架构设计分析
  • C# Enumerable类 之 数据分组
  • 插入排序算法优化
  • 数字电路逻辑代数 | 运算 / 定律 / 公式 / 规则 / 例解
  • 【设计模式】《设计模式:可复用面向对象软件的基础》设计模式的分类与组织
  • 类和对象(下)
  • 大语言模型-语言模型发展历程
  • ⭐算法OJ⭐链表排序【归并排序】(C++/JavaScript 实现)
  • 基于Ollama平台部署的Qwen大模型实现聊天机器人
  • ⭐算法OJ⭐经典题目分类索引(持续更新)
  • NVSHMEM介绍、InfiniBand GPUDirect、和NVshmem使用案例说明
  • Application.OnTime如何引用带参数的过程
  • 记录--有惊无险
  • 数据结构全解析:从线性到非线性,优缺点与应用场景深度剖析
  • 大模型在甲状腺良性肿瘤诊疗全流程中的应用研究报告
  • Nginx 监控方法(‌Nginx Monitoring Methods)
  • LearnOpenGL-笔记-其二