HEVC学习(三) —— 帧内预测系列之一

来源:互联网 发布:开淘宝店知识 编辑:程序博客网 时间:2024/05/24 22:42

由于研究的需要,现将一晨不变大神的关于HEVC帧内预测的相关博客进行转载,方便自己查阅。一直都看一晨不变大神的帖子,受益匪浅。原著博客地址:http://blog.csdn.net/HEVC_CJL/article/list/1


今天开始进入实质性内容的讨论,主要是从代码实现的角度比较深入地研究帧内预测算法。由于帧内预测涉及到的函数的数量相对于编解码器复杂部分来说少,但事实上大大小小也牵涉到了十几二十个函数(没具体统计过,只是大概估算了下),想要一下子讨论完比较困难,所以打算在接下来的若干篇文章里逐步地尽可能详尽地分析每一个较为重要的函数。今天所要讨论的是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样点值对其进行赋值;对于除第一个点外的其它邻点,如果该点不可用,则使用它的前一个样点值进行赋值(前一个步骤保证了前一个样点值一定是存在的),直到遍历完毕。

在了解了算法的大致流程后,我们就可以看看代码是怎么具体实现的了。我主要把我对代码的注释连同代码一起贴出来,以供大家参考,有些地方理解上肯定还是有所欠缺的,望大家不吝赐教。

  1. 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 )  
  2. {  
  3.   Pel* piRoiTemp; //!< piRoiOrgin指向重建Yuv图像对应于当前PU所在位置的首地址,piRoiTemp用于指向所感兴趣的重建Yuv的位置,piAdiTemp  
  4.   Int  i, j;  
  5.   Int  iDCValue = ( 1<<( g_uiBitDepth + g_uiBitIncrement - 1) );  
  6.   
  7.   if (iNumIntraNeighbor == 0) // all samples are not available  
  8.   {  
  9.     // Fill border with DC value  
  10.     for (i=0; i<uiWidth; i++)  //!< AboveLeft + Above + AboveRight  
  11.     {  
  12.       piAdiTemp[i] = iDCValue;  
  13.     }  
  14.     for (i=1; i<uiHeight; i++)  //!< Left + BelowLeft  
  15.     {  
  16.       piAdiTemp[i*uiWidth] = iDCValue;  
  17.     }  
  18.   }  
  19.   else if (iNumIntraNeighbor == iTotalUnits) // all samples are available  
  20.   {  
  21.     // Fill top-left border with rec. samples  
  22.     piRoiTemp = piRoiOrigin - iPicStride - 1;  //!< 左上  
  23.     piAdiTemp[0] = piRoiTemp[0];  
  24.   
  25.     // Fill left border with rec. samples  
  26.     piRoiTemp = piRoiOrigin - 1;  //!< 左  
  27.   
  28.     if (bLMmode) //!< bLMmode 默认值为false  
  29.     {  
  30.       piRoiTemp --; // move to the second left column  
  31.     }  
  32.   
  33.     for (i=0; i<uiCuHeight; i++)  
  34.     {  
  35.       piAdiTemp[(1+i)*uiWidth] = piRoiTemp[0]; //!< 每个参考样点赋值为对应位置重建Yuv样点值  
  36.       piRoiTemp += iPicStride; //!< 指向重建Yuv下一行  
  37.     }  
  38.   
  39.     // Fill below left border with rec. samples  
  40.     for (i=0; i<uiCuHeight; i++)  //!< 左下  
  41.     {  
  42.       piAdiTemp[(1+uiCuHeight+i)*uiWidth] = piRoiTemp[0]; //!< 每个参考样点赋值为对应位置重建Yuv样点值  
  43.       piRoiTemp += iPicStride;  
  44.     }  
  45.   
  46.     // Fill top border with rec. samples  
  47.     piRoiTemp = piRoiOrigin - iPicStride; //!< 重新指向重建Yuv的上方  
  48.     for (i=0; i<uiCuWidth; i++)  
  49.     {  
  50.       piAdiTemp[1+i] = piRoiTemp[i]; //!< 每个参考样点赋值为对应位置重建Yuv样点值  
  51.     }  
  52.       
  53.     // Fill top right border with rec. samples  
  54.     piRoiTemp = piRoiOrigin - iPicStride + uiCuWidth; //!< 指向右上  
  55.     for (i=0; i<uiCuWidth; i++)  
  56.     {  
  57.       piAdiTemp[1+uiCuWidth+i] = piRoiTemp[i]; //!< 每个参考样点赋值为对应位置重建Yuv样点值  
  58.     }  
  59.   }  
  60.   else // reference samples are partially available  
  61.   {  
  62.     Int  iNumUnits2 = iNumUnitsInCu<<1;  
  63.     Int  iTotalSamples = iTotalUnits*iUnitSize;  //!< neighboring samples的总数,iTotalUnits以4x4块为单位,iUnitSize为块的大小  
  64.     Pel  piAdiLine[5 * MAX_CU_SIZE];  
  65.     Pel  *piAdiLineTemp; //!<临时存储用于填充neighboring samples的样点值  
  66.     Bool *pbNeighborFlags;  
  67.     Int  iNext, iCurr;  
  68.     Pel  piRef = 0; //!< 存储临时样点值  
  69.   
  70.     // Initialize  
  71.     for (i=0; i<iTotalSamples; i++)  //!< 先将所有样点值赋值为DC值  
  72.     {  
  73.       piAdiLine[i] = iDCValue;  
  74.     }  
  75.       
  76.     // Fill top-left sample  
  77.     piRoiTemp = piRoiOrigin - iPicStride - 1;  //!< 指向重建Yuv左上角  
  78.     piAdiLineTemp = piAdiLine + (iNumUnits2*iUnitSize); //!< piAdiLine的扫描顺序为左下到左上,再从左到右上  
  79.     pbNeighborFlags = bNeighborFlags + iNumUnits2; //!< 标记neighbor可用性的数组同样移动至左上角  
  80.     if (*pbNeighborFlags) //!< 如果左上角可用,则左上角4个像素点均赋值为重建Yuv左上角的样点值  
  81.     {  
  82.       piAdiLineTemp[0] = piRoiTemp[0];  
  83.       for (i=1; i<iUnitSize; i++)  
  84.       {  
  85.         piAdiLineTemp[i] = piAdiLineTemp[0];  
  86.       }  
  87.     }  
  88.   
  89.     // Fill left & below-left samples  
  90.     piRoiTemp += iPicStride; //!< piRoiTemp指向重建Yuv的左边界  
  91.     if (bLMmode)  
  92.     {  
  93.       piRoiTemp --; // move the second left column  
  94.     }  
  95.     piAdiLineTemp--;  //!< 移动指针置左边界  
  96.     pbNeighborFlags--; //!< 移动指针置左边界  
  97.     for (j=0; j<iNumUnits2; j++) //!< 从左往左下扫描  
  98.     {  
  99.       if (*pbNeighborFlags)  //!< 如果可用  
  100.       {  
  101.         for (i=0; i<iUnitSize; i++) //!< 每个4x4块里的4个样点分别被赋值为对应位置的重建Yuv的样点值  
  102.         {  
  103.           piAdiLineTemp[-i] = piRoiTemp[i*iPicStride];  
  104.         }  
  105.       }  
  106.       piRoiTemp += iUnitSize*iPicStride; //!< 指针挪到下一个行(以4x4块为单位,即实际上下移了4行)  
  107.       piAdiLineTemp -= iUnitSize; //!< 指针下移  
  108.       pbNeighborFlags--; //!< 指针下移  
  109.     }  
  110.   
  111.     // Fill above & above-right samples  
  112.     piRoiTemp = piRoiOrigin - iPicStride; //!< piRoiTemp 指向重建Yuv的上边界  
  113.     piAdiLineTemp = piAdiLine + ((iNumUnits2+1)*iUnitSize); //!< 指向上边界  
  114.     pbNeighborFlags = bNeighborFlags + iNumUnits2 + 1; //!< 指向上边界  
  115.     for (j=0; j<iNumUnits2; j++) //!< 从左扫描至右上  
  116.     {  
  117.       if (*pbNeighborFlags)  
  118.       {  
  119.         for (i=0; i<iUnitSize; i++)  
  120.         {  
  121.           piAdiLineTemp[i] = piRoiTemp[i]; //!< 每个4x4块里的4个样点分别被赋值为对应位置的重建Yuv的样点值  
  122.         }  
  123.       }  
  124.       piRoiTemp += iUnitSize; //!< 指针右移  
  125.       piAdiLineTemp += iUnitSize; //!< 指针右移  
  126.       pbNeighborFlags++; //!< 指针右移  
  127.     }  
  128.   
  129.     // Pad reference samples when necessary  
  130.     iCurr = 0;  
  131.     iNext = 1;  
  132.     piAdiLineTemp = piAdiLine; //!< 指向左下角(纵坐标最大的那个位置,即扫描起点)  
  133.     while (iCurr < iTotalUnits) //!< 遍历所有neighboring samples  
  134.     {  
  135.       if (!bNeighborFlags[iCurr]) //!< 该点不可用  
  136.       {  
  137.         if(iCurr == 0) //!< 第一个点就不可用  
  138.         {  
  139.           while (iNext < iTotalUnits && !bNeighborFlags[iNext]) //!< 找到第1个可用点  
  140.           {  
  141.             iNext++;  
  142.           }  
  143.           piRef = piAdiLine[iNext*iUnitSize]; //!< 保存该可用点的样点值  
  144.           // Pad unavailable samples with new value  
  145.           while (iCurr < iNext) //!< 使用保存下来的第一个可用点的样点值赋值给在其之前被标记为不可用的点  
  146.           {  
  147.             for (i=0; i<iUnitSize; i++)  
  148.             {  
  149.               piAdiLineTemp[i] = piRef;  
  150.             }  
  151.             piAdiLineTemp += iUnitSize;  
  152.             iCurr++;  
  153.           }  
  154.         }  
  155.         else //!< 当前点不可用且其不是第一个点,则使用该点的前一个可用点的样点值进行赋值  
  156.         {  
  157.           piRef = piAdiLine[iCurr*iUnitSize-1];  
  158.           for (i=0; i<iUnitSize; i++)  
  159.           {  
  160.             piAdiLineTemp[i] = piRef;  
  161.           }  
  162.           piAdiLineTemp += iUnitSize;  
  163.           iCurr++;  
  164.         }  
  165.       }  
  166.       else  //!< 当前点可用,继续检查下一点  
  167.       {  
  168.         piAdiLineTemp += iUnitSize;  
  169.         iCurr++;  
  170.       }  
  171.     }  
  172.   
  173.     // Copy processed samples  
  174.     piAdiLineTemp = piAdiLine + uiHeight + iUnitSize - 2;  //!< piAdiLineTemp = (piAdiLine + 128) + 3,跳过之前对左上角扩充的3个像素点  
  175.     for (i=0; i<uiWidth; i++)  //!< 将最终结果拷贝到左上、上、右上边界  
  176.     {  
  177.       piAdiTemp[i] = piAdiLineTemp[i];  
  178.     }  
  179.     piAdiLineTemp = piAdiLine + uiHeight - 1; //!< uiHeight = uiCUHeight2 + 1  
  180.     for (i=1; i<uiHeight; i++)  //!< 将最终结果拷贝到左和左下边界  
  181.     {  
  182.       piAdiTemp[i*uiWidth] = piAdiLineTemp[-i]; //!< piAdiLineTemp下标为-i是因为赋值方向与实际存储方向是相反的  
  183.     }  
  184.   }  
  185. 关于一个PU的相邻点,以及它的相邻点的可用性如何判断的问题,是一个细节问题,并不会影响我们对这个函数实现功能的理解,但是,为了更好地理解这一个过程,这些坐标为什么这么算还是一个值得讨论的问题,限于篇幅,本文不作讨论。我会在接下来的文章中陆续对留下来的问题作更为详尽的讨论。 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 mac电脑打不开机怎么办 steam改密码上限了怎么办 qq加密了登不了怎么办 别人登我的淘宝怎么办 植物2被禁止登录怎么办 淘宝网东西未收到怎么办 淘宝网卖家不许退货退款怎么办 身份证以前开过淘宝店怎么办 支付宝登录名尚未激活怎么办 淘宝退货卖家不收货退款买家怎么办 淘宝账号刷得太多违规怎么办 闲鱼交易关闭了怎么办 淘宝店开了没做怎么办 微店店铺严重违规怎么办 淘宝违规扣2分怎么办 淘宝被扣6分怎么办 淘宝被扣2分怎么办 淘宝被海关扣了怎么办 淘宝被扣36分后怎么办 淘宝售假查封店铺资金怎么办 淘宝店扣48分怎么办 淘宝a内被扣48分怎么办 饿了么店铺满减怎么办 淘宝店扣a48分怎么办 淘宝短信营销无法获取人群怎么办 淘宝货发了退款怎么办 极速退款后卖家不确认收货怎么办 把货退了卖家不退款怎么办? 退款了又收到货怎么办 退货忘了填单号怎么办 手机换号了淘宝怎么办 换了手机支付宝怎么办 手机丢了微信登不上去了怎么办 前面手机丢了微信登不上去怎么办 淘宝密码忘了怎么办呢 融e借逾期一天怎么办 拼多多处罚下架怎么办 永久无法解绑支付宝怎么办 淘宝下单购买人数太多怎么办 新浪微博被拉黑暂时无法评论怎么办 闲鱼交易成功后卖家反悔怎么办