diff mbox

[FFmpeg-devel] avfilter: add ANSNR filter

Message ID 1499622173-23084-1-git-send-email-ashk43712@gmail.com
State Superseded
Headers show

Commit Message

Ashish Singh July 9, 2017, 5:42 p.m. UTC
Hi, added metadata scores and changed multipe string comparisons to few integer 
comparisons.

---
 Changelog                |   1 +
 doc/filters.texi         |  20 +++
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/ansnr.h      |  29 ++++
 libavfilter/vf_ansnr.c   | 416 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 468 insertions(+)
 create mode 100644 libavfilter/ansnr.h
 create mode 100644 libavfilter/vf_ansnr.c

Comments

Tobias Rapp July 10, 2017, 9:02 a.m. UTC | #1
On 09.07.2017 19:42, Ashish Singh wrote:
> Hi, added metadata scores and changed multipe string comparisons to few integer
> comparisons.
>
> ---
>  Changelog                |   1 +
>  doc/filters.texi         |  20 +++
>  libavfilter/Makefile     |   1 +
>  libavfilter/allfilters.c |   1 +
>  libavfilter/ansnr.h      |  29 ++++
>  libavfilter/vf_ansnr.c   | 416 +++++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 468 insertions(+)
>  create mode 100644 libavfilter/ansnr.h
>  create mode 100644 libavfilter/vf_ansnr.c
>
> diff --git a/Changelog b/Changelog
> index 1778980..bfe848a 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -10,6 +10,7 @@ version <next>:
>  - config.log and other configuration files moved into ffbuild/ directory
>  - update cuvid/nvenc headers to Video Codec SDK 8.0.14
>  - afir audio filter
> +- ansnr video filter
>
>  version 3.3:
>  - CrystalHD decoder moved to new decode API
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 5985db6..7a0856b 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -4419,6 +4419,26 @@ input reaches end of stream. This will cause problems if your encoding
>  pipeline drops frames. If you're trying to apply an image as an
>  overlay to a video stream, consider the @var{overlay} filter instead.
>
> +@section ansnr
> +
> +Obtain the average ANSNR (Anti-Noise Signal to Noise
> +Ratio) between two input videos.
> +
> +This filter takes in input two input videos.
> +
> +Both video inputs must have the same resolution and pixel format for
> +this filter to work correctly. Also it assumes that both inputs
> +have the same number of frames, which are compared one by one.
> +
> +The obtained average ANSNR is printed through the logging system.
> +
> +In the below example the input file @file{main.mpg} being processed is compared
> +with the reference file @file{ref.mpg}.
> +
> +@example
> +ffmpeg -i main.mpg -i ref.mpg -lavfi ansnr -f null -
> +@end example
> +
>  @section ass
>
>  Same as the @ref{subtitles} filter, except that it doesn't require libavcodec
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index f7dfe8a..705e5a1 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -124,6 +124,7 @@ OBJS-$(CONFIG_ANULLSINK_FILTER)              += asink_anullsink.o
>  # video filters
>  OBJS-$(CONFIG_ALPHAEXTRACT_FILTER)           += vf_extractplanes.o
>  OBJS-$(CONFIG_ALPHAMERGE_FILTER)             += vf_alphamerge.o
> +OBJS-$(CONFIG_ANSNR_FILTER)                  += vf_ansnr.o dualinput.o framesync.o
>  OBJS-$(CONFIG_ASS_FILTER)                    += vf_subtitles.o
>  OBJS-$(CONFIG_ATADENOISE_FILTER)             += vf_atadenoise.o
>  OBJS-$(CONFIG_AVGBLUR_FILTER)                += vf_avgblur.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index cd35ae4..c1f67c4 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -136,6 +136,7 @@ static void register_all(void)
>
>      REGISTER_FILTER(ALPHAEXTRACT,   alphaextract,   vf);
>      REGISTER_FILTER(ALPHAMERGE,     alphamerge,     vf);
> +    REGISTER_FILTER(ANSNR,          ansnr,          vf);
>      REGISTER_FILTER(ASS,            ass,            vf);
>      REGISTER_FILTER(ATADENOISE,     atadenoise,     vf);
>      REGISTER_FILTER(AVGBLUR,        avgblur,        vf);
> diff --git a/libavfilter/ansnr.h b/libavfilter/ansnr.h
> new file mode 100644
> index 0000000..44fb3ba
> --- /dev/null
> +++ b/libavfilter/ansnr.h
> @@ -0,0 +1,29 @@
> +/*
> + * Copyright (c) 2017 Ronald S. Bultje <rsbultje@gmail.com>
> + * Copyright (c) 2017 Ashish Pratap Singh <ashk43712@gmail.com>
> + *
> + * 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
> + */
> +
> +#ifndef AVFILTER_ANSNR_H
> +#define AVFILTER_ANSNR_H
> +
> +static int compute_ansnr(const void *ref, const void *dis, int w,
> +                         int h, int ref_stride, int dis_stride, double *score,
> +                         double *score_psnr, double peak, double psnr_max, void *ctx);
> +
> +#endif /* AVFILTER_ANSNR_H */
> diff --git a/libavfilter/vf_ansnr.c b/libavfilter/vf_ansnr.c
> new file mode 100644
> index 0000000..78c71e1
> --- /dev/null
> +++ b/libavfilter/vf_ansnr.c
> @@ -0,0 +1,416 @@
> +/*
> + * Copyright (c) 2017 Ronald S. Bultje <rsbultje@gmail.com>
> + * Copyright (c) 2017 Ashish Pratap Singh <ashk43712@gmail.com>
> + *
> + * 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
> + * Calculate Anti-Noise Singnal to Noise Ratio (ANSNR) between two input videos.
> + */
> +
> +#include <inttypes.h>
> +#include "libavutil/avstring.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/pixdesc.h"
> +#include "avfilter.h"
> +#include "dualinput.h"
> +#include "drawutils.h"
> +#include "formats.h"
> +#include "internal.h"
> +#include "ansnr.h"
> +#include "video.h"
> +
> +typedef struct ANSNRContext {
> +    const AVClass *class;
> +    FFDualInputContext dinput;
> +    int width;
> +    int height;
> +    char *format;

The format field seems unused now and can be removed.

> +    uint8_t type;
> +    float *data_buf;
> +    double ansnr_sum;
> +    uint64_t nb_frames;
> +} ANSNRContext;
> +
> +#define OFFSET(x) offsetof(ANSNRContext, x)
> +#define MAX_ALIGN 32
> +#define ALIGN_CEIL(x) ((x) + ((x) % MAX_ALIGN ? MAX_ALIGN - (x) % MAX_ALIGN : 0))
> +#define OPT_RANGE_PIXEL_OFFSET (-128)
> +
> +const int ansnr_filter2d_ref_width = 3;
> +const int ansnr_filter2d_dis_width = 5;
> +const float ansnr_filter2d_ref[3 * 3] = {
> +    1.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0,
> +    2.0 / 16.0, 4.0 / 16.0, 2.0 / 16.0,
> +    1.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0
> +};
> +const float ansnr_filter2d_dis[5 * 5] = {
> +     2.0 / 571.0,  7.0 / 571.0,  12.0 / 571.0,  7.0 / 571.0,  2.0 / 571.0,
> +     7.0 / 571.0, 31.0 / 571.0,  52.0 / 571.0, 31.0 / 571.0,  7.0 / 571.0,
> +    12.0 / 571.0, 52.0 / 571.0, 127.0 / 571.0, 52.0 / 571.0, 12.0 / 571.0,
> +     7.0 / 571.0, 31.0 / 571.0,  52.0 / 571.0, 31.0 / 571.0,  7.0 / 571.0,
> +     2.0 / 571.0,  7.0 / 571.0,  12.0 / 571.0,  7.0 / 571.0,  2.0 / 571.0
> +};
> +
> +static const AVOption ansnr_options[] = {
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(ansnr);
> +
> +static inline double get_ansnr_avg(double ansnr_sum, uint64_t nb_frames)
> +{
> +    return ansnr_sum / nb_frames;
> +}
> +
> +static inline float pow_2(float base)
> +{
> +    return base*base;
> +}
> +
> +static void ansnr_mse(float *ref, float *dis, float *signal, float *noise,
> +                      int w, int h, int ref_stride, int dis_stride)
> +{
> +    int i, j;
> +
> +    int ref_ind;
> +    int dis_ind;
> +
> +    float signal_sum = 0;
> +    float noise_sum = 0;
> +
> +    for (i = 0; i < h; i++) {
> +        for (j = 0; j < w; j++) {
> +            ref_ind = i * ref_stride + j;
> +            dis_ind = i * dis_stride + j;
> +
> +            signal_sum += pow_2(ref[ref_ind]);
> +            noise_sum += pow_2(ref[ref_ind] - dis[dis_ind]);
> +        }
> +    }
> +
> +    if (signal) {
> +        *signal = signal_sum;
> +    }
> +    if (noise) {
> +        *noise = noise_sum;
> +    }
> +}
> +
> +static void ansnr_filter2d(const float *filt, const void *src, float *dst,
> +                           int w, int h, int src_stride, int dst_stride,
> +                           int filt_width, ANSNRContext *s)
> +{
> +    uint8_t sz;
> +
> +    uint8_t *src_8bit = (uint8_t *) src;
> +    uint16_t *src_10bit = (uint16_t *) src;

Nit: Adding "const" on the left and right side of these two lines 
silences some compiler warnings (gcc: "cast discards ‘const’ qualifier 
from pointer target type").

> +
> +    int src_px_stride;
> +
> +    float filt_coeff, img_coeff;
> +    int i, j, filt_i, filt_j, src_i, src_j;
> +
> +    if (s->type == 8) {
> +        sz = sizeof(uint8_t);
> +    }
> +    else if (s->type == 10) {
> +        sz = sizeof(uint16_t);
> +    }
> +
> +    src_px_stride = src_stride / sizeof(sz);
> +
> +    for (i = 0; i < h; ++i) {
> +        for (j = 0; j < w; ++j) {
> +            float accum = 0;
> +            for (filt_i = 0; filt_i < filt_width; filt_i++) {
> +                for (filt_j = 0; filt_j < filt_width; filt_j++) {
> +                    filt_coeff = filt[filt_i * filt_width + filt_j];
> +
> +                    src_i = i - filt_width / 2 + filt_i;
> +                    src_j = j - filt_width / 2 + filt_j;
> +
> +                    src_i = FFABS(src_i);
> +                    if (src_i >= h) {
> +                        src_i = 2 * h - src_i - 1;
> +                    }
> +                    src_j = FFABS(src_j);
> +                    if (src_j >= w) {
> +                        src_j = 2 * w - src_j - 1;
> +                    }
> +
> +                    if (s->type == 8) {
> +                        img_coeff = src_8bit[src_i * src_px_stride + src_j] +
> +                            OPT_RANGE_PIXEL_OFFSET;
> +                    } else {
> +                        img_coeff = src_10bit[src_i * src_px_stride + src_j] +
> +                            OPT_RANGE_PIXEL_OFFSET;
> +                    }
> +
> +                    accum += filt_coeff * img_coeff;
> +                }
> +            }
> +            dst[i * dst_stride + j] = accum;
> +        }
> +    }
> +}
> +
> +static int compute_ansnr(const void *ref, const void *dis, int w, int h,
> +                         int ref_stride, int dis_stride, double *score,
> +                         double *score_psnr, double peak, double psnr_max,
> +                         void *ctx)
> +{
> +    ANSNRContext *s = (ANSNRContext *) ctx;
> +
> +    char *data_top;
> +
> +    float *ref_filt;
> +    float *dis_filt;
> +
> +    float signal, noise;
> +
> +    int buf_stride = ALIGN_CEIL(w * sizeof(float));
> +    size_t buf_sz = (size_t) (buf_stride * h);
> +
> +    double eps = 1e-10;
> +
> +    data_top = (float *) (s->data_buf);
> +
> +    ref_filt = (float *) data_top;
> +    data_top += buf_sz;
> +    dis_filt = (float *) data_top;
> +    data_top += buf_sz;
> +
> +    buf_stride = buf_stride / sizeof(float);
> +
> +    ansnr_filter2d(ansnr_filter2d_ref, ref, ref_filt, w, h, ref_stride,
> +                   buf_stride, ansnr_filter2d_ref_width, s);
> +    ansnr_filter2d(ansnr_filter2d_dis, dis, dis_filt, w, h, dis_stride,
> +                   buf_stride, ansnr_filter2d_dis_width, s);
> +
> +    ansnr_mse(ref_filt, dis_filt, &signal, &noise, w, h, buf_stride,
> +              buf_stride);
> +
> +    *score = (noise==0) ? (psnr_max) : (10.0 * log10(signal / noise));
> +
> +    *score_psnr = FFMIN(10.0 * log10(pow_2(peak) * w * h / FFMAX(noise, eps)),
> +                        psnr_max);
> +
> +    return 0;
> +}
> +
> +static void set_meta(AVDictionary **metadata, const char *key, char comp, float d)
> +{
> +    char value[128];
> +    snprintf(value, sizeof(value), "%0.2f", d);
> +    if (comp) {
> +        char key2[128];
> +        snprintf(key2, sizeof(key2), "%s%c", key, comp);
> +        av_dict_set(metadata, key2, value, 0);

The comp argument seems unused and could be removed together with this 
if-branch.

> +    } else {
> +        av_dict_set(metadata, key, value, 0);
> +    }
> +}
> +
> +static AVFrame *do_ansnr(AVFilterContext *ctx, AVFrame *main, const AVFrame *ref)
> +{
> +    ANSNRContext *s = ctx->priv;
> +    AVDictionary **metadata = &main->metadata;
> +
> +    char *format = s->format;

Unused variable.

> +
> +    double score = 0.0;
> +    double score_psnr = 0.0;
> +
> +    int w = s->width;
> +    int h = s->height;
> +
> +    double stride;
> +
> +    double max_psnr;
> +    double peak;
> +
> +    uint8_t sz;
> +
> +    if (s->type == 8) {
> +        peak = 255.0;
> +        max_psnr = 60.0;
> +        sz = sizeof(uint8_t);
> +    }
> +    else if (s->type == 10) {
> +        peak = 255.75;
> +        max_psnr = 72.0;
> +        sz = sizeof(uint16_t);
> +    }
> +
> +    stride = ALIGN_CEIL(w * sz);
> +
> +    compute_ansnr(ref->data[0], main->data[0], w, h, stride, stride, &score,
> +                  &score_psnr, peak, max_psnr, s);
> +
> +    set_meta(metadata, "lavfi.ansnr.score", 0, score);
> +
> +    s->nb_frames++;
> +
> +    s->ansnr_sum += score;
> +
> +    return main;
> +}
> +
> +static av_cold int init(AVFilterContext *ctx)
> +{
> +    ANSNRContext *s = ctx->priv;
> +
> +    s->dinput.process = do_ansnr;
> +
> +    return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum AVPixelFormat pix_fmts[] = {
> +        AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P,
> +        AV_PIX_FMT_YUV444P10LE, AV_PIX_FMT_YUV422P10LE, AV_PIX_FMT_YUV420P10LE,
> +        AV_PIX_FMT_NONE
> +    };
> +
> +    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
> +    if (!fmts_list)
> +        return AVERROR(ENOMEM);
> +    return ff_set_common_formats(ctx, fmts_list);
> +}
> +
> +static int config_input_ref(AVFilterLink *inlink)
> +{
> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
> +    AVFilterContext *ctx  = inlink->dst;
> +    ANSNRContext *s = ctx->priv;
> +    int buf_stride;
> +    size_t buf_sz;
> +
> +    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 height of input videos must be same.\n");
> +        return AVERROR(EINVAL);
> +    }
> +    if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
> +        av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    s->width = ctx->inputs[0]->w;
> +    s->height = ctx->inputs[0]->h;
> +    s->format = av_get_pix_fmt_name(ctx->inputs[0]->format);

Unused field (see above).

> +
> +    buf_stride = ALIGN_CEIL(s->width * sizeof(float));
> +    buf_sz = (size_t)buf_stride * s->height;
> +
> +    if (SIZE_MAX / buf_sz < 3) {
> +        av_log(ctx, AV_LOG_ERROR, "insufficient size.\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (!(s->data_buf = av_malloc(buf_sz * 3))) {
> +        av_log(ctx, AV_LOG_ERROR, "data_buf allocation failed.\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    s->type = desc->comp[0].depth > 8 ? 10 : 8;
> +
> +    return 0;
> +}
> +
> +
> +static int config_output(AVFilterLink *outlink)
> +{
> +    AVFilterContext *ctx = outlink->src;
> +    ANSNRContext *s = ctx->priv;
> +    AVFilterLink *mainlink = ctx->inputs[0];
> +    int ret;
> +
> +    outlink->w = mainlink->w;
> +    outlink->h = mainlink->h;
> +    outlink->time_base = mainlink->time_base;
> +    outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio;
> +    outlink->frame_rate = mainlink->frame_rate;
> +    if ((ret = ff_dualinput_init(ctx, &s->dinput)) < 0)
> +        return ret;
> +
> +    return 0;
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref)
> +{
> +    ANSNRContext *s = inlink->dst->priv;
> +    return ff_dualinput_filter_frame(&s->dinput, inlink, inpicref);
> +}
> +
> +static int request_frame(AVFilterLink *outlink)
> +{
> +    ANSNRContext *s = outlink->src->priv;
> +    return ff_dualinput_request_frame(&s->dinput, outlink);
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    ANSNRContext *s = ctx->priv;
> +
> +    ff_dualinput_uninit(&s->dinput);
> +
> +    av_free(s->data_buf);
> +
> +    av_log(ctx, AV_LOG_INFO, "ANSNR AVG: %.3f\n", get_ansnr_avg(s->ansnr_sum,
> +                                                                s->nb_frames));
> +}
> +
> +static const AVFilterPad ansnr_inputs[] = {
> +    {
> +        .name         = "main",
> +        .type         = AVMEDIA_TYPE_VIDEO,
> +        .filter_frame = filter_frame,
> +    },{
> +        .name         = "reference",
> +        .type         = AVMEDIA_TYPE_VIDEO,
> +        .filter_frame = filter_frame,
> +        .config_props = config_input_ref,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad ansnr_outputs[] = {
> +    {
> +        .name          = "default",
> +        .type          = AVMEDIA_TYPE_VIDEO,
> +        .config_props  = config_output,
> +        .request_frame = request_frame,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter ff_vf_ansnr = {
> +    .name          = "ansnr",
> +    .description   = NULL_IF_CONFIG_SMALL("Calculate the ANSNR between two video streams."),
> +    .init          = init,
> +    .uninit        = uninit,
> +    .query_formats = query_formats,
> +    .priv_size     = sizeof(ANSNRContext),
> +    .priv_class    = &ansnr_class,
> +    .inputs        = ansnr_inputs,
> +    .outputs       = ansnr_outputs,
> +};
>

BTW: Some lines contain trailing whitespace which should be clean up.

Best regards,
Tobias
diff mbox

Patch

diff --git a/Changelog b/Changelog
index 1778980..bfe848a 100644
--- a/Changelog
+++ b/Changelog
@@ -10,6 +10,7 @@  version <next>:
 - config.log and other configuration files moved into ffbuild/ directory
 - update cuvid/nvenc headers to Video Codec SDK 8.0.14
 - afir audio filter
+- ansnr video filter
 
 version 3.3:
 - CrystalHD decoder moved to new decode API
diff --git a/doc/filters.texi b/doc/filters.texi
index 5985db6..7a0856b 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -4419,6 +4419,26 @@  input reaches end of stream. This will cause problems if your encoding
 pipeline drops frames. If you're trying to apply an image as an
 overlay to a video stream, consider the @var{overlay} filter instead.
 
+@section ansnr
+
+Obtain the average ANSNR (Anti-Noise Signal to Noise
+Ratio) between two input videos.
+
+This filter takes in input two input videos.
+
+Both video inputs must have the same resolution and pixel format for
+this filter to work correctly. Also it assumes that both inputs
+have the same number of frames, which are compared one by one.
+
+The obtained average ANSNR is printed through the logging system.
+
+In the below example the input file @file{main.mpg} being processed is compared
+with the reference file @file{ref.mpg}.
+
+@example
+ffmpeg -i main.mpg -i ref.mpg -lavfi ansnr -f null -
+@end example
+
 @section ass
 
 Same as the @ref{subtitles} filter, except that it doesn't require libavcodec
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index f7dfe8a..705e5a1 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -124,6 +124,7 @@  OBJS-$(CONFIG_ANULLSINK_FILTER)              += asink_anullsink.o
 # video filters
 OBJS-$(CONFIG_ALPHAEXTRACT_FILTER)           += vf_extractplanes.o
 OBJS-$(CONFIG_ALPHAMERGE_FILTER)             += vf_alphamerge.o
+OBJS-$(CONFIG_ANSNR_FILTER)                  += vf_ansnr.o dualinput.o framesync.o
 OBJS-$(CONFIG_ASS_FILTER)                    += vf_subtitles.o
 OBJS-$(CONFIG_ATADENOISE_FILTER)             += vf_atadenoise.o
 OBJS-$(CONFIG_AVGBLUR_FILTER)                += vf_avgblur.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index cd35ae4..c1f67c4 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -136,6 +136,7 @@  static void register_all(void)
 
     REGISTER_FILTER(ALPHAEXTRACT,   alphaextract,   vf);
     REGISTER_FILTER(ALPHAMERGE,     alphamerge,     vf);
+    REGISTER_FILTER(ANSNR,          ansnr,          vf);
     REGISTER_FILTER(ASS,            ass,            vf);
     REGISTER_FILTER(ATADENOISE,     atadenoise,     vf);
     REGISTER_FILTER(AVGBLUR,        avgblur,        vf);
diff --git a/libavfilter/ansnr.h b/libavfilter/ansnr.h
new file mode 100644
index 0000000..44fb3ba
--- /dev/null
+++ b/libavfilter/ansnr.h
@@ -0,0 +1,29 @@ 
+/*
+ * Copyright (c) 2017 Ronald S. Bultje <rsbultje@gmail.com>
+ * Copyright (c) 2017 Ashish Pratap Singh <ashk43712@gmail.com>
+ *
+ * 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
+ */
+
+#ifndef AVFILTER_ANSNR_H
+#define AVFILTER_ANSNR_H
+
+static int compute_ansnr(const void *ref, const void *dis, int w,
+                         int h, int ref_stride, int dis_stride, double *score,
+                         double *score_psnr, double peak, double psnr_max, void *ctx);
+
+#endif /* AVFILTER_ANSNR_H */
diff --git a/libavfilter/vf_ansnr.c b/libavfilter/vf_ansnr.c
new file mode 100644
index 0000000..78c71e1
--- /dev/null
+++ b/libavfilter/vf_ansnr.c
@@ -0,0 +1,416 @@ 
+/*
+ * Copyright (c) 2017 Ronald S. Bultje <rsbultje@gmail.com>
+ * Copyright (c) 2017 Ashish Pratap Singh <ashk43712@gmail.com>
+ *
+ * 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
+ * Calculate Anti-Noise Singnal to Noise Ratio (ANSNR) between two input videos.
+ */
+
+#include <inttypes.h>
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "avfilter.h"
+#include "dualinput.h"
+#include "drawutils.h"
+#include "formats.h"
+#include "internal.h"
+#include "ansnr.h"
+#include "video.h"
+
+typedef struct ANSNRContext {
+    const AVClass *class;
+    FFDualInputContext dinput;
+    int width;
+    int height;
+    char *format;
+    uint8_t type;
+    float *data_buf;
+    double ansnr_sum;
+    uint64_t nb_frames;
+} ANSNRContext;
+
+#define OFFSET(x) offsetof(ANSNRContext, x)
+#define MAX_ALIGN 32
+#define ALIGN_CEIL(x) ((x) + ((x) % MAX_ALIGN ? MAX_ALIGN - (x) % MAX_ALIGN : 0))
+#define OPT_RANGE_PIXEL_OFFSET (-128)
+
+const int ansnr_filter2d_ref_width = 3;
+const int ansnr_filter2d_dis_width = 5;
+const float ansnr_filter2d_ref[3 * 3] = {
+    1.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0,
+    2.0 / 16.0, 4.0 / 16.0, 2.0 / 16.0,
+    1.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0
+};
+const float ansnr_filter2d_dis[5 * 5] = {
+     2.0 / 571.0,  7.0 / 571.0,  12.0 / 571.0,  7.0 / 571.0,  2.0 / 571.0,
+     7.0 / 571.0, 31.0 / 571.0,  52.0 / 571.0, 31.0 / 571.0,  7.0 / 571.0,
+    12.0 / 571.0, 52.0 / 571.0, 127.0 / 571.0, 52.0 / 571.0, 12.0 / 571.0,
+     7.0 / 571.0, 31.0 / 571.0,  52.0 / 571.0, 31.0 / 571.0,  7.0 / 571.0,
+     2.0 / 571.0,  7.0 / 571.0,  12.0 / 571.0,  7.0 / 571.0,  2.0 / 571.0
+};
+
+static const AVOption ansnr_options[] = {
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(ansnr);
+
+static inline double get_ansnr_avg(double ansnr_sum, uint64_t nb_frames)
+{
+    return ansnr_sum / nb_frames;
+}
+
+static inline float pow_2(float base)
+{
+    return base*base;
+}
+
+static void ansnr_mse(float *ref, float *dis, float *signal, float *noise,
+                      int w, int h, int ref_stride, int dis_stride)
+{
+    int i, j;
+
+    int ref_ind;
+    int dis_ind;
+
+    float signal_sum = 0;
+    float noise_sum = 0;
+
+    for (i = 0; i < h; i++) {
+        for (j = 0; j < w; j++) {
+            ref_ind = i * ref_stride + j;
+            dis_ind = i * dis_stride + j;
+
+            signal_sum += pow_2(ref[ref_ind]);
+            noise_sum += pow_2(ref[ref_ind] - dis[dis_ind]);
+        }
+    }
+
+    if (signal) {
+        *signal = signal_sum;
+    }
+    if (noise) {
+        *noise = noise_sum;
+    }
+}
+
+static void ansnr_filter2d(const float *filt, const void *src, float *dst,
+                           int w, int h, int src_stride, int dst_stride,
+                           int filt_width, ANSNRContext *s)
+{
+    uint8_t sz;
+
+    uint8_t *src_8bit = (uint8_t *) src;
+    uint16_t *src_10bit = (uint16_t *) src;
+
+    int src_px_stride;
+
+    float filt_coeff, img_coeff;
+    int i, j, filt_i, filt_j, src_i, src_j;
+
+    if (s->type == 8) {
+        sz = sizeof(uint8_t);
+    }
+    else if (s->type == 10) {
+        sz = sizeof(uint16_t);
+    }
+
+    src_px_stride = src_stride / sizeof(sz);
+
+    for (i = 0; i < h; ++i) {
+        for (j = 0; j < w; ++j) {
+            float accum = 0;
+            for (filt_i = 0; filt_i < filt_width; filt_i++) {
+                for (filt_j = 0; filt_j < filt_width; filt_j++) {
+                    filt_coeff = filt[filt_i * filt_width + filt_j];
+
+                    src_i = i - filt_width / 2 + filt_i;
+                    src_j = j - filt_width / 2 + filt_j;
+
+                    src_i = FFABS(src_i);
+                    if (src_i >= h) {
+                        src_i = 2 * h - src_i - 1;
+                    }
+                    src_j = FFABS(src_j);
+                    if (src_j >= w) {
+                        src_j = 2 * w - src_j - 1;
+                    }
+
+                    if (s->type == 8) {
+                        img_coeff = src_8bit[src_i * src_px_stride + src_j] +
+                            OPT_RANGE_PIXEL_OFFSET;
+                    } else {
+                        img_coeff = src_10bit[src_i * src_px_stride + src_j] +
+                            OPT_RANGE_PIXEL_OFFSET;
+                    }
+
+                    accum += filt_coeff * img_coeff;
+                }
+            }
+            dst[i * dst_stride + j] = accum;
+        }
+    }
+}
+
+static int compute_ansnr(const void *ref, const void *dis, int w, int h,
+                         int ref_stride, int dis_stride, double *score,
+                         double *score_psnr, double peak, double psnr_max,
+                         void *ctx)
+{
+    ANSNRContext *s = (ANSNRContext *) ctx;
+
+    char *data_top;
+
+    float *ref_filt;
+    float *dis_filt;
+
+    float signal, noise;
+
+    int buf_stride = ALIGN_CEIL(w * sizeof(float));
+    size_t buf_sz = (size_t) (buf_stride * h);
+
+    double eps = 1e-10;
+
+    data_top = (float *) (s->data_buf);
+
+    ref_filt = (float *) data_top;
+    data_top += buf_sz;
+    dis_filt = (float *) data_top;
+    data_top += buf_sz;
+
+    buf_stride = buf_stride / sizeof(float);
+
+    ansnr_filter2d(ansnr_filter2d_ref, ref, ref_filt, w, h, ref_stride,
+                   buf_stride, ansnr_filter2d_ref_width, s);
+    ansnr_filter2d(ansnr_filter2d_dis, dis, dis_filt, w, h, dis_stride,
+                   buf_stride, ansnr_filter2d_dis_width, s);
+
+    ansnr_mse(ref_filt, dis_filt, &signal, &noise, w, h, buf_stride,
+              buf_stride);
+
+    *score = (noise==0) ? (psnr_max) : (10.0 * log10(signal / noise));
+
+    *score_psnr = FFMIN(10.0 * log10(pow_2(peak) * w * h / FFMAX(noise, eps)),
+                        psnr_max);
+
+    return 0;
+}
+
+static void set_meta(AVDictionary **metadata, const char *key, char comp, float d)
+{
+    char value[128];
+    snprintf(value, sizeof(value), "%0.2f", d);
+    if (comp) {
+        char key2[128];
+        snprintf(key2, sizeof(key2), "%s%c", key, comp);
+        av_dict_set(metadata, key2, value, 0);
+    } else {
+        av_dict_set(metadata, key, value, 0);
+    }
+}
+
+static AVFrame *do_ansnr(AVFilterContext *ctx, AVFrame *main, const AVFrame *ref)
+{
+    ANSNRContext *s = ctx->priv;
+    AVDictionary **metadata = &main->metadata;
+
+    char *format = s->format;
+
+    double score = 0.0;
+    double score_psnr = 0.0;
+
+    int w = s->width;
+    int h = s->height;
+
+    double stride;
+
+    double max_psnr;
+    double peak;
+
+    uint8_t sz;
+
+    if (s->type == 8) {
+        peak = 255.0;
+        max_psnr = 60.0;
+        sz = sizeof(uint8_t);
+    }
+    else if (s->type == 10) {
+        peak = 255.75;
+        max_psnr = 72.0;
+        sz = sizeof(uint16_t);
+    }
+
+    stride = ALIGN_CEIL(w * sz);
+
+    compute_ansnr(ref->data[0], main->data[0], w, h, stride, stride, &score,
+                  &score_psnr, peak, max_psnr, s);
+    
+    set_meta(metadata, "lavfi.ansnr.score", 0, score);
+    
+    s->nb_frames++;
+
+    s->ansnr_sum += score;
+
+    return main;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    ANSNRContext *s = ctx->priv;
+
+    s->dinput.process = do_ansnr;
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    static const enum AVPixelFormat pix_fmts[] = {
+        AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P,
+        AV_PIX_FMT_YUV444P10LE, AV_PIX_FMT_YUV422P10LE, AV_PIX_FMT_YUV420P10LE,
+        AV_PIX_FMT_NONE
+    };
+
+    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
+    if (!fmts_list)
+        return AVERROR(ENOMEM);
+    return ff_set_common_formats(ctx, fmts_list);
+}
+
+static int config_input_ref(AVFilterLink *inlink)
+{
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+    AVFilterContext *ctx  = inlink->dst;
+    ANSNRContext *s = ctx->priv;
+    int buf_stride;
+    size_t buf_sz;
+
+    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 height of input videos must be same.\n");
+        return AVERROR(EINVAL);
+    }
+    if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
+        av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->width = ctx->inputs[0]->w;
+    s->height = ctx->inputs[0]->h;
+    s->format = av_get_pix_fmt_name(ctx->inputs[0]->format);
+
+    buf_stride = ALIGN_CEIL(s->width * sizeof(float));
+    buf_sz = (size_t)buf_stride * s->height;
+
+    if (SIZE_MAX / buf_sz < 3) {
+        av_log(ctx, AV_LOG_ERROR, "insufficient size.\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (!(s->data_buf = av_malloc(buf_sz * 3))) {
+        av_log(ctx, AV_LOG_ERROR, "data_buf allocation failed.\n");
+        return AVERROR(EINVAL);
+    }
+    
+    s->type = desc->comp[0].depth > 8 ? 10 : 8;
+
+    return 0;
+}
+
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    ANSNRContext *s = ctx->priv;
+    AVFilterLink *mainlink = ctx->inputs[0];
+    int ret;
+
+    outlink->w = mainlink->w;
+    outlink->h = mainlink->h;
+    outlink->time_base = mainlink->time_base;
+    outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio;
+    outlink->frame_rate = mainlink->frame_rate;
+    if ((ret = ff_dualinput_init(ctx, &s->dinput)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref)
+{
+    ANSNRContext *s = inlink->dst->priv;
+    return ff_dualinput_filter_frame(&s->dinput, inlink, inpicref);
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    ANSNRContext *s = outlink->src->priv;
+    return ff_dualinput_request_frame(&s->dinput, outlink);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    ANSNRContext *s = ctx->priv;
+
+    ff_dualinput_uninit(&s->dinput);
+
+    av_free(s->data_buf);
+
+    av_log(ctx, AV_LOG_INFO, "ANSNR AVG: %.3f\n", get_ansnr_avg(s->ansnr_sum,
+                                                                s->nb_frames));
+}
+
+static const AVFilterPad ansnr_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+    },{
+        .name         = "reference",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input_ref,
+    },
+    { NULL }
+};
+
+static const AVFilterPad ansnr_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+        .request_frame = request_frame,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_ansnr = {
+    .name          = "ansnr",
+    .description   = NULL_IF_CONFIG_SMALL("Calculate the ANSNR between two video streams."),
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .priv_size     = sizeof(ANSNRContext),
+    .priv_class    = &ansnr_class,
+    .inputs        = ansnr_inputs,
+    .outputs       = ansnr_outputs,
+};