文章目录

  • 函数流程
  • 源码注释

函数流程

参考自 H.266/VVC-VTM代码学习19-CU层确定测试模式函数initCULevel, VVC中CTU的编码,块划分以及qp的决策 - 知乎 (zhihu.com),本文主要讲解VTM18.0中的EncModeCtrlMTnoRQT::initCULevel的流程,该函数在EncCu::xCompressCU的开始被调用,将需要测试的划分模式和编码模式加入堆栈。主要步骤:

1、确定最大和最小搜索深度,如果开启Large CTU,会根据周围块的深度进行调整

2、根据baseQP设置附近的minQP和maxQP,并在添加后续模式时遍历可能的QP

3、添加划分模式,默认添加顺序是QT、TTV、TTH、BTV 、BTH和Non-split,因为是保存在堆栈中,测试顺序相反。值得注意的是,当current CU的 QtDepth 小于左侧和上侧相邻CU的 QtDepth 时,或者 current CU的决策深度较浅时,会将QT的测试顺序放在BT之前。此外,QT和TT共用一套QP range,BT单独使用一套QP range

4、添加测试模式,此时的QP range是Non-split的,包括 intra、inter、IBC 和 palette等模式

该函数进一步调用canSplit确定划分模式是否合法,

源码注释

// VTM18.0
void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStructure& cs )
{// 该函数主要完成编码端RD cost的模式筛选,并加入各种划分模式和inter intra模式等至RDO列表// Min/max depthunsigned minDepth = 0;unsigned maxDepth = floorLog2(cs.sps->getCTUSize()) - floorLog2(cs.sps->getMinQTSize( m_slice->getSliceType(), partitioner.chType ));// 最大搜索深度 = log(CTU_Size / minQTSize), by default log(128 / 4) = 5if( m_pcEncCfg->getUseFastLCTU() ){if( auto adPartitioner = dynamic_cast<AdaptiveDepthPartitioner*>( &partitioner ) ){// LARGE CTU// 根据当前块的临近块拓展maxDepth和minDepthadPartitioner->setMaxMinDepth( minDepth, maxDepth, cs );}}// 写入最大和最小深度m_ComprCUCtxList.push_back( ComprCUCtx( cs, minDepth, maxDepth, NUM_EXTRA_FEATURES ) ); const CodingUnit* cuLeft  = cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( -1, 0 ), partitioner.chType );const CodingUnit* cuAbove = cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( 0, -1 ), partitioner.chType );// 如果左侧CU和上方CU存在,而且存在CU的qtDepth大于当前CU的QT深度,则在BT之前尝试QT划分(默认顺序是non-split, BTH, BTV, TTH, QT)// 如果都不存在,Width >= 32 * 2^depth and Width > minQTSize * 2, 也将QT提前到BT之前const bool qtBeforeBt = ( (  cuLeft  &&  cuAbove  && cuLeft ->qtDepth > partitioner.currQtDepth && cuAbove->qtDepth > partitioner.currQtDepth )|| (  cuLeft  && !cuAbove  && cuLeft ->qtDepth > partitioner.currQtDepth )|| ( !cuLeft  &&  cuAbove  && cuAbove->qtDepth > partitioner.currQtDepth )|| ( !cuAbove && !cuLeft   && cs.area.lwidth() >= ( 32 << cs.slice->getDepth() ) ) )&& ( cs.area.lwidth() > ( cs.pcv->getMinQtSize( *cs.slice, partitioner.chType ) << 1 ) );// set featuresComprCUCtx &cuECtx  = m_ComprCUCtxList.back();cuECtx.set( BEST_NON_SPLIT_COST,  MAX_DOUBLE );cuECtx.set( BEST_VERT_SPLIT_COST, MAX_DOUBLE );cuECtx.set( BEST_HORZ_SPLIT_COST, MAX_DOUBLE );cuECtx.set( BEST_TRIH_SPLIT_COST, MAX_DOUBLE );cuECtx.set( BEST_TRIV_SPLIT_COST, MAX_DOUBLE );cuECtx.set( DO_TRIH_SPLIT,        1 );cuECtx.set( DO_TRIV_SPLIT,        1 );cuECtx.set( BEST_IMV_COST,        MAX_DOUBLE * .5 );cuECtx.set( BEST_NO_IMV_COST,     MAX_DOUBLE * .5 );cuECtx.set( QT_BEFORE_BT,         qtBeforeBt );cuECtx.set( DID_QUAD_SPLIT,       false );cuECtx.set( IS_BEST_NOSPLIT_SKIP, false );cuECtx.set( MAX_QT_SUB_DEPTH,     0 );// QPint baseQP = cs.baseQP;  // Slice级别的QP// SepTree 包括 帧内的Dual tree,以及帧间的local dual Treeif (!partitioner.isSepTree(cs) || isLuma(partitioner.chType))  // joint Tree 或者 luma 分量{if (m_pcEncCfg->getUseAdaptiveQP()){// baseQP 取 -(6 * (m_bitDepth[CHANNEL_TYPE_LUMA] - 8) 和 63 和 sliceQP + delta 中的中值baseQP = Clip3(-cs.sps->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, baseQP + xComputeDQP(cs, partitioner));}
#if ENABLE_QPA_SUB_CTUelse if (m_pcEncCfg->getUsePerceptQPA() && !m_pcEncCfg->getUseRateCtrl() && cs.pps->getUseDQP() && cs.slice->getCuQpDeltaSubdiv() > 0){const PreCalcValues &pcv = *cs.pcv;if ((partitioner.currArea().lwidth() < pcv.maxCUWidth) && (partitioner.currArea().lheight() < pcv.maxCUHeight) && cs.picture){const Position    &pos = partitioner.currQgPos;const unsigned mtsLog2 = (unsigned)floorLog2(std::min (cs.sps->getMaxTbSize(), pcv.maxCUWidth));const unsigned  stride = pcv.maxCUWidth >> mtsLog2;baseQP = cs.picture->m_subCtuQP[((pos.x & pcv.maxCUWidthMask) >> mtsLog2) + stride * ((pos.y & pcv.maxCUHeightMask) >> mtsLog2)];}}
#endif
#if SHARP_LUMA_DELTA_QPif (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()){if (partitioner.currQgEnable()){m_lumaQPOffset = calculateLumaDQP (cs.getOrgBuf (clipArea (cs.area.Y(), cs.picture->Y())));}baseQP = Clip3 (-cs.sps->getQpBDOffset (CHANNEL_TYPE_LUMA), MAX_QP, baseQP - m_lumaQPOffset);}
#endifif (m_pcEncCfg->getSmoothQPReductionEnable()){int smoothQPoffset = 0;if (partitioner.currQgEnable()){// enable smooth QP reduction on selected framesbool checkSmoothQP = false;if (m_pcEncCfg->getSmoothQPReductionPeriodicity() != 0){checkSmoothQP = ((m_pcEncCfg->getSmoothQPReductionPeriodicity() == 0) && cs.slice->isIntra()) || (m_pcEncCfg->getSmoothQPReductionPeriodicity() == 1) || ((cs.slice->getPOC() % m_pcEncCfg->getSmoothQPReductionPeriodicity()) == 0);}else{checkSmoothQP = ((m_pcEncCfg->getSmoothQPReductionPeriodicity() == 0) && cs.slice->isIntra());}if (checkSmoothQP){bool isIntraSlice = cs.slice->isIntra();if (isIntraSlice){smoothQPoffset = calculateLumaDQPsmooth(cs.getOrgBuf(clipArea(cs.area.Y(), cs.picture->Y())), baseQP, m_pcEncCfg->getSmoothQPReductionThresholdIntra(), m_pcEncCfg->getSmoothQPReductionModelScaleIntra(), m_pcEncCfg->getSmoothQPReductionModelOffsetIntra(), m_pcEncCfg->getSmoothQPReductionLimitIntra());}else{smoothQPoffset = calculateLumaDQPsmooth(cs.getOrgBuf(clipArea(cs.area.Y(), cs.picture->Y())), baseQP, m_pcEncCfg->getSmoothQPReductionThresholdInter(), m_pcEncCfg->getSmoothQPReductionModelScaleInter(), m_pcEncCfg->getSmoothQPReductionModelOffsetInter(), m_pcEncCfg->getSmoothQPReductionLimitInter());}}}baseQP = Clip3(-cs.sps->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, baseQP + smoothQPoffset);}}// 以下是在base QP附近设定minQP和maxQP,再RD 测试两者之间的所有取值,确定最佳的minQP和maxQPint minQP = baseQP;int maxQP = baseQP;xGetMinMaxQP( minQP, maxQP, cs, partitioner, baseQP, *cs.sps, *cs.pps, CU_QUAD_SPLIT );  // 针对四叉树和三叉树//色度分量不检查IBC模式bool checkIbc = true;if (partitioner.chType == CHANNEL_TYPE_CHROMA){checkIbc = false;}// Add coding modes here// NOTE: Working back to front, as a stack, which is more efficient with the container// NOTE: First added modes will be processed at the end.// 注意:划分模式是先入后出//// Add unit split modes// 添加划分模式进入测试列表if( !cuECtx.get<bool>( QT_BEFORE_BT ) ){for( int qp = maxQP; qp >= minQP; qp-- )  // 遍历可能的QP参数{m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_QT, ETO_STANDARD, qp } );  // if not qt_before_bt, then push back QT_mode}}if( partitioner.canSplit( CU_TRIV_SPLIT, cs ) ){// add split modesfor( int qp = maxQP; qp >= minQP; qp-- ){m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_TT_V, ETO_STANDARD, qp } );    // 三叉树竖直划分}}if( partitioner.canSplit( CU_TRIH_SPLIT, cs ) ){// add split modesfor( int qp = maxQP; qp >= minQP; qp-- ){m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_TT_H, ETO_STANDARD, qp } );    // 三叉树水平划分}}int minQPq = minQP;int maxQPq = maxQP;xGetMinMaxQP( minQP, maxQP, cs, partitioner, baseQP, *cs.sps, *cs.pps, CU_BT_SPLIT );  // 针对二叉树if( partitioner.canSplit( CU_VERT_SPLIT, cs ) ){// add split modesfor( int qp = maxQP; qp >= minQP; qp-- ){m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_BT_V, ETO_STANDARD, qp } );  // 二叉树竖直划分}m_ComprCUCtxList.back().set( DID_VERT_SPLIT, true );}else{m_ComprCUCtxList.back().set( DID_VERT_SPLIT, false );}if( partitioner.canSplit( CU_HORZ_SPLIT, cs ) ){// add split modesfor( int qp = maxQP; qp >= minQP; qp-- ){m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_BT_H, ETO_STANDARD, qp } );  // 二叉树水平划分}m_ComprCUCtxList.back().set( DID_HORZ_SPLIT, true );}else{m_ComprCUCtxList.back().set( DID_HORZ_SPLIT, false );}if( cuECtx.get<bool>( QT_BEFORE_BT ) ){for( int qp = maxQPq; qp >= minQPq; qp-- ){m_ComprCUCtxList.back().testModes.push_back( { ETM_SPLIT_QT, ETO_STANDARD, qp } );   // QT优先于BT划分}}m_ComprCUCtxList.back().testModes.push_back( { ETM_POST_DONT_SPLIT } );  // 不划分xGetMinMaxQP( minQP, maxQP, cs, partitioner, baseQP, *cs.sps, *cs.pps, CU_DONT_SPLIT );  // 设置不划分的QP,即当前块的intra inter IBC等模式需要test的QP rangeint  lowestQP = minQP;//// Add unit coding modes: Intra, InterME, InterMerge ...// 添加单元编码模式bool tryIntraRdo = true;  // 是否尝试Intra mode RDObool tryInterRdo = true;  // 是否尝试Inter mode RDObool tryIBCRdo   = true;if( partitioner.isConsIntra() ){tryInterRdo = false;  }else if( partitioner.isConsInter() ){tryIntraRdo = tryIBCRdo = false;}checkIbc &= tryIBCRdo;for( int qpLoop = maxQP; qpLoop >= minQP; qpLoop-- ){const int  qp       = std::max( qpLoop, lowestQP );
#if REUSE_CU_RESULTSconst bool isReusingCu = isValid( cs, partitioner, qp );cuECtx.set( IS_REUSING_CU, isReusingCu );if( isReusingCu ){m_ComprCUCtxList.back().testModes.push_back( {ETM_RECO_CACHED, ETO_STANDARD, qp} );}
#endif// add intra modesif( tryIntraRdo ){if (cs.slice->getSPS()->getPLTMode()&& (partitioner.treeType != TREE_D || cs.slice->isIntra()|| (cs.area.lwidth() == 4 && cs.area.lheight() == 4))&& getPltEnc()){m_ComprCUCtxList.back().testModes.push_back({ ETM_PALETTE, ETO_STANDARD, qp });  // paletee mode, 屏幕内容编码的一种模式}m_ComprCUCtxList.back().testModes.push_back({ ETM_INTRA, ETO_STANDARD, qp });if (cs.slice->getSPS()->getPLTMode() && partitioner.treeType == TREE_D && !cs.slice->isIntra()&& !(cs.area.lwidth() == 4 && cs.area.lheight() == 4) && getPltEnc()){m_ComprCUCtxList.back().testModes.push_back({ ETM_PALETTE, ETO_STANDARD, qp });}}// add ibc mode to intra pathif (cs.sps->getIBCFlag() && checkIbc){m_ComprCUCtxList.back().testModes.push_back({ ETM_IBC,         ETO_STANDARD,  qp });if (partitioner.chType == CHANNEL_TYPE_LUMA){m_ComprCUCtxList.back().testModes.push_back({ ETM_IBC_MERGE,   ETO_STANDARD,  qp });}}}// add first pass modesif ( !m_slice->isIntra() && !( cs.area.lwidth() == 4 && cs.area.lheight() == 4 ) && tryInterRdo ){for( int qpLoop = maxQP; qpLoop >= minQP; qpLoop-- ){const int  qp       = std::max( qpLoop, lowestQP );if (m_pcEncCfg->getIMV()){m_ComprCUCtxList.back().testModes.push_back({ ETM_INTER_ME,  EncTestModeOpts( 4 << ETO_IMV_SHIFT ), qp });  }if( m_pcEncCfg->getIMV() || m_pcEncCfg->getUseAffineAmvr() ){int imv = m_pcEncCfg->getIMV4PelFast() ? 3 : 2;m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME, EncTestModeOpts( imv << ETO_IMV_SHIFT ), qp } );m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME, EncTestModeOpts( 1 << ETO_IMV_SHIFT ), qp } );}// add inter modesif( m_pcEncCfg->getUseEarlySkipDetection() ){if( cs.sps->getUseGeo() && cs.slice->isInterB() ){m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_GEO, ETO_STANDARD, qp } );}m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_SKIP,  ETO_STANDARD, qp } );if (cs.sps->getUseAffine() || (cs.sps->getSbTMVPEnabledFlag() && cs.slice->getPicHeader()->getEnableTMVPFlag())){m_ComprCUCtxList.back().testModes.push_back( { ETM_AFFINE,    ETO_STANDARD, qp } );}m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME,    ETO_STANDARD, qp } );}else{m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME,    ETO_STANDARD, qp } );if( cs.sps->getUseGeo() && cs.slice->isInterB() ){m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_GEO, ETO_STANDARD, qp } );}m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_SKIP,  ETO_STANDARD, qp } );if (cs.sps->getUseAffine() || (cs.sps->getSbTMVPEnabledFlag() && cs.slice->getPicHeader()->getEnableTMVPFlag())){m_ComprCUCtxList.back().testModes.push_back( { ETM_AFFINE,    ETO_STANDARD, qp } );}}if (m_pcEncCfg->getUseHashME()){int minSize = min(cs.area.lwidth(), cs.area.lheight());if (minSize < 128 && minSize >= 4){m_ComprCUCtxList.back().testModes.push_back({ ETM_HASH_INTER, ETO_STANDARD, qp });}}}}

VVC系列(四)initCULevel代码解读相关推荐

  1. C# 发送xml报文到用友U8生成凭证系列四(Biz代码)最终代码

    C# 发送xml报文到用友U8生成凭证系列一(配置信息) C# 发送xml报文到用友U8生成凭证系列二(基础代码) C# 发送xml报文到用友U8生成凭证系列三(Modal代码) C# 发送xml报文 ...

  2. Lossless Codec---APE代码解读系列(二)

    APE file 一些概念 APE代码解读系列(一) APE代码解读系列(三) 1. 先要了解APE compression level APE主要有5level, 分别是: CompressionL ...

  3. YOLO系列代码解读(图像检测)

    1.YOLO v1 yolo v1源码解析 - 筱 - CSDN博客  https://blog.csdn.net/baidu_27643275/article/details/82794559 [D ...

  4. STM32学习心得三十四:外部SRAM原理及实验代码解读

    记录一下,方便以后翻阅~ 主要内容: 1) IS62WV51216简介: 2) FSMC简介及相关寄存器介绍: 3) 相关实验代码解读. 参考手册: <STM32中文参考手册_V10>-第 ...

  5. 有趣的python代码系列四:小猪佩奇

    先看效果图: python代码: import turtle from turtle import * turtle.title('小猪佩奇')def nose(x,y):""&q ...

  6. ResNet及其变种的结构梳理、有效性分析与代码解读(PyTorch)

    点击我爱计算机视觉标星,更快获取CVML新技术 本文来自知乎,作者费敬敬,现为同济大学计算机科学与技术硕士. https://zhuanlan.zhihu.com/p/54289848 温故而知新,理 ...

  7. 鱼眼图像自监督深度估计原理分析和Omnidet核心代码解读

    作者丨苹果姐@知乎 来源丨https://zhuanlan.zhihu.com/p/508090405 编辑丨3D视觉工坊 在自动驾驶实际应用中,对相机传感器的要求之一是拥有尽可能大的视野范围,鱼眼相 ...

  8. 无码系列5.1 代码重构 消除重复代码

    1 前言 本文可以视为对ThoughtWorks高级顾问yuanyingjie关于"正交四原则"策略"消除重复"的"个人解读". 如有谬误, ...

  9. mask rcnn 超详细代码解读(一)

    mask r-cnn 代码解读(一) 文章目录 1 代码架构 2 model.py 的结构 3 train过程代码解析 3.1 Resnet Graph 3.2 Region Proposal Net ...

