[MPlayer-dev-eng] patch: mencoder -vf threshold,label

Tuukka Toivonen tuukkat at ee.oulu.fi
Mon Nov 17 09:11:59 CET 2003


This patch adds two new video filters into Mplayer/Mencoder (patch against
cvs-20031114).

The first (-vf threshold) binarizes frames, setting all pixels to black
which are further away from a specified color than the specified distance,
all other pixels are set to white. Colors can be specified in RGB followed
by the limit distance (i.e. -vf threshold=255:255:255:47).

The second (-vf label) takes a binarized video and sets the blue channel in
all white areas into maximum and green and red and alpha channels are used
to number connected areas (e.g. first connected blob in the image would
get RGBA value of 0,0,255,1 and the second 0,0,255,2, and so on).

Both filters work only in IMGFMT_BGR32 colorspace.

Status: the filters are not yet stable, so I don't recommend inclusion yet.
This is rather an RFC (request for comments).

Purpose: I'm planning to extract subtitles from video, using maybe gocr.
First step is to extract as much of the subtitles as possible and reject
other connected areas based e.g. on motion.

URL:
http://www.ee.oulu.fi/~tuukkat/mplayer/MPlayer-20031114.threshold.patch

diff -PruN MPlayer-20031114.orig/libmpcodecs/Makefile MPlayer-20031114/libmpcodecs/Makefile
--- MPlayer-20031114.orig/libmpcodecs/Makefile	Sun Nov 16 21:36:25 2003
+++ MPlayer-20031114/libmpcodecs/Makefile	Sun Nov 16 22:00:20 2003
@@ -14,7 +14,7 @@
 VIDEO_SRCS_OPT=vd_realvid.c vd_ffmpeg.c vd_dshow.c vd_dmo.c vd_vfw.c vd_vfwex.c vd_odivx.c vd_divx4.c vd_zrmjpeg.c vd_xanim.c vd_xvid.c vd_xvid4.c vd_libdv.c vd_qtvideo.c vd_theora.c
 VIDEO_SRCS=dec_video.c vd.c $(VIDEO_SRCS_NAT) $(VIDEO_SRCS_LIB) $(VIDEO_SRCS_OPT)

-VFILTER_SRCS=vf.c vf_vo.c vf_crop.c vf_expand.c vf_scale.c vf_format.c vf_yuy2.c vf_flip.c vf_rgb2bgr.c vf_rotate.c vf_mirror.c vf_palette.c vf_lavc.c vf_dvbscale.c vf_cropdetect.c vf_test.c vf_noise.c vf_yvu9.c vf_rectangle.c vf_lavcdeint.c vf_eq.c vf_eq2.c vf_halfpack.c vf_dint.c vf_1bpp.c vf_bmovl.c vf_2xsai.c vf_unsharp.c vf_swapuv.c vf_il.c vf_boxblur.c vf_sab.c vf_smartblur.c vf_perspective.c vf_down3dright.c vf_field.c vf_denoise3d.c vf_hqdn3d.c vf_detc.c vf_telecine.c vf_tfields.c vf_ivtc.c vf_ilpack.c vf_dsize.c vf_decimate.c vf_softpulldown.c vf_tinterlace.c vf_pullup.c pullup.c vf_framestep.c vf_tile.c vf_delogo.c vf_fil.c vf_hue.c vf_spp.c
+VFILTER_SRCS=vf.c vf_vo.c vf_crop.c vf_expand.c vf_scale.c vf_format.c vf_yuy2.c vf_flip.c vf_rgb2bgr.c vf_rotate.c vf_mirror.c vf_palette.c vf_lavc.c vf_dvbscale.c vf_cropdetect.c vf_test.c vf_noise.c vf_yvu9.c vf_rectangle.c vf_lavcdeint.c vf_eq.c vf_eq2.c vf_halfpack.c vf_dint.c vf_1bpp.c vf_bmovl.c vf_2xsai.c vf_unsharp.c vf_swapuv.c vf_il.c vf_boxblur.c vf_sab.c vf_smartblur.c vf_perspective.c vf_down3dright.c vf_field.c vf_denoise3d.c vf_hqdn3d.c vf_detc.c vf_telecine.c vf_tfields.c vf_ivtc.c vf_ilpack.c vf_dsize.c vf_decimate.c vf_softpulldown.c vf_tinterlace.c vf_pullup.c pullup.c vf_framestep.c vf_tile.c vf_delogo.c vf_fil.c vf_hue.c vf_spp.c vf_threshold.c vf_label.c
 ifeq ($(HAVE_FFPOSTPROCESS),yes)
 VFILTER_SRCS += vf_pp.c
 endif
