此系列是为了记录自己学习VTM10.0的过程和锻炼表达能力,主要是从解码端进行入手。由于本人水平有限,出现的错误恳请大家指正,欢迎与大家一起交流进步。

上一篇博客(VTM10.0代码学习4)讲述了将语法元素的处理从CTU级划分到CU级,并在其2.5节提到coding_unit()这个函数。我原本以为能用一篇博客讲述完coding_unit(),但实际需要两篇,错估了自己的能力(真不想承认啊,这是我太过年轻而犯下的错)。CU级语法元素处理的主要部分可以分为预测模式的参数和变换残差,本篇博客先讲述coding_unit()这个函数,然后再详解一下如何从码流获取预测模式的参数,至于变换残差的部分就留到下一篇博客中。

1. coding_unit()

在这里画了一张流程图,希望能帮大家更好地理解

CodingStructure& cs = *cu.cs;
PredictionUnit&    pu = cs.addPU(cu, partitioner.chType);//向cs类加入当前PU

addPU:与先前提到的addCU类似,但稍微有些不同,函数的写法允许一个CU对应多个PU。但是在H.266中并不需要一个CU划分成多个PU,即使有基于子块的帧间预测,但是在语法元素传输中,一个CU还是对应一个PU就够了。

// skip flag
// 在当前CU存在亮度分量的前提下,满足当前slice不是I slice或所在CLVS允许开启IBC
if ((!cs.slice->isIntra() || cs.slice->getSPS()->getIBCFlag()) && cu.Y().valid())
{cu_skip_flag( cu );//获取cu_skip_flag和skip情况下的pred_mode_ibc_flag的获取
}// skip data
if( cu.skip )
{cu.colorTransform = false;cs.addEmptyTUs( partitioner );//向cs类加入空的TUMergeCtx           mrgCtx;prediction_unit  ( pu, mrgCtx );//在这种情况下只执行了prediction_unit()里面的merge_data()函数end_of_ctu( cu, cuCtx );return;
}

第一个分支:主要解析了语法元素cu_skip_flag和在cu_skip_flag为1情况下的语法元素pred_mode_ibc_flag。前者表示当前CU是否开启skip模式,后者表示当前CU的预测模式是否是IBC。

如果当前CU开启skip模式:

  • 如果当前解码过程处于P slice或B slice,之后只可能解析语法元素pred_mode_ibc_flag和语法结构merge_data()
  • 如果当前解码过程处于I slice,之后只可能解析语法元素merge_idx

第二个分支:里面的语句之后还会遇到,目前只需要知道:在这种情况下调用prediction_unit(),只执行了里面的merge_data()函数。并跳过之后的语法元素解析。

pred_mode ( cu );//获得CU的预测模式
if (CU::isIntra(cu))
{adaptive_color_transform(cu);//解析语法元素cu_act_enabled_flag
}

pred_mode():获取当前CU的预测模式,预测模式一共有四种:传统帧内,帧间,IBC和PLT。IBC和PLT本质上也是种帧内预测,但在语法元素传输中,将其与传统帧内区分开。

分支:解析语法元素cu_act_enabled_flag,表示当前CU是否开启ACT

//解析PLT的语法元素
if (CU::isPLT(cu))
{cu.colorTransform = false;cs.addTU(cu, partitioner.chType);//向cs类加入TUif (cu.isSepTree()){if (isLuma(partitioner.chType)){cu_palette_info(cu, COMPONENT_Y, 1, cuCtx);//具体参考JVET-S2001 7.3.11.6 P121,不想跟进去看= =}if (cu.chromaFormat != CHROMA_400 && (partitioner.chType == CHANNEL_TYPE_CHROMA)){cu_palette_info(cu, COMPONENT_Cb, 2, cuCtx);}}else{if( cu.chromaFormat != CHROMA_400 ){cu_palette_info(cu, COMPONENT_Y, 3, cuCtx);}else{cu_palette_info(cu, COMPONENT_Y, 1, cuCtx);}}end_of_ctu(cu, cuCtx);return;
}

如果当前CU的预测模式是PLT,则会调用cu_palette_info()解析相应的语法元素,并跳过之后的语法元素解析。cu_palette_info()可以参考JVET-S2001的7.3.11.6小节,这里就不再展开(我也没看)。

// prediction data ( intra prediction modes / reference indexes + motion vectors )
cu_pred_data( cu );// residual data ( coded block flags + transform coefficient levels )
cu_residual( cu, partitioner, cuCtx );// check end of cu
end_of_ctu( cu, cuCtx );

