[MPlayer-dev-eng] [PATCH] automatic cropping in vf_crop

Alexander Stege mplayer at legale-software.com
Mon Jun 18 01:24:51 CEST 2007


Hello everyone!

This is a patch that merges the functionalities of the cropdetect and the
crop video filter to enable automatic/interactive cropping of black borders
without having to manually copy&paste and restart mplayer.

To apply the crop parameters that cropdetect has detected, I have connected
the filter to the 'change_rectangle' input.conf directive that belongs to
the rectangle filter. rectangle is just a dummy-crop anyway I guess.

cropdetect is only run on every 50th frame, so no worries about CPU-eating.


have a nice day, 
Alex
-------------- next part --------------
Index: libmpcodecs/vf_crop.c
===================================================================
--- libmpcodecs/vf_crop.c	(revision 23572)
+++ libmpcodecs/vf_crop.c	(working copy)
@@ -13,12 +13,20 @@
 #include "m_option.h"
 #include "m_struct.h"
 
+#define AC_NOVAL	(-2)
+
 static struct vf_priv_s {
     int crop_w,crop_h;
     int crop_x,crop_y;
+    int x1,y1,x2,y2;
+    int limit;
+    int fno;
 } const vf_priv_dflt = {
   -1,-1,
-  -1,-1
+  -1,-1, 
+  -1,-1,-1,-1,
+  AC_NOVAL,
+  0
 };
 
 extern int opt_screen_size_x;
@@ -26,6 +34,28 @@
 
 //===========================================================================//
 
+static int checkline(unsigned char* src,int stride,int len,int bpp){
+    int total=0;
+    int div=len;
+    switch(bpp){
+    case 1:
+	while(--len>=0){
+	    total+=src[0]; src+=stride;
+	}
+	break;
+    case 3:
+    case 4:
+	while(--len>=0){
+	    total+=src[0]+src[1]+src[2]; src+=stride;
+	}
+	div*=3;
+	break;
+    }
+    total/=div;
+//    printf("total=%d\n",total);
+    return total;
+}
+
 static int config(struct vf_instance_s* vf,
         int width, int height, int d_width, int d_height,
 	unsigned int flags, unsigned int outfmt){
@@ -34,6 +64,12 @@
     if(vf->priv->crop_h<=0 || vf->priv->crop_h>height) vf->priv->crop_h=height;
     if(vf->priv->crop_x<0) vf->priv->crop_x=(width-vf->priv->crop_w)/2;
     if(vf->priv->crop_y<0) vf->priv->crop_y=(height-vf->priv->crop_h)/2;
+    // reset cropdetect parameters:
+    vf->priv->x1=width - 1;
+    vf->priv->y1=height - 1;
+    vf->priv->x2=0;
+    vf->priv->y2=0;
+    vf->priv->fno=0;
     // rounding:
     if(!IMGFMT_IS_RGB(outfmt) && !IMGFMT_IS_BGR(outfmt)){
 	switch(outfmt){
@@ -43,15 +79,19 @@
 	    break;
 	case IMGFMT_YVU9:
 	case IMGFMT_IF09:
+		vf->priv->crop_h += vf->priv->crop_y&3;
 	    vf->priv->crop_y&=~3;
 	case IMGFMT_411P:
+		vf->priv->crop_w += vf->priv->crop_x&3;
 	    vf->priv->crop_x&=~3;
 	    break;
 	case IMGFMT_YV12:
 	case IMGFMT_I420:
 	case IMGFMT_IYUV:
+		vf->priv->crop_h += vf->priv->crop_y&1;
 	    vf->priv->crop_y&=~1;
 	default:
+		vf->priv->crop_w += vf->priv->crop_x&1;
 	    vf->priv->crop_x&=~1;
 	}
     }
@@ -68,8 +108,53 @@
     return vf_next_config(vf,vf->priv->crop_w,vf->priv->crop_h,d_width,d_height,flags,outfmt);
 }
 
