HEVC代码阅读- -fillReferenceSamples
看到这个函数的注释写的很好,所以搬运过来,日后慢慢消化吸收。
文章出处:
------ ----- ---- ------ ------- ------以下为正文----- ----- ---- -------- ---- -----------
fillReferenceSamples( )//填充参考像素值
iNumIntraNeighbor标识参考像素可用块数,以iUnitSize块长为单位。
分三种情况,1.像素全部可用,往相应位置填;2.像素部分可用,不可用的填默认值;3.像素不可用,全部填默认值;
在像素全部不可用时,代码不按一个一个像素复制,而是划分为iUnitSize大小的块,长宽都按多少个块长为单位,目前不了解为什么这么设置,在后面的分析中会了解。
Void TComPattern::fillReferenceSamples( TComDataCU* pcCU, 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; //!< piRoiOrgin指向重建Yuv图像对应于当前PU所在位置的首地址,piRoiTemp用于指向所感兴趣的重建Yuv的位置,piAdiTemp
Int i, j;
Int iDCValue = ( 1<<( g_uiBitDepth + g_uiBitIncrement - 1) );
if (iNumIntraNeighbor == 0) // all samples are not available
{
// Fill border with DC value
for (i=0; i<uiWidth; i++) //!< AboveLeft + Above + AboveRight
{
piAdiTemp[i] = iDCValue;
}
for (i=1; i<uiHeight; i++) //!< Left + BelowLeft
{
piAdiTemp[i*uiWidth] = iDCValue;
}
}
else if (iNumIntraNeighbor == iTotalUnits) // all samples are available
{
// Fill top-left border with rec. samples
piRoiTemp = piRoiOrigin - iPicStride - 1; //!< 左上
piAdiTemp[0] = piRoiTemp[0];
// Fill left border with rec. samples
piRoiTemp = piRoiOrigin - 1; //!< 左
if (bLMmode) //!< bLMmode 默认值为false
{
piRoiTemp --; // move to the second left column
}
for (i=0; i<uiCuHeight; i++)
{
piAdiTemp[(1+i)*uiWidth] = piRoiTemp[0]; //!< 每个参考样点赋值为对应位置重建Yuv样点值
piRoiTemp += iPicStride; //!< 指向重建Yuv下一行
}
// Fill below left border with rec. samples
for (i=0; i<uiCuHeight; i++) //!< 左下
{
piAdiTemp[(1+uiCuHeight+i)*uiWidth] = piRoiTemp[0]; //!< 每个参考样点赋值为对应位置重建Yuv样点值
piRoiTemp += iPicStride;
}
// Fill top border with rec. samples
piRoiTemp = piRoiOrigin - iPicStride; //!< 重新指向重建Yuv的上方
for (i=0; i<uiCuWidth; i++)
{
piAdiTemp[1+i] = piRoiTemp[i]; //!< 每个参考样点赋值为对应位置重建Yuv样点值
}
// Fill top right border with rec. samples
piRoiTemp = piRoiOrigin - iPicStride + uiCuWidth; //!< 指向右上
for (i=0; i<uiCuWidth; i++)
{
piAdiTemp[1+uiCuWidth+i] = piRoiTemp[i]; //!< 每个参考样点赋值为对应位置重建Yuv样点值
}
}
else // reference samples are partially available
{
Int iNumUnits2 = iNumUnitsInCu<<1;
Int iTotalSamples = iTotalUnits*iUnitSize; //!< neighboring samples的总数,iTotalUnits以4x4块为单位,iUnitSize为块的大小
Pel piAdiLine[5 * MAX_CU_SIZE];
Pel *piAdiLineTemp; //!<临时存储用于填充neighboring samples的样点值
Bool *pbNeighborFlags;
Int iNext, iCurr;
Pel piRef = 0; //!< 存储临时样点值
// Initialize
for (i=0; i<iTotalSamples; i++) //!< 先将所有样点值赋值为DC值
{
piAdiLine[i] = iDCValue;
}
// Fill top-left sample
piRoiTemp = piRoiOrigin - iPicStride - 1; //!< 指向重建Yuv左上角
piAdiLineTemp = piAdiLine + (iNumUnits2*iUnitSize); //!< piAdiLine的扫描顺序为左下到左上,再从左到右上
pbNeighborFlags = bNeighborFlags + iNumUnits2; //!< 标记neighbor可用性的数组同样移动至左上角
if (*pbNeighborFlags) //!< 如果左上角可用,则左上角4个像素点均赋值为重建Yuv左上角的样点值
{
piAdiLineTemp[0] = piRoiTemp[0];
for (i=1; i<iUnitSize; i++)
{
piAdiLineTemp[i] = piAdiLineTemp[0];
}
}
// Fill left & below-left samples
piRoiTemp += iPicStride; //!< piRoiTemp指向重建Yuv的左边界
if (bLMmode)
{
piRoiTemp --; // move the second left column
}
piAdiLineTemp--; //!< 移动指针置左边界
pbNeighborFlags--; //!< 移动指针置左边界
for (j=0; j<iNumUnits2; j++) //!< 从左往左下扫描
{
if (*pbNeighborFlags) //!< 如果可用
{
for (i=0; i<iUnitSize; i++) //!< 每个4x4块里的4个样点分别被赋值为对应位置的重建Yuv的样点值
{
piAdiLineTemp[-i] = piRoiTemp[i*iPicStride];
}
}
piRoiTemp += iUnitSize*iPicStride; //!< 指针挪到下一个行(以4x4块为单位,即实际上下移了4行)
piAdiLineTemp -= iUnitSize; //!< 指针下移
pbNeighborFlags--; //!< 指针下移
}
// Fill above & above-right samples
piRoiTemp = piRoiOrigin - iPicStride; //!< piRoiTemp 指向重建Yuv的上边界
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]; //!< 每个4x4块里的4个样点分别被赋值为对应位置的重建Yuv的样点值
}
}
piRoiTemp += iUnitSize; //!< 指针右移
piAdiLineTemp += iUnitSize; //!< 指针右移
pbNeighborFlags++; //!< 指针右移
}
// Pad reference samples when necessary
iCurr = 0;
iNext = 1;
piAdiLineTemp = piAdiLine; //!< 指向左下角(纵坐标最大的那个位置,即扫描起点)
while (iCurr < iTotalUnits) //!< 遍历所有neighboring samples
{
if (!bNeighborFlags[iCurr]) //!< 该点不可用
{
if(iCurr == 0) //!< 第一个点就不可用
{
while (iNext < iTotalUnits && !bNeighborFlags[iNext]) //!< 找到第1个可用点
{
iNext++;
}
piRef = piAdiLine[iNext*iUnitSize]; //!< 保存该可用点的样点值
// Pad unavailable samples with new value
while (iCurr < iNext) //!< 使用保存下来的第一个可用点的样点值赋值给在其之前被标记为不可用的点
{
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 //!< 当前点可用,继续检查下一点
{
piAdiLineTemp += iUnitSize;
iCurr++;
}
}
// Copy processed samples
piAdiLineTemp = piAdiLine + uiHeight + iUnitSize - 2; //!< piAdiLineTemp = (piAdiLine + 128) + 3,跳过之前对左上角扩充的3个像素点
for (i=0; i<uiWidth; i++) //!< 将最终结果拷贝到左上、上、右上边界
{
piAdiTemp[i] = piAdiLineTemp[i];
}
piAdiLineTemp = piAdiLine + uiHeight - 1; //!< uiHeight = uiCUHeight2 + 1
for (i=1; i<uiHeight; i++) //!< 将最终结果拷贝到左和左下边界
{
piAdiTemp[i*uiWidth] = piAdiLineTemp[-i]; //!< piAdiLineTemp下标为-i是因为赋值方向与实际存储方向是相反的
}
}
}