x264源码分析之P帧的16x16宏块的帧间分析

2021/6/12 22:51:05

本文主要是介绍x264源码分析之P帧的16x16宏块的帧间分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

H264以算法复杂度增加为代价在以下几个方向做了极大的改进:
1、可变大小的图像分块已经帧间、帧内编码
2、基于1/4(亮度)和1/8(色度)像素精度的运动估计
3、多预测参考帧的使用
4、4x4的整数变换
5、先进的熵编码技术
6、去方块滤波器
在以上技术的改进中,帧间编码采用了多模式的块匹配运动估计技术(BMME),其中包括16x16、16x8、8x16、8x8、8x4、4x8、4x4共7种宏块模式。全搜索(FS)算法是BMME最直接的实现方法。FS算法遍历了分块的每一种模式,运算律巨大。在对H264各编码模块计算量的统计结果中,多模式的BMME占据了整个编码的大约70%的计算量。在H264原算法中,没有对图像特性进行分析,每一幅图像都要进行 7种模式预测,而且每个模式下的每个子宏块也都要进行运动估计,最后利用率失真算法对所有可能模式进行比较,选出最佳预测模式。

typedef struct
{
    /* input */
    int      i_pixel;   /* PIXEL_WxH */
    int      lm;        /* lambda motion */

    uint8_t *p_fref;//参考帧
    int      i_fref;//参考帧i_stride
    uint8_t *p_img;//原始像素
    int      i_img;//原始像素i_stride

    int mvp[2];//运动矢量预测值

    int b_mvc;
    int mvc[2];

    /* output */
    int cost;           /* satd + lm * nbits */
    int mv[2];//搜索到的运动矢量mv
} x264_me_t;

typedef struct
{
    /* 16x16 */
    int i_ref;//list中参考图像的序号
    x264_me_t me16x16;//16x16的运动矢量相关参数

    /* 8x8 */
    int       i_cost8x8;//8x8时的运动矢量最优sad值
    x264_me_t me8x8[4];//4个8x8的运动矢量相关参数

    /* Sub 4x4 */
    int       i_cost4x4[4]; /* cost per 8x8 partition */
    x264_me_t me4x4[4][4];

    /* Sub 8x4 */
    int       i_cost8x4[4]; /* cost per 8x8 partition */
    x264_me_t me8x4[4][2];

    /* Sub 4x8 */
    int       i_cost4x8[4]; /* cost per 8x8 partition */
    x264_me_t me4x8[4][4];

    /* 16x8 */
    int       i_cost16x8;
    x264_me_t me16x8[2];

    /* 8x16 */
    int       i_cost8x16;
    x264_me_t me8x16[2];

} x264_mb_analysis_list_t;

typedef struct
{
    /* conduct the analysis using this lamda and QP */
    int i_lambda;//拉马拉的值,用于码率控制
    int i_qp;//量化参数qp值


    /* I: Intra part */
    /* Luma part 16x16 and 4x4 modes stats */
    int i_sad_i16x16;//16x16帧内预测的最优sad值
    int i_predict16x16;//帧内预测的预测模式

    int i_sad_i4x4;//4x4帧内预测的最优sad值
    int i_predict4x4[4][4];//16个4x4宏块的帧内预测模式

    /* Chroma part */
    int i_sad_i8x8;//色度的帧内预测sad值
    int i_predict8x8;//色度的帧内预测模式,和16x16一样,一共4种

    /* II: Inter part P/B frame */

    x264_mb_analysis_list_t l0;//list0的帧间预测
    x264_mb_analysis_list_t l1;//list1的帧间预测

    int i_cost16x16bi; /* used the same ref and mv as l0 and l1 (at least for now) */

} x264_mb_analysis_t;


static void x264_mb_analyse_inter_p16x16( x264_t *h, x264_mb_analysis_t *a )
{
    x264_me_t m;//运动矢量
    int i_ref;

    /* 16x16 Search on all ref frame */
    m.i_pixel = PIXEL_16x16;//16x16宏块
    m.lm      = a->i_lambda;//拉马拉的值
    m.p_img   = h->mb.pic.p_img[0];//原始像素
    m.i_img   = h->mb.pic.i_img[0];//原始像素i_stride
    m.i_fref  = h->mb.pic.i_fdec[0];//参考帧的i_stride
    m.b_mvc   = 0;
//    m.mvc[0]  = 0;
//    m.mvc[1]  = 0;

    /* ME for ref 0 */
    m.p_fref = h->mb.pic.p_fref[0][0][0];//第一个0:参考帧list0;第二个0:参考帧序号;第三个0:Y分量
    x264_mb_predict_mv_16x16( h, 0, 0, m.mvp );//先根据之前的预测得到运动矢量搜索的起点
    x264_me_search( h, &m );//运动矢量搜索得到mv

    a->l0.i_ref = 0;//参考帧索引
    a->l0.me16x16 = m;

    for( i_ref = 1; i_ref < h->i_ref0; i_ref++ )//搜索所有的参考帧,i_ref0为参考帧个数
    {
        /* search with ref */
        m.p_fref = h->mb.pic.p_fref[0][i_ref][0];//获取第i_ref索引的参考帧的像素指针
        x264_mb_predict_mv_16x16( h, 0, i_ref, m.mvp );//获取搜索的起始位置
        x264_me_search( h, &m );//搜索最佳mv

        /* add ref cost */
        m.cost += m.lm * bs_size_te( h->sh.i_num_ref_idx_l0_active - 1, i_ref );//i_num_ref_idx_l0_active为参考帧数量

        if( m.cost < a->l0.me16x16.cost )//比较当前参考帧索引的sad与前面计算得到的最优sad值
        {
            a->l0.i_ref = i_ref;//最优sad对应的参考索引
            a->l0.me16x16 = m;//保存运动矢量
        }
    }

    /* Set global ref, needed for all others modes */
    x264_macroblock_cache_ref( h, 0, 0, 4, 4, 0, a->l0.i_ref );
}

