☰
AVS3参考软件HPM4.0源码阅读指南:从编译到掌握核心模块
2026/10/4 8:51:35 网站建设 项目流程

两年前我第一次打开AVS3的HPM4.0参考软件源码时,第一反应是:这比我预想中大太多了。几十个目录、上百个源文件、几十万行代码,而且没有注释的地方占了大半。更麻烦的是,当时网上能搜到的资料基本都停留在“AVS3比HEVC节省多少码率”“AVS3是面向8K的新一代标准”这类层面,真正告诉你“这些性能是从哪一行代码里长出来的”的文档几乎没有。这篇文章想聊的,就是我一个人把HPM4.0从“能编译”磨到“能看懂”、再磨到“能改着做实验”的完整路径。

如果你是刚接触AVS3的研究生、从H.264/HEVC转过来想快速上手的工程师,或者单纯想搞明白参考软件内部到底怎么工作的爱好者,这篇东西应该能帮你省掉不少瞎转悠的时间。它不会逐行给你讲代码,但会给你一张地图——有了地图,读代码就只是按图索骥的事。

1. 先别急着读代码:HPM4.0在AVS3生态里的定位

很多人拿到HPM4.0源码后的第一个动作是双击打开某个.cpp文件从头开始读,这基本等于拿到一本字典然后决定从A字母背到Z字母,不是不行,但效率极低。在碰代码之前,我们得先搞清楚参考软件在整个AVS3生态里到底扮演什么角色。

AVS3标准文档描述的是一套“抽象的语法和解码规则”——它规定了码流长什么样、解码器应该怎么解释这串比特。至于编码器怎么把原始像素变成这串比特,标准文档基本不管,那是编码器的自由发挥空间。而参考软件HPM4.0,是AVS工作组发布的一份“既能编码又能解码”的可执行参考实现。它的意义有两个:对解码器来说,它是检验码流合法性的基准;对研究者来说,它是新算法落地的试验田。

这里要解释一下HPM这几个字母。如果你经常逛AVS相关的代码仓库,会发现参考软件有很多系列:RD系列、HPM系列、还有后来的各种分支。HPM是High Performance Mode的意思,翻译过来就是“高性能模式”,HPM4.0是这个模式系列里的一个具体版本配置。它跟RD系列最大的区别在于,HPM里默认开启的工具组合更“激进”,以编码效率优先,不太在意编码耗时。简单说,同一条视频序列喂进去,HPM4.0跑出来的码率更低、质量更好,代价是编码时间感人——我曾经拿一个1080p的短序列测过,单帧编码时间能用“秒”来计。这不是bug,这是设计取向。

所以你在读代码的时候,心態首先要摆正:HPM4.0是一份为了“验证标准工具集性能上限”而存在的代码,不是一份为了“工业界实时编码”而优化的代码。很多你在论文里看到的工具,比如扩展四叉树划分、解码端帧内模式推导、仿射运动补偿,在HPM4.0里都是以“默认开启”或“配置可开”的形式存在的。你要做的事情,是在这份代码里找到它们各自住在哪、跟邻居怎么通信、为什么这么设计。

读参考软件还有一个隐性好处:它是标准文档的“唯一无歧义说明书”。标准文档里一句话“对参考像素进行滤波”,可能对应代码里几十行你对齐半天都搞不清逻辑的实现。代码不会说谎,哪怕它的变量命名再随意,跑起来是什么行为就是什么行为。所以我的建议是:标准文档要备着,但遇到理解分歧时,以代码实际行为为准。

如果你手里的版本号跟我这里说的细节对不上,比如函数名略有出入、配置文件选项不一样,不用慌,参考软件迭代很快,不同tag之间的代码差异经常存在,你以自己那份源码为基准就行。

2. 环境准备:让HPM4.0先跑起来再谈阅读

在读任何代码之前,先让它编译通过、跑出码流、再跑回解码出重建视频,这步一定要先做。为什么?因为只有当你能亲手验证“代码确实在干活”,后续你才敢在关键位置下断点、改参数、观察行为变化。一个永远处于“正在编译”状态的工程,你是没有办法建立信心的。

先说获取源码。HPM4.0并没有像一些开源项目那样挂在一个固定官网下载,通常是通过AVS工作组的代码仓库获取,或者在各院校、研究机构分享的镜像里找到。拿到手后,先看顶层目录结构。AVS3参考软件的代码组织方式跟HM、VTM这些编码器参考软件是一脉相承的,大致会分为这样几个部分:编码器应用层(类似TAppEncoder)、解码器应用层(类似TAppDecoder)、公共库/核心算法库(类似TLibEncoder和TLibCommon)、以及数据/配置文件目录。

编译之前,建议先检查一下你的CMake版本和编译器版本。HPM4.0这个年代的项目对C++标准的要求不算苛刻,但老版本编译器遇到新代码照样会报一堆“语法错误”,其实不是代码错,是编译器太老。我自己在Ubuntu上用的是gcc 7.5以上的版本,Windows上用的VS2019,都能顺利编过。如果你是第一次编译,直接用CMake生成Makefile或者VS工程就行,一般不需要额外装第三方依赖库——这一点比很多开源项目省心。

编译常见失败之一:找不到某个头文件,或者报“xxx未定义”。这类问题大概率是某些工具没有默认开启,源码里的条件编译宏被顶掉了。解决办法不是硬改代码,而是去查CMakeLists.txt里跟这个宏相关的选项,重新配置后再编。

编译通过之后,找一个标准测试序列来跑。AVS3常见的测试序列是YUV格式,比如Class A到Class E的各类分辨率。如果你是第一个晚上就想看到效果,别拿高分辨率跑,选一个352x288或者416x240的小序列,几帧就行。命令行大致长这样:

./hpm_encoder -c encoder.cfg

配置文件encoder.cfg里最重要的几项是:

  • InputFile:输入YUV文件的路径
  • SourceWidth、SourceHeight:输入图像尺寸
  • FramesToBeEncoded:编码帧数
  • FrameRate:帧率
  • IntraPeriod:GOP里的I帧间隔,-1表示全I帧
  • QP:量化参数,越大码率越低、质量越差

如果你跑的是全I帧(IntraPeriod=-1),那输出就是一个帧内编码码流,可以用来验证帧内预测那条链路;如果想跑带运动搜索的,就得配置成IPPP或者IBBB的结构。第一次跑通建议用全I帧,因为链路短,出问题时好排查。

跑通编码之后,记得把解码器也跑一遍。参考软件的解码器通常是一个单独的可执行文件,命令行格式一般是:

./hpm_decoder -b output.bin -o rec.yuv

解码出来的rec.yuv跟原始输入YUV做一下PSNR对比,数值正常(比如QP=32时PSNR在35dB左右),说明整个编码解码闭环是通的。到这步,环境准备才算真正完成。

调试版本和Release版本的选择也值得说一句。你阅读代码期间,建议编一个Debug版或者RelWithDebInfo版,这样能在函数里下断点、看变量值。但Debug版跑起来是真的慢,尤其HPM这类高性能模式配置,一帧跑几十秒很正常。我的做法是同时在build目录下编两个版本:一个Debug版用来跟踪逻辑,一个Release版用来跑实验拿数据。不要指望一个版本解决所有问题。

3. 主调用链:从main函数到图像压缩

环境通了之后,整个人就有点底气了。接下来要做的不是随便点开一个文件开读,而是沿着“入口 -> 配置 -> 编码一帧 -> 编码一个块”这条主干走一遍,把整棵树的骨架摸出来。

参考软件的程序入口一般在编码器应用层,一个类似encmain.cpp的文件里。从main函数进去,你会看到它做了几件事:解析命令行参数、创建编码器实例、初始化编码器、然后进入一个循环不断喂图像进去。这个循环是编码器的主循环:

  1. 读一帧YUV数据;
  2. 调用编码器的encode函数,传入帧数据;
  3. encode内部会先做帧级初始化,然后按光栅扫描顺序遍历这一帧的所有CTU(编码树单元);
  4. 每个CTU进入递归的xCompressCU函数,开始划分和模式决策;
  5. 编码完一帧后,输出码流,回到循环读下一帧。

如果你在xCompressCU这个函数入口下一个断点,你会惊恐地发现这个函数会被调用几万次——别慌,那恰恰说明你摸到了整棵决策树的根部。

xCompressCU是什么?它是编码器里最核心的递归函数,对每个编码单元做“划分还是不划分、用什么预测模式”的决策。它的逻辑大体是:

  • 先尝试把当前块当做一个整体进行编码,计算率失真代价(RD Cost);
  • 再尝试将它划分成若干子块(具体子块形状由划分结构决定),对每个子块递归调用xCompressCU;
  • 比较“不划分”和“划分”的代价,更小的胜出,决定最终的编码树结构。

这个递归在代码里通常表现为:第一次调用时传入的是整个CTU,然后函数内部根据划分标志,对子区域逐一递归调用自身。理解了这个递归,你基本就理解了整个编码器为什么要这样组织代码——因为码流里本来就存的是一棵四叉树兼容其他划分的树结构,代码只是把这棵树的构建过程用递归实现了。

读完主干之后,你再看其他模块,视角会完全不一样。帧内预测不是在某个角落里独立存在的代码,它是在xCompressCU决策“这个块用帧内模式”时被调用的工具函数;帧间预测也是,只是在决策“用帧间模式”时被调用的另一套工具。所以主线永远是xCompressCU,其他全是它的支线。

这里还要提醒一点:参考软件的调用链特别深,一个函数套一个函数,很容易跟丢。我的习惯是每追一个调用就记一笔笔记,画一画调用关系。不需要画得很漂亮,自己能看懂就行。比如:

encode(帧) -> xCompressCU(CTU) -> xCheckRDCostIntra(当前CU) -> estIntraPredQT(遍历预测模式) -> predIntraAng(实际做预测) -> xTransformAndQuantize(变换量化) -> xCheckRDCostInter(当前CU) -> xEstimateMv(运动估计)

这张草图就是你读代码的导航图。

4. 数据结构地图:CTU、CU、PU、TU之间的关系

读编码器代码,最容易让人崩溃的不是算法多复杂,而是数据结构之间绕来绕去的指针关系。AVS3参考软件里,几个高频出现的类的名字大概是:CodingUnit(编码单元)、PredictionUnit(预测单元)、TransformUnit(变换单元)、以及对应的CTU根节点。搞懂它们之间的关系,等于拿到了整个代码库的地图。

先打个生活化的比方。CTU就像一整块土地,编码器决定把它切成几块地皮,每块地皮是一个编码单元CU;每块地皮上怎么盖房子,是预测单元PU的事;房子内部怎么装修,是变换单元TU的事。这三者并不是完全独立的——AVS3里有联合划分的概念,PU和TU的分割形状经常是从CU的划分里继承下来的,具体继承规则在代码里对应着一张张查表函数和条件分支。

在代码里,通常每个CTU会有一个根节点对象,里面挂着一组子节点指针。每个子节点指向一个CU,CU内部又通过firstPU、nextPU这样的指针把所有预测单元串起来;变换单元之间也有类似的链表关系。你在调试时打印一个CTU的指针结构,看到的其实就是一棵树:根是CTU,枝干是嵌套的CU划分,叶子是PU和TU。

这块有个特别容易看晕的地方:参考软件里同一个功能往往有多个入口。比如“获取当前CU的宽度高度”,你可能会在至少三四个类里看到类似的成员函数或局部变量。这是因为不同模块对数据的访问角度不同:帧内预测关心的是参考像素范围,变换量化关心的是残差块尺寸,熵编码关心的是语法元素的上下文。同一个CU,在不同阶段会被不同模块以不同方式“切”开来看。所以读的时候不要试图找一个“万能数据结构”,而是要顺着当前模块的视角去找对应的访问方式。

我在读这块时踩过一个很实在的坑:想当然地认为PU的划分方式跟HEVC里一样,结果对着代码找半天没找到预想中的“对称划分”标志。后来才意识到AVS3的划分体系比HEVC复杂不少,除了常规的四叉树(QT)划分,还引入了扩展四叉树(EQT)、二叉树(BT)和三叉树(TT)等划分方式。这些划分方式在代码里通常体现在划分标志位、划分类型枚举值以及对应的递归子块尺寸计算上。你如果带着HEVC的固有印象去读,很容易把自己绕晕。

我的建议是:第一步,把CTU怎么变成CU的整个过程画出来,包括每个划分标志位对应的子块宽高;第二步,搞清楚PU怎么从CU继承划分,它携带哪些与预测相关的信息(帧内模式号、运动矢量、参考帧索引等);第三步,搞明白TU的划分边界在哪些情况下会跟PU不一致。这三步做完,你基本就能自如地“按图索骥”了——看到一个算法模块,你能迅速判断它作用在哪个结构上、访问哪些字段。

