---
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