/*最优mv搜索,只在[-16, 16]正方形范围内进行搜索*/
void x264_me_search( x264_t *h, x264_me_t *m )
{
    const int i_pixel = m->i_pixel;
    int bcost;//sad值
    int bmx, bmy;//当前的搜索位置
    uint8_t *p_fref = m->p_fref;//参考像素指针


    /* init with mvp */
    bmx = x264_clip3( m->mvp[0]>>2, -16, 16 );//搜索范围在-16到16
    bmy = x264_clip3( m->mvp[1]>>2, -16, 16 );

    p_fref = &m->p_fref[bmy * m->i_fref + bmx];//获取参考帧(bmx, bmy)处的指针
    bcost = h->pixf.sad[i_pixel]( m->p_img, m->i_img, p_fref, m->i_fref );//首先计算初始(bmx, bmy)处时,16x16宏块的sad值


    /* try a candidate if provided */
    if( m->b_mvc )//16x16宏块时,b_mvc=0
    {
        const int mx = m->mvc[0] >> 2;
        const int my = m->mvc[1] >> 2;
        uint8_t *p_fref2 = &m->p_fref[my*m->i_fref+mx];
        int cost = h->pixf.sad[i_pixel]( m->p_img, m->i_img, p_fref2, m->i_fref ) +
                   m->lm * ( bs_size_se( m->mvc[0] - m->mvp[0] ) + bs_size_se( m->mvc[1] - m->mvp[1] ) );
        if( cost < bcost )
        {
            bmx = mx;
            bmy = my;
            bcost = cost;
            p_fref = p_fref2;
        }
    }

    /* diamond */
    for( ;; )//开始进行搜索,获取最佳匹配mv
    {
        int best = 0;
        int cost[4];//用于记录上下左右像素位置的sad值

        if( bmx < -16 ||bmx > 16 || bmy < -16 || bmy > 16)//搜索范围为[-16, 16]的正方形搜索范围,x264只搜索[-16, 16]正方形的范围
            break;

#define COST_MV( c, dx, dy ) \
        (c) = h->pixf.sad[i_pixel]( m->p_img, m->i_img,                    \
                               &p_fref[(dy)*m->i_fref+(dx)], m->i_fref ) + \
              m->lm * ( bs_size_se(((bmx+(dx))<<2) - m->mvp[0] ) +         \
                        bs_size_se(((bmy+(dy))<<2) - m->mvp[1] ) )//计算sad值,&p_fref[(dy)*m->i_fref+(dx)为最左上角像素

        COST_MV( cost[0],  0, -1 );//计算以上侧像素为起始点的sad值
        COST_MV( cost[1],  0,  1 );//计算以下侧像素为起始点的sad值
        COST_MV( cost[2], -1,  0 );//计算以左侧像素为起始点的sad值
        COST_MV( cost[3],  1,  0 );//计算以右侧像素为起始点的sad值
#undef COST_MV
		/*得到最优sad值*/
        if( cost[1] < cost[0] )    best = 1;
        if( cost[2] < cost[best] ) best = 2;
        if( cost[3] < cost[best] ) best = 3;

        if( bcost <= cost[best] )//如果当前的mv比上下左右的sat都要优,得到最优mv,跳出循环
            break;

        bcost = cost[best];

        if( best == 0 ) {//如果上侧像素为最优mv,那么继续向上搜索
            bmy--;
            p_fref -= m->i_fref;
        } else if( best == 1 ) {//如果下侧像素为最优mv,那么继续向下搜索
            bmy++;
            p_fref += m->i_fref;
        } else if( best == 2 ) {//如果左侧像素为最优mv,那么继续向左搜索
            bmx--;
            p_fref--;
        } else if( best == 3 ) {//如果右侧像素为最优mv,那么继续向右搜索
            bmx++;
            p_fref++;
        }
    }

    /* -> qpel mv */
    m->mv[0] = bmx << 2;//转换为1/4像素单位
    m->mv[1] = bmy << 2;

    /* compute the real cost */
    m->cost = h->pixf.satd[i_pixel]( m->p_img, m->i_img, p_fref, m->i_fref ) +
                m->lm * ( bs_size_se( m->mv[0] - m->mvp[0] ) +
                          bs_size_se( m->mv[1] - m->mvp[1] ) );//计算得到最优sad值
}


这篇关于x264源码分析之P帧的16x16宏块的帧间分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程