源码阅读 x264 - 代价计算

x264x264_pixel_init 函数中初始化了与代价计算有关的函数,本文分析部分代价计算函数的实现,包括 SADSATDSSD、等

  • SAD (Sum of Absolute Difference) = SAE(Sum of Absolute Error) 绝对误差和
  • SATD(Sum of Absolute Transformed Difference)即 hadamard 变换后再绝对值求和
  • SSD(Sum of Squared Difference)= SSE(Sum of Squared Error) 差值的平方和
  • MAD(Mean Absolute Difference)= MAE(Mean Absolute Error) 平均绝对差值
  • MSD(Mean Squared Difference)= MSE(Mean Squared Error)平均平方误差

下面分别介绍 SADSSDSATD 的实现过程。

SAD 实现过程

x264_pixel_init 函数中的 INIT8(sad,) 展开,可以得到如下代码:

pixf->sad[PIXEL_16x16] = x264_pixel_sad_16x16;
pixf->sad[PIXEL_16x8]  = x264_pixel_sad_16x8;
pixf->sad[PIXEL_8x16]  = x264_pixel_sad_8x16;
pixf->sad[PIXEL_8x8]   = x264_pixel_sad_8x8;
pixf->sad[PIXEL_8x4]   = x264_pixel_sad_8x4;
pixf->sad[PIXEL_4x8]   = x264_pixel_sad_4x8;
pixf->sad[PIXEL_4x4]   = x264_pixel_sad_4x4;
pixf->sad[PIXEL_4x16]  = x264_pixel_sad_4x16;

我们选取其中最简单的 x264_pixel_sad_4x4 继续展开,它是通过一个宏来定义的:

#define PIXEL_SAD_C(name, lx, ly) \
static int name( pixel *pix1, intptr_t i_stride_pix1,  \
                 pixel *pix2, intptr_t i_stride_pix2 ) \
{                                                   \
    int i_sum = 0;                                  \
    for(int y = 0; y < ly; y++)                   \
    {                                               \
        for(int x = 0; x < lx; x++)               \
        {                                           \
            i_sum += abs(pix1[x] - pix2[x] );      \
        }                                           \
        pix1 += i_stride_pix1;                      \
        pix2 += i_stride_pix2;                      \
    }                                               \
    return i_sum;                                   \
}
PIXEL_SAD_C(x264_pixel_sad_4x4, 4, 4)
/* 展开宏定义如下 */
static int x264_pixel_sad_4x4(uint8_t *pix1, intptr_t i_stride_pix1, uint8_t *pix2, intptr_t i_stride_pix2) {
    int i_sum = 0;
    for(int y = 0; y < 4; y++) {
        for(int x = 0; x < 4; x++) {
            i_sum += abs(pix1[x] - pix2[x]);
        }
        pix1 += i_stride_pix1;
        pix2 += i_stride_pix2;
    }
    return i_sum;
}

SSD 实现过程

x264_pixel_init 函数中的 INIT8(ssd,) 展开,可以得到如下代码:

pixf->ssd[PIXEL_16x16] = x264_pixel_ssd_16x16;
pixf->ssd[PIXEL_16x8]  = x264_pixel_ssd_16x8;
pixf->ssd[PIXEL_8x16]  = x264_pixel_ssd_8x16;
pixf->ssd[PIXEL_8x8]   = x264_pixel_ssd_8x8;
pixf->ssd[PIXEL_8x4]   = x264_pixel_ssd_8x4;
pixf->ssd[PIXEL_4x8]   = x264_pixel_ssd_4x8;
pixf->ssd[PIXEL_4x4]   = x264_pixel_ssd_4x4;
pixf->ssd[PIXEL_4x16]  = x264_pixel_ssd_4x16;

我们选取其中最简单的 x264_pixel_ssd_4x4 继续展开,它也是通过一个宏来定义的:

