H.266/VVC专栏传送
上一篇:H.266/VVC-VTM代码学习-帧内预测16-解码端xIntraRecBlk完成TU指定分量帧内重建(附ISP变换和预测尺寸不同、JCCR、LMCS简述)
下一篇:H.266/VVC-VTM代码学习18-自适应QP设置(Adaptive QP)
目录
前言
VTM是H.266/VVC视频编码标准的参考软件,研究VTM代码给研究人员解释了VVC编码标准的详细标准规范与细节。
本文是笔者对VTM代码的一点学习记录,成文于笔者刚开始接触VVC期间,期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大视频编码学习者沟通交流、共同进步。
VTM代码的下载及编译请参考博文:
【视频编码学习】H.266/VVC参考软件VTM配置运行(VTM-6.0版本)
本文涉及的代码主要存在于工程下的Lib\CommonLib\IntraPrediction.cpp文件中。
一、initIntraPatternChTypeISP函数初始化ISP的帧内预测
void IntraPrediction::initIntraPatternChTypeISP(const CodingUnit& cu, const CompArea& area, PelBuf& recBuf, const bool forceRefFilterFlag)
{
const CodingStructure& cs = *cu.cs;
if (!forceRefFilterFlag)
{
//帧内预测参数初始化
initPredIntraParams(*cu.firstPU, area, *cs.sps);
}
const Position posLT = area;
//左侧CU可用,且已解码
bool isLeftAvail = (cs.getCURestricted(posLT.offset(-1, 0), cu, CHANNEL_TYPE_LUMA) != NULL) && cs.isDecomp(posLT.offset(-1, 0), CHANNEL_TYPE_LUMA);
//上侧CU可用,且已解码
bool isAboveAvail = (cs.getCURestricted(posLT.offset(0, -1), cu, CHANNEL_TYPE_LUMA) != NULL) && cs.isDecomp(posLT.offset(0, -1), CHANNEL_TYPE_LUMA);
// ----- Step 1: unfiltered reference samples -----
// ----- 步骤 1:获取未滤波的参考像素 -------
//若当前预测区域是当前CU的第一块区域
if (cu.blocks[area.compID].x == area.x && cu.blocks[area.compID].y == area.y)
{
//当前块的未滤波参考缓存
Pel *refBufUnfiltered = m_refBuffer[area.compID][PRED_BUF_UNFILTERED];
// With the first subpartition all the CU reference samples are fetched at once in a single call to xFillReferenceSamples
//通过第一个子块,一次调用xFillReferenceSamples即可获取所有的CU参考采样点。
//若为水平划分
if (cu.ispMode == HOR_INTRA_SUBPARTITIONS)
{
//左侧参考长度为CUheight * 2
m_leftRefLength = cu.Y().height << 1;
//上侧参考长度为CUwidth * 2
m_topRefLength = cu.Y().width + area.width;
}
//若为垂直划分
else
{
//左侧参考长度为CUheight * 2
m_leftRefLength = cu.Y().height + area.height;
//上侧参考长度为CUwidth * 2
m_topRefLength = cu.Y().width << 1;
}
//获取参考像素,将其存至refBufUnfiltered
xFillReferenceSamples(cs.picture->getRecoBuf(cu.Y()), refBufUnfiltered, cu.Y(), cu);
// After having retrieved all the CU reference samples, the number of reference samples is now adjusted for the current subpartition
//检索完所有的CU参考采样点后,现在针对当前子块调整参考采样点的数量
//划分侧参考长度为 CU在对应侧尺寸 + 预测区域对应侧尺寸
//不划分测参考长度为 2*CU在对应侧尺寸
m_topRefLength = cu.blocks[area.compID].width + area.width;
m_leftRefLength = cu.blocks[area.compID].height + area.height;
}
//若当前预测区域不是当前CU的第一块区域
else
{
//左侧和上侧的参考长度都为CUsize + areasize
m_topRefLength = cu.blocks[area.compID].width + area.width;
m_leftRefLength = cu.blocks[area.compID].height + area.height;
//水平预测尺寸
const int predSizeHor = m_topRefLength;
//垂直预测尺寸
const int predSizeVer = m_leftRefLength;
//若为水平划分
if (cu.ispMode == HOR_INTRA_SUBPARTITIONS)
{
//解码缓存
Pel* src = recBuf.bufAt(0, -1);
//左侧参考列
Pel *ref = m_refBuffer[area.compID][PRED_BUF_UNFILTERED] + m_refBufferStride[area.compID];
//左侧CU可用
if (isLeftAvail)
{
//参考列设置为原左侧参考列上移预测单元高度
for (int i = 0; i <= 2 * cu.blocks[area.compID].height - area.height; i++)
{
ref[i] = ref[i + area.height];
}
}
//左侧CU不可用
else
{
//参考列设置用已解码的上侧相邻块最后一行第一个值填充
for (int i = 0; i <= predSizeVer; i++)
{
ref[i] = src[0];
}
}
//上侧参考行
Pel *dst = m_refBuffer[area.compID][PRED_BUF_UNFILTERED] + 1;
dst[-1] = ref[0];
//上侧参考行0~预测区域宽度用已解码缓存(ISP前一子块)最后一行填充
for (int i = 0; i < area.width; i++)
{
dst[i] = src[i];
}
//已解码缓存(ISP前一子块)最后一行最后一个值
Pel sample = src[area.width - 1];
dst += area.width;
//上侧参考行剩下区域用已解码缓存(ISP前一子块)最后一行最后一个值填充
for (int i = 0; i < predSizeHor - area.width; i++)
{
dst[i] = sample;
}
}//if (cu.ispMode == HOR_INTRA_SUBPARTITIONS)
//若为垂直划分
else
{
//解码缓存
Pel* src = recBuf.bufAt(-1, 0);
//上侧参考行
Pel *ref = m_refBuffer[area.compID][PRED_BUF_UNFILTERED];
//上侧CU可用
if (isAboveAvail)
{
//参考行设置为原上侧参考行左移预测单元宽度
for (int i = 0; i <= 2 * cu.blocks[area.compID].width - area.width; i++)
{
ref[i] = ref[i + area.width];
}
}
//上侧CU不可用
else
{
//参考行设置用已解码的左侧相邻块最后一列第一个值填充
for (int i = 0; i <= predSizeHor; i++)
{
ref[i] = src[0];
}
}
//左侧参考列
Pel *dst = m_refBuffer[area.compID][PRED_BUF_UNFILTERED] + m_refBufferStride[area.compID] + 1;
dst[-1] = ref[0];
//左侧参考列0~预测区域高度用已解码缓存(ISP前一子块)最后一列填充
for (int i = 0; i < area.height; i++)
{
*dst = *src;
src += recBuf.stride;
dst++;
}
//已解码缓存(ISP前一子块)最后一列最后一个值
Pel sample = src[-recBuf.stride];
//左侧参考行剩下区域用已解码缓存(ISP前一子块)最后一列最后一个值填充
for (int i = 0; i < predSizeVer - area.height; i++)
{
*dst = sample;
dst++;
}
}//若为垂直划分
}//若当前预测区域不是当前CU的第一块区域
// ----- Step 2: filtered reference samples -----
// ----- 参考像素滤波 -----
if (m_ipaParam.refFilterFlag || forceRefFilterFlag)
{
Pel *refBufUnfiltered = m_refBuffer[area.compID][PRED_BUF_UNFILTERED];
Pel *refBufFiltered = m_refBuffer[area.compID][PRED_BUF_FILTERED];
xFilterReferenceSamples(refBufUnfiltered, refBufFiltered, area, *cs.sps, cu.firstPU->multiRefIdx);
}
}
二、函数结构
1.参数初始化
2.获取未滤波的参考像素
(1)当前预测区域是当前CU的第一块区域
左侧和上侧的参考像素长度都设置为当前CU的相应尺寸的两倍,调用xFillReferenceSamples函数获取全部未滤波的参考像素。
将参考像素长度设置为划分侧:AreaSize + CUSize;非划分侧:2CUSize。
(2)当前预测区域不是当前CU的第一块区域
左侧和上侧的参考长度都为CUSize + AreaSize。
划分侧参考像素可用时,划分侧参考像素设置为原参考像素整体上移(或左移)AreaSize。划分侧参考像素不可用时,划分侧参考像素设置为上一划分单元的最后一行(最后一列)第一个值。
不划分侧参考像素0~预测区域不划分侧尺寸的值设置为上一划分单元最后一行(最后一列)对应位置的值,预测区域不划分侧尺寸之后的不划分侧参考像素由上一划分单元最后一行(最后一列)最后位置的值填充。
3.参考像素滤波
若参考像素滤波标志有效,调用函数xFilterReferenceSamples对参考像素进行滤波。
上一篇:H.266/VVC-VTM代码学习-帧内预测16-解码端xIntraRecBlk完成TU指定分量帧内重建(附ISP变换和预测尺寸不同、JCCR、LMCS简述)
下一篇:H.266/VVC-VTM代码学习18-自适应QP设置(Adaptive QP)