]> git.sesse.net Git - vlc/blob - modules/video_filter/gradient.c
Use var_InheritString for --decklink-video-connection.
[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         plane_CopyPixels( &p_outpic->p[U_PLANE], &p_inpic->p[U_PLANE] );
364         plane_CopyPixels( &p_outpic->p[V_PLANE], &p_inpic->p[V_PLANE] );
365     }
366     else
367     {
368         vlc_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
369             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
370         vlc_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
371             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
372     }
373
374     GaussianConvolution( p_inpic, p_smooth );
375
376     /* Sobel gradient
377
378      | -1 0 1 |     |  1  2  1 |
379      | -2 0 2 | and |  0  0  0 |
380      | -1 0 1 |     | -1 -2 -1 | */
381
382 #define FOR                                                     \
383     for( y = 1; y < i_num_lines - 1; y++ )                      \
384     {                                                           \
385         for( x = 1; x < i_src_visible - 1; x++ )                \
386         {                                                       \
387             const uint32_t a =                                  \
388             (                                                   \
389               abs(                                              \
390                  ( p_smooth[(y-1)*i_src_visible+x-1]            \
391                    - p_smooth[(y+1)*i_src_visible+x-1] )        \
392                + ( ( p_smooth[(y-1)*i_src_visible+x]            \
393                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )   \
394                + ( p_smooth[(y-1)*i_src_visible+x+1]            \
395                    - p_smooth[(y+1)*i_src_visible+x+1] )        \
396               )                                                 \
397             +                                                   \
398               abs(                                              \
399                  ( p_smooth[(y-1)*i_src_visible+x-1]            \
400                    - p_smooth[(y-1)*i_src_visible+x+1] )        \
401                + ( ( p_smooth[y*i_src_visible+x-1]              \
402                     - p_smooth[y*i_src_visible+x+1] ) <<1 )     \
403                + ( p_smooth[(y+1)*i_src_visible+x-1]            \
404                    - p_smooth[(y+1)*i_src_visible+x+1] )        \
405               )                                                 \
406             );
407     if( p_filter->p_sys->i_gradient_type )
408     {
409         if( p_filter->p_sys->b_cartoon )
410         {
411             FOR
412             if( a > 60 )
413             {
414                 p_outpix[y*i_dst_pitch+x] = 0x00;
415             }
416             else
417             {
418                 if( p_smooth[y*i_src_visible+x] > 0xa0 )
419                     p_outpix[y*i_dst_pitch+x] =
420                         0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
421                 else if( p_smooth[y*i_src_visible+x] > 0x70 )
422                     p_outpix[y*i_dst_pitch+x] =
423                         0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
424                 else if( p_smooth[y*i_src_visible+x] > 0x28 )
425                     p_outpix[y*i_dst_pitch+x] =
426                         0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
427                 else
428                     p_outpix[y*i_dst_pitch+x] =
429                         0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
430             }
431             }}
432         }
433         else
434         {
435             FOR
436             p_outpix[y*i_dst_pitch+x] = clip_uint8_vlc( a );
437             }}
438         }
439     }
440     else
441     {
442         FOR
443         if( a>>8 )
444             p_outpix[y*i_dst_pitch+x] = 0;
445         else
446             p_outpix[y*i_dst_pitch+x] = 0xff-(uint8_t)a;
447         }}
448     }
449 #undef FOR
450 }
451
452 /*****************************************************************************
453  * FilterEdge: Canny edge detection algorithm
454  *****************************************************************************
455  * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
456  * (well ... my implementation isn't really the canny algorithm ... but some
457  * ideas are the same)
458  *****************************************************************************/
459 /* angle : | */
460 #define THETA_Y 0
461 /* angle : - */
462 #define THETA_X 1
463 /* angle : / */
464 #define THETA_P 2
465 /* angle : \ */
466 #define THETA_M 3
467 static void FilterEdge( filter_t *p_filter, picture_t *p_inpic,
468                                             picture_t *p_outpic )
469 {
470     int x, y;
471
472     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
473     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
474     const int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
475     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
476
477     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
478     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
479
480     uint32_t *p_smooth;
481     uint32_t *p_grad;
482     uint8_t *p_theta;
483
484     if( !p_filter->p_sys->p_buf32 )
485         p_filter->p_sys->p_buf32 =
486         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
487     p_smooth = p_filter->p_sys->p_buf32;
488
489     if( !p_filter->p_sys->p_buf32_bis )
490         p_filter->p_sys->p_buf32_bis =
491         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
492     p_grad = p_filter->p_sys->p_buf32_bis;
493
494     if( !p_filter->p_sys->p_buf8 )
495         p_filter->p_sys->p_buf8 =
496         (uint8_t *)malloc( i_num_lines * i_src_visible * sizeof(uint8_t));
497     p_theta = p_filter->p_sys->p_buf8;
498
499     if( !p_smooth || !p_grad || !p_theta ) return;
500
501     if( p_filter->p_sys->b_cartoon )
502     {
503         plane_CopyPixels( &p_outpic->p[U_PLANE], &p_inpic->p[U_PLANE] );
504         plane_CopyPixels( &p_outpic->p[V_PLANE], &p_inpic->p[V_PLANE] );
505     }
506     else
507     {
508         vlc_memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
509               p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
510         vlc_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
511             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
512         vlc_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
513             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
514     }
515
516     GaussianConvolution( p_inpic, p_smooth );
517
518     /* Sobel gradient
519
520      | -1 0 1 |     |  1  2  1 |
521      | -2 0 2 | and |  0  0  0 |
522      | -1 0 1 |     | -1 -2 -1 | */
523
524     for( y = 1; y < i_num_lines - 1; y++ )
525     {
526         for( x = 1; x < i_src_visible - 1; x++ )
527         {
528
529             const int gradx =
530                  ( p_smooth[(y-1)*i_src_visible+x-1]
531                    - p_smooth[(y+1)*i_src_visible+x-1] )
532                + ( ( p_smooth[(y-1)*i_src_visible+x]
533                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
534                + ( p_smooth[(y-1)*i_src_visible+x+1]
535                    - p_smooth[(y+1)*i_src_visible+x+1] );
536             const int grady =
537                  ( p_smooth[(y-1)*i_src_visible+x-1]
538                    - p_smooth[(y-1)*i_src_visible+x+1] )
539                + ( ( p_smooth[y*i_src_visible+x-1]
540                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
541                + ( p_smooth[(y+1)*i_src_visible+x-1]
542                    - p_smooth[(y+1)*i_src_visible+x+1] );
543
544             p_grad[y*i_src_visible+x] = (uint32_t)(abs( gradx ) + abs( grady ));
545
546             /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
547              * tan( 26,565051177 ) = 0.5
548              * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
549              * tan( 63,434948823 ) 2 */
550             if( (grady<<1) > gradx )
551                 p_theta[y*i_src_visible+x] = THETA_P;
552             else if( (grady<<1) < -gradx )
553                 p_theta[y*i_src_visible+x] = THETA_M;
554             else if( !gradx || abs(grady) > abs(gradx)<<1 )
555                 p_theta[y*i_src_visible+x] = THETA_Y;
556             else
557                 p_theta[y*i_src_visible+x] = THETA_X;
558         }
559     }
560
561     /* edge computing */
562     for( y = 1; y < i_num_lines - 1; y++ )
563     {
564         for( x = 1; x < i_src_visible - 1; x++ )
565         {
566             if( p_grad[y*i_src_visible+x] > 40 )
567             {
568                 switch( p_theta[y*i_src_visible+x] )
569                 {
570                     case THETA_Y:
571                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x]
572                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x] )
573                         {
574                             p_outpix[y*i_dst_pitch+x] = 0;
575                             break;
576                         } else goto colorize;
577                     case THETA_P:
578                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x-1]
579                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x+1] )
580                         {
581                             p_outpix[y*i_dst_pitch+x] = 0;
582                             break;
583                         } else goto colorize;
584                     case THETA_M:
585                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x+1]
586                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x-1] )
587                         {
588                             p_outpix[y*i_dst_pitch+x] = 0;
589                             break;
590                         } else goto colorize;
591                     case THETA_X:
592                         if(    p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x-1]
593                             && p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x+1] )
594                         {
595                             p_outpix[y*i_dst_pitch+x] = 0;
596                             break;
597                         } else goto colorize;
598                 }
599             }
600             else
601             {
602                 colorize:
603                 if( p_filter->p_sys->b_cartoon )
604                 {
605                     if( p_smooth[y*i_src_visible+x] > 0xa0 )
606                         p_outpix[y*i_dst_pitch+x] = (uint8_t)
607                             0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
608                     else if( p_smooth[y*i_src_visible+x] > 0x70 )
609                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
610                             0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
611                     else if( p_smooth[y*i_src_visible+x] > 0x28 )
612                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
613                             0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
614                     else
615                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
616                             0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
617                 }
618             }
619         }
620     }
621 }
622
623 /*****************************************************************************
624  * FilterHough
625  *****************************************************************************/
626 #define p_pre_hough p_filter->p_sys->p_pre_hough
627 static void FilterHough( filter_t *p_filter, picture_t *p_inpic,
628                                              picture_t *p_outpic )
629 {
630     int x, y, i;
631     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
632     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
633     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
634
635     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
636
637     int i_diag = sqrt( i_num_lines * i_num_lines +
638                         i_src_visible * i_src_visible);
639     int i_max, i_phi_max, i_rho, i_rho_max;
640     int i_nb_steps = 90;
641     double d_step = M_PI / i_nb_steps;
642     double d_sin;
643     double d_cos;
644     uint32_t *p_smooth;
645
646     int *p_hough = malloc( i_diag * i_nb_steps * sizeof(int) );
647     if( ! p_hough ) return;
648
649     p_smooth = (uint32_t *)malloc( i_num_lines*i_src_visible*sizeof(uint32_t));
650     if( !p_smooth )
651     {
652         free( p_hough );
653         return;
654     }
655
656     if( ! p_pre_hough )
657     {
658         msg_Dbg(p_filter, "Starting precalculation");
659         p_pre_hough = malloc( i_num_lines*i_src_visible*i_nb_steps*sizeof(int));
660         if( ! p_pre_hough )
661         {
662             free( p_smooth );
663             free( p_hough );
664             return;
665         }
666         for( i = 0 ; i < i_nb_steps ; i++)
667         {
668             d_sin = sin(d_step * i);
669             d_cos = cos(d_step * i);
670             for( y = 0 ; y < i_num_lines ; y++ )
671                 for( x = 0 ; x < i_src_visible ; x++ )
672                 {
673                     p_pre_hough[(i*i_num_lines+y)*i_src_visible + x] =
674                         ceil(x*d_sin + y*d_cos);
675                 }
676         }
677         msg_Dbg(p_filter, "Precalculation done");
678     }
679
680     vlc_memset( p_hough, 0, i_diag * i_nb_steps * sizeof(int) );
681
682     plane_CopyPixels( &p_outpic->p[Y_PLANE], &p_inpic->p[Y_PLANE] );
683     plane_CopyPixels( &p_outpic->p[U_PLANE], &p_inpic->p[U_PLANE] );
684     plane_CopyPixels( &p_outpic->p[V_PLANE], &p_inpic->p[V_PLANE] );
685
686     GaussianConvolution( p_inpic, p_smooth );
687
688     /* Sobel gradient
689
690      | -1 0 1 |     |  1  2  1 |
691      | -2 0 2 | and |  0  0  0 |
692      | -1 0 1 |     | -1 -2 -1 | */
693
694     i_max = 0;
695     i_rho_max = 0;
696     i_phi_max = 0;
697     for( y = 4; y < i_num_lines - 4; y++ )
698     {
699         for( x = 4; x < i_src_visible - 4; x++ )
700         {
701             uint32_t a =
702             (
703               abs(
704                 ( ( p_smooth[(y-1)*i_src_visible+x]
705                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
706                + ( p_smooth[(y-1)*i_src_visible+x-1]
707                    - p_smooth[(y+1)*i_src_visible+x-1] )
708                + ( p_smooth[(y-1)*i_src_visible+x+1]
709                    - p_smooth[(y+1)*i_src_visible+x+1] )
710               )
711             +
712               abs(
713                 ( ( p_smooth[y*i_src_visible+x-1]
714                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
715                + ( p_smooth[(y-1)*i_src_visible+x-1]
716                    - p_smooth[(y-1)*i_src_visible+x+1] )
717                + ( p_smooth[(y+1)*i_src_visible+x-1]
718                    - p_smooth[(y+1)*i_src_visible+x+1] )
719               )
720             );
721             if( a>>8 )
722             {
723                 for( i = 0 ; i < i_nb_steps ; i ++ )
724                 {
725                     i_rho = p_pre_hough[(i*i_num_lines+y)*i_src_visible + x];
726                     if( p_hough[i_rho + i_diag/2 + i * i_diag]++ > i_max )
727                     {
728                         i_max = p_hough[i_rho + i_diag/2 + i * i_diag];
729                         i_rho_max = i_rho;
730                         i_phi_max = i;
731                     }
732                 }
733             }
734         }
735     }
736
737     d_sin = sin(i_phi_max*d_step);
738     d_cos = cos(i_phi_max*d_step);
739     if( d_cos != 0 )
740     {
741         for( x = 0 ; x < i_src_visible ; x++ )
742         {
743             y = (i_rho_max - x * d_sin) / d_cos;
744             if( y >= 0 && y < i_num_lines )
745                 p_outpix[y*i_dst_pitch+x] = 255;
746         }
747     }
748
749     free( p_hough );
750     free( p_smooth );
751 }
752 #undef p_pre_hough
753
754
755 static int GradientCallback( vlc_object_t *p_this, char const *psz_var,
756                              vlc_value_t oldval, vlc_value_t newval,
757                              void *p_data )
758 {
759     VLC_UNUSED(oldval);
760     filter_sys_t *p_sys = (filter_sys_t *)p_data;
761
762     vlc_mutex_lock( &p_sys->lock );
763     if( !strcmp( psz_var, FILTER_PREFIX "mode" ) )
764     {
765         if( !strcmp( newval.psz_string, "gradient" ) )
766         {
767             p_sys->i_mode = GRADIENT;
768         }
769         else if( !strcmp( newval.psz_string, "edge" ) )
770         {
771             p_sys->i_mode = EDGE;
772         }
773         else if( !strcmp( newval.psz_string, "hough" ) )
774         {
775             p_sys->i_mode = HOUGH;
776         }
777         else
778         {
779             msg_Err( p_this, "no valid gradient mode provided (%s)", newval.psz_string );
780             p_sys->i_mode = GRADIENT;
781         }
782     }
783     else if( !strcmp( psz_var, FILTER_PREFIX "type" ) )
784     {
785         p_sys->i_gradient_type = newval.i_int;
786     }
787     else if( !strcmp( psz_var, FILTER_PREFIX "cartoon" ) )
788     {
789         p_sys->b_cartoon = newval.b_bool;
790     }
791     vlc_mutex_unlock( &p_sys->lock );
792
793     return VLC_SUCCESS;
794 }