热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

H.266/VVC帧间预测技术学习:基于子块的时间运动矢量预测(Subblockbasedtemporalmotionvectorprediction)

基于子块的时间运动矢量预测VVC支持基于子块的时间运动矢量预测(SbTMVP)方法。类似于HEVC中的时间运动矢量预测(temporal

基于子块的时间运动矢量预测

VVC支持基于子块的时间运动矢量预测(SbTMVP)方法。 类似于HEVC中的时间运动矢量预测(temporal motion vector prediction, TMVP),SbTMVP使用同位图片中的运动场来改善当前图片中CU的运动矢量预测和Merge模式。 SbTVMP和TMVP使用的相同的同位图片。 (同位块:相邻已编码图像中对应位置的块)

SbTMVP在以下两个主要方面与TMVP不同:

  1. TMVP预测CU级别的运动,但SbTMVP预测子CU级别的运动;
  2. TMVP直接从同位图片的同位块获取时间运动矢量(同位块是相对于当前CU的右下或中心块),而SbTMVP在从同位图片中获取时间运动信息之前应用了运动偏移 ,其中运动偏移来自当前CU的空间相邻块的运动矢量。

SbTMVP中使用的子CU大小固定为8x8,SbTMVP模式仅适用于宽度和高度都大于或等于8的CU。另外,SbTMVP Merge候选的编码逻辑与其他Merge候选的编码逻辑相同,即,对于P或B片中的每个CU,执行附加RD检查以决定是否使用SbTMVP候选。

SbTMVP分两步预测当前CU内子CU的运动矢量:

Step1:检查下图中的空域相邻块A1。 如果A1具有将同位图片用作其参考图片的运动矢量(A1可用且其参考图像就是当前图像的同位图像),则将该运动矢量选择为要应用的运动偏移。 如果未识别到此类运动,则将运动偏移设置为(0,0)。

Step2:利用①中A1块的运动偏移,定位当前CU的参考同位块的位置。然后将当前CU以及其对应的同位图像中的同位块分割为8*8的子块,对于每个子块,获取对应同位子块的运动信息( 8*8 子块中心位置处)。在确定同位子CU的运动信息之后,以与HEVC的TMVP过程类似的方式,将其转换为当前子CU的运动矢量和参考帧索引,其中应用时间运动缩放以将时间运动矢量的参考图片与当前CU的参考图片对齐。

SbtMVP和Affine Merge的关系

在VVC中,SbTMVP和Affine Merge的候选是共存的,将SbTVMP得到的候选和仿射Merge候选共同组成基于子块的Merge候选列表。 通过序列参数集(SPS)标志启用/禁用SbTVMP模式。 如果启用了SbTMVP模式,则将SbTMVP预测变量添加为基于子块的Merge候选列表的第一项,然后是仿射Merge候选项。 基于子块的Merge列表的大小在SPS中用信号通知,并且基于子块的Merge列表的最大允许大小为5。

SbTMVP的相关代码及注释如下所示:(基于VTM10.0)

// 基于子块的TMVP技术if (sbTmvpEnableFlag && slice.getPicHeader()->getEnableTMVPFlag()){MergeCtx mrgCtx &#61; *affMrgCtx.mrgCtx;bool tmpLICFlag &#61; false;CHECK( mrgCtx.subPuMvpMiBuf.area() &#61;&#61; 0 || !mrgCtx.subPuMvpMiBuf.buf, "Buffer not initialized" );mrgCtx.subPuMvpMiBuf.fill( MotionInfo() );int pos &#61; 0;// Get spatial MVconst Position posCurLB &#61; pu.Y().bottomLeft();MotionInfo miLeft;//leftconst PredictionUnit* puLeft &#61; cs.getPURestricted( posCurLB.offset( -1, 0 ), pu, pu.chType );const bool isAvailableA1 &#61; puLeft && isDiffMER(pu.lumaPos(), posCurLB.offset(-1, 0), plevel) && pu.cu !&#61; puLeft->cu && CU::isInter( *puLeft->cu );if ( isAvailableA1 ){ //A1块可用miLeft &#61; puLeft->getMotionInfo( posCurLB.offset( -1, 0 ) );// get Inter DirmrgCtx.interDirNeighbours[pos] &#61; miLeft.interDir;// get Mv from Left 获得A1块的MVmrgCtx.mvFieldNeighbours[pos <<1].setMvField( miLeft.mv[0], miLeft.refIdx[0] );if ( slice.isInterB() ){mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 1].setMvField( miLeft.mv[1], miLeft.refIdx[1] );}pos&#43;&#43;;}mrgCtx.numValidMergeCand &#61; pos;// 获得子PU的时域MV预测isAvailableSubPu &#61; getInterMergeSubPuMvpCand(pu, mrgCtx, tmpLICFlag, pos, 0);if ( isAvailableSubPu ){// 将子块的时域MV添加到Affine Merge列表中for ( int mvNum &#61; 0; mvNum <3; mvNum&#43;&#43; ){affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand <<1) &#43; 0][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 0].mv, mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 0].refIdx );affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand <<1) &#43; 1][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 1].mv, mrgCtx.mvFieldNeighbours[(pos <<1) &#43; 1].refIdx );}affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] &#61; mrgCtx.interDirNeighbours[pos];affMrgCtx.affineType[affMrgCtx.numValidMergeCand] &#61; AFFINE_MODEL_NUM;affMrgCtx.mergeType[affMrgCtx.numValidMergeCand] &#61; MRG_TYPE_SUBPU_ATMVP;if ( affMrgCtx.numValidMergeCand &#61;&#61; mrgCandIdx ){return;}affMrgCtx.numValidMergeCand&#43;&#43;;// early terminationif ( affMrgCtx.numValidMergeCand &#61;&#61; maxNumAffineMergeCand ){return;}}}

其中&#xff0c;在getInterMergeSubPuMvpCand函数中获得子PU的MVP候选项

bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, bool& LICFlag, const int count, int mmvdList
)
{const Slice &slice &#61; *pu.cs->slice;const unsigned scale &#61; 4 * std::max(1, 4 * AMVP_DECIMATION_FACTOR / 4);const unsigned mask &#61; ~(scale - 1);// 获得同位图片const Picture *pColPic &#61; slice.getRefPic(RefPicList(slice.isInterB() ? 1 - slice.getColFromL0Flag() : 0), slice.getColRefIdx());Mv cTMv;if ( count ){if ( (mrgCtx.interDirNeighbours[0] & (1 <> ATMVP_SUB_BLOCK_SIZE, 1u);int numPartCol &#61; std::max(puSize.height >> ATMVP_SUB_BLOCK_SIZE, 1u);int puHeight &#61; numPartCol &#61;&#61; 1 ? puSize.height : 1 <> 1) &#43; tempX; centerPos.y &#61; puPos.y &#43; (puSize.height >> 1) &#43; tempY;clipColPos(centerPos.x, centerPos.y, pu);centerPos &#61; Position{ PosType(centerPos.x & mask), PosType(centerPos.y & mask) };// derivation of center motion parameters from the collocated CU// 从同位CU得出中心运动参数const MotionInfo &mi &#61; pColPic->cs->getMotionInfo(centerPos);if (mi.isInter && mi.isIBCmot &#61;&#61; false){mrgCtx.interDirNeighbours[count] &#61; 0;for (unsigned currRefListId &#61; 0; currRefListId <(bBSlice ? 2 : 1); currRefListId&#43;&#43;){RefPicList currRefPicList &#61; RefPicList(currRefListId); //参考帧列表索引if (getColocatedMVP(pu, currRefPicList, centerPos, cColMv, refIdx, true)) //从同位CU中获得MVP{// set as default, for further motion vector field spanningmrgCtx.mvFieldNeighbours[(count <<1) &#43; currRefListId].setMvField(cColMv, 0);mrgCtx.interDirNeighbours[count] |&#61; (1 <> 1) &#43; tempX;int yOff &#61; (puHeight >> 1) &#43; tempY;MotionBuf &mb &#61; mrgCtx.subPuMvpMiBuf;const bool isBiPred &#61; isBipredRestriction(pu);for (int y &#61; puPos.y; y cs->getMotionInfo(colPos);MotionInfo mi;found &#61; false;mi.isInter &#61; true;mi.sliceIdx &#61; slice.getIndependentSliceIdx();mi.isIBCmot &#61; false;if (colMi.isInter && colMi.isIBCmot &#61;&#61; false){for (unsigned currRefListId &#61; 0; currRefListId <(bBSlice ? 2 : 1); currRefListId&#43;&#43;){RefPicList currRefPicList &#61; RefPicList(currRefListId);if (getColocatedMVP(pu, currRefPicList, colPos, cColMv, refIdx, true)){mi.refIdx[currRefListId] &#61; 0;mi.mv[currRefListId] &#61; cColMv;found &#61; true;}}}if (!found){mi.mv[0] &#61; mrgCtx.mvFieldNeighbours[(count <<1) &#43; 0].mv;mi.mv[1] &#61; mrgCtx.mvFieldNeighbours[(count <<1) &#43; 1].mv;mi.refIdx[0] &#61; mrgCtx.mvFieldNeighbours[(count <<1) &#43; 0].refIdx;mi.refIdx[1] &#61; mrgCtx.mvFieldNeighbours[(count <<1) &#43; 1].refIdx;}mi.interDir &#61; (mi.refIdx[0] !&#61; -1 ? 1 : 0) &#43; (mi.refIdx[1] !&#61; -1 ? 2 : 0);if (isBiPred && mi.interDir &#61;&#61; 3){mi.interDir &#61; 1;mi.mv[1] &#61; Mv();mi.refIdx[1] &#61; NOT_VALID;}mb.subBuf(g_miScaling.scale(Position{ x, y } - pu.lumaPos()), g_miScaling.scale(Size(puWidth, puHeight))).fill(mi);}}}return true;
}

 sbtMVP对应的mergetype是MRG_TYPE_SUBPU_ATMVP&#xff0c;相应的运动补偿函数为&#xff1a;

