HEVC学习之CTU划分

一,CTU相关概念

H.265将图像划分为“树编码单元(coding tree units, CTU)”,而不是像H.264那样的16×16的宏块。根据不同的编码设置,树编码块的尺寸可以被设置为64×64或有限的32×32或16×16。
在这里插入图片描述

上图就是一个64×64树编码块的分区示例,每个树编码块可以被递归分割,利用四叉树结构,分割为32×32、16×16、8×8的子区域。

右侧四叉树中每个节点有4个子节点,依次对应CU块中左上、右上、左下、右下子块,若子块需要继续划分,则该四叉树该子节点下方还由4个子节点。此外还能看到随着二叉树深度加深,对应的CU尺寸也是逐渐减少。

如下图所示,一个CTU由1个亮度CTB和两个色度CTB,一个语法元素组成,其中B(Block)是真正存储数据的地方。若采用YUV420格式,其中的色度CTB的长宽都是亮度CTB的1/2
在这里插入图片描述
。编码树单元可向下分成编码单元(Coding Unit,CU)、预测单元(Prediction Unit,PU)及转换单元(Transform Unit,TU)。
在这里插入图片描述
PU:在转换和量化之前,首先是预测阶段(包括帧内预测和帧间预测)。一个编码单元CU可以使用以下八种预测模式中的一种进行预测。编码单元与预测单元的不同之处在于预测单元只能被切割一次。预测单元PU规定了编码单元的所有预测模式,一切与预测有关的信息都定义在预测单元部分。比如,帧内预测的方向、帧间预测的分割方式、运动矢量预测,以及帧间预测参考图像索引号都属于预测单元的范畴。

TU:转换单元是呈现残量(Residual)或是转换系数(Transform Coefficients)的区块,这个区块主要是做整数转换(Integer Transform)或是量化(Quantization)。变换单元是独立完成变换和量化的基本单元,其尺寸也是灵活变化的。根据预测残差的局部变化特性,TU可以自适应地选择最优的模式。大块的TU模式能够将能量更好地集中,小块的TU模式能够保存更多的图像细节。 这种灵活的分割结构,可以使变换后的残差能量得到充分压缩,以进一步提高编码增益。

参考:http://t.zoukankan.com/sddai-p-14365572.html

二、HM中CTU划分的代码

2.1HM代码层次:
在这里插入图片描述
01.encmain函数:中主要做了编码器对象的创建、分析配置文件,初始化配置参数,和编码器最重要的功能"encode"。

02.encode函数:主要实现了读取YUV文件的数据、初始化工具对象例如:GOPEncoder、SliceEncoder、CUEncder……。在此函数里,还包括一个encode函数,调用CompressGOP函数来具体执行编码任务。

03.CompressGOP函数:InitGOP将文件的码流分成若干GOP以便后续程序能够顺利执行,InitEncSlice创建编码的Slice。

03.preCompressSlice和CompressSlice两个函数:前者的作用是选择不同的lamuda进行编码(编码是调用了CompressCU函数,后续介绍),后者是在最好的lamuda下进行编码。

04.xCompressCU函数:CompressCU函数的主体也是调用xComprssCU函数),先进行帧间预测xCheckRDCostMerge2Nx2N,xCheckRDCostInter等。在做完帧间预测后进行阵内预测,这是调用的函数是xCheckRDCostIntra,在xCompressCU函数的后续部分,还递归调用自身以实现对每个CU的编码。变换编码在encodeCoeff中实现,量化在xCheckIntraPCM完成。

5.xCheckRDCostIntra函数,主要完成帧内预测的任务,对亮度的预测使用estIntraPredQT,对色度使用estIntraPredChromaQT。predIntraLumaAng函数实现了方向的预测;calcHAD函数计算了SATD;xModeBitsIntra函数计算编码的码率;xUpdateCandList更新了最好的RDCost所使用的模式。

参考:https://blog.51cto.com/u_15785281/5663436

2.2HM调用结构
在这里插入图片描述
图片来源:https://zhuanlan.zhihu.com/p/39438463
先读入yuv序列,压缩过程可以理解为是一种深度优先遍历,从上往下依次是压缩GOP,压缩slice,压缩CTU,压缩CU。当开始压缩一个CU时(即到达了叶子结点),然后将当前CTU中的CU都进行压缩,压缩完毕时开始回溯压缩下一个CTU,以此类推,直至将yuv压缩完毕。

