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