H.266/VVC专栏传送

上一篇:H.266/VVC-VTM代码学习-帧内预测04-Planar模式下计算预测像素值xPredIntraPlanar
下一篇:H.266/VVC-VTM代码学习-帧内预测06-VVC的宽角度预测模式

目录

前言

VTM是H.266/VVC视频编码标准的参考软件,研究VTM代码给研究人员解释了VVC编码标准的详细标准规范与细节。

本文是笔者对VTM代码的一点学习记录,成文于笔者刚开始接触VVC期间,期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大视频编码学习者沟通交流、共同进步。

VTM代码的下载及编译请参考博文:
【视频编码学习】H.266/VVC参考软件VTM配置运行(VTM-6.0版本)
本文涉及的代码存在于工程下的/lib/CommonLib/SourceFiles/IntraPrediction.cpp文件中。

一、主要函数

1.函数代码

void IntraPrediction::xPredIntraAng( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const ClpRng& clpRng)
{int width =int(pDst.width);int height=int(pDst.height);//predMode >= DIA_IDX(34) 视为类垂直模式const bool bIsModeVer     = m_ipaParam.isModeVer;//多参考行索引const int  multiRefIdx    = m_ipaParam.multiRefIndex;//signAng * absAng 带正负号的偏移角度const int  intraPredAngle = m_ipaParam.intraPredAngle;//角度偏移值的倒数*512*32,便于除法运算const int  absInvAngle    = m_ipaParam.absInvAngle;Pel* refMain;Pel* refSide;//用于存放上侧参考像素Pel  refAbove[2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];//用于存放左侧参考像素Pel  refLeft [2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];//Initialize the Main and Left reference array.初始化参考像素//偏移角度值小于0即19~49,不需要用到segmentA和segmentF//垂直类模式(34~49),需将左侧映射到上侧,水平类模式(19~33),需将上侧映射到左侧if (intraPredAngle < 0){for (int x = 0; x <= width + 1 + multiRefIdx; x++){//将segmentD、segmentE参考像素放于refAbove[x + height]//+height是给投影到上方的左侧参考像素留位置refAbove[x + height] = pSrc.at(x, 0);}for (int y = 0; y <= height + 1 + multiRefIdx; y++){//将segmentC、segmentB参考像素放于refAbove[y + width]//+width是给投影到左侧的上方参考像素留位置refLeft[y + width] = pSrc.at(y, 1);}//主要侧:垂直类为上侧,水平类为左侧refMain = bIsModeVer ? refAbove + height : refLeft + width;// 次要侧:水平类为上侧,垂直类为左侧refSide = bIsModeVer ? refLeft + width : refAbove + height;//Extend the Main reference to the left.将次要侧参考行投影到主要侧int sizeSide = bIsModeVer ? height : width;for (int k = -sizeSide; k <= -1; k++){//(offset/32)是偏移角的tan值,因为投影时主要侧的k位置存放次要测的k/tan(θ)位置//k/tan(θ)即k/(offset/32)即32*k/offset//又因为absInvAngle = 32 * 512 /offset//所以k/tan(θ) = k * absInvAngle / 512 refMain[k] = refSide[std::min((-k * absInvAngle + 256) >> 9, sizeSide)];}}//对于角度偏移值大于0的模式,只需用segmentA和segmentB或segmentE和segmentFelse{for (int x = 0; x <= m_topRefLength + multiRefIdx; x++){//将segmentD、segmentE参考像素放于refAbove[x]refAbove[x] = pSrc.at(x, 0);}for (int y = 0; y <= m_leftRefLength + multiRefIdx; y++){//将segmentC、segmentB参考像素放于refAbove[x]refLeft[y] = pSrc.at(y, 1);}//主要侧:垂直类为上侧,水平类为左侧refMain = bIsModeVer ? refAbove : refLeft;//次要侧:水平类为上侧,垂直类为左侧refSide = bIsModeVer ? refLeft : refAbove;//Extend main reference to right using replication使用复制将主参考行向右扩展//log2(width / height)const int log2Ratio = floorLog2(width) - floorLog2(height);const int s         = std::max<int>(0, bIsModeVer ? log2Ratio : -log2Ratio);//垂直模式:multiRefIdx * width / height + 2//水平模式:multiRefIdx * height / width + 2const int maxIndex  = (multiRefIdx << s) + 2;const int refLength = bIsModeVer ? m_topRefLength : m_leftRefLength;const Pel val       = refMain[refLength + multiRefIdx];//复制segmentE最后像素,填充segmentFfor (int z = 1; z <= maxIndex; z++){refMain[refLength + multiRefIdx + z] = val;}}// swap width/height if we are doing a horizontal mode:水平模式时交换height和width,便于后续操作if (!bIsModeVer){std::swap(width, height);}Pel       tempArray[MAX_CU_SIZE * MAX_CU_SIZE];const int dstStride = bIsModeVer ? pDst.stride : width;Pel *     pDstBuf   = bIsModeVer ? pDst.buf : tempArray;// compensate for line offset in reference line buffers补充多参考行中的偏移//将指针指向segmentB或segmentE第一个参考像素refMain += multiRefIdx;//将指针指向segmentE或segmentB第一个参考像素refSide += multiRefIdx;Pel *pDsty = pDstBuf;//pure vertical or pure horizontal 完全水平或完全垂直模式(18和50),直接用对应参考像素预测if( intraPredAngle == 0 ){for( int y = 0; y < height; y++ ){for( int x = 0; x < width; x++ ){//直接预测pDsty[x] = refMain[x + 1];}//使用PDPCif (m_ipaParam.applyPDPC){const int scale   = (floorLog2(width) + floorLog2(height) - 2) >> 2;const Pel topLeft = refMain[0];const Pel left    = refSide[1 + y];for (int x = 0; x < std::min(3 << scale, width); x++){const int wL  = 32 >> (2 * x >> scale);const Pel val = pDsty[x];//预测值是原始得到的预测值(主参考行的投影)加上对应次要侧位置和次要侧起始位置的差值pDsty[x]      = ClipPel(val + ((wL * (left - topLeft) + 32) >> 6), clpRng);}}pDsty += dstStride;}}//对除完全水平和完全垂直的模式else{//遍历次要侧,deltaPos为当前遍历点的y*offsetfor (int y = 0, deltaPos = intraPredAngle * (1 + multiRefIdx); y<height; y++, deltaPos += intraPredAngle, pDsty += dstStride){//当前像素对应主要侧中的位置,即当前遍历点的(y*offset)/32const int deltaInt   = deltaPos >> 5;// 计算当前像素对应参考像素的加权因子Ωconst int deltaFract = deltaPos & 31;//对于除水平、垂直、2、34、66之外的模式进行使用高斯插值滤波器插值处理//偏移值不是32的整数倍if ( !isIntegerSlope( abs(intraPredAngle) ) ){//亮度块四抽头滤波if( isLuma(channelType) ){const bool useCubicFilter = !m_ipaParam.interpolationFlag;const TFilterCoeff        intraSmoothingFilter[4] = {TFilterCoeff(16 - (deltaFract >> 1)), TFilterCoeff(32 - (deltaFract >> 1)), TFilterCoeff(16 + (deltaFract >> 1)), TFilterCoeff(deltaFract >> 1)};const TFilterCoeff* const f                       = (useCubicFilter) ? InterpolationFilter::getChromaFilterTable(deltaFract) : intraSmoothingFilter;//对主要侧遍历,滤波求出预测值for (int x = 0; x < width; x++){Pel p[4];p[0] = refMain[deltaInt + x];p[1] = refMain[deltaInt + x + 1];p[2] = refMain[deltaInt + x + 2];p[3] = refMain[deltaInt + x + 3];Pel val = (f[0] * p[0] + f[1] * p[1] + f[2] * p[2] + f[3] * p[3] + 32) >> 6;pDsty[x] = ClipPel(val, clpRng);   // always clip even though not always needed}}//色度块线性滤波else{// Do linear filtering线性滤波for (int x = 0; x < width; x++){Pel p[2];p[0] = refMain[deltaInt + x + 1];p[1] = refMain[deltaInt + x + 2];pDsty[x] = p[0] + ((deltaFract * (p[1] - p[0]) + 16) >> 5);}}}//对于不插值处理的模式(水平、垂直、2、34、66),直接将滤波后的参考像素作为预测像素else{// Just copy the integer samplesfor( int x = 0; x < width; x++ ){pDsty[x] = refMain[x + deltaInt + 1];}}if (m_ipaParam.applyPDPC)// 使用PDPC{const int scale       = m_ipaParam.angularScale;int       invAngleSum = 256;for (int x = 0; x < std::min(3 << scale, width); x++){invAngleSum += absInvAngle;int wL   = 32 >> (2 * x >> scale);Pel left = refSide[y + (invAngleSum >> 9) + 1];//pDsty[x]是插值滤波后的结果,left是其对应次要侧的参考像素(未经过滤波),wL调节left在结果中的权重pDsty[x] = pDsty[x] + ((wL * (left - pDsty[x]) + 32) >> 6);}}}}//Flip the block if this is the horizontal mode 如果是水平模式,反转块。因为水平模式时交换了height和width,是列优先进行处理的,故需要将输出缓存列优先排列if( !bIsModeVer ){for( int y = 0; y < height; y++ ){for( int x = 0; x < width; x++ ){pDst.at( y, x ) = pDstBuf[x];}pDstBuf += dstStride;}}
}

