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 #include "onyx_int.h"
     15 
     16 #if CONFIG_RUNTIME_CPU_DETECT
     17 #define IF_RTCD(x)  (x)
     18 #else
     19 #define IF_RTCD(x)  NULL
     20 #endif
     21 // Google version of SSIM
     22 // SSIM
     23 #define KERNEL 3
     24 #define KERNEL_SIZE  (2 * KERNEL + 1)
     25 
     26 typedef unsigned char uint8;
     27 typedef unsigned int uint32;
     28 
     29 static const int K[KERNEL_SIZE] =
     30 {
     31     1, 4, 11, 16, 11, 4, 1    // 16 * exp(-0.3 * i * i)
     32 };
     33 static const double ki_w = 1. / 2304.;  // 1 / sum(i:0..6, j..6) K[i]*K[j]
     34 double get_ssimg(const uint8 *org, const uint8 *rec,
     35                  int xo, int yo, int W, int H,
     36                  const int stride1, const int stride2
     37                 )
     38 {
     39     // TODO(skal): use summed tables
     40     int y, x;
     41 
     42     const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL;
     43     const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL;
     44     const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL;
     45     const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL;
     46     // worst case of accumulation is a weight of 48 = 16 + 2 * (11 + 4 + 1)
     47     // with a diff of 255, squares. That would a max error of 0x8ee0900,
     48     // which fits into 32 bits integers.
     49     uint32 w = 0, xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
     50     org += ymin * stride1;
     51     rec += ymin * stride2;
     52 
     53     for (y = ymin; y <= ymax; ++y, org += stride1, rec += stride2)
     54     {
     55         const int Wy = K[KERNEL + y - yo];
     56 
     57         for (x = xmin; x <= xmax; ++x)
     58         {
     59             const  int Wxy = Wy * K[KERNEL + x - xo];
     60             // TODO(skal): inlined assembly
     61             w   += Wxy;
     62             xm  += Wxy * org[x];
     63             ym  += Wxy * rec[x];
     64             xxm += Wxy * org[x] * org[x];
     65             xym += Wxy * org[x] * rec[x];
     66             yym += Wxy * rec[x] * rec[x];
     67         }
     68     }
     69 
     70     {
     71         const double iw = 1. / w;
     72         const double iwx = xm * iw;
     73         const double iwy = ym * iw;
     74         double sxx = xxm * iw - iwx * iwx;
     75         double syy = yym * iw - iwy * iwy;
     76 
     77         // small errors are possible, due to rounding. Clamp to zero.
     78         if (sxx < 0.) sxx = 0.;
     79 
     80         if (syy < 0.) syy = 0.;
     81 
     82         {
     83             const double sxsy = sqrt(sxx * syy);
     84             const double sxy = xym * iw - iwx * iwy;
     85             static const double C11 = (0.01 * 0.01) * (255 * 255);
     86             static const double C22 = (0.03 * 0.03) * (255 * 255);
     87             static const double C33 = (0.015 * 0.015) * (255 * 255);
     88             const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
     89             const double c = (2. * sxsy      + C22) / (sxx + syy + C22);
     90 
     91             const double s = (sxy + C33) / (sxsy + C33);
     92             return l * c * s;
     93 
     94         }
     95     }
     96 
     97 }
     98 
     99 double get_ssimfull_kernelg(const uint8 *org, const uint8 *rec,
    100                             int xo, int yo, int W, int H,
    101                             const int stride1, const int stride2)
    102 {
    103     // TODO(skal): use summed tables
    104     // worst case of accumulation is a weight of 48 = 16 + 2 * (11 + 4 + 1)
    105     // with a diff of 255, squares. That would a max error of 0x8ee0900,
    106     // which fits into 32 bits integers.
    107     int y_, x_;
    108     uint32 xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
    109     org += (yo - KERNEL) * stride1;
    110     org += (xo - KERNEL);
    111     rec += (yo - KERNEL) * stride2;
    112     rec += (xo - KERNEL);
    113 
    114     for (y_ = 0; y_ < KERNEL_SIZE; ++y_, org += stride1, rec += stride2)
    115     {
    116         const int Wy = K[y_];
    117 
    118         for (x_ = 0; x_ < KERNEL_SIZE; ++x_)
    119         {
    120             const int Wxy = Wy * K[x_];
    121             // TODO(skal): inlined assembly
    122             const int org_x = org[x_];
    123             const int rec_x = rec[x_];
    124             xm  += Wxy * org_x;
    125             ym  += Wxy * rec_x;
    126             xxm += Wxy * org_x * org_x;
    127             xym += Wxy * org_x * rec_x;
    128             yym += Wxy * rec_x * rec_x;
    129         }
    130     }
    131 
    132     {
    133         const double iw = ki_w;
    134         const double iwx = xm * iw;
    135         const double iwy = ym * iw;
    136         double sxx = xxm * iw - iwx * iwx;
    137         double syy = yym * iw - iwy * iwy;
    138 
    139         // small errors are possible, due to rounding. Clamp to zero.
    140         if (sxx < 0.) sxx = 0.;
    141 
    142         if (syy < 0.) syy = 0.;
    143 
    144         {
    145             const double sxsy = sqrt(sxx * syy);
    146             const double sxy = xym * iw - iwx * iwy;
    147             static const double C11 = (0.01 * 0.01) * (255 * 255);
    148             static const double C22 = (0.03 * 0.03) * (255 * 255);
    149             static const double C33 = (0.015 * 0.015) * (255 * 255);
    150             const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
    151             const double c = (2. * sxsy      + C22) / (sxx + syy + C22);
    152             const double s = (sxy + C33) / (sxsy + C33);
    153             return l * c * s;
    154         }
    155     }
    156 }
    157 
    158 double calc_ssimg(const uint8 *org, const uint8 *rec,
    159                   const int image_width, const int image_height,
    160                   const int stride1, const int stride2
    161                  )
    162 {
    163     int j, i;
    164     double SSIM = 0.;
    165 
    166     for (j = 0; j < KERNEL; ++j)
    167     {
    168         for (i = 0; i < image_width; ++i)
    169         {
    170             SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
    171         }
    172     }
    173 
    174     for (j = KERNEL; j < image_height - KERNEL; ++j)
    175     {
    176         for (i = 0; i < KERNEL; ++i)
    177         {
    178             SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
    179         }
    180 
    181         for (i = KERNEL; i < image_width - KERNEL; ++i)
    182         {
    183             SSIM += get_ssimfull_kernelg(org, rec, i, j,
    184                                          image_width, image_height, stride1, stride2);
    185         }
    186 
    187         for (i = image_width - KERNEL; i < image_width; ++i)
    188         {
    189             SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
    190         }
    191     }
    192 
    193     for (j = image_height - KERNEL; j < image_height; ++j)
    194     {
    195         for (i = 0; i < image_width; ++i)
    196         {
    197             SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
    198         }
    199     }
    200 
    201     return SSIM;
    202 }
    203 
    204 
    205 double vp8_calc_ssimg
    206 (
    207     YV12_BUFFER_CONFIG *source,
    208     YV12_BUFFER_CONFIG *dest,
    209     double *ssim_y,
    210     double *ssim_u,
    211     double *ssim_v
    212 )
    213 {
    214     double ssim_all = 0;
    215     int ysize  = source->y_width * source->y_height;
    216     int uvsize = ysize / 4;
    217 
    218     *ssim_y = calc_ssimg(source->y_buffer, dest->y_buffer,
    219                          source->y_width, source->y_height,
    220                          source->y_stride, dest->y_stride);
    221 
    222 
    223     *ssim_u = calc_ssimg(source->u_buffer, dest->u_buffer,
    224                          source->uv_width, source->uv_height,
    225                          source->uv_stride, dest->uv_stride);
    226 
    227 
    228     *ssim_v = calc_ssimg(source->v_buffer, dest->v_buffer,
    229                          source->uv_width, source->uv_height,
    230                          source->uv_stride, dest->uv_stride);
    231 
    232     ssim_all = (*ssim_y + *ssim_u + *ssim_v) / (ysize + uvsize + uvsize);
    233     *ssim_y /= ysize;
    234     *ssim_u /= uvsize;
    235     *ssim_v /= uvsize;
    236     return ssim_all;
    237 }
    238 
    239 
    240 void ssim_parms_c
    241 (
    242     unsigned char *s,
    243     int sp,
    244     unsigned char *r,
    245     int rp,
    246     unsigned long *sum_s,
    247     unsigned long *sum_r,
    248     unsigned long *sum_sq_s,
    249     unsigned long *sum_sq_r,
    250     unsigned long *sum_sxr
    251 )
    252 {
    253     int i,j;
    254     for(i=0;i<16;i++,s+=sp,r+=rp)
    255      {
    256          for(j=0;j<16;j++)
    257          {
    258              *sum_s += s[j];
    259              *sum_r += r[j];
    260              *sum_sq_s += s[j] * s[j];
    261              *sum_sq_r += r[j] * r[j];
    262              *sum_sxr += s[j] * r[j];
    263          }
    264      }
    265 }
    266 void ssim_parms_8x8_c
    267 (
    268     unsigned char *s,
    269     int sp,
    270     unsigned char *r,
    271     int rp,
    272     unsigned long *sum_s,
    273     unsigned long *sum_r,
    274     unsigned long *sum_sq_s,
    275     unsigned long *sum_sq_r,
    276     unsigned long *sum_sxr
    277 )
    278 {
    279     int i,j;
    280     for(i=0;i<8;i++,s+=sp,r+=rp)
    281      {
    282          for(j=0;j<8;j++)
    283          {
    284              *sum_s += s[j];
    285              *sum_r += r[j];
    286              *sum_sq_s += s[j] * s[j];
    287              *sum_sq_r += r[j] * r[j];
    288              *sum_sxr += s[j] * r[j];
    289          }
    290      }
    291 }
    292 
    293 const static long long c1 =  426148; // (256^2*(.01*255)^2
    294 const static long long c2 = 3835331; //(256^2*(.03*255)^2
    295 
    296 static double similarity
    297 (
    298     unsigned long sum_s,
    299     unsigned long sum_r,
    300     unsigned long sum_sq_s,
    301     unsigned long sum_sq_r,
    302     unsigned long sum_sxr,
    303     int count
    304 )
    305 {
    306     long long ssim_n = (2*sum_s*sum_r+ c1)*(2*count*sum_sxr-2*sum_s*sum_r+c2);
    307 
    308     long long ssim_d = (sum_s*sum_s +sum_r*sum_r+c1)*
    309             (count*sum_sq_s-sum_s*sum_s + count*sum_sq_r-sum_r*sum_r +c2) ;
    310 
    311     return ssim_n * 1.0 / ssim_d;
    312 }
    313 
    314 static double ssim_16x16(unsigned char *s,int sp, unsigned char *r,int rp,
    315             const vp8_variance_rtcd_vtable_t *rtcd)
    316 {
    317     unsigned long sum_s=0,sum_r=0,sum_sq_s=0,sum_sq_r=0,sum_sxr=0;
    318     rtcd->ssimpf(s, sp, r, rp, &sum_s, &sum_r, &sum_sq_s, &sum_sq_r, &sum_sxr);
    319     return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 256);
    320 }
    321 static double ssim_8x8(unsigned char *s,int sp, unsigned char *r,int rp,
    322                 const vp8_variance_rtcd_vtable_t *rtcd)
    323 {
    324     unsigned long sum_s=0,sum_r=0,sum_sq_s=0,sum_sq_r=0,sum_sxr=0;
    325     rtcd->ssimpf_8x8(s, sp, r, rp, &sum_s, &sum_r, &sum_sq_s, &sum_sq_r, &sum_sxr);
    326     return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64);
    327 }
    328 
    329 // TODO: (jbb) tried to scale this function such that we may be able to use it
    330 // for distortion metric in mode selection code ( provided we do a reconstruction)
    331 long dssim(unsigned char *s,int sp, unsigned char *r,int rp,
    332            const vp8_variance_rtcd_vtable_t *rtcd)
    333 {
    334     unsigned long sum_s=0,sum_r=0,sum_sq_s=0,sum_sq_r=0,sum_sxr=0;
    335     double ssim3;
    336     long long ssim_n;
    337     long long ssim_d;
    338 
    339     rtcd->ssimpf(s, sp, r, rp, &sum_s, &sum_r, &sum_sq_s, &sum_sq_r, &sum_sxr);
    340     ssim_n = (2*sum_s*sum_r+ c1)*(2*256*sum_sxr-2*sum_s*sum_r+c2);
    341 
    342     ssim_d = (sum_s*sum_s +sum_r*sum_r+c1)*
    343             (256*sum_sq_s-sum_s*sum_s + 256*sum_sq_r-sum_r*sum_r +c2) ;
    344 
    345     ssim3 = 256 * (ssim_d-ssim_n) / ssim_d;
    346     return (long)( 256*ssim3 * ssim3 );
    347 }
    348 // TODO: (jbb) this 8x8 window might be too big + we may want to pick pixels
    349 // such that the window regions overlap block boundaries to penalize blocking
    350 // artifacts.
    351 
    352 double vp8_ssim2
    353 (
    354     unsigned char *img1,
    355     unsigned char *img2,
    356     int stride_img1,
    357     int stride_img2,
    358     int width,
    359     int height,
    360     const vp8_variance_rtcd_vtable_t *rtcd
    361 )
    362 {
    363     int i,j;
    364 
    365     double ssim_total=0;
    366 
    367     // we can sample points as frequently as we like start with 1 per 8x8
    368     for(i=0; i < height; i+=8, img1 += stride_img1*8, img2 += stride_img2*8)
    369     {
    370         for(j=0; j < width; j+=8 )
    371         {
    372             ssim_total += ssim_8x8(img1, stride_img1, img2, stride_img2, rtcd);
    373         }
    374     }
    375     ssim_total /= (width/8 * height /8);
    376     return ssim_total;
    377 
    378 }
    379 double vp8_calc_ssim
    380 (
    381     YV12_BUFFER_CONFIG *source,
    382     YV12_BUFFER_CONFIG *dest,
    383     int lumamask,
    384     double *weight,
    385     const vp8_variance_rtcd_vtable_t *rtcd
    386 )
    387 {
    388     double a, b, c;
    389     double ssimv;
    390 
    391     a = vp8_ssim2(source->y_buffer, dest->y_buffer,
    392                  source->y_stride, dest->y_stride, source->y_width,
    393                  source->y_height, rtcd);
    394 
    395     b = vp8_ssim2(source->u_buffer, dest->u_buffer,
    396                  source->uv_stride, dest->uv_stride, source->uv_width,
    397                  source->uv_height, rtcd);
    398 
    399     c = vp8_ssim2(source->v_buffer, dest->v_buffer,
    400                  source->uv_stride, dest->uv_stride, source->uv_width,
    401                  source->uv_height, rtcd);
    402 
    403     ssimv = a * .8 + .1 * (b + c);
    404 
    405     *weight = 1;
    406 
    407     return ssimv;
    408 }
    409