如果嫌代码里直接跳转太累,可以在调试器里观察。比如在xCompressCU里加个断点,用调试器看当前CodingUnit对象的成员变量,你会直观地看到宽高、深度、划分标志、预测模式这些字段的变化。这种观察比干读代码学得快得多。

5. 帧内预测:沿着RDO这条路走一遍

在主干和数据结构都有了大致概念之后,建议挑一个具体模块深入进去,我推荐从帧内预测入手。为什么选它?因为帧内预测不涉及运动估计那套巨复杂的搜索逻辑,核心就一件事:从周围已重建像素预测当前块,然后比较各种预测模式哪个更好。逻辑链路短,却贯穿了预测、变换、量化、率失真优化这些核心机制,性价比极高。

帧内预测的入口通常是xCheckRDCostIntra这类函数。它干的事情是:对当前CU遍历所有候选帧内预测模式,计算每种模式的率失真代价,选出最优模式。AVS3的帧内预测候选模式跟HEVC那一套很不一样,除了Planar、DC和传统角度模式外,还有更多细分的角度模式,以及一些解码端推导工具,比如DIMD(解码端帧内模式推导)和TIMD(模板帧内模式推导)。不过HPM4.0里不是所有工具在所有配置下都默认开,需要看配置开关。

在这一函数内部,流程大体是这样:

  • 准备参考像素。预测不是凭空算的,它要用当前块左侧和上方的已重建像素作为参考。代码里会先把这些像素拷贝到一个缓冲区,可能还会做滤波平滑。这一步对应标准里的“参考像素滤波”;
  • 对每种候选模式调用实际的预测函数,生成预测像素块;
  • 计算原始像素与预测像素的残差,然后交给变换量化模块;
  • 用编码残差所需的比特数和失真度算出RD Cost,所有模式比完后取最小值对应的模式。

你可能会问:为什么帧内预测的代码里会牵扯到变换量化?因为率失真代价里的“率”,指的是把残差真正编码成比特之后的花费,如果不走到变换量化就估不准。这也是为什么参考软件跑得慢——它为了精确衡量代价,宁愿把后续的编码流程提前走一遍。

我个人觉得,在帧内预测这条链上,最值得停下来多看一眼的是“参考像素准备”那一段。代码不长,但坑不少:边界情况(当前块在图像边缘时左侧或上方没有像素怎么办)、参考像素的滤波强度怎么选择、不同预测模式对参考像素的依赖差异。这些细节在标准文档里往往是一句话带过,但真到优化算法或者硬件实现时,全是要命的细节。

读帧内预测时,有个小技巧特别管用:把预测模式的编号和像素重建结果绑定起来看。比如你用一个很小的测试序列,在某个CU上打印每种模式对应的预测像素块,你会很直观地看到角度模式为什么能压住纹理方向的残差,Planar模式为什么在平滑区域表现好。这种感性认识,是纯看代码得不到的。

另一个建议是顺手给代码画一条“数据流”:原始像素 -> 参考像素 -> 预测像素 -> 残差 -> 变换系数 -> 重建像素。每到一个节点,就找到对应的函数和数据结构,标记下来。这条数据流不仅适用于帧内预测,整份代码都是沿着这条流组织的。摸透了它,你甚至能猜到一个陌生函数大概在流的哪个位置。

6. 帧间预测:运动估计、AMVP与Merge模式

帧间预测是编码器里代码量最大、逻辑最绕的部分,但读它的方法其实跟帧内预测一样:沿着决策链走,搞清楚每一步在比较什么、选什么。HPM4.0里的帧间预测,核心要解决的问题是:给定当前块,在参考帧里找到一块最相似的区域,把两者的位移用运动矢量(MV)表示出来,并对运动信息本身做编码。

在xCompressCU的决策树里,帧间预测的入口一般是xCheckRDCostInter。它内部会做几个关键步骤:

第一步是运动估计。参考软件里通常有个专门的运动估计函数,比如xEstimateMv一类的存在。它先做一个整像素搜索,在搜索范围内找到匹配代价最低的位置,然后在整像素附近做亚像素精化(比如1/2像素、1/4像素甚至1/8像素的插值搜索)。AVS3对运动矢量的精度支持包括整数、半像素、四分之一像素等不同档位,具体用哪个档位,由AMVR(自适应运动矢量精度)机制决定。

