VVC帧内学习1(附代码)

学习帧内算是比较简单的了,所以更要打好基础

versatile video coding,我一直手误打成VCC,这主要源于我一开始学习的时候的臭毛病,没懂,但是一直挂在嘴上,久而久之就一直说成VCC了,大家不要学我;

帧内预测是消除空间性的,编码又是对像素而言的,那么显然我们就要用各种方法去预测,去降低码率,有0~67种方法索引,0和1分别为planar和DC模式,2~67则为各种角度下的角度模式;


帧内预测角度有65种(非常多)(还有两个不是角度)

VTM新增了很多帧内技术,具体有跨分量线性预测(Corss-Component Linear Mode, CCLM)、位置自适应帧内联合预测(Position dependent intra prediciton Combination, PDPC)、帧内多行预测(Multiple reference line intra prediction, MRL)、帧内再划分预测(Intra sub-Partition prediction, ISP)等。
 

ISP:Intra Sub-Partitions,也就是帧内再划分,VTM新增的工具,它将亮度帧内预测块按照一个维度划分为2个或4个子块,其中所有子块共享同一种帧内预测模式。

ISP是否被允许或者如何划分参考下表

宽或高大于最大变换块尺寸的块也不进行子块划分

VVC帧内学习1(附代码)_第1张图片


1.帧内预测角度的更新

之前的帧内预测包括方向角度预测和平滑预测(DC和Planar)两类,VVC为提高预测精度,将HEVC帧内的33种角度预测模式扩展到65种,如图所示,在原来的每两个预测方向间再建立一个预测方向;

对于水平类模式(19-33),需要将左侧参考像素投影到上侧参考像素的左侧,以扩展上侧参考像素;

对于垂直类模式(34-49),需要将上侧参考像素投影到左侧参考像素的上方,以扩展左侧参考像素。

垂直模式(模式50)

水平模式(模式18)

左下角对角线模式和与其相邻的8个模式(模式2~10)

右上角对角线模式和与其相邻的8个模式(模式58~66)。

宽角度模式:

相比于只需要对方块进行帧内预测处理的HEVC,VVC需要处理长宽不等的矩形块。由于角度模式时只能使用左侧和上方的长为2h和2w的参考像素,长宽不等时帧内预测受到限制。针对矩形块帧内预测的特点,VVC中引入了如上图虚线指示的宽角度模式。

宽角度模式在原来的两个45°角上进行了延申,使得能够适应w和h不相等的状况;

VVC帧内学习1(附代码)_第2张图片

对于宽大于高且比值大于一定程度的模式,需要将模式2附近(大于模式2)的一些类水平角度模式替换成为模式66附近(大于模式66)的一些类垂直角度模式(是因为此时对于模式2附近(大于模式2)存在块部分点无法索引到参考像素的情况,而模式66附近(大于模式66)仍有部分能够索引到参考像素)。

如下图所示,宽高比过大时mode2已经索引不到参考像素,而mode76这样的大于mode66的模式变得可以使用。

VVC帧内学习1(附代码)_第3张图片

 同理,对于高大于宽且比值大于一定程度的模式,需要将模式66附近(小于模式66)的一些类垂直角度模式替换成为模式2附近(小于模式2)的一些类水平角度模式(是因为此时对于模式66附近(小于模式66)存在部分点无法索引到参考像素的情况,而模式2附近(小于模式2)仍有部分能够索引到参考像素)。

代码用modeShift作为一个设定,用于调节触发宽角度模式的条件。当宽大于高时,宽高比越大modeShift越大,就会有更多的接近2的类垂直模式号进入宽角度模式转换模式号。当宽小于高时,宽高比越小modeShift越大,就会有更多接近66的类水平模式号进入宽角度模式转换模式号。


 附上学习的代码:

// Function for initialization of intra prediction parameters  用于初始化内部预测参数的函数
void IntraPrediction::initPredIntraParams(const PredictionUnit & pu, const CompArea area, const SPS& sps)  //传pu,area,sps信息
{
  const ComponentID compId = area.compID;
  const ChannelType chType = toChannelType(compId);
  // 是否使用ISP(多划分预测)
  const bool useISP = NOT_INTRA_SUBPARTITIONS != pu.cu->ispMode && isLuma(chType);
  // 取size  
  const Size  cuSize    = Size(pu.cu->blocks[compId].width, pu.cu->blocks[compId].height);
  const Size  puSize    = Size(area.width, area.height);
  const Size &blockSize = useISP ? cuSize : puSize;
  const int   dirMode   = PU::getFinalIntraMode(pu, chType);  // 获取最后的帧内模式
  const int   predMode  = getModifiedWideAngle(blockSize.width, blockSize.height, dirMode); //将预测模式转为宽角度模式,wide—angle
  // ver-vertical 垂直  标号大于34视作垂直类模式
  m_ipaParam.isModeVer         = predMode >= DIA_IDX;
  //  参数,参考行索引
  m_ipaParam.multiRefIndex     = isLuma(chType) ? pu.multiRefIdx : 0;
  //  参考像素滤波初始化为否
  m_ipaParam.refFilterFlag     = false;
  // 插值初始化为否
  m_ipaParam.interpolationFlag = false;
  // 是否提供PDPC技术(若PU边长均≥4且使用0号参考行则提供PDPC)
  m_ipaParam.applyPDPC =
    (puSize.width >= MIN_TB_SIZEY && puSize.height >= MIN_TB_SIZEY) && m_ipaParam.multiRefIndex == 0;
  //记录角度模式关于垂直或水平的偏移量
  //若predMode>=34,则intraPredAngLeMode=predMode-VER_IDX(50)  VER_IDX = 50; HOR_IDX = 18
  //若predMode<34,则intraPredAngLeMode=predMode-HOR_IDX(18)
  //宽角度模式是-14~80,故该值范围为-32~30
  // 是垂直类模式吗?  是:不是
  const int intraPredAngleMode = (m_ipaParam.isModeVer) ? predMode - VER_IDX : -(predMode - HOR_IDX);

  int absAng = 0;
  //预测模式是角度模式(即模式号大于DC)且模式号小于LUMA模式总数67,planar:0 ,DC:1
  if (dirMode > DC_IDX && dirMode < NUM_LUMA_MODE) // intraPredAngle for directional modes 用于定向模式的帧内角度预测
  { //偏离水平或垂直不同程度的不同模式对应的模式偏移值offset,应该是跟50,18相对应
    static const int angTable[32]    = { 0,    1,    2,    3,    4,    6,     8,   10,   12,   14,   16,   18,   20,   23,   26,   29,   32,   35,   39,  45,  51,  57,  64,  73,  86, 102, 128, 171, 256, 341, 512, 1024 };
    // (512 * 32) / offset,跟上面的表格对应,为什么这么算还不清楚
    static const int invAngTable[32] = {
      0,   16384, 8192, 5461, 4096, 2731, 2048, 1638, 1365, 1170, 1024, 910, 819, 712, 630, 565,
      512, 468,   420,  364,  321,  287,  256,  224,  191,  161,  128,  96,  64,  48,  32,  16
    };   // (512 * 32) / Angle
   //模式偏离水平或垂直的值(取绝对值),值范围为0~32
    const int absAngMode = abs(intraPredAngleMode);
    //模式19~49为-1,其他为1,也就是给垂直类和水平类一个符号
    const int signAng    = intraPredAngleMode < 0 ? -1 : 1;
    //查找对应偏移值offset
    absAng               = angTable[absAngMode];
    //查找表中角度偏移值的倒数*512*32,后续便于除法运算
    m_ipaParam.absInvAngle    = invAngTable[absAngMode];
    // 帧内角度预测,两数相乘,此处就是给值附上“水平”或“垂直”
    m_ipaParam.intraPredAngle = signAng * absAng;
    //——————————————————————————————————————————————————
int IntraPrediction::getModifiedWideAngle( int width, int height, int predMode )
{
  //The function returns a 'modified' wide angle index, given that it is not necessary
  //in this software implementation to reserve the values 0 and 1 for Planar and DC to generate the prediction signal.
  //It should only be used to obtain the intraPredAngle parameter.
  //To simply obtain the wide angle index, the function PU::getWideAngle should be used instead.
  // 如果没有必要,该函数返回一个“修改过的”广角索引
  // 在本软件实现中,为Planar和DC保留0和1的值来产生预测信号。 它应该只用于获取intrapredangle参数。
  // 为了简单地获得广角索引,应该使用函数PU::getWideAngle来代替。
  // 66 是右上角45°位置的角度索引  2是左下角45°的角度索引
  if ( predMode > DC_IDX && predMode <= VDIA_IDX )  // 判断条件,是否为角度模式?意思是0和1滚
  {
    int modeShift[] = { 0, 6, 10, 12, 14, 15 };  //这个表的建立应该是做这个的人测出来的,知道这么个表就ok;
    // 计算意义上的宽高比
    int deltaSize = abs(floorLog2(width) - floorLog2(height));
    // 若宽大于高,且模式小于与宽高比对应的指定值,可以理解,如果比这个值还要大,那可能就不需要宽
    if (width > height && predMode < 2 + modeShift[deltaSize])  //若宽大于高 且小于这个计算的值
    { //改变模式标号为原标号+65,对应图就可以理解
      predMode += (VDIA_IDX - 1);   
    }
    // 若高大于宽,且模式大于与宽高比对应的指定值
    else if (height > width && predMode > VDIA_IDX - modeShift[deltaSize])
    {
      //若高大于宽,且模式大于与宽高比对应的指定值
      predMode -= (VDIA_IDX - 1);
    }
  }
  return predMode;
}

你可能感兴趣的:(H266视频编码,学习,视频编解码,1024程序员节)