[FFmpeg-devel] [PATCH] libavfilter: created a new filter that obtains the average peak signal-to-noise ratio (PSNR) of two input video files in YUV format.

Stefano Sabatini stefano.sabatini-lala at poste.it
Tue Jun 7 01:47:03 CEST 2011


On date Monday 2011-06-06 15:49:46 +0200, Roger Pau Monné encoded:
> 2011/6/6 Stefano Sabatini <stefano.sabatini-lala at poste.it>:
> > On date Sunday 2011-06-05 21:00:35 +0200, Roger Pau Monn� encoded:
> >>
> >> Signed-off-by: Roger Pau Monn? <roger.pau at entel.upc.edu>
> >> ---
> >>  libavfilter/vf_psnr.c |  272 +++++++++++++++++++++++++++++++++++++++++++++++++
> >>  1 files changed, 272 insertions(+), 0 deletions(-)
> >>  create mode 100644 libavfilter/vf_psnr.c
> >>
> >> diff --git a/libavfilter/vf_psnr.c b/libavfilter/vf_psnr.c
> >> new file mode 100644
> >> index 0000000..c6a0b78
> >> --- /dev/null
> >> +++ b/libavfilter/vf_psnr.c
> >> @@ -0,0 +1,272 @@
> >> +/*
> >> + * Copyright (c) 2011 Roger Pau Monné <roger.pau at entel.upc.edu>
> >> + *
> >> + * This file is part of FFmpeg.
> >> + *
> >> + * FFmpeg is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2.1 of the License, or (at your option) any later version.
> >> + *
> >> + * FFmpeg is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser General Public
> >> + * License along with FFmpeg; if not, write to the Free Software
> >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> >> + */
> >> +
> >> +/**
> >> + * @file
> >> + * Caculate the PSNR between two input videos
> >> + * Based on the overlay filter
> >> + */
> >> +
> >> +#include "libavutil/pixdesc.h"
> >> +#include "avfilter.h"
> >> +
> >> +#undef fprintf
> >> +
> >> +typedef struct {
> >> +    AVFilterBufferRef *picref;
> >> +    double mse, min_mse, max_mse;
> >> +    int nb_frames;
> >> +    FILE *vstats_file;
> >> +    uint16_t *line1, *line2;
> >> +} PSNRContext;
> >> +
> >> +static inline int pow2(int base)
> >> +{
> >> +    return base*base;
> >> +}
> >> +
> >> +static inline double psnr(double mse, int nb_frames, int max)
> >> +{
> >> +    return 10.0*log((pow2(max))/(mse/nb_frames))/log(10.0);
> >> +}
> >> +
> >> +static inline void av_compute_images_mse(const uint8_t *ref_data[4], const uint8_t *data[4], const int linesizes[4],
> >> +                                         int w, int h, const AVPixFmtDescriptor *desc, double mse[4], uint16_t *line1,
> >> +                                         uint16_t *line2)
> >
> > av_ is not required
> >
> >> +{
> >> +    int i, c, j = w;
> >> +
> >> +    memset(mse, 0, sizeof(*mse)*4);
> >> +
> >> +    for (c = 0; c < desc->nb_components; c++) {
> >> +        int w1 = c == 1 || c == 2 ? w>>desc->log2_chroma_w : w;
> >> +        int h1 = c == 1 || c == 2 ? h>>desc->log2_chroma_h : h;
> >> +
> >> +        for (i = 0; i < h1; i++) {
> >> +            av_read_image_line(line1,
> >> +                                ref_data,
> >> +                                linesizes,
> >> +                                desc,
> >> +                                0, i, c, w1, 0);
> >> +            av_read_image_line(line2,
> >> +                                data,
> >> +                                linesizes,
> >> +                                desc,
> >> +                                0, i, c, w1, 0);
> >> +            for(j = 0; j < w1; j++)
> >> +                mse[c] += pow2(line1[j] - line2[j]);
> >> +        }
> >> +        mse[c] /= w1*h1;
> >> +    }
> >> +}
> >> +
> >> +
> >> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> >> +{
> >> +    PSNRContext *psnr_context = ctx->priv;
> >
> > You can only use "psnr" here and below and save some chars, as it is
> > done in the other filters (and move psnr() -> get_psnr() or
> > compute_psnr()).
> >
> >> +
> >> +    psnr_context->mse = psnr_context->nb_frames = 0;
> >> +    psnr_context->min_mse = psnr_context->max_mse = -1.0;
> >> +    psnr_context->picref = NULL;
> >> +
> >> +    if (args != NULL && strlen(args) > 0) {
> >> +        psnr_context->vstats_file = fopen(args, "w");
> >> +        if (!psnr_context->vstats_file) {
> >> +            av_log(ctx, AV_LOG_ERROR, "Could not open stats file %s\n", args);
> >
> > Here you can use strerr(errno) for improving feedback:
> >            av_log(ctx, AV_LOG_ERROR, "Could not open stats file %s: %s\n", args, strerror(errno);
> >
> >> +            return AVERROR(EINVAL);
> >> +        }
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static av_cold void uninit(AVFilterContext *ctx)
> >> +{
> >> +    PSNRContext *psnr_context = ctx->priv;
> >> +
> >> +    av_log(ctx, AV_LOG_INFO, "PSNR average:%0.2fdB min:%0.2fdB max:%0.2fdB\n",
> >> +                                psnr(psnr_context->mse, psnr_context->nb_frames, 255),
> >> +                                psnr(psnr_context->max_mse, 1, 255),
> >> +                                psnr(psnr_context->min_mse, 1, 255));
> >> +
> >> +    if (psnr_context->picref) {
> >> +        avfilter_unref_buffer(psnr_context->picref);
> >> +        psnr_context->picref = NULL;
> >> +    }
> >
> >> +    if (psnr_context->line1)
> >> +        av_free(psnr_context->line1);
> >> +    if (psnr_context->line2)
> >> +        av_free(psnr_context->line2);
> >
> > av_freep
> >
> >> +    if (psnr_context->vstats_file)
> >> +        fclose(psnr_context->vstats_file);
> >> +}
> >> +
> >> +static int config_input_ref(AVFilterLink *inlink)
> >> +{
> >> +    AVFilterContext *ctx  = inlink->dst;
> >> +    PSNRContext *psnr_context = ctx->priv;
> >> +
> >> +    if (ctx->inputs[0]->w != ctx->inputs[1]->w || ctx->inputs[0]->h != ctx->inputs[1]->h) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Width and/or heigth of input videos are different, could not calculate PSNR\n");
> >> +        return AVERROR(EINVAL);
> >> +    }
> >> +    if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Input filters have different pixel formats, could not calculate PSNR\n");
> >> +        return AVERROR(EINVAL);
> >> +    }
> >> +
> >> +    if (!(psnr_context->line1 = av_malloc(sizeof(*psnr_context->line1) * inlink->w)) || !(psnr_context->line2 = av_malloc(sizeof(*psnr_context->line2) * inlink->w)))
> >> +        return AVERROR(ENOMEM);
> >> +
> >
> >> +    av_log(ctx, AV_LOG_INFO, "PSNR filter started correctly\n");
> >
> > please remove this
> >
> >> +
> >> +    return 0;
> >> +}
> >> +
> >
> >> +static int config_output(AVFilterLink *outlink)
> >> +{
> >> +    AVFilterContext *ctx = outlink->src;
> >> +
> >> +    outlink->time_base = outlink->src->inputs[0]->time_base;
> >> +    outlink->w = ctx->inputs[0]->w;
> >> +    outlink->h = ctx->inputs[0]->h;
> >> +    return 0;
> >> +}
> >
> > This should not be required IIRC.
> >
> >> +
> >> +static int query_formats(AVFilterContext *ctx)
> >> +{
> >> +    static const enum PixelFormat pix_fmts[] = {
> >> +        PIX_FMT_YUV444P,  PIX_FMT_YUV422P,  PIX_FMT_YUV420P,
> >> +        PIX_FMT_YUV411P,  PIX_FMT_YUV410P,
> >> +        PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P,
> >> +        PIX_FMT_YUV440P,  PIX_FMT_YUVJ440P

Missing trailing PIX_FMT_NONE, that was causing weird crashes.

> >> +    };
> >> +
> >> +    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
> >> +
> >> +    return 0;
> >
> > Would be feasible to extend the filter to all formats? compute_images_mse()
> > should be generic enough, and you would only need to set the max
> > value. Anyway this should not block the patch.
> 

> I'm not sure about how to obtain the max value of pixel formats, if
> someone knows it, I will gladly expand the filter to compute the PSNR
> for other formats.

We don't have this information, but we need to get it from each filter
(check for example my recently posted lut filter).

[...]
+ at section psnr
+
+Obtain the PSNR between two input videos. Both video files must have
+the same resolution and pixel format for this filter to work correctly.

It is not clear what the output is, and also where and how the PSNR
is computed and printed.

+
+ at table @option
+ at item vstats
+file used to save the PSNR of each individual frame (optional).

This filter accepts a parameter which specifies the file ...
If not specified the filter will not print any file.

+
+For example:
+ at example
+movie=ref_movie.mpg, setpts=PTS-STARTPTS [ref]; [in] setpts=PTS-STARTPTS,
+[ref] psnr=stats.log [out]
+ at end example

Add some words for describing what this filterchain accomplishes.
[...]

I'm attaching an edited version of your patch with some nits addressed
(mainly style fixes), please continue to edit on this version.
-- 
FFmpeg = Freak Formidable Mastodontic Portable Efficient Guide
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-lavfi-add-psnr-filter.patch
Type: text/x-diff
Size: 12320 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20110607/261c516b/attachment.bin>


More information about the ffmpeg-devel mailing list