• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 2 Clause License and
5  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6  * was not distributed with this source code in the LICENSE file, you can
7  * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8  * Media Patent License 1.0 was not distributed with this source code in the
9  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10  */
11 
12 #include <assert.h>
13 #include <math.h>
14 
15 #include "config/aom_dsp_rtcd.h"
16 
17 #include "aom_dsp/psnr.h"
18 #include "aom_scale/yv12config.h"
19 
aom_sse_to_psnr(double samples,double peak,double sse)20 double aom_sse_to_psnr(double samples, double peak, double sse) {
21   if (sse > 0.0) {
22     const double psnr = 10.0 * log10(samples * peak * peak / sse);
23     return psnr > MAX_PSNR ? MAX_PSNR : psnr;
24   } else {
25     return MAX_PSNR;
26   }
27 }
28 
encoder_sse(const uint8_t * a,int a_stride,const uint8_t * b,int b_stride,int w,int h)29 static int64_t encoder_sse(const uint8_t *a, int a_stride, const uint8_t *b,
30                            int b_stride, int w, int h) {
31   int i, j;
32   int64_t sse = 0;
33 
34   for (i = 0; i < h; i++) {
35     for (j = 0; j < w; j++) {
36       const int diff = a[j] - b[j];
37       sse += diff * diff;
38     }
39 
40     a += a_stride;
41     b += b_stride;
42   }
43   return sse;
44 }
45 
46 #if CONFIG_AV1_HIGHBITDEPTH
encoder_highbd_8_sse(const uint8_t * a8,int a_stride,const uint8_t * b8,int b_stride,int w,int h)47 static int64_t encoder_highbd_8_sse(const uint8_t *a8, int a_stride,
48                                     const uint8_t *b8, int b_stride, int w,
49                                     int h) {
50   const uint16_t *a = CONVERT_TO_SHORTPTR(a8);
51   const uint16_t *b = CONVERT_TO_SHORTPTR(b8);
52   int64_t sse = 0;
53   for (int i = 0; i < h; ++i) {
54     for (int j = 0; j < w; ++j) {
55       const int diff = a[j] - b[j];
56       sse += diff * diff;
57     }
58     a += a_stride;
59     b += b_stride;
60   }
61   return sse;
62 }
63 
64 #endif  // CONFIG_AV1_HIGHBITDEPTH
65 
get_sse(const uint8_t * a,int a_stride,const uint8_t * b,int b_stride,int width,int height)66 static int64_t get_sse(const uint8_t *a, int a_stride, const uint8_t *b,
67                        int b_stride, int width, int height) {
68   const int dw = width % 16;
69   const int dh = height % 16;
70   int64_t total_sse = 0;
71   int x, y;
72 
73   if (dw > 0) {
74     total_sse += encoder_sse(&a[width - dw], a_stride, &b[width - dw], b_stride,
75                              dw, height);
76   }
77 
78   if (dh > 0) {
79     total_sse +=
80         encoder_sse(&a[(height - dh) * a_stride], a_stride,
81                     &b[(height - dh) * b_stride], b_stride, width - dw, dh);
82   }
83 
84   for (y = 0; y < height / 16; ++y) {
85     const uint8_t *pa = a;
86     const uint8_t *pb = b;
87     unsigned int sse;
88     for (x = 0; x < width / 16; ++x) {
89       aom_mse16x16(pa, a_stride, pb, b_stride, &sse);
90       total_sse += sse;
91 
92       pa += 16;
93       pb += 16;
94     }
95 
96     a += 16 * a_stride;
97     b += 16 * b_stride;
98   }
99 
100   return total_sse;
101 }
102 
103 #if CONFIG_AV1_HIGHBITDEPTH
highbd_get_sse_shift(const uint8_t * a8,int a_stride,const uint8_t * b8,int b_stride,int width,int height,unsigned int input_shift)104 static int64_t highbd_get_sse_shift(const uint8_t *a8, int a_stride,
105                                     const uint8_t *b8, int b_stride, int width,
106                                     int height, unsigned int input_shift) {
107   const uint16_t *a = CONVERT_TO_SHORTPTR(a8);
108   const uint16_t *b = CONVERT_TO_SHORTPTR(b8);
109   int64_t total_sse = 0;
110   int x, y;
111   for (y = 0; y < height; ++y) {
112     for (x = 0; x < width; ++x) {
113       int64_t diff;
114       diff = (a[x] >> input_shift) - (b[x] >> input_shift);
115       total_sse += diff * diff;
116     }
117     a += a_stride;
118     b += b_stride;
119   }
120   return total_sse;
121 }
122 
highbd_get_sse(const uint8_t * a,int a_stride,const uint8_t * b,int b_stride,int width,int height)123 static int64_t highbd_get_sse(const uint8_t *a, int a_stride, const uint8_t *b,
124                               int b_stride, int width, int height) {
125   int64_t total_sse = 0;
126   int x, y;
127   const int dw = width % 16;
128   const int dh = height % 16;
129 
130   if (dw > 0) {
131     total_sse += encoder_highbd_8_sse(&a[width - dw], a_stride, &b[width - dw],
132                                       b_stride, dw, height);
133   }
134   if (dh > 0) {
135     total_sse += encoder_highbd_8_sse(&a[(height - dh) * a_stride], a_stride,
136                                       &b[(height - dh) * b_stride], b_stride,
137                                       width - dw, dh);
138   }
139 
140   for (y = 0; y < height / 16; ++y) {
141     const uint8_t *pa = a;
142     const uint8_t *pb = b;
143     unsigned int sse;
144     for (x = 0; x < width / 16; ++x) {
145       aom_highbd_8_mse16x16(pa, a_stride, pb, b_stride, &sse);
146       total_sse += sse;
147       pa += 16;
148       pb += 16;
149     }
150     a += 16 * a_stride;
151     b += 16 * b_stride;
152   }
153   return total_sse;
154 }
155 #endif  // CONFIG_AV1_HIGHBITDEPTH
156 
aom_get_y_var(const YV12_BUFFER_CONFIG * a,int hstart,int width,int vstart,int height)157 uint64_t aom_get_y_var(const YV12_BUFFER_CONFIG *a, int hstart, int width,
158                        int vstart, int height) {
159   return aom_var_2d_u8(a->y_buffer + vstart * a->y_stride + hstart, a->y_stride,
160                        width, height) /
161          (width * height);
162 }
163 
aom_get_u_var(const YV12_BUFFER_CONFIG * a,int hstart,int width,int vstart,int height)164 uint64_t aom_get_u_var(const YV12_BUFFER_CONFIG *a, int hstart, int width,
165                        int vstart, int height) {
166   return aom_var_2d_u8(a->u_buffer + vstart * a->uv_stride + hstart,
167                        a->uv_stride, width, height) /
168          (width * height);
169 }
170 
aom_get_v_var(const YV12_BUFFER_CONFIG * a,int hstart,int width,int vstart,int height)171 uint64_t aom_get_v_var(const YV12_BUFFER_CONFIG *a, int hstart, int width,
172                        int vstart, int height) {
173   return aom_var_2d_u8(a->v_buffer + vstart * a->uv_stride + hstart,
174                        a->uv_stride, width, height) /
175          (width * height);
176 }
177 
aom_get_y_sse_part(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,int hstart,int width,int vstart,int height)178 int64_t aom_get_y_sse_part(const YV12_BUFFER_CONFIG *a,
179                            const YV12_BUFFER_CONFIG *b, int hstart, int width,
180                            int vstart, int height) {
181   return get_sse(a->y_buffer + vstart * a->y_stride + hstart, a->y_stride,
182                  b->y_buffer + vstart * b->y_stride + hstart, b->y_stride,
183                  width, height);
184 }
185 
aom_get_y_sse(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b)186 int64_t aom_get_y_sse(const YV12_BUFFER_CONFIG *a,
187                       const YV12_BUFFER_CONFIG *b) {
188   assert(a->y_crop_width == b->y_crop_width);
189   assert(a->y_crop_height == b->y_crop_height);
190 
191   return get_sse(a->y_buffer, a->y_stride, b->y_buffer, b->y_stride,
192                  a->y_crop_width, a->y_crop_height);
193 }
194 
aom_get_u_sse_part(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,int hstart,int width,int vstart,int height)195 int64_t aom_get_u_sse_part(const YV12_BUFFER_CONFIG *a,
196                            const YV12_BUFFER_CONFIG *b, int hstart, int width,
197                            int vstart, int height) {
198   return get_sse(a->u_buffer + vstart * a->uv_stride + hstart, a->uv_stride,
199                  b->u_buffer + vstart * b->uv_stride + hstart, b->uv_stride,
200                  width, height);
201 }
202 
aom_get_u_sse(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b)203 int64_t aom_get_u_sse(const YV12_BUFFER_CONFIG *a,
204                       const YV12_BUFFER_CONFIG *b) {
205   assert(a->uv_crop_width == b->uv_crop_width);
206   assert(a->uv_crop_height == b->uv_crop_height);
207 
208   return get_sse(a->u_buffer, a->uv_stride, b->u_buffer, b->uv_stride,
209                  a->uv_crop_width, a->uv_crop_height);
210 }
211 
aom_get_v_sse_part(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,int hstart,int width,int vstart,int height)212 int64_t aom_get_v_sse_part(const YV12_BUFFER_CONFIG *a,
213                            const YV12_BUFFER_CONFIG *b, int hstart, int width,
214                            int vstart, int height) {
215   return get_sse(a->v_buffer + vstart * a->uv_stride + hstart, a->uv_stride,
216                  b->v_buffer + vstart * b->uv_stride + hstart, b->uv_stride,
217                  width, height);
218 }
219 
aom_get_v_sse(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b)220 int64_t aom_get_v_sse(const YV12_BUFFER_CONFIG *a,
221                       const YV12_BUFFER_CONFIG *b) {
222   assert(a->uv_crop_width == b->uv_crop_width);
223   assert(a->uv_crop_height == b->uv_crop_height);
224 
225   return get_sse(a->v_buffer, a->uv_stride, b->v_buffer, b->uv_stride,
226                  a->uv_crop_width, a->uv_crop_height);
227 }
228 
229 #if CONFIG_AV1_HIGHBITDEPTH
aom_highbd_get_y_var(const YV12_BUFFER_CONFIG * a,int hstart,int width,int vstart,int height)230 uint64_t aom_highbd_get_y_var(const YV12_BUFFER_CONFIG *a, int hstart,
231                               int width, int vstart, int height) {
232   return aom_var_2d_u16(a->y_buffer + vstart * a->y_stride + hstart,
233                         a->y_stride, width, height) /
234          (width * height);
235 }
236 
aom_highbd_get_u_var(const YV12_BUFFER_CONFIG * a,int hstart,int width,int vstart,int height)237 uint64_t aom_highbd_get_u_var(const YV12_BUFFER_CONFIG *a, int hstart,
238                               int width, int vstart, int height) {
239   return aom_var_2d_u16(a->u_buffer + vstart * a->uv_stride + hstart,
240                         a->uv_stride, width, height) /
241          (width * height);
242 }
243 
aom_highbd_get_v_var(const YV12_BUFFER_CONFIG * a,int hstart,int width,int vstart,int height)244 uint64_t aom_highbd_get_v_var(const YV12_BUFFER_CONFIG *a, int hstart,
245                               int width, int vstart, int height) {
246   return aom_var_2d_u16(a->v_buffer + vstart * a->uv_stride + hstart,
247                         a->uv_stride, width, height) /
248          (width * height);
249 }
250 
aom_highbd_get_y_sse_part(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,int hstart,int width,int vstart,int height)251 int64_t aom_highbd_get_y_sse_part(const YV12_BUFFER_CONFIG *a,
252                                   const YV12_BUFFER_CONFIG *b, int hstart,
253                                   int width, int vstart, int height) {
254   return highbd_get_sse(
255       a->y_buffer + vstart * a->y_stride + hstart, a->y_stride,
256       b->y_buffer + vstart * b->y_stride + hstart, b->y_stride, width, height);
257 }
258 
aom_highbd_get_y_sse(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b)259 int64_t aom_highbd_get_y_sse(const YV12_BUFFER_CONFIG *a,
260                              const YV12_BUFFER_CONFIG *b) {
261   assert(a->y_crop_width == b->y_crop_width);
262   assert(a->y_crop_height == b->y_crop_height);
263   assert((a->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
264   assert((b->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
265 
266   return highbd_get_sse(a->y_buffer, a->y_stride, b->y_buffer, b->y_stride,
267                         a->y_crop_width, a->y_crop_height);
268 }
269 
aom_highbd_get_u_sse_part(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,int hstart,int width,int vstart,int height)270 int64_t aom_highbd_get_u_sse_part(const YV12_BUFFER_CONFIG *a,
271                                   const YV12_BUFFER_CONFIG *b, int hstart,
272                                   int width, int vstart, int height) {
273   return highbd_get_sse(a->u_buffer + vstart * a->uv_stride + hstart,
274                         a->uv_stride,
275                         b->u_buffer + vstart * b->uv_stride + hstart,
276                         b->uv_stride, width, height);
277 }
278 
aom_highbd_get_u_sse(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b)279 int64_t aom_highbd_get_u_sse(const YV12_BUFFER_CONFIG *a,
280                              const YV12_BUFFER_CONFIG *b) {
281   assert(a->uv_crop_width == b->uv_crop_width);
282   assert(a->uv_crop_height == b->uv_crop_height);
283   assert((a->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
284   assert((b->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
285 
286   return highbd_get_sse(a->u_buffer, a->uv_stride, b->u_buffer, b->uv_stride,
287                         a->uv_crop_width, a->uv_crop_height);
288 }
289 
aom_highbd_get_v_sse_part(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,int hstart,int width,int vstart,int height)290 int64_t aom_highbd_get_v_sse_part(const YV12_BUFFER_CONFIG *a,
291                                   const YV12_BUFFER_CONFIG *b, int hstart,
292                                   int width, int vstart, int height) {
293   return highbd_get_sse(a->v_buffer + vstart * a->uv_stride + hstart,
294                         a->uv_stride,
295                         b->v_buffer + vstart * b->uv_stride + hstart,
296                         b->uv_stride, width, height);
297 }
298 
aom_highbd_get_v_sse(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b)299 int64_t aom_highbd_get_v_sse(const YV12_BUFFER_CONFIG *a,
300                              const YV12_BUFFER_CONFIG *b) {
301   assert(a->uv_crop_width == b->uv_crop_width);
302   assert(a->uv_crop_height == b->uv_crop_height);
303   assert((a->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
304   assert((b->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
305 
306   return highbd_get_sse(a->v_buffer, a->uv_stride, b->v_buffer, b->uv_stride,
307                         a->uv_crop_width, a->uv_crop_height);
308 }
309 #endif  // CONFIG_AV1_HIGHBITDEPTH
310 
aom_get_sse_plane(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,int plane,int highbd)311 int64_t aom_get_sse_plane(const YV12_BUFFER_CONFIG *a,
312                           const YV12_BUFFER_CONFIG *b, int plane, int highbd) {
313 #if CONFIG_AV1_HIGHBITDEPTH
314   if (highbd) {
315     switch (plane) {
316       case 0: return aom_highbd_get_y_sse(a, b);
317       case 1: return aom_highbd_get_u_sse(a, b);
318       case 2: return aom_highbd_get_v_sse(a, b);
319       default: assert(plane >= 0 && plane <= 2); return 0;
320     }
321   } else {
322     switch (plane) {
323       case 0: return aom_get_y_sse(a, b);
324       case 1: return aom_get_u_sse(a, b);
325       case 2: return aom_get_v_sse(a, b);
326       default: assert(plane >= 0 && plane <= 2); return 0;
327     }
328   }
329 #else
330   (void)highbd;
331   switch (plane) {
332     case 0: return aom_get_y_sse(a, b);
333     case 1: return aom_get_u_sse(a, b);
334     case 2: return aom_get_v_sse(a, b);
335     default: assert(plane >= 0 && plane <= 2); return 0;
336   }
337 #endif
338 }
339 
340 #if CONFIG_AV1_HIGHBITDEPTH
aom_calc_highbd_psnr(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,PSNR_STATS * psnr,uint32_t bit_depth,uint32_t in_bit_depth)341 void aom_calc_highbd_psnr(const YV12_BUFFER_CONFIG *a,
342                           const YV12_BUFFER_CONFIG *b, PSNR_STATS *psnr,
343                           uint32_t bit_depth, uint32_t in_bit_depth) {
344   assert(a->y_crop_width == b->y_crop_width);
345   assert(a->y_crop_height == b->y_crop_height);
346   assert(a->uv_crop_width == b->uv_crop_width);
347   assert(a->uv_crop_height == b->uv_crop_height);
348   const int widths[3] = { a->y_crop_width, a->uv_crop_width, a->uv_crop_width };
349   const int heights[3] = { a->y_crop_height, a->uv_crop_height,
350                            a->uv_crop_height };
351   const int a_strides[3] = { a->y_stride, a->uv_stride, a->uv_stride };
352   const int b_strides[3] = { b->y_stride, b->uv_stride, b->uv_stride };
353   int i;
354   uint64_t total_sse = 0;
355   uint32_t total_samples = 0;
356   double peak = (double)((1 << in_bit_depth) - 1);
357   const unsigned int input_shift = bit_depth - in_bit_depth;
358 
359   for (i = 0; i < 3; ++i) {
360     const int w = widths[i];
361     const int h = heights[i];
362     const uint32_t samples = w * h;
363     uint64_t sse;
364     if (a->flags & YV12_FLAG_HIGHBITDEPTH) {
365       if (input_shift) {
366         sse = highbd_get_sse_shift(a->buffers[i], a_strides[i], b->buffers[i],
367                                    b_strides[i], w, h, input_shift);
368       } else {
369         sse = highbd_get_sse(a->buffers[i], a_strides[i], b->buffers[i],
370                              b_strides[i], w, h);
371       }
372     } else {
373       sse = get_sse(a->buffers[i], a_strides[i], b->buffers[i], b_strides[i], w,
374                     h);
375     }
376     psnr->sse[1 + i] = sse;
377     psnr->samples[1 + i] = samples;
378     psnr->psnr[1 + i] = aom_sse_to_psnr(samples, peak, (double)sse);
379 
380     total_sse += sse;
381     total_samples += samples;
382   }
383 
384   psnr->sse[0] = total_sse;
385   psnr->samples[0] = total_samples;
386   psnr->psnr[0] =
387       aom_sse_to_psnr((double)total_samples, peak, (double)total_sse);
388 
389   // Compute PSNR based on stream bit depth
390   if ((a->flags & YV12_FLAG_HIGHBITDEPTH) && (in_bit_depth < bit_depth)) {
391     peak = (double)((1 << bit_depth) - 1);
392     total_sse = 0;
393     total_samples = 0;
394     for (i = 0; i < 3; ++i) {
395       const int w = widths[i];
396       const int h = heights[i];
397       const uint32_t samples = w * h;
398       uint64_t sse;
399       sse = highbd_get_sse(a->buffers[i], a_strides[i], b->buffers[i],
400                            b_strides[i], w, h);
401       psnr->sse_hbd[1 + i] = sse;
402       psnr->samples_hbd[1 + i] = samples;
403       psnr->psnr_hbd[1 + i] = aom_sse_to_psnr(samples, peak, (double)sse);
404       total_sse += sse;
405       total_samples += samples;
406     }
407 
408     psnr->sse_hbd[0] = total_sse;
409     psnr->samples_hbd[0] = total_samples;
410     psnr->psnr_hbd[0] =
411         aom_sse_to_psnr((double)total_samples, peak, (double)total_sse);
412   }
413 }
414 #endif
415 
aom_calc_psnr(const YV12_BUFFER_CONFIG * a,const YV12_BUFFER_CONFIG * b,PSNR_STATS * psnr)416 void aom_calc_psnr(const YV12_BUFFER_CONFIG *a, const YV12_BUFFER_CONFIG *b,
417                    PSNR_STATS *psnr) {
418   assert(a->y_crop_width == b->y_crop_width);
419   assert(a->y_crop_height == b->y_crop_height);
420   assert(a->uv_crop_width == b->uv_crop_width);
421   assert(a->uv_crop_height == b->uv_crop_height);
422   static const double peak = 255.0;
423   const int widths[3] = { a->y_crop_width, a->uv_crop_width, a->uv_crop_width };
424   const int heights[3] = { a->y_crop_height, a->uv_crop_height,
425                            a->uv_crop_height };
426   const int a_strides[3] = { a->y_stride, a->uv_stride, a->uv_stride };
427   const int b_strides[3] = { b->y_stride, b->uv_stride, b->uv_stride };
428   int i;
429   uint64_t total_sse = 0;
430   uint32_t total_samples = 0;
431 
432   for (i = 0; i < 3; ++i) {
433     const int w = widths[i];
434     const int h = heights[i];
435     const uint32_t samples = w * h;
436     const uint64_t sse =
437         get_sse(a->buffers[i], a_strides[i], b->buffers[i], b_strides[i], w, h);
438     psnr->sse[1 + i] = sse;
439     psnr->samples[1 + i] = samples;
440     psnr->psnr[1 + i] = aom_sse_to_psnr(samples, peak, (double)sse);
441 
442     total_sse += sse;
443     total_samples += samples;
444   }
445 
446   psnr->sse[0] = total_sse;
447   psnr->samples[0] = total_samples;
448   psnr->psnr[0] =
449       aom_sse_to_psnr((double)total_samples, peak, (double)total_sse);
450 }
451