2.逻辑结构

1.初始化参考像素:
对于角度偏移值小于0的模式(19-49):
将次要侧参考像素投影至主要侧的对应位置。
对于角度偏移值大于0的模式(2-17和51-66):
将主参考侧向右或向下扩展。

2.水平模式时交换width和height的值,便于后续操作。

3.完全水平或完全垂直模式时,直接投影对应的参考像素作为预测值。若使用PDPC,则在原预测值基础上加上次要侧对应位置像素与次要侧第一个像素的差值。

4.若为非水平、垂直、对角(2、34、66)的模式,亮度块进行四抽头插值滤波,色度块进行线性滤波。若为对角模式,直接将对应的参考像素作为预测值。若使用PDPC,则将未插值滤波的当前点在次要侧对应点像素与原预测值加权结合。

上一篇:H.266/VVC-VTM代码学习-帧内预测04-Planar模式下计算预测像素值xPredIntraPlanar
下一篇:H.266/VVC-VTM代码学习-帧内预测06-VVC的宽角度预测模式

H.266/VVC-VTM代码学习-帧内预测05-Angular模式下计算预测像素值xPredIntraAng相关推荐

  1. H.266/VVC相关技术学习笔记16:VTM6.0中的CIIP技术(帧内帧间联合预测)

    今天讲一下目前VTM6.0版本中的CIIP技术,CIIP即为帧内帧间联合预测技术,这属于Merge系列的一个分支. 该技术需要先计算当前预测块的帧内预测值,即用Planar.DC.角度预测等传统的帧内 ...

  2. H.266/VVC相关技术学习笔记4:HEVC和VVC中块划分的差别

    关于H.265/HEVC和H.266/VVC中的块划分的区别: 一.HEVC中首先需要将一个图像固定划分为多个CTU. ① CTU的尺寸固定划分为64×64,一个CTU由一个亮度CTB和两个色度CTB ...

  3. H.266/VVC SCC技术学习:帧内块拷贝(Intra block copy, IBC)

    帧内块拷贝 (Intra block copy, IBC) 是 HEVC 针对屏幕内容编码(Screen content coding)序列的扩展工具,它显着提高了屏幕内容序列的编码效率. IBC 是 ...

  4. H.266/VVC相关技术学习笔记18:帧间预测中的AMVR技术(自适应运动适量精度)

    AMVR技术也称为自适应运动适量精度技术,就是在以前的HEVC中,MVD的精度只有一个默认的1/4像素精度,但是由于要适应不同分辨率的图像,仅仅使用一个精度去表示MVD是远远不够的,因此在VTM6.0 ...

  5. H.266/VVC相关技术学习笔记21:帧间预测中五种Merge模式的熵编码方式

    今天主要详细讲一下帧间预测中五种Merge模式的熵编码方式,以及对应的VTM的代码中的编码方式的实现.现阶段VTM6.0中Merge模式大致上分为五种,分别是Subblock_Merge.MMVD_M ...

  6. H.266/VVC相关技术学习笔记20:帧间预测技术中的MMVD技术(Merge mode with MVD)

    今天介绍一下帧间预测技术中的MMVD技术(Merge mode with MVD),也称带有运动矢量差的融合技术,MMVD也属于基于Merge的技术中的一种,在解码端的语法元素中也属于Merge分支. ...

  7. 【十六】 H.266/VVC | VVC中帧间预测技术详细总结 | 所有帧间预测技术代码汇总

    前言 ​ 帧间预测是影响视频编码性能的关键环节之一,H.266/VVC帧间预测在传统只能应对简单的平移运动的基础上,采用了仿射运动模型,可以描述更加复杂的缩放.旋转等运动.为了更好的发挥合并模式(Me ...

  8. 【Codecs系列】H.266/VVC视频编码标准 技术系列汇总

    DATE: 2020.9.30 文章目录 1.H.266/VCC标准专栏 2.H.266/VCC视频编码标准 技术汇总 1.H.266/VCC标准专栏       视音频技术之H.266/VVC 2. ...

  9. H.266/VVC代码学习:帧内预测之角度预测函数(predIntraAng、xPredIntraAng)

    predIntraAng函数 VTM中,帧内预测的角度预测的入口函数为predIntraAng函数,该函数主要是用于进行传统的帧内预测(Planar.DC.角度预测),然后对Planar和DC模式使用 ...

最新文章

  1. 浅淡Windows7 32位与64位/x86与x64的区别
  2. #20145238荆玉茗《网络对抗》-逆向及Bof进阶实践
  3. Dell R720上的系统安装问题的解决办法(关于RAID建立磁盘阵列的技术)
  4. lintcode-514-栅栏染色
  5. ssl初一组周六模拟赛【2018.4.14】
  6. python爬虫 库_七款必备的Python爬虫库,你知道几个?
  7. Java 核心 API 必须掌握的程度
  8. UbuntuSkills
  9. golang 数组随机排序
  10. 论文精读- The Evaluation of the Urban Road Network Based on the Complex Network
  11. 【数据结构】从零实现顺序表+链表相关操作
  12. centos改变文件拥有者_linux修改文件所有者和文件所在组
  13. 浅析EL表达式注入漏洞
  14. 一个开源知识管理系统,满足企业定制化需求
  15. Managing Supply and Demand Balance Through Machine Learning-笔记
  16. 折纸珠峰c语言程序,c语言折纸超过珠穆拉玛峰
  17. Error与Exception的异常定义以及简介(简单理解介绍是为了下一节的异常处理与捕捉)
  18. 抖音seo源码,抖音矩阵,抖音seo系统,抖音搜索排名
  19. 520表白攻略 程序员必要的相亲准备 最近你相亲表白了嘛
  20. python中os模块用法

热门文章

  1. 运维日常之机房浪潮服务器硬盘红灯亮起,服务器一直响,raid磁盘红色。。。故障解决方法...
  2. OpenGL学习十九:纹理过滤
  3. webdriver.Chrome.set_network_conditions:Chrome网络仿真设置。
  4. Qt QSet 详解:从底层原理到高级用法
  5. Linux磁盘故障和文件系统修复(救援模式Centos7、Centos8)
  6. JAVA-超大文件上传-如何上传文件-大文件上传
  7. python写网络爬虫编程环境设置
  8. Python+OpenCV实用案例应用教程:基于OpenCV的图像处理
  9. 二本计算机考研简单吗,普通二本考研很难吗 哪些大学不收二本考研
  10. 工作中遇到的问题和一些经验总结