二、HEVC代码学习之帧内预测编码参考像素赋值函数fillReferenceSamples
程序员文章站
2022-06-07 14:14:56
...
fillReferenceSamples
函数的作用:它的主要功能是在真正进行帧内预测之前。使用重建后的YUV图像对当前PU的相邻点进行赋值,为接下来的角度预测提供参考点的样值。
fillReferenceSamples
函数的算法流程:
- 如果所有相邻点的均不可用(还没有被编码重建),则参考点的样值均被赋值为DC值;
- 如果所有相邻点均可用,则参考样值都会被赋值为重建YUV图像中与参考点位置相同的样点值;
- 如果上面两种情况不满足,则按照从左下往左上的,从左上往右上的顺序进行遍历,如果第一个点不可用,则使用下一个可用点对应的重建YUV样点值对其进行赋值;对于除第一个点外的其他相邻点,则使用它的前一个样点值进行赋值,第一个步骤就保证了前一个样点值一定存在。
提醒:如果想具体了解HEVC帧内编码的朋友们,可以参考我写的这篇博客:
HEVC帧内编码的具体原理详解
Void fillReferenceSamples( const Int bitDepth, //一个像素点用几位表示
#if O0043_BEST_EFFORT_DECODING
const Int bitDepthDelta,
#endif
//piRoiOrigin指向重建的YUV图像对应于当前PU所在位置的首地址
//该piIntraTemp指针指向当前PU的参考点的位置,piIntraTemp[i]表示PU上方像素点的位置, piIntraTemp[i*uiWidth]指向PU左边像素点的位置
const Pel* piRoiOrigin,
Pel* piIntraTemp,
//最左下方第一个位置可用标志
const Bool* bNeighborFlags,
//iNumIntraNeighbor用于记录当前PU可用相邻参考点的个数
const Int iNumIntraNeighbor,
//一个单元的宽度的像素个数
const Int unitWidth,
//一个单元的高度的像素个数
const Int unitHeight,
//unitWidth用于记录当前PU左上方,上方,右上方需要填充的参考点的个数
const Int iAboveUnits,
//unitWidth用于记录当前PU左上方,左边,左下方需要填充的参考点的个数
const Int iLeftUnits,
const UInt uiWidth,
const UInt uiHeight,
//iPicStride用于左边像素点遍历,如果是上方像素点遍历则直接更新指针的索引值i即可,简单来说,就是用于上下单元的切换
const Int iPicStride )
{
//piRoiTemp用于指向所感兴趣的重建YUV的位置地址
const Pel* piRoiTemp;
Int i, j;
//计算出DC值,用整形变量iDCValues存储
Int iDCValue = 1 << (bitDepth - 1);
//计算所需参考点的总个数,左边的参考点个数+右边参考点个数,在加上一个左上角的像素点
const Int iTotalUnits = iAboveUnits + iLeftUnits + 1; //+1 for top-left
//当前PU的相邻像素的可用个数为0,则参考点像素用DC值进行填充
if (iNumIntraNeighbor == 0)
{
// Fill border with DC value
for (i=0; i<uiWidth; i++)
{
piIntraTemp[i] = iDCValue;
}
for (i=1; i<uiHeight; i++)
{
piIntraTemp[i*uiWidth] = iDCValue;
}
}
//如果所有的相邻参考点像素值都可用
else if (iNumIntraNeighbor == iTotalUnits)
{
// Fill top-left border and top and top right with rec. samples
//对左上角的参考像素的样值、上方的参考点样值、右上方的参考点样值进行填充
piRoiTemp = piRoiOrigin - iPicStride - 1; //该piRoiTemp指针指向左上位置,piRoiOrigin指针向上移动一个像素,载左移动一个像素,到达左上方位置
for (i=0; i<uiWidth; i++) //左上方、和上方、右上方位置填充
{
#if O0043_BEST_EFFORT_DECODING
piIntraTemp[i] = piRoiTemp[i] << bitDepthDelta;
#else
piIntraTemp[i] = piRoiTemp[i];
#endif
}
// Fill left and below left border with rec. samples
//填充左边和左下方参考点的像素值
piRoiTemp = piRoiOrigin - 1; //该指针指向重构像图像PU位置的左边第一个像素点
for (i=1; i<uiHeight; i++)
{
#if O0043_BEST_EFFORT_DECODING
piIntraTemp[i*uiWidth] = (*(piRoiTemp)) << bitDepthDelta;
#else
piIntraTemp[i*uiWidth] = *(piRoiTemp);
#endif
piRoiTemp += iPicStride; //往下移动一个像素
}
}
//相邻参考点像素部分可用的情况
else // reference samples are partially available
{
// all above units have "unitWidth" samples each, all left/below-left units have "unitHeight" samples each
//iTotalSamples计算所有样值点的个数
const Int iTotalSamples = (iLeftUnits * unitHeight) + ((iAboveUnits + 1) * unitWidth);
//定义一个数组,用于临时存储用于填充相邻像素的样点值
Pel piIntraLine[5 * MAX_CU_SIZE];
Pel *piIntraLineTemp;
const Bool *pbNeighborFlags;
// Initialize 初始化,先将所有的样点值赋值为DC值
for (i=0; i<iTotalSamples; i++)
{
piIntraLine[i] = iDCValue;
}
// Fill top-left sample 填充左上角的样点值
//指向参考帧中当前PU所在位置的左上角
piRoiTemp = piRoiOrigin - iPicStride - 1;
//piIntraLine的扫描顺序为左下到左上,再从左到右上
piIntraLineTemp = piIntraLine + (iLeftUnits * unitHeight);
//相邻像素的可用标志首先位于左下方最后一个像素,把它移动到左上方,用于标志左上方像素是否可用
pbNeighborFlags = bNeighborFlags + iLeftUnits;
//如果左上方的样值可用
if (*pbNeighborFlags)
{
#if O0043_BEST_EFFORT_DECODING
Pel topLeftVal=piRoiTemp[0] << bitDepthDelta;
#else
Pel topLeftVal=piRoiTemp[0];
#endif
for (i=0; i<unitWidth; i++)
{
piIntraLineTemp[i] = topLeftVal;
}
}
// Fill left & below-left samples (downwards)
//填充左边和左下方的像素值
//piRoiTemp指向重建的YUV图像当前PU位置的左边界,从左上方向下移动一行
piRoiTemp += iPicStride;
//移动piIntraLineTemp指针至左边界
piIntraLineTemp--;
//移动pbNeighborFlags指针至左边界
pbNeighborFlags--;
//从左往左下开始扫描遍历,检查可用标志,并对相邻像素进行填充
for (j=0; j<iLeftUnits; j++)
{
if (*pbNeighborFlags)
{
for (i=0; i<unitHeight; i++)
{
#if O0043_BEST_EFFORT_DECODING
piIntraLineTemp[-i] = piRoiTemp[i*iPicStride] << bitDepthDelta;
#else
piIntraLineTemp[-i] = piRoiTemp[i*iPicStride];
#endif
}
}
//piRoiTemp指针移动到下一行,以unitHeight为单为(4个像素点)实际上移动了4行
piRoiTemp += unitHeight*iPicStride;
//piIntraLineTemp指针下移
piIntraLineTemp -= unitHeight;
//pbNeighborFlags指针下移
pbNeighborFlags--;
}
// Fill above & above-right samples (left-to-right) (each unit has "unitWidth" samples)填充上方和左上方的样本值,每一个单元的长度为unitWidth像素
//piRoiTemp指针指向参考图像PU位置的上方
piRoiTemp = piRoiOrigin - iPicStride;
// offset line buffer by iNumUints2*unitHeight (for left/below-left) + unitWidth (for above-left)偏移line缓存器piIntraLineTemp,步长为(iLeftUnits * unitHeight) + unitWidth,使之指向上边界
piIntraLineTemp = piIntraLine + (iLeftUnits * unitHeight) + unitWidth;
//pbNeighborFlags指向上边界第一个单元位置
pbNeighborFlags = bNeighborFlags + iLeftUnits + 1;
//从左至有开始扫描遍历
for (j=0; j<iAboveUnits; j++)
{
if (*pbNeighborFlags)
{
for (i=0; i<unitWidth; i++)
{
#if O0043_BEST_EFFORT_DECODING
piIntraLineTemp[i] = piRoiTemp[i] << bitDepthDelta;
#else
piIntraLineTemp[i] = piRoiTemp[i];
#endif
}
}
piRoiTemp += unitWidth; //piRoiTemp指针右移
piIntraLineTemp += unitWidth; //piIntraLineTemp指针右移
pbNeighborFlags++; //pbNeighborFlags指针右移
}
// Pad reference samples when necessary必要时补齐参考样本
Int iCurrJnit = 0;
//piIntraLineCur指向左下角,纵坐标最大的那个位置即扫描起点
Pel *piIntraLineCur = piIntraLine;
const UInt piIntraLineTopRowOffset = iLeftUnits * (unitHeight - unitWidth);
//检查最下方参考单元的可用性,不可用
if (!bNeighborFlags[0])
{
// very bottom unit of bottom-left; at least one unit will be valid.左下方至少有一个单元可用
{
Int iNext = 1;
//找到第一个可用点
while (iNext < iTotalUnits && !bNeighborFlags[iNext])
{
iNext++;
}
Pel *piIntraLineNext = piIntraLine + ((iNext < iLeftUnits) ? (iNext * unitHeight) : (piIntraLineTopRowOffset + (iNext * unitWidth)));
const Pel refSample = *piIntraLineNext;
//保存该可用的样值点
// Pad unavailable samples with new value用新的样值更新不可用的样值点
Int iNextOrTop = std::min<Int>(iNext, iLeftUnits);
// fill left column填充左边列
while (iCurrJnit < iNextOrTop)
{
for (i=0; i<unitHeight; i++)
{
piIntraLineCur[i] = refSample;
}
piIntraLineCur += unitHeight;
iCurrJnit++;
}
// fill top row填充上边行
while (iCurrJnit < iNext)
{
for (i=0; i<unitWidth; i++)
{
piIntraLineCur[i] = refSample;
}
piIntraLineCur += unitWidth;
iCurrJnit++;
}
}
}
// pad all other reference samples.其他点不可用的情况,使用该点前一个可用的样值点进行更新
while (iCurrJnit < iTotalUnits)
{
if (!bNeighborFlags[iCurrJnit]) // samples not available
{
{
const Int numSamplesInCurrUnit = (iCurrJnit >= iLeftUnits) ? unitWidth : unitHeight;
const Pel refSample = *(piIntraLineCur-1);
for (i=0; i<numSamplesInCurrUnit; i++)
{
piIntraLineCur[i] = refSample;
}
piIntraLineCur += numSamplesInCurrUnit;
iCurrJnit++;
}
}
else //当前点可用,继续检查下一个点
{
piIntraLineCur += (iCurrJnit >= iLeftUnits) ? unitWidth : unitHeight;
iCurrJnit++;
}
}
// Copy processed samples
piIntraLineTemp = piIntraLine + uiHeight + unitWidth - 2;
// top left, top and top right samples将最终结果拷贝到当前PU左上、上、右上参考位置
for (i=0; i<uiWidth; i++)
{
piIntraTemp[i] = piIntraLineTemp[i];
}
//将最终结果拷贝到当前PU左、左下方参考位置
piIntraLineTemp = piIntraLine + uiHeight - 1;
for (i=1; i<uiHeight; i++)
{
piIntraTemp[i*uiWidth] = piIntraLineTemp[-i];
}
}
}
更多关于视频编码的知识和资源,更精致的文章排版,欢迎关注博主微信公众号,一起交流、学习、进步!!!
上一篇: 1025 反转链表 (25分)