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