[MPlayer-dev-eng] [PATCH] An unsharp mask & gaussian blur video filter

Rémi Guyomarch rguyom at pobox.com
Tue Oct 29 00:43:56 CET 2002


This video filter implements the unsharp mask algorithm and at the
same time it's a classic gaussian blur.

Usage:

-vop unsharp=l7x5:0.9:c3x3:-0.2

	l	luma 
	c	chroma
	7x5	matrix size, 3x3 to 13x13, odd-sized matrix only
           	(automatically enforced)
		note: "n" = "nxn", eg "l5" = "l5x5"
	0.9	relative amount of sharpening / bluring
		postive = unsharp masking
		negative = gaussian bluring
		there's no limit (you can use 5 or -20 if you want)

It can be applied multiple times, for example before & after scaling :
-vop unsharp=l3x3:0.7,c5x5:0.2,scale=576:256:0:2,unsharp=l5x5:-0.3,crop=712:422:2:76

You can only apply the mask to the luma component or to the chroma
components or do both at the same time.

You can apply an unsharp mask to the luma component and at the same
time blur the chroma or vice-versa.

Position of the "l" and "c" doesn't matter, ie :
	-vop unsharp=l7x5:0.9:c3x3:-0.2
is the same thing as
	-vop unsharp=c3x3:-0.2:l7x5:0.9

Matrix size is limited by the type used (uint32_t). Converting to a
double-based code is rather easy (only a few lines needs patching). I
may add double-based code which will be automatically selected at
run-time (depending on the matrix size you specify) if you
want. Although I think matrix sizes larger than 13x13 won't be very
useful for video, YMMV.

I didn't added MMX, MMX2 or SSE implementations but feel free to code
them if you want ;)

-- 
Rémi
-------------- next part --------------
--- libmpcodecs//Makefile	25 Oct 2002 16:49:33 -0000	1.68
+++ libmpcodecs//Makefile	28 Oct 2002 07:55:00 -0000
@@ -6,7 +6,7 @@
 
 AUDIO_SRCS=dec_audio.c ad.c ad_liba52.c ad_acm.c ad_alaw.c ad_dk3adpcm.c ad_dshow.c ad_dvdpcm.c ad_ffmpeg.c ad_hwac3.c ad_imaadpcm.c ad_mp3lib.c ad_msadpcm.c ad_pcm.c ad_roqaudio.c ad_msgsm.c ad_faad.c ad_libvorbis.c ad_libmad.c ad_realaud.c ad_libdv.c
 VIDEO_SRCS=dec_video.c vd.c vd_null.c vd_realvid.c vd_cinepak.c vd_qtrpza.c vd_ffmpeg.c vd_dshow.c vd_vfw.c vd_vfwex.c vd_odivx.c vd_divx4.c vd_raw.c vd_xanim.c vd_msvidc.c vd_fli.c vd_qtrle.c vd_qtsmc.c vd_roqvideo.c vd_cyuv.c vd_nuv.c vd_libmpeg2.c vd_msrle.c vd_huffyuv.c vd_mpegpes.c vd_svq1.c vd_xvid.c vd_libdv.c vd_lcl.c vd_mtga.c vd_lzo.c
-VFILTER_SRCS=vf.c vf_vo.c vf_crop.c vf_expand.c vf_pp.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
+VFILTER_SRCS=vf.c vf_vo.c vf_crop.c vf_expand.c vf_pp.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
 ENCODER_SRCS=ve.c ve_divx4.c ve_lavc.c ve_vfw.c ve_rawrgb.c ve_libdv.c ve_xvid.c
 NATIVE_SRCS=native/RTjpegN.c native/cinepak.c native/cyuv.c native/fli.c native/minilzo.c native/msvidc.c native/nuppelvideo.c native/qtrle.c native/qtrpza.c native/qtsmc.c native/roqav.c native/xa_gsm.c native/svq1.c
 
--- libmpcodecs//vf.c	25 Oct 2002 16:49:33 -0000	1.54
+++ libmpcodecs//vf.c	28 Oct 2002 07:55:01 -0000
@@ -45,6 +45,7 @@
 extern vf_info_t vf_info_dint;
 extern vf_info_t vf_info_1bpp;
 extern vf_info_t vf_info_2xsai;
+extern vf_info_t vf_info_unsharp;
 
 char** vo_plugin_args=(char**) NULL;
 
@@ -83,6 +84,7 @@
     &vf_info_dint,
     &vf_info_1bpp,
     &vf_info_2xsai,
