| OLD | NEW |
| 1 // Copyright 2011 Google Inc. | 1 // Copyright 2011 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // This code is licensed under the same terms as WebM: | 3 // This code is licensed under the same terms as WebM: |
| 4 // Software License Agreement: http://www.webmproject.org/license/software/ | 4 // Software License Agreement: http://www.webmproject.org/license/software/ |
| 5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ | 5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ |
| 6 // ----------------------------------------------------------------------------- | 6 // ----------------------------------------------------------------------------- |
| 7 // | 7 // |
| 8 // Selecting filter level | 8 // Selecting filter level |
| 9 // | 9 // |
| 10 // Author: somnath@google.com (Somnath Banerjee) | 10 // Author: somnath@google.com (Somnath Banerjee) |
| 11 | 11 |
| 12 #include <math.h> | 12 #include "./vp8enci.h" |
| 13 #include "vp8enci.h" | |
| 14 | 13 |
| 15 #if defined(__cplusplus) || defined(c_plusplus) | 14 #if defined(__cplusplus) || defined(c_plusplus) |
| 16 extern "C" { | 15 extern "C" { |
| 17 #endif | 16 #endif |
| 18 | 17 |
| 19 // NOTE: clip1, tables and InitTables are repeated entries of dsp.c | 18 // NOTE: clip1, tables and InitTables are repeated entries of dsp.c |
| 20 static uint8_t abs0[255 + 255 + 1]; // abs(i) | 19 static uint8_t abs0[255 + 255 + 1]; // abs(i) |
| 21 static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1 | 20 static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1 |
| 22 static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127] | 21 static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127] |
| 23 static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15] | 22 static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15] |
| (...skipping 18 matching lines...) Expand all Loading... |
| 42 clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i; | 41 clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i; |
| 43 } | 42 } |
| 44 tables_ok = 1; | 43 tables_ok = 1; |
| 45 } | 44 } |
| 46 } | 45 } |
| 47 | 46 |
| 48 //------------------------------------------------------------------------------ | 47 //------------------------------------------------------------------------------ |
| 49 // Edge filtering functions | 48 // Edge filtering functions |
| 50 | 49 |
| 51 // 4 pixels in, 2 pixels out | 50 // 4 pixels in, 2 pixels out |
| 52 static inline void do_filter2(uint8_t* p, int step) { | 51 static WEBP_INLINE void do_filter2(uint8_t* p, int step) { |
| 53 const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; | 52 const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; |
| 54 const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1]; | 53 const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1]; |
| 55 const int a1 = sclip2[112 + ((a + 4) >> 3)]; | 54 const int a1 = sclip2[112 + ((a + 4) >> 3)]; |
| 56 const int a2 = sclip2[112 + ((a + 3) >> 3)]; | 55 const int a2 = sclip2[112 + ((a + 3) >> 3)]; |
| 57 p[-step] = clip1[255 + p0 + a2]; | 56 p[-step] = clip1[255 + p0 + a2]; |
| 58 p[ 0] = clip1[255 + q0 - a1]; | 57 p[ 0] = clip1[255 + q0 - a1]; |
| 59 } | 58 } |
| 60 | 59 |
| 61 // 4 pixels in, 4 pixels out | 60 // 4 pixels in, 4 pixels out |
| 62 static inline void do_filter4(uint8_t* p, int step) { | 61 static WEBP_INLINE void do_filter4(uint8_t* p, int step) { |
| 63 const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; | 62 const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; |
| 64 const int a = 3 * (q0 - p0); | 63 const int a = 3 * (q0 - p0); |
| 65 const int a1 = sclip2[112 + ((a + 4) >> 3)]; | 64 const int a1 = sclip2[112 + ((a + 4) >> 3)]; |
| 66 const int a2 = sclip2[112 + ((a + 3) >> 3)]; | 65 const int a2 = sclip2[112 + ((a + 3) >> 3)]; |
| 67 const int a3 = (a1 + 1) >> 1; | 66 const int a3 = (a1 + 1) >> 1; |
| 68 p[-2*step] = clip1[255 + p1 + a3]; | 67 p[-2*step] = clip1[255 + p1 + a3]; |
| 69 p[- step] = clip1[255 + p0 + a2]; | 68 p[- step] = clip1[255 + p0 + a2]; |
| 70 p[ 0] = clip1[255 + q0 - a1]; | 69 p[ 0] = clip1[255 + q0 - a1]; |
| 71 p[ step] = clip1[255 + q1 - a3]; | 70 p[ step] = clip1[255 + q1 - a3]; |
| 72 } | 71 } |
| 73 | 72 |
| 74 // high edge-variance | 73 // high edge-variance |
| 75 static inline int hev(const uint8_t* p, int step, int thresh) { | 74 static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) { |
| 76 const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; | 75 const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; |
| 77 return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh); | 76 return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh); |
| 78 } | 77 } |
| 79 | 78 |
| 80 static inline int needs_filter(const uint8_t* p, int step, int thresh) { | 79 static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) { |
| 81 const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; | 80 const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; |
| 82 return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh; | 81 return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh; |
| 83 } | 82 } |
| 84 | 83 |
| 85 static inline int needs_filter2(const uint8_t* p, int step, int t, int it) { | 84 static WEBP_INLINE int needs_filter2(const uint8_t* p, |
| 85 int step, int t, int it) { |
| 86 const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step]; | 86 const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step]; |
| 87 const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step]; | 87 const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step]; |
| 88 if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t) | 88 if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t) |
| 89 return 0; | 89 return 0; |
| 90 return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it && | 90 return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it && |
| 91 abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it && | 91 abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it && |
| 92 abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it; | 92 abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it; |
| 93 } | 93 } |
| 94 | 94 |
| 95 //------------------------------------------------------------------------------ | 95 //------------------------------------------------------------------------------ |
| (...skipping 29 matching lines...) Expand all Loading... |
| 125 int k; | 125 int k; |
| 126 for (k = 3; k > 0; --k) { | 126 for (k = 3; k > 0; --k) { |
| 127 p += 4; | 127 p += 4; |
| 128 SimpleHFilter16(p, stride, thresh); | 128 SimpleHFilter16(p, stride, thresh); |
| 129 } | 129 } |
| 130 } | 130 } |
| 131 | 131 |
| 132 //------------------------------------------------------------------------------ | 132 //------------------------------------------------------------------------------ |
| 133 // Complex In-loop filtering (Paragraph 15.3) | 133 // Complex In-loop filtering (Paragraph 15.3) |
| 134 | 134 |
| 135 static inline void FilterLoop24(uint8_t* p, int hstride, int vstride, int size, | 135 static WEBP_INLINE void FilterLoop24(uint8_t* p, |
| 136 int thresh, int ithresh, int hev_thresh) { | 136 int hstride, int vstride, int size, |
| 137 int thresh, int ithresh, int hev_thresh) { |
| 137 while (size-- > 0) { | 138 while (size-- > 0) { |
| 138 if (needs_filter2(p, hstride, thresh, ithresh)) { | 139 if (needs_filter2(p, hstride, thresh, ithresh)) { |
| 139 if (hev(p, hstride, hev_thresh)) { | 140 if (hev(p, hstride, hev_thresh)) { |
| 140 do_filter2(p, hstride); | 141 do_filter2(p, hstride); |
| 141 } else { | 142 } else { |
| 142 do_filter4(p, hstride); | 143 do_filter4(p, hstride); |
| 143 } | 144 } |
| 144 } | 145 } |
| 145 p += vstride; | 146 p += vstride; |
| 146 } | 147 } |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 226 VP8EncHFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh); | 227 VP8EncHFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh); |
| 227 VP8EncVFilter16i(y_dst, BPS, limit, ilevel, hev_thresh); | 228 VP8EncVFilter16i(y_dst, BPS, limit, ilevel, hev_thresh); |
| 228 VP8EncVFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh); | 229 VP8EncVFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh); |
| 229 } | 230 } |
| 230 } | 231 } |
| 231 | 232 |
| 232 //------------------------------------------------------------------------------ | 233 //------------------------------------------------------------------------------ |
| 233 // SSIM metric | 234 // SSIM metric |
| 234 | 235 |
| 235 enum { KERNEL = 3 }; | 236 enum { KERNEL = 3 }; |
| 236 typedef struct { | 237 static const double kMinValue = 1.e-10; // minimal threshold |
| 237 double w, xm, ym, xxm, xym, yym; | |
| 238 } SSIMStats; | |
| 239 | 238 |
| 240 static void Accumulate(const uint8_t* src1, int stride1, | 239 void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst) { |
| 241 const uint8_t* src2, int stride2, | 240 dst->w += src->w; |
| 242 int xo, int yo, int W, int H, | 241 dst->xm += src->xm; |
| 243 SSIMStats* const stats) { | 242 dst->ym += src->ym; |
| 243 dst->xxm += src->xxm; |
| 244 dst->xym += src->xym; |
| 245 dst->yym += src->yym; |
| 246 } |
| 247 |
| 248 static void VP8SSIMAccumulate(const uint8_t* src1, int stride1, |
| 249 const uint8_t* src2, int stride2, |
| 250 int xo, int yo, int W, int H, |
| 251 DistoStats* const stats) { |
| 244 const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL; | 252 const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL; |
| 245 const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL; | 253 const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL; |
| 246 const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL; | 254 const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL; |
| 247 const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL; | 255 const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL; |
| 248 int x, y; | 256 int x, y; |
| 249 src1 += ymin * stride1; | 257 src1 += ymin * stride1; |
| 250 src2 += ymin * stride2; | 258 src2 += ymin * stride2; |
| 251 for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) { | 259 for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) { |
| 252 for (x = xmin; x <= xmax; ++x) { | 260 for (x = xmin; x <= xmax; ++x) { |
| 253 const int s1 = src1[x]; | 261 const int s1 = src1[x]; |
| 254 const int s2 = src2[x]; | 262 const int s2 = src2[x]; |
| 255 stats->w += 1; | 263 stats->w += 1; |
| 256 stats->xm += s1; | 264 stats->xm += s1; |
| 257 stats->ym += s2; | 265 stats->ym += s2; |
| 258 stats->xxm += s1 * s1; | 266 stats->xxm += s1 * s1; |
| 259 stats->xym += s1 * s2; | 267 stats->xym += s1 * s2; |
| 260 stats->yym += s2 * s2; | 268 stats->yym += s2 * s2; |
| 261 } | 269 } |
| 262 } | 270 } |
| 263 } | 271 } |
| 264 | 272 |
| 265 static double GetSSIM(const SSIMStats* const stats) { | 273 double VP8SSIMGet(const DistoStats* const stats) { |
| 266 const double xmxm = stats->xm * stats->xm; | 274 const double xmxm = stats->xm * stats->xm; |
| 267 const double ymym = stats->ym * stats->ym; | 275 const double ymym = stats->ym * stats->ym; |
| 268 const double xmym = stats->xm * stats->ym; | 276 const double xmym = stats->xm * stats->ym; |
| 269 const double w2 = stats->w * stats->w; | 277 const double w2 = stats->w * stats->w; |
| 270 double sxx = stats->xxm * stats->w - xmxm; | 278 double sxx = stats->xxm * stats->w - xmxm; |
| 271 double syy = stats->yym * stats->w - ymym; | 279 double syy = stats->yym * stats->w - ymym; |
| 272 double sxy = stats->xym * stats->w - xmym; | 280 double sxy = stats->xym * stats->w - xmym; |
| 273 double C1, C2; | 281 double C1, C2; |
| 274 double fnum; | 282 double fnum; |
| 275 double fden; | 283 double fden; |
| 276 // small errors are possible, due to rounding. Clamp to zero. | 284 // small errors are possible, due to rounding. Clamp to zero. |
| 277 if (sxx < 0.) sxx = 0.; | 285 if (sxx < 0.) sxx = 0.; |
| 278 if (syy < 0.) syy = 0.; | 286 if (syy < 0.) syy = 0.; |
| 279 C1 = 6.5025 * w2; | 287 C1 = 6.5025 * w2; |
| 280 C2 = 58.5225 * w2; | 288 C2 = 58.5225 * w2; |
| 281 fnum = (2 * xmym + C1) * (2 * sxy + C2); | 289 fnum = (2 * xmym + C1) * (2 * sxy + C2); |
| 282 fden = (xmxm + ymym + C1) * (sxx + syy + C2); | 290 fden = (xmxm + ymym + C1) * (sxx + syy + C2); |
| 283 return (fden != 0) ? fnum / fden : 0.; | 291 return (fden != 0.) ? fnum / fden : kMinValue; |
| 292 } |
| 293 |
| 294 double VP8SSIMGetSquaredError(const DistoStats* const s) { |
| 295 if (s->w > 0.) { |
| 296 const double iw2 = 1. / (s->w * s->w); |
| 297 const double sxx = s->xxm * s->w - s->xm * s->xm; |
| 298 const double syy = s->yym * s->w - s->ym * s->ym; |
| 299 const double sxy = s->xym * s->w - s->xm * s->ym; |
| 300 const double SSE = iw2 * (sxx + syy - 2. * sxy); |
| 301 if (SSE > kMinValue) return SSE; |
| 302 } |
| 303 return kMinValue; |
| 304 } |
| 305 |
| 306 void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1, |
| 307 const uint8_t* src2, int stride2, |
| 308 int W, int H, DistoStats* const stats) { |
| 309 int x, y; |
| 310 for (y = 0; y < H; ++y) { |
| 311 for (x = 0; x < W; ++x) { |
| 312 VP8SSIMAccumulate(src1, stride1, src2, stride2, x, y, W, H, stats); |
| 313 } |
| 314 } |
| 284 } | 315 } |
| 285 | 316 |
| 286 static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { | 317 static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { |
| 287 int x, y; | 318 int x, y; |
| 288 SSIMStats s = { .0, .0, .0, .0, .0, .0 }; | 319 DistoStats s = { .0, .0, .0, .0, .0, .0 }; |
| 289 | 320 |
| 290 // compute SSIM in a 10 x 10 window | 321 // compute SSIM in a 10 x 10 window |
| 291 for (x = 3; x < 13; x++) { | 322 for (x = 3; x < 13; x++) { |
| 292 for (y = 3; y < 13; y++) { | 323 for (y = 3; y < 13; y++) { |
| 293 Accumulate(yuv1 + Y_OFF, BPS, yuv2 + Y_OFF, BPS, x, y, 16, 16, &s); | 324 VP8SSIMAccumulate(yuv1 + Y_OFF, BPS, yuv2 + Y_OFF, BPS, x, y, 16, 16, &s); |
| 294 } | 325 } |
| 295 } | 326 } |
| 296 for (x = 1; x < 7; x++) { | 327 for (x = 1; x < 7; x++) { |
| 297 for (y = 1; y < 7; y++) { | 328 for (y = 1; y < 7; y++) { |
| 298 Accumulate(yuv1 + U_OFF, BPS, yuv2 + U_OFF, BPS, x, y, 8, 8, &s); | 329 VP8SSIMAccumulate(yuv1 + U_OFF, BPS, yuv2 + U_OFF, BPS, x, y, 8, 8, &s); |
| 299 Accumulate(yuv1 + V_OFF, BPS, yuv2 + V_OFF, BPS, x, y, 8, 8, &s); | 330 VP8SSIMAccumulate(yuv1 + V_OFF, BPS, yuv2 + V_OFF, BPS, x, y, 8, 8, &s); |
| 300 } | 331 } |
| 301 } | 332 } |
| 302 return GetSSIM(&s); | 333 return VP8SSIMGet(&s); |
| 303 } | 334 } |
| 304 | 335 |
| 305 //------------------------------------------------------------------------------ | 336 //------------------------------------------------------------------------------ |
| 306 // Exposed APIs: Encoder should call the following 3 functions to adjust | 337 // Exposed APIs: Encoder should call the following 3 functions to adjust |
| 307 // loop filter strength | 338 // loop filter strength |
| 308 | 339 |
| 309 void VP8InitFilter(VP8EncIterator* const it) { | 340 void VP8InitFilter(VP8EncIterator* const it) { |
| 310 int s, i; | 341 int s, i; |
| 311 if (!it->lf_stats_) return; | 342 if (!it->lf_stats_) return; |
| 312 | 343 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 369 best_level = i; | 400 best_level = i; |
| 370 } | 401 } |
| 371 } | 402 } |
| 372 enc->dqm_[s].fstrength_ = best_level; | 403 enc->dqm_[s].fstrength_ = best_level; |
| 373 } | 404 } |
| 374 } | 405 } |
| 375 | 406 |
| 376 #if defined(__cplusplus) || defined(c_plusplus) | 407 #if defined(__cplusplus) || defined(c_plusplus) |
| 377 } // extern "C" | 408 } // extern "C" |
| 378 #endif | 409 #endif |
| OLD | NEW |