Commit 6c565fad authored by Yaowu Xu's avatar Yaowu Xu
Browse files

change to use fast ssim code for internal ssim calculations

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
parent aeca5990
......@@ -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;
......
......@@ -18,223 +18,6 @@
#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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment