2 * Copyright (c) 2011 Roger Pau Monné <roger.pau@entel.upc.edu>
3 * Copyright (c) 2011 Stefano Sabatini
4 * Copyright (c) 2013 Paul B Mahol
6 * This file is part of FFmpeg.
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.
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.
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
25 * Caculate the PSNR between two input videos.
28 #include "libavutil/opt.h"
29 #include "libavutil/pixdesc.h"
31 #include "dualinput.h"
32 #include "drawutils.h"
37 typedef struct PSNRContext
{
39 FFDualInputContext dinput
;
40 double mse
, min_mse
, max_mse
;
44 int max
[4], average_max
;
52 void (*compute_mse
)(struct PSNRContext
*s
,
53 const uint8_t *m
[4], const int ml
[4],
54 const uint8_t *r
[4], const int rl
[4],
55 int w
, int h
, double mse
[4]);
58 #define OFFSET(x) offsetof(PSNRContext, x)
59 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
61 static const AVOption psnr_options
[] = {
62 {"stats_file", "Set file where to store per-frame difference information", OFFSET(stats_file_str
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
63 {"f", "Set file where to store per-frame difference information", OFFSET(stats_file_str
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
67 AVFILTER_DEFINE_CLASS(psnr
);
69 static inline unsigned pow2(unsigned base
)
74 static inline double get_psnr(double mse
, uint64_t nb_frames
, int max
)
76 return 10.0 * log(pow2(max
) / (mse
/ nb_frames
)) / log(10.0);
80 void compute_images_mse(PSNRContext
*s
,
81 const uint8_t *main_data
[4], const int main_linesizes
[4],
82 const uint8_t *ref_data
[4], const int ref_linesizes
[4],
83 int w
, int h
, double mse
[4])
87 for (c
= 0; c
< s
->nb_components
; c
++) {
88 const int outw
= s
->planewidth
[c
];
89 const int outh
= s
->planeheight
[c
];
90 const uint8_t *main_line
= main_data
[c
];
91 const uint8_t *ref_line
= ref_data
[c
];
92 const int ref_linesize
= ref_linesizes
[c
];
93 const int main_linesize
= main_linesizes
[c
];
96 for (i
= 0; i
< outh
; i
++) {
98 for (j
= 0; j
< outw
; j
++)
99 m2
+= pow2(main_line
[j
] - ref_line
[j
]);
101 ref_line
+= ref_linesize
;
102 main_line
+= main_linesize
;
104 mse
[c
] = m
/ (double)(outw
* outh
);
109 void compute_images_mse_16bit(PSNRContext
*s
,
110 const uint8_t *main_data
[4], const int main_linesizes
[4],
111 const uint8_t *ref_data
[4], const int ref_linesizes
[4],
112 int w
, int h
, double mse
[4])
116 for (c
= 0; c
< s
->nb_components
; c
++) {
117 const int outw
= s
->planewidth
[c
];
118 const int outh
= s
->planeheight
[c
];
119 const uint16_t *main_line
= (uint16_t *)main_data
[c
];
120 const uint16_t *ref_line
= (uint16_t *)ref_data
[c
];
121 const int ref_linesize
= ref_linesizes
[c
] / 2;
122 const int main_linesize
= main_linesizes
[c
] / 2;
125 for (i
= 0; i
< outh
; i
++) {
126 for (j
= 0; j
< outw
; j
++)
127 m
+= pow2(main_line
[j
] - ref_line
[j
]);
128 ref_line
+= ref_linesize
;
129 main_line
+= main_linesize
;
131 mse
[c
] = m
/ (double)(outw
* outh
);
135 static void set_meta(AVDictionary
**metadata
, const char *key
, char comp
, float d
)
138 snprintf(value
, sizeof(value
), "%0.2f", d
);
141 snprintf(key2
, sizeof(key2
), "%s%c", key
, comp
);
142 av_dict_set(metadata
, key2
, value
, 0);
144 av_dict_set(metadata
, key
, value
, 0);
148 static AVFrame
*do_psnr(AVFilterContext
*ctx
, AVFrame
*main
,
151 PSNRContext
*s
= ctx
->priv
;
152 double comp_mse
[4], mse
= 0;
154 AVDictionary
**metadata
= avpriv_frame_get_metadatap(main
);
156 s
->compute_mse(s
, (const uint8_t **)main
->data
, main
->linesize
,
157 (const uint8_t **)ref
->data
, ref
->linesize
,
158 main
->width
, main
->height
, comp_mse
);
160 for (j
= 0; j
< s
->nb_components
; j
++)
162 mse
/= s
->nb_components
;
164 s
->min_mse
= FFMIN(s
->min_mse
, mse
);
165 s
->max_mse
= FFMAX(s
->max_mse
, mse
);
170 for (j
= 0; j
< s
->nb_components
; j
++) {
171 c
= s
->is_rgb
? s
->rgba_map
[j
] : j
;
172 set_meta(metadata
, "lavfi.psnr.mse.", s
->comps
[j
], comp_mse
[c
]);
173 set_meta(metadata
, "lavfi.psnr.mse_avg", 0, mse
);
174 set_meta(metadata
, "lavfi.psnr.psnr.", s
->comps
[j
], get_psnr(comp_mse
[c
], 1, s
->max
[c
]));
175 set_meta(metadata
, "lavfi.psnr.psnr_avg", 0, get_psnr(mse
, 1, s
->average_max
));
179 fprintf(s
->stats_file
, "n:%"PRId64
" mse_avg:%0.2f ", s
->nb_frames
, mse
);
180 for (j
= 0; j
< s
->nb_components
; j
++) {
181 c
= s
->is_rgb
? s
->rgba_map
[j
] : j
;
182 fprintf(s
->stats_file
, "mse_%c:%0.2f ", s
->comps
[j
], comp_mse
[c
]);
184 for (j
= 0; j
< s
->nb_components
; j
++) {
185 c
= s
->is_rgb
? s
->rgba_map
[j
] : j
;
186 fprintf(s
->stats_file
, "psnr_%c:%0.2f ", s
->comps
[j
],
187 get_psnr(comp_mse
[c
], 1, s
->max
[c
]));
189 fprintf(s
->stats_file
, "\n");
195 static av_cold
int init(AVFilterContext
*ctx
)
197 PSNRContext
*s
= ctx
->priv
;
199 s
->min_mse
= +INFINITY
;
200 s
->max_mse
= -INFINITY
;
202 if (s
->stats_file_str
) {
203 s
->stats_file
= fopen(s
->stats_file_str
, "w");
204 if (!s
->stats_file
) {
205 int err
= AVERROR(errno
);
207 av_strerror(err
, buf
, sizeof(buf
));
208 av_log(ctx
, AV_LOG_ERROR
, "Could not open stats file %s: %s\n",
209 s
->stats_file_str
, buf
);
214 s
->dinput
.process
= do_psnr
;
218 static int query_formats(AVFilterContext
*ctx
)
220 static const enum PixelFormat pix_fmts
[] = {
221 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GRAY16
,
222 #define PF_NOALPHA(suf) AV_PIX_FMT_YUV420##suf, AV_PIX_FMT_YUV422##suf, AV_PIX_FMT_YUV444##suf
223 #define PF_ALPHA(suf) AV_PIX_FMT_YUVA420##suf, AV_PIX_FMT_YUVA422##suf, AV_PIX_FMT_YUVA444##suf
224 #define PF(suf) PF_NOALPHA(suf), PF_ALPHA(suf)
225 PF(P
), PF(P9
), PF(P10
), PF_NOALPHA(P12
), PF_NOALPHA(P14
), PF(P16
),
226 AV_PIX_FMT_YUV440P
, AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
227 AV_PIX_FMT_YUVJ411P
, AV_PIX_FMT_YUVJ420P
, AV_PIX_FMT_YUVJ422P
,
228 AV_PIX_FMT_YUVJ440P
, AV_PIX_FMT_YUVJ444P
,
229 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRP9
, AV_PIX_FMT_GBRP10
,
230 AV_PIX_FMT_GBRP12
, AV_PIX_FMT_GBRP14
, AV_PIX_FMT_GBRP16
,
231 AV_PIX_FMT_GBRAP
, AV_PIX_FMT_GBRAP16
,
235 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
239 static int config_input_ref(AVFilterLink
*inlink
)
241 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
242 AVFilterContext
*ctx
= inlink
->dst
;
243 PSNRContext
*s
= ctx
->priv
;
246 s
->nb_components
= desc
->nb_components
;
247 if (ctx
->inputs
[0]->w
!= ctx
->inputs
[1]->w
||
248 ctx
->inputs
[0]->h
!= ctx
->inputs
[1]->h
) {
249 av_log(ctx
, AV_LOG_ERROR
, "Width and height of input videos must be same.\n");
250 return AVERROR(EINVAL
);
252 if (ctx
->inputs
[0]->format
!= ctx
->inputs
[1]->format
) {
253 av_log(ctx
, AV_LOG_ERROR
, "Inputs must be of same pixel format.\n");
254 return AVERROR(EINVAL
);
257 switch (inlink
->format
) {
258 case AV_PIX_FMT_GRAY8
:
259 case AV_PIX_FMT_GRAY16
:
260 case AV_PIX_FMT_GBRP
:
261 case AV_PIX_FMT_GBRP9
:
262 case AV_PIX_FMT_GBRP10
:
263 case AV_PIX_FMT_GBRP12
:
264 case AV_PIX_FMT_GBRP14
:
265 case AV_PIX_FMT_GBRP16
:
266 case AV_PIX_FMT_GBRAP
:
267 case AV_PIX_FMT_GBRAP16
:
268 case AV_PIX_FMT_YUVJ411P
:
269 case AV_PIX_FMT_YUVJ420P
:
270 case AV_PIX_FMT_YUVJ422P
:
271 case AV_PIX_FMT_YUVJ440P
:
272 case AV_PIX_FMT_YUVJ444P
:
273 s
->max
[0] = (1 << (desc
->comp
[0].depth_minus1
+ 1)) - 1;
274 s
->max
[1] = (1 << (desc
->comp
[1].depth_minus1
+ 1)) - 1;
275 s
->max
[2] = (1 << (desc
->comp
[2].depth_minus1
+ 1)) - 1;
276 s
->max
[3] = (1 << (desc
->comp
[3].depth_minus1
+ 1)) - 1;
279 s
->max
[0] = 235 * (1 << (desc
->comp
[0].depth_minus1
- 7));
280 s
->max
[1] = 240 * (1 << (desc
->comp
[1].depth_minus1
- 7));
281 s
->max
[2] = 240 * (1 << (desc
->comp
[2].depth_minus1
- 7));
282 s
->max
[3] = (1 << (desc
->comp
[3].depth_minus1
+ 1)) - 1;
285 s
->is_rgb
= ff_fill_rgba_map(s
->rgba_map
, inlink
->format
) >= 0;
286 s
->comps
[0] = s
->is_rgb
? 'r' : 'y' ;
287 s
->comps
[1] = s
->is_rgb
? 'g' : 'u' ;
288 s
->comps
[2] = s
->is_rgb
? 'b' : 'v' ;
291 for (j
= 0; j
< s
->nb_components
; j
++)
292 s
->average_max
+= s
->max
[j
];
293 s
->average_max
/= s
->nb_components
;
295 s
->planeheight
[1] = s
->planeheight
[2] = FF_CEIL_RSHIFT(inlink
->h
, desc
->log2_chroma_h
);
296 s
->planeheight
[0] = s
->planeheight
[3] = inlink
->h
;
297 s
->planewidth
[1] = s
->planewidth
[2] = FF_CEIL_RSHIFT(inlink
->w
, desc
->log2_chroma_w
);
298 s
->planewidth
[0] = s
->planewidth
[3] = inlink
->w
;
300 s
->compute_mse
= desc
->comp
[0].depth_minus1
> 7 ? compute_images_mse_16bit
: compute_images_mse
;
305 static int config_output(AVFilterLink
*outlink
)
307 AVFilterContext
*ctx
= outlink
->src
;
308 PSNRContext
*s
= ctx
->priv
;
309 AVFilterLink
*mainlink
= ctx
->inputs
[0];
312 outlink
->w
= mainlink
->w
;
313 outlink
->h
= mainlink
->h
;
314 outlink
->time_base
= mainlink
->time_base
;
315 outlink
->sample_aspect_ratio
= mainlink
->sample_aspect_ratio
;
316 outlink
->frame_rate
= mainlink
->frame_rate
;
317 if ((ret
= ff_dualinput_init(ctx
, &s
->dinput
)) < 0)
323 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*inpicref
)
325 PSNRContext
*s
= inlink
->dst
->priv
;
326 return ff_dualinput_filter_frame(&s
->dinput
, inlink
, inpicref
);
329 static int request_frame(AVFilterLink
*outlink
)
331 PSNRContext
*s
= outlink
->src
->priv
;
332 return ff_dualinput_request_frame(&s
->dinput
, outlink
);
335 static av_cold
void uninit(AVFilterContext
*ctx
)
337 PSNRContext
*s
= ctx
->priv
;
339 if (s
->nb_frames
> 0) {
340 av_log(ctx
, AV_LOG_INFO
, "PSNR average:%0.2f min:%0.2f max:%0.2f\n",
341 get_psnr(s
->mse
, s
->nb_frames
, s
->average_max
),
342 get_psnr(s
->max_mse
, 1, s
->average_max
),
343 get_psnr(s
->min_mse
, 1, s
->average_max
));
346 ff_dualinput_uninit(&s
->dinput
);
349 fclose(s
->stats_file
);
352 static const AVFilterPad psnr_inputs
[] = {
355 .type
= AVMEDIA_TYPE_VIDEO
,
356 .filter_frame
= filter_frame
,
359 .type
= AVMEDIA_TYPE_VIDEO
,
360 .filter_frame
= filter_frame
,
361 .config_props
= config_input_ref
,
366 static const AVFilterPad psnr_outputs
[] = {
369 .type
= AVMEDIA_TYPE_VIDEO
,
370 .config_props
= config_output
,
371 .request_frame
= request_frame
,
376 AVFilter ff_vf_psnr
= {
378 .description
= NULL_IF_CONFIG_SMALL("Calculate the PSNR between two video streams."),
381 .query_formats
= query_formats
,
382 .priv_size
= sizeof(PSNRContext
),
383 .priv_class
= &psnr_class
,
384 .inputs
= psnr_inputs
,
385 .outputs
= psnr_outputs
,