x264帧内预测

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/NB_vol_1/article/details/78396361

x264帧内预测


理论

    
1、宏块大小是 16x16,每个宏块包含一个 16x16 大小的亮度块和两个 8x8 的色度块
2、亮度分量
    对于亮度分量而言,帧内预测只针对 16x16 的块或者 4x4 的小块,换句话说,对于亮度分量而言,进行帧内预测的时候,它只能被划分为 16x16(即不进行划分)的块,或者 4x4 的小块。
    2.1、16x16 亮度块
        有4种帧内预测模式:DC、H(水平模式)、V(垂直模式)、plane(平面模式)。对于的序号分别是:0、1、2、3.
    2.2、4x4 亮度块
        有 9 种帧内预测模式:模式 0(垂直),模式 1(水平),模式 2(DC),模式3(45°),模式 4(135°),模式 5(116.6°),模式 6(153.4°),模式 7(63.4°),模式 8(26.6°)
    2.3、额外补充
        按照理论上,亮度分量只支持 16x16 和 4x4 的块,但是后来增加了 8x8 的DCT,它要求提供 8x8 的帧内预测,因此,x264 提供了 8x8 块的帧内预测。它的模式和 4x4 小块的模式是一样的。
3、色度分量对于色度分量,帧内预测只支持 8x8 的色度分量,也就是说宏块对应的色度分量不用划分,直接进行预测。它使用的预测方式和 16x16 的亮度块一样。
4、处理过程
    先计算宏块下每个 4x4 小块的最优模式,把所有 4x4 小块的最优代价相加(假设为 sum),然后计算 16x16 的块的最优模式,与 sum 进行比较,决定使用哪一个作为最优模式。



代码


1、入口函数 x264_mb_analyse_intra
2、处理步骤
    2.1、进行 16x16 亮度块的帧内预测
        (1)调用函数 predict_16x16_mode_available,选出 16x16 亮度块可用的帧内模式(要根据邻居块是否存在来判别哪些模式可用)
        (2) 遍 历 所 有 可 用 的 16x16 亮 度 块 的 模 式 , 调 用 函 数 指 针predict_16x16[...]进行预测,然后计算 sad,最后选出 16x16 块的最优模式
    2.2、进行 8x8 色度块的帧内预测
        (1) 如 果 使 用 了 rdo 模 式 ( 可 以 由 用 户 设 置 ), 那 么 调 用x264_mb_analyse_intra_chroma 函数,进行下面处理:调用 predict_8x8chroma_mode_available 函数,选取 8x8 色度块可用的帧内预测模式2)遍历所有的 8x8 色度块的预测模式,进行做鱼操作,计算 sad,选出最优的模式
        (2)如果没有使用 rdo 模式,那么计算一些额外的 sad(相当于是 8x8色度块的 sad),它们的模式应该和对应的 16x16 亮度块的模式一样,不用另外计算。
    2.3、进行 4x4 亮度块的帧内预测,遍历宏块下所有的 4x4 小块,对于每一个小块,进行下面操作
        (1)调用 x264_mb_predict_intra4x4_mode 函数,得到一个从邻居块预测来的模式 pred_mode,用于后面 sad 的计算。
        (2)调用 predict_4x4_mode_available 函数,选取可用的 4x4 亮度块的预测模式。
        (3)遍历所有可用的预测模式,进行预测操作,选取最优的模式
        (4)对于最优的模式,调用 x264_mb_encode_i4x4,计算比特率
        (5)把所有的 4x4 小块最优模式的代价相加,就得到了宏块下 4x4 划分的最优模式的代价
    2.4、如果有必要(使用 8x8 的 DCT 时),那么进行 8x8 亮度块的帧内预测
        (1)宏块由 4 个 8x8 块构成,因此要依次处理这 4 个块。
        (2)对于每一个 8x8 可用的模式(由于 8x8 亮度预测模式和 4x4 的一样,因此实际调用 predict_4x4_mode_available 函数来得到可用的预测模式),遍历这些可用模式,进行 8x8 的预测操作,选出最优的模式,然后调用x264_mb_encode_i8x8 计算该最优模式的比特率。
        (3)累加宏块下 8x8 小块的最优代价,即为 8x8 帧内预测模式的最优代价
3、具体代码实现,抽取了几个典型的函数作为例子:

/* 帧内预测主函数 */
static void x264_mb_analyse_intra( x264_t *h, x264_mb_analysis_t *a, int i_cost_inter )
{
    const unsigned int flags = h->sh.i_type == SLICE_TYPE_I ? h->param.analyse.intra :
                                                              h->param.analyse.inter;
    const int i_stride = h->mb.pic.i_stride[0];
    uint8_t *p_src = h->mb.pic.p_fenc[0];
    uint8_t *p_dst = h->mb.pic.p_fdec[0];
    int
            f8_satd_rd_ratio = 0;
    int i, idx;
    int i_max;
    int predict_mode[9];
    const int i_satd_thresh = a->i_best_satd * 5/4 + a->i_lambda * 10;
    /*---------------- Try all mode and calculate their score ---------------*/
    /* 16x16 prediction selection */
    /* 16x16亮度 预测 */
    predict_16x16_mode_available( h->mb.i_neighbour, predict_mode, &i_max );
    for( i = 0; i < i_max; i++ )
    {
        int i_sad;
        int i_mode;
        i_mode = predict_mode[i];
        h->predict_16x16[i_mode]( p_dst, i_stride );
        i_sad = h->pixf.mbcmp[PIXEL_16x16]( p_dst, i_stride, p_src, i_stride ) +
                a->i_lambda * bs_size_ue( x264_mb_pred_mode16x16_fix[i_mode] );
        if( a->i_sad_i16x16 > i_sad )
        {
            a->i_predict16x16 = i_mode;
            a->i_sad_i16x16
        }
    }
    = i_sad;/* 8x8色度预测 */
    if( a->b_mbrd )
    {
        f8_satd_rd_ratio = ((unsigned)i_cost_inter << 8) / a->i_best_satd + 1;
        x264_mb_analyse_intra_chroma( h, a );
        if( h->mb.b_chroma_me )
            a->i_sad_i16x16 += a->i_sad_i8x8chroma;
        if( a->i_sad_i16x16 < i_satd_thresh )
        {
            h->mb.i_type = I_16x16;
            h->mb.i_intra16x16_pred_mode = a->i_predict16x16;
            a->i_sad_i16x16 = x264_rd_cost_mb( h, a->i_lambda2 );
        }
        else
            a->i_sad_i16x16 = a->i_sad_i16x16 * f8_satd_rd_ratio >> 8;
    }
    else
    {
        if( h->sh.i_type == SLICE_TYPE_B )
            /* cavlc mb type prefix */
            a->i_sad_i16x16 += a->i_lambda * i_mb_b_cost_table[I_16x16];
        if( a->b_fast_intra && a->i_sad_i16x16 > 2*i_cost_inter )
            return;
    }
    /* 4x4 prediction selection */
    /* 4x4亮度预测 */
    if( flags & X264_ANALYSE_I4x4 )
    {
        a->i_sad_i4x4 = 0;
        for( idx = 0; idx < 16; idx++ )
        {
            uint8_t *p_src_by;
            uint8_t *p_dst_by;
            int
                    i_best;
            int x, y;
            int i_pred_mode;
            i_pred_mode= x264_mb_predict_intra4x4_mode( h, idx );
            x = block_idx_x[idx];
            y = block_idx_y[idx];
            p_src_by = p_src + 4 * x + 4 * y * i_stride;
            p_dst_by = p_dst + 4 * x + 4 * y * i_stride;i_best = COST_MAX;
            predict_4x4_mode_available( h->mb.i_neighbour4[idx], predict_mode,
                                        &i_max );
            for( i = 0; i < i_max; i++ )
            {
                int i_sad;
                int i_mode;
                i_mode = predict_mode[i];
                h->predict_4x4[i_mode]( p_dst_by, i_stride );
                i_sad = h->pixf.mbcmp[PIXEL_4x4]( p_dst_by, i_stride,
                        p_src_by, i_stride )
                        + a->i_lambda * (i_pred_mode ==
                                         x264_mb_pred_mode4x4_fix(i_mode) ? 1 : 4);
                if( i_best > i_sad )
                {
                    a->i_predict4x4[x][y] = i_mode;
                    i_best = i_sad;
                }
            }
            a->i_sad_i4x4 += i_best;
            /* we need to encode this block now (for next ones) */
            h->predict_4x4[a->i_predict4x4[x][y]]( p_dst_by, i_stride );
            /* 计算该最优模式下的比特率 */
            x264_mb_encode_i4x4( h, idx, a->i_qp );
            h->mb.cache.intra4x4_pred_mode[x264_scan8[idx]] = a->i_predict4x4[x][y];
        }
        a->i_sad_i4x4 += a->i_lambda * 24;
        /* from JVT (SATD0) */
        if( a->b_mbrd )
        {
            if( h->mb.b_chroma_me )
                a->i_sad_i4x4 += a->i_sad_i8x8chroma;
            if( a->i_sad_i4x4 < i_satd_thresh )
            {
                h->mb.i_type = I_4x4;
                a->i_sad_i4x4 = x264_rd_cost_mb( h, a->i_lambda2 );
            }
            elsea->i_sad_i4x4 = a->i_sad_i4x4 * f8_satd_rd_ratio >> 8;
        }
        else
        {
            if( h->sh.i_type == SLICE_TYPE_B )
                a->i_sad_i4x4 += a->i_lambda * i_mb_b_cost_table[I_4x4];
        }
    }
    /* 8x8 prediction selection */
    /* 如果有必要,那么进行8x8的亮度预测 */
    if( flags & X264_ANALYSE_I8x8 )
    {
        a->i_sad_i8x8 = 0;
        for( idx = 0; idx < 4; idx++ )
        {
            uint8_t *p_src_by;
            uint8_t *p_dst_by;
            int
                    i_best;
            int x, y;
            int i_pred_mode;
            i_pred_mode= x264_mb_predict_intra4x4_mode( h, 4*idx );
            x = idx&1;
            y = idx>>1;
            p_src_by = p_src + 8 * x + 8 * y * i_stride;
            p_dst_by = p_dst + 8 * x + 8 * y * i_stride;
            i_best = COST_MAX;
            predict_4x4_mode_available( h->mb.i_neighbour8[idx], predict_mode,
                                        &i_max );
            for( i = 0; i < i_max; i++ )
            {
                int i_sad;
                int i_mode;
                i_mode = predict_mode[i];
                h->predict_8x8[i_mode]( p_dst_by, i_stride, h->mb.i_neighbour );
                /* could use sa8d, but it doesn't seem worth the speed cost (without
mmx at least) */
                i_sad = h->pixf.mbcmp[PIXEL_8x8]( p_dst_by, i_stride,
                        p_src_by, i_stride )+ a->i_lambda * (i_pred_mode ==
                                                             x264_mb_pred_mode4x4_fix(i_mode) ? 1 : 4);
                if( i_best > i_sad )
                {
                    a->i_predict8x8[x][y] = i_mode;
                    i_best = i_sad;
                }
            }
            a->i_sad_i8x8 += i_best;
            /* we need to encode this block now (for next ones) */
            h->predict_8x8[a->i_predict8x8[x][y]]( p_dst_by, i_stride,
                                                   h->mb.i_neighbour );
            x264_mb_encode_i8x8( h, idx, a->i_qp );
            x264_macroblock_cache_intra8x8_pred( h, 2*x, 2*y, a->i_predict8x8[x][y] );
        }
        if( a->b_mbrd )
        {
            if( h->mb.b_chroma_me )
                a->i_sad_i8x8 += a->i_sad_i8x8chroma;
            if( a->i_sad_i8x8 < i_satd_thresh )
            {
                h->mb.i_type = I_8x8;
                a->i_sad_i8x8 = x264_rd_cost_mb( h, a->i_lambda2 );
            }
            else
                a->i_sad_i8x8 = a->i_sad_i8x8 * f8_satd_rd_ratio >> 8;
        }
        else
        {
            // FIXME some bias like in i4x4?
            if( h->sh.i_type == SLICE_TYPE_B )
                a->i_sad_i8x8 += a->i_lambda * i_mb_b_cost_table[I_8x8];
        }
    }
}
/* 选取 16x16 亮度块,可用的帧内预测模式 */
static void predict_16x16_mode_available( unsigned int i_neighbour, int *mode, int
                                          *pi_count ){
    if( i_neighbour & MB_TOPLEFT )
    {
        /* top and left avaible */
        *mode++ = I_PRED_16x16_V;
        *mode++ = I_PRED_16x16_H;
        *mode++ = I_PRED_16x16_DC;
        *mode++ = I_PRED_16x16_P;
        *pi_count = 4;
    }
    else if( i_neighbour & MB_LEFT )
    {
        /* left available*/
        *mode++ = I_PRED_16x16_DC_LEFT;
        *mode++ = I_PRED_16x16_H;
        *pi_count = 2;
    }
    else if( i_neighbour & MB_TOP )
    {
        /* top available*/
        *mode++ = I_PRED_16x16_DC_TOP;
        *mode++ = I_PRED_16x16_V;
        *pi_count = 2;
    }
    else
    {
        /* none avaible */
        *mode = I_PRED_16x16_DC_128;
        *pi_count = 1;
    }
}
/* 从附近块中预测出一个模式 pred_mode */
int x264_mb_predict_intra4x4_mode( x264_t *h, int idx )
{
    const int ma = h->mb.cache.intra4x4_pred_mode[x264_scan8[idx] - 1];
    const int mb = h->mb.cache.intra4x4_pred_mode[x264_scan8[idx] - 8];
    const int m = X264_MIN( x264_mb_pred_mode4x4_fix(ma),
                            x264_mb_pred_mode4x4_fix(mb) );
    if( m < 0 )
        return I_PRED_4x4_DC;
    return m;}