第二步是运动信息的表示。AVS3支持多种运动信息编码方式,最基础的两种就是AMVP(先进运动矢量预测)和Merge模式。AMVP的思路是:先从一个候选列表里选出一个预测运动矢量(MVP),然后编码“当前MV与MVP的差值”,这样能省比特;Merge模式的思路更极端:直接从候选列表里选一个运动信息作为当前块的运动信息,几乎不用编码差值。

第三步是运动补偿。选定了运动信息后,从参考帧里把对应位置的像素块“搬”过来,作为预测块。这一步看起来简单,但AVS3里还有其他工具叠加在这个基础上,比如仿射运动补偿(Affine),它允许块内不同位置有不同的运动矢量,适合旋转、缩放等复杂运动;还有基于子块的时间运动矢量预测(SbTMVP)这类更高级的工具。在代码里,这些工具各有各的入口函数和对应的候选构建逻辑。

读这块的时候,我特别想强调一个词:候选列表。AMVP有AMVP的候选列表,Merge有Merge的候选列表,每个列表的构建顺序、剔除重复候选的规则、候选取数上限,都是标准里明确规定的,也都一五一十地写在了代码里。我看过很多人在读帧间预测代码时,一头扎进运动估计的搜索循环里,结果把候选列表这块给跳过去了。这是本末倒置的——候选列表决定了运动信息的竞争范围,它的设计逻辑反而更值得理解。

我的建议是:先从Merge模式读起,它没有运动搜索,逻辑更短,方便你理解“候选列表 -> 选最优 -> 编码索引”这个套路。然后回到AMVP,看它多出来的“预测差值编码”是怎么实现的。最后再去看运动估计的搜索循环,那时候你已经不会被绕晕了。

如果你是从HEVC那一代参考软件转过来的,这里的代码风格你会觉得似曾相识,但细节变化很大。AVS3的Merge候选列表类型更多、还包含了历史候选、成对平均候选等构成方式,每个候选类型的优先级在代码里都有明确的插入顺序。读懂这个顺序,你就能理解为什么相同运动信息在不同位置编码出来的比特数不一样。

7. 变换、量化与重建环路:从残差到系数再到重建像素

预测做完之后,剩下的殘差要去掉空间冗余,这一步就是变换量化。在很多介绍视频编码的文章里,“变换+量化”通常是一笔带过的,好像它们就是DCT加除法。但真到代码层面,你会发现处处是坑,而且这些坑直接影响最终输出码流。

先说变换。AVS3的变换以块为单位,先把残差像素块从空间域变到频率域,得到变换系数。HPM4.0里不同尺寸的块会用不同的变换核,比如DCT-II、DST等,代码里通常以查表的形式把变换矩阵预存起来,调用时按块尺寸选表。我第一次在代码里看到那几十个矩阵常量时,一度以为自己在看某本数学手册。

在变换代码里,有一个细节特别值得看:变换是否支持跳过。某些模式下,比如帧内预测质量已经很好的块,可能残差非常小,这时直接对残差做变换反而产生大量非零系数,不利于压缩。因此参考软件里有变换跳过(Transform Skip)机制,代码里对应的分支就是在那次率失真比较里给“跳过变换”加一个候选。这类开关在HPM4.0里不是默认全开的,配置开关一关,代码里对应的分支就绕过去了。

量化部分的作用是把连续范围的变换系数映射到有限的量化级别上。代码里量化通常用一个查表加移位的方法实现,避免浮点运算。对应的反量化在解码器里也有,实现思路是逆运算。这里有一个容易踩的坑:正反量化的精度不匹配。参考软件的设计目标是“编码端算出来的系数,解码端能无损还原”,所以正反量化必须严格对齐。你如果在代码里改动量化步长解析、移位数量或者舍入方式,非常容易造成编解码不一致,然后输出一堆花屏。我踩过一次之后,就养成了“动量化必做编解码闭环验证”的习惯。

重建环路则是把预测像素和量化反变换后的残差加起来,得到重建像素。重建像素会存入参考缓冲,供后续块做帧内预测或帧间预测时取参考像素。所以你在代码里会看到,编码器每编码完一个块,会立刻把重建像素写回某个缓冲区。这个“边编码边重建”的流程,是视频编码器跟普通图像压缩器最大的区别之一——解码器未来就是靠着同样顺序的重建像素工作的,任何顺序偏差都会导致飘移。

