]> git.sesse.net Git - vlc/blob - modules/video_filter/gradient.c
Use %4.4s instead of %4s for fourccs cast to char *.
[vlc] / modules / video_filter / gradient.c
1 /*****************************************************************************
2  * gradient.c : Gradient and edge detection video effects plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Antoine Cellerier <dionoea -at- videolan -dot- org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <math.h>                                            /* sin(), cos() */
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_sout.h>
38
39 #include <vlc_filter.h>
40 #include "filter_picture.h"
41
42 enum { GRADIENT, EDGE, HOUGH };
43
44 /*****************************************************************************
45  * Local prototypes
46  *****************************************************************************/
47 static int  Create    ( vlc_object_t * );
48 static void Destroy   ( vlc_object_t * );
49
50 static picture_t *Filter( filter_t *, picture_t * );
51 static int GradientCallback( vlc_object_t *, char const *,
52                              vlc_value_t, vlc_value_t,
53                              void * );
54
55 static void FilterGradient( filter_t *, picture_t *, picture_t * );
56 static void FilterEdge    ( filter_t *, picture_t *, picture_t * );
57 static void FilterHough   ( filter_t *, picture_t *, picture_t * );
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 #define MODE_TEXT N_("Distort mode")
63 #define MODE_LONGTEXT N_("Distort mode, one of \"gradient\", \"edge\" and \"hough\".")
64
65 #define GRADIENT_TEXT N_("Gradient image type")
66 #define GRADIENT_LONGTEXT N_("Gradient image type (0 or 1). 0 will " \
67         "turn the image to white while 1 will keep colors." )
68
69 #define CARTOON_TEXT N_("Apply cartoon effect")
70 #define CARTOON_LONGTEXT N_("Apply cartoon effect. It is only used by " \
71     "\"gradient\" and \"edge\".")
72
73 #define GRADIENT_HELP N_("Apply color gradient or edge detection effects")
74
75 static const char *const mode_list[] = { "gradient", "edge", "hough" };
76 static const char *const mode_list_text[] = { N_("Gradient"), N_("Edge"), N_("Hough") };
77
78 #define FILTER_PREFIX "gradient-"
79
80 vlc_module_begin ()
81     set_description( N_("Gradient video filter") )
82     set_shortname( N_( "Gradient" ))
83     set_help(GRADIENT_HELP)
84     set_capability( "video filter2", 0 )
85     set_category( CAT_VIDEO )
86     set_subcategory( SUBCAT_VIDEO_VFILTER )
87
88     add_string( FILTER_PREFIX "mode", "gradient", NULL,
89                 MODE_TEXT, MODE_LONGTEXT, false )
90         change_string_list( mode_list, mode_list_text, 0 )
91
92     add_integer_with_range( FILTER_PREFIX "type", 0, 0, 1, NULL,
93                 GRADIENT_TEXT, GRADIENT_LONGTEXT, false )
94     add_bool( FILTER_PREFIX "cartoon", true, NULL,
95                 CARTOON_TEXT, CARTOON_LONGTEXT, false )
96
97     add_shortcut( "gradient" )
98     set_callbacks( Create, Destroy )
99 vlc_module_end ()
100
101 static const char *const ppsz_filter_options[] = {
102     "mode", "type", "cartoon", NULL
103 };
104
105 /*****************************************************************************
106  * filter_sys_t: Distort video output method descriptor
107  *****************************************************************************
108  * This structure is part of the video output thread descriptor.
109  * It describes the Distort specific properties of an output thread.
110  *****************************************************************************/
111 struct filter_sys_t
112 {
113     vlc_mutex_t lock;
114     int i_mode;
115
116     /* For the gradient mode */
117     int i_gradient_type;
118     bool b_cartoon;
119
120     uint32_t *p_buf32;
121     uint32_t *p_buf32_bis;
122     uint8_t *p_buf8;
123
124     /* For hough mode */
125     int *p_pre_hough;
126 };
127
128 /*****************************************************************************
129  * Create: allocates Distort video thread output method
130  *****************************************************************************
131  * This function allocates and initializes a Distort vout method.
132  *****************************************************************************/
133 static int Create( vlc_object_t *p_this )
134 {
135     filter_t *p_filter = (filter_t *)p_this;
136     char *psz_method;
137
138     switch( p_filter->fmt_in.video.i_chroma )
139     {
140         CASE_PLANAR_YUV
141             break;
142
143         default:
144              msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
145                       (char*)&(p_filter->fmt_in.video.i_chroma) );
146             return VLC_EGENERIC;
147     }
148
149     /* Allocate structure */
150     p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
151     if( p_filter->p_sys == NULL )
152         return VLC_ENOMEM;
153
154     p_filter->pf_video_filter = Filter;
155
156     p_filter->p_sys->p_pre_hough = NULL;
157
158     config_ChainParse( p_filter, FILTER_PREFIX, ppsz_filter_options,
159                    p_filter->p_cfg );
160
161     if( !(psz_method =
162         var_CreateGetNonEmptyStringCommand( p_filter, FILTER_PREFIX "mode" )) )
163     {
164         msg_Err( p_filter, "configuration variable "
165                  FILTER_PREFIX "mode empty" );
166         p_filter->p_sys->i_mode = GRADIENT;
167     }
168     else
169     {
170         if( !strcmp( psz_method, "gradient" ) )
171         {
172             p_filter->p_sys->i_mode = GRADIENT;
173         }
174         else if( !strcmp( psz_method, "edge" ) )
175         {
176             p_filter->p_sys->i_mode = EDGE;
177         }
178         else if( !strcmp( psz_method, "hough" ) )
179         {
180             p_filter->p_sys->i_mode = HOUGH;
181         }
182         else
183         {
184             msg_Err( p_filter, "no valid gradient mode provided (%s)", psz_method );
185             p_filter->p_sys->i_mode = GRADIENT;
186         }
187     }
188     free( psz_method );
189
190     p_filter->p_sys->i_gradient_type =
191         var_CreateGetIntegerCommand( p_filter, FILTER_PREFIX "type" );
192     p_filter->p_sys->b_cartoon =
193         var_CreateGetBoolCommand( p_filter, FILTER_PREFIX "cartoon" );
194
195     vlc_mutex_init( &p_filter->p_sys->lock );
196     var_AddCallback( p_filter, FILTER_PREFIX "mode",
197                      GradientCallback, p_filter->p_sys );
198     var_AddCallback( p_filter, FILTER_PREFIX "type",
199                      GradientCallback, p_filter->p_sys );
200     var_AddCallback( p_filter, FILTER_PREFIX "cartoon",
201                      GradientCallback, p_filter->p_sys );
202
203     p_filter->p_sys->p_buf32 = NULL;
204     p_filter->p_sys->p_buf32_bis = NULL;
205     p_filter->p_sys->p_buf8 = NULL;
206
207     return VLC_SUCCESS;
208 }
209
210 /*****************************************************************************
211  * Destroy: destroy Distort video thread output method
212  *****************************************************************************
213  * Terminate an output method created by DistortCreateOutputMethod
214  *****************************************************************************/
215 static void Destroy( vlc_object_t *p_this )
216 {
217     filter_t *p_filter = (filter_t *)p_this;
218     filter_sys_t *p_sys = p_filter->p_sys;
219
220     var_DelCallback( p_filter, FILTER_PREFIX "mode",
221                      GradientCallback, p_sys );
222     var_DelCallback( p_filter, FILTER_PREFIX "type",
223                      GradientCallback, p_sys );
224     var_DelCallback( p_filter, FILTER_PREFIX "cartoon",
225                      GradientCallback, p_sys );
226     vlc_mutex_destroy( &p_sys->lock );
227
228     free( p_sys->p_buf32 );
229     free( p_sys->p_buf32_bis );
230     free( p_sys->p_buf8 );
231     free( p_sys->p_pre_hough );
232
233     free( p_sys );
234 }
235
236 /*****************************************************************************
237  * Render: displays previously rendered output
238  *****************************************************************************
239  * This function send the currently rendered image to Distort image, waits
240  * until it is displayed and switch the two rendering buffers, preparing next
241  * frame.
242  *****************************************************************************/
243 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
244 {
245     picture_t *p_outpic;
246
247     if( !p_pic ) return NULL;
248
249     p_outpic = filter_NewPicture( p_filter );
250     if( !p_outpic )
251     {
252         picture_Release( p_pic );
253         return NULL;
254     }
255
256     vlc_mutex_lock( &p_filter->p_sys->lock );
257     switch( p_filter->p_sys->i_mode )
258     {
259         case EDGE:
260             FilterEdge( p_filter, p_pic, p_outpic );
261             break;
262
263         case GRADIENT:
264             FilterGradient( p_filter, p_pic, p_outpic );
265             break;
266
267         case HOUGH:
268             FilterHough( p_filter, p_pic, p_outpic );
269             break;
270
271         default:
272             break;
273     }
274     vlc_mutex_unlock( &p_filter->p_sys->lock );
275
276     return CopyInfoAndRelease( p_outpic, p_pic );
277 }
278
279 /*****************************************************************************
280  * Gaussian Convolution
281  *****************************************************************************
282  *    Gaussian convolution ( sigma == 1.4 )
283  *
284  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
285  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
286  *    |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
287  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
288  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
289  *****************************************************************************/
290 static void GaussianConvolution( picture_t *p_inpic, uint32_t *p_smooth )
291 {
292     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
293     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
294     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
295     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
296
297     int x,y;
298     for( y = 2; y < i_num_lines - 2; y++ )
299     {
300         for( x = 2; x < i_src_visible - 2; x++ )
301         {
302             p_smooth[y*i_src_visible+x] = (uint32_t)(
303               /* 2 rows up */
304                 ( p_inpix[(y-2)*i_src_pitch+x-2] )
305               + ((p_inpix[(y-2)*i_src_pitch+x-1]
306               +   p_inpix[(y-2)*i_src_pitch+x]
307               +   p_inpix[(y-2)*i_src_pitch+x+1])<<1 )
308               + ( p_inpix[(y-2)*i_src_pitch+x+2] )
309               /* 1 row up */
310               + ((p_inpix[(y-1)*i_src_pitch+x-2]
311               + ( p_inpix[(y-1)*i_src_pitch+x-1]<<1 )
312               + ( p_inpix[(y-1)*i_src_pitch+x]*3 )
313               + ( p_inpix[(y-1)*i_src_pitch+x+1]<<1 )
314               +   p_inpix[(y-1)*i_src_pitch+x+2]
315               /* */
316               +   p_inpix[y*i_src_pitch+x-2]
317               + ( p_inpix[y*i_src_pitch+x-1]*3 )
318               + ( p_inpix[y*i_src_pitch+x]<<2 )
319               + ( p_inpix[y*i_src_pitch+x+1]*3 )
320               +   p_inpix[y*i_src_pitch+x+2]
321               /* 1 row down */
322               +   p_inpix[(y+1)*i_src_pitch+x-2]
323               + ( p_inpix[(y+1)*i_src_pitch+x-1]<<1 )
324               + ( p_inpix[(y+1)*i_src_pitch+x]*3 )
325               + ( p_inpix[(y+1)*i_src_pitch+x+1]<<1 )
326               +   p_inpix[(y+1)*i_src_pitch+x+2] )<<1 )
327               /* 2 rows down */
328               + ( p_inpix[(y+2)*i_src_pitch+x-2] )
329               + ((p_inpix[(y+2)*i_src_pitch+x-1]
330               +   p_inpix[(y+2)*i_src_pitch+x]
331               +   p_inpix[(y+2)*i_src_pitch+x+1])<<1 )
332               + ( p_inpix[(y+2)*i_src_pitch+x+2] )
333               ) >> 6 /* 115 */;
334         }
335     }
336 }
337
338 /*****************************************************************************
339  * FilterGradient: Sobel
340  *****************************************************************************/
341 static void FilterGradient( filter_t *p_filter, picture_t *p_inpic,
342                                                 picture_t *p_outpic )
343 {
344     int x, y;
345     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
346     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
347     const int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
348     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
349
350     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
351     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
352
353     uint32_t *p_smooth;
354     if( !p_filter->p_sys->p_buf32 )
355         p_filter->p_sys->p_buf32 =
356         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
357     p_smooth = p_filter->p_sys->p_buf32;
358
359     if( !p_smooth ) return;
360
361     if( p_filter->p_sys->b_cartoon )
362     {
363         vlc_memcpy( p_outpic->p[U_PLANE].p_pixels,
364             p_inpic->p[U_PLANE].p_pixels,
365             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
366         vlc_memcpy( p_outpic->p[V_PLANE].p_pixels,
367             p_inpic->p[V_PLANE].p_pixels,
368             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
369     }
370     else
371     {
372         vlc_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
373             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
374         vlc_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
375             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
376     }
377
378     GaussianConvolution( p_inpic, p_smooth );
379
380     /* Sobel gradient
381
382      | -1 0 1 |     |  1  2  1 |
383      | -2 0 2 | and |  0  0  0 |
384      | -1 0 1 |     | -1 -2 -1 | */
385
386 #define FOR                                                     \
387     for( y = 1; y < i_num_lines - 1; y++ )                      \
388     {                                                           \
389         for( x = 1; x < i_src_visible - 1; x++ )                \
390         {                                                       \
391             const uint32_t a =                                  \
392             (                                                   \
393               abs(                                              \
394                  ( p_smooth[(y-1)*i_src_visible+x-1]            \
395                    - p_smooth[(y+1)*i_src_visible+x-1] )        \
396                + ( ( p_smooth[(y-1)*i_src_visible+x]            \
397                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )   \
398                + ( p_smooth[(y-1)*i_src_visible+x+1]            \
399                    - p_smooth[(y+1)*i_src_visible+x+1] )        \
400               )                                                 \
401             +                                                   \
402               abs(                                              \
403                  ( p_smooth[(y-1)*i_src_visible+x-1]            \
404                    - p_smooth[(y-1)*i_src_visible+x+1] )        \
405                + ( ( p_smooth[y*i_src_visible+x-1]              \
406                     - p_smooth[y*i_src_visible+x+1] ) <<1 )     \
407                + ( p_smooth[(y+1)*i_src_visible+x-1]            \
408                    - p_smooth[(y+1)*i_src_visible+x+1] )        \
409               )                                                 \
410             );
411     if( p_filter->p_sys->i_gradient_type )
412     {
413         if( p_filter->p_sys->b_cartoon )
414         {
415             FOR
416             if( a > 60 )
417             {
418                 p_outpix[y*i_dst_pitch+x] = 0x00;
419             }
420             else
421             {
422                 if( p_smooth[y*i_src_visible+x] > 0xa0 )
423                     p_outpix[y*i_dst_pitch+x] =
424                         0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
425                 else if( p_smooth[y*i_src_visible+x] > 0x70 )
426                     p_outpix[y*i_dst_pitch+x] =
427                         0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
428                 else if( p_smooth[y*i_src_visible+x] > 0x28 )
429                     p_outpix[y*i_dst_pitch+x] =
430                         0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
431                 else
432                     p_outpix[y*i_dst_pitch+x] =
433                         0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
434             }
435             }}
436         }
437         else
438         {
439             FOR
440             p_outpix[y*i_dst_pitch+x] = clip_uint8_vlc( a );
441             }}
442         }
443     }
444     else
445     {
446         FOR
447         if( a>>8 )
448             p_outpix[y*i_dst_pitch+x] = 0;
449         else
450             p_outpix[y*i_dst_pitch+x] = 0xff-(uint8_t)a;
451         }}
452     }
453 #undef FOR
454 }
455
456 /*****************************************************************************
457  * FilterEdge: Canny edge detection algorithm
458  *****************************************************************************
459  * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
460  * (well ... my implementation isn't really the canny algorithm ... but some
461  * ideas are the same)
462  *****************************************************************************/
463 /* angle : | */
464 #define THETA_Y 0
465 /* angle : - */
466 #define THETA_X 1
467 /* angle : / */
468 #define THETA_P 2
469 /* angle : \ */
470 #define THETA_M 3
471 static void FilterEdge( filter_t *p_filter, picture_t *p_inpic,
472                                             picture_t *p_outpic )
473 {
474     int x, y;
475
476     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
477     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
478     const int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
479     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
480
481     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
482     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
483
484     uint32_t *p_smooth;
485     uint32_t *p_grad;
486     uint8_t *p_theta;
487
488     if( !p_filter->p_sys->p_buf32 )
489         p_filter->p_sys->p_buf32 =
490         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
491     p_smooth = p_filter->p_sys->p_buf32;
492
493     if( !p_filter->p_sys->p_buf32_bis )
494         p_filter->p_sys->p_buf32_bis =
495         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
496     p_grad = p_filter->p_sys->p_buf32_bis;
497
498     if( !p_filter->p_sys->p_buf8 )
499         p_filter->p_sys->p_buf8 =
500         (uint8_t *)malloc( i_num_lines * i_src_visible * sizeof(uint8_t));
501     p_theta = p_filter->p_sys->p_buf8;
502
503     if( !p_smooth || !p_grad || !p_theta ) return;
504
505     if( p_filter->p_sys->b_cartoon )
506     {
507         vlc_memcpy( p_outpic->p[U_PLANE].p_pixels,
508             p_inpic->p[U_PLANE].p_pixels,
509             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
510         vlc_memcpy( p_outpic->p[V_PLANE].p_pixels,
511             p_inpic->p[V_PLANE].p_pixels,
512             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
513     }
514     else
515     {
516         vlc_memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
517               p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
518         vlc_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
519             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
520         vlc_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
521             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
522     }
523
524     GaussianConvolution( p_inpic, p_smooth );
525
526     /* Sobel gradient
527
528      | -1 0 1 |     |  1  2  1 |
529      | -2 0 2 | and |  0  0  0 |
530      | -1 0 1 |     | -1 -2 -1 | */
531
532     for( y = 1; y < i_num_lines - 1; y++ )
533     {
534         for( x = 1; x < i_src_visible - 1; x++ )
535         {
536
537             const int gradx =
538                  ( p_smooth[(y-1)*i_src_visible+x-1]
539                    - p_smooth[(y+1)*i_src_visible+x-1] )
540                + ( ( p_smooth[(y-1)*i_src_visible+x]
541                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
542                + ( p_smooth[(y-1)*i_src_visible+x+1]
543                    - p_smooth[(y+1)*i_src_visible+x+1] );
544             const int grady =
545                  ( p_smooth[(y-1)*i_src_visible+x-1]
546                    - p_smooth[(y-1)*i_src_visible+x+1] )
547                + ( ( p_smooth[y*i_src_visible+x-1]
548                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
549                + ( p_smooth[(y+1)*i_src_visible+x-1]
550                    - p_smooth[(y+1)*i_src_visible+x+1] );
551
552             p_grad[y*i_src_visible+x] = (uint32_t)(abs( gradx ) + abs( grady ));
553
554             /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
555              * tan( 26,565051177 ) = 0.5
556              * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
557              * tan( 63,434948823 ) 2 */
558             if( (grady<<1) > gradx )
559                 p_theta[y*i_src_visible+x] = THETA_P;
560             else if( (grady<<1) < -gradx )
561                 p_theta[y*i_src_visible+x] = THETA_M;
562             else if( !gradx || abs(grady) > abs(gradx)<<1 )
563                 p_theta[y*i_src_visible+x] = THETA_Y;
564             else
565                 p_theta[y*i_src_visible+x] = THETA_X;
566         }
567     }
568
569     /* edge computing */
570     for( y = 1; y < i_num_lines - 1; y++ )
571     {
572         for( x = 1; x < i_src_visible - 1; x++ )
573         {
574             if( p_grad[y*i_src_visible+x] > 40 )
575             {
576                 switch( p_theta[y*i_src_visible+x] )
577                 {
578                     case THETA_Y:
579                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x]
580                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x] )
581                         {
582                             p_outpix[y*i_dst_pitch+x] = 0;
583                             break;
584                         } else goto colorize;
585                     case THETA_P:
586                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x-1]
587                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x+1] )
588                         {
589                             p_outpix[y*i_dst_pitch+x] = 0;
590                             break;
591                         } else goto colorize;
592                     case THETA_M:
593                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x+1]
594                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x-1] )
595                         {
596                             p_outpix[y*i_dst_pitch+x] = 0;
597                             break;
598                         } else goto colorize;
599                     case THETA_X:
600                         if(    p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x-1]
601                             && p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x+1] )
602                         {
603                             p_outpix[y*i_dst_pitch+x] = 0;
604                             break;
605                         } else goto colorize;
606                 }
607             }
608             else
609             {
610                 colorize:
611                 if( p_filter->p_sys->b_cartoon )
612                 {
613                     if( p_smooth[y*i_src_visible+x] > 0xa0 )
614                         p_outpix[y*i_dst_pitch+x] = (uint8_t)
615                             0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
616                     else if( p_smooth[y*i_src_visible+x] > 0x70 )
617                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
618                             0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
619                     else if( p_smooth[y*i_src_visible+x] > 0x28 )
620                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
621                             0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
622                     else
623                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
624                             0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
625                 }
626             }
627         }
628     }
629 }
630
631 /*****************************************************************************
632  * FilterHough
633  *****************************************************************************/
634 #define p_pre_hough p_filter->p_sys->p_pre_hough
635 static void FilterHough( filter_t *p_filter, picture_t *p_inpic,
636                                              picture_t *p_outpic )
637 {
638     int x, y, i;
639     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
640     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
641     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
642
643     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
644
645     int i_diag = sqrt( i_num_lines * i_num_lines +
646                         i_src_visible * i_src_visible);
647     int i_max, i_phi_max, i_rho, i_rho_max;
648     int i_nb_steps = 90;
649     double d_step = M_PI / i_nb_steps;
650     double d_sin;
651     double d_cos;
652     uint32_t *p_smooth;
653
654     int *p_hough = malloc( i_diag * i_nb_steps * sizeof(int) );
655     if( ! p_hough ) return;
656
657     p_smooth = (uint32_t *)malloc( i_num_lines*i_src_visible*sizeof(uint32_t));
658     if( !p_smooth )
659     {
660         free( p_hough );
661         return;
662     }
663
664     if( ! p_pre_hough )
665     {
666         msg_Dbg(p_filter, "Starting precalculation");
667         p_pre_hough = malloc( i_num_lines*i_src_visible*i_nb_steps*sizeof(int));
668         if( ! p_pre_hough )
669         {
670             free( p_smooth );
671             free( p_hough );
672             return;
673         }
674         for( i = 0 ; i < i_nb_steps ; i++)
675         {
676             d_sin = sin(d_step * i);
677             d_cos = cos(d_step * i);
678             for( y = 0 ; y < i_num_lines ; y++ )
679                 for( x = 0 ; x < i_src_visible ; x++ )
680                 {
681                     p_pre_hough[(i*i_num_lines+y)*i_src_visible + x] =
682                         ceil(x*d_sin + y*d_cos);
683                 }
684         }
685         msg_Dbg(p_filter, "Precalculation done");
686     }
687
688     vlc_memset( p_hough, 0, i_diag * i_nb_steps * sizeof(int) );
689
690     vlc_memcpy(
691         p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
692         p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
693     vlc_memcpy(
694         p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
695         p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
696     vlc_memcpy(
697         p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
698         p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
699
700     GaussianConvolution( p_inpic, p_smooth );
701
702     /* Sobel gradient
703
704      | -1 0 1 |     |  1  2  1 |
705      | -2 0 2 | and |  0  0  0 |
706      | -1 0 1 |     | -1 -2 -1 | */
707
708     i_max = 0;
709     i_rho_max = 0;
710     i_phi_max = 0;
711     for( y = 4; y < i_num_lines - 4; y++ )
712     {
713         for( x = 4; x < i_src_visible - 4; x++ )
714         {
715             uint32_t a =
716             (
717               abs(
718                 ( ( p_smooth[(y-1)*i_src_visible+x]
719                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
720                + ( p_smooth[(y-1)*i_src_visible+x-1]
721                    - p_smooth[(y+1)*i_src_visible+x-1] )
722                + ( p_smooth[(y-1)*i_src_visible+x+1]
723                    - p_smooth[(y+1)*i_src_visible+x+1] )
724               )
725             +
726               abs(
727                 ( ( p_smooth[y*i_src_visible+x-1]
728                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
729                + ( p_smooth[(y-1)*i_src_visible+x-1]
730                    - p_smooth[(y-1)*i_src_visible+x+1] )
731                + ( p_smooth[(y+1)*i_src_visible+x-1]
732                    - p_smooth[(y+1)*i_src_visible+x+1] )
733               )
734             );
735             if( a>>8 )
736             {
737                 for( i = 0 ; i < i_nb_steps ; i ++ )
738                 {
739                     i_rho = p_pre_hough[(i*i_num_lines+y)*i_src_visible + x];
740                     if( p_hough[i_rho + i_diag/2 + i * i_diag]++ > i_max )
741                     {
742                         i_max = p_hough[i_rho + i_diag/2 + i * i_diag];
743                         i_rho_max = i_rho;
744                         i_phi_max = i;
745                     }
746                 }
747             }
748         }
749     }
750
751     d_sin = sin(i_phi_max*d_step);
752     d_cos = cos(i_phi_max*d_step);
753     if( d_cos != 0 )
754     {
755         for( x = 0 ; x < i_src_visible ; x++ )
756         {
757             y = (i_rho_max - x * d_sin) / d_cos;
758             if( y >= 0 && y < i_num_lines )
759                 p_outpix[y*i_dst_pitch+x] = 255;
760         }
761     }
762
763     free( p_hough );
764     free( p_smooth );
765 }
766 #undef p_pre_hough
767
768
769 static int GradientCallback( vlc_object_t *p_this, char const *psz_var,
770                              vlc_value_t oldval, vlc_value_t newval,
771                              void *p_data )
772 {
773     VLC_UNUSED(oldval);
774     filter_sys_t *p_sys = (filter_sys_t *)p_data;
775
776     vlc_mutex_lock( &p_sys->lock );
777     if( !strcmp( psz_var, FILTER_PREFIX "mode" ) )
778     {
779         if( !strcmp( newval.psz_string, "gradient" ) )
780         {
781             p_sys->i_mode = GRADIENT;
782         }
783         else if( !strcmp( newval.psz_string, "edge" ) )
784         {
785             p_sys->i_mode = EDGE;
786         }
787         else if( !strcmp( newval.psz_string, "hough" ) )
788         {
789             p_sys->i_mode = HOUGH;
790         }
791         else
792         {
793             msg_Err( p_this, "no valid gradient mode provided (%s)", newval.psz_string );
794             p_sys->i_mode = GRADIENT;
795         }
796     }
797     else if( !strcmp( psz_var, FILTER_PREFIX "type" ) )
798     {
799         p_sys->i_gradient_type = newval.i_int;
800     }
801     else if( !strcmp( psz_var, FILTER_PREFIX "cartoon" ) )
802     {
803         p_sys->b_cartoon = newval.b_bool;
804     }
805     vlc_mutex_unlock( &p_sys->lock );
806
807     return VLC_SUCCESS;
808 }