HEVC学习-帧内预测-initAdiPattern函数

本文章主要参考
https://blog.csdn.net/HEVC_CJL/article/details/8184276
https://blog.csdn.net/sujunzy666/article/details/16928337
https://blog.csdn.net/g0ose/article/details/52116453
http://www.cnblogs.com/gamedes/p/4541765.html
大佬们的代码进行学习,作为学习记录
一、首先是涉及到的全局变量

#define     MAX_CU_DEPTH             6                          // log2(LCUSize) 最大CU深度为6
#define     MAX_CU_SIZE             (1<<(MAX_CU_DEPTH))         // maximum allowable size of CU, surely 64? (not 1<<7 = 128)最大CU_size
#define     MIN_PU_SIZE              4
#define     MIN_TU_SIZE              4
#define     MAX_TU_SIZE             32
#define     MAX_NUM_SPU_W           (MAX_CU_SIZE/MIN_PU_SIZE)   // maximum number of SPU in horizontal line水平方向上SPU的最大数目
#define     SCALING_LIST_REM_NUM     6

注:(ps:1<<7表示二进制的1向左左移7位即10000000=128)(原谅我是个小白,大佬尽情鄙视)
二、对于图像步长值(stride value)的解释
先贴一段HM代码中的源代码

  // Access starting position of YUV transform unit buffer by pix offset for square & non-square blocks;对于正方形和非正方形块,通过pix偏移访问YUV转换单元缓冲区的起始位置
  Pel*         getAddrPix                 (const ComponentID id, const UInt iPixX, const UInt iPixY )       { return m_apiBuf[id] + iPixY * getStride(id) + iPixX; }
  const Pel*   getAddrPix                 (const ComponentID id, const UInt iPixX, const UInt iPixY ) const { return m_apiBuf[id] + iPixY * getStride(id) + iPixX; }

  //  Get stride value of YUV buffer;得到YUV缓冲区域的步长值
  UInt         getStride                  (const ComponentID id) const { return m_iWidth >> getComponentScaleX(id);   }
  UInt         getHeight                  (const ComponentID id) const { return m_iHeight >> getComponentScaleY(id);  }
  UInt         getWidth                   (const ComponentID id) const { return m_iWidth >> getComponentScaleX(id);   }
  ChromaFormat getChromaFormat            ()                     const { return m_chromaFormatIDC; }
  UInt         getNumberValidComponents   ()                     const { return ::getNumberValidComponents(m_chromaFormatIDC); }
  UInt         getComponentScaleX         (const ComponentID id) const { return ::getComponentScaleX(id, m_chromaFormatIDC); }
  UInt         getComponentScaleY         (const ComponentID id) const { return ::getComponentScaleY(id, m_chromaFormatIDC); }

};

下面对图像步长值(stride value)进行解释
当视频图像存储在内存时,图像的每一行末尾也许包含一些扩展的内容,这些扩展的内容只影响图像如何存储在内存中,但是不影响图像如何显示出来。Stride 就是这些扩展内容的名称,Stride 也被称作 Pitch,如果图像的每一行像素末尾拥有扩展内容,Stride 的值一定大于图像的宽度值,就像下图所示:

在这里插入图片描述
两个缓冲区包含同样大小(宽度和高度)的视频帧,却不一定拥有同样的 Stride 值,如果你处理一个视频帧,你必须在计算的时候把 Stride 考虑进去。
另外,一张图像在内存中有两种不同的存储序列(arranged),对于一个从上而下存储(Top-Down) 的图像,最顶行的像素保存在内存中最开头的部分,对于一张从下而上存储(Bottom-Up)的图像,最后一行的像素保存在内存中最开头的部分,下面图示展示了这两种情况:
在这里插入图片描述
一张从下而上的图像拥有一个负的 Stride 值,因为 Stride 被定义为[从一行像素移动到下一行像素时需要跨过多少个像素],仅相对于被显示出来的图像而言;而 YUV 图像永远都是从上而下表示的,以及任何包含在 Direct3D Surface 中的图像必须是从上而下,RGB 图像保存在系统内存时通常是从下而上。尤其是视频变换,特别需要处理不同 Stride 值的图像,因为输入缓冲也许与输出缓冲不匹配。
三、具体代码注释实现(如果有大佬能够指出错误或者解答疑惑,小弟万分感谢)

Void TComPattern::initAdiPattern(TComDataCU* pcCU,       //当前待处理的CU
	UInt uiZorderIdxInPart, //当前待处理的PU相对于pcCU的位置,以4*4块为单位
	UInt uiPartDepth, //当前PU的深度(相对于当前的CU),非0即1
	Int* piAdiBuf,    //其指向的空间是为了存储预测数据,这里只是使用上一行,左一列来存储参考点的数据
	Int iOrgBufStride, //m_iYuvExtStride = ((MAX_CU_SIZE  + 8) << 4);??
	Int iOrgBufHeight, //m_iYuvExtHeight  = ((MAX_CU_SIZE + 2) << 4);??
	Bool& bAbove,      //指示上面块可否使用,根据代码调试的过程看,这个是为了计算均值时使用的一个标记
	Bool& bLeft,       //指示左面块可否使用,同上
	Bool bLMmode)     //
{
	Pel*  piRoiOrigin;//指向原始数据
	Int*  piAdiTemp;
	//当前PU的尺寸:先计算当前CU的尺寸,在根据uiPartDepth计算
	UInt  uiCuWidth = pcCU->getWidth(0) >> uiPartDepth;
	UInt  uiCuHeight = pcCU->getHeight(0) >> uiPartDepth;

	//参考点水平和竖直方向上的个数(比当前PU的尺寸大一倍)
	UInt  uiCuWidth2 = uiCuWidth << 1;//uiCuWidth2、uiCuHeight2分别等于二倍的当前CU的宽度和高度
	UInt  uiCuHeight2 = uiCuHeight << 1;
	UInt  uiWidth;
	UInt  uiHeight;
	//当前图像的跨度(比图像的宽稍微大点)
	Int   iPicStride = pcCU->getPic()->getStride();//图像跨度(步长)值要大于图像宽度

	Int   iUnitSize = 0;
	Int   iNumUnitsInCu = 0;
	Int   iTotalUnits = 0;
	Bool  bNeighborFlags[4 * MAX_NUM_SPU_W + 1];//MAX_NUM_SPU_W是水平方向上的SPU的数量,MAX_NUM_SPU_W=(MAX_CU_SIZE/MIN_PU_SIZE);存储五个方向上的邻域标志位,即是否可用

	Int   iNumIntraNeighbor = 0;//用来统计可用邻域的数目

	UInt uiPartIdxLT, uiPartIdxRT, uiPartIdxLB;

	//获取当前PU左上角,右上角以及左下角的位置, 以4x4块为单位的ZScanOrder   
	pcCU->deriveLeftRightTopIdxAdi(uiPartIdxLT, uiPartIdxRT, uiZorderIdxInPart, uiPartDepth);//指针指向左上角和右上角的4x4ZScanOrder
	pcCU->deriveLeftBottomIdxAdi(uiPartIdxLB, uiZorderIdxInPart, uiPartDepth);//指针指向左下角的4x4ZScanOrder

	iUnitSize = g_uiMaxCUWidth >> g_uiMaxCUDepth;
	iNumUnitsInCu = uiCuWidth / iUnitSize;
	iTotalUnits = (iNumUnitsInCu << 2) + 1;
	//bNeighborFlags存放的是参考点是否可用,依次存放左下,左,左上,上,右上5个区域的参考点可用性,即扫描顺序为从左下到左上,再从左上到右上 
	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);//计算5个区域内的可用邻域的个数

	bAbove = true;
	bLeft = true;
	//参考点在水平和竖直方向上的个数,+1是因为要记录左上角的一个位置
	uiWidth = uiCuWidth2 + 1;
	uiHeight = uiCuHeight2 + 1;
	//这个if语句我也不知道干嘛的,可能是为了让大家看不懂而设置????
	if (((uiWidth << 2)>iOrgBufStride) || ((uiHeight << 2)>iOrgBufHeight))
	{
		return;
	}

	//获得当前PU对应的原始数据的地址
	piRoiOrigin = pcCU->getPic()->getPicYuvRec()->getLumaAddr(pcCU->getAddr(), pcCU->getZorderIdxInCU() + uiZorderIdxInPart);
	piAdiTemp = piAdiBuf;

	//获得当前PU的参考点(内部分为3种情况:1 所有块都可用2所有块都不可用3有部分块可用)
	fillReferenceSamples(g_bitDepthY, piRoiOrigin, piAdiTemp, bNeighborFlags, iNumIntraNeighbor, iUnitSize, iNumUnitsInCu, iTotalUnits, uiCuWidth, uiCuHeight, uiWidth, uiHeight, iPicStride, bLMmode);


	//! 下面所进行的工作主要是对参考样点进行3抽头的滤波。piAdiBuf指向滤波前的参考样点的首地址,在滤波前,先将所有参考样点
	//! 拷贝到piFilterBuf指向的区域,经滤波后的样点值保存在piFilterBufN指向的区域,最终将滤波后的样点值拷贝到piFilterBuf1
	//! 值得一提的是,最终的结果是,piAdiBuf指向的区域是未经滤波的样点值,而piFilterBuf1指向的区域是经过滤波的样点值,
	//! 两者的地址相差uiWH = uiWidth * uiHeight = (uiCuWidth2 + 1) * (uiCuHeight2 + 1),这就解释了在进行真正的帧内预测时,
	//! 在需要滤波时,指向piAdiBuf的指针需要加上uiWH的偏移量
	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) //!<存放的是参考样点经3抽头滤波后的值

												  // draft 8.4.4.2.3 Filtering process of neighbouring samples
	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];  //!<上边界,存储顺序为从左往右
	}

	// 1. filtering with [1 2 1]
	piFilterBufN[0] = piFilterBuf[0]; //!< 第1个点直接保存,不滤波
	piFilterBufN[iBufSize - 1] = piFilterBuf[iBufSize - 1]; //!< 最后一个点也直接保存,不滤波
	for (i = 1; i < iBufSize - 1; i++) //!< 对中间样点值进行3抽头[1 2 1] / 4 的平滑滤波
	{
		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++];  // left border from bottom to top //!< 左边界
	}
	piFilteredBuf1[0] = piFilterBufN[l++]; //!< 左上边界
	for (i = 0; i < uiCuWidth2; i++)
	{
		piFilteredBuf1[1 + i] = piFilterBufN[l++]; // above border from left to right //!< 上边界
	}
}

在这里插入图片描述
(注,上图中,uiWidth和uiHeight实际上对应的是代码中的uiCUWidth和uiCUHeight,因画图的时候发生了遗漏,特此说明)

本博文仅作小白学习记录使用,所有文章出处均在开头链接处可见,所有疑惑点均作了标注。欢迎各位大佬交流批评指正,侵删。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值