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 |