---
A quick and extremly simple filter for comparing two images.
Vittorio

 Changelog                       |   1 +
 doc/filters.texi                |   4 +
 libavfilter/Makefile            |   1 +
 libavfilter/allfilters.c        |   1 +
 libavfilter/version.h           |   2 +-
 libavfilter/vf_diff.c           | 193 ++++++++++++++++++++++++++++++++++++++++
 tests/fate/filter-video.mak     |   8 ++
 tests/ref/fate/filter-diff-neg  |   2 +
 tests/ref/fate/filter-diff-same |   2 +
 9 files changed, 213 insertions(+), 1 deletion(-)
 create mode 100644 libavfilter/vf_diff.c
 create mode 100644 tests/ref/fate/filter-diff-neg
 create mode 100644 tests/ref/fate/filter-diff-same

diff --git a/Changelog b/Changelog
index 99b3d1d..180c029 100644
--- a/Changelog
+++ b/Changelog
@@ -39,6 +39,7 @@ version <next>:
 - rewritten ASF demuxer
 - Go2Meeting decoding support
 - Intel QSV-accelerated MPEG-2 video and HEVC encoding
+- diff filter
 
 
 version 11:
diff --git a/doc/filters.texi b/doc/filters.texi
index d9874b6..836faab 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -1109,6 +1109,10 @@ delogo=x=0:y=0:w=100:h=77:band=10
 
 @end itemize
 
+@section diff
+
+Visually show frame difference from two input on a scale between 0-255.
+
 @section drawbox
 
 Draw a colored box on the input image.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 7b94f22..aee6c19 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -46,6 +46,7 @@ OBJS-$(CONFIG_COPY_FILTER)                   += vf_copy.o
 OBJS-$(CONFIG_CROP_FILTER)                   += vf_crop.o
 OBJS-$(CONFIG_CROPDETECT_FILTER)             += vf_cropdetect.o
 OBJS-$(CONFIG_DELOGO_FILTER)                 += vf_delogo.o
+OBJS-$(CONFIG_DIFF_FILTER)                   += vf_diff.o
 OBJS-$(CONFIG_DRAWBOX_FILTER)                += vf_drawbox.o
 OBJS-$(CONFIG_DRAWTEXT_FILTER)               += vf_drawtext.o
 OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 67a298d..a792635 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -71,6 +71,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(CROP,           crop,           vf);
     REGISTER_FILTER(CROPDETECT,     cropdetect,     vf);
     REGISTER_FILTER(DELOGO,         delogo,         vf);