+static int
+control(struct vf_instance_s* vf, int request, void *data)
+{
+    const int *const tmp = data;
+    switch(request){
+    case VFCTRL_CHANGE_RECTANGLE:
+	switch (tmp[0]){
+	case 0:
+	    vf->priv->crop_w += tmp[1];
+	    return 1;
+	case 1:
+	    vf->priv->crop_h += tmp[1];
+	    return 1;
+	case 2:
+	    vf->priv->crop_x += tmp[1];
+	    return 1;
+	case 3:
+	    vf->priv->crop_y += tmp[1];
+	    return 1;
+	case 4:
+		// change limit for next cropdetect results
+		// -1: disables cropdetect
+		// -2: leave previous limit untouched
+		if (tmp[1] >= -1) vf->priv->limit = tmp[1];
+        // has cropdetect found a non-black frame yet?
+		if (vf->priv->x2 <= vf->priv->x1 || 
+		    vf->priv->y2 <= vf->priv->y1) return 1;
+		// get values from cropdetect filter
+		vf->priv->crop_w = vf->priv->x2 - vf->priv->x1 + 1;
+		vf->priv->crop_h = vf->priv->y2 - vf->priv->y1 + 1;
+		vf->priv->crop_x = vf->priv->x1;
+		vf->priv->crop_y = vf->priv->y1;
+		return 1;
+	default:
+	    mp_msg(MSGT_VFILTER,MSGL_FATAL,"Unknown param %d \n", tmp[0]);
+	    return 0;
+	}
+    }
+    return vf_next_control(vf, request, data);
+    return 0;
+}
+
 static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts){
     mp_image_t *dmpi;
+    int bpp=mpi->bpp/8;
+    int x, y;
+    
     if (mpi->flags&MP_IMGFLAG_DRAW_CALLBACK)
 	return vf_next_put_image(vf,vf->dmpi, pts);
     dmpi=vf_get_image(vf->next,mpi->imgfmt,
@@ -92,6 +177,43 @@
     }
     dmpi->stride[0]=mpi->stride[0];
     dmpi->width=mpi->width;
+
+	// cropdetect will run all the time, so we should not waste CPU
+	if (vf->priv->limit>=0 && ++vf->priv->fno>50)
+	{
+		vf->priv->fno = 0;
+		
+	    for(y=0;y<vf->priv->y1;y++){
+		if(checkline(mpi->planes[0]+mpi->stride[0]*y,bpp,mpi->w,bpp)>vf->priv->limit){
+		    vf->priv->y1=y;
+		    break;
+		}
+	    }
+	
+	    for(y=mpi->h-1;y>vf->priv->y2;y--){
+		if(checkline(mpi->planes[0]+mpi->stride[0]*y,bpp,mpi->w,bpp)>vf->priv->limit){
+		    vf->priv->y2=y;
+		    break;
+		}
+	    }
+	
+	    for(y=0;y<vf->priv->x1;y++){
+		if(checkline(mpi->planes[0]+bpp*y,mpi->stride[0],mpi->h,bpp)>vf->priv->limit){
+		    vf->priv->x1=y;
+		    break;
+		}
+	    }
+	
+	    for(y=mpi->w-1;y>vf->priv->x2;y--){
+		if(checkline(mpi->planes[0]+bpp*y,mpi->stride[0],mpi->h,bpp)>vf->priv->limit){
+		    vf->priv->x2=y;
+		    break;
+		}
+	    }
+	    mp_msg(MSGT_VFILTER, MSGL_DBG2, "Cropbox: %d - %d, %d - %d\n", 
+	    vf->priv->x1, vf->priv->x2, vf->priv->y1, vf->priv->y2);
+	}
+
     return vf_next_put_image(vf,dmpi, pts);
 }
 
@@ -140,6 +262,7 @@
 
 static int open(vf_instance_t *vf, char* args){
     vf->config=config;
+    vf->control=control;
     vf->put_image=put_image;
     vf->start_slice=start_slice;
     vf->draw_slice=draw_slice;
@@ -151,17 +274,31 @@
     vf->priv->crop_y=
     vf->priv->crop_w=
     vf->priv->crop_h=-1;
+    vf->priv->limit=AC_NOVAL;
     } //if(!vf->priv)