最新文章

  1. Android程序员的技术要求和学习路线
  2. bat 批量提取指定目录下的文件
  3. 怎么利用Excel统计各分数段的人数?(亲测sum函数可用)
  4. 巴川数据科学炼成记_【脑王直通车】小小记忆高手炼成记
  5. 标准模板库 STL 使用之 —— vector 使用 tricks
  6. python网络通信效率_Python之网络通信
  7. os.path.join()函数
  8. Latex公式空格输入
  9. div 中img 居中
  10. 控件ShowWindow(SW_HIDE)不起作用
  11. win7无法打开计算机共享文件夹,win7共享文件夹怎么设置?win7共享文件夹无法访问...
  12. 空中交通通信控制设备的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  13. 解析几何----双曲线左支极坐标方程
  14. 使用EasyExcel导出图片及异常处理
  15. 梯度下降及具体计算方式
  16. 条码固定资产管理系统的作用,固定资产条码化管理
  17. DataGrip离线安装驱动
  18. 零基础HTML教程(4)--动手创建第一个网页吧
  19. 【eclipse】版本代号
  20. 2019年 团体程序设计天梯赛——题解集

热门文章

  1. 超实用的折纸翻书,开发出这款漫画绝对爆了!
  2. 随机森林和神经网络有什么区别?
  3. CorelCAD 2020 for mac(cad绘图软件)
  4. 计算机与电视如何通过网络连接,笔记本怎么连电视显示屏,电脑通过wifi连接电视图解...
  5. php 代付功能_娱谷API聚合支付系统是怎么实现支付代付功能的?
  6. 形状补间及路径引导动画
  7. 台式计算机家用推荐哪款,家用台式电脑哪款好 家用台式电脑排行榜介绍
  8. ArcGIS基础:找出宗地对应的所有界址点号(空间连接)
  9. 企业级GIT分支管控方案
  10. LeeCode 5455 贪心 + BIT