+    REGISTER_FILTER(DIFF,           diff,           vf);
     REGISTER_FILTER(DRAWBOX,        drawbox,        vf);
     REGISTER_FILTER(DRAWTEXT,       drawtext,       vf);
     REGISTER_FILTER(FADE,           fade,           vf);
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 3981f34..20ad29f 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -30,7 +30,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVFILTER_VERSION_MAJOR  5
-#define LIBAVFILTER_VERSION_MINOR  1
+#define LIBAVFILTER_VERSION_MINOR  2
 #define LIBAVFILTER_VERSION_MICRO  0
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
diff --git a/libavfilter/vf_diff.c b/libavfilter/vf_diff.c
new file mode 100644
index 0000000..8f92e06
--- /dev/null
+++ b/libavfilter/vf_diff.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2015 Vittorio Giovara
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Dump image difference on the output frame.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "libavutil/opt.h"
+#include "libavutil/rational.h"
+
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+#define REF 0
+#define CMP 1
+
+typedef struct DiffContext {
+    const AVClass *class;
+
+    AVFrame *input_frames[2];            ///< input frames
+} DiffContext;
+
+static const enum AVPixelFormat formats_supported[] = {
+    AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE
+};
+
+static int query_formats(AVFilterContext *ctx)
+{
+    /* This will ensure that formats are the same on all pads */
+    ff_set_common_formats(ctx, ff_make_format_list(formats_supported));
+    return 0;
+}
+
+static av_cold void diff_uninit(AVFilterContext *ctx)
+{
+    DiffContext *s = ctx->priv;
+
+    /* clean any leftover frame */
+    av_frame_free(&s->input_frames[REF]);
+    av_frame_free(&s->input_frames[CMP]);
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+
+    int width            = ctx->inputs[REF]->w;
+    int height           = ctx->inputs[REF]->h;
+    AVRational time_base = ctx->inputs[REF]->time_base;
+
+    /* check size and fps match (pixel format always matches) */
+    if (width  != ctx->inputs[CMP]->w ||
+        height != ctx->inputs[CMP]->h) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Left and right sizes differ (%dx%d vs %dx%d).\n",
+               width, height,
+               ctx->inputs[CMP]->w, ctx->inputs[CMP]->h);
+        return AVERROR_INVALIDDATA;
+    } else if (av_cmp_q(time_base, ctx->inputs[CMP]->time_base) != 0) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Left and right framerates differ (%d/%d vs %d/%d).\n",
+               time_base.num, time_base.den,
+               ctx->inputs[CMP]->time_base.num,
+               ctx->inputs[CMP]->time_base.den);
+        return AVERROR_INVALIDDATA;
+    }
+
+    outlink->w         = width;
+    outlink->h         = height;
+    outlink->time_base = time_base;
+
+    return 0;
+}
+
+static int filter_frame_ref(AVFilterLink *inlink, AVFrame *frame)
+{
+    DiffContext *s = inlink->dst->priv;
+    s->input_frames[REF] = frame;
+    return 0;
+}
+
+static int filter_frame_cmp(AVFilterLink *inlink, AVFrame *frame)
+{
+    DiffContext *s = inlink->dst->priv;
+    s->input_frames[CMP] = frame;
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    DiffContext *s = ctx->priv;
+    AVFrame *dst;
+    uint8_t *p, *ref, *cmp;
+    int ret, i;
+
+    /* get a frame on both input, stop as soon as a video ends */
+    for (i = 0; i < 2; i++) {
+        if (!s->input_frames[i]) {
+            ret = ff_request_frame(ctx->inputs[i]);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    dst = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!dst)
+        return AVERROR(ENOMEM);
+
+    p = dst->data[0];
+    ref = s->input_frames[REF]->data[0];
+    cmp = s->input_frames[CMP]->data[0];
+    for (i = 0; i < outlink->w * 4 * outlink->h; i++)
+        p[i] = abs(cmp[i] - ref[i]);
+
+    s->input_frames[REF] = NULL;
+    s->input_frames[CMP] = NULL;
+
+    return ff_filter_frame(outlink, dst);
+}
+
+#define OFFSET(x) offsetof(DiffContext, x)
+#define V AV_OPT_FLAG_VIDEO_PARAM
+static const AVOption options[] = {
+    { NULL },
+};
+
+static const AVClass diff_class = {
+    .class_name = "diff",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVFilterPad diff_inputs[] = {
+    {
+        .name         = "ref",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame_ref,
+        .needs_fifo   = 1,
+    },
+    {
+        .name         = "cmp",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame_cmp,
+        .needs_fifo   = 1,
+    },
+    { NULL }
+};
+
+static const AVFilterPad diff_outputs[] = {
+    {
+        .name          = "diff",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+        .request_frame = request_frame,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_diff = {
+    .name          = "diff",
+    .description   = NULL_IF_CONFIG_SMALL("Show frame difference visually."),
+    .priv_size     = sizeof(DiffContext),
+    .priv_class    = &diff_class,
+    .query_formats = query_formats,
+    .inputs        = diff_inputs,
+    .outputs       = diff_outputs,
+    .uninit        = diff_uninit,
+};
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index 42fb063..01a1447 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -15,6 +15,14 @@ FATE_SAMPLES_AVCONV += $(FATE_FILTER-yes)
 FATE_FILTER_VSYNTH-$(CONFIG_BOXBLUR_FILTER) += fate-filter-boxblur
 fate-filter-boxblur: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf boxblur=2:1
 
+FATE_DIFF-$(CONFIG_DIFF_FILTER) += fate-filter-diff-same
+fate-filter-diff-same: CMD = framecrc -c:v pgmyuv -i $(SRC) -c:v pgmyuv -i 
$(SRC) -filter_complex diff -frames 1
+
+FATE_DIFF-$(call ALLYES, NEGATE_FILTER DIFF_FILTER) += fate-filter-diff-neg
+fate-filter-diff-neg: CMD = framecrc -c:v pgmyuv -i $(SRC) -c:v pgmyuv -i 
$(SRC) -filter_complex [1:v]negate[cmp],[0][cmp]diff -frames 1
+
+FATE_FILTER_VSYNTH-$(CONFIG_DIFF_FILTER) += $(FATE_DIFF-yes)
+
 FATE_FILTER_VSYNTH-$(CONFIG_DRAWBOX_FILTER) += fate-filter-drawbox
 fate-filter-drawbox: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf 
drawbox=10:20:200:60:[email protected]
 
diff --git a/tests/ref/fate/filter-diff-neg b/tests/ref/fate/filter-diff-neg
new file mode 100644
index 0000000..86fa616
--- /dev/null
+++ b/tests/ref/fate/filter-diff-neg
@@ -0,0 +1,2 @@
+#tb 0: 1/25
+0,          0,          0,        1,   405504, 0xec7e3788
diff --git a/tests/ref/fate/filter-diff-same b/tests/ref/fate/filter-diff-same
new file mode 100644
index 0000000..df9adeb
--- /dev/null
+++ b/tests/ref/fate/filter-diff-same
@@ -0,0 +1,2 @@
+#tb 0: 1/25
+0,          0,          0,        1,   405504, 0x00000000
-- 
1.9.5 (Apple Git-50.3)

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to