]> granicus.if.org Git - libvpx/commitdiff
change to use fast ssim code for internal ssim calculations
authorYaowu Xu <yaowu@google.com>
Mon, 2 May 2011 22:27:14 +0000 (15:27 -0700)
committerYaowu Xu <yaowu@google.com>
Tue, 3 May 2011 15:36:17 +0000 (08:36 -0700)
The commit also removed the slow ssim calculation that uses a 7x7
kernel, and revised the comments to better describe how sample ssim
values are computed and averaged

Change-Id: I1d874073cddca00f3c997f4b9a9a3db0aa212276

vp8/encoder/onyx_if.c
vp8/encoder/ssim.c

index 7461edca4f558c891d9abab408ee6bde3dad9526..70b76f1cb7ffeebb80009ba5b8b3da6571d8a4cd 100644 (file)
@@ -96,7 +96,8 @@ extern double vp8_calc_ssimg
     YV12_BUFFER_CONFIG *dest,
     double *ssim_y,
     double *ssim_u,
-    double *ssim_v
+    double *ssim_v,
+    const vp8_variance_rtcd_vtable_t *rtcd
 );
 
 
@@ -4984,7 +4985,8 @@ int vp8_get_compressed_data(VP8_PTR ptr, unsigned int *frame_flags, unsigned lon
             if (cpi->b_calculate_ssimg)
             {
                 double y, u, v, frame_all;
-                frame_all =  vp8_calc_ssimg(cpi->Source, cm->frame_to_show, &y, &u, &v);
+                frame_all =  vp8_calc_ssimg(cpi->Source, cm->frame_to_show,
+                    &y, &u, &v, IF_RTCD(&cpi->rtcd.variance));
                 cpi->total_ssimg_y += y;
                 cpi->total_ssimg_u += u;
                 cpi->total_ssimg_v += v;
index c78be37a3d4e7e78aa6c638881773b97f0982b15..8646b5fdb0398c885462e90b67280a419d4d6cc1 100644 (file)
 #else
 #define IF_RTCD(x)  NULL
 #endif
-// Google version of SSIM
-// SSIM
-#define KERNEL 3
-#define KERNEL_SIZE  (2 * KERNEL + 1)
-
-typedef unsigned char uint8;
-typedef unsigned int uint32;
-
-static const int K[KERNEL_SIZE] =
-{
-    1, 4, 11, 16, 11, 4, 1    // 16 * exp(-0.3 * i * i)
-};
-static const double ki_w = 1. / 2304.;  // 1 / sum(i:0..6, j..6) K[i]*K[j]
-double get_ssimg(const uint8 *org, const uint8 *rec,
-                 int xo, int yo, int W, int H,
-                 const int stride1, const int stride2
-                )
-{
-    // TODO(skal): use summed tables
-    int y, x;
-
-    const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL;
-    const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL;
-    const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL;
-    const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL;
-    // worst case of accumulation is a weight of 48 = 16 + 2 * (11 + 4 + 1)
-    // with a diff of 255, squares. That would a max error of 0x8ee0900,
-    // which fits into 32 bits integers.
-    uint32 w = 0, xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
-    org += ymin * stride1;
-    rec += ymin * stride2;
-
-    for (y = ymin; y <= ymax; ++y, org += stride1, rec += stride2)
-    {
-        const int Wy = K[KERNEL + y - yo];
-
-        for (x = xmin; x <= xmax; ++x)
-        {
-            const  int Wxy = Wy * K[KERNEL + x - xo];
-            // TODO(skal): inlined assembly
-            w   += Wxy;
-            xm  += Wxy * org[x];
-            ym  += Wxy * rec[x];
-            xxm += Wxy * org[x] * org[x];
-            xym += Wxy * org[x] * rec[x];
-            yym += Wxy * rec[x] * rec[x];
-        }
-    }
-
-    {
-        const double iw = 1. / w;
-        const double iwx = xm * iw;
-        const double iwy = ym * iw;
-        double sxx = xxm * iw - iwx * iwx;
-        double syy = yym * iw - iwy * iwy;
-
-        // small errors are possible, due to rounding. Clamp to zero.
-        if (sxx < 0.) sxx = 0.;
-
-        if (syy < 0.) syy = 0.;
-
-        {
-            const double sxsy = sqrt(sxx * syy);
-            const double sxy = xym * iw - iwx * iwy;
-            static const double C11 = (0.01 * 0.01) * (255 * 255);
-            static const double C22 = (0.03 * 0.03) * (255 * 255);
-            static const double C33 = (0.015 * 0.015) * (255 * 255);
-            const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
-            const double c = (2. * sxsy      + C22) / (sxx + syy + C22);
-
-            const double s = (sxy + C33) / (sxsy + C33);
-            return l * c * s;
-
-        }
-    }
-
-}
-
-double get_ssimfull_kernelg(const uint8 *org, const uint8 *rec,
-                            int xo, int yo, int W, int H,
-                            const int stride1, const int stride2)
-{
-    // TODO(skal): use summed tables
-    // worst case of accumulation is a weight of 48 = 16 + 2 * (11 + 4 + 1)
-    // with a diff of 255, squares. That would a max error of 0x8ee0900,
-    // which fits into 32 bits integers.
-    int y_, x_;
-    uint32 xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
-    org += (yo - KERNEL) * stride1;
-    org += (xo - KERNEL);
-    rec += (yo - KERNEL) * stride2;
-    rec += (xo - KERNEL);
-
-    for (y_ = 0; y_ < KERNEL_SIZE; ++y_, org += stride1, rec += stride2)
-    {
-        const int Wy = K[y_];
-
-        for (x_ = 0; x_ < KERNEL_SIZE; ++x_)
-        {
-            const int Wxy = Wy * K[x_];
-            // TODO(skal): inlined assembly
-            const int org_x = org[x_];
-            const int rec_x = rec[x_];
-            xm  += Wxy * org_x;
-            ym  += Wxy * rec_x;
-            xxm += Wxy * org_x * org_x;
-            xym += Wxy * org_x * rec_x;
-            yym += Wxy * rec_x * rec_x;
-        }
-    }
-
-    {
-        const double iw = ki_w;
-        const double iwx = xm * iw;
-        const double iwy = ym * iw;
-        double sxx = xxm * iw - iwx * iwx;
-        double syy = yym * iw - iwy * iwy;
-
-        // small errors are possible, due to rounding. Clamp to zero.
-        if (sxx < 0.) sxx = 0.;
-
-        if (syy < 0.) syy = 0.;
-
-        {
-            const double sxsy = sqrt(sxx * syy);
-            const double sxy = xym * iw - iwx * iwy;
-            static const double C11 = (0.01 * 0.01) * (255 * 255);
-            static const double C22 = (0.03 * 0.03) * (255 * 255);
-            static const double C33 = (0.015 * 0.015) * (255 * 255);
-            const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
-            const double c = (2. * sxsy      + C22) / (sxx + syy + C22);
-            const double s = (sxy + C33) / (sxsy + C33);
-            return l * c * s;
-        }
-    }
-}
-
-double calc_ssimg(const uint8 *org, const uint8 *rec,
-                  const int image_width, const int image_height,
-                  const int stride1, const int stride2
-                 )
-{
-    int j, i;
-    double SSIM = 0.;
-
-    for (j = 0; j < KERNEL; ++j)
-    {
-        for (i = 0; i < image_width; ++i)
-        {
-            SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
-        }
-    }
-
-    for (j = KERNEL; j < image_height - KERNEL; ++j)
-    {
-        for (i = 0; i < KERNEL; ++i)
-        {
-            SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
-        }
-
-        for (i = KERNEL; i < image_width - KERNEL; ++i)
-        {
-            SSIM += get_ssimfull_kernelg(org, rec, i, j,
-                                         image_width, image_height, stride1, stride2);
-        }
-
-        for (i = image_width - KERNEL; i < image_width; ++i)
-        {
-            SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
-        }
-    }
-
-    for (j = image_height - KERNEL; j < image_height; ++j)
-    {
-        for (i = 0; i < image_width; ++i)
-        {
-            SSIM += get_ssimg(org, rec, i, j, image_width, image_height, stride1, stride2);
-        }
-    }
-
-    return SSIM;
-}
-
-
-double vp8_calc_ssimg
-(
-    YV12_BUFFER_CONFIG *source,
-    YV12_BUFFER_CONFIG *dest,
-    double *ssim_y,
-    double *ssim_u,
-    double *ssim_v
-)
-{
-    double ssim_all = 0;
-    int ysize  = source->y_width * source->y_height;
-    int uvsize = ysize / 4;
-
-    *ssim_y = calc_ssimg(source->y_buffer, dest->y_buffer,
-                         source->y_width, source->y_height,
-                         source->y_stride, dest->y_stride);
-
-
-    *ssim_u = calc_ssimg(source->u_buffer, dest->u_buffer,
-                         source->uv_width, source->uv_height,
-                         source->uv_stride, dest->uv_stride);
-
-
-    *ssim_v = calc_ssimg(source->v_buffer, dest->v_buffer,
-                         source->uv_width, source->uv_height,
-                         source->uv_stride, dest->uv_stride);
-
-    ssim_all = (*ssim_y + *ssim_u + *ssim_v) / (ysize + uvsize + uvsize);
-    *ssim_y /= ysize;
-    *ssim_u /= uvsize;
-    *ssim_v /= uvsize;
-    return ssim_all;
-}
 
 
 void ssim_parms_c
@@ -369,10 +152,10 @@ long dssim(unsigned char *s,int sp, unsigned char *r,int rp,
         ssim3=0;
     return (long)( ssim3  );
 }
-// TODO: (jbb) this 8x8 window might be too big + we may want to pick pixels
-// such that the window regions overlap block boundaries to penalize blocking
-// artifacts.
 
+// We are using a 8x8 moving window with starting location of each 8x8 window
+// on the 4x4 pixel grid. Such arrangement allows the windows to overlap
+// block boundaries to penalize blocking artifacts.
 double vp8_ssim2
 (
     unsigned char *img1,
@@ -388,7 +171,7 @@ double vp8_ssim2
     int samples =0;
     double ssim_total=0;
 
-    // we can sample points as frequently as we like start with 1 per 4x4
+    // sample point start with each 4x4 location
     for(i=0; i < height-8; i+=4, img1 += stride_img1*4, img2 += stride_img2*4)
     {
         for(j=0; j < width-8; j+=4 )
@@ -400,7 +183,6 @@ double vp8_ssim2
     }
     ssim_total /= samples;
     return ssim_total;
-
 }
 double vp8_calc_ssim
 (
@@ -431,4 +213,36 @@ double vp8_calc_ssim
     *weight = 1;
 
     return ssimv;
+}
+
+double vp8_calc_ssimg
+(
+    YV12_BUFFER_CONFIG *source,
+    YV12_BUFFER_CONFIG *dest,
+    double *ssim_y,
+    double *ssim_u,
+    double *ssim_v,
+    const vp8_variance_rtcd_vtable_t *rtcd
+)
+{
+    double ssim_all = 0;
+    double a, b, c;
+
+    a = vp8_ssim2(source->y_buffer, dest->y_buffer,
+                 source->y_stride, dest->y_stride, source->y_width,
+                 source->y_height, rtcd);
+
+    b = vp8_ssim2(source->u_buffer, dest->u_buffer,
+                 source->uv_stride, dest->uv_stride, source->uv_width,
+                 source->uv_height, rtcd);
+
+    c = vp8_ssim2(source->v_buffer, dest->v_buffer,
+                 source->uv_stride, dest->uv_stride, source->uv_width,
+                 source->uv_height, rtcd);
+    *ssim_y = a;
+    *ssim_u = b;
+    *ssim_v = c;
+    ssim_all = (a * 4 + b + c) /6;
+
+    return ssim_all;
 }
\ No newline at end of file