Commit 15ed7cfb authored by Antoine Cellerier's avatar Antoine Cellerier

* new gradient and edge detection video filters (sobel gradient)

parent 97d37e89
/***************************************************************************** /*****************************************************************************
* distort.c : Misc video effects plugin for vlc * distort.c : Misc video effects plugin for vlc
***************************************************************************** *****************************************************************************
* Copyright (C) 2000, 2001, 2002, 2003 the VideoLAN team * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 the VideoLAN team
* $Id$ * $Id$
* *
* Authors: Samuel Hocevar <sam@zoy.org> * Authors: Samuel Hocevar <sam@zoy.org>
* Antoine Cellerier <dionoea -at- videolan -dot- org>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -34,8 +35,10 @@ ...@@ -34,8 +35,10 @@
#include "filter_common.h" #include "filter_common.h"
#define DISTORT_MODE_WAVE 1 #define DISTORT_MODE_WAVE 1
#define DISTORT_MODE_RIPPLE 2 #define DISTORT_MODE_RIPPLE 2
#define DISTORT_MODE_GRADIENT 3
#define DISTORT_MODE_EDGE 4
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
...@@ -49,6 +52,8 @@ static void Render ( vout_thread_t *, picture_t * ); ...@@ -49,6 +52,8 @@ static void Render ( vout_thread_t *, picture_t * );
static void DistortWave ( vout_thread_t *, picture_t *, picture_t * ); static void DistortWave ( vout_thread_t *, picture_t *, picture_t * );
static void DistortRipple ( vout_thread_t *, picture_t *, picture_t * ); static void DistortRipple ( vout_thread_t *, picture_t *, picture_t * );
static void DistortGradient( vout_thread_t *, picture_t *, picture_t * );
static void DistortEdge ( vout_thread_t *, picture_t *, picture_t * );
static int SendEvents ( vlc_object_t *, char const *, static int SendEvents ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * ); vlc_value_t, vlc_value_t, void * );
...@@ -57,10 +62,16 @@ static int SendEvents ( vlc_object_t *, char const *, ...@@ -57,10 +62,16 @@ static int SendEvents ( vlc_object_t *, char const *,
* Module descriptor * Module descriptor
*****************************************************************************/ *****************************************************************************/
#define MODE_TEXT N_("Distort mode") #define MODE_TEXT N_("Distort mode")
#define MODE_LONGTEXT N_("Distort mode, one of \"wave\" and \"ripple\"") #define MODE_LONGTEXT N_("Distort mode, one of \"wave\", \"ripple\", \"gradient\" and \"edge\"")
static char *mode_list[] = { "wave", "ripple" }; #define GRADIENT_TEXT N_("Gradient image type")
static char *mode_list_text[] = { N_("Wave"), N_("Ripple") }; #define GRADIENT_LONGTEXT N_("Gradient image type (0 or 1)")
#define CARTOON_TEXT N_("Apply cartoon effect")
#define CARTOON_LONGTEXT N_("Apply cartoon effect. Used by \"gradient\" and \"edge\".")
static char *mode_list[] = { "wave", "ripple", "gradient", "edge" };
static char *mode_list_text[] = { N_("Wave"), N_("Ripple"), N_("gradient"), N_("Edge") };
vlc_module_begin(); vlc_module_begin();
set_description( _("Distort video filter") ); set_description( _("Distort video filter") );
...@@ -73,6 +84,11 @@ vlc_module_begin(); ...@@ -73,6 +84,11 @@ vlc_module_begin();
VLC_FALSE ); VLC_FALSE );
change_string_list( mode_list, mode_list_text, 0 ); change_string_list( mode_list, mode_list_text, 0 );
add_integer_with_range( "distort-gradient-type", 0, 0, 1, NULL,
GRADIENT_TEXT, GRADIENT_LONGTEXT, VLC_FALSE );
add_bool( "distort-cartoon", 1, NULL,
CARTOON_TEXT, CARTOON_LONGTEXT, VLC_FALSE );
add_shortcut( "distort" ); add_shortcut( "distort" );
set_callbacks( Create, Destroy ); set_callbacks( Create, Destroy );
vlc_module_end(); vlc_module_end();
...@@ -91,6 +107,10 @@ struct vout_sys_t ...@@ -91,6 +107,10 @@ struct vout_sys_t
/* For the wave mode */ /* For the wave mode */
double f_angle; double f_angle;
mtime_t last_date; mtime_t last_date;
/* For the gradient mode */
int i_gradient_type;
vlc_bool_t b_cartoon;
}; };
/***************************************************************************** /*****************************************************************************
...@@ -146,6 +166,14 @@ static int Create( vlc_object_t *p_this ) ...@@ -146,6 +166,14 @@ static int Create( vlc_object_t *p_this )
{ {
p_vout->p_sys->i_mode = DISTORT_MODE_RIPPLE; p_vout->p_sys->i_mode = DISTORT_MODE_RIPPLE;
} }
else if( !strcmp( psz_method, "gradient" ) )
{
p_vout->p_sys->i_mode = DISTORT_MODE_GRADIENT;
}
else if( !strcmp( psz_method, "edge" ) )
{
p_vout->p_sys->i_mode = DISTORT_MODE_EDGE;
}
else else
{ {
msg_Err( p_vout, "no valid distort mode provided, " msg_Err( p_vout, "no valid distort mode provided, "
...@@ -155,6 +183,11 @@ static int Create( vlc_object_t *p_this ) ...@@ -155,6 +183,11 @@ static int Create( vlc_object_t *p_this )
} }
free( psz_method_tmp ); free( psz_method_tmp );
p_vout->p_sys->i_gradient_type =
config_GetInt( p_vout, "distort-gradient-type" );
p_vout->p_sys->b_cartoon =
config_GetInt( p_vout, "distort-cartoon" );
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -271,6 +304,14 @@ static void Render( vout_thread_t *p_vout, picture_t *p_pic ) ...@@ -271,6 +304,14 @@ static void Render( vout_thread_t *p_vout, picture_t *p_pic )
DistortRipple( p_vout, p_pic, p_outpic ); DistortRipple( p_vout, p_pic, p_outpic );
break; break;
case DISTORT_MODE_EDGE:
DistortEdge( p_vout, p_pic, p_outpic );
break;
case DISTORT_MODE_GRADIENT:
DistortGradient( p_vout, p_pic, p_outpic );
break;
default: default:
break; break;
} }
...@@ -425,6 +466,339 @@ static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic, ...@@ -425,6 +466,339 @@ static void DistortRipple( vout_thread_t *p_vout, picture_t *p_inpic,
} }
} }
/*****************************************************************************
* DistortGradient: Sobel
*****************************************************************************/
static void DistortGradient( vout_thread_t *p_vout, picture_t *p_inpic,
picture_t *p_outpic )
{
int x, y;
int i_height = p_inpic->format.i_height;
int i_width = p_inpic->format.i_width;
uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
uint32_t p_smooth[ i_height * i_width ];
if( p_vout->p_sys->b_cartoon )
{
memcpy( p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
memcpy( p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
}
else
{
memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
}
/* Gaussian convolution ( sigma == 1.4 )
| 2 4 5 4 2 | | 2 4 4 4 2 |
| 4 9 12 9 4 | | 4 8 12 8 4 |
| 5 12 15 12 5 | ~ | 4 12 16 12 4 |
| 4 9 12 9 4 | | 4 8 12 8 4 |
| 2 4 5 4 2 | | 2 4 4 4 2 | */
for( y = 2; y < i_height - 2; y++ )
{
for( x = 2; x < i_width - 2; x++ )
{
p_smooth[y*i_width+x] = (
/* 2 rows up */
( p_inpix[(y-2)*i_width+x-2]<<1 )
+ ( p_inpix[(y-2)*i_width+x-1]<<2 )
+ ( p_inpix[(y-2)*i_width+x]<<2 )
+ ( p_inpix[(y-2)*i_width+x+1]<<2 )
+ ( p_inpix[(y-2)*i_width+x+2]<<1 )
/* 1 row up */
+ ( p_inpix[(y-1)*i_width+x-1]<<3 )
+ ( p_inpix[(y-1)*i_width+x-2]<<2 )
+ ( p_inpix[(y-1)*i_width+x]*12 )
+ ( p_inpix[(y-1)*i_width+x+1]<<3 )
+ ( p_inpix[(y-1)*i_width+x+2]<<2 )
/* */
+ ( p_inpix[y*i_width+x-2]<<2 )
+ ( p_inpix[y*i_width+x-1]*12 )
+ ( p_inpix[y*i_width+x]<<4 )
+ ( p_inpix[y*i_width+x+1]*12 )
+ ( p_inpix[y*i_width+x+2]<<2 )
/* 1 row down */
+ ( p_inpix[(y+1)*i_width+x-2]<<2 )
+ ( p_inpix[(y+1)*i_width+x-1]<<3 )
+ ( p_inpix[(y+1)*i_width+x]*12 )
+ ( p_inpix[(y+1)*i_width+x+1]<<3 )
+ ( p_inpix[(y+1)*i_width+x+2]<<2 )
/* 2 rows down */
+ ( p_inpix[(y+2)*i_width+x-2]<<1 )
+ ( p_inpix[(y+2)*i_width+x-1]<<2 )
+ ( p_inpix[(y+2)*i_width+x]<<2 )
+ ( p_inpix[(y+2)*i_width+x+1]<<2 )
+ ( p_inpix[(y+2)*i_width+x+2]<<1 )
) >> 7 /* 115 */;
}
}
/* Sobel gradient
| -1 0 1 | | 1 2 1 |
| -2 0 2 | and | 0 0 0 |
| -1 0 1 | | -1 -2 -1 | */
for( y = 1; y < i_height - 1; y++ )
{
for( x = 1; x < i_width - 1; x++ )
{
uint32_t a =
(
abs(
((p_smooth[(y-1)*i_width+x] - p_smooth[(y+1)*i_width+x])<<1)
+ (p_smooth[(y-1)*i_width+x-1] - p_smooth[(y+1)*i_width+x-1])
+ (p_smooth[(y-1)*i_width+x+1] - p_smooth[(y+1)*i_width+x+1])
)
+
abs(
((p_smooth[y*i_width+x-1] - p_smooth[y*i_width+x+1])<<1)
+ (p_smooth[(y-1)*i_width+x-1] - p_smooth[(y-1)*i_width+x+1])
+ (p_smooth[(y+1)*i_width+x-1] - p_smooth[(y+1)*i_width+x+1])
)
);
if( p_vout->p_sys->i_gradient_type )
{
if( p_vout->p_sys->b_cartoon )
{
if( a > 60 )
{
p_outpix[y*i_width+x] = 0x00;
}
else
{
if( p_smooth[y*i_width+x] > 0xa0 )
p_outpix[y*i_width+x] =
0xff - ((0xff - p_inpix[y*i_width+x] )>>2);
else if( p_smooth[y*i_width+x] > 0x70 )
p_outpix[y*i_width+x] =
0xa0 - ((0xa0 - p_inpix[y*i_width+x] )>>2);
else if( p_smooth[y*i_width+x] > 0x28 )
p_outpix[y*i_width+x] =
0x70 - ((0x70 - p_inpix[y*i_width+x] )>>2);
else
p_outpix[y*i_width+x] =
0x28 - ((0x28 - p_inpix[y*i_width+x] )>>2);
}
}
else
{
if( a>>8 )
p_outpix[y*i_width+x] = 255;
else
p_outpix[y*i_width+x] = (uint8_t)a;
}
}
else
{
if( a>>8 )
p_outpix[y*i_width+x] = 0;
else
p_outpix[y*i_width+x] = (uint8_t)(255 - a);
}
}
}
}
/*****************************************************************************
* DistortEdge: Canny edge detection algorithm
*****************************************************************************
* http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
* (well ... my implementation isn't really the canny algorithm ... but some
* ideas are the same)
*****************************************************************************/
/* angle : | */
#define THETA_Y 0
/* angle : - */
#define THETA_X 1
/* angle : / */
#define THETA_P 2
/* angle : \ */
#define THETA_M 3
static void DistortEdge( vout_thread_t *p_vout, picture_t *p_inpic,
picture_t *p_outpic )
{
int x, y;
int i_height = p_inpic->format.i_height;
int i_width = p_inpic->format.i_width;
uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
uint32_t p_smooth[ i_height * i_width ];
uint32_t p_grad[ i_height * i_width ];
uint8_t p_theta[ i_height * i_width ];
if( p_vout->p_sys->b_cartoon )
{
memcpy( p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
memcpy( p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
}
else
{
memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
}
/* Gaussian convolution ( sigma == 1.4 )
| 2 4 5 4 2 | | 2 4 4 4 2 |
| 4 9 12 9 4 | | 4 8 12 8 4 |
| 5 12 15 12 5 | ~ | 4 12 16 12 4 |
| 4 9 12 9 4 | | 4 8 12 8 4 |
| 2 4 5 4 2 | | 2 4 4 4 2 | */
for( y = 2; y < i_height - 2; y++ )
{
for( x = 2; x < i_width - 2; x++ )
{
p_smooth[y*i_width+x] = (
/* 2 rows up */
( p_inpix[(y-2)*i_width+x-2]<<1 )
+ ( p_inpix[(y-2)*i_width+x-1]<<2 )
+ ( p_inpix[(y-2)*i_width+x]<<2 )
+ ( p_inpix[(y-2)*i_width+x+1]<<2 )
+ ( p_inpix[(y-2)*i_width+x+2]<<1 )
/* 1 row up */
+ ( p_inpix[(y-1)*i_width+x-1]<<3 )
+ ( p_inpix[(y-1)*i_width+x-2]<<2 )
+ ( p_inpix[(y-1)*i_width+x]*12 )
+ ( p_inpix[(y-1)*i_width+x+1]<<3 )
+ ( p_inpix[(y-1)*i_width+x+2]<<2 )
/* */
+ ( p_inpix[y*i_width+x-2]<<2 )
+ ( p_inpix[y*i_width+x-1]*12 )
+ ( p_inpix[y*i_width+x]<<4 )
+ ( p_inpix[y*i_width+x+1]*12 )
+ ( p_inpix[y*i_width+x+2]<<2 )
/* 1 row down */
+ ( p_inpix[(y+1)*i_width+x-2]<<2 )
+ ( p_inpix[(y+1)*i_width+x-1]<<3 )
+ ( p_inpix[(y+1)*i_width+x]*12 )
+ ( p_inpix[(y+1)*i_width+x+1]<<3 )
+ ( p_inpix[(y+1)*i_width+x+2]<<2 )
/* 2 rows down */
+ ( p_inpix[(y+2)*i_width+x-2]<<1 )
+ ( p_inpix[(y+2)*i_width+x-1]<<2 )
+ ( p_inpix[(y+2)*i_width+x]<<2 )
+ ( p_inpix[(y+2)*i_width+x+1]<<2 )
+ ( p_inpix[(y+2)*i_width+x+2]<<1 )
) >> 7 /* 115 */;
}
}
/* Sobel gradient
| -1 0 1 | | 1 2 1 |
| -2 0 2 | and | 0 0 0 |
| -1 0 1 | | -1 -2 -1 | */
for( y = 1; y < i_height - 1; y++ )
{
for( x = 1; x < i_width - 1; x++ )
{
int gradx =
((p_smooth[(y-1)*i_width+x] - p_smooth[(y+1)*i_width+x])<<1)
+ (p_smooth[(y-1)*i_width+x-1] - p_smooth[(y+1)*i_width+x-1])
+ (p_smooth[(y-1)*i_width+x+1] - p_smooth[(y+1)*i_width+x+1]);
int grady =
((p_smooth[y*i_width+x-1] - p_smooth[y*i_width+x+1])<<1)
+ (p_smooth[(y-1)*i_width+x-1] - p_smooth[(y-1)*i_width+x+1])
+ (p_smooth[(y+1)*i_width+x-1] - p_smooth[(y+1)*i_width+x+1]);
p_grad[y*i_width+x] = abs( gradx ) + abs( grady );
/* tan( 22.5 ) = 0,414213562 .. * 128 = 53
* tan( 26,565051177 ) = 0.5
* tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
* tan( 63,434948823 ) 2 */
if( (grady<<1) > gradx )
p_theta[y*i_width+x] = THETA_P;
else if( (grady<<1) < -gradx )
p_theta[y*i_width+x] = THETA_M;
else if( !gradx || abs(grady) > abs(gradx)<<1 )
p_theta[y*i_width+x] = THETA_Y;
else
p_theta[y*i_width+x] = THETA_X;
}
}
/* edge computing */
for( y = 1; y < i_height - 1; y++ )
{
for( x = 1; x < i_width - 1; x++ )
{
if( p_grad[y*i_width+x] > 40 )
{
switch( p_theta[y*i_width+x] )
{
case THETA_Y:
if( p_grad[y*i_width+x] > p_grad[(y-1)*i_width+x]
&& p_grad[y*i_width+x] > p_grad[(y+1)*i_width+x] )
{
p_outpix[y*i_width+x] = 0;
} else goto colorize;
break;
case THETA_P:
if( p_grad[y*i_width+x] > p_grad[(y-1)*i_width+x-1]
&& p_grad[y*i_width+x] > p_grad[(y+1)*i_width+x+1] )
{
p_outpix[y*i_width+x] = 0;
} else goto colorize;
break;
case THETA_M:
if( p_grad[y*i_width+x] > p_grad[(y-1)*i_width+x+1]
&& p_grad[y*i_width+x] > p_grad[(y+1)*i_width+x-1] )
{
p_outpix[y*i_width+x] = 0;
} else goto colorize;
break;
case THETA_X:
if( p_grad[y*i_width+x] > p_grad[y*i_width+x-1]
&& p_grad[y*i_width+x] > p_grad[y*i_width+x+1] )
{
p_outpix[y*i_width+x] = 0;
} else goto colorize;
break;
}
}
else
{
colorize:
if( p_vout->p_sys->b_cartoon )
{
if( p_smooth[y*i_width+x] > 0xa0 )
p_outpix[y*i_width+x] =
0xff - ((0xff - p_inpix[y*i_width+x] )>>2);
else if( p_smooth[y*i_width+x] > 0x70 )
p_outpix[y*i_width+x] =
0xa0 - ((0xa0 - p_inpix[y*i_width+x] )>>2);
else if( p_smooth[y*i_width+x] > 0x28 )
p_outpix[y*i_width+x] =
0x70 - ((0x70 - p_inpix[y*i_width+x] )>>2);
else
p_outpix[y*i_width+x] =
0x28 - ((0x28 - p_inpix[y*i_width+x] )>>2);
}
}
}
}
}
/***************************************************************************** /*****************************************************************************
* SendEvents: forward mouse and keyboard events to the parent p_vout * SendEvents: forward mouse and keyboard events to the parent p_vout
*****************************************************************************/ *****************************************************************************/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment