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

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


预处理

    注意:如果没有特别说明,操作的对象都是亮度块(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];
		}
	}
}



  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值