x264 码率控制中自适应量化模式 AQ mode分析

AQ mode

  1. Adaptive Quantization mode,即自适应量化模式,根据 MB 的复杂度来调整每个 MB 量化时的量化参数。该模式可以更好地将码率分配到各个宏块中,以获得更好的视频质量和压缩效果。
  2. x264 中与之相关的参数i_aq_modef_aq_strength

i_aq_mode

1. i_aq_mode取值为X264_AQ_NONE(0)X264_AQ_VARIANCE(1)X264_AQ_AUTOVARIANCE(2)X264_AQ_AUTOVARIANCE_BIASED(3)。在 validate_parameters函数中会校验,将取值锁定到[0, 3]中。0 表示禁用 AQ,1 表示方差 AQ(复杂度掩码),2 表示自动方差 AQ,3 表示偏暗场景的自动方差AQ。

在这里插入图片描述

  1. x264_param_default中设置为X264_AQ_VARIANCE(1)。在param_apply_presetpreset=ultrafast时,设置0;在param_apply_tunetune=psnr时设置X264_AQ_NONE(0)tune=ssim时设置X264_AQ_AUTOVARIANCE(2)

f_aq_strength

  1. f_aq_strength取值为 0.0~3.0,自适应量化的强度调节,设置 AQ 偏向于低细节度宏块的强度,在 validate_parameters函数中会校验,将取值锁定到[0.0, 3.0]中。
    在这里插入图片描述
  2. x264_param_default中设置为 1.0;在param_apply_tunetune=animation 时设置为 0.6,tune=gain 时设置 为 0.5,tune=stillimage 时设置为 1.2,tune=touhou 时设置为 1.3。

AQ 的起源

  • 为什么需要 AQ,x264 开发者在分析研究 VP8 之后的一段论述:

For quantization, the core process is basically the same among all MPEG-like video formats, and VP8 is no exception. The primary ways that video formats tend to differentiate themselves here is by varying quantization scaling factors. There are two ways in which this is primarily done: frame-based offsets that apply to all coefficients or just some portion of them, and macroblock-level offsets. VP8 primarily uses the former; in a scheme much less flexible than H.264′s custom quantization matrices, it allows for adjusting the quantizer of luma DC, luma AC, chroma DC, and so forth, separately. The latter (macroblock-level quantizer choice) can, in theory, be done using its “segmentation map” features, albeit very hackily and not very efficiently.
The killer mistake that VP8 has made here is not making macroblock-level quantization a core feature of VP8. Algorithms that take advantage of macroblock-level quantization are known as “adaptive quantization” and are absolutely critical to competitive visual quality. My implementation of variance-based adaptive quantization (before, after) in x264 still stands to this day as the single largest visual quality gain in x264 history. Encoder comparisons have showed over and over that encoders without adaptive quantization simply cannot compete.
Thus, while adaptive quantization is possible in VP8, the only way to implement it is to define one segment map for every single quantizer that one wants and to code the segment map index for every macroblock. This is inefficient and cumbersome; even the relatively suboptimal MPEG-style delta quantizer system would be a better option. Furthermore, only 4 segment maps are allowed, for a maximum of 4 quantizers per frame.
Verdict on Quantization: Lack of well-integrated adaptive quantization is going to be a killer when the time comes to implement psy optimizations. Overall, much worse.

AQ原理

逻辑

  • aqmode实现逻辑在函数x264_adaptive_quant_frame中完成,根据不同的i_aq_modef_aq_strength的取值来对f_qp_offsetf_qp_offset_aq进行赋值。
  • f_qp_offsetf_qp_offset_aq的值会在函数x264_ratecontrol_mb_qp中应用,在该函数中,对每个宏块在原先的 qp 基础上再加上x264_adaptive_quant_frame计算出来的 qp 偏移值。
  • f_qp_offsetf_qp_offset_aq也会应用到 mbtree 模块中,在macroblock_tree_finish函数中进一步调整f_qp_offset的取值。

