summaryrefslogtreecommitdiffstats
path: root/TOOLS
diff options
context:
space:
mode:
authorRudolf Polzer <divverent@xonotic.org>2013-06-14 14:32:18 +0200
committerRudolf Polzer <divverent@xonotic.org>2013-06-16 17:11:25 +0200
commit7ae4242820236bdd32ed080d05d674fdfe16483e (patch)
treef625666c77cc3abee2772816484ecb4a0be07230 /TOOLS
parentde63e1d15e93892c2c74c417fdf4b195b70fc8ee (diff)
downloadmpv-7ae4242820236bdd32ed080d05d674fdfe16483e.tar.bz2
mpv-7ae4242820236bdd32ed080d05d674fdfe16483e.tar.xz
vf_dlopen filters: add an "ildetect" filter that detects interlacing
The filter analyzes each frame for combing, and decides at the end whether the content is likely interlaced, telecined or progressive.
Diffstat (limited to 'TOOLS')
-rw-r--r--TOOLS/vf_dlopen/Makefile2
-rw-r--r--TOOLS/vf_dlopen/ildetect.c292
-rwxr-xr-xTOOLS/vf_dlopen/ildetect.sh43
3 files changed, 336 insertions, 1 deletions
diff --git a/TOOLS/vf_dlopen/Makefile b/TOOLS/vf_dlopen/Makefile
index c1eb5e0c9f..8057676d57 100644
--- a/TOOLS/vf_dlopen/Makefile
+++ b/TOOLS/vf_dlopen/Makefile
@@ -19,7 +19,7 @@
# 02110-1301 USA
#
-FILTERS = showqscale telecine tile rectangle framestep
+FILTERS = showqscale telecine tile rectangle framestep ildetect
COMMON = filterutils.o
OBJECTS = $(patsubst %,%.o,$(FILTERS)) $(COMMON)
diff --git a/TOOLS/vf_dlopen/ildetect.c b/TOOLS/vf_dlopen/ildetect.c
new file mode 100644
index 0000000000..6aa62a1153
--- /dev/null
+++ b/TOOLS/vf_dlopen/ildetect.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2012 Rudolf Polzer <divVerent@xonotic.org>
+ *
+ * This file is part of mpv's vf_dlopen examples.
+ *
+ * mpv's vf_dlopen examples are free software; you can redistribute them and/or
+ * modify them 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.
+ *
+ * mpv's vf_dlopen examples are distributed in the hope that they 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 mpv's vf_dlopen examples; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "vf_dlopen.h"
+
+#include "filterutils.h"
+
+/*
+ * interlacing detector
+ *
+ * usage: -vf dlopen=./ildetect.so:<method>:<threshold>
+ *
+ * outputs an interlacing detection report at the end
+ *
+ * methods:
+ * 0 = transcode 32detect (default)
+ * 1 = decomb IsCombed
+ * 2 = IsCombedTIVTC
+ * 3 = simple average
+ *
+ * threshold:
+ * normalized at 1
+ */
+
+typedef struct {
+ int method;
+ double combing_threshold;
+ double motion_threshold;
+ double motion_amount;
+ double yes_threshold;
+ double no_threshold;
+ double total_yes_threshold;
+ double total_no_threshold;
+ double tc_threshold;
+ double decision_threshold;
+ double tc_decision_threshold;
+ double lastcombed;
+ int numtotalframes;
+ int numdecidedframes;
+ int totalcombedframes;
+ int numjumpingadjacentframes;
+ int numdecidedadjacentframes;
+ unsigned char *buffer_data;
+ size_t buffer_size;
+} ildetect_data_t;
+
+static int il_config(struct vf_dlopen_context *ctx)
+{
+ ctx->out_height -= 4;
+ ctx->out_d_height = ctx->out_height;
+ return 1;
+}
+
+static int il_decision(struct vf_dlopen_context *ctx,
+ int p0, int p1, int p2, int p3, int p4)
+{
+ ildetect_data_t *il = ctx->priv;
+
+ // model for threshold: p0, p2, p4 = 0; p1, p3 = t
+ switch (il->method) {
+ case 0: { // diff-diff (transcode 32detect)
+ int d12 = p1 - p2; // t
+ int d13 = p1 - p3; // 0
+ if (abs(d12) > 15 * il->combing_threshold &&
+ abs(d13) < 10 * il->combing_threshold)
+ return 1;
+ // true for t > 15
+ break;
+ }
+ case 1: { // multiply (decomb IsCombed)
+ int d12 = p1 - p2; // t
+ int d32 = p3 - p2; // t
+ if (d12 * d32 > pow(il->combing_threshold, 2) * (25*25))
+ return 1;
+ // true for t > 21
+ break;
+ }
+ case 2: { // blur-blur (IsCombedTIVTC)
+ int b024 = p0 + 6 * p2 + p4; // 0
+ int b13 = 4 * p1 + 4 * p3; // 8t
+ if (abs(b024 - b13) > il->combing_threshold * 8 * 20)
+ return 1;
+ // true for t > 20
+ break;
+ }
+ case 3: { // average-average
+ int d123 = p1 + p3 - 2 * p2; // 2t
+ int d024 = p0 + p4 - 2 * p2; // 0
+ if ((abs(d123) - abs(d024)) > il->combing_threshold * 30)
+ return 1;
+ // true for t > 15
+ break;
+ }
+ }
+ return 0;
+}
+
+static int il_put_image(struct vf_dlopen_context *ctx)
+{
+ ildetect_data_t *il = ctx->priv;
+ unsigned int x, y;
+ int first_frame = 0;
+
+ size_t sz = ctx->inpic.planestride[0] * ctx->inpic.planeheight[0];
+ if (sz != il->buffer_size) {
+ il->buffer_data = realloc(il->buffer_data, sz);
+ il->buffer_size = sz;
+ first_frame = 1;
+ }
+
+ assert(ctx->inpic.planes == 1);
+ assert(ctx->outpic[0].planes == 1);
+
+ assert(ctx->inpic.planewidth[0] == ctx->outpic[0].planewidth[0]);
+ assert(ctx->inpic.planeheight[0] == ctx->outpic[0].planeheight[0] + 4);
+
+ if (first_frame) {
+ printf("First frame\n");
+ il->lastcombed = -1;
+ } else {
+ // detect interlacing
+ // for each row of 5 pixels, compare:
+ // p2 vs (p1 + p3) / 2
+ // p2 vs (p0 + p4) / 2
+ unsigned int totalcombedframes = 0; // add 255 per combed pixel
+ unsigned int totalpixels = 0;
+ for (y = 0; y < ctx->inpic.planeheight[0] - 4; ++y) {
+ unsigned char *in_line =
+ &ctx->inpic.plane[0][ctx->inpic.planestride[0] * y];
+ unsigned char *buf_line =
+ &il->buffer_data[ctx->inpic.planestride[0] * y];
+ unsigned char *out_line =
+ &ctx->outpic->plane[0][ctx->outpic->planestride[0] * y];
+ for (x = 0; x < ctx->inpic.planewidth[0]; ++x) {
+ int b2 = buf_line[x + ctx->inpic.planestride[0] * 2];
+ int p0 = in_line[x];
+ int p1 = in_line[x + ctx->inpic.planestride[0]];
+ int p2 = in_line[x + ctx->inpic.planestride[0] * 2];
+ int p3 = in_line[x + ctx->inpic.planestride[0] * 3];
+ int p4 = in_line[x + ctx->inpic.planestride[0] * 4];
+ int is_moving = abs(b2 - p2) > il->motion_threshold;
+
+ if (!is_moving) {
+ out_line[x] = 128;
+ continue;
+ }
+
+ ++totalpixels;
+
+ int combed = il_decision(ctx, p0, p1, p2, p3, p4);
+ totalcombedframes += combed;
+ out_line[x] = 255 * combed;
+ }
+ }
+
+ double avgpixels = totalpixels / (double)
+ ((ctx->inpic.planeheight[0] - 4) * ctx->inpic.planewidth[0]);
+
+ if (avgpixels > il->motion_amount) {
+ double avgcombed = totalcombedframes / (double) totalpixels;
+
+ if (il->lastcombed >= 0) {
+ if (il->lastcombed > il->yes_threshold &&
+ avgcombed < il->no_threshold)
+ ++il->numjumpingadjacentframes;
+ if (il->lastcombed < il->no_threshold &&
+ avgcombed > il->yes_threshold)
+ ++il->numjumpingadjacentframes;
+ ++il->numdecidedadjacentframes;
+ }
+
+ il->lastcombed = avgcombed;
+
+ if (avgcombed > il->yes_threshold) {
+ ++il->numdecidedframes;
+ ++il->totalcombedframes;
+ } else if (avgcombed < il->no_threshold) {
+ ++il->numdecidedframes;
+ }
+ } else
+ il->lastcombed = -1;
+ }
+
+ ++il->numtotalframes;
+
+ copy_plane(
+ il->buffer_data, ctx->inpic.planestride[0],
+ ctx->inpic.plane[0], ctx->inpic.planestride[0],
+ ctx->inpic.planewidth[0],
+ ctx->inpic.planeheight[0]);
+
+ ctx->outpic[0].pts = ctx->inpic.pts;
+ return 1;
+}
+
+void il_uninit(struct vf_dlopen_context *ctx)
+{
+ ildetect_data_t *il = ctx->priv;
+
+ double avgdecided = il->numtotalframes
+ ? il->numdecidedframes / (double) il->numtotalframes : -1;
+ double avgadjacent = il->numdecidedframes
+ ? il->numdecidedadjacentframes / (double) il->numdecidedframes : -1;
+ double avgscore = il->numdecidedframes
+ ? il->totalcombedframes / (double) il->numdecidedframes : -1;
+ double avgjumps = il->numdecidedadjacentframes
+ ? il->numjumpingadjacentframes / (double) il->numdecidedadjacentframes : -1;
+
+ printf("ildetect: Avg decided: %f\n", avgdecided);
+ printf("ildetect: Avg adjacent decided: %f\n", avgadjacent);
+ printf("ildetect: Avg interlaced decided: %f\n", avgscore);
+ printf("ildetect: Avg interlaced/progressive adjacent decided: %f\n", avgjumps);
+
+ if (avgdecided < il->decision_threshold)
+ avgadjacent = avgscore = avgjumps = -1;
+
+ if (avgadjacent < il->tc_decision_threshold)
+ avgadjacent = avgjumps = -1;
+
+ if (avgscore < 0)
+ printf("ildetect: Content is probably: undecided\n");
+ else if (avgscore < il->total_no_threshold)
+ printf("ildetect: Content is probably: PROGRESSIVE\n");
+ else if (avgscore > il->total_yes_threshold && avgjumps < 0)
+ printf("ildetect: Content is probably: INTERLACED (possibly telecined)\n");
+ else if (avgjumps > il->tc_threshold)
+ printf("ildetect: Content is probably: TELECINED\n");
+ else if (avgscore > il->total_yes_threshold)
+ printf("ildetect: Content is probably: INTERLACED\n");
+ else
+ printf("ildetect: Content is probably: unknown\n");
+
+ free(ctx->priv);
+}
+
+int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **argv)
+{
+ VF_DLOPEN_CHECK_VERSION(ctx);
+ (void) argc;
+ (void) argv;
+
+ ildetect_data_t *il = malloc(sizeof(ildetect_data_t));
+ memset(il, 0, sizeof(*il));
+
+#define A(i,d) ((argc>(i) && *argv[i]) ? atof(argv[i]) : (d))
+ il->method = A(0, 0);
+ il->combing_threshold = A(1, 1);
+ il->motion_threshold = A(2, 6);
+ il->motion_amount = A(3, 0.1);
+ il->yes_threshold = A(4, 0.1);
+ il->no_threshold = A(5, 0.05);
+ il->total_yes_threshold = A(6, 0.1);
+ il->total_no_threshold = A(7, 0.05);
+ il->tc_threshold = A(8, 0.1);
+ il->decision_threshold = A(9, 0.2);
+ il->tc_decision_threshold = A(10, 0.2);
+
+ static struct vf_dlopen_formatpair map[] = {
+ { "y8", "y8" },
+ { NULL, NULL }
+ };
+ ctx->format_mapping = map;
+ ctx->config = il_config;
+ ctx->put_image = il_put_image;
+ ctx->uninit = il_uninit;
+ ctx->priv = il;
+ return 1;
+}
diff --git a/TOOLS/vf_dlopen/ildetect.sh b/TOOLS/vf_dlopen/ildetect.sh
new file mode 100755
index 0000000000..e9027077d2
--- /dev/null
+++ b/TOOLS/vf_dlopen/ildetect.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+case "$0" in
+ */*)
+ MYDIR=${0%/*}
+ ;;
+ *)
+ MYDIR=.
+ ;;
+esac
+
+set -e
+
+make -C "$MYDIR"
+
+testfun()
+{
+ ${MPV:-mpv} "$@" \
+ -vf dlopen="$MYDIR/ildetect.so" \
+ -o /dev/null -of nut -ovc rawvideo -no-audio \
+ | grep "^ildetect:"
+}
+
+out=`testfun "$@"`
+echo
+echo
+echo "$out"
+echo
+echo
+case "$out" in
+ *"probably: PROGRESSIVE"*)
+ ${MPV:-mpv} "$@"
+ ;;
+ *"probably: INTERLACED"*)
+ ${MPV:-mpv} "$@" -vf-pre yadif
+ ;;
+ *"probably: TELECINED"*)
+ ${MPV:-mpv} "$@" -vf-pre pullup
+ ;;
+ *)
+ false
+ ;;
+esac