H.266/VVC代码学习:帧内预测之角度预测函数(predIntraAng、xPredIntraAng)
predIntraAng函数
VTM中,帧内预测的角度预测的入口函数为predIntraAng函数,该函数主要是用于进行传统的帧内预测(Planar、DC、角度预测),然后对Planar和DC模式使用PDPC(其余角度模式的PDPC在xPredIntraAng函数中进行)
void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu)
{const ComponentID compID = MAP_CHROMA( compId );const ChannelType channelType = toChannelType( compID );const int iWidth = piPred.width;const int iHeight = piPred.height;
#if JVET_P0641_REMOVE_2xN_CHROMA_INTRACHECK(iWidth == 2, "Width of 2 is not supported");
#endif
#if JVET_P0059_CHROMA_BDPCM//获取预测模式const uint32_t uiDirMode = isLuma( compId ) && pu.cu->bdpcmMode ? BDPCM_IDX : !isLuma(compId) && pu.cu->bdpcmModeChroma ? BDPCM_IDX : PU::getFinalIntraMode(pu, channelType);
#elseconst uint32_t uiDirMode = isLuma( compId ) && pu.cu->bdpcmMode ? BDPCM_IDX : PU::getFinalIntraMode( pu, channelType );
#endifCHECK( floorLog2(iWidth) < 2 && pu.cs->pcv->noChroma2x2, "Size not allowed" );CHECK( floorLog2(iWidth) > 7, "Size not allowed" );const int srcStride = m_refBufferStride[compID];const int srcHStride = 2;const CPelBuf & srcBuf = CPelBuf(getPredictorPtr(compID), srcStride, srcHStride);//参考像素const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compID));switch (uiDirMode){case(PLANAR_IDX): xPredIntraPlanar(srcBuf, piPred); break;//Planar模式case(DC_IDX): xPredIntraDc(srcBuf, piPred, channelType, false); break;//DC模式
#if JVET_P0059_CHROMA_BDPCMcase(BDPCM_IDX): xPredIntraBDPCM(srcBuf, piPred, isLuma(compID) ? pu.cu->bdpcmMode : pu.cu->bdpcmModeChroma, clpRng); break;
#elsecase(BDPCM_IDX): xPredIntraBDPCM(srcBuf, piPred, pu.cu->bdpcmMode, clpRng); break;
#endifdefault: xPredIntraAng(srcBuf, piPred, channelType, clpRng); break;}//PDPC,这里进对Planar和DC做,其余角度模式的PDPC在xPredIntraAng函数中进行if (m_ipaParam.applyPDPC){PelBuf dstBuf = piPred;const int scale = ((floorLog2(iWidth) - 2 + floorLog2(iHeight) - 2 + 2) >> 2);CHECK(scale < 0 || scale > 31, "PDPC: scale < 0 || scale > 31");if (uiDirMode == PLANAR_IDX || uiDirMode == DC_IDX)//PLANAR和DC的PDPC{for (int y = 0; y < iHeight; y++){const int wT = 32 >> std::min(31, ((y << 1) >> scale)); //32是2的5次方。即超过5就会变为0,不再受上影响const Pel left = srcBuf.at(y + 1, 1);for (int x = 0; x < iWidth; x++){const int wL = 32 >> std::min(31, ((x << 1) >> scale));const Pel top = srcBuf.at(x + 1, 0);const Pel val = dstBuf.at(x, y);dstBuf.at(x, y) = val + ((wL * (left - val) + wT * (top - val) + 32) >> 6);}}}}
}
xPredIntraAng函数
角度预测模式的步骤:
- 填充参考像素向量ref(包括扩展参考像素)
- 根据角度计算投影到参考像素位置的偏移
- 根据偏移值获取预测值
1. 填充参考像素向量ref(包括扩展参考像素)
xPredIntraAng函数主要进行传统的角度预测,需要注意的是变量intraPredAngle和absInvAngle
const int intraPredAngle = m_ipaParam.intraPredAngle;const int absInvAngle = m_ipaParam.absInvAngle;
这两变量是在initPredIntraParams函数中初始化的,其中intraPredAngle变量指代的是角度偏移值,,即每一种角度模式都相当于在水平方向或者垂直方向上做了一个角度偏移,从角度模式到偏移值的映射如下表所示:
absInvAngle变量表示反角度参数,具体地计算为
在VTM中为了消除除法运算,预先将(512 * 32) /Angle的值存了起来,存在了invAngTable表中。
VTM中具体实现代码如下所示:
// 先乘以256(左移8位),运算出结果再右移8位,以定点实现浮点运算精度static const int angTable[32] = { 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 23, 26, 29, 32, 35, 39, 45, 51, 57, 64, 73, 86, 102, 128, 171, 256, 341, 512, 1024 };// 设置invAngTable的目的是为了消除帧内角度预测模式在计算预测值时麻烦的除法运算static const int invAngTable[32] = {0, 16384, 8192, 5461, 4096, 2731, 2048, 1638, 1365, 1170, 1024, 910, 819, 712, 630, 565,512, 468, 420, 364, 321, 287, 256, 224, 191, 161, 128, 96, 64, 48, 32, 16}; // (512 * 32) /Angle 投影的位置,用的时候需要右移9位const int absAngMode = abs(intraPredAngleMode);//值为0~32const int signAng = intraPredAngleMode < 0 ? -1 : 1;absAng = angTable [absAngMode];//当前角度的对应的下标值 得到的表格值m_ipaParam.absInvAngle = invAngTable[absAngMode]; // 每种角度对应的投影位置m_ipaParam.intraPredAngle = signAng * absAng;// 角度偏移值 offset
设置反角度模式是用来扩展参考像素的,具体地:
- 对于水平类模式(19-33),需要将左侧参考像素投影到上侧参考像素的左侧,以扩展上侧参考像素;
- 对于垂直类模式(34-49),需要将上侧参考像素投影到左侧参考像素的上方,以扩展左侧参考像素,一个例子如下:
上图中,以预测模式21为例(HEVC中的21),其进行预测时,参考像素为上一行的参考像素。但是对于某些位置,需要用到上一行的左侧的参考像素(即p[-2][-1]、p[-3][-1]、p[-4][-1]位置处的像素),此时的做法是将左侧参考像素投影到上侧参考像素的左侧,以扩展上侧参考像素。
如上图,当x < 0时,需要使用左侧参考进行投影,在投影的过程中,具体投影到的左侧参考像素的位置是通过intraPredAngle变量确定的,即y(x) = -1 + Round(32 * x / intraPredAngle(k)),为了消除除法运算,使用invAngle代替x / intraPredAngle(k),即y(x) = -1 + (x * invAngle)。
注:将参考像素投影到扩展行/列的投影位置可由invAngle得到,即上图x<0的情况。如果投影的时候出现非整数位置处的像素,则使用最近的参考像素进行投影,如下图所示:
2.根据角度计算投影到参考像素位置的偏移
填充完参考像素后,根据角度偏移值intraPredAngle进行预测值的计算,具体的:根据角度偏移值intraPredAngle得到索引变量iIdx和乘法因子iFact:(iFact应该表示的是从待预测像素投影到参考像素的两个整数参考像素间的分像素的位置(以32点为单位),idx表示投影到参考像素的偏移)
对于垂直类角度模式
iIdx = ( ( ( y + 1 + refIdx ) * intraPredAngle ) >> 5 ) + refIdx
iFact = ( ( y + 1 + refIdx ) * intraPredAngle ) & 31
其中refIdx表示的是参考行的索引。
通过iIdx和iFact计算预测值:
对于水平类角度模式:
iIdx = ( ( ( x + 1 + refIdx ) * intraPredAngle ) >> 5 ) + refIdx
iFact = ( ( x + 1 + refIdx ) * intraPredAngle ) & 31
3. 根据偏移值获取预测值
通过iIdx和iFact计算预测值:
其中cIdx = 0表示亮度,否则表示色度。 在计算预测值的时候,由于一些角度模式会使用到非整数位置处的像素,对于亮度分量,对位于非整数位置处的像素使用三次插值滤波器或者高斯插值滤波器对分数像素位置处的像素进行插值。插值滤波器的选择参考H.266/VVC技术学习:帧内预测之角度预测模式的第三部分的内容。对于色度分量,使用HEVC中的两抽头插值滤波器。
获得预测像素之后,在还需要对以下几种模式进行PDPC操作:
- 垂直模式(模式50)
- 水平模式(模式18)
- 左下角对角线模式和与其相邻的8个模式(模式2~10)
- 右上角对角线模式和与其相邻的8个模式(模式58~66)。
void IntraPrediction::xPredIntraAng( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const ClpRng& clpRng)
{int width =int(pDst.width);int height=int(pDst.height);const bool bIsModeVer = m_ipaParam.isModeVer;const int multiRefIdx = m_ipaParam.multiRefIndex;const int intraPredAngle = m_ipaParam.intraPredAngle;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.// 初始化参考像素// 偏移值offset小于0,即模式为19~49// 对于垂直类模式(34~49),需要将左侧参考行映射到上方参考行// 对于水平类模式(18~33),需要将上方参考行映射到左侧参考行if (intraPredAngle < 0){for (int x = 0; x <= width + 1 + multiRefIdx; x++){refAbove[x + height] = pSrc.at(x, 0);}for (int y = 0; y <= height + 1 + multiRefIdx; y++){refLeft[y + width] = pSrc.at(y, 1);}// 如果是垂直类模式(34~49),则主要侧为正上方参考行,次要侧为正左侧参考行// 如果是水平类模式(19~33),则主要侧为正左侧参考行,次要侧为正上方参考行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++){// 右移9位是因为存储在表里的是 (512 * 32) /Angle,需要再给它除以512refMain[k] = refSide[std::min((-k * absInvAngle + 256) >> 9, sizeSide)];}}else// 偏移值大于等于0,只需要用到上方参考像素或者左侧参考像素{for (int x = 0; x <= m_topRefLength + multiRefIdx; x++){refAbove[x] = pSrc.at(x, 0);}for (int y = 0; y <= m_leftRefLength + multiRefIdx; y++){refLeft[y] = pSrc.at(y, 1);}// 垂直类模式,主参考行为上方参考行// 水平类模式,主参考行为左侧参考行refMain = bIsModeVer ? refAbove : refLeft;refSide = bIsModeVer ? refLeft : refAbove;// Extend main reference to right using replication// 使用复制将主参考行向右扩展或者向下扩展,即填充Segment A和Segment F(MRL)const int log2Ratio = floorLog2(width) - floorLog2(height);const int s = std::max<int>(0, bIsModeVer ? log2Ratio : -log2Ratio);const int maxIndex = (multiRefIdx << s) + 2; // 扩展长度const int refLength = bIsModeVer ? m_topRefLength : m_leftRefLength;const Pel val = refMain[refLength + multiRefIdx]; //边界处的像素值for (int z = 1; z <= maxIndex; z++){refMain[refLength + multiRefIdx + z] = val;}}// swap width/height if we are doing a horizontal mode:// 如果是水平类模式则交换width和heightif (!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// 补充多参考行中的偏移refMain += multiRefIdx;refSide += multiRefIdx;Pel *pDsty = pDstBuf;if( intraPredAngle == 0 ) // pure vertical or pure horizontal 水平模式和垂直模式(18和50){for( int y = 0; y < height; y++ ){for( int x = 0; x < width; x++ ){pDsty[x] = refMain[x + 1];//直接投影}if (m_ipaParam.applyPDPC) // 使用PDPC技术{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//对于其余模式{for (int y = 0, deltaPos = intraPredAngle * (1 + multiRefIdx); y<height; y++, deltaPos += intraPredAngle, pDsty += dstStride){const int deltaInt = deltaPos >> 5; // 整像素位置const int deltaFract = deltaPos & 31; // 分像素位置//对于除水平、垂直、模式2、34、66之外的模式需进行使用高斯插值滤波器进行插值处理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);}}}else{// Just copy the integer samples 直接复制整数位置处的参考像素for( 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] = pDsty[x] + ((wL * (left - pDsty[x]) + 32) >> 6);}}}}// Flip the block if this is the horizontal mode// 如果这是水平模式,翻转块if( !bIsModeVer ){for( int y = 0; y < height; y++ ){for( int x = 0; x < width; x++ ){pDst.at( y, x ) = pDstBuf[x];}pDstBuf += dstStride;}}
}
H.266/VVC代码学习:帧内预测之角度预测函数(predIntraAng、xPredIntraAng)相关推荐
- H.266/VVC-VTM代码学习-帧内预测05-Angular模式下计算预测像素值xPredIntraAng
H.266/VVC专栏传送 上一篇:H.266/VVC-VTM代码学习-帧内预测04-Planar模式下计算预测像素值xPredIntraPlanar 下一篇:H.266/VVC-VTM代码学习-帧内 ...
- H.266/VVC代码学习20:角度预测入口 / 特殊模式的PDPC技术(predIntraAng)
1.predIntraAng函数 predIntraAng是帧内0~66这67种预测的入口.其中可细分为: 模式0:PLANAR模式 模式1:DC模式 模式2~66:角度模式 此函数在亮度预测和色度预 ...
- H.266/VVC代码学习21:帧内角度预测的实现 / 近对角模式的PDPC(xPredIntraAng)
xPredIntraAng函数的作用是对任意大小的块和任意模式,如何将参考像素的值根据其模式的角度填充进每一个像素. 下图是basketball drill的一个16*16的块,其预测模式为10(偏斜 ...
- H.266/VVC代码学习17:帧内亮度预测的编解码(intra_luma_pred_modes)
引--亮度预测:H.266/VVC代码学习5:VTM4.0帧内亮度预测代码(estIntraPredLumaQT) 一.结论: 亮度编解码根据MPM列表的值分为两个阶段: 1 亮度模式在MPM列表中: ...
- H.266/VVC:色度帧内预测模式之CCLM技术
一.色度模式编码 1.色度预测模式候选列表 VVC中色度分量的预测过程和亮度分量预测过程不同,其首先是构建色度预测模式候选列表如下表,有8种模式,由4种和亮度模式相同的模式.3种CCLM模式和1种DM ...
- H.266/VVC代码学习:xCompressCU函数
xCompressCU函数是用来进行CU划分(递归调用自身)以及模式选择. 首先调用m_modeCtrl->initCULevel函数初始化模式选择列表,由堆栈控制,将当前CU可以划分的模式(四 ...
- H.266/VVC代码学习:MIP技术相关代码之initIntraMip函数
initIntraMip函数主要是对参考像素进行下采样并为MIP矩阵乘法准备输入数据,函数结构如下: MIP根据块尺寸可以分为以下三种情况: 块尺寸 下采样后的边界长度 m_reducedBdry ...
- H.266/VVC代码学习32:VTM5.0解码端最上层函数
解码部分的研究不像编码端那样需要精雕细琢,但如果想研究一个内容划分或选择模式等的最终结果是怎样,那么应该从解码端入手.下面让我们来学习解码端的框架及最上层的三个函数. 文章目录 1 main() 2 ...
- H.266/VVC代码学习:DC模式和Planar模式
Planar模式和DC模式时两种特殊的角度模式,分别对应于模式号0和1. 一.DC模式 DC模式适用于大面积平坦区域,其预测值是通过计算左边和(或)上边参考像素的平均值获得的. 1.1.计算 DC模式 ...
最新文章
- 哈哈,咱们团队早就不用try-catch-finally关闭资源了!
- 数据增强_开源算法FMix:用于深度学习中增强混合样本数据增强
- .NET弹出对话框小结
- 【温故知新】CSS学习笔记(行高简介)
- 单核工作法12:现在专注一件事(下)
- 敏捷冲刺每日报告一(Java-Team)
- dedecms批量删除文档关键词可以吗
- Linux加密框架 crypto 算法模板 HMAC模板举例
- java swing 多个线程,Swing与多线程
- Docker学习笔记 - Docker容器的日志
- 对于有一定编程基础的学生来说,看下面这个链接的文章比较好
- Phoenix使用注意事项以及跟标准sql的不同
- 编址与存储相关计算(一)——软考之路
- 逆向分析CRACKME 第一章 Acid burn
- 笔记本win10系统部分应用显示模糊的问题——已解决!
- (一)Apollo配置中心介绍
- 《计算机体系结构量化研究方法》1.8 性能的测量、报告和汇总
- python随手记自动记账_菜鸟也疯狂!8分钟用Python做一个酷炫的家庭随手记
- 使用JavaScrip实现简单问卷星快速生成自定义数据
- 我所首席执行主任律师王杰接受《电脑报》记者采访就sp发展发表观点
热门文章
- Android课设:简易音乐播放器
- USF MSDS501 计算数据科学中文讲义 2.4 Python 中的编程模式
- windows下Python2.7 的 pyOpenGL模块安装
- 民办三本,我从3K到15K的一年
- 软件工程 软件质量模型(ISO/IEC 9126)
- _spellmod_leech_aura
- PHP 获取第一个汉字大写首字母
- 每日一题——判断素数
- 网络流建图方法(二)——辅助点(虚点)决策法洛谷 P1361 小M 的作物 Dinic
- Pexels Videos – 可以免费商业使用的短视频