-    if(args) sscanf(args, "%d:%d:%d:%d", 
+    if(args) sscanf(args, "%d:%d:%d:%d:%d", 
     &vf->priv->crop_w,
     &vf->priv->crop_h,
     &vf->priv->crop_x,
-    &vf->priv->crop_y);
-    mp_msg(MSGT_VFILTER, MSGL_INFO, "Crop: %d x %d, %d ; %d\n",
-    vf->priv->crop_w,
-    vf->priv->crop_h,
-    vf->priv->crop_x,
-    vf->priv->crop_y);
+    &vf->priv->crop_y,
+    &vf->priv->limit);
+	// enable autocrop if width and height are not explicitly given
+    if(vf->priv->limit<=AC_NOVAL && vf->priv->crop_w<=0 && vf->priv->crop_h<=0) 
+    	vf->priv->limit=24;
+
+	if(vf->priv->limit>=0)
+	{  
+	    mp_msg(MSGT_VFILTER, MSGL_INFO, "Autocrop: threshold=%d\n",
+	    vf->priv->limit);
+    }
+    else
+    {
+	    mp_msg(MSGT_VFILTER, MSGL_INFO, "Crop: %d x %d, %d ; %d\n",
+	    vf->priv->crop_w,
+	    vf->priv->crop_h,
+	    vf->priv->crop_x,
+	    vf->priv->crop_y);
+    }
     return 1;
 }
 
@@ -171,6 +308,7 @@
   {"h", ST_OFF(crop_h), CONF_TYPE_INT, M_OPT_MIN,0 ,0, NULL},
   {"x", ST_OFF(crop_x), CONF_TYPE_INT, M_OPT_MIN,-1 ,0, NULL},
   {"y", ST_OFF(crop_y), CONF_TYPE_INT, M_OPT_MIN,-1 ,0, NULL},
+  {"limit", ST_OFF(limit), CONF_TYPE_INT, M_OPT_MIN,-1 ,0, NULL},
   { NULL, NULL, 0, 0, 0, 0,  NULL }
 };
 
Index: DOCS/tech/slave.txt
===================================================================
--- DOCS/tech/slave.txt	(revision 23572)
+++ DOCS/tech/slave.txt	(working copy)
@@ -41,13 +41,14 @@
     <value> is in the range [-100, 100].
 
 change_rectangle <val1> <val2>
-    Change the position of the rectangle filter rectangle.
+    Change the position of the rectangle filter and the crop filter rectangle.
         <val1>
             Must be one of the following:
                 0 = width
                 1 = height
                 2 = x position
                 3 = y position
+		4 = use autocrop values (only for crop filter)
         <val2>
             If <val1> is 0 or 1:
                 Integer amount to add/subtract from the width/height.
@@ -57,6 +58,11 @@
                 Relative integer amount by which to move the upper left
                 rectangle corner. Positive values move the rectangle
                 right/down and negative values move the rectangle left/up.
+	    If <val1> is 4:
+		New luma threshold for builtin cropdetect filter.
+		Special values:
+		-1: Disable cropdetect
+		-2: Keep previous value
 
 dvb_set_channel <channel_number> <card_number>
     Set DVB channel.
Index: DOCS/man/en/mplayer.1
===================================================================
--- DOCS/man/en/mplayer.1	(revision 23572)
+++ DOCS/man/en/mplayer.1	(working copy)
@@ -5098,15 +5098,25 @@
 Available filters are:
 .
 .TP
-.B crop[=w:h:x:y]
+.B crop[=w:h:x:y:limit]
 Crops the given part of the image and discards the rest.
 Useful to remove black bands from widescreen movies.
+If limit is set to a non-negative value, autocrop is enabled and the video
+is scanned for black borders. The scanned values are applied by the 
+input.conf directive 'change_rectangle' that takes two parameters.
+Give the filter a few seconds to detect the optimal crop boundaries
+before triggering the actual cropping with 'change_rectangle'.
 .PD 0
 .RSs
 .IPs <w>,<h>
-Cropped width and height, defaults to original width and height.
+Cropped width and height. If the crop area is not supplied, autocrop 
+is enabled with limit=24, except if limit is also explicitly set to a 
+negative value, in which case it defaults to the original width and height.
 .IPs <x>,<y>
 Position of the cropped picture, defaults to center.
+.IPs <limit>
+Luma threshold for builtin cropdetect filter. It is the same as for the
+standalone cropdetect.
 .RE
 .PD 1
 .
Index: command.c
===================================================================
--- command.c	(revision 23572)
+++ command.c	(working copy)
@@ -2461,6 +2461,7 @@
 
 	case MP_CMD_VF_CHANGE_RECTANGLE:
 	    set_rectangle(sh_video, cmd->args[0].v.i, cmd->args[1].v.i);
+	    mpcodecs_config_vo(sh_video, sh_video->disp_w, sh_video->disp_h, 0);
 	    break;
 
 	case MP_CMD_GET_TIME_LENGTH:{


More information about the MPlayer-dev-eng mailing list