diff -PruN MPlayer-20031114.orig/libmpcodecs/vf.c MPlayer-20031114/libmpcodecs/vf.c
--- MPlayer-20031114.orig/libmpcodecs/vf.c	Mon Oct 27 01:07:21 2003
+++ MPlayer-20031114/libmpcodecs/vf.c	Sun Nov 16 22:01:01 2003
@@ -80,6 +80,8 @@
 extern vf_info_t vf_info_delogo;
 extern vf_info_t vf_info_hue;
 extern vf_info_t vf_info_spp;
+extern vf_info_t vf_info_threshold;
+extern vf_info_t vf_info_label;

 // list of available filters:
 static vf_info_t* filter_list[]={
@@ -149,6 +151,8 @@
 #ifdef USE_LIBAVCODEC
     &vf_info_spp,
 #endif
+    &vf_info_threshold,
+    &vf_info_label,
     NULL
 };

diff -PruN MPlayer-20031114.orig/libmpcodecs/vf_label.c MPlayer-20031114/libmpcodecs/vf_label.c
--- MPlayer-20031114.orig/libmpcodecs/vf_label.c	Thu Jan  1 02:00:00 1970
+++ MPlayer-20031114/libmpcodecs/vf_label.c	Mon Nov 17 01:04:12 2003
@@ -0,0 +1,143 @@
+//===========================================================================//
+// Copyright (C) Tuukka Toivonen 2003
+// released under GNU General Public License (GPL)
+
+/* {{{ [fold] Includes */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "../config.h"
+#include "../mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "../libvo/fastmemcpy.h"
+#include "../postproc/rgb2rgb.h"
+/* }}} */
+
+#define THIS "Label: "
+#define FORMAT IMGFMT_BGR32
+
+/* {{{ [fold] is_zero, get_label, set_label */
+static inline int is_zero(unsigned char *pix) {
+	return *(unsigned int *)pix == 0;
+}
+static inline int get_label(unsigned char *pix) {
+	return *(unsigned int *)pix >> 8;
+}
+static inline int set_label(unsigned char *pix, unsigned int l) {
+	*(unsigned int *)pix = (l==0?0:0xFF) | (l << 8);
+}
+/* }}} */
+/* {{{ [fold] label */
+static void label(unsigned char *pic, int width, int height, int stride) {
+	static const int bpp = 4;
+	static const int r = 2;
+	static const int g = 1;
+	static const int b = 0;
+	int x,y,l,l1,l2,lmin;
+	unsigned char *pix;
+	int *parent;
+	parent = calloc(width*height, sizeof(*parent));
+
+	/* Create union trees */
+	l = 0;
+	for (y=0; y<height; y++) {
+		x = 0;
+		while (x<width) {
+			while (is_zero(&pic[y*stride+x*bpp]) && x<width) x++;
+			if (x>=width) break;
+			l++;
+			while (!is_zero(&pic[y*stride+x*bpp]) && x<width) {
+				set_label(&pic[y*stride+x*bpp], l);
+				if (y>0 && !is_zero(&pic[(y-1)*stride+x*bpp])) {
+					l1 = l;
+					l2 = get_label(&pic[(y-1)*stride+x*bpp]);
+					/* Merge trees containing l1 and l2 */
+					while (parent[l1]!=0) l1 = parent[l1];
+					while (parent[l2]!=0) l2 = parent[l2];
+					if (l1 != l2) parent[l1] = l2;
+				}
+				x++;
+			}
+		}
+	}
+
+	/* Numerate tree */
+	for (l1=1, l2=0; l1<l; l1++) {
+		if (parent[l1]!=0) continue;
+		/* It's a root node */
+		l2--;
+		parent[l1] = l2;
+	}
+	l2 = -l2;
+
+	mp_msg(MSGT_VFILTER,MSGL_INFO, THIS "segments: %i  \n", l2);
+
+	/* Merge image */
+	for (y=0; y<height; y++) for (x=0; x<width; x++) {
+		l = get_label(&pic[y*stride+x*bpp]);
+		if (l==0) continue;
+		while (l>0) l = parent[l];
+		l = -l;
+		set_label(&pic[y*stride+x*bpp], l);
+	}
+
+	free(parent);
+}
+/* }}} */
+/* {{{ [fold] put_image */
+static int put_image(struct vf_instance_s *vf, mp_image_t *mpi) {
+	mp_image_t *dmpi;
+	unsigned int bpp = mpi->bpp / 8;
+	dmpi=vf_get_image(vf->next,mpi->imgfmt,MP_IMGTYPE_TEMP,MP_IMGFLAG_ACCEPT_STRIDE,mpi->w,mpi->h);
+	memcpy_pic(dmpi->planes[0],mpi->planes[0],mpi->w*bpp, mpi->h, dmpi->stride[0],mpi->stride[0]);
+	if (mpi->flags&MP_IMGFLAG_PLANAR && mpi->flags&MP_IMGFLAG_YUV) {
+		memcpy_pic(dmpi->planes[1],mpi->planes[1],mpi->w>>mpi->chroma_x_shift,
+					mpi->h>>mpi->chroma_y_shift,dmpi->stride[1],mpi->stride[1]);
+		memcpy_pic(dmpi->planes[2],mpi->planes[2],mpi->w>>mpi->chroma_x_shift,
+					mpi->h>>mpi->chroma_y_shift,dmpi->stride[2],mpi->stride[2]);
+	}
+	label(dmpi->planes[0], dmpi->width, dmpi->height, dmpi->stride[0]);
+	return vf_next_put_image(vf,dmpi);
+}
+/* }}} */
+/* {{{ [fold] query_format */
+static int query_format(struct vf_instance_s *vf, unsigned int fmt) {
+	int r = 0;
+	switch(fmt) {
+	case FORMAT:
+		r = vf_next_query_format(vf,FORMAT) & (~VFCAP_CSP_SUPPORTED_BY_HW);
+	}
+	return r;
+}
+/* }}} */
+/* {{{ [fold] uninit */
+static void uninit(struct vf_instance_s *vf) {
+}
+/* }}} */
+/* {{{ [fold] open */
+static int open(vf_instance_t *vf, char* args) {
+	vf->query_format = query_format;
+	vf->put_image = put_image;
+	vf->uninit = uninit;
+	return 1;
+}
+/* }}} */
+
+/* {{{ [fold] vf_info_label */
+vf_info_t vf_info_label = {
+	"labeling",
+	"label",
+	"tuukkat at ee.oulu.fi",
+	"Args: <none>",
+	open,
+	NULL
+};
+/* }}} */
+
+//===========================================================================//
diff -PruN MPlayer-20031114.orig/libmpcodecs/vf_threshold.c MPlayer-20031114/libmpcodecs/vf_threshold.c
--- MPlayer-20031114.orig/libmpcodecs/vf_threshold.c	Thu Jan  1 02:00:00 1970
+++ MPlayer-20031114/libmpcodecs/vf_threshold.c	Mon Nov 17 01:01:48 2003
@@ -0,0 +1,115 @@
+//===========================================================================//
+// Copyright (C) Tuukka Toivonen 2003
+// released under GNU General Public License (GPL)
+
+/* {{{ [fold] Includes */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "../config.h"
+#include "../mp_msg.h"
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+
+#include "../libvo/fastmemcpy.h"
+#include "../postproc/rgb2rgb.h"
+/* }}} */
+
+#define THIS "Threshold: "
+#define FORMAT IMGFMT_BGR32
+
+struct vf_priv_s {
+	int r, g, b, d;
+};
+
+/* {{{ [fold] threshold */
+static void threshold(struct vf_priv_s *th, unsigned char *pic, int width, int height, int stride) {
+	static const int bpp = 4;
+	static const int r = 2;
+	static const int g = 1;
+	static const int b = 0;
+	int x,y;
+	unsigned char *pix;
+	for (y=0; y<height; y++) for (x=0; x<width; x++) {
+		pix = &pic[y*stride+x*bpp];
+		if (abs((int)pix[r] - th->r)>th->d ||
+		    abs((int)pix[g] - th->g)>th->d ||
+		    abs((int)pix[b] - th->b)>th->d)
+		{
+			pix[0] = 0;
+			pix[1] = 0;
+			pix[2] = 0;
+			pix[3] = 0;
+		} else {
+			pix[0] = 255;
+			pix[1] = 255;
+			pix[2] = 255;
+			pix[3] = 255;
+		}
+	}
+}
+/* }}} */
+/* {{{ [fold] put_image */
+static int put_image(struct vf_instance_s *vf, mp_image_t *mpi){
+	mp_image_t *dmpi;
+	unsigned int bpp = mpi->bpp / 8;
+	dmpi=vf_get_image(vf->next,mpi->imgfmt,MP_IMGTYPE_TEMP,MP_IMGFLAG_ACCEPT_STRIDE,mpi->w,mpi->h);
+	memcpy_pic(dmpi->planes[0],mpi->planes[0],mpi->w*bpp, mpi->h, dmpi->stride[0],mpi->stride[0]);
+	if (mpi->flags&MP_IMGFLAG_PLANAR && mpi->flags&MP_IMGFLAG_YUV) {
+		memcpy_pic(dmpi->planes[1],mpi->planes[1],mpi->w>>mpi->chroma_x_shift,
+					mpi->h>>mpi->chroma_y_shift,dmpi->stride[1],mpi->stride[1]);
+		memcpy_pic(dmpi->planes[2],mpi->planes[2],mpi->w>>mpi->chroma_x_shift,
+					mpi->h>>mpi->chroma_y_shift,dmpi->stride[2],mpi->stride[2]);
+	}
+	threshold(vf->priv, dmpi->planes[0], dmpi->width, dmpi->height, dmpi->stride[0]);
+	return vf_next_put_image(vf,dmpi);
+}
+/* }}} */
+/* {{{ [fold] query_format */
+static int query_format(struct vf_instance_s *vf, unsigned int fmt) {
+	int r = 0;
+	switch(fmt) {
+	case FORMAT:
+		r = vf_next_query_format(vf,FORMAT) & (~VFCAP_CSP_SUPPORTED_BY_HW);
+	}
+	return r;
+}
+/* }}} */
+/* {{{ [fold] uninit */
+static void uninit(struct vf_instance_s *vf) {
+}
+/* }}} */
+/* {{{ [fold] open */
+static int open(vf_instance_t *vf, char* args) {
+	vf->query_format = query_format;
+	vf->put_image = put_image;
+	vf->uninit = uninit;
+	vf->priv = malloc(sizeof(struct vf_priv_s));
+	vf->priv->r = 255;
+	vf->priv->g = 255;
+	vf->priv->b = 255;
+	vf->priv->d = 32;
+	if (args) {
+		sscanf(args, "%d:%d:%d:%d",
+			&vf->priv->r, &vf->priv->g, &vf->priv->b, &vf->priv->d);
+	}
+	return 1;
+}
+/* }}} */
+
+/* {{{ [fold] vf_info_threshold */
+vf_info_t vf_info_threshold = {
+	"thresholding",
+	"threshold",
+	"tuukkat at ee.oulu.fi",
+	"Args: r:g:b:dist",
+	open,
+	NULL
+};
+/* }}} */
+
+//===========================================================================//



More information about the MPlayer-dev-eng mailing list