美文网首页
(十二)宏块损耗计算x264_slicetype_mb_cost

(十二)宏块损耗计算x264_slicetype_mb_cost

作者: 奔向火星005 | 来源:发表于2018-09-09 10:20 被阅读0次

我们知道,x264的主要压缩策略是,计算待编码宏块与各种模式下生成的参考宏块的差值,也就是代码里cost(损耗)的意思。然后比较各种模式下的损耗,选择损耗最小的一种预测模式。用一个不太恰当的简化的等式,如下:
帧内编码:cost = 待编码宏块 - 参考宏块
帧间编码:cost = 待编码宏块 - 参考宏块 + 运动向量

之所以说不太恰当,是因为cost实际上是待编码宏块与参考宏块之间的sad或satd值。目前x264默认是satd值,前面文章中有过描述。

x264_slicetype_mb_cost函数主要就是来完成上述的工作。当待编码帧是I帧时,只有帧内编码,当待编码帧是P帧或B帧时,还需要计算帧间编码的损耗及运动向量(mv)本身的损耗。本文主要介绍帧内编码损耗。源码如下:

static void x264_slicetype_mb_cost( x264_t *h, x264_mb_analysis_t *a,
                                    x264_frame_t **frames, int p0, int p1, int b,
                                    int dist_scale_factor, int do_search[2], const x264_weight_t *w,
                                    int *output_inter, int *output_intra )
{
//省略
/////////////////////////

    if( p0 == p1 ) // 前向参考帧=后向参考帧, 则进行I帧处理
        goto lowres_intra_mb;

//省略
/////////////////////////
lowres_intra_mb:
    if( !fenc->b_intra_calculated )
    {
        ALIGNED_ARRAY_16( pixel, edge,[36] );  //pixel edge[36]
        pixel *pix = &pix1[8+FDEC_STRIDE];   //pix1指向一个9*32的内存块
        pixel *src = &fenc->lowres[0][i_pel_offset]; //i_pel_offset = 8 * (i_mb_x + i_mb_y * i_stride);说明src指向待编码帧的一个宏块
        const int intra_penalty = 5 * a->i_lambda;
        int satds[3];
        int pixoff = 4 / sizeof(pixel);  //pixoff等于4;

        /* Avoid store forwarding stalls by writing larger chunks */
        memcpy( pix-FDEC_STRIDE, src-i_stride, 16 * sizeof(pixel) );  //1.拷贝宏块的正上方一行的像素到pix
        for( int i = -1; i < 8; i++ )  //2.拷贝宏块左边边缘的像素到pix
            M32( &pix[i*FDEC_STRIDE-pixoff] ) = M32( &src[i*i_stride-pixoff] );

        h->pixf.intra_mbcmp_x3_8x8c( h->mb.pic.p_fenc[0], pix, satds ); //3.生成DC,H,V三种模式的宏块,并计算它们与待编码宏块的satd,保存到satds数组中
        int i_icost = X264_MIN3( satds[0], satds[1], satds[2] );  //选出最小损耗赋给i_icost

        if( h->param.analyse.i_subpel_refine > 1 )
        {
            h->predict_8x8c[I_PRED_CHROMA_P]( pix );  //4.生成plane模式预测宏块
            int satd = h->pixf.mbcmp[PIXEL_8x8]( h->mb.pic.p_fenc[0], FENC_STRIDE, pix, FDEC_STRIDE ); //计算plane宏块与待编码宏块的satd
            i_icost = X264_MIN( i_icost, satd );
//5.待编码宏块的左方和上方的像素值进行滤波,然后拷贝到edge数组
            h->predict_8x8_filter( pix, edge, ALL_NEIGHBORS, ALL_NEIGHBORS ); 
            for( int i = 3; i < 9; i++ )  //6.生成模式3到模式8的预测宏块,并计算出最小损耗的模式
            {
                h->predict_8x8[i]( pix, edge );
                satd = h->pixf.mbcmp[PIXEL_8x8]( h->mb.pic.p_fenc[0], FENC_STRIDE, pix, FDEC_STRIDE );
                i_icost = X264_MIN( i_icost, satd );
            }
        }

        i_icost = ((i_icost + intra_penalty) >> (BIT_DEPTH - 8)) + lowres_penalty;
        fenc->i_intra_cost[i_mb_xy] = i_icost; //7.保存i_mb_xy坐标点对应的宏块的损耗
        int i_icost_aq = i_icost;
        if( h->param.rc.i_aq_mode )
            i_icost_aq = (i_icost_aq * fenc->i_inv_qscale_factor[i_mb_xy] + 128) >> 8;
        output_intra[ROW_SATD] += i_icost_aq;
        if( b_frame_score_mb )
        {
            output_intra[COST_EST] += i_icost;
            output_intra[COST_EST_AQ] += i_icost_aq;
        }
    }
    i_bcost = (i_bcost >> (BIT_DEPTH - 8)) + lowres_penalty;

//省略
/////////////////////////
}

如注释中所示,我把大致过程分成了7步,再列一下:
1和2:拷贝宏块的正上方一行的像素和左边边缘像素到pix,这是为了预测宏块生成而准备的;
3.生成DC,H,V三种模式的宏块,并计算它们与待编码宏块的satd,选出最小的cost值赋给i_icost。
4.生成plane模式预测宏块,计算plane宏块与待编码宏块的satd,选出最小的cost值赋给i_icost。
5.待编码宏块的左方和上方的像素值进行滤波,然后拷贝到edge数组
6.生成模式3到模式8的预测宏块,并计算出最小损耗的模式,赋给i_icost。
7.将i_icost保存到fenc->i_intra_cost[i_mb_xy]。

其中1到4步,主要是对dc,h,v,plane四种预测模式进行计算,在前面的(四)(五)(六)(七)这几篇文章中已有描述。第5和6,主要是对剩下的3到8预测模式进行计算,下面再列一下帧内预测模式:


帧内预测模式00.png

具体原理可参看h264白皮书,这里不再赘述了。

相关文章

网友评论

      本文标题:(十二)宏块损耗计算x264_slicetype_mb_cost

      本文链接:https://www.haomeiwen.com/subject/uvprgftx.html