于是我们查看compressGOP:
在这里插入图片描述
也是一个dfs,梳理下一次深度遍历的过程:读取yuv序列的GOP个数,将POC重排序,分配内存,初始化slice编码器,设置field,设置slice类型,设置NALU类型,设置参考帧列表,压缩slice,为码率控制计算lamda,环路滤波,初始化熵编码器(环路滤波中的SAO信息需要放到熵编码中,因此在环路滤波之后),将码流写进NALU。

为了获取到CU或者TU的划分信息,我们可以在完成环路滤波后进行划分信息的获取:
其由嵌套的两个for循环组成,外循坏用于遍历当前帧中的CTU数量,小循环用于遍历当前CTU中CU
在这里插入图片描述

如当前帧为416X240的,LCUSIZE=64,因此CTU数量为28(416/64=6.5,240/64=3.7,向上取整,47=28)
256=16
16,由于TU最小尺寸为4X4,因此按照这个尺寸去平分6464得到1616个最小尺寸

 ofstream myfile_TU;
    string TU_file_name = "TU_file_name"+to_string(frame);
    myfile_TU.open(TU_file_name);
    for (UInt ctuRsAddr = 0; ctuRsAddr < pcPic->getNumberOfCtusInFrame(); ctuRsAddr++) 
    {
    
    
        TComDataCU* pCtu = pcPic->getCtu(ctuRsAddr);//通过具体的CU地址获取到CU数据

        const TComSPS& sps = *(pCtu->getSlice()->getSPS());//通过该CU数据获取sps信息
        const UInt maxCuWidth = sps.getMaxCUWidth();//从sps中拿到CU的最大宽度

        int iCount = 0;
        int iWidthInPart = maxCuWidth >> 2;
      

        for (int i = 0; i < pCtu->getTotalNumPart(); i++) //从CU数据中获取总共划分的数量,并遍历这些划分的块
        {
    
    
            int TU_depth = pCtu->getTransformIdx(g_auiRasterToZscan[i]) + pCtu->getDepth(g_auiRasterToZscan[i]);
             if (int(pCtu->getTransformIdx(g_auiRasterToZscan[i])) > 1)
            {
    
    
                cout << "TU深度" << int(pCtu->getTransformIdx(g_auiRasterToZscan[i])) << endl;
                cout << "CU深度" << int(pCtu->getDepth(g_auiRasterToZscan[i])) << endl;
            }
            myfile_TU << to_string(TU_depth) + " "; //将TU深度写到myfile_TU中
            iCount++;
        }
    }
    frame++;
    myfile_TU.close();

ZScan扫描是Z字扫描,从左上角开始扫描完一块扫描另一块。HM代码中ZScan扫描顺序存储在g_auiRasterToZscan数组中,这里存储深度信息的顺序也使用Z字顺序
getTransformIdx(g_auiRasterToZscan[i]))与getDepth(g_auiRasterToZscan[i]))分别为得到TU深度和CU深度。注意getTransformIdx不是获得的TU是否划分的标志位,其虽然是在CU层面去遍历,但是是按照TU的最小尺度去遍历pCtu->getTransformIdx(i)的输出结果是0或者1:0表示当前TU块并不继续向下划分,1表示当前TU块继续向下划分,将其加上该位置的深度存到划分文件中
其中TU最大深度取决于设定的RQT最大深度,不过TU深度+CU深度最深也就是4在这里插入图片描述

  xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug) );

三,CTU划分直观展示

将CU划分方法文件通过matlab直观展示在压缩后的YUV序列中:
在这里插入图片描述
在这里插入图片描述
可以看到,figure1的块划分远比figure2的细致,再打开HEVC分析软件查看:
在这里插入图片描述
在这里插入图片描述
可以看到figure1位为I帧,采用的全是帧内编码,figure2为P帧,其中0.02%的帧内编码,13.99%的帧间编码,85.99%的skip模式(跳过型CU只能采用帧间预测模式,而且产生的运动向量和图像的残差信息不需要传送给解码器)。

再查看这两帧与压缩前帧之差:
figure1:
在这里插入图片描述
figure2:
在这里插入图片描述
可以看到figure2的残差中不会因为采取了skip策略残差就变少,因此只提取CU信息去辅助压缩视频多帧增强任务是不准确的

猜你喜欢

转载自blog.csdn.net/qq_42567607/article/details/128407060
今日推荐