void InterPrediction::xSubPuMC( PredictionUnit& pu, PelUnitBuf& predBuf, const RefPicList &eRefPicList /*&#61; REF_PIC_LIST_X*/, const bool luma /*&#61; true*/, const bool chroma /*&#61; true*/)
{// compute the location of the current PU// 当前PU的位置和尺寸Position puPos &#61; pu.lumaPos();Size puSize &#61; pu.lumaSize();int numPartLine, numPartCol, puHeight, puWidth;{numPartLine &#61; std::max(puSize.width >> ATMVP_SUB_BLOCK_SIZE, 1u); // 行存在多少个子PUnumPartCol &#61; std::max(puSize.height >> ATMVP_SUB_BLOCK_SIZE, 1u); // 列存在多少个子PU//subPu 的宽度/高度&#xff0c;如果当前PU的宽度/高度大于8&#xff0c;则subPU的宽度/高度为8&#xff0c;否则subPU的宽度/高度和PU的宽度/高度相等puHeight &#61; numPartCol &#61;&#61; 1 ? puSize.height : 1 <affine;subPu.cu->affine &#61; false;// join sub-pus containing the same motionbool verMC &#61; puSize.height > puSize.width;int fstStart &#61; (!verMC ? puPos.y : puPos.x);int secStart &#61; (!verMC ? puPos.x : puPos.y);int fstEnd &#61; (!verMC ? puPos.y &#43; puSize.height : puPos.x &#43; puSize.width);int secEnd &#61; (!verMC ? puPos.x &#43; puSize.width : puPos.y &#43; puSize.height);int fstStep &#61; (!verMC ? puHeight : puWidth);int secStep &#61; (!verMC ? puWidth : puHeight);bool scaled &#61; pu.cu->slice->getRefPic( REF_PIC_LIST_0, 0 )->isRefScaled( pu.cs->pps ) || ( pu.cs->slice->getSliceType() &#61;&#61; B_SLICE ? pu.cu->slice->getRefPic( REF_PIC_LIST_1, 0 )->isRefScaled( pu.cs->pps ) : false );m_subPuMC &#61; true;for (int fstDim &#61; fstStart; fstDim affine &#61; isAffine;
}


