帧内预测的参考像素值的获取在标准文档的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];
- }
- }
- }