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