原文出处:
http://blog.csdn.net/HEVC_CJL/article/details/8175721
http://blog.csdn.net/HEVC_CJL/article/details/8183144
今天所要讨论的是fillReferenceSamples这个函数,它主要功能是在真正进行帧内预测之前,使用重建后的Yuv图像对当前PU的相邻样点进行赋值,为接下来进行的角度预测提供参考样点值。
这个函数实际上实现的是官方当前标准(JCTVC-J1003)draft 8.4.4.2.2(Reference sample substitution process for intra sample prediction),具体内容我这里就不重复了,有兴趣的朋友可以自己下下来去看看,我先简单把该过程复述一遍:(1)如果所有相邻点均不可用,则参考样点值均被赋值为DC值;(2)如果所有相邻点均可用,则参考样点值都会被赋值为重建Yuv图像中与其位置相同的样点值;(3)如果不满足上述两个条件,则按照从左下往左上,从左上往右上的扫描顺序进行遍历,(如下图所示),如果第一个点不可用,则使用下一个可用点对应的重建Yuv样点值对其进行赋值;对于除第一个点外的其它邻点,如果该点不可用,则使用它的前一个样点值进行赋值(前一个步骤保证了前一个样点值一定是存在的),直到遍历完毕。
以下为HM 16.0的代码解释,不能全部看懂,按照自己理解对应原博客进行了解释。
Void fillReferenceSamples( const Int bitDepth, // 位深度
#if O0043_BEST_EFFORT_DECODING
const Int bitDepthDelta,
#endif
const Pel* piRoiOrigin, //见下面
Pel* piIntraTemp,//指向当前图像的参考样点
const Bool* bNeighborFlags,
const Int iNumIntraNeighbor,
const Int unitWidth,
const Int unitHeight,
const Int iAboveUnits,
const Int iLeftUnits,
const UInt uiWidth,
const UInt uiHeight,
const Int iPicStride )
{
const Pel* piRoiTemp;////!< piRoiOrgin指向重建Yuv图像对应于当前PU所在位置的首地址,piRoiTemp用于指向所感兴趣的重建Yuv的位置,是局部变量
Int i, j;
Int iDCValue = 1 << (bitDepth - 1);//左移云算符,左移n位相当于乘以2的n次方,参考书本
const Int iTotalUnits = iAboveUnits + iLeftUnits + 1; //+1 for top-left左上角 该变量代表总的参考单元
if (iNumIntraNeighbor == 0)// all samples are not available
{
// Fill border with DC value
for (i=0; i(iNext, iLeftUnits);//假设用,选出inext和LeftUnits中的较小值
//!< 使用保存下来的第一个可用点的样点值赋值给在其之前被标记为不可用的点
// fill left column
while (iCurrJnit < iNextOrTop)//填充不可用点的左边列
{
for (i=0; i= iLeftUnits) ? unitWidth : unitHeight;
const Pel refSample = *(piIntraLineCur-1);//!< 当前点不可用且其不是第一个点,则使用该点的前一个可用点的样点值进行赋值
for (i=0; i= iLeftUnits) ? unitWidth : unitHeight;
iCurrJnit++;
}
}
// Copy processed samples
piIntraLineTemp = piIntraLine + uiHeight + unitWidth - 2;
// top left, top and top right samples
for (i=0; i
为了更好地理解帧内预测中的各个函数的原理过程,有必要对CU、PU地址计算方法有着较好的了解,因此,本文将对这个问题先做个讨论。
对视频编解码有一定了解的人应该会知道,有一种扫描顺序叫光栅扫描,即从左往右,由上往下,先扫描完一行,再移至下一行起始位置继续扫描。H.264使用的主要就是光栅扫描顺序,(当然它还有其它扫描顺序,被包含在FMO即灵活宏块顺序技术里)。
HEVC里同样也有光栅扫描顺序,但是,由于它对CU采用的是递归划分的方式,如果仍是采用光栅扫描顺序,对CU的寻址会很不方便,因此,HEVC定义了Z扫描顺序,如下图所示:
右图可见,这种扫描顺序保证了对于不同分割都能按照相同的遍历顺序进行寻址,有利于程序中的递归实现。
具体到代码中,为了处理的方便,并没有使用上图这种定义方式,而是以4x4块为最小单位,对CU进行分割,同时,为了简化计算,在初始化时定义了几个地址映射的数组,g_auiRasterToZscan, g_auiZscanToRaster, g_auiRasterToX, g_auiRasterToY。
第一个是从光栅扫描顺序转换为Z扫描顺序,第二个是从Z扫描顺序转换为光栅扫描顺序,第三、第四个则是得到某一个块相对于所在PU左上角的横纵坐标,且以像素为单位。
下面几张表是我在最大CU为64x64的前提下打印出来的:
上面这些值的含义,这里就不多解释了,相信大家自己画个CU,以4x4块对其进行分割,再对照上面几张表琢磨琢磨就清楚了。
值得一提的是,可能会有人有这样的疑问:图像的分辨率不同,它怎么能够保证这张表就能用呢?需要指出的是,Z扫描是针对一个CU来说的,它是用于递归扫描CU的分割。定位一幅图像中的一个CU(或其分割)大致是这么个过程,首先,由于CU的尺寸的最大值是已知的,会根据这个定位到该CU左上角相对于图像左上角的位置,即得到它的坐标,接着,才是对当前块进行Z扫描,单位是4x4块,换句话说,Z扫描地址是对一个CU有效的,不能直接使用这个地址来确定它在图像中的位置,这个地方是需要大家注意的。