#define PIXEL_SSD_C(name, lx, ly) \
static int x264_pixel_ssd_4x4( pixel *pix1, intptr_t i_stride_pix1,  \
                 pixel *pix2, intptr_t i_stride_pix2 ) \
{                                                   \
    int i_sum = 0;                                  \
    for(int y = 0; y < ly; y++)                   \
    {                                               \
        for(int x = 0; x < lx; x++)               \
        {                                           \
            int d = pix1[x] - pix2[x];              \
            i_sum += d*d;                           \
        }                                           \
        pix1 += i_stride_pix1;                      \
        pix2 += i_stride_pix2;                      \
    }                                               \
    return i_sum;                                   \
}
PIXEL_SSD_C(x264_pixel_ssd_4x4, 4, 4)
/* 展开宏定义如下 */
tatic int name(uint8_t *pix1, intptr_t i_stride_pix1, uint8_t *pix2, intptr_t i_stride_pix2) {
    int i_sum = 0;
    for(int y = 0; y < 4; y++) {
        for(int x = 0; x < 4; x++) {
            int d = pix1[x] - pix2[x];
            i_sum += d*d;
        }
        pix1 += i_stride_pix1;
        pix2 += i_stride_pix2;
    }
    return i_sum;
}

SATD 实现过程

x264_pixel_init 函数中的 INIT8(satd,) 展开,可以得到如下代码:

pixf->satd[PIXEL_16x16] = x264_pixel_satd_16x16;
pixf->satd[PIXEL_16x8]  = x264_pixel_satd_16x8;
pixf->satd[PIXEL_8x16]  = x264_pixel_satd_8x16;
pixf->satd[PIXEL_8x8]   = x264_pixel_satd_8x8;
pixf->satd[PIXEL_8x4]   = x264_pixel_satd_8x4;
pixf->satd[PIXEL_4x8]   = x264_pixel_satd_4x8;
pixf->satd[PIXEL_4x4]   = x264_pixel_satd_4x4;
pixf->satd[PIXEL_4x16]  = x264_pixel_satd_4x16;

同样,选取最简单的 x264_pixel_satd_4x4 继续展开,它的定义如下:

typedef uint8_t  pixel;
typedef uint16_t sum_t;
typedef uint32_t sum2_t;
#define BITS_PER_SUM (8 * sizeof(sum_t))

#define HADAMARD4(d0, d1, d2, d3, s0, s1, s2, s3) {\
    sum2_t t0 = s0 + s1;\
    sum2_t t1 = s0 - s1;\
    sum2_t t2 = s2 + s3;\
    sum2_t t3 = s2 - s3;\
    d0 = t0 + t2;\
    d2 = t0 - t2;\
    d1 = t1 + t3;\
    d3 = t1 - t3;\
}
static NOINLINE int x264_pixel_satd_4x4(pixel *pix1, intptr_t i_pix1, pixel *pix2, intptr_t i_pix2 ) {
    sum2_t tmp[4][2];
    sum2_t a0, a1, a2, a3, b0, b1;
    sum2_t sum = 0;
    for ( int i = 0; i < 4; i++, pix1 += i_pix1, pix2 += i_pix2 ) {
        a0 = pix1[0] - pix2[0];
        a1 = pix1[1] - pix2[1];
        b0 = (a0+a1) + ((a0-a1)<<BITS_PER_SUM);
        a2 = pix1[2] - pix2[2];
        a3 = pix1[3] - pix2[3];
        b1 = (a2+a3) + ((a2-a3)<<BITS_PER_SUM);
        tmp[i][0] = b0 + b1;
        tmp[i][1] = b0 - b1;
    }
    for( int i = 0; i < 2; i++ ) {
        HADAMARD4( a0, a1, a2, a3, tmp[0][i], tmp[1][i], tmp[2][i], tmp[3][i] );
        a0 = abs2(a0) + abs2(a1) + abs2(a2) + abs2(a3);
        sum += ((sum_t)a0) + (a0>>BITS_PER_SUM);
    }
    return sum >> 1;
}

参考资料