Home | History | Annotate | Download | only in encoder
      1 /*
      2  *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 
     12 #include "vpx_scale/yv12config.h"
     13 #include "math.h"
     14 
     15 #define C1 (float)(64 * 64 * 0.01*255*0.01*255)
     16 #define C2 (float)(64 * 64 * 0.03*255*0.03*255)
     17 
     18 static int width_y;
     19 static int height_y;
     20 static int height_uv;
     21 static int width_uv;
     22 static int stride_uv;
     23 static int stride;
     24 static int lumimask;
     25 static int luminance;
     26 static double plane_summed_weights = 0;
     27 
     28 static short img12_sum_block[8*4096*4096*2] ;
     29 
     30 static short img1_sum[8*4096*2];
     31 static short img2_sum[8*4096*2];
     32 static int   img1_sq_sum[8*4096*2];
     33 static int   img2_sq_sum[8*4096*2];
     34 static int   img12_mul_sum[8*4096*2];
     35 
     36 
     37 double vp8_similarity
     38 (
     39     int mu_x,
     40     int mu_y,
     41     int pre_mu_x2,
     42     int pre_mu_y2,
     43     int pre_mu_xy2
     44 )
     45 {
     46     int mu_x2, mu_y2, mu_xy, theta_x2, theta_y2, theta_xy;
     47 
     48     mu_x2 = mu_x * mu_x;
     49     mu_y2 = mu_y * mu_y;
     50     mu_xy = mu_x * mu_y;
     51 
     52     theta_x2 = 64 * pre_mu_x2 - mu_x2;
     53     theta_y2 = 64 * pre_mu_y2 - mu_y2;
     54     theta_xy = 64 * pre_mu_xy2 - mu_xy;
     55 
     56     return (2 * mu_xy + C1) * (2 * theta_xy + C2) / ((mu_x2 + mu_y2 + C1) * (theta_x2 + theta_y2 + C2));
     57 }
     58 
     59 double vp8_ssim
     60 (
     61     const unsigned char *img1,
     62     const unsigned char *img2,
     63     int stride_img1,
     64     int stride_img2,
     65     int width,
     66     int height
     67 )
     68 {
     69     int x, y, x2, y2, img1_block, img2_block, img1_sq_block, img2_sq_block, img12_mul_block, temp;
     70 
     71     double plane_quality, weight, mean;
     72 
     73     short *img1_sum_ptr1, *img1_sum_ptr2;
     74     short *img2_sum_ptr1, *img2_sum_ptr2;
     75     int *img1_sq_sum_ptr1, *img1_sq_sum_ptr2;
     76     int *img2_sq_sum_ptr1, *img2_sq_sum_ptr2;
     77     int *img12_mul_sum_ptr1, *img12_mul_sum_ptr2;
     78 
     79     plane_quality = 0;
     80 
     81     if (lumimask)
     82         plane_summed_weights = 0.0f;
     83     else
     84         plane_summed_weights = (height - 7) * (width - 7);
     85 
     86     //some prologue for the main loop
     87     temp = 8 * width;
     88 
     89     img1_sum_ptr1      = img1_sum + temp;
     90     img2_sum_ptr1      = img2_sum + temp;
     91     img1_sq_sum_ptr1   = img1_sq_sum + temp;
     92     img2_sq_sum_ptr1   = img2_sq_sum + temp;
     93     img12_mul_sum_ptr1 = img12_mul_sum + temp;
     94 
     95     for (x = 0; x < width; x++)
     96     {
     97         img1_sum[x]      = img1[x];
     98         img2_sum[x]      = img2[x];
     99         img1_sq_sum[x]   = img1[x] * img1[x];
    100         img2_sq_sum[x]   = img2[x] * img2[x];
    101         img12_mul_sum[x] = img1[x] * img2[x];
    102 
    103         img1_sum_ptr1[x]      = 0;
    104         img2_sum_ptr1[x]      = 0;
    105         img1_sq_sum_ptr1[x]   = 0;
    106         img2_sq_sum_ptr1[x]   = 0;
    107         img12_mul_sum_ptr1[x] = 0;
    108     }
    109 
    110     //the main loop
    111     for (y = 1; y < height; y++)
    112     {
    113         img1 += stride_img1;
    114         img2 += stride_img2;
    115 
    116         temp = (y - 1) % 9 * width;
    117 
    118         img1_sum_ptr1      = img1_sum + temp;
    119         img2_sum_ptr1      = img2_sum + temp;
    120         img1_sq_sum_ptr1   = img1_sq_sum + temp;
    121         img2_sq_sum_ptr1   = img2_sq_sum + temp;
    122         img12_mul_sum_ptr1 = img12_mul_sum + temp;
    123 
    124         temp = y % 9 * width;
    125 
    126         img1_sum_ptr2      = img1_sum + temp;
    127         img2_sum_ptr2      = img2_sum + temp;
    128         img1_sq_sum_ptr2   = img1_sq_sum + temp;
    129         img2_sq_sum_ptr2   = img2_sq_sum + temp;
    130         img12_mul_sum_ptr2 = img12_mul_sum + temp;
    131 
    132         for (x = 0; x < width; x++)
    133         {
    134             img1_sum_ptr2[x]      = img1_sum_ptr1[x] + img1[x];
    135             img2_sum_ptr2[x]      = img2_sum_ptr1[x] + img2[x];
    136             img1_sq_sum_ptr2[x]   = img1_sq_sum_ptr1[x] + img1[x] * img1[x];
    137             img2_sq_sum_ptr2[x]   = img2_sq_sum_ptr1[x] + img2[x] * img2[x];
    138             img12_mul_sum_ptr2[x] = img12_mul_sum_ptr1[x] + img1[x] * img2[x];
    139         }
    140 
    141         if (y > 6)
    142         {
    143             //calculate the sum of the last 8 lines by subtracting the total sum of 8 lines back from the present sum
    144             temp = (y + 1) % 9 * width;
    145 
    146             img1_sum_ptr1      = img1_sum + temp;
    147             img2_sum_ptr1      = img2_sum + temp;
    148             img1_sq_sum_ptr1   = img1_sq_sum + temp;
    149             img2_sq_sum_ptr1   = img2_sq_sum + temp;
    150             img12_mul_sum_ptr1 = img12_mul_sum + temp;
    151 
    152             for (x = 0; x < width; x++)
    153             {
    154                 img1_sum_ptr1[x]      = img1_sum_ptr2[x] - img1_sum_ptr1[x];
    155                 img2_sum_ptr1[x]      = img2_sum_ptr2[x] - img2_sum_ptr1[x];
    156                 img1_sq_sum_ptr1[x]   = img1_sq_sum_ptr2[x] - img1_sq_sum_ptr1[x];
    157                 img2_sq_sum_ptr1[x]   = img2_sq_sum_ptr2[x] - img2_sq_sum_ptr1[x];
    158                 img12_mul_sum_ptr1[x] = img12_mul_sum_ptr2[x] - img12_mul_sum_ptr1[x];
    159             }
    160 
    161             //here we calculate the sum over the 8x8 block of pixels
    162             //this is done by sliding a window across the column sums for the last 8 lines
    163             //each time adding the new column sum, and subtracting the one which fell out of the window
    164             img1_block      = 0;
    165             img2_block      = 0;
    166             img1_sq_block   = 0;
    167             img2_sq_block   = 0;
    168             img12_mul_block = 0;
    169 
    170             //prologue, and calculation of simularity measure from the first 8 column sums
    171             for (x = 0; x < 8; x++)
    172             {
    173                 img1_block      += img1_sum_ptr1[x];
    174                 img2_block      += img2_sum_ptr1[x];
    175                 img1_sq_block   += img1_sq_sum_ptr1[x];
    176                 img2_sq_block   += img2_sq_sum_ptr1[x];
    177                 img12_mul_block += img12_mul_sum_ptr1[x];
    178             }
    179 
    180             if (lumimask)
    181             {
    182                 y2 = y - 7;
    183                 x2 = 0;
    184 
    185                 if (luminance)
    186                 {
    187                     mean = (img2_block + img1_block) / 128.0f;
    188 
    189                     if (!(y2 % 2 || x2 % 2))
    190                         *(img12_sum_block + y2 / 2 * width_uv + x2 / 2) = img2_block + img1_block;
    191                 }
    192                 else
    193                 {
    194                     mean = *(img12_sum_block + y2 * width_uv + x2);
    195                     mean += *(img12_sum_block + y2 * width_uv + x2 + 4);
    196                     mean += *(img12_sum_block + (y2 + 4) * width_uv + x2);
    197                     mean += *(img12_sum_block + (y2 + 4) * width_uv + x2 + 4);
    198 
    199                     mean /= 512.0f;
    200                 }
    201 
    202                 weight = mean < 40 ? 0.0f :
    203                          (mean < 50 ? (mean - 40.0f) / 10.0f : 1.0f);
    204                 plane_summed_weights += weight;
    205 
    206                 plane_quality += weight * vp8_similarity(img1_block, img2_block, img1_sq_block, img2_sq_block, img12_mul_block);
    207             }
    208             else
    209                 plane_quality += vp8_similarity(img1_block, img2_block, img1_sq_block, img2_sq_block, img12_mul_block);
    210 
    211             //and for the rest
    212             for (x = 8; x < width; x++)
    213             {
    214                 img1_block      = img1_block + img1_sum_ptr1[x] - img1_sum_ptr1[x - 8];
    215                 img2_block      = img2_block + img2_sum_ptr1[x] - img2_sum_ptr1[x - 8];
    216                 img1_sq_block   = img1_sq_block + img1_sq_sum_ptr1[x] - img1_sq_sum_ptr1[x - 8];
    217                 img2_sq_block   = img2_sq_block + img2_sq_sum_ptr1[x] - img2_sq_sum_ptr1[x - 8];
    218                 img12_mul_block = img12_mul_block + img12_mul_sum_ptr1[x] - img12_mul_sum_ptr1[x - 8];
    219 
    220                 if (lumimask)
    221                 {
    222                     y2 = y - 7;
    223                     x2 = x - 7;
    224 
    225                     if (luminance)
    226                     {
    227                         mean = (img2_block + img1_block) / 128.0f;
    228 
    229                         if (!(y2 % 2 || x2 % 2))
    230                             *(img12_sum_block + y2 / 2 * width_uv + x2 / 2) = img2_block + img1_block;
    231                     }
    232                     else
    233                     {
    234                         mean = *(img12_sum_block + y2 * width_uv + x2);
    235                         mean += *(img12_sum_block + y2 * width_uv + x2 + 4);
    236                         mean += *(img12_sum_block + (y2 + 4) * width_uv + x2);
    237                         mean += *(img12_sum_block + (y2 + 4) * width_uv + x2 + 4);
    238 
    239                         mean /= 512.0f;
    240                     }
    241 
    242                     weight = mean < 40 ? 0.0f :
    243                              (mean < 50 ? (mean - 40.0f) / 10.0f : 1.0f);
    244                     plane_summed_weights += weight;
    245 
    246                     plane_quality += weight * vp8_similarity(img1_block, img2_block, img1_sq_block, img2_sq_block, img12_mul_block);
    247                 }
    248                 else
    249                     plane_quality += vp8_similarity(img1_block, img2_block, img1_sq_block, img2_sq_block, img12_mul_block);
    250             }
    251         }
    252     }
    253 
    254     if (plane_summed_weights == 0)
    255         return 1.0f;
    256     else
    257         return plane_quality / plane_summed_weights;
    258 }
    259 
    260 double vp8_calc_ssim
    261 (
    262     YV12_BUFFER_CONFIG *source,
    263     YV12_BUFFER_CONFIG *dest,
    264     int lumamask,
    265     double *weight
    266 )
    267 {
    268     double a, b, c;
    269     double frame_weight;
    270     double ssimv;
    271 
    272     width_y = source->y_width;
    273     height_y = source->y_height;
    274     height_uv = source->uv_height;
    275     width_uv = source->uv_width;
    276     stride_uv = dest->uv_stride;
    277     stride = dest->y_stride;
    278 
    279     lumimask = lumamask;
    280 
    281     luminance = 1;
    282     a = vp8_ssim(source->y_buffer, dest->y_buffer,
    283                  source->y_stride, dest->y_stride, source->y_width, source->y_height);
    284     luminance = 0;
    285 
    286     frame_weight = plane_summed_weights / ((width_y - 7) * (height_y - 7));
    287 
    288     if (frame_weight == 0)
    289         a = b = c = 1.0f;
    290     else
    291     {
    292         b = vp8_ssim(source->u_buffer, dest->u_buffer,
    293                      source->uv_stride, dest->uv_stride, source->uv_width, source->uv_height);
    294 
    295         c = vp8_ssim(source->v_buffer, dest->v_buffer,
    296                      source->uv_stride, dest->uv_stride, source->uv_width, source->uv_height);
    297     }
    298 
    299     ssimv = a * .8 + .1 * (b + c);
    300 
    301     *weight = frame_weight;
    302 
    303     return ssimv;
    304 }
    305 
    306 // Google version of SSIM
    307 // SSIM
    308 #define KERNEL 3
    309 #define KERNEL_SIZE  (2 * KERNEL + 1)
    310 
    311 typedef unsigned char uint8;
    312 typedef unsigned int uint32;
    313 
    314 static const int K[KERNEL_SIZE] =
    315 {
    316     1, 4, 11, 16, 11, 4, 1    // 16 * exp(-0.3 * i * i)
    317 };
    318 static const double ki_w = 1. / 2304.;  // 1 / sum(i:0..6, j..6) K[i]*K[j]
    319 double get_ssimg(const uint8 *org, const uint8 *rec,
    320                  int xo, int yo, int W, int H,
    321                  const int stride1, const int stride2
    322                 )
    323 {
    324     // TODO(skal): use summed tables
    325     int y, x;
    326 
    327     const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL;
    328     const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL;
    329     const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL;
    330     const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL;
    331     // worst case of accumulation is a weight of 48 = 16 + 2 * (11 + 4 + 1)
    332     // with a diff of 255, squares. That would a max error of 0x8ee0900,
    333     // which fits into 32 bits integers.
    334     uint32 w = 0, xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
    335     org += ymin * stride1;
    336     rec += ymin * stride2;
    337 
    338     for (y = ymin; y <= ymax; ++y, org += stride1, rec += stride2)
    339     {
    340         const int Wy = K[KERNEL + y - yo];
    341 
    342         for (x = xmin; x <= xmax; ++x)
    343         {
    344             const  int Wxy = Wy * K[KERNEL + x - xo];
    345             // TODO(skal): inlined assembly
    346             w   += Wxy;
    347             xm  += Wxy * org[x];
    348             ym  += Wxy * rec[x];
    349             xxm += Wxy * org[x] * org[x];
    350             xym += Wxy * org[x] * rec[x];
    351             yym += Wxy * rec[x] * rec[x];
    352         }
    353     }
    354 
    355     {
    356         const double iw = 1. / w;
    357         const double iwx = xm * iw;
    358         const double iwy = ym * iw;
    359         double sxx = xxm * iw - iwx * iwx;
    360         double syy = yym * iw - iwy * iwy;
    361 
    362         // small errors are possible, due to rounding. Clamp to zero.
    363         if (sxx < 0.) sxx = 0.;
    364 
    365         if (syy < 0.) syy = 0.;
    366 
    367         {
    368             const double sxsy = sqrt(sxx * syy);
    369             const double sxy = xym * iw - iwx * iwy;
    370             static const double C11 = (0.01 * 0.01) * (255 * 255);
    371             static const double C22 = (0.03 * 0.03) * (255 * 255);
    372             static const double C33 = (0.015 * 0.015) * (255 * 255);
    373             const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
    374             const double c = (2. * sxsy      + C22) / (sxx + syy + C22);
    375 
    376             const double s = (sxy + C33) / (sxsy + C33);
    377             return l * c * s;
    378 
    379         }
    380     }
    381 
    382 }
    383 
    384 double get_ssimfull_kernelg(const uint8 *org, const uint8 *rec,
    385                             int xo, int yo, int W, int H,
    386                             const int stride1, const int stride2)
    387 {
    388     // TODO(skal): use summed tables
    389     // worst case of accumulation is a weight of 48 = 16 + 2 * (11 + 4 + 1)
    390     // with a diff of 255, squares. That would a max error of 0x8ee0900,
    391     // which fits into 32 bits integers.
    392     int y_, x_;
    393     uint32 xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
    394     org += (yo - KERNEL) * stride1;
    395     org += (xo - KERNEL);
    396     rec += (yo - KERNEL) * stride2;
    397     rec += (xo - KERNEL);
    398 
    399     for (y_ = 0; y_ < KERNEL_SIZE; ++y_, org += stride1, rec += stride2)
    400     {
    401         const int Wy = K[y_];
    402 
    403         for (x_ = 0; x_ < KERNEL_SIZE; ++x_)
    404         {
    405             const int Wxy = Wy * K[x_];
    406             // TODO(skal): inlined assembly
    407             const int org_x = org[x_];
    408             const int rec_x = rec[x_];
    409             xm  += Wxy * org_x;
    410             ym  += Wxy * rec_x;
    411             xxm += Wxy * org_x * org_x;
    412             xym += Wxy * org_x * rec_x;
    413             yym += Wxy * rec_x * rec_x;
    414         }
    415     }
    416 
    417     {
    418         const double iw = ki_w;
    419         const double iwx = xm * iw;
    420         const double iwy = ym * iw;
    421         double sxx = xxm * iw - iwx * iwx;
    422         double syy = yym * iw - iwy * iwy;
    423 
    424         // small errors are possible, due to rounding. Clamp to zero.
    425         if (sxx < 0.) sxx = 0.;
    426 
    427         if (syy < 0.) syy = 0.;
    428 
    429         {
    430             const double sxsy = sqrt(sxx * syy);
    431             const double sxy = xym * iw - iwx * iwy;
    432             static const double C11 = (0.01 * 0.01) * (255 * 255);
    433             static const double C22 = (0.03 * 0.03) * (255 * 255);
    434             static const double C33 = (0.015 * 0.015) * (255 * 255);
    435             const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
    436             const double c = (2. * sxsy      + C22) / (sxx + syy + C22);
    437             const double s = (sxy + C33) / (sxsy + C33);
    438             return l * c * s;
    439         }
    440     }
    441 }
    442 
    443 double calc_ssimg(const uint8 *org, const uint8 *rec,
    444                   const int image_width, const int image_height,
    445                   const int stride1, const int stride2
    446                  )
    447 {
    448     int j, i;
    449     double SSIM = 0.;
    450 
    451     for (j = 0; j < KERNEL; ++j)
    452     {
    453         for (i = 0; i < image_width; ++i)
    454         {
    455             SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
    456         }
    457     }
    458 
    459     for (j = KERNEL; j < image_height - KERNEL; ++j)
    460     {
    461         for (i = 0; i < KERNEL; ++i)
    462         {
    463             SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
    464         }
    465 
    466         for (i = KERNEL; i < image_width - KERNEL; ++i)
    467         {
    468             SSIM += get_ssimfull_kernelg(org, rec, i, j,
    469                                          image_width, image_height, stride1, stride2);
    470         }
    471 
    472         for (i = image_width - KERNEL; i < image_width; ++i)
    473         {
    474             SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
    475         }
    476     }
    477 
    478     for (j = image_height - KERNEL; j < image_height; ++j)
    479     {
    480         for (i = 0; i < image_width; ++i)
    481         {
    482             SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
    483         }
    484     }
    485 
    486     return SSIM;
    487 }
    488 
    489 
    490 double vp8_calc_ssimg
    491 (
    492     YV12_BUFFER_CONFIG *source,
    493     YV12_BUFFER_CONFIG *dest,
    494     double *ssim_y,
    495     double *ssim_u,
    496     double *ssim_v
    497 )
    498 {
    499     double ssim_all = 0;
    500     int ysize  = source->y_width * source->y_height;
    501     int uvsize = ysize / 4;
    502 
    503     *ssim_y = calc_ssimg(source->y_buffer, dest->y_buffer,
    504                          source->y_width, source->y_height,
    505                          source->y_stride, dest->y_stride);
    506 
    507 
    508     *ssim_u = calc_ssimg(source->u_buffer, dest->u_buffer,
    509                          source->uv_width, source->uv_height,
    510                          source->uv_stride, dest->uv_stride);
    511 
    512 
    513     *ssim_v = calc_ssimg(source->v_buffer, dest->v_buffer,
    514                          source->uv_width, source->uv_height,
    515                          source->uv_stride, dest->uv_stride);
    516 
    517     ssim_all = (*ssim_y + *ssim_u + *ssim_v) / (ysize + uvsize + uvsize);
    518     *ssim_y /= ysize;
    519     *ssim_u /= uvsize;
    520     *ssim_v /= uvsize;
    521     return ssim_all;
    522 }
    523