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