源码分析

  1. 流程
  • x264_adaptive_quant_frame函数在x264_encoder_encode函数中被调用。
  • 禁用 AQ,即i_aq_mode = X264_AQ_NONEf_aq_strength = 0时,外部如果有quant_offsets时,给f_qp_offsetf_qp_offset_aq赋值,否者全部置 0;此时如果开启加权预测,对每个 MB调用ac_energy_mb来计算方差数据。
  • 开启 AQ,第一步骤根据i_aq_mode的值来分情况讨论。当i_aq_mode = 2 或 3 时,对每个 MB 调用ac_energy_mb来完成对strengthavg_adjbias_strength变量的赋值;当 i_aq_mode=1 时,变量strength根据参数f_aq_strength和一个常量相加得到。
  • 开启 AQ,第二步骤,对每个 MB 根据i_aq_mode不同的取值,得到变量qp_adj的取值;此外如果外部有quant_offsets时,还要对每个 MB 的qp_adj加上额外的值;
  • 开启 AQ,第三步骤,将qp_adj赋值给f_qp_offsetf_qp_offset_aq;
  • 最后从 SSD 计算中移除均值。
  1. 源码
void x264_adaptive_quant_frame( x264_t *h, x264_frame_t *frame, float *quant_offsets )
{/* Initialize frame stats */for( int i = 0; i < 3; i++ ){frame->i_pixel_sum[i] = 0;frame->i_pixel_ssd[i] = 0;}/* Degenerate cases */if( h->param.rc.i_aq_mode == X264_AQ_NONE || h->param.rc.f_aq_strength == 0 ){/* Need to init it anyways for MB tree */if( h->param.rc.i_aq_mode && h->param.rc.f_aq_strength == 0 ){if( quant_offsets ){for( int mb_xy = 0; mb_xy < h->mb.i_mb_count; mb_xy++ )frame->f_qp_offset[mb_xy] = frame->f_qp_offset_aq[mb_xy] = quant_offsets[mb_xy];if( h->frames.b_have_lowres )for( int mb_xy = 0; mb_xy < h->mb.i_mb_count; mb_xy++ )frame->i_inv_qscale_factor[mb_xy] = x264_exp2fix8( frame->f_qp_offset[mb_xy] );}else{memset( frame->f_qp_offset, 0, h->mb.i_mb_count * sizeof(float) );memset( frame->f_qp_offset_aq, 0, h->mb.i_mb_count * sizeof(float) );if( h->frames.b_have_lowres )for( int mb_xy = 0; mb_xy < h->mb.i_mb_count; mb_xy++ )frame->i_inv_qscale_factor[mb_xy] = 256;}}/* Need variance data for weighted prediction */if( h->param.analyse.i_weighted_pred ){for( int mb_y = 0; mb_y < h->mb.i_mb_height; mb_y++ )for( int mb_x = 0; mb_x < h->mb.i_mb_width; mb_x++ )ac_energy_mb( h, mb_x, mb_y, frame );}elsereturn;}/* Actual adaptive quantization */else{/* constants chosen to result in approximately the same overall bitrate as without AQ.* FIXME: while they're written in 5 significant digits, they're only tuned to 2. */float strength;float avg_adj = 0.f;float bias_strength = 0.f;if( h->param.rc.i_aq_mode == X264_AQ_AUTOVARIANCE || h->param.rc.i_aq_mode == X264_AQ_AUTOVARIANCE_BIASED ){float bit_depth_correction = 1.f / (1 << (2*(BIT_DEPTH-8)));float avg_adj_pow2 = 0.f;for( int mb_y = 0; mb_y < h->mb.i_mb_height; mb_y++ )for( int mb_x = 0; mb_x < h->mb.i_mb_width; mb_x++ ){uint32_t energy = ac_energy_mb( h, mb_x, mb_y, frame );float qp_adj = powf( energy * bit_depth_correction + 1, 0.125f );frame->f_qp_offset[mb_x + mb_y*h->mb.i_mb_stride] = qp_adj;avg_adj += qp_adj;avg_adj_pow2 += qp_adj * qp_adj;}avg_adj /= h->mb.i_mb_count;avg_adj_pow2 /= h->mb.i_mb_count;strength = h->param.rc.f_aq_strength * avg_adj;avg_adj = avg_adj - 0.5f * (avg_adj_pow2 - 14.f) / avg_adj;bias_strength = h->param.rc.f_aq_strength;}elsestrength = h->param.rc.f_aq_strength * 1.0397f;for( int mb_y = 0; mb_y < h->mb.i_mb_height; mb_y++ )for( int mb_x = 0; mb_x < h->mb.i_mb_width; mb_x++ ){float qp_adj;int mb_xy = mb_x + mb_y*h->mb.i_mb_stride;if( h->param.rc.i_aq_mode == X264_AQ_AUTOVARIANCE_BIASED ){qp_adj = frame->f_qp_offset[mb_xy];qp_adj = strength * (qp_adj - avg_adj) + bias_strength * (1.f - 14.f / (qp_adj * qp_adj));}else if( h->param.rc.i_aq_mode == X264_AQ_AUTOVARIANCE ){qp_adj = frame->f_qp_offset[mb_xy];qp_adj = strength * (qp_adj - avg_adj);}else{uint32_t energy = ac_energy_mb( h, mb_x, mb_y, frame );qp_adj = strength * (x264_log2( X264_MAX(energy, 1) ) - (14.427f + 2*(BIT_DEPTH-8)));}if( quant_offsets )qp_adj += quant_offsets[mb_xy];frame->f_qp_offset[mb_xy] =frame->f_qp_offset_aq[mb_xy] = qp_adj;if( h->frames.b_have_lowres )frame->i_inv_qscale_factor[mb_xy] = x264_exp2fix8(qp_adj);}}/* Remove mean from SSD calculation */for( int i = 0; i < 3; i++ ){uint64_t ssd = frame->i_pixel_ssd[i];uint64_t sum = frame->i_pixel_sum[i];int width  = 16*h->mb.i_mb_width  >> (i && CHROMA_H_SHIFT);int height = 16*h->mb.i_mb_height >> (i && CHROMA_V_SHIFT);frame->i_pixel_ssd[i] = ssd - (sum * sum + width * height / 2) / (width * height);}
}
  1. aqmode 的应用
  • 在函数x264_ratecontrol_mb_qp中将f_qp_offsetf_qp_offset_aq赋值给变量f_qp_offset,再加到变量 qp 上,作为宏块的 qp 值。
