HM编码器代码阅读(36)——帧内预测(三)帧内预测之参考像素块的预处理和滤波

时间:2021-10-28 11:38:18

参考像素块的预处理和滤波


预处理

    注意:如果没有特别说明,操作的对象都是亮度块(Luma)     主要是判断参考像素是否可用,然后根据重建像素构建参考像素(由fillReferenceSamples完成)。     特别说一句:虽然预测是在PU的基础上进行处理的,但是HM中的帧内预测过程是以TU为单位进行的,标准规定帧内预测的时候,一个PU可以按照四叉树的方式划分为TU,一个PU内的所有TU贡献一种预测模式。

对参考像素进行滤波

     滤波的目的是为了更好利用邻近像素之间的相关性,提高预测精度      针对不同大小的TU选择不同数量的模式进行滤波(所谓滤波就是,如果当前TU选择了某一个模式进行预测,那么需要判断,对于该模式,是否需要对参考像素进行滤波)           1、DC模式和以及4x4大小的TU不需要进行滤波           2、32x32的TU:除了水平模式(模式10)和垂直模式(模式26),其他模式都需要进行滤波           3、16x16的TU:在32x32TU的基础上去除最接近水平和垂直方向的4个模式——模式9、模式11、模式25、模式27           4、8x8的TU:仅对3个45度的模式(模式2,18,34)以及planar模式进行滤波     有两种滤波方式:常规滤波,强滤波(只处理32x32的TU

代码实现

入口函数

入口函数是initAdiPattern

1、检查周围参考块的像素的可用性
2、对于不可用的像素值,使用某种方法对其进行计算,fillReferenceSamples使用重建后的Yuv图像对当前PU的相邻样点进行赋值,为接下来进行的角度预测提供参考样点值
3、对参考像素值进行滤波处理,滤波还分为强滤波和普通滤波,只有下面几种情况才需要滤波:
    32x32的TU:除了模式10、26之外的所有模式都要进行滤波
    16x16的TU:在32x32TU的基础上去除四个模式——9,11,25,27

    8x8的TU:仅针对3个45度角的模式(2,18,34)以及planar模式进行滤波

其中,强滤波只处理32x32的TU

/*
** 主要功能:
** 1、检测参考像素的可用性
** 2、对于不可用的参考像素,需要用某种方法进行替换,由函数fillReferenceSamples完成
** 3、参考像素的平滑滤波
*/
Void TComPattern::initAdiPattern( TComDataCU* pcCU, UInt uiZorderIdxInPart, UInt uiPartDepth, Int* piAdiBuf, Int iOrgBufStride, Int iOrgBufHeight, Bool& bAbove, Bool& bLeft, Bool bLMmode )
{

Pel* piRoiOrigin;
Int* piAdiTemp;

UInt uiCuWidth = pcCU->getWidth(0) >> uiPartDepth;// 64 (起始都是LCU,所以是64)
UInt uiCuHeight = pcCU->getHeight(0)>> uiPartDepth;// 64

UInt uiCuWidth2 = uiCuWidth<<1; // 128两倍
UInt uiCuHeight2 = uiCuHeight<<1; // 128
UInt uiWidth;
UInt uiHeight;
Int iPicStride = pcCU->getPic()->getStride(); // 336 步幅
Int iUnitSize = 0;
Int iNumUnitsInCu = 0;
Int iTotalUnits = 0;

// 邻居的标志
Bool bNeighborFlags[4 * MAX_NUM_SPU_W + 1];
Int iNumIntraNeighbor = 0;

UInt uiPartIdxLT, uiPartIdxRT, uiPartIdxLB;

// 地址转换
pcCU->deriveLeftRightTopIdxAdi( uiPartIdxLT, uiPartIdxRT, uiZorderIdxInPart, uiPartDepth );
pcCU->deriveLeftBottomIdxAdi ( uiPartIdxLB, uiZorderIdxInPart, uiPartDepth );

iUnitSize = g_uiMaxCUWidth >> g_uiMaxCUDepth; // 4
iNumUnitsInCu = uiCuWidth / iUnitSize; // 一个LCU中所拥有的单元的个数是16
iTotalUnits = (iNumUnitsInCu << 2) + 1; // 总的单元的数量

// 获取左上角是否可用的标志
bNeighborFlags[iNumUnitsInCu*2] = isAboveLeftAvailable( pcCU, uiPartIdxLT );

iNumIntraNeighbor += (Int)(bNeighborFlags[iNumUnitsInCu*2]);
iNumIntraNeighbor += isAboveAvailable ( pcCU, uiPartIdxLT, uiPartIdxRT, bNeighborFlags+(iNumUnitsInCu*2)+1 );
iNumIntraNeighbor += isAboveRightAvailable( pcCU, uiPartIdxLT, uiPartIdxRT, bNeighborFlags+(iNumUnitsInCu*3)+1 );
iNumIntraNeighbor += isLeftAvailable ( pcCU, uiPartIdxLT, uiPartIdxLB, bNeighborFlags+(iNumUnitsInCu*2)-1 );
iNumIntraNeighbor += isBelowLeftAvailable ( pcCU, uiPartIdxLT, uiPartIdxLB, bNeighborFlags+ iNumUnitsInCu -1 );

bAbove = true;
bLeft = true;

uiWidth=uiCuWidth2+1;
uiHeight=uiCuHeight2+1;

if (((uiWidth<<2)>iOrgBufStride)||((uiHeight<<2)>iOrgBufHeight))
{
return;
}

piRoiOrigin = pcCU->getPic()->getPicYuvRec()->getLumaAddr(pcCU->getAddr(), pcCU->getZorderIdxInCU()+uiZorderIdxInPart);
piAdiTemp = piAdiBuf;

// 这个函数很重要,它主要功能是在真正进行帧内预测之前,使用重建后的Yuv图像对当前PU的相邻样点进行赋值,为接下来进行的角度预测提供参考样点值
fillReferenceSamples (g_bitDepthY, piRoiOrigin, piAdiTemp, bNeighborFlags, iNumIntraNeighbor, iUnitSize, iNumUnitsInCu, iTotalUnits, uiCuWidth, uiCuHeight, uiWidth, uiHeight, iPicStride, bLMmode);

Int i;
// generate filtered intra prediction samples

Int iBufSize = uiCuHeight2 + uiCuWidth2 + 1; // left and left above border + above and above right border + top left corner = length of 3. filter buffer

// 一个缓冲中元素的个数
UInt uiWH = uiWidth * uiHeight; // number of elements in one buffer

// 两个过滤缓冲,应该适用于滤波处理
Int* piFilteredBuf1 = piAdiBuf + uiWH; // 1. filter buffer
Int* piFilteredBuf2 = piFilteredBuf1 + uiWH; // 2. filter buffer

Int* piFilterBuf = piFilteredBuf2 + uiWH; // buffer for 2. filtering (sequential)
Int* piFilterBufN = piFilterBuf + iBufSize; // buffer for 1. filtering (sequential)

Int l = 0;
// left border from bottom to top
for (i = 0; i < uiCuHeight2; i++)
{
piFilterBuf[l++] = piAdiTemp[uiWidth * (uiCuHeight2 - i)];
}
// top left corner
piFilterBuf[l++] = piAdiTemp[0];
// above border from left to right
for (i=0; i < uiCuWidth2; i++)
{
piFilterBuf[l++] = piAdiTemp[1 + i];
}

/* 滤波处理(获取到参考像素之后还需要进行滤波)
** 这里的滤波还分为强滤波和普通滤波
**
** 只有下面几种情况才需要滤波
** 32x32的TU:除了模式10、26之外的所有模式都要进行滤波
** 16x16的TU:在32x32TU的基础上去除四个模式——9,11,25,27
** 8x8的TU:仅针对3个45度角的模式(2,18,34)以及planar模式进行滤波
**
** 强滤波只处理32x32的TU
*/
if (pcCU->getSlice()->getSPS()->getUseStrongIntraSmoothing())
{
// 进入这里
Int blkSize = 32;
Int bottomLeft = piFilterBuf[0];// 128 左下角
Int topLeft = piFilterBuf[uiCuHeight2];// 128 左上角
Int topRight = piFilterBuf[iBufSize-1];// 128 右上角
Int threshold = 1 << (g_bitDepthY - 5);// 8 阈值
Bool bilinearLeft = abs(bottomLeft+topLeft-2*piFilterBuf[uiCuHeight]) < threshold;// true 线性插值?二值?
Bool bilinearAbove = abs(topLeft+topRight-2*piFilterBuf[uiCuHeight2+uiCuHeight]) < threshold;// true

if (uiCuWidth>=blkSize && (bilinearLeft && bilinearAbove))
{
// 进入这里
// 7
Int shift = g_aucConvertToBit[uiCuWidth] + 3; // log2(uiCuHeight2)
piFilterBufN[0] = piFilterBuf[0];
piFilterBufN[uiCuHeight2] = piFilterBuf[uiCuHeight2];
piFilterBufN[iBufSize - 1] = piFilterBuf[iBufSize - 1];
for (i = 1; i < uiCuHeight2; i++)
{
piFilterBufN[i] = ((uiCuHeight2-i)*bottomLeft + i*topLeft + uiCuHeight) >> shift;
}

for (i = 1; i < uiCuWidth2; i++)
{
piFilterBufN[uiCuHeight2 + i] = ((uiCuWidth2-i)*topLeft + i*topRight + uiCuWidth) >> shift;
}
}
else
{
// 1. filtering with [1 2 1]
piFilterBufN[0] = piFilterBuf[0];
piFilterBufN[iBufSize - 1] = piFilterBuf[iBufSize - 1];
for (i = 1; i < iBufSize - 1; i++)
{
piFilterBufN[i] = (piFilterBuf[i - 1] + 2 * piFilterBuf[i]+piFilterBuf[i + 1] + 2) >> 2;
}
}
}
else
{
// 1. filtering with [1 2 1]
piFilterBufN[0] = piFilterBuf[0];
piFilterBufN[iBufSize - 1] = piFilterBuf[iBufSize - 1];
for (i = 1; i < iBufSize - 1; i++)
{
piFilterBufN[i] = (piFilterBuf[i - 1] + 2 * piFilterBuf[i]+piFilterBuf[i + 1] + 2) >> 2;
}
}

// fill 1. filter buffer with filtered values
l=0;
for (i = 0; i < uiCuHeight2; i++)
{
piFilteredBuf1[uiWidth * (uiCuHeight2 - i)] = piFilterBufN[l++];
}
piFilteredBuf1[0] = piFilterBufN[l++];
for (i = 0; i < uiCuWidth2; i++)
{
piFilteredBuf1[1 + i] = piFilterBufN[l++];
}
}

根据重建像素来构建参考像素

操作步骤如下:

1、相邻点均不可用,则参考像素均被赋值为DC值
2、相邻点均可用,那么参考像素均被赋值为重建YUV图像中相同位置的像素
3、如果不满足上述两个条件,则按照从左下往左上,从左上往右上的扫描顺序进行遍历,如果第一个点不可用,则使用下一个可用点对应的重建像素对其进行赋值;对于除第一个点外的其它邻点,如果该点不可用,则使用它的前一个像素进行赋值,直到遍历完毕

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;//用于指向所感兴趣的的重建YUV位置
Int i, j;

// DC值
Int iDCValue = 1 << (bitDepth - 1);

// 相邻点均不可用,则参考样点均被赋值为DC值
if (iNumIntraNeighbor == 0)
{
// Fill border with DC value
for (i=0; i<uiWidth; i++)
{
piAdiTemp[i] = iDCValue;
}
for (i=1; i<uiHeight; i++)
{
piAdiTemp[i*uiWidth] = iDCValue;
}
}
// 相邻点均可用,那么参考样点均被赋值为重建YUV图像中相同位置的样点值
else if (iNumIntraNeighbor == iTotalUnits)
{
// 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)// 默认值为false
{
piRoiTemp --; // move to the second left column 移到左边的第二列
}

for (i=0; i<uiCuHeight; i++)
{
piAdiTemp[(1+i)*uiWidth] = piRoiTemp[0];//每个点赋值为参考样本对应位置的YUV样点值
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;// 重新指向重建YUV上方
for (i=0; i<uiCuWidth; i++)
{
piAdiTemp[1+i] = piRoiTemp[i];
}

// 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;
Pel piAdiLine[5 * MAX_CU_SIZE];
Pel *piAdiLineTemp;
Bool *pbNeighborFlags;
Int iNext, iCurr;
Pel piRef = 0;

// Initialize
// 现将所有点的值置为DC值
for (i=0; i<iTotalSamples; i++)
{
piAdiLine[i] = iDCValue;
}

// Fill top-left sample
piRoiTemp = piRoiOrigin - iPicStride - 1;
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++)
{
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)
{
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;
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];
}
}
}