读这部分代码时,我建议把重点放在“系数数据的流转”上:残差进变换得到系数、系数进量化得到量化系数、量化系数被熵编码写入码流的同时、也走了反量化和反变换变成重建残差、最后跟预测像素相加进重建缓冲。每一步的缓冲区和中间变量在代码里都有对应物,理清这个流转,你对整个编码器的理解会突然变得非常立体。

另外别忘了扫描顺序。量化系数在写入码流前不是随便排的,要按特定的扫描顺序排列,让零系数的分布更有利于压缩。AVS3里的扫描方式会根据帧内预测模式的方向做调整,这部分代码看逻辑很简单,但它跟帧内预测的方向性绑在一起,理解它需要回看帧内预测那一章。这种“看似独立的模块其实互相咬合”的现象,在参考软件里到处都是。

8. 几个实操里最容易踩的坑

到这里,代码阅读的主线基本走完了,但我知道你在实际操作中一定会碰到一堆跟“读代码”本身无关、却足以让你摔跟头的问题。我把这几年在HPM4.0上踩过的坑集中写一下,希望能帮你省点时间。

第一个坑:配置文件和代码宏对不上。HPM4.0里有大量条件编译宏,很多工具是“编译期决定存不存在,配置期决定开不开”。如果你改了一个配置选项却发现行为没变,大概率是那个工具对应的代码被你手里这个build版本给裁剪掉了。排查方法很简单:在源码里搜配置项对应的宏名,确认它被定义,并且对应的条件编译分支真的在编译范围内。看到宏定义不等于宏生效,还得看它有没有被#ifdef包住。

第二个坑:Debug和Release行为不一致。参考软件里偶尔会有依赖未初始化内存的行为,Debug版会把未初始化的变量置成固定值,看起来一切正常;Release版编译器优化后,未初始化变量可能是任何值,结果码流完全不可复现。如果你在改代码过程中发现“我啥都没动,但跑两次结果不一样”,先怀疑未初始化变量,再怀疑多线程竞争。HPM4.0的编码器在多线程配置下,输出不一定可复现——这是参考软件的家常便饭,不代表你的代码改错了。

第三个坑:改代码后忘记做编解码闭环验证。参考软件的编码器和解码器是两个独立程序,你改了编码端的语法元素写码,但忘了同步改解码端,就会出现“编码器跑得挺好,解码器解出一片花”的惨剧。我的习惯是,只要动了语法元素、变换量化、预测模式相关代码,立刻编解码闭环跑一个极小的序列,哪怕只有一帧,也要验证重建YUV和源YUV的PSNR在正常水平。

第四个坑:在错误的地方断点。参考软件的调用次数极其恐怖,有些函数一帧里会被调用几十万次。你要是随便在某个热点函数上下断点,会愣在调试器前面怀疑人生。正确做法是加条件断点,比如只对某个特定坐标的CU、某种特定尺寸的块触发断点。调试器支持条件表达式的话,把条件设成cu.csx == 128 && cu.csy == 64这种坐标判断,能极大缩小排查范围。

第五个坑:忽略编码器的“非确定性”。HPM4.0默认配置下,编码器内部可能会有依赖哈希或者未定义遍历顺序的地方,导致同一份配置跑两遍,中间某几帧的比特数略有波动。这通常不影响性能结论,但如果你在做“改前改后对比实验”,一定不要拿单次运行的码率当基准,至少跑三遍取中位数,或者干脆把多线程关掉(很多参考软件的重复性问题跟多线程有关),让编码器以单线程确定性的方式输出。

从我自己的体会来说,读HPM4.0代码最磨人的阶段不是刚开始,而是“觉得自己已经懂了、一改就翻车”的中间段。代码规模摆在那里,各种工具之间的交互细节多到超出预期。但反过来讲,如果你能把参考软件里某一套工具从前到后读通、改通、验证通,你对整个AVS3标准的理解会远超那些只读标准文档的人——因为代码里藏着标准文档所有没说出口的实现细节。

最后再分享一个我一直在用的小技巧:每读完一个模块,就试着在某个不起眼的位置改一个参数,比如把某个候选列表长度上限从5改成4,或者把某个搜索范围的初始值扩大一倍,然后跑一次编码,观察码率和PSNR的变化。这个过程会逼着你去确认“这个参数到底影响哪条链路、通过什么机制影响”,比任何阅读笔记都更能检验你是不是真读懂了。如果改动之后结果跟你预想的一致,恭喜你,这块代码你已经拿下了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询