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