int x264_ratecontrol_mb_qp( x264_t *h )
{x264_emms();float qp = h->rc->qpm;if( h->param.rc.i_aq_mode ){/* MB-tree currently doesn't adjust quantizers in unreferenced frames. */float qp_offset = h->fdec->b_kept_as_ref ? h->fenc->f_qp_offset[h->mb.i_mb_xy] : h->fenc->f_qp_offset_aq[h->mb.i_mb_xy];/* Scale AQ's effect towards zero in emergency mode. */if( qp > QP_MAX_SPEC )qp_offset *= (QP_MAX - qp) / (QP_MAX - QP_MAX_SPEC);qp += qp_offset;}return x264_clip3( qp + 0.5f, h->param.rc.i_qp_min, h->param.rc.i_qp_max );
}
  • 此外在 mbtree 中模块也会跟 f_qp_offset 有联系,如果mbtree改变了量化器,我们需要重新计算帧成本,而不需要重新运行lookahead;在slicetype_frame_cost_recalculate函数中f_qp_offset_aqf_qp_offset用来计算宏块代价;在macroblock_tree_finish函数会进一步改变f_qp_offset的值,在macroblock_tree函数里有处理。
  • mbtree 的应用也会影响 VBV 模块,在函数vbv_frame_cost中有所体现。
  • 因此,AQ、mbtree、VBV 都会对最终的码率和质量产生影响和作用,属于码率控制的重要部分。

参考

  • x264

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/449550.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

367. Valid Perfect Square(有效的完全平方数)

题目描述 给你一个正整数 num 。如果 num 是一个完全平方数&#xff0c;则返回 true &#xff0c;否则返回 false 。 完全平方数 是一个可以写成某个整数的平方的整数。换句话说&#xff0c;它可以写成某个整数和自身的乘积。 不能使用任何内置的库函数&#xff0c;如 sqrt(…