+    &vf_info_unsharp,
     NULL
 };
 
--- DOCS/tech/vop.txt	22 Aug 2002 01:47:56 -0000	1.17
+++ DOCS/tech/vop.txt	28 Oct 2002 23:41:29 -0000
@@ -173,3 +173,14 @@
     given on the command line, and the eq filter is controllable
     interactively just like with normal hardware equalizer controls.
 
+-vop unsharp=l|cWxH:amount[:l|cWxH:amount]
+    unsharp mask / gaussian blur.
+        l   apply effect on luma component
+        c   apply effect on chroma components
+      WxH   width and height of the matrix, odd sized in both directions
+            min = 3x3, max = 13x11 or 11x13
+            usually you will use somthing between 3x3 and 7x7
+    amount  relative amount of sharpness / blur to add to the image
+            amount < 0 = blur, amount > 0 = sharpen
+            usually you will use something between -1.5 and 1.5
+    MPI: DR (if possible) or TEMP, accepts stride
--- /dev/null	Tue Oct 29 00:17:33 2002
+++ libmpcodecs/vf_unsharp.c	Tue Oct 29 00:16:25 2002
@@ -0,0 +1,327 @@
+/*
+    Copyright (C) 2002 Rémi Guyomarch <rguyom at pobox.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "../config.h"
+#include "../mp_msg.h"
+#include "../cpudetect.h"
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#include "img_format.h"
+#include "mp_image.h"
+#include "vf.h"
+#include "../libvo/fastmemcpy.h"
+
+#ifndef MIN
+#define        MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef MAX
+#define        MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+//===========================================================================//
+
+#define MIN_MATRIX_SIZE 3
+#define MAX_MATRIX_SIZE 63
+
+typedef struct FilterParam {
+    int msizeX, msizeY;
+    double amount;
+    uint32_t *SC[MAX_MATRIX_SIZE-1];
+} FilterParam;
+
+struct vf_priv_s {
+    FilterParam lumaParam;
+    FilterParam chromaParam;
+    mp_image_t *dmpi;
+    unsigned int outfmt;
+};
+
+
+//===========================================================================//
+
+/* This code is based on :
+
+An Efficient algorithm for Gaussian blur using finite-state machines
+Frederick M. Waltz and John W. V. Miller
+
+SPIE Conf. on Machine Vision Systems for Inspection and Metrology VII
+Originally published Boston, Nov 98
+
+*/
+
+static void unsharp( uint8_t *dst, uint8_t *src, int dstStride, int srcStride, int width, int height, FilterParam *fp ) {
+
+    uint32_t **SC = fp->SC;
+    uint32_t SR[MAX_MATRIX_SIZE-1], Tmp1, Tmp2;
+    uint8_t* src2 = src; // avoid gcc warning
+
+    int32_t res;
+    int x, y, z;
+    int amount = fp->amount * 65536.0;
+    int stepsX = fp->msizeX/2;
+    int stepsY = fp->msizeY/2;
+    int scalebits = (stepsX+stepsY)*2;
+    int32_t halfscale = 1 << (stepsX+stepsY);
+
+    if( !fp->amount ) {
+	if( src == dst )
+	    return;
+	if( dstStride == srcStride ) 
+	    memcpy( dst, src, srcStride*height );
+	else
+	    for( y=0; y<height; y++, dst+=dstStride, src+=srcStride )
+		memcpy( dst, src, width );
+	return;
+    }
+
+    for( y=0; y<2*stepsY; y++ )
+	memset( SC[y], 0, sizeof(SC[y][0]) * (width+2*stepsX) );
+
+    for( y=-stepsY; y<height+stepsY; y++ ) {
+	if( y < height ) src2 = src;
+	memset( SR, 0, sizeof(SR[0]) * (2*stepsX-1) );
+	for( x=-stepsX; x<width+stepsX; x++ ) {
+	    Tmp1 = x<=0 ? src2[0] : x>=width ? src2[width-1] : src2[x];
+	    for( z=0; z<stepsX*2; z+=2 ) {
+		Tmp2 = SR[z+0] + Tmp1; SR[z+0] = Tmp1;
+		Tmp1 = SR[z+1] + Tmp2; SR[z+1] = Tmp2;
+	    }
+	    for( z=0; z<stepsY*2; z+=2 ) {
+		Tmp2 = SC[z+0][x+stepsX] + Tmp1; SC[z+0][x+stepsX] = Tmp1;
+		Tmp1 = SC[z+1][x+stepsX] + Tmp2; SC[z+1][x+stepsX] = Tmp2;
+	    }
+	    if( x>=stepsX && y>=stepsY ) {
+		uint8_t* srx = src - stepsY*srcStride + x - stepsX;
+		uint8_t* dsx = dst - stepsY*dstStride + x - stepsX;
+		
+		res = (int32_t)*srx + ( ( ( (int32_t)*srx - (int32_t)((Tmp1+halfscale) >> scalebits) ) * amount ) >> 16 );
+		*dsx = res>255 ? 255 : res<0 ? 0 : (uint8_t)res;
+	    }
+	}
+	if( y >= 0 ) {
+	    dst += dstStride;
+	    src += srcStride;
+	}
+    }
+}
+
+//===========================================================================//
+
+static int config( struct vf_instance_s* vf,
+		   int width, int height, int d_width, int d_height,
+		   unsigned int flags, unsigned int outfmt ) {
+
+    int z, stepsX, stepsY;
+    FilterParam *fp;
+
+    // allocate buffers
+
+    fp = &vf->priv->lumaParam;
+    mp_msg( MSGT_VFILTER, MSGL_INFO, "unsharp: %dx%d:%0.2f (luma) \n", fp->msizeX, fp->msizeY, fp->amount );
+    memset( fp->SC, 0, sizeof( fp->SC ) );
+    stepsX = fp->msizeX/2;
+    stepsY = fp->msizeY/2;
+    for( z=0; z<2*stepsY; z++ )
+	fp->SC[z] = memalign( 16, sizeof(*(fp->SC[z])) * (width+2*stepsX) );
+
+    fp = &vf->priv->chromaParam;
+    mp_msg( MSGT_VFILTER, MSGL_INFO, "unsharp: %dx%d:%0.2f (chroma)\n", fp->msizeX, fp->msizeY, fp->amount );
+    memset( fp->SC, 0, sizeof( fp->SC ) );
+    stepsX = fp->msizeX/2;
+    stepsY = fp->msizeY/2;
+    for( z=0; z<2*stepsY; z++ )
+	fp->SC[z] = memalign( 16, sizeof(*(fp->SC[z])) * (width+2*stepsX) );
+
+    return vf_next_config( vf, width, height, d_width, d_height, flags, outfmt );
+}
+
+//===========================================================================//
+
+static void get_image( struct vf_instance_s* vf, mp_image_t *mpi ) {
+    if( mpi->flags & MP_IMGFLAG_PRESERVE ) 
+	return; // don't change
+    if( mpi->imgfmt!=vf->priv->outfmt )
+	return; // colorspace differ
+
+    vf->priv->dmpi = vf_get_image( vf->next, mpi->imgfmt, mpi->type, mpi->flags, mpi->w, mpi->h );
+    mpi->planes[0] = vf->priv->dmpi->planes[0];
+    mpi->stride[0] = vf->priv->dmpi->stride[0];
+    mpi->width = vf->priv->dmpi->width;
+    if( mpi->flags & MP_IMGFLAG_PLANAR ) {
+        mpi->planes[1] = vf->priv->dmpi->planes[1];
+        mpi->planes[2] = vf->priv->dmpi->planes[2];
+	mpi->stride[1] = vf->priv->dmpi->stride[1];
+	mpi->stride[2] = vf->priv->dmpi->stride[2];
+    }
+    mpi->flags |= MP_IMGFLAG_DIRECT;
+}
+
+static int put_image( struct vf_instance_s* vf, mp_image_t *mpi ) {
+    mp_image_t *dmpi;
+
+    if( !(mpi->flags & MP_IMGFLAG_DIRECT) )
+	// no DR, so get a new image! hope we'll get DR buffer:
+	vf->priv->dmpi = vf_get_image( vf->next,vf->priv->outfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->w, mpi->h);
+    dmpi= vf->priv->dmpi;
+    
+    unsharp( dmpi->planes[0], mpi->planes[0], dmpi->stride[0], mpi->stride[0], mpi->w,   mpi->h,   &vf->priv->lumaParam );
+    unsharp( dmpi->planes[1], mpi->planes[1], dmpi->stride[1], mpi->stride[1], mpi->w/2, mpi->h/2, &vf->priv->chromaParam );
+    unsharp( dmpi->planes[2], mpi->planes[2], dmpi->stride[2], mpi->stride[2], mpi->w/2, mpi->h/2, &vf->priv->chromaParam );
+    
+    dmpi->qscale = mpi->qscale;
+    dmpi->qstride = mpi->qstride;
+    
+#ifdef HAVE_MMX
+    if(gCpuCaps.hasMMX)
+	asm volatile ("emms\n\t");
+#endif
+#ifdef HAVE_MMX2
+    if(gCpuCaps.hasMMX2)
+	asm volatile ("sfence\n\t");
+#endif
+    
+    return vf_next_put_image( vf, dmpi );
+}
+
+static void uninit( struct vf_instance_s* vf ) {
+    unsigned int z;
+    FilterParam *fp;
+
+    if( !vf->priv ) return;
+
+    fp = &vf->priv->lumaParam;
+    for( z=0; z<sizeof(fp->SC)/sizeof(fp->SC[0]); z++ ) {
+	if( fp->SC[z] ) free( fp->SC[z] );
+	fp->SC[z] = NULL;
+    }
+    fp = &vf->priv->chromaParam;
+    for( z=0; z<sizeof(fp->SC)/sizeof(fp->SC[0]); z++ ) {
+	if( fp->SC[z] ) free( fp->SC[z] );
+	fp->SC[z] = NULL;
+    }
+
+    free( vf->priv );
+    vf->priv = NULL;
+}
+
+//===========================================================================//
+
+static int query_format( struct vf_instance_s* vf, unsigned int fmt ) {
+    switch(fmt) {
+    case IMGFMT_YV12:
+    case IMGFMT_I420:
+    case IMGFMT_IYUV:
+	return vf_next_query_format( vf, vf->priv->outfmt );
+    }
+    return 0;
+}
+
+//===========================================================================//
+
+static void parse( FilterParam *fp, char* args ) {
+
+    // l7x5:0.8:c3x3:-0.2
+
+    char *z;
+    char *pos = args;
+    char *max = args + strlen(args);
+
+    // parse matrix sizes
+    fp->msizeX = ( pos && pos+1<max ) ? atoi( pos+1 ) : 0;
+    z = strchr( pos+1, 'x' );
+    fp->msizeY = ( z && z+1<max ) ? atoi( pos=z+1 ) : fp->msizeX;
+
+    // min/max & odd
+    fp->msizeX = 1 | MIN( MAX( fp->msizeX, MIN_MATRIX_SIZE ), MAX_MATRIX_SIZE );
+    fp->msizeY = 1 | MIN( MAX( fp->msizeY, MIN_MATRIX_SIZE ), MAX_MATRIX_SIZE );
+
+    // parse amount
+    pos = strchr( pos+1, ':' );
+    fp->amount = ( pos && pos+1<max ) ? atof( pos+1 ) : 0;
+}
+
+//===========================================================================//
+
+static unsigned int fmt_list[] = {
+    IMGFMT_YV12,
+    IMGFMT_I420,
+    IMGFMT_IYUV,
+    0
+};
+
+static int open( vf_instance_t *vf, char* args ) {
+    vf->config       = config;
+    vf->put_image    = put_image;
+    vf->get_image    = get_image;
+    vf->query_format = query_format;
+    vf->uninit       = uninit;
+    vf->priv         = malloc( sizeof(struct vf_priv_s) );
+    memset( vf->priv, 0, sizeof(struct vf_priv_s) );
+
+    if( args ) {
+	char *args2 = strchr( args, 'l' );
+	if( args2 )
+	    parse( &vf->priv->lumaParam, args2 );
+	else {
+	    vf->priv->lumaParam.amount = 
+	    vf->priv->lumaParam.msizeX = 
+	    vf->priv->lumaParam.msizeY = 0;
+	}
+
+	args2 = strchr( args, 'c' );
+	if( args2 ) 
+	    parse( &vf->priv->chromaParam, args2 );
+	else {
+	    vf->priv->chromaParam.amount = 
+	    vf->priv->chromaParam.msizeX = 
+	    vf->priv->chromaParam.msizeY = 0;
+	}
+
+	if( !vf->priv->lumaParam.msizeX && !vf->priv->chromaParam.msizeX )
+	    return 0; // nothing to do
+    }
+
+    // check csp:
+    vf->priv->outfmt = vf_match_csp( &vf->next, fmt_list, IMGFMT_YV12 );
+    if( !vf->priv->outfmt ) {
+	uninit( vf );
+        return 0; // no csp match :(
+    }
+
+    return 1;
+}
+
+vf_info_t vf_info_unsharp = {
+    "unsharp mask",
+    "unsharp",
+    "Rémi Guyomarch",
+    "",
+    open
+};
+
+//===========================================================================//


More information about the MPlayer-dev-eng mailing list