推荐阅读
  • 在运行于MS SQL Server 2005的.NET 2.0 Web应用中,我偶尔会遇到令人头疼的SQL死锁问题。过去,我们主要通过调整查询来解决这些问题,但这既耗时又不可靠。我希望能找到一种确定性的查询模式,确保从设计上彻底避免SQL死锁。 ... [详细]
  • 本打算教一步步实现koa-router,因为要解释的太多了,所以先简化成mini版本,从实现部分功能到阅读源码,希望能让你好理解一些。希望你之前有读过koa源码,没有的话,给你链接 ... [详细]
  • 深入解析Struts、Spring与Hibernate三大框架的面试要点与技巧 ... [详细]
  • 在使用 Cacti 进行监控时,发现已运行的转码机未产生流量,导致 Cacti 监控界面显示该转码机处于宕机状态。进一步检查 Cacti 日志,发现数据库中存在 SQL 查询失败的问题,错误代码为 145。此问题可能是由于数据库表损坏或索引失效所致,建议对相关表进行修复操作以恢复监控功能。 ... [详细]
  • 笔记说明重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系& ... [详细]
  • 视觉Transformer综述
    本文综述了视觉Transformer在计算机视觉领域的应用,从原始Transformer出发,详细介绍了其在图像分类、目标检测和图像分割等任务中的最新进展。文章不仅涵盖了基础的Transformer架构,还深入探讨了各类增强版Transformer模型的设计思路和技术细节。 ... [详细]
  • 服务器虚拟化存储设计,完美规划储存与资源,部署高性能虚拟化桌面
    规划部署虚拟桌面环境前,必须先估算目前所使用实体桌面环境的工作负载与IOPS性能,并慎选储存设备。唯有谨慎估算贴近实际的IOPS性能,才能 ... [详细]
  • 申请地址:https://developer.apple.com/appstore/contact/?topic=expedite 常见申请理由:1. 我们即将发布新产品,这是一个媒体活动,我们无法承担任何风险,因此在多个方面努力提升应用质量。 ... [详细]
  • 本文详细介绍了 Java 网站开发的相关资源和步骤,包括常用网站、开发环境和框架选择。 ... [详细]
  • 微信公众号推送模板40036问题
    返回码错误码描述说明40001invalidcredential不合法的调用凭证40002invalidgrant_type不合法的grant_type40003invalidop ... [详细]
  • 本文介绍了如何通过安装Build Token Root插件并配置身份验证令牌来解决Jenkins远程触发器无法正常工作的问题。 ... [详细]
  • 如何将Python与Excel高效结合:常用操作技巧解析
    本文深入探讨了如何将Python与Excel高效结合,涵盖了一系列实用的操作技巧。文章内容详尽,步骤清晰,注重细节处理,旨在帮助读者掌握Python与Excel之间的无缝对接方法,提升数据处理效率。 ... [详细]
  • 阿里巴巴终面技术挑战:如何利用 UDP 实现 TCP 功能?
    在阿里巴巴的技术面试中,技术总监曾提出一道关于如何利用 UDP 实现 TCP 功能的问题。当时回答得不够理想,因此事后进行了详细总结。通过与总监的进一步交流,了解到这是一道常见的阿里面试题。面试官的主要目的是考察应聘者对 UDP 和 TCP 在原理上的差异的理解,以及如何通过 UDP 实现类似 TCP 的可靠传输机制。 ... [详细]
  • 本文详细介绍了在MySQL中如何高效利用EXPLAIN命令进行查询优化。通过实例解析和步骤说明,文章旨在帮助读者深入理解EXPLAIN命令的工作原理及其在性能调优中的应用,内容通俗易懂且结构清晰,适合各水平的数据库管理员和技术人员参考学习。 ... [详细]
  • Python ATM与购物车项目实战:深入解析三层架构设计
    本文详细解析了Python ATM与购物车项目的三层架构设计,重点介绍了MVC(Model-View-Controller)模式的应用。在用户界面层,系统通过图形化界面与用户进行交互,接收并处理用户的输入数据,随后将这些数据传递给控制层进行进一步处理。该层不仅负责展示信息,还承担了用户请求的初步处理任务。 ... [详细]
author-avatar
无心之无心
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有