cu_pred_data():解析预测模式的参数,第2节细讲

cu_residual():解析变换残差,留到下一篇博客

end_of_ctu():每个CTU语法解析结束后要判断是否更新CU上下文类中的isDQPCoded

2. cu_pred_data()

函数里面大致可以分为两部分,第一部分是有关于传统帧内预测的,第二部分是有关于IBC和帧间预测的。为啥把IBC和帧间预测放一块咧?因为虽然IBC是帧内预测但是和帧间预测一样有MV,所以放一块好处理。

2.1 关于传统帧内的部分

if( CU::isIntra( cu ) )//如果当前CU是帧内编码
{if( cu.Y().valid() ){bdpcm_mode(cu, COMPONENT_Y );//解析语法元素intra_bdpcm_luma_flag,intra_bdpcm_luma_dir_flag}intra_luma_pred_modes( cu );//解析帧内有关亮度的语法元素信息if( ( !cu.Y().valid() || (!cu.isSepTree() && cu.Y().valid() ) ) && isChromaEnabled(cu.chromaFormat) ){bdpcm_mode(cu, ComponentID(CHANNEL_TYPE_CHROMA));//解析语法元素intra_bdpcm_chroma_flag,intra_bdpcm_chroma_dir_flag}intra_chroma_pred_modes( cu );//解析帧内有关色度的语法元素信息
}

这里调用了两次bdpcm_mode(),第一次是解析语法元素intra_bdpcm_luma_flag和intra_bdpcm_luma_dir_flag,会在CU类里面存储属性bdpcmMode。bdpcmMode为0表示不开启BDPCM;为1表示BDPCM预测方向是水平的;为2表示BDPCM预测方向是垂直的。第二次调用也类似,只不过是存在CU的属性bdpcmModeChroma中。

intra_luma_pred_modes():解析帧内有关亮度的语法元素信息

intra_chroma_pred_modes():解析帧内有关色度的语法元素信息

下面来仔细看看这两个函数

2.1.1 intra_luma_pred_modes()

if( !cu.Y().valid() )//如果当前CU不存在亮度分量则结束此函数
{return;
}if( cu.bdpcmMode )//如果当前CU开启BDPCM则结束此函数
{cu.firstPU->intraDir[0] = cu.bdpcmMode == 2? VER_IDX : HOR_IDX;//设置亮度对应的帧内预测模式return;
}

第一个分支:理所当然,亮度分量都不存在了还解析啥

第二个分支:开启亮度BDPCM也就不用解析剩余帧内亮度语法元素,intraDir[0]存储着亮度的帧内预测模式,BDPCM用的也就是传统帧内预测的垂直和水平模式

mip_flag(cu);//解析语法元素intra_mip_flag
if (cu.mipFlag)
{mip_pred_modes(cu);//解析语法元素intra_mip_transposed_flag,intra_mip_modereturn;
}

这里就是解析有关于MIP的语法元素,如果开启MIP,也就不用解析剩余帧内亮度语法元素

extend_ref_line( cu );//解析语法元素intra_luma_ref_idx
isp_mode( cu );//解析语法元素intra_subpartitions_mode_flag,intra_subpartitions_split_flag

这就自己看吧,第一句有关于MRL,第二句有关于ISP

下面就是有关于亮度MPM的语法元素解析(由于太长就不贴出来了),感兴趣的可以自己看看,值得注意的就是开启MRL后默认从MPM得到预测模式,而且还不是Planar模式。

2.1.2 intra_chroma_pred_modes()

if( cu.chromaFormat == CHROMA_400 || ( cu.isSepTree() && cu.chType == CHANNEL_TYPE_LUMA ) )//如果当前CU不存在色度分量,则结束此函数
{return;
}if( cu.bdpcmModeChroma )//如果当前色度分量开启BDPCM
{cu.firstPU->intraDir[1] = cu.bdpcmModeChroma == 2 ? VER_IDX : HOR_IDX;//设置色度对应的帧内预测模式return;
}

这部分与亮度类似,不再赘述

if (pu.cu->colorTransform)//如果开启ACT,则色度帧内预测模式通过DM得到
{pu.intraDir[CHANNEL_TYPE_CHROMA] = DM_CHROMA_IDX;return;
}

如果开启ACT,则色度帧内预测模式通过DM得到

if (pu.cs->sps->getUseLMChroma() && pu.cu->checkCCLMAllowed())
{bool isLMCMode = m_BinDecoder.decodeBin(Ctx::CclmModeFlag(0)) ? true : false;//解析语法元素cclm_mode_flagif (isLMCMode){intra_chroma_lmc_mode(pu);return;}
}

解析有关于CCLM的语法元素,如果开启CCLM,其它帧内预测模式肯定就用不上了

unsigned candId = m_BinDecoder.decodeBinsEP(2);//获取语法元素intra_chroma_pred_modeunsigned chromaCandModes[NUM_CHROMA_MODE];//用来存储帧内色度预测模式的候选列表
PU::getIntraChromaCandModes(pu, chromaCandModes);//获取帧内色度预测模式的候选列表pu.intraDir[1] = chromaCandModes[candId];//设置对应的帧内色度预测模式

这段就是设置色度的帧内预测模式,而且肯定不是CCLM和BDPCM

2.2 关于帧间和IBC的部分

MergeCtx mrgCtx;//声明有关于merge的上下文类
prediction_unit( pu, mrgCtx );imv_mode   ( cu, mrgCtx );//解析非affine模式下(包括IBC)的语法元素amvr_flag和amvr_precision_idx
affine_amvr_mode( cu, mrgCtx );//解析affine模式下的语法元素amvr_flag和amvr_precision_idx
cu_bcw_flag( cu );//解析语法元素bcw_idx

mrgCtx:有关于merge的上下文类实例

prediction_unit():这个下一节细讲

imv_mode():解析非affine模式下(包括IBC)的语法元素amvr_flag和amvr_precision_idx

affine_amvr_mode():解析affine模式下的语法元素amvr_flag和amvr_precision_idx

通过amvr_flag和amvr_precision_idx可以得到AMVR采用的精度,具体参考JVET-S2001的Table 16。当前CU开启IBC模式时,amvr_flag默认为1,直接解析amvr_precision_idx。

cu_bcw_flag():解析语法元素bcw_idx

注意如果当前CU开启IBC模式时,affine_amvr_mode()和cu_bcw_flag()都相当于不执行

2.3 prediction_unit()

if( pu.cu->skip )//如果当前pu所在的CU是skip模式
{pu.mergeFlag = true;
}
else
{merge_flag( pu );//解析语法元素general_merge_flag
}

如果当前CU开启skip模式,默认general_merge_flag为1,否则需要解析语法元素general_merge_flag。general_merge_flag为1表示开启merge模式,并需要解析关于merge的语法元素。

if( pu.mergeFlag )
{merge_data(pu);//具体参考JVET-S2001 7.3.11.7 P124
}
else if (CU::isIBC(*pu.cu))//如果当前PU所在CU开启IBC
{pu.interDir = 1;pu.cu->affine = false;pu.refIdx[REF_PIC_LIST_0] = MAX_NUM_REF;mvd_coding(pu.mvd[REF_PIC_LIST_0]);//具体参考JVET-S2001 7.3.11.8 P125if (pu.cs->sps->getMaxNumIBCMergeCand() == 1){pu.mvpIdx[REF_PIC_LIST_0] = 0;}else{mvp_flag(pu, REF_PIC_LIST_0);//解析语法元素mvp_l0_flag}
}
else
{}

可以看出这里分了三种情况:

  • 当前CU开启merge模式:需要调用merge_data()解析有关于merge的语法元素,具体参考JVET-S2001的7.3.11.7小节,由于篇幅有限不再展开。稍微提一下merge模式分成了IBC、Affine、Regular、CIIP和GPM五种情况,其中Regular模式有是否开启MMVD两种情况。在开启MMVD的Regular模式或GPM模式的情况下,有关于merge的语法元素解析会稍微复杂些。
  • 当前CU不开启merge模式,但开启IBC模式:只需要解析一个MVD和语法元素mvp_l0_flag,有些语句的含义之后再讲
  • 当前CU即不开启merge模式,也不开启IBC模式:在2.3.1小节细讲
if ( pu.cu->smvdMode )//如果当前PU所在CU开启SMVD模式
{//设置另外一个反向的SMVDRefPicList eCurRefList = (RefPicList)(pu.cu->smvdMode - 1);pu.mvd[1 - eCurRefList].set( -pu.mvd[eCurRefList].hor, -pu.mvd[eCurRefList].ver );pu.refIdx[1 - eCurRefList] = pu.cs->slice->getSymRefIdx( 1 - eCurRefList );
}

如果开启SMVD,码流只会传一个MVD,所以在这里要将另一个MVD设置为其相反方向,长度不变。注意如果CU开启merge模式或IBC模式,是不可能开启SMVD

PU::spanMotionInfo( pu, mrgCtx );//向cs类加入当前PU内所有像素点的MotionInfo

spanMotionInfo():向cs类加入当前PU内所有像素点的MotionInfo

无论是否是merge模式或是否是IBC模式都会调用此函数。可以先记住CS类里面的属性m_motionBuf

2.3.1 else分支

inter_pred_idc( pu );//解析语法元素inter_pred_idc
affine_flag   ( *pu.cu );//解析语法元素inter_affine_flag和cu_affine_type_flag
smvd_mode( pu );//解析语法元素sym_mvd_flag

inter_pred_idc():解析语法元素inter_pred_idc,将其加1存在pu.interDir中。pu.interDir为1表示使用list0,为2表示使用list1,为3表示同时用list0和list1

affine_flag():解析语法元素inter_affine_flag和cu_affine_type_flag。inter_affine_flag为1表示当前CU开启Affine模式,cu_affine_type_flag为1表示当前CU采用6参数的Affine模式,否则就是4参数的Affine模式

smvd_mode():解析语法元素sym_mvd_flag,表示是否开启SMVD模式

if( pu.interDir != 2 /* PRED_L1 */ )
{ref_idx     ( pu, REF_PIC_LIST_0 );//获取语法元素ref_idx_l0if( pu.cu->affine )//如果当前PU所在CU开启affine模式{mvd_coding( pu.mvdAffi[REF_PIC_LIST_0][0] );mvd_coding( pu.mvdAffi[REF_PIC_LIST_0][1] );if ( pu.cu->affineType == AFFINEMODEL_6PARAM )//如果当前PU所在CU开启的affine是6PARAM{mvd_coding( pu.mvdAffi[REF_PIC_LIST_0][2] );}}else{mvd_coding( pu.mvd[REF_PIC_LIST_0] );}mvp_flag    ( pu, REF_PIC_LIST_0 );//获取语法元素mvp_l0_flag
}

如果当前PU用到了list0,就进入此分支:

ref_idx():获取语法元素ref_idx_l0,表示参考帧在参考帧列表中的Index

mvd_coding():解析MVD的语法元素,具体参考JVET-S2001的7.3.11.8小节。在开启Affine的情况下,一个预测方向就可能有两个或三个MVD,不开启Affine就只有一个MVD

mvp_flag():获取语法元素mvp_l0_flag

if( pu.interDir != 1 /* PRED_L0 */ )
{if ( pu.cu->smvdMode != 1 )//当前PU所在CU不开启SMVD{ref_idx(pu, REF_PIC_LIST_1);//获取语法元素ref_idx_l1if (pu.cu->cs->picHeader->getMvdL1ZeroFlag() && pu.interDir == 3 /* PRED_BI */){pu.mvd[REF_PIC_LIST_1]        = Mv();pu.mvdAffi[REF_PIC_LIST_1][0] = Mv();pu.mvdAffi[REF_PIC_LIST_1][1] = Mv();pu.mvdAffi[REF_PIC_LIST_1][2] = Mv();}else if (pu.cu->affine)//如果当前PU所在CU开启affine模式{mvd_coding(pu.mvdAffi[REF_PIC_LIST_1][0]);mvd_coding(pu.mvdAffi[REF_PIC_LIST_1][1]);if (pu.cu->affineType == AFFINEMODEL_6PARAM)//如果当前PU所在CU开启的affine是6PARAM{mvd_coding(pu.mvdAffi[REF_PIC_LIST_1][2]);}}else{mvd_coding(pu.mvd[REF_PIC_LIST_1]);}}mvp_flag    ( pu, REF_PIC_LIST_1 );//获取语法元素mvp_l1_flag
}

如果当前PU用到了list1,就进入此分支,这里与前面分支类似就说两点不同:

  • 如果开启SMVD,就不用解析语法元素ref_idx_l1和MVD
  • 如果当前CU同时使用list0和list1,并且ph_mvd_l1_zero_flag为1就不解析list1的MVD

VTM10.0代码学习5:coding_unit()cu_pred_data()相关推荐

  1. VTM10.0代码学习10:EncGOP_compressGOP()

    此系列是为了记录自己学习VTM10.0的过程,目前正在看编码端.主要的参考文档有JVET-S2001-vH和JVET-S2002-v1.由于本人水平有限,出现的错误恳请大家指正,欢迎与大家一起交流进步 ...

  2. VTM10.0代码学习7:decompressCtu()xReconIntraQT()

    此系列是为了记录自己学习VTM10.0的过程和锻炼表达能力,主要是从解码端进行入手.由于本人水平有限,出现的错误恳请大家指正,欢迎与大家一起交流进步. 之前的博文(VTM10.0代码学习3)提到两个重 ...

  3. VTM10.0代码学习3:DecSlice_decompressSlice()

    此系列是为了记录自己学习VTM10.0的过程和锻炼表达能力,主要是从解码端进行入手.由于本人水平有限,出现的错误恳请大家指正,欢迎与大家一起交流进步. 接着本系列的上一篇博客继续讲,上一篇博客的末尾讲 ...

  4. 【VTM10.0代码学习】帧间预测xCheckRdCostMerge2N*2N

      xCheckRdCostMerge函数在xCompressCU中被调用,这里包括了常规Merge模式.CIIP模式和MMVD模式,GPM在xCheckRDCostGeo函数中.也就是说常规Merg ...

  5. 资源|2019 年 11 月最新《TensorFlow 2.0 深度学习算法实战》中文版教材免费开源(附随书代码+pdf)...

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送 2019 年 10 月,谷歌正式宣布,开源机器学习库 TensorFlow 2.0 现在 ...

  6. 从0开始学习GitHub系列之「向GitHub 提交代码」

    DevStore首页 >文章 >文章详情 从0开始学习GitHub系列之「向GitHub 提交代码」 糖果果| 2016-06-15 10:57    浏览量(500)    评论(1) ...

  7. 400页《TensorFlow 2.0 深度学习算法实战》中文版教材免费下载(附随书代码+pdf)...

    Tensorflow自谷歌提出以来就成为最受欢迎的深度学习框架之一,到目前为止也已经被下载超过 4000 万次.其中TensorFlow2.0更是修复之前非常多的不人性的特性,备大家欢迎. 今天给大家 ...

  8. 从0开始学习 GitHub 系列之「04.向GitHub 提交代码」----转载自stormzhang 原创文章

    之前的这篇文章「从0开始学习 GitHub 系列之「Git速成」」相信大家都已经对 Git 的基本操作熟悉了,但是这篇文章只介绍了对本地 Git 仓库的基本操作,今天我就来介绍下如何跟远程仓库一起协作 ...

  9. Transformer学习总结附TF2.0代码实现

    Transformer学习总结附TF2.0代码实现 Transformer 1.Transformer详解 1.1 transformer总体架构 1.2 输入部分 1.3 Multi-Head At ...

最新文章

  1. [分享]C# 获取Outlook帐号和密码
  2. python必背内容-学 Python 必背的42个常见单词,看看你记住了几个?
  3. 华东交通大学计算机调剂,华东交通大学2018考研调剂信息
  4. 人工智能 | 人工智能的发展历程
  5. 深度学习-Tensorflow2.2-卷积神经网络{3}-卷积神经网络CNN基础-11
  6. Shell脚本经典之Fork炸弹
  7. dart系列之:创建Library package
  8. pycharm最左侧Tool Buttons显示不全的问题解决
  9. 重庆大学 计算机组成原理,重庆大学计算机组成原理集(含部分)解决方案.doc
  10. 2021年广东省高考成绩查询入口,广东省教育考试院:2021年广东高考成绩查询入口、查分系统...
  11. Linux c modbus 线程,Modbus TCP Slave Thread - 设置和获取寄存器值
  12. 揭秘Keras推荐系统如何建立模型、获取用户爱好
  13. blogCommed
  14. 190319每日一句
  15. 导入android工程报错,eclipse导入appcompat项目报错解决办法
  16. PID闭环控制系统的Simulink仿真
  17. 基于JAVA高校信息资源共享平台计算机毕业设计源码+系统+mysql数据库+lw文档+部署
  18. 火车头采集的数据库文件*.bd3是什么格式的数据库?
  19. 计算机excel怎么添加实线边框,给excel表格添加边框线
  20. matlab两矩阵相似性,两个矩阵同时相似对角化MATLAB程序.docx

热门文章

  1. android md5加密登录,Android开发之MD5加密
  2. Symbol - 看似平凡的Symbol其实我们每天都在用 - 字符串操作
  3. stm32——手动移植HAL库以及错误解决方案(以STM32F103ZE为例)
  4. 迅捷画图中套用流程图模板编辑方法介绍
  5. MyEclipse设置字体格式和大小,及字体推荐
  6. Android系统VIN码识别SDK
  7. 科研篇一:NeurIPS2019 分类整理-对抗样本Meta-Learning
  8. ctf 抓捕赵德汉_第三届网络空间安全技术大赛WriteUp(cstc2017)
  9. 校招前端笔试面试回顾
  10. 基于MatLab实现LSB(最低有效位)算法完成图片数字水印隐写功能