/* 4x4(8x8)亮度块,可用的预测模式 */
static void predict_4x4_mode_available( unsigned int i_neighbour,
                                        int *mode, int *pi_count )
{
    /* FIXME even when b_tr == 0 there is some case where missing pixels
* are emulated and thus more mode are available TODO
* analysis and encode should be fixed too */
    int b_l = i_neighbour & MB_LEFT;
    int b_t = i_neighbour & MB_TOP;
    int b_tr = i_neighbour & MB_TOPRIGHT;
    if( b_l && b_t )
    {
        *mode++ = I_PRED_4x4_DC;
        *mode++ = I_PRED_4x4_H;
        *mode++ = I_PRED_4x4_V;
        *mode++ = I_PRED_4x4_DDR;
        *mode++ = I_PRED_4x4_VR;
        *mode++ = I_PRED_4x4_HD;
        *mode++ = I_PRED_4x4_HU;
        *pi_count = 7;
    }
    else if( b_l )
    {
        *mode++ = I_PRED_4x4_DC_LEFT;
        *mode++ = I_PRED_4x4_H;
        *mode++ = I_PRED_4x4_HU;
        *pi_count = 3;
    }
    else if( b_t )
    {
        *mode++ = I_PRED_4x4_DC_TOP;
        *mode++ = I_PRED_4x4_V;
        *pi_count = 2;
    }
    else
    {
        *mode++ = I_PRED_4x4_DC_128;
        *pi_count = 1;
    }
    if( b_t && b_tr ){
        *mode++ = I_PRED_4x4_DDL;
        *mode++ = I_PRED_4x4_VL;
        (*pi_count) += 2;
    }
}
/* 编码/计算某个模式的比特率(为了选取最优模式) */
void x264_mb_encode_i4x4( x264_t *h, int idx, int i_qscale )
{
    const int i_stride = h->mb.pic.i_stride[0];
    const int i_offset = 4 * block_idx_x[idx] + 4 * block_idx_y[idx] * i_stride;
    uint8_t *p_src = &h->mb.pic.p_fenc[0][i_offset];
    uint8_t *p_dst = &h->mb.pic.p_fdec[0][i_offset];
    int16_t dct4x4[4][4];
    if( h->mb.b_lossless )
    {
        sub_zigzag_4x4full( h->dct.block[idx].luma4x4, p_src, p_dst, i_stride );
        return;
    }
    h->dctf.sub4x4_dct( dct4x4, p_src, i_stride, p_dst, i_stride );
    quant_4x4( dct4x4, h->quant4_mf[CQM_4IY], i_qscale, 1 );
    scan_zigzag_4x4full( h->dct.block[idx].luma4x4, dct4x4 );
    x264_mb_dequant_4x4( dct4x4, h->dequant4_mf[CQM_4IY], i_qscale );
    /* output samples to fdec */
    h->dctf.add4x4_idct( p_dst, i_stride, dct4x4 );
}
/* 16x16 亮度块水平预测 */
static void predict_16x16_h( uint8_t *src, int i_stride )
{
    int i,j;
    for( i = 0; i < 16; i++ )
    {
        uint8_t v;
        v = src[-1];
        for( j = 0; j < 16; j++ )
        {src[j] = v;
        }
        src += i_stride;
    }
}

猜你喜欢

转载自blog.csdn.net/NB_vol_1/article/details/78396361