C语言项目---贪吃蛇

目录 一 、知识铺垫1.win32API介绍 二、贪吃蛇的数据结构的设计1.整体框架2.初始化界面3.贪吃蛇的运行4.游戏的退出 三、整体代码 一 、知识铺垫 贪吃蛇涉及的知识&#xff1a;C语言函数、枚举、结构体、动态内存管理、预处理指令、链表、win32API等 1.win32API介绍 Windows…

关于破解IDEA后启动闪退的问题

问题描述&#xff1a;2023.1启动不了&#xff0c;双击桌面图标&#xff0c;没有响应。 解决办法&#xff1a; 打开C:\Users\c\AppData\Roaming\JetBrains\IntelliJIdea2023.1\idea64.exe.vmoptions 这个文件。 内容如下所示&#xff1a; 删除红框的数据以后&#xff0c;再登录…

「递归算法」:Pow(x,n)

一、题目 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出&#xff1a;9…

使用pyinstaller打包tkinter程序

主要问题&#xff1a; &#xff08;1&#xff09;如何同时打包多个python文件 &#xff08;2&#xff09;打包过程中有缺失的包怎么处理 &#xff08;3&#xff09;如何解决打包程序过大的问题 以上三个问题是使用pyinstaller打包python文件常见的问题&#xff0c;我将以自己…

Django的web框架Django Rest_Framework精讲(二)

文章目录 1.自定义校验功能&#xff08;1&#xff09;validators&#xff08;2&#xff09;局部钩子&#xff1a;单字段校验&#xff08;3&#xff09;全局钩子&#xff1a;多字段校验 2.raise_exception 参数3.context参数4.反序列化校验后保存&#xff0c;新增和更新数据&…

【数据库】创建索引的注意事项

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;数据库 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 在数据库设计和优化中&#xff0c;索引的合理使用是提高查询性能和加速数据检索的关键因素之一。通过选…

关于华为应用市场上架,申请权限未告知目的被驳回问题的简单处理方式

关于华为应用市场上架过程中出现的【您的应用在运行时&#xff0c;未同步告知权限申请的使用目的&#xff0c;向用户索取&#xff08;存储、拍照&#xff09;等权限&#xff0c;不符合华为应用市场审核标准。】 使用方式&#xff1a; 1、引入 import permision from "/m…

使用MATLAB驱动USRP-N320实现OFDM自收自发

文章目录 前言一、收发代码二、截取一帧 OFDM三、执行主函数四、运行结果五、资源自取 前言 本文作为实验结果记录及测试&#xff0c;方便后面回顾所做的工作。本文基于一台电脑和一台 USRP 设备实现了 OFDM 自发和自收功能 一、收发代码 ofdm_tx_rx_test.m 核心代码&#x…

Linux 系统开始配置

文章目录 备份源为root 设置密码安装基本工具切换root 用户删除snap从 Ubuntu 移除 Snap 后使用 deb 文件安装软件商店和 Firefox在 Ubuntu 系统恢复到 Snap 软件包总结 删除 vim安装neovim在线安装neovim压缩安装neovim安装lazyvim安装剪切板 安装qt配置 Qt 环境不在sudoers文…

预处理详解(上)

⽬录&#xff1a; 1. 预定义符号 2. #define定义常量 3. #define定义宏 4. 带有副作⽤的宏参数 5. 宏替换的规则 6. 宏函数的对⽐ 7. #和## 8. 命名约定 9. #undef 10. 命令⾏定义 11. 条件编译 12. 头⽂件的包含 13. 其他预处理指令 正⽂开始 1. 预…

Facebook未来展望:社交媒体的下一个篇章

社交媒体一直是连接人与人之间的纽带&#xff0c;而Facebook则一直在推动这一领域的发展。随着科技不断演进和社会需求的不断变迁&#xff0c;Facebook正积极筹谋社交媒体的下一个篇章。本文将深入剖析Facebook的未来展望&#xff0c;探讨其在社交媒体领域所迎接的新时代。 1. …