帧内预测的参考像素值的获取在标准文档的8.4.4.2.2中指明。
举例说明,当前demo中,我们用来单步调试的第一个CU为64×64像素大小,那么参考像素由两部分组成,一部分包含2×64+1=129个,另一部分包含2×64=128个像素。这两部分分别作为垂直和水平方向上的预测数据。在编码的过程中,根据预测数据是否可得,共分为两种情况:
第一种:所有的预测数据都不可得。最直观的情况就是一帧数据中的第一个CU,该CU左侧和上方的数据都不存在,如下图所示。此时所有的预测数据都会制定一个默认值,计算方法为:1 << (bitDepth - 1);(图中的格子数只是示意图,不代表CU的像素大小和参考像素的个数)。
第二种:至少有一个像素点是可获得的,如下图所示。如果参考数据中的第一个点是不可获得的,那么将沿着当前CU的边缘,先从下到上,后从左到右查找第一个可获得的参考点并赋给第一个点;对于其他的点,如果不可得,那么就直接复制它前面一个参考点的值。如果所有点都是可获得的,那么参考数据直接使用该值就可以了。
基本算法已经明了,接下来研究一下HM中的实现。代码如下:
Void TComPattern::fillReferenceSamples(Int bitDepth, Pel* piRoiOrigin, Int* piAdiTemp, Bool* bNeighborFlags, Int iNumIntraNeighbor, Int iUnitSize, Int iNumUnitsInCu, Int iTotalUnits, UInt uiCuWidth, UInt uiCuHeight, UInt uiWidth, UInt uiHeight, Int iPicStride, Bool bLMmode )
{
Pel* piRoiTemp;
Int i, j;
Int iDCValue = 1 << (bitDepth - 1);
if (iNumIntraNeighbor == 0)//所欲参考点均不可得,按照DC模式设置参考点
{
// Fill border with DC value
for (i=0; i<uiWidth; i++)
{
piAdiTemp[i] = iDCValue;//<span style="font-family: Arial, Helvetica, sans-serif;">piAdiTemp指向数据接收内存,保存了实际的参考像素数组的地址;</span>
}
for (i=1; i<uiHeight; i++)
{
piAdiTemp[i*uiWidth] = iDCValue;
}
}
else if (iNumIntraNeighbor == iTotalUnits)//所有参考点都可获得,直接设为当前CU的参考值
{
// Fill top-left border with rec. samples
piRoiTemp = piRoiOrigin - iPicStride - 1;//左上角边界,其实就是CU左上角的一个点
piAdiTemp[0] = piRoiTemp[0];
// Fill left border with rec. samples
piRoiTemp = piRoiOrigin - 1;//当前CU左上顶点的左边像素
if (bLMmode)
{
piRoiTemp --; // move to the second left column
}
for (i=0; i<uiCuHeight; i++)//将左列的像素设为参考像素
{
piAdiTemp[(1+i)*uiWidth] = piRoiTemp[0];
piRoiTemp += iPicStride;
}
// Fill below left border with rec. samples
for (i=0; i<uiCuHeight; i++)//继续将该列下面的像素值作为左下方的参考像素
{
piAdiTemp[(1+uiCuHeight+i)*uiWidth] = piRoiTemp[0];
piRoiTemp += iPicStride;
}
// Fill top border with rec. samples
piRoiTemp = piRoiOrigin - iPicStride;//指向当前CU左上角像素的正上方
for (i=0; i<uiCuWidth; i++)
{
piAdiTemp[1+i] = piRoiTemp[i];
}
// Fill top right border with rec. samples
piRoiTemp = piRoiOrigin - iPicStride + uiCuWidth;//当前CU右上方的像素起始位置
for (i=0; i<uiCuWidth; i++)
{
piAdiTemp[1+uiCuWidth+i] = piRoiTemp[i];
}
}
else // reference samples are partially available
{
Int iNumUnits2 = iNumUnitsInCu<<1;
Int iTotalSamples = iTotalUnits*iUnitSize;
Pel piAdiLine[5 * MAX_CU_SIZE];
Pel *piAdiLineTemp;
Bool *pbNeighborFlags;
Int iNext, iCurr;
Pel piRef = 0;
// Initialize
for (i=0; i<iTotalSamples; i++)//用均值模式进行初始化
{
piAdiLine[i] = iDCValue;
}
// Fill top-left sample
piRoiTemp = piRoiOrigin - iPicStride - 1;//指向重建像素中当前CU的左上角位置
piAdiLineTemp = piAdiLine + (iNumUnits2*iUnitSize);
pbNeighborFlags = bNeighborFlags + iNumUnits2;
if (*pbNeighborFlags)//如果左上方的参考数据可用
{
piAdiLineTemp[0] = piRoiTemp[0];
for (i=1; i<iUnitSize; i++)
{
piAdiLineTemp[i] = piAdiLineTemp[0];
}
}
// Fill left & below-left samples
piRoiTemp += iPicStride;//从左上顶点的左上角移动到左方
if (bLMmode)
{
piRoiTemp --; // move the second left column
}
piAdiLineTemp--;//缓存指针前移一位
pbNeighborFlags--;//可用性标记指针前移一位
for (j=0; j<iNumUnits2; j++)
{
if (*pbNeighborFlags)
{
for (i=0; i<iUnitSize; i++)//判断过程分组进行处理,如对于一个32×32的CU,左侧和左下侧共64个预测点,总共进行16×4次赋值
{
piAdiLineTemp[-i] = piRoiTemp[i*iPicStride];
}
}
piRoiTemp += iUnitSize*iPicStride;
piAdiLineTemp -= iUnitSize;
pbNeighborFlags--;
}
// Fill above & above-right samples
piRoiTemp = piRoiOrigin - iPicStride;//水平方向上的处理与垂直方向类似
piAdiLineTemp = piAdiLine + ((iNumUnits2+1)*iUnitSize);
pbNeighborFlags = bNeighborFlags + iNumUnits2 + 1;
for (j=0; j<iNumUnits2; j++)
{
if (*pbNeighborFlags)
{
for (i=0; i<iUnitSize; i++)
{
piAdiLineTemp[i] = piRoiTemp[i];
}
}
piRoiTemp += iUnitSize;
piAdiLineTemp += iUnitSize;
pbNeighborFlags++;
}
// Pad reference samples when necessary
iCurr = 0;
iNext = 1;
piAdiLineTemp = piAdiLine;//指向参考数组的起点,见上图
while (iCurr < iTotalUnits)//遍历给定的参考点
{
if (!bNeighborFlags[iCurr])//某个点不可获得
{
if(iCurr == 0)//第一个参考点就找不到
{
while (iNext < iTotalUnits && !bNeighborFlags[iNext])//找到第一个可以获得的点
{
iNext++;
}
piRef = piAdiLine[iNext*iUnitSize];//记录该点的值
// Pad unavailable samples with new value
while (iCurr < iNext)//将找到的可用参考点赋给第一个参考点(以4个像素点一组为单位)
{
for (i=0; i<iUnitSize; i++)
{
piAdiLineTemp[i] = piRef;
}
piAdiLineTemp += iUnitSize;
iCurr++;
}
}
else
{
piRef = piAdiLine[iCurr*iUnitSize-1];//不可用的点不是第一个参考点,查找前一个可用的点为其赋值。
for (i=0; i<iUnitSize; i++)
{
piAdiLineTemp[i] = piRef;
}
piAdiLineTemp += iUnitSize;
iCurr++;
}
}
else//当前点可用,pass
{
piAdiLineTemp += iUnitSize;
iCurr++;
}
}
// Copy processed samples 输出前面所准备的数据
piAdiLineTemp = piAdiLine + uiHeight + iUnitSize - 2;
for (i=0; i<uiWidth; i++)
{
piAdiTemp[i] = piAdiLineTemp[i];
}
piAdiLineTemp = piAdiLine + uiHeight - 1;
for (i=1; i<uiHeight; i++)
{
piAdiTemp[i*uiWidth] = piAdiLineTemp[-i];
}
}
}