Coverage Report

Created: 2025-06-22 08:04

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