1 /*
2 * Copyright (c) 2024 Christian R. Helmrich
3 * Copyright (c) 2024 Christian Lehmann
4 * Copyright (c) 2024 Christian Stoffers
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 /**
24 * @file
25 * Calculate the extended perceptually weighted PSNR (XPSNR) between two input videos.
26 *
27 * Authors: Christian Helmrich, Lehmann, and Stoffers, Fraunhofer HHI, Berlin, Germany
28 */
29
41
42 /* XPSNR structure definition */
43
45 /* required basic variables */
47 int bpp;
/* unpacked */
60 /* XPSNR specific variables */
75
76 /* required macro definitions */
77
78 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
79 #define OFFSET(x) offsetof(XPSNRContext, x)
81
86 };
87
89
90 /* XPSNR function definitions */
91
92 static uint64_t
highds(
const int x_act,
const int y_act,
const int w_act,
const int h_act,
const int16_t *o_m0,
const int o)
93 {
94 uint64_t sa_act = 0;
95
96 for (int y = y_act; y < h_act; y += 2) {
97 for (int x = x_act; x < w_act; x += 2) {
98 const int f = 12 * ((int)o_m0[ y *o + x ] + (
int)o_m0[ y *o + x+1] + (int)o_m0[(y+1)*o + x ] + (int)o_m0[(y+1)*o + x+1])
99 - 3 * ((int)o_m0[(y-1)*o + x ] + (int)o_m0[(y-1)*o + x+1] + (int)o_m0[(y+2)*o + x ] + (int)o_m0[(y+2)*o + x+1])
100 - 3 * ((int)o_m0[ y *o + x-1] + (int)o_m0[ y *o + x+2] + (int)o_m0[(y+1)*o + x-1] + (int)o_m0[(y+1)*o + x+2])
101 - 2 * ((int)o_m0[(y-1)*o + x-1] + (int)o_m0[(y-1)*o + x+2] + (int)o_m0[(y+2)*o + x-1] + (int)o_m0[(y+2)*o + x+2])
102 - ((int)o_m0[(y-2)*o + x-1] + (int)o_m0[(y-2)*o + x ] + (int)o_m0[(y-2)*o + x+1] + (int)o_m0[(y-2)*o + x+2]
103 + (int)o_m0[(y+3)*o + x-1] + (int)o_m0[(y+3)*o + x ] + (int)o_m0[(y+3)*o + x+1] + (int)o_m0[(y+3)*o + x+2]
104 + (int)o_m0[(y-1)*o + x-2] + (int)o_m0[ y *o + x-2] + (int)o_m0[(y+1)*o + x-2] + (int)o_m0[(y+2)*o + x-2]
105 + (int)o_m0[(y-1)*o + x+3] + (int)o_m0[ y *o + x+3] + (int)o_m0[(y+1)*o + x+3] + (int)o_m0[(y+2)*o + x+3]);
106 sa_act += (uint64_t)
abs(
f);
107 }
108 }
109 return sa_act;
110 }
111
112 static uint64_t
diff1st(
const uint32_t w_act,
const uint32_t h_act,
const int16_t *o_m0, int16_t *o_m1,
const int o)
113 {
114 uint64_t ta_act = 0;
115
116 for (uint32_t y = 0; y < h_act; y += 2) {
117 for (uint32_t x = 0; x < w_act; x += 2) {
118 const int t = (int)o_m0[y*o + x] + (int)o_m0[y*o + x+1] + (int)o_m0[(y+1)*o + x] + (int)o_m0[(y+1)*o + x+1]
119 - ((int)o_m1[y*o + x] + (int)o_m1[y*o + x+1] + (int)o_m1[(y+1)*o + x] + (int)o_m1[(y+1)*o + x+1]);
120 ta_act += (uint64_t)
abs(t);
121 o_m1[y*o + x ] = o_m0[y*o + x ]; o_m1[(y+1)*o + x ] = o_m0[(y+1)*o + x ];
122 o_m1[y*o + x+1] = o_m0[y*o + x+1]; o_m1[(y+1)*o + x+1] = o_m0[(y+1)*o + x+1];
123 }
124 }
126 }
127
128 static uint64_t
diff2nd(
const uint32_t w_act,
const uint32_t h_act,
const int16_t *o_m0, int16_t *o_m1, int16_t *o_m2,
const int o)
129 {
130 uint64_t ta_act = 0;
131
132 for (uint32_t y = 0; y < h_act; y += 2) {
133 for (uint32_t x = 0; x < w_act; x += 2) {
134 const int t = (int)o_m0[y*o + x] + (int)o_m0[y*o + x+1] + (int)o_m0[(y+1)*o + x] + (int)o_m0[(y+1)*o + x+1]
135 - 2 * ((int)o_m1[y*o + x] + (int)o_m1[y*o + x+1] + (int)o_m1[(y+1)*o + x] + (int)o_m1[(y+1)*o + x+1])
136 + (int)o_m2[y*o + x] + (int)o_m2[y*o + x+1] + (int)o_m2[(y+1)*o + x] + (int)o_m2[(y+1)*o + x+1];
137 ta_act += (uint64_t)
abs(t);
138 o_m2[y*o + x ] = o_m1[y*o + x ]; o_m2[(y+1)*o + x ] = o_m1[(y+1)*o + x ];
139 o_m2[y*o + x+1] = o_m1[y*o + x+1]; o_m2[(y+1)*o + x+1] = o_m1[(y+1)*o + x+1];
140 o_m1[y*o + x ] = o_m0[y*o + x ]; o_m1[(y+1)*o + x ] = o_m0[(y+1)*o + x ];
141 o_m1[y*o + x+1] = o_m0[y*o + x+1]; o_m1[(y+1)*o + x+1] = o_m0[(y+1)*o + x+1];
142 }
143 }
145 }
146
148 const int16_t *blk_org, const uint32_t stride_org,
149 const int16_t *blk_rec, const uint32_t stride_rec,
150 const uint32_t block_width, const uint32_t block_height)
151 {
152 uint64_t
sse = 0;
/* sum of squared errors */
153
154 for (uint32_t y = 0; y < block_height; y++) {
155 sse +=
s->pdsp.sse_line((
const uint8_t *) blk_org, (
const uint8_t *) blk_rec, (
int) block_width);
156 blk_org += stride_org;
157 blk_rec += stride_rec;
158 }
159
160 /* return nonweighted sum of squared errors */
162 }
163
165 const int16_t *pic_org, const uint32_t stride_org,
166 int16_t *pic_org_m1, int16_t *pic_org_m2,
167 const int16_t *pic_rec, const uint32_t stride_rec,
168 const uint32_t offset_x, const uint32_t offset_y,
169 const uint32_t block_width, const uint32_t block_height,
170 const uint32_t
bit_depth,
const uint32_t int_frame_rate,
double *ms_act)
171 {
172 const int o = (int) stride_org;
173 const int r = (int) stride_rec;
174 const int16_t *o_m0 = pic_org + offset_y * o + offset_x;
175 int16_t *o_m1 = pic_org_m1 + offset_y * o + offset_x;
176 int16_t *o_m2 = pic_org_m2 + offset_y * o + offset_x;
177 const int16_t *r_m0 = pic_rec + offset_y *
r + offset_x;
178 const int b_val = (
s->plane_width[0] *
s->plane_height[0] > 2048 * 1152 ? 2 : 1);
/* threshold is a bit more than HD resolution */
179 const int x_act = (offset_x > 0 ? 0 : b_val);
180 const int y_act = (offset_y > 0 ? 0 : b_val);
181 const int w_act = (offset_x + block_width < (uint32_t)
s->plane_width [0] ? (
int) block_width : (int) block_width - b_val);
182 const int h_act = (offset_y + block_height < (uint32_t)
s->plane_height[0] ? (
int) block_height : (int) block_height - b_val);
183
185 r_m0, stride_rec,
186 block_width, block_height);
187 uint64_t sa_act = 0; /* spatial abs. activity */
188 uint64_t ta_act = 0; /* temporal abs. activity */
189
190 if (w_act <= x_act || h_act <= y_act) /* small */
192
193 if (b_val > 1) { /* highpass with downsampling */
194 if (w_act > 12)
195 sa_act =
s->dsp.highds_func(x_act, y_act, w_act, h_act, o_m0, o);
196 else
197 highds(x_act, y_act, w_act, h_act, o_m0, o);
198 } else { /* <=HD highpass without downsampling */
199 for (int y = y_act; y < h_act; y++) {
200 for (int x = x_act; x < w_act; x++) {
201 const int f = 12 * (int)o_m0[y*o + x] - 2 * ((
int)o_m0[y*o + x-1] + (int)o_m0[y*o + x+1] + (
int)o_m0[(y-1)*o + x] + (
int)o_m0[(y+1)*o + x])
202 - ((int)o_m0[(y-1)*o + x-1] + (int)o_m0[(y-1)*o + x+1] + (int)o_m0[(y+1)*o + x-1] + (int)o_m0[(y+1)*o + x+1]);
203 sa_act += (uint64_t)
abs(
f);
204 }
205 }
206 }
207
208 /* calculate weight (average squared activity) */
209 *ms_act = (
double) sa_act / ((
double) (w_act - x_act) * (
double) (h_act - y_act));
210
211 if (b_val > 1) { /* highpass with downsampling */
212 if (int_frame_rate < 32) /* 1st-order diff */
213 ta_act =
s->dsp.diff1st_func(block_width, block_height, o_m0, o_m1, o);
214 else /* 2nd-order diff (diff of two diffs) */
215 ta_act =
s->dsp.diff2nd_func(block_width, block_height, o_m0, o_m1, o_m2, o);
216 } else { /* <=HD highpass without downsampling */
217 if (int_frame_rate < 32) { /* 1st-order diff */
218 for (uint32_t y = 0; y < block_height; y++) {
219 for (uint32_t x = 0; x < block_width; x++) {
220 const int t = (int)o_m0[y * o + x] - (int)o_m1[y * o + x];
221
223 o_m1[y * o + x] = o_m0[y * o + x];
224 }
225 }
226 } else { /* 2nd-order diff (diff of 2 diffs) */
227 for (uint32_t y = 0; y < block_height; y++) {
228 for (uint32_t x = 0; x < block_width; x++) {
229 const int t = (int)o_m0[y * o + x] - 2 * (int)o_m1[y * o + x] + (int)o_m2[y * o + x];
230
232 o_m2[y * o + x] = o_m1[y * o + x];
233 o_m1[y * o + x] = o_m0[y * o + x];
234 }
235 }
236 }
237 }
238
239 /* weight += mean squared temporal activity */
240 *ms_act += (
double) ta_act / ((
double) block_width * (
double) block_height);
241
242 /* lower limit, accounts for high-pass gain */
243 if (*ms_act < (
double) (1 << (
bit_depth - 6)))
244 *ms_act = (
double) (1 << (
bit_depth - 6));
245
246 *ms_act *= *ms_act; /* since SSE is squared */
247
248 /* return nonweighted sum of squared errors */
250 }
251
252 static inline double get_avg_xpsnr (
const double sqrt_wsse_val,
const double sum_xpsnr_val,
253 const uint32_t image_width, const uint32_t image_height,
254 const uint64_t max_error_64, const uint64_t num_frames_64)
255 {
256 if (num_frames_64 == 0)
258
259 if (sqrt_wsse_val >= (double) num_frames_64) { /* square-mean-root average */
260 const double avg_dist = sqrt_wsse_val / (
double) num_frames_64;
261 const uint64_t num64 = (uint64_t) image_width * (uint64_t) image_height * max_error_64;
262
263 return 10.0 * log10((double) num64 / ((double) avg_dist * (double) avg_dist));
264 }
265
266 return sum_xpsnr_val / (
double) num_frames_64;
/* older log-domain average */
267 }
268
270 int16_t *org_m2, int16_t **rec, uint64_t *const wsse64)
271 {
273 const uint32_t
w =
s->plane_width [0];
/* luma image width in pixels */
274 const uint32_t
h =
s->plane_height[0];
/* luma image height in pixels */
275 const double r = (
double)(
w *
h) / (3840.0 * 2160.0);
/* UHD ratio */
277 0.5)); /* block size, integer multiple of 4 for SIMD */
278 const uint32_t w_blk = (
w +
b - 1) /
b;
/* luma width in units of blocks */
279 const double avg_act = sqrt(16.0 * (
double) (1 << (2 *
s->depth - 9)) / sqrt(
FFMAX(0.00001,
280 r)));
/* the sqrt(a_pic) */
281 const int *stride_org = (
s->bpp == 1 ?
s->plane_width :
s->line_sizes);
282 uint32_t x, y, idx_blk = 0; /* the "16.0" above is due to fixed-point code */
283 double *
const sse_luma =
s->sse_luma;
286
287 if (!wsse64 || (
s->depth < 6) || (
s->depth > 16) || (
s->num_comps <= 0) ||
288 (
s->num_comps > 3) || (
w == 0) || (
h == 0)) {
291 }
292 if (!
weights || (
b >= 4 && !sse_luma)) {
295 }
296
298 const int16_t *p_org = org[0];
299 const uint32_t s_org = stride_org[0] /
s->bpp;
300 const int16_t *p_rec = rec[0];
301 const uint32_t s_rec =
s->plane_width[0];
302 double wsse_luma = 0.0;
303
304 for (y = 0; y <
h; y +=
b) {
/* calculate block SSE and perceptual weights */
305 const uint32_t block_height = (y +
b >
h ?
h - y :
b);
306
307 for (x = 0; x <
w; x +=
b, idx_blk++) {
308 const uint32_t block_width = (x +
b >
w ?
w - x :
b);
309 double ms_act = 1.0, ms_act_prev = 0.0;
310
312 org_m1 /* pixel */,
313 org_m2 /* memory */,
314 p_rec, s_rec,
315 x, y,
316 block_width, block_height,
317 s->depth,
s->frame_rate, &ms_act);
318 weights[idx_blk] = 1.0 / sqrt(ms_act);
319
320 if (
w *
h <= 640 * 480) {
/* in-line "min-smoothing" as in paper */
321 if (x == 0) /* first column */
322 ms_act_prev = (idx_blk > 1 ?
weights[idx_blk - 2] : 0);
323 else /* after first column */
325
326 if (idx_blk > w_blk) /* after the first row and first column */
327 ms_act_prev =
FFMAX(ms_act_prev,
weights[idx_blk - 1 - w_blk]);
/* min (L, T) */
328 if ((idx_blk > 0) && (
weights[idx_blk - 1] > ms_act_prev))
329 weights[idx_blk - 1] = ms_act_prev;
330
331 if ((x +
b >=
w) && (y +
b >=
h) && (idx_blk > w_blk)) {
/* last block in picture */
333 if (
weights[idx_blk] > ms_act_prev)
334 weights[idx_blk] = ms_act_prev;
335 }
336 }
337 } /* for x */
338 } /* for y */
339
340 for (y = idx_blk = 0; y <
h; y +=
b) {
/* calculate sum for luma (Y) XPSNR */
341 for (x = 0; x <
w; x +=
b, idx_blk++) {
342 wsse_luma += sse_luma[idx_blk] *
weights[idx_blk];
343 }
344 }
345 wsse64[0] = (wsse_luma <= 0.0 ? 0 : (uint64_t) (wsse_luma * avg_act + 0.5));
346 } /* b >= 4 */
347
348 for (
c = 0;
c <
s->num_comps;
c++) {
/* finalize WSSE value for each component */
349 const int16_t *p_org = org[
c];
350 const uint32_t s_org = stride_org[
c] /
s->bpp;
351 const int16_t *p_rec = rec[
c];
352 const uint32_t s_rec =
s->plane_width[
c];
353 const uint32_t w_pln =
s->plane_width[
c];
354 const uint32_t h_pln =
s->plane_height[
c];
355
356 if (
b < 4)
/* picture is too small for XPSNR, calculate nonweighted PSNR */
358 p_rec, s_rec,
359 w_pln, h_pln);
360 else if (
c > 0) {
/* b >= 4 so Y XPSNR has already been calculated above */
361 const uint32_t bx = (
b * w_pln) /
w;
362 const uint32_t by = (
b * h_pln) /
h;
/* up to chroma downsampling by 4 */
363 double wsse_chroma = 0.0;
364
365 for (y = idx_blk = 0; y < h_pln; y += by) { /* calc chroma (Cb/Cr) XPSNR */
366 const uint32_t block_height = (y + by > h_pln ? h_pln - y : by);
367
368 for (x = 0; x < w_pln; x += bx, idx_blk++) {
369 const uint32_t block_width = (x + bx > w_pln ? w_pln - x : bx);
370
372 p_rec + y * s_rec + x, s_rec,
373 block_width, block_height) *
weights[idx_blk];
374 }
375 }
376 wsse64[
c] = (wsse_chroma <= 0.0 ? 0 : (uint64_t) (wsse_chroma * avg_act + 0.5));
377 }
378 } /* for c */
379
380 return 0;
381 }
382
384 {
388 char key2[128];
391 } else {
393 }
394 }
395
397 {
400 const uint32_t
w =
s->plane_width [0];
/* luma image width in pixels */
401 const uint32_t
h =
s->plane_height[0];
/* luma image height in pixels */
402 const uint32_t
b =
FFMAX(0, 4 * (
int32_t) (32.0 * sqrt((
double) (
w *
h) / (3840.0 * 2160.0)) + 0.5));
/* block size */
403 const uint32_t w_blk = (
w +
b - 1) /
b;
/* luma width in units of blocks */
404 const uint32_t h_blk = (
h +
b - 1) /
b;
/* luma height in units of blocks */
406 int16_t *porg [3];
407 int16_t *prec [3];
408 uint64_t wsse64 [3] = {0, 0, 0};
410 int c, ret_value, stride_org_bpp;
412
414 return ret_value;
415 if (
ctx->is_disabled || !
ref)
418
419 /* prepare XPSNR calculations: allocate temporary picture and block memory */
424
425 for (
c = 0;
c <
s->num_comps;
c++)
/* create temporal org buffer memory */
427
428 stride_org_bpp = (
s->bpp == 1 ?
s->plane_width[0] :
s->line_sizes[0] /
s->bpp);
429
431 s->buf_org_m1 =
av_calloc(
s->plane_height[0], stride_org_bpp *
sizeof(int16_t));
433 s->buf_org_m2 =
av_calloc(
s->plane_height[0], stride_org_bpp *
sizeof(int16_t));
434
435 if (
s->bpp == 1) {
/* 8 bit */
436 for (
c = 0;
c <
s->num_comps;
c++) {
/* allocate org/rec buffer memory */
437 const int m =
s->line_sizes[
c];
/* master stride */
438 const int r =
ref->linesize[
c];
/* ref/c stride */
439 const int o =
s->plane_width[
c];
/* XPSNR stride */
440
442 s->buf_org[
c] =
av_calloc(
s->plane_width[
c],
s->plane_height[
c] *
sizeof(int16_t));
444 s->buf_rec[
c] =
av_calloc(
s->plane_width[
c],
s->plane_height[
c] *
sizeof(int16_t));
445
446 porg[
c] =
s->buf_org[
c];
447 prec[
c] =
s->buf_rec[
c];
448
449 for (
int y = 0; y <
s->plane_height[
c]; y++) {
450 for (
int x = 0; x <
s->plane_width[
c]; x++) {
451 porg[
c][y * o + x] = (int16_t)
master->data[
c][y * m + x];
452 prec[
c][y * o + x] = (int16_t)
ref->data[
c][y *
r + x];
453 }
454 }
455 }
456 } else { /* 10, 12, 14 bit */
457 for (
c = 0;
c <
s->num_comps;
c++) {
458 porg[
c] = (int16_t *)
master->data[
c];
459 prec[
c] = (int16_t *)
ref->data[
c];
460 }
461 }
462
463 /* extended perceptually weighted peak signal-to-noise ratio (XPSNR) value */
464 ret_value =
get_wsse(
ctx, porg,
s->buf_org_m1,
s->buf_org_m2, prec, wsse64);
465 if ( ret_value < 0 )
466 return ret_value; /* an error here means something went wrong earlier! */
467
468 for (
c = 0;
c <
s->num_comps;
c++) {
469 const double sqrt_wsse = sqrt((
double) wsse64[
c]);
470
472 s->plane_width[
c],
s->plane_height[
c],
473 s->max_error_64, 1
/* single frame */);
474 s->sum_wdist[
c] += sqrt_wsse;
475 s->sum_xpsnr[
c] += cur_xpsnr[
c];
476 s->and_is_inf[
c] &=
isinf(cur_xpsnr[
c]);
477 }
479
480 for (
int j = 0; j <
s->num_comps; j++) {
481 int c =
s->is_rgb ?
s->rgba_map[j] : j;
483 }
484
485 if (
s->stats_file) {
/* print out frame- and component-wise XPSNR averages */
486 fprintf(
s->stats_file,
"n: %4"PRId64
"",
s->num_frames_64);
487
488 for (
c = 0;
c <
s->num_comps;
c++)
489 fprintf(
s->stats_file,
" XPSNR %c: %3.4f",
s->comps[
c], cur_xpsnr[
c]);
490 fprintf(
s->stats_file,
"\n");
491 }
492
494 }
495
497 {
500
501 if (
s->stats_file_str) {
502 if (!strcmp(
s->stats_file_str,
"-"))
/* no stats file, so use stdout */
503 s->stats_file = stdout;
504 else {
506
507 if (!
s->stats_file) {
508 const int err =
AVERROR(errno);
509
512 return err;
513 }
514 }
515 }
516
519
520 for (
c = 0;
c < 3;
c++) {
/* initialize XPSNR data of each color component */
523 s->sum_wdist [
c] = 0.0;
524 s->sum_xpsnr [
c] = 0.0;
525 s->and_is_inf[
c] = 1;
526 }
527
529
530 return 0;
531 }
532
535 #define PF_NOALPHA(suf) AV_PIX_FMT_YUV420##suf, AV_PIX_FMT_YUV422##suf, AV_PIX_FMT_YUV444##suf
536 #define PF_ALPHA(suf) AV_PIX_FMT_YUVA420##suf, AV_PIX_FMT_YUVA422##suf, AV_PIX_FMT_YUVA444##suf
537 #define PF(suf) PF_NOALPHA(suf), PF_ALPHA(suf)
546 };
547
549 {
555
556 if ((
ctx->inputs[0]->w !=
ctx->inputs[1]->w) ||
557 (
ctx->inputs[0]->h !=
ctx->inputs[1]->h)) {
560 }
561 if (
ctx->inputs[0]->format !=
ctx->inputs[1]->format) {
564 }
565
566 s->bpp = (
desc->comp[0].depth <= 8 ? 1 : 2);
567 s->depth =
desc->comp[0].depth;
568 s->max_error_64 = (1 <<
s->depth) - 1;
/* conventional limit */
569 s->max_error_64 *=
s->max_error_64;
570
571 // Avoid division by zero
574
575 s->num_comps = (
desc->nb_components > 3 ? 3 :
desc->nb_components);
576
578 s->comps[0] = (
s->is_rgb ?
'r' :
'y');
579 s->comps[1] = (
s->is_rgb ?
'g' :
'u');
580 s->comps[2] = (
s->is_rgb ?
'b' :
'v');
582
584 s->plane_width [0] =
s->plane_width [3] =
inlink->w;
586 s->plane_height[0] =
s->plane_height[3] =
inlink->h;
587
588 /* XPSNR always operates with 16-bit internal precision */
590 s->dsp.highds_func =
highds;
/* initialize filtering methods */
593
594 return 0;
595 }
596
598 {
605
608
609 outlink->
w = mainlink->
w;
610 outlink->
h = mainlink->
h;
614
617
619
622 av_log(
ctx,
AV_LOG_WARNING,
"not matching timebases found between first input: %d/%d and second input %d/%d, results may be incorrect!\n",
624 ctx->inputs[1]->time_base.num,
ctx->inputs[1]->time_base.den);
625
626 return 0;
627 }
628
630 {
632
634 }
635
637 {
640
641 if (
s->num_frames_64 > 0) {
/* print out overall component-wise mean XPSNR */
642 const double xpsnr_luma =
get_avg_xpsnr(
s->sum_wdist[0],
s->sum_xpsnr[0],
643 s->plane_width[0],
s->plane_height[0],
644 s->max_error_64,
s->num_frames_64);
645 double xpsnr_min = xpsnr_luma;
646
647 /* luma */
650 fprintf(
s->stats_file,
"\nXPSNR average, %"PRId64
" frames",
s->num_frames_64);
651 fprintf(
s->stats_file,
" %c: %3.4f",
s->comps[0], xpsnr_luma);
652 }
653 /* chroma */
654 for (
c = 1;
c <
s->num_comps;
c++) {
656 s->plane_width[
c],
s->plane_height[
c],
657 s->max_error_64,
s->num_frames_64);
658 if (xpsnr_min > xpsnr_chroma)
659 xpsnr_min = xpsnr_chroma;
660
662 if (
s->stats_file &&
s->stats_file != stdout)
663 fprintf(
s->stats_file,
" %c: %3.4f",
s->comps[
c], xpsnr_chroma);
664 }
665 /* print out line break, and minimum XPSNR across the color components */
666 if (
s->num_comps > 1) {
668 if (
s->stats_file &&
s->stats_file != stdout)
669 fprintf(
s->stats_file,
" (minimum: %3.4f)\n", xpsnr_min);
670 } else {
672 if (
s->stats_file &&
s->stats_file != stdout)
673 fprintf(
s->stats_file,
"\n");
674 }
675 }
676
678
679 if (
s->stats_file &&
s->stats_file != stdout)
680 fclose(
s->stats_file);
681
684
687
688 for (
c = 0;
c <
s->num_comps;
c++) {
691 }
692 }
693
695 {
698 }, {
699 .name = "reference",
702 }
703 };
704
706 {
710 }
711 };
712
715 .p.description =
NULL_IF_CONFIG_SMALL(
"Calculate the extended perceptually weighted peak signal-to-noise ratio (XPSNR) between two video streams."),
716 .p.priv_class = &xpsnr_class,
718 .preinit = xpsnr_framesync_preinit,
726 };