Coverage Report

Created: 2026-05-30 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libavif/ext/aom/aom_dsp/psnr.c
Line
Count
Source
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
299k
                           int b_stride, int w, int h) {
40
299k
  int i, j;
41
299k
  int64_t sse = 0;
42
43
8.06M
  for (i = 0; i < h; i++) {
44
72.9M
    for (j = 0; j < w; j++) {
45
65.2M
      const int diff = a[j] - b[j];
46
65.2M
      sse += diff * diff;
47
65.2M
    }
48
49
7.76M
    a += a_stride;
50
7.76M
    b += b_stride;
51
7.76M
  }
52
299k
  return sse;
53
299k
}
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
151k
                                  int h) {
59
151k
  const uint16_t *a = CONVERT_TO_SHORTPTR(a8);
60
151k
  const uint16_t *b = CONVERT_TO_SHORTPTR(b8);
61
151k
  int64_t sse = 0;
62
3.37M
  for (int i = 0; i < h; ++i) {
63
29.8M
    for (int j = 0; j < w; ++j) {
64
26.5M
      const int diff = a[j] - b[j];
65
26.5M
      sse += diff * diff;
66
26.5M
    }
67
3.22M
    a += a_stride;
68
3.22M
    b += b_stride;
69
3.22M
  }
70
151k
  return sse;
71
151k
}
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
159k
                       int b_stride, int width, int height) {
77
159k
  const int dw = width % 16;
78
159k
  const int dh = height % 16;
79
159k
  int64_t total_sse = 0;
80
159k
  int x, y;
81
82
159k
  if (dw > 0) {
83
148k
    total_sse += encoder_sse(&a[width - dw], a_stride, &b[width - dw], b_stride,
84
148k
                             dw, height);
85
148k
  }
86
87
159k
  if (dh > 0) {
88
150k
    total_sse +=
89
150k
        encoder_sse(&a[(height - dh) * a_stride], a_stride,
90
150k
                    &b[(height - dh) * b_stride], b_stride, width - dw, dh);
91
150k
  }
92
93
580k
  for (y = 0; y < height / 16; ++y) {
94
421k
    const uint8_t *pa = a;
95
421k
    const uint8_t *pb = b;
96
1.91M
    for (x = 0; x < width / 16; ++x) {
97
1.49M
      total_sse += aom_sse(pa, a_stride, pb, b_stride, 16, 16);
98
99
1.49M
      pa += 16;
100
1.49M
      pb += 16;
101
1.49M
    }
102
103
421k
    a += 16 * a_stride;
104
421k
    b += 16 * b_stride;
105
421k
  }
106
107
159k
  return total_sse;
108
159k
}
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
79.9k
                              int b_stride, int width, int height) {
132
79.9k
  int64_t total_sse = 0;
133
79.9k
  int x, y;
134
79.9k
  const int dw = width % 16;
135
79.9k
  const int dh = height % 16;
136
137
79.9k
  if (dw > 0) {
138
75.9k
    total_sse += encoder_highbd_sse(&a[width - dw], a_stride, &b[width - dw],
139
75.9k
                                    b_stride, dw, height);
140
75.9k
  }
141
79.9k
  if (dh > 0) {
142
75.8k
    total_sse += encoder_highbd_sse(&a[(height - dh) * a_stride], a_stride,
143
75.8k
                                    &b[(height - dh) * b_stride], b_stride,
144
75.8k
                                    width - dw, dh);
145
75.8k
  }
146
147
241k
  for (y = 0; y < height / 16; ++y) {
148
161k
    const uint8_t *pa = a;
149
161k
    const uint8_t *pb = b;
150
370k
    for (x = 0; x < width / 16; ++x) {
151
209k
      total_sse += aom_highbd_sse(pa, a_stride, pb, b_stride, 16, 16);
152
209k
      pa += 16;
153
209k
      pb += 16;
154
209k
    }
155
161k
    a += 16 * a_stride;
156
161k
    b += 16 * b_stride;
157
161k
  }
158
79.9k
  return total_sse;
159
79.9k
}
160
#endif  // CONFIG_AV1_HIGHBITDEPTH
161
162
uint64_t aom_get_y_var(const YV12_BUFFER_CONFIG *a, int hstart, int width,
163
9.23k
                       int vstart, int height) {
164
9.23k
  return aom_var_2d_u8(a->y_buffer + vstart * a->y_stride + hstart, a->y_stride,
165
9.23k
                       width, height) /
166
9.23k
         (width * height);
167
9.23k
}
168
169
uint64_t aom_get_u_var(const YV12_BUFFER_CONFIG *a, int hstart, int width,
170
2.58k
                       int vstart, int height) {
171
2.58k
  return aom_var_2d_u8(a->u_buffer + vstart * a->uv_stride + hstart,
172
2.58k
                       a->uv_stride, width, height) /
173
2.58k
         (width * height);
174
2.58k
}
175
176
uint64_t aom_get_v_var(const YV12_BUFFER_CONFIG *a, int hstart, int width,
177
2.58k
                       int vstart, int height) {
178
2.58k
  return aom_var_2d_u8(a->v_buffer + vstart * a->uv_stride + hstart,
179
2.58k
                       a->uv_stride, width, height) /
180
2.58k
         (width * height);
181
2.58k
}
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
11.8k
                           int vstart, int height) {
186
11.8k
  return get_sse(a->y_buffer + vstart * a->y_stride + hstart, a->y_stride,
187
11.8k
                 b->y_buffer + vstart * b->y_stride + hstart, b->y_stride,
188
11.8k
                 width, height);
189
11.8k
}
190
191
int64_t aom_get_y_sse(const YV12_BUFFER_CONFIG *a,
192
79.0k
                      const YV12_BUFFER_CONFIG *b) {
193
79.0k
  assert(a->y_crop_width == b->y_crop_width);
194
79.0k
  assert(a->y_crop_height == b->y_crop_height);
195
196
79.0k
  return get_sse(a->y_buffer, a->y_stride, b->y_buffer, b->y_stride,
197
79.0k
                 a->y_crop_width, a->y_crop_height);
198
79.0k
}
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
3.44k
                           int vstart, int height) {
203
3.44k
  return get_sse(a->u_buffer + vstart * a->uv_stride + hstart, a->uv_stride,
204
3.44k
                 b->u_buffer + vstart * b->uv_stride + hstart, b->uv_stride,
205
3.44k
                 width, height);
206
3.44k
}
207
208
int64_t aom_get_u_sse(const YV12_BUFFER_CONFIG *a,
209
30.8k
                      const YV12_BUFFER_CONFIG *b) {
210
30.8k
  assert(a->uv_crop_width == b->uv_crop_width);
211
30.8k
  assert(a->uv_crop_height == b->uv_crop_height);
212
213
30.8k
  return get_sse(a->u_buffer, a->uv_stride, b->u_buffer, b->uv_stride,
214
30.8k
                 a->uv_crop_width, a->uv_crop_height);
215
30.8k
}
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
3.48k
                           int vstart, int height) {
220
3.48k
  return get_sse(a->v_buffer + vstart * a->uv_stride + hstart, a->uv_stride,
221
3.48k
                 b->v_buffer + vstart * b->uv_stride + hstart, b->uv_stride,
222
3.48k
                 width, height);
223
3.48k
}
224
225
int64_t aom_get_v_sse(const YV12_BUFFER_CONFIG *a,
226
30.9k
                      const YV12_BUFFER_CONFIG *b) {
227
30.9k
  assert(a->uv_crop_width == b->uv_crop_width);
228
30.9k
  assert(a->uv_crop_height == b->uv_crop_height);
229
230
30.9k
  return get_sse(a->v_buffer, a->uv_stride, b->v_buffer, b->uv_stride,
231
30.9k
                 a->uv_crop_width, a->uv_crop_height);
232
30.9k
}
233
234
#if CONFIG_AV1_HIGHBITDEPTH
235
uint64_t aom_highbd_get_y_var(const YV12_BUFFER_CONFIG *a, int hstart,
236
2.71k
                              int width, int vstart, int height) {
237
2.71k
  return aom_var_2d_u16(a->y_buffer + vstart * a->y_stride + hstart,
238
2.71k
                        a->y_stride, width, height) /
239
2.71k
         (width * height);
240
2.71k
}
241
242
uint64_t aom_highbd_get_u_var(const YV12_BUFFER_CONFIG *a, int hstart,
243
836
                              int width, int vstart, int height) {
244
836
  return aom_var_2d_u16(a->u_buffer + vstart * a->uv_stride + hstart,
245
836
                        a->uv_stride, width, height) /
246
836
         (width * height);
247
836
}
248
249
uint64_t aom_highbd_get_v_var(const YV12_BUFFER_CONFIG *a, int hstart,
250
836
                              int width, int vstart, int height) {
251
836
  return aom_var_2d_u16(a->v_buffer + vstart * a->uv_stride + hstart,
252
836
                        a->uv_stride, width, height) /
253
836
         (width * height);
254
836
}
255
256
int64_t aom_highbd_get_y_sse_part(const YV12_BUFFER_CONFIG *a,
257
                                  const YV12_BUFFER_CONFIG *b, int hstart,
258
3.44k
                                  int width, int vstart, int height) {
259
3.44k
  return highbd_get_sse(
260
3.44k
      a->y_buffer + vstart * a->y_stride + hstart, a->y_stride,
261
3.44k
      b->y_buffer + vstart * b->y_stride + hstart, b->y_stride, width, height);
262
3.44k
}
263
264
int64_t aom_highbd_get_y_sse(const YV12_BUFFER_CONFIG *a,
265
40.0k
                             const YV12_BUFFER_CONFIG *b) {
266
40.0k
  assert(a->y_crop_width == b->y_crop_width);
267
40.0k
  assert(a->y_crop_height == b->y_crop_height);
268
40.0k
  assert((a->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
269
40.0k
  assert((b->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
270
271
40.0k
  return highbd_get_sse(a->y_buffer, a->y_stride, b->y_buffer, b->y_stride,
272
40.0k
                        a->y_crop_width, a->y_crop_height);
273
40.0k
}
274
275
int64_t aom_highbd_get_u_sse_part(const YV12_BUFFER_CONFIG *a,
276
                                  const YV12_BUFFER_CONFIG *b, int hstart,
277
1.14k
                                  int width, int vstart, int height) {
278
1.14k
  return highbd_get_sse(a->u_buffer + vstart * a->uv_stride + hstart,
279
1.14k
                        a->uv_stride,
280
1.14k
                        b->u_buffer + vstart * b->uv_stride + hstart,
281
1.14k
                        b->uv_stride, width, height);
282
1.14k
}
283
284
int64_t aom_highbd_get_u_sse(const YV12_BUFFER_CONFIG *a,
285
17.1k
                             const YV12_BUFFER_CONFIG *b) {
286
17.1k
  assert(a->uv_crop_width == b->uv_crop_width);
287
17.1k
  assert(a->uv_crop_height == b->uv_crop_height);
288
17.1k
  assert((a->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
289
17.1k
  assert((b->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
290
291
17.1k
  return highbd_get_sse(a->u_buffer, a->uv_stride, b->u_buffer, b->uv_stride,
292
17.1k
                        a->uv_crop_width, a->uv_crop_height);
293
17.1k
}
294
295
int64_t aom_highbd_get_v_sse_part(const YV12_BUFFER_CONFIG *a,
296
                                  const YV12_BUFFER_CONFIG *b, int hstart,
297
1.15k
                                  int width, int vstart, int height) {
298
1.15k
  return highbd_get_sse(a->v_buffer + vstart * a->uv_stride + hstart,
299
1.15k
                        a->uv_stride,
300
1.15k
                        b->v_buffer + vstart * b->uv_stride + hstart,
301
1.15k
                        b->uv_stride, width, height);
302
1.15k
}
303
304
int64_t aom_highbd_get_v_sse(const YV12_BUFFER_CONFIG *a,
305
17.1k
                             const YV12_BUFFER_CONFIG *b) {
306
17.1k
  assert(a->uv_crop_width == b->uv_crop_width);
307
17.1k
  assert(a->uv_crop_height == b->uv_crop_height);
308
17.1k
  assert((a->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
309
17.1k
  assert((b->flags & YV12_FLAG_HIGHBITDEPTH) != 0);
310
311
17.1k
  return highbd_get_sse(a->v_buffer, a->uv_stride, b->v_buffer, b->uv_stride,
312
17.1k
                        a->uv_crop_width, a->uv_crop_height);
313
17.1k
}
314
#endif  // CONFIG_AV1_HIGHBITDEPTH
315
316
int64_t aom_get_sse_plane(const YV12_BUFFER_CONFIG *a,
317
215k
                          const YV12_BUFFER_CONFIG *b, int plane, int highbd) {
318
215k
#if CONFIG_AV1_HIGHBITDEPTH
319
215k
  if (highbd) {
320
74.2k
    switch (plane) {
321
40.0k
      case 0: return aom_highbd_get_y_sse(a, b);
322
17.1k
      case 1: return aom_highbd_get_u_sse(a, b);
323
17.1k
      case 2: return aom_highbd_get_v_sse(a, b);
324
0
      default: assert(plane >= 0 && plane <= 2); return 0;
325
74.2k
    }
326
140k
  } else {
327
140k
    switch (plane) {
328
79.0k
      case 0: return aom_get_y_sse(a, b);
329
30.8k
      case 1: return aom_get_u_sse(a, b);
330
30.9k
      case 2: return aom_get_v_sse(a, b);
331
0
      default: assert(plane >= 0 && plane <= 2); return 0;
332
140k
    }
333
140k
  }
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
215k
}
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
}