From 6c565fada0820c9c2be6e931237b858cf8bbb5d1 Mon Sep 17 00:00:00 2001 From: Yaowu Xu Date: Mon, 2 May 2011 15:27:14 -0700 Subject: [PATCH 1/2] 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 --- vp8/encoder/onyx_if.c | 6 +- vp8/encoder/ssim.c | 258 ++++++------------------------------------ 2 files changed, 40 insertions(+), 224 deletions(-) diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c index 7461edca4..70b76f1cb 100644 --- a/vp8/encoder/onyx_if.c +++ b/vp8/encoder/onyx_if.c @@ -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; diff --git a/vp8/encoder/ssim.c b/vp8/encoder/ssim.c index c78be37a3..8646b5fdb 100644 --- a/vp8/encoder/ssim.c +++ b/vp8/encoder/ssim.c @@ -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 From 04ec930abc97892eba8f0914fad0016b1d13b733 Mon Sep 17 00:00:00 2001 From: Yunqing Wang Date: Mon, 2 May 2011 13:21:59 -0400 Subject: [PATCH 2/2] Modify HEX search Changed 8-neighbor searching to 4-neighour searching, and continued searching until the center point is the best match. Test on test set showed 1.3% encoding speed improvement as well as 0.1% PSNR and SSIM improvement at speed=-5 (rt mode). Will continue to improve it. Change-Id: If4993b1907dd742b906fd3f86fee77cc5932ee9a --- vp8/encoder/mcomp.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/vp8/encoder/mcomp.c b/vp8/encoder/mcomp.c index 651890d81..9d447b210 100644 --- a/vp8/encoder/mcomp.c +++ b/vp8/encoder/mcomp.c @@ -831,7 +831,9 @@ int vp8_hex_search ) { MV hex[6] = { { -1, -2}, {1, -2}, {2, 0}, {1, 2}, { -1, 2}, { -2, 0} } ; - MV neighbors[8] = { { -1, -1}, {0, -1}, {1, -1}, { -1, 0}, {1, 0}, { -1, 1}, {0, 1}, {1, 1} } ; + //MV neighbors[8] = { { -1, -1}, {0, -1}, {1, -1}, { -1, 0}, {1, 0}, { -1, 1}, {0, 1}, {1, 1} } ; + MV neighbors[4] = {{0, -1}, { -1, 0}, {1, 0}, {0, 1}} ; + int i, j; unsigned char *src = (*(b->base_src) + b->src); int src_stride = b->src_stride; @@ -918,24 +920,31 @@ int vp8_hex_search break; } - // check 8 1 away neighbors + // check 4 1-away neighbors cal_neighbors: - tr = br; - tc = bc; - for (i = 0; i < 8; i++) + for (j = 0; j < 32; j++) { - int nr = tr + neighbors[i].row, nc = tc + neighbors[i].col; + tr = br; + tc = bc; - if (nc < x->mv_col_min) continue; + for (i = 0; i < 4; i++) + { + int nr = tr + neighbors[i].row, nc = tc + neighbors[i].col; - if (nc > x->mv_col_max) continue; + if (nc < x->mv_col_min) continue; - if (nr < x->mv_row_min) continue; + if (nc > x->mv_col_max) continue; - if (nr > x->mv_row_max) continue; + if (nr < x->mv_row_min) continue; - CHECK_BETTER(thiserr, nr, nc); + if (nr > x->mv_row_max) continue; + + CHECK_BETTER(thiserr, nr, nc); + } + + if (tr == br && tc == bc) + break; } best_mv->row = br;