]> git.sesse.net Git - vlc/blob - modules/video_filter/gradient.c
3d70a4ac273dc84269f5d71f883501e153ec7266
[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/vlc.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     p_outpic->date = p_pic->date;
267     p_outpic->b_force = p_pic->b_force;
268     p_outpic->i_nb_fields = p_pic->i_nb_fields;
269     p_outpic->b_progressive = p_pic->b_progressive;
270     p_outpic->b_top_field_first = p_pic->b_top_field_first;
271
272     if( p_pic->pf_release )
273         p_pic->pf_release( p_pic );
274
275     return p_outpic;
276 }
277
278 /*****************************************************************************
279  * Gaussian Convolution
280  *****************************************************************************
281  *    Gaussian convolution ( sigma == 1.4 )
282  *
283  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
284  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
285  *    |  5 12 15 12  5  | ~ |  4 12 16 12  4 |
286  *    |  4  9 12  9  4  |   |  4  8 12  8  4 |
287  *    |  2  4  5  4  2  |   |  2  4  4  4  2 |
288  *****************************************************************************/
289 static void GaussianConvolution( picture_t *p_inpic, uint32_t *p_smooth )
290 {
291     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
292     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
293     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
294     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
295
296     int x,y;
297     for( y = 2; y < i_num_lines - 2; y++ )
298     {
299         for( x = 2; x < i_src_visible - 2; x++ )
300         {
301             p_smooth[y*i_src_visible+x] = (uint32_t)(
302               /* 2 rows up */
303                 ( p_inpix[(y-2)*i_src_pitch+x-2] )
304               + ((p_inpix[(y-2)*i_src_pitch+x-1]
305               +   p_inpix[(y-2)*i_src_pitch+x]
306               +   p_inpix[(y-2)*i_src_pitch+x+1])<<1 )
307               + ( p_inpix[(y-2)*i_src_pitch+x+2] )
308               /* 1 row up */
309               + ((p_inpix[(y-1)*i_src_pitch+x-2]
310               + ( p_inpix[(y-1)*i_src_pitch+x-1]<<1 )
311               + ( p_inpix[(y-1)*i_src_pitch+x]*3 )
312               + ( p_inpix[(y-1)*i_src_pitch+x+1]<<1 )
313               +   p_inpix[(y-1)*i_src_pitch+x+2]
314               /* */
315               +   p_inpix[y*i_src_pitch+x-2]
316               + ( p_inpix[y*i_src_pitch+x-1]*3 )
317               + ( p_inpix[y*i_src_pitch+x]<<2 )
318               + ( p_inpix[y*i_src_pitch+x+1]*3 )
319               +   p_inpix[y*i_src_pitch+x+2]
320               /* 1 row down */
321               +   p_inpix[(y+1)*i_src_pitch+x-2]
322               + ( p_inpix[(y+1)*i_src_pitch+x-1]<<1 )
323               + ( p_inpix[(y+1)*i_src_pitch+x]*3 )
324               + ( p_inpix[(y+1)*i_src_pitch+x+1]<<1 )
325               +   p_inpix[(y+1)*i_src_pitch+x+2] )<<1 )
326               /* 2 rows down */
327               + ( p_inpix[(y+2)*i_src_pitch+x-2] )
328               + ((p_inpix[(y+2)*i_src_pitch+x-1]
329               +   p_inpix[(y+2)*i_src_pitch+x]
330               +   p_inpix[(y+2)*i_src_pitch+x+1])<<1 )
331               + ( p_inpix[(y+2)*i_src_pitch+x+2] )
332               ) >> 6 /* 115 */;
333         }
334     }
335 }
336
337 /*****************************************************************************
338  * FilterGradient: Sobel
339  *****************************************************************************/
340 static void FilterGradient( filter_t *p_filter, picture_t *p_inpic,
341                                                 picture_t *p_outpic )
342 {
343     int x, y;
344     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
345     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
346     const int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
347     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
348
349     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
350     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
351
352     uint32_t *p_smooth;
353     if( !p_filter->p_sys->p_buf32 )
354         p_filter->p_sys->p_buf32 =
355         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
356     p_smooth = p_filter->p_sys->p_buf32;
357
358     if( !p_smooth ) return;
359
360     if( p_filter->p_sys->b_cartoon )
361     {
362         vlc_memcpy( p_outpic->p[U_PLANE].p_pixels,
363             p_inpic->p[U_PLANE].p_pixels,
364             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
365         vlc_memcpy( p_outpic->p[V_PLANE].p_pixels,
366             p_inpic->p[V_PLANE].p_pixels,
367             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
368     }
369     else
370     {
371         vlc_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
372             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
373         vlc_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
374             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
375     }
376
377     GaussianConvolution( p_inpic, p_smooth );
378
379     /* Sobel gradient
380
381      | -1 0 1 |     |  1  2  1 |
382      | -2 0 2 | and |  0  0  0 |
383      | -1 0 1 |     | -1 -2 -1 | */
384
385 #define FOR                                                     \
386     for( y = 1; y < i_num_lines - 1; y++ )                      \
387     {                                                           \
388         for( x = 1; x < i_src_visible - 1; x++ )                \
389         {                                                       \
390             const uint32_t a =                                  \
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-1)*i_src_visible+x]            \
396                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )   \
397                + ( p_smooth[(y-1)*i_src_visible+x+1]            \
398                    - p_smooth[(y+1)*i_src_visible+x+1] )        \
399               )                                                 \
400             +                                                   \
401               abs(                                              \
402                  ( p_smooth[(y-1)*i_src_visible+x-1]            \
403                    - p_smooth[(y-1)*i_src_visible+x+1] )        \
404                + ( ( p_smooth[y*i_src_visible+x-1]              \
405                     - p_smooth[y*i_src_visible+x+1] ) <<1 )     \
406                + ( p_smooth[(y+1)*i_src_visible+x-1]            \
407                    - p_smooth[(y+1)*i_src_visible+x+1] )        \
408               )                                                 \
409             );
410     if( p_filter->p_sys->i_gradient_type )
411     {
412         if( p_filter->p_sys->b_cartoon )
413         {
414             FOR
415             if( a > 60 )
416             {
417                 p_outpix[y*i_dst_pitch+x] = 0x00;
418             }
419             else
420             {
421                 if( p_smooth[y*i_src_visible+x] > 0xa0 )
422                     p_outpix[y*i_dst_pitch+x] =
423                         0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
424                 else if( p_smooth[y*i_src_visible+x] > 0x70 )
425                     p_outpix[y*i_dst_pitch+x] =
426                         0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
427                 else if( p_smooth[y*i_src_visible+x] > 0x28 )
428                     p_outpix[y*i_dst_pitch+x] =
429                         0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
430                 else
431                     p_outpix[y*i_dst_pitch+x] =
432                         0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
433             }
434             }}
435         }
436         else
437         {
438             FOR
439             p_outpix[y*i_dst_pitch+x] = clip_uint8_vlc( a );
440             }}
441         }
442     }
443     else
444     {
445         FOR
446         if( a>>8 )
447             p_outpix[y*i_dst_pitch+x] = 0;
448         else
449             p_outpix[y*i_dst_pitch+x] = 0xff-(uint8_t)a;
450         }}
451     }
452 #undef FOR
453 }
454
455 /*****************************************************************************
456  * FilterEdge: Canny edge detection algorithm
457  *****************************************************************************
458  * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
459  * (well ... my implementation isn't really the canny algorithm ... but some
460  * ideas are the same)
461  *****************************************************************************/
462 /* angle : | */
463 #define THETA_Y 0
464 /* angle : - */
465 #define THETA_X 1
466 /* angle : / */
467 #define THETA_P 2
468 /* angle : \ */
469 #define THETA_M 3
470 static void FilterEdge( filter_t *p_filter, picture_t *p_inpic,
471                                             picture_t *p_outpic )
472 {
473     int x, y;
474
475     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
476     const int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
477     const int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
478     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
479
480     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
481     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
482
483     uint32_t *p_smooth;
484     uint32_t *p_grad;
485     uint8_t *p_theta;
486
487     if( !p_filter->p_sys->p_buf32 )
488         p_filter->p_sys->p_buf32 =
489         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
490     p_smooth = p_filter->p_sys->p_buf32;
491
492     if( !p_filter->p_sys->p_buf32_bis )
493         p_filter->p_sys->p_buf32_bis =
494         (uint32_t *)malloc( i_num_lines * i_src_visible * sizeof(uint32_t));
495     p_grad = p_filter->p_sys->p_buf32_bis;
496
497     if( !p_filter->p_sys->p_buf8 )
498         p_filter->p_sys->p_buf8 =
499         (uint8_t *)malloc( i_num_lines * i_src_visible * sizeof(uint8_t));
500     p_theta = p_filter->p_sys->p_buf8;
501
502     if( !p_smooth || !p_grad || !p_theta ) return;
503
504     if( p_filter->p_sys->b_cartoon )
505     {
506         vlc_memcpy( p_outpic->p[U_PLANE].p_pixels,
507             p_inpic->p[U_PLANE].p_pixels,
508             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
509         vlc_memcpy( p_outpic->p[V_PLANE].p_pixels,
510             p_inpic->p[V_PLANE].p_pixels,
511             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
512     }
513     else
514     {
515         vlc_memset( p_outpic->p[Y_PLANE].p_pixels, 0xff,
516               p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
517         vlc_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
518             p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
519         vlc_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
520             p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
521     }
522
523     GaussianConvolution( p_inpic, p_smooth );
524
525     /* Sobel gradient
526
527      | -1 0 1 |     |  1  2  1 |
528      | -2 0 2 | and |  0  0  0 |
529      | -1 0 1 |     | -1 -2 -1 | */
530
531     for( y = 1; y < i_num_lines - 1; y++ )
532     {
533         for( x = 1; x < i_src_visible - 1; x++ )
534         {
535
536             const int gradx =
537                  ( p_smooth[(y-1)*i_src_visible+x-1]
538                    - p_smooth[(y+1)*i_src_visible+x-1] )
539                + ( ( p_smooth[(y-1)*i_src_visible+x]
540                     - p_smooth[(y+1)*i_src_visible+x] ) <<1 )
541                + ( p_smooth[(y-1)*i_src_visible+x+1]
542                    - p_smooth[(y+1)*i_src_visible+x+1] );
543             const int grady =
544                  ( p_smooth[(y-1)*i_src_visible+x-1]
545                    - p_smooth[(y-1)*i_src_visible+x+1] )
546                + ( ( p_smooth[y*i_src_visible+x-1]
547                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
548                + ( p_smooth[(y+1)*i_src_visible+x-1]
549                    - p_smooth[(y+1)*i_src_visible+x+1] );
550
551             p_grad[y*i_src_visible+x] = (uint32_t)(abs( gradx ) + abs( grady ));
552
553             /* tan( 22.5 ) = 0,414213562 .. * 128 = 53
554              * tan( 26,565051177 ) = 0.5
555              * tan( 45 + 22.5 ) = 2,414213562 .. * 128 = 309
556              * tan( 63,434948823 ) 2 */
557             if( (grady<<1) > gradx )
558                 p_theta[y*i_src_visible+x] = THETA_P;
559             else if( (grady<<1) < -gradx )
560                 p_theta[y*i_src_visible+x] = THETA_M;
561             else if( !gradx || abs(grady) > abs(gradx)<<1 )
562                 p_theta[y*i_src_visible+x] = THETA_Y;
563             else
564                 p_theta[y*i_src_visible+x] = THETA_X;
565         }
566     }
567
568     /* edge computing */
569     for( y = 1; y < i_num_lines - 1; y++ )
570     {
571         for( x = 1; x < i_src_visible - 1; x++ )
572         {
573             if( p_grad[y*i_src_visible+x] > 40 )
574             {
575                 switch( p_theta[y*i_src_visible+x] )
576                 {
577                     case THETA_Y:
578                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x]
579                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x] )
580                         {
581                             p_outpix[y*i_dst_pitch+x] = 0;
582                             break;
583                         } else goto colorize;
584                     case THETA_P:
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_M:
592                         if(    p_grad[y*i_src_visible+x] > p_grad[(y-1)*i_src_visible+x+1]
593                             && p_grad[y*i_src_visible+x] > p_grad[(y+1)*i_src_visible+x-1] )
594                         {
595                             p_outpix[y*i_dst_pitch+x] = 0;
596                             break;
597                         } else goto colorize;
598                     case THETA_X:
599                         if(    p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x-1]
600                             && p_grad[y*i_src_visible+x] > p_grad[y*i_src_visible+x+1] )
601                         {
602                             p_outpix[y*i_dst_pitch+x] = 0;
603                             break;
604                         } else goto colorize;
605                 }
606             }
607             else
608             {
609                 colorize:
610                 if( p_filter->p_sys->b_cartoon )
611                 {
612                     if( p_smooth[y*i_src_visible+x] > 0xa0 )
613                         p_outpix[y*i_dst_pitch+x] = (uint8_t)
614                             0xff - ((0xff - p_inpix[y*i_src_pitch+x] )>>2);
615                     else if( p_smooth[y*i_src_visible+x] > 0x70 )
616                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
617                             0xa0 - ((0xa0 - p_inpix[y*i_src_pitch+x] )>>2);
618                     else if( p_smooth[y*i_src_visible+x] > 0x28 )
619                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
620                             0x70 - ((0x70 - p_inpix[y*i_src_pitch+x] )>>2);
621                     else
622                         p_outpix[y*i_dst_pitch+x] =(uint8_t)
623                             0x28 - ((0x28 - p_inpix[y*i_src_pitch+x] )>>2);
624                 }
625             }
626         }
627     }
628 }
629
630 /*****************************************************************************
631  * FilterHough
632  *****************************************************************************/
633 #define p_pre_hough p_filter->p_sys->p_pre_hough
634 static void FilterHough( filter_t *p_filter, picture_t *p_inpic,
635                                              picture_t *p_outpic )
636 {
637     int x, y, i;
638     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
639     int i_dst_pitch = p_outpic->p[Y_PLANE].i_pitch;
640     int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
641
642     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
643
644     int i_diag = sqrt( i_num_lines * i_num_lines +
645                         i_src_visible * i_src_visible);
646     int i_max, i_phi_max, i_rho, i_rho_max;
647     int i_nb_steps = 90;
648     double d_step = M_PI / i_nb_steps;
649     double d_sin;
650     double d_cos;
651     uint32_t *p_smooth;
652     int *p_hough = malloc( i_diag * i_nb_steps * sizeof(int) );
653     if( ! p_hough ) return;
654     p_smooth = (uint32_t *)malloc( i_num_lines*i_src_visible*sizeof(uint32_t));
655     if( !p_smooth ) return;
656
657     if( ! p_pre_hough )
658     {
659         msg_Dbg(p_filter, "Starting precalculation");
660         p_pre_hough = malloc( i_num_lines*i_src_visible*i_nb_steps*sizeof(int));
661         if( ! p_pre_hough ) return;
662         for( i = 0 ; i < i_nb_steps ; i++)
663         {
664             d_sin = sin(d_step * i);
665             d_cos = cos(d_step * i);
666             for( y = 0 ; y < i_num_lines ; y++ )
667                 for( x = 0 ; x < i_src_visible ; x++ )
668                 {
669                     p_pre_hough[(i*i_num_lines+y)*i_src_visible + x] =
670                         ceil(x*d_sin + y*d_cos);
671                 }
672         }
673         msg_Dbg(p_filter, "Precalculation done");
674     }
675
676     vlc_memset( p_hough, 0, i_diag * i_nb_steps * sizeof(int) );
677
678     vlc_memcpy(
679         p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
680         p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
681     vlc_memcpy(
682         p_outpic->p[U_PLANE].p_pixels, p_inpic->p[U_PLANE].p_pixels,
683         p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
684     vlc_memcpy(
685         p_outpic->p[V_PLANE].p_pixels, p_inpic->p[V_PLANE].p_pixels,
686         p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
687
688     GaussianConvolution( p_inpic, p_smooth );
689
690     /* Sobel gradient
691
692      | -1 0 1 |     |  1  2  1 |
693      | -2 0 2 | and |  0  0  0 |
694      | -1 0 1 |     | -1 -2 -1 | */
695
696     i_max = 0;
697     i_rho_max = 0;
698     i_phi_max = 0;
699     for( y = 4; y < i_num_lines - 4; y++ )
700     {
701         for( x = 4; x < i_src_visible - 4; x++ )
702         {
703             uint32_t a =
704             (
705               abs(
706                 ( ( p_smooth[(y-1)*i_src_visible+x]
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                + ( p_smooth[(y-1)*i_src_visible+x+1]
711                    - p_smooth[(y+1)*i_src_visible+x+1] )
712               )
713             +
714               abs(
715                 ( ( p_smooth[y*i_src_visible+x-1]
716                     - p_smooth[y*i_src_visible+x+1] ) <<1 )
717                + ( p_smooth[(y-1)*i_src_visible+x-1]
718                    - p_smooth[(y-1)*i_src_visible+x+1] )
719                + ( p_smooth[(y+1)*i_src_visible+x-1]
720                    - p_smooth[(y+1)*i_src_visible+x+1] )
721               )
722             );
723             if( a>>8 )
724             {
725                 for( i = 0 ; i < i_nb_steps ; i ++ )
726                 {
727                     i_rho = p_pre_hough[(i*i_num_lines+y)*i_src_visible + x];
728                     if( p_hough[i_rho + i_diag/2 + i * i_diag]++ > i_max )
729                     {
730                         i_max = p_hough[i_rho + i_diag/2 + i * i_diag];
731                         i_rho_max = i_rho;
732                         i_phi_max = i;
733                     }
734                 }
735             }
736         }
737     }
738
739     d_sin = sin(i_phi_max*d_step);
740     d_cos = cos(i_phi_max*d_step);
741     if( d_cos != 0 )
742     {
743         for( x = 0 ; x < i_src_visible ; x++ )
744         {
745             y = (i_rho_max - x * d_sin) / d_cos;
746             if( y >= 0 && y < i_num_lines )
747                 p_outpix[y*i_dst_pitch+x] = 255;
748         }
749     }
750
751     free( p_hough );
752     free( p_smooth );
753 }
754 #undef p_pre_hough
755
756
757 static int GradientCallback( vlc_object_t *p_this, char const *psz_var,
758                              vlc_value_t oldval, vlc_value_t newval,
759                              void *p_data )
760 {
761     VLC_UNUSED(oldval);
762     filter_sys_t *p_sys = (filter_sys_t *)p_data;
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     return VLC_SUCCESS;
792 }