• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
vp8_similarity(int mu_x,int mu_y,int pre_mu_x2,int pre_mu_y2,int pre_mu_xy2)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 
vp8_ssim(const unsigned char * img1,const unsigned char * img2,int stride_img1,int stride_img2,int width,int height)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 
vp8_calc_ssim(YV12_BUFFER_CONFIG * source,YV12_BUFFER_CONFIG * dest,int lumamask,double * weight)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]
get_ssimg(const uint8 * org,const uint8 * rec,int xo,int yo,int W,int H,const int stride1,const int stride2)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 
get_ssimfull_kernelg(const uint8 * org,const uint8 * rec,int xo,int yo,int W,int H,const int stride1,const int stride2)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 
calc_ssimg(const uint8 * org,const uint8 * rec,const int image_width,const int image_height,const int stride1,const int stride2)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 
vp8_calc_ssimg(YV12_BUFFER_CONFIG * source,YV12_BUFFER_CONFIG * dest,double * ssim_y,double * ssim_u,double * ssim_v)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