FFmpeg
Loading...
Searching...
No Matches
vf_xpsnr.c
Go to the documentation of this file.
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
30#include "libavutil/avstring.h"
31#include "libavutil/file_open.h"
32#include "libavutil/mem.h"
33#include "libavutil/opt.h"
34#include "libavutil/pixdesc.h"
35#include "avfilter.h"
36#include "drawutils.h"
37#include "filters.h"
38#include "framesync.h"
39#include "psnr.h"
40#include "xpsnr.h"
41
42/* XPSNR structure definition */
43
44typedef struct XPSNRContext {
45 /* required basic variables */
46 const AVClass *class;
47 int bpp; /* unpacked */
48 int depth; /* packed */
49 char comps[4];
51 uint64_t num_frames_64;
52 unsigned frame_rate;
54 int line_sizes[4];
57 uint8_t rgba_map[4];
60 /* XPSNR specific variables */
61 double *sse_luma;
62 double *weights;
63 int16_t *buf_org_m1;
64 int16_t *buf_org_m2;
65 int16_t *buf_org [3];
66 int16_t *buf_rec [3];
67 uint64_t max_error_64;
68 double sum_wdist [3];
69 double sum_xpsnr [3];
70 int and_is_inf[3];
71 int is_rgb;
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)
80#define XPSNR_GAMMA 2
81
82static const AVOption xpsnr_options[] = {
83 {"stats_file", "Set file where to store per-frame XPSNR information", OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS},
84 {"f", "Set file where to store per-frame XPSNR information", OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS},
85 { NULL }
86};
87
89
90/* XPSNR function definitions */
91
92static 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
112static 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 }
125 return (ta_act * XPSNR_GAMMA);
126}
127
128static 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 }
144 return (ta_act * XPSNR_GAMMA);
145}
146
147static inline uint64_t calc_squared_error(XPSNRContext const *s,
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 */
161 return sse;
162}
163
164static inline double calc_squared_error_and_weight (XPSNRContext const *s,
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
184 const double sse = (double) calc_squared_error (s, o_m0, stride_org,
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 */
191 return sse;
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
222 ta_act += XPSNR_GAMMA * (uint64_t) abs(t);
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
231 ta_act += XPSNR_GAMMA * (uint64_t) abs(t);
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 */
249 return sse;
250}
251
252static 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)
257 return INFINITY;
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
269static int get_wsse(AVFilterContext *ctx, int16_t **org, int16_t *org_m1,
270 int16_t *org_m2, int16_t **rec, uint64_t *const wsse64)
271{
272 XPSNRContext *const s = ctx->priv;
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 */
276 const uint32_t b = FFMAX(1, 4 * (int32_t) (32.0 * sqrt(r) +
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;
284 double *const weights = s->weights;
285 int c;
286
287 if (!wsse64 || (s->depth < 6) || (s->depth > 16) || (s->num_comps <= 0) ||
288 (s->num_comps > 3) || (w == 0) || (h == 0)) {
289 av_log(ctx, AV_LOG_ERROR, "Error in XPSNR routine: invalid argument(s).\n");
290 return AVERROR(EINVAL);
291 }
292 if (!weights || (b >= 4 && !sse_luma)) {
293 av_log(ctx, AV_LOG_ERROR, "Failed to allocate temporary block memory.\n");
294 return AVERROR(ENOMEM);
295 }
296
297 if (b >= 4) {
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
311 sse_luma[idx_blk] = calc_squared_error_and_weight(s, p_org, s_org,
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 */
324 ms_act_prev = (x > b ? FFMAX(weights[idx_blk - 2], weights[idx_blk]) : weights[idx_blk]);
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 */
332 ms_act_prev = FFMAX(weights[idx_blk - 1], weights[idx_blk - w_blk]);
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 */
357 wsse64[c] = calc_squared_error (s, p_org, s_org,
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
371 wsse_chroma += (double) calc_squared_error (s, p_org + y * s_org + x, s_org,
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
383static void set_meta(AVDictionary **metadata, const char *key, char comp, float d)
384{
385 char value[128];
386 snprintf(value, sizeof(value), "%f", d);
387 if (comp) {
388 char key2[128];
389 snprintf(key2, sizeof(key2), "%s%c", key, comp);
390 av_dict_set(metadata, key2, value, 0);
391 } else {
393 }
394}
395
397{
398 AVFilterContext *ctx = fs->parent;
399 XPSNRContext *const s = ctx->priv;
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(1, 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 */
405 AVFrame *master, *ref = NULL;
406 int16_t *porg [3];
407 int16_t *prec [3];
408 uint64_t wsse64 [3] = {0, 0, 0};
409 double cur_xpsnr[3] = {INFINITY, INFINITY, INFINITY};
410 int c, ret_value, stride_org_bpp;
412
413 if ((ret_value = ff_framesync_dualinput_get(fs, &master, &ref)) < 0)
414 return ret_value;
415 if (ctx->is_disabled || !ref)
416 return ff_filter_frame(ctx->outputs[0], master);
417 metadata = &master->metadata;
418
419 /* prepare XPSNR calculations: allocate temporary picture and block memory */
420 if (!s->sse_luma)
421 s->sse_luma = av_malloc_array(w_blk * h_blk, sizeof(double));
422 if (!s->weights)
423 s->weights = av_malloc_array(w_blk * h_blk, sizeof(double));
424
425 for (c = 0; c < s->num_comps; c++) /* create temporal org buffer memory */
426 s->line_sizes[c] = master->linesize[c];
427
428 stride_org_bpp = (s->bpp == 1 ? s->plane_width[0] : s->line_sizes[0] / s->bpp);
429
430 if (!s->buf_org_m1)
431 s->buf_org_m1 = av_calloc(s->plane_height[0], stride_org_bpp * sizeof(int16_t));
432 if (!s->buf_org_m2)
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
441 if (!s->buf_org[c])
442 s->buf_org[c] = av_calloc(s->plane_width[c], s->plane_height[c] * sizeof(int16_t));
443 if (!s->buf_rec[c])
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
471 cur_xpsnr[c] = get_avg_xpsnr (sqrt_wsse, INFINITY,
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 }
478 s->num_frames_64++;
479
480 for (int j = 0; j < s->num_comps; j++) {
481 int c = s->is_rgb ? s->rgba_map[j] : j;
482 set_meta(metadata, "lavfi.xpsnr.xpsnr.", s->comps[j], cur_xpsnr[c]);
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
493 return ff_filter_frame(ctx->outputs[0], master);
494}
495
497{
498 XPSNRContext *const s = ctx->priv;
499 int c;
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 {
505 s->stats_file = avpriv_fopen_utf8(s->stats_file_str, "w");
506
507 if (!s->stats_file) {
508 const int err = AVERROR(errno);
509
510 av_log(ctx, AV_LOG_ERROR, "Could not open statistics file %s: %s\n",
511 s->stats_file_str, av_err2str(err));
512 return err;
513 }
514 }
515 }
516
517 s->sse_luma = NULL;
518 s->weights = NULL;
519
520 for (c = 0; c < 3; c++) { /* initialize XPSNR data of each color component */
521 s->buf_org [c] = NULL;
522 s->buf_rec [c] = NULL;
523 s->sum_wdist [c] = 0.0;
524 s->sum_xpsnr [c] = 0.0;
525 s->and_is_inf[c] = 1;
526 }
527
528 s->fs.on_event = do_xpsnr;
529
530 return 0;
531}
532
533static const enum AVPixelFormat xpsnr_formats[] = {
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)
538 PF(P), PF(P9), PF(P10), PF_NOALPHA(P12), PF_NOALPHA(P14), PF(P16),
546};
547
549{
551 AVFilterContext *ctx = inlink->dst;
552 XPSNRContext *const s = ctx->priv;
553 FilterLink *il = ff_filter_link(inlink);
554 FilterLink *ml = ff_filter_link(ctx->inputs[0]);
555
556 if ((ctx->inputs[0]->w != ctx->inputs[1]->w) ||
557 (ctx->inputs[0]->h != ctx->inputs[1]->h)) {
558 av_log(ctx, AV_LOG_ERROR, "Width and height of the input videos must match.\n");
559 return AVERROR(EINVAL);
560 }
561 if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
562 av_log(ctx, AV_LOG_ERROR, "The input videos must be of the same pixel format.\n");
563 return AVERROR(EINVAL);
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
572 s->frame_rate = il->frame_rate.den ? (il->frame_rate.num / il->frame_rate.den) :
573 ml->frame_rate.den ? (ml->frame_rate.num / ml->frame_rate.den) : 0;
574
575 s->num_comps = (desc->nb_components > 3 ? 3 : desc->nb_components);
576
577 s->is_rgb = (ff_fill_rgba_map(s->rgba_map, inlink->format) >= 0);
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');
581 s->comps[3] = 'a';
582
583 s->plane_width [1] = s->plane_width [2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
584 s->plane_width [0] = s->plane_width [3] = inlink->w;
585 s->plane_height[1] = s->plane_height[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
586 s->plane_height[0] = s->plane_height[3] = inlink->h;
587
588 /* XPSNR always operates with 16-bit internal precision */
589 ff_psnr_init(&s->pdsp, 15);
590 s->dsp.highds_func = highds; /* initialize filtering methods */
591 s->dsp.diff1st_func = diff1st;
592 s->dsp.diff2nd_func = diff2nd;
593
594 return 0;
595}
596
597static int config_output(AVFilterLink *outlink)
598{
599 AVFilterContext *ctx = outlink->src;
600 XPSNRContext *s = ctx->priv;
601 AVFilterLink *mainlink = ctx->inputs[0];
602 FilterLink *il = ff_filter_link(mainlink);
603 FilterLink *ol = ff_filter_link(outlink);
604 int ret;
605
606 if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0)
607 return ret;
608
609 outlink->w = mainlink->w;
610 outlink->h = mainlink->h;
611 outlink->time_base = mainlink->time_base;
612 outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio;
613 ol->frame_rate = il->frame_rate;
614
615 if ((ret = ff_framesync_configure(&s->fs)) < 0)
616 return ret;
617
618 outlink->time_base = s->fs.time_base;
619
620 if (av_cmp_q(mainlink->time_base, outlink->time_base) ||
621 av_cmp_q(ctx->inputs[1]->time_base, outlink->time_base))
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",
623 mainlink->time_base.num, mainlink->time_base.den,
624 ctx->inputs[1]->time_base.num, ctx->inputs[1]->time_base.den);
625
626 return 0;
627}
628
630{
631 XPSNRContext *s = ctx->priv;
632
633 return ff_framesync_activate(&s->fs);
634}
635
637{
638 XPSNRContext *const s = ctx->priv;
639 int c;
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 */
648 av_log(ctx, AV_LOG_INFO, "XPSNR %c: %3.4f", s->comps[0], xpsnr_luma);
649 if (s->stats_file) {
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++) {
655 const double xpsnr_chroma = get_avg_xpsnr(s->sum_wdist[c], s->sum_xpsnr[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
661 av_log(ctx, AV_LOG_INFO, " %c: %3.4f", s->comps[c], xpsnr_chroma);
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) {
667 av_log(ctx, AV_LOG_INFO, " (minimum: %3.4f)\n", xpsnr_min);
668 if (s->stats_file && s->stats_file != stdout)
669 fprintf(s->stats_file, " (minimum: %3.4f)\n", xpsnr_min);
670 } else {
671 av_log(ctx, AV_LOG_INFO, "\n");
672 if (s->stats_file && s->stats_file != stdout)
673 fprintf(s->stats_file, "\n");
674 }
675 }
676
677 ff_framesync_uninit(&s->fs); /* free temporary picture or block buf memory */
678
679 if (s->stats_file && s->stats_file != stdout)
680 fclose(s->stats_file);
681
682 av_freep(&s->sse_luma);
683 av_freep(&s->weights );
684
685 av_freep(&s->buf_org_m1);
686 av_freep(&s->buf_org_m2);
687
688 for (c = 0; c < s->num_comps; c++) {
689 av_freep(&s->buf_org[c]);
690 av_freep(&s->buf_rec[c]);
691 }
692}
693
694static const AVFilterPad xpsnr_inputs[] = {
695 {
696 .name = "main",
697 .type = AVMEDIA_TYPE_VIDEO,
698 }, {
699 .name = "reference",
700 .type = AVMEDIA_TYPE_VIDEO,
701 .config_props = config_input_ref,
702 }
703};
704
705static const AVFilterPad xpsnr_outputs[] = {
706 {
707 .name = "default",
708 .type = AVMEDIA_TYPE_VIDEO,
709 .config_props = config_output,
710 }
711};
712
714 .p.name = "xpsnr",
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,
719 .init = init,
720 .uninit = uninit,
721 .activate = activate,
722 .priv_size = sizeof(XPSNRContext),
726};
static void bit_depth(AudioStatsContext *s, const uint64_t *const mask, uint8_t *depth)
Definition af_astats.c:246
const FFFilter ff_vf_xpsnr
Definition vf_xpsnr.c:713
static AVFormatContext * ctx
int32_t
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
Main libavfilter public API header.
static int FUNC metadata(CodedBitstreamContext *ctx, RWContext *rw, APVRawMetadata *current)
#define f(width, name)
Definition cbs_vp8.c:236
#define s(width, name)
Definition cbs_vp9.c:198
#define fs(width, name, subs,...)
Definition cbs_vp9.c:200
#define FLAGS
Definition cmdutils.c:598
#define AV_CEIL_RSHIFT(a, b)
Definition common.h:60
#define NULL
Definition coverity.c:32
#define abs(x)
int ff_fill_rgba_map(uint8_t *rgba_map, enum AVPixelFormat pix_fmt)
Definition drawutils.c:80
misc drawing utilities
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
static void comp(unsigned char *dst, ptrdiff_t dst_stride, unsigned char *src, ptrdiff_t src_stride, int add)
Definition eamad.c:79
double value
Definition eval.c:102
const char * key
int ff_framesync_configure(FFFrameSync *fs)
Configure a frame sync structure.
Definition framesync.c:137
int ff_framesync_dualinput_get(FFFrameSync *fs, AVFrame **f0, AVFrame **f1)
Definition framesync.c:390
int ff_framesync_activate(FFFrameSync *fs)
Examine the frames in the filter's input and try to produce output.
Definition framesync.c:352
int ff_framesync_init_dualinput(FFFrameSync *fs, AVFilterContext *parent)
Initialize a frame sync structure for dualinput.
Definition framesync.c:372
void ff_framesync_uninit(FFFrameSync *fs)
Free all memory currently allocated.
Definition framesync.c:301
#define FRAMESYNC_DEFINE_CLASS(name, context, field)
Definition framesync.h:352
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
#define AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
Same as AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, except that the filter will have its filter_frame() c...
Definition avfilter.h:204
#define AVFILTER_FLAG_METADATA_ONLY
The filter is a "metadata" filter - it does not modify the frame data in any way.
Definition avfilter.h:182
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition dict.c:86
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition error.h:122
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition rational.h:89
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
static const int weights[]
Definition hevc_pel.c:32
#define r
Definition input.c:42
#define b
Definition input.c:43
static av_cold void uninit(AVBitStreamFilterContext *ctx)
static int activate(AVBitStreamFilterContext *ctx)
static int config_output(AVBitStreamFilterLink *outlink)
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FILTER_PIXFMTS_ARRAY(array)
Definition filters.h:244
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition filters.h:199
#define av_cold
Definition attributes.h:117
FILE * avpriv_fopen_utf8(const char *path, const char *mode)
Open a file using a UTF-8 filename.
Definition file_open.c:160
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
#define isinf(x)
Definition libm.h:319
const char * desc
Definition libsvtav1.c:83
uint8_t w
Definition llvidencdsp.c:39
#define FFMAX(a, b)
Definition macros.h:47
#define INFINITY
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
#define P
static int sse(const MPVEncContext *const s, const uint8_t *src1, const uint8_t *src2, int w, int h, int stride)
AVOptions.
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
#define AV_PIX_FMT_GBRAP12
Definition pixfmt.h:569
#define AV_PIX_FMT_GRAY9
Definition pixfmt.h:524
#define AV_PIX_FMT_GBRAP16
Definition pixfmt.h:571
#define AV_PIX_FMT_GBRP9
Definition pixfmt.h:563
#define AV_PIX_FMT_GBRP10
Definition pixfmt.h:564
#define AV_PIX_FMT_GRAY12
Definition pixfmt.h:526
#define AV_PIX_FMT_GBRP12
Definition pixfmt.h:565
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_YUV440P
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition pixfmt.h:106
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition pixfmt.h:81
@ AV_PIX_FMT_YUVJ440P
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
Definition pixfmt.h:107
@ AV_PIX_FMT_YUV410P
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition pixfmt.h:79
@ AV_PIX_FMT_YUV411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition pixfmt.h:80
@ AV_PIX_FMT_YUVJ411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor ...
Definition pixfmt.h:283
@ AV_PIX_FMT_GBRAP
planar GBRA 4:4:4:4 32bpp
Definition pixfmt.h:212
@ AV_PIX_FMT_YUVJ422P
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition pixfmt.h:86
@ AV_PIX_FMT_GBRP
planar GBR 4:4:4 24bpp
Definition pixfmt.h:165
@ AV_PIX_FMT_YUVJ444P
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition pixfmt.h:87
@ AV_PIX_FMT_YUVJ420P
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition pixfmt.h:85
#define AV_PIX_FMT_GRAY10
Definition pixfmt.h:525
#define AV_PIX_FMT_GRAY14
Definition pixfmt.h:527
#define AV_PIX_FMT_GRAY16
Definition pixfmt.h:528
#define AV_PIX_FMT_GBRAP10
Definition pixfmt.h:568
#define AV_PIX_FMT_GBRP16
Definition pixfmt.h:567
#define AV_PIX_FMT_GBRP14
Definition pixfmt.h:566
void ff_psnr_init(PSNRDSPContext *dsp, int bpp)
Definition psnr.c:58
#define snprintf
Definition snprintf.h:34
Describe the class of an AVClass context structure.
Definition log.h:76
An instance of a filter.
Definition avfilter.h:273
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
Frame sync structure.
Definition framesync.h:168
uint8_t rgba_map[4]
Definition vf_xpsnr.c:57
unsigned frame_rate
Definition vf_xpsnr.c:52
char * stats_file_str
Definition vf_xpsnr.c:59
char comps[4]
Definition vf_xpsnr.c:49
int num_comps
Definition vf_xpsnr.c:50
int and_is_inf[3]
Definition vf_xpsnr.c:70
int16_t * buf_rec[3]
Definition vf_xpsnr.c:66
uint64_t max_error_64
Definition vf_xpsnr.c:67
FFFrameSync fs
Definition vf_xpsnr.c:53
uint64_t num_frames_64
Definition vf_xpsnr.c:51
int plane_height[4]
Definition vf_xpsnr.c:55
int16_t * buf_org_m2
Definition vf_xpsnr.c:64
int line_sizes[4]
Definition vf_xpsnr.c:54
XPSNRDSPContext dsp
Definition vf_xpsnr.c:72
int plane_width[4]
Definition vf_xpsnr.c:56
int16_t * buf_org_m1
Definition vf_xpsnr.c:63
double sum_wdist[3]
Definition vf_xpsnr.c:68
double sum_xpsnr[3]
Definition vf_xpsnr.c:69
double * sse_luma
Definition vf_xpsnr.c:61
int16_t * buf_org[3]
Definition vf_xpsnr.c:65
double * weights
Definition vf_xpsnr.c:62
FILE * stats_file
Definition vf_xpsnr.c:58
PSNRDSPContext pdsp
Definition vf_xpsnr.c:73
#define av_malloc_array(a, b)
#define av_freep(p)
#define av_log(a,...)
int num64
Definition utils.c:186
static int ref[MAX_W *MAX_W]
static int config_input_ref(AVFilterLink *inlink)
Definition vf_corr.c:287
const char * master
Definition vf_curves.c:130
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)
Definition vf_xpsnr.c:92
static uint64_t calc_squared_error(XPSNRContext const *s, const int16_t *blk_org, const uint32_t stride_org, const int16_t *blk_rec, const uint32_t stride_rec, const uint32_t block_width, const uint32_t block_height)
Definition vf_xpsnr.c:147
static double calc_squared_error_and_weight(XPSNRContext const *s, const int16_t *pic_org, const uint32_t stride_org, int16_t *pic_org_m1, int16_t *pic_org_m2, const int16_t *pic_rec, const uint32_t stride_rec, const uint32_t offset_x, const uint32_t offset_y, const uint32_t block_width, const uint32_t block_height, const uint32_t bit_depth, const uint32_t int_frame_rate, double *ms_act)
Definition vf_xpsnr.c:164
#define PF_NOALPHA(suf)
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)
Definition vf_xpsnr.c:128
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)
Definition vf_xpsnr.c:112
static int get_wsse(AVFilterContext *ctx, int16_t **org, int16_t *org_m1, int16_t *org_m2, int16_t **rec, uint64_t *const wsse64)
Definition vf_xpsnr.c:269
static int config_input_ref(AVFilterLink *inlink)
Definition vf_xpsnr.c:548
static void set_meta(AVDictionary **metadata, const char *key, char comp, float d)
Definition vf_xpsnr.c:383
#define PF(suf)
static const AVFilterPad xpsnr_outputs[]
Definition vf_xpsnr.c:705
static int do_xpsnr(FFFrameSync *fs)
Definition vf_xpsnr.c:396
static int activate(AVFilterContext *ctx)
Definition vf_xpsnr.c:629
static av_cold void uninit(AVFilterContext *ctx)
Definition vf_xpsnr.c:636
static double get_avg_xpsnr(const double sqrt_wsse_val, const double sum_xpsnr_val, const uint32_t image_width, const uint32_t image_height, const uint64_t max_error_64, const uint64_t num_frames_64)
Definition vf_xpsnr.c:252
#define OFFSET(x)
Definition vf_xpsnr.c:79
static int config_output(AVFilterLink *outlink)
Definition vf_xpsnr.c:597
#define XPSNR_GAMMA
Definition vf_xpsnr.c:80
static const AVFilterPad xpsnr_inputs[]
Definition vf_xpsnr.c:694
static const AVOption xpsnr_options[]
Definition vf_xpsnr.c:82
static enum AVPixelFormat xpsnr_formats[]
Definition vf_xpsnr.c:533
static double c[64]
Public declaration of DSP context structure of XPSNR measurement filter for FFmpeg.