]> git.sesse.net Git - vlc/blob - modules/video_filter/colorthres.c
Add colorthres support for YUV 422 packed.
[vlc] / modules / video_filter / colorthres.c
1 /*****************************************************************************
2  * colorthres.c: Threshold color based on similarity to reference color
3  *****************************************************************************
4  * Copyright (C) 2000-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sigmund Augdal <dnumgis@videolan.org>
8  *          Antoine Cellerier <dionoea at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <errno.h>
34 #include <math.h>
35
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38 #include <vlc_sout.h>
39
40 #include <vlc_filter.h>
41 #include "filter_picture.h"
42
43 /*****************************************************************************
44  * Local prototypes
45  *****************************************************************************/
46 static int  Create    ( vlc_object_t * );
47 static void Destroy   ( vlc_object_t * );
48
49 static picture_t *Filter( filter_t *, picture_t * );
50 static picture_t *FilterPacked( filter_t *, picture_t * );
51
52 /*****************************************************************************
53  * Module descriptor
54  *****************************************************************************/
55 #define COLOR_TEXT N_("Color")
56 #define COLOR_LONGTEXT N_("Colors similar to this will be kept, others will be "\
57     "grayscaled. This must be an hexadecimal (like HTML colors). The first two "\
58     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
59     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
60 #define COLOR_HELP N_("Select one color in the video")
61 static const int pi_color_values[] = {
62   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x0000FF00, 0x000000FF, 0x0000FFFF };
63
64 static const char *const ppsz_color_descriptions[] = {
65   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Lime"), N_("Blue"), N_("Aqua") };
66
67 #define CFG_PREFIX "colorthres-"
68
69 vlc_module_begin ()
70     set_description( N_("Color threshold filter") )
71     set_shortname( N_("Color threshold" ))
72     set_help(COLOR_HELP)
73     set_category( CAT_VIDEO )
74     set_subcategory( SUBCAT_VIDEO_VFILTER )
75     set_capability( "video filter2", 0 )
76     add_integer( CFG_PREFIX "color", 0x00FF0000, NULL, COLOR_TEXT,
77                  COLOR_LONGTEXT, false )
78         change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
79     add_integer( CFG_PREFIX "saturationthres", 20, NULL,
80                  N_("Saturaton threshold"), "", false )
81     add_integer( CFG_PREFIX "similaritythres", 15, NULL,
82                  N_("Similarity threshold"), "", false )
83     set_callbacks( Create, Destroy )
84 vlc_module_end ()
85
86 static const char *const ppsz_filter_options[] = {
87     "color", "saturationthes", "similaritythres", NULL
88 };
89
90 /*****************************************************************************
91  * callback prototypes
92  *****************************************************************************/
93 static int FilterCallback( vlc_object_t *, char const *,
94                            vlc_value_t, vlc_value_t, void * );
95
96
97 /*****************************************************************************
98  * filter_sys_t: adjust filter method descriptor
99  *****************************************************************************/
100 struct filter_sys_t
101 {
102     int i_simthres;
103     int i_satthres;
104     int i_color;
105     vlc_mutex_t lock;
106 };
107
108 /*****************************************************************************
109  * Create: allocates adjust video thread output method
110  *****************************************************************************
111  * This function allocates and initializes a adjust vout method.
112  *****************************************************************************/
113 static int Create( vlc_object_t *p_this )
114 {
115     filter_t *p_filter = (filter_t *)p_this;
116     filter_sys_t *p_sys;
117
118     switch( p_filter->fmt_in.video.i_chroma )
119     {
120         CASE_PLANAR_YUV
121             p_filter->pf_video_filter = Filter;
122             break;
123
124         CASE_PACKED_YUV_422
125             p_filter->pf_video_filter = FilterPacked;
126             break;
127
128         default:
129             msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
130                      (char*)&(p_filter->fmt_in.video.i_chroma) );
131             return VLC_EGENERIC;
132     }
133
134     if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
135     {
136         msg_Err( p_filter, "Input and output chromas don't match" );
137         return VLC_EGENERIC;
138     }
139
140     /* Allocate structure */
141     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
142     if( p_filter->p_sys == NULL )
143         return VLC_ENOMEM;
144
145     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
146                        p_filter->p_cfg );
147     p_sys->i_color = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "color" );
148     p_sys->i_simthres = var_CreateGetIntegerCommand( p_filter,
149                                                      CFG_PREFIX "similaritythres" );
150     p_sys->i_satthres = var_CreateGetIntegerCommand( p_filter,
151                                                      CFG_PREFIX "saturationthres" );
152
153     vlc_mutex_init( &p_sys->lock );
154
155     var_AddCallback( p_filter, CFG_PREFIX "color", FilterCallback, NULL );
156     var_AddCallback( p_filter, CFG_PREFIX "similaritythres", FilterCallback, NULL );
157     var_AddCallback( p_filter, CFG_PREFIX "saturationthres", FilterCallback, NULL );
158
159     return VLC_SUCCESS;
160 }
161
162 /*****************************************************************************
163  * Destroy: destroy adjust video thread output method
164  *****************************************************************************
165  * Terminate an output method created by adjustCreateOutputMethod
166  *****************************************************************************/
167 static void Destroy( vlc_object_t *p_this )
168 {
169     filter_t *p_filter = (filter_t *)p_this;
170
171     var_DelCallback( p_filter, CFG_PREFIX "color", FilterCallback, NULL );
172     var_DelCallback( p_filter, CFG_PREFIX "similaritythres", FilterCallback, NULL );
173     var_DelCallback( p_filter, CFG_PREFIX "saturationthres", FilterCallback, NULL );
174
175     vlc_mutex_destroy( &p_filter->p_sys->lock );
176     free( p_filter->p_sys );
177 }
178
179 /*****************************************************************************
180  * Render: displays previously rendered output
181  *****************************************************************************
182  * This function send the currently rendered image to adjust modified image,
183  * waits until it is displayed and switch the two rendering buffers, preparing
184  * next frame.
185  *****************************************************************************/
186 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
187 {
188     picture_t *p_outpic;
189     filter_sys_t *p_sys = p_filter->p_sys;
190     uint8_t *p_in_y, *p_in_u, *p_in_v, *p_in_end_u;
191     uint8_t *p_out_y, *p_out_u, *p_out_v;
192
193     vlc_mutex_lock( &p_sys->lock );
194     int i_simthres = p_sys->i_simthres;
195     int i_satthres = p_sys->i_satthres;
196     int i_color = p_sys->i_color;
197     vlc_mutex_unlock( &p_sys->lock );
198
199     if( !p_pic ) return NULL;
200
201     p_outpic = filter_NewPicture( p_filter );
202     if( !p_outpic )
203     {
204         picture_Release( p_pic );
205         return NULL;
206     }
207
208     p_in_u = p_pic->p[U_PLANE].p_pixels;
209     p_in_v = p_pic->p[V_PLANE].p_pixels;
210     p_in_y = p_pic->p[Y_PLANE].p_pixels;
211     p_in_end_u = p_in_u + p_pic->p[U_PLANE].i_visible_lines
212                         * p_pic->p[U_PLANE].i_pitch - 8;
213
214     p_out_y = p_outpic->p[Y_PLANE].p_pixels;
215     p_out_u = p_outpic->p[U_PLANE].p_pixels;
216     p_out_v = p_outpic->p[V_PLANE].p_pixels;
217
218     /* Create grayscale version of input */
219     vlc_memcpy( p_out_y, p_in_y, p_pic->p[Y_PLANE].i_visible_lines
220                * p_pic->p[Y_PLANE].i_pitch - 8 );
221     vlc_memset( p_out_u, 0x80, p_pic->p[U_PLANE].i_visible_lines
222                * p_pic->p[U_PLANE].i_pitch - 8 );
223     vlc_memset( p_out_v, 0x80, p_pic->p[U_PLANE].i_visible_lines
224                * p_pic->p[U_PLANE].i_pitch - 8 );
225
226     /*
227      * Do the U and V planes
228      */
229     int i_red = ( i_color & 0xFF0000 ) >> 16;
230     int i_green = ( i_color & 0xFF00 ) >> 8;
231     int i_blue = i_color & 0xFF;
232     int i_u = (int8_t)(( -38 * i_red - 74 * i_green +
233                      112 * i_blue + 128) >> 8) + 128;
234     int i_v = (int8_t)(( 112 * i_red  -  94 * i_green -
235                       18 * i_blue + 128) >> 8) + 128;
236     int refu = i_u - 0x80;         /*bright red*/
237     int refv = i_v - 0x80;
238     int reflength = sqrt(refu*refu+refv*refv);
239
240     while( p_in_u < p_in_end_u ) {
241         /* Length of color vector */
242         int inu = (*p_in_u) - 0x80;
243         int inv = (*p_in_v) - 0x80;
244         int length = sqrt(inu*inu+inv*inv);
245
246         int diffu = refu * length - inu *reflength;
247         int diffv = refv * length - inv *reflength;
248         long long int difflen2=diffu*diffu;
249         difflen2 +=diffv*diffv;
250         long long int thres = length*reflength;
251         thres *= thres;
252         if( length > i_satthres && (difflen2*i_simthres< thres ) ) {
253             *p_out_u = *p_in_u;
254             *p_out_v = *p_in_v;
255 //        fprintf(stderr,"keeping color %d %d\n", length, difflen2);
256         }
257         p_in_u++;
258         p_in_v++;
259         p_out_u++;
260         p_out_v++;
261     }
262
263     return CopyInfoAndRelease( p_outpic, p_pic );
264 }
265
266 static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
267 {
268     picture_t *p_outpic;
269     filter_sys_t *p_sys = p_filter->p_sys;
270     uint8_t *p_in_y, *p_in_u, *p_in_v, *p_in_end_u;
271     uint8_t *p_out_y, *p_out_u, *p_out_v;
272
273     vlc_mutex_lock( &p_sys->lock );
274     int i_simthres = p_sys->i_simthres;
275     int i_satthres = p_sys->i_satthres;
276     int i_color = p_sys->i_color;
277     vlc_mutex_unlock( &p_sys->lock );
278
279     if( !p_pic ) return NULL;
280
281     p_outpic = filter_NewPicture( p_filter );
282     if( !p_outpic )
283     {
284         picture_Release( p_pic );
285         return NULL;
286     }
287
288     int i_y_offset, i_u_offset, i_v_offset;
289     GetPackedYuvOffsets( p_filter->fmt_in.video.i_chroma,
290                          &i_y_offset, &i_u_offset, &i_v_offset );
291     p_in_y = p_pic->p->p_pixels+i_y_offset;
292     p_in_u = p_pic->p->p_pixels+i_u_offset;
293     p_in_v = p_pic->p->p_pixels+i_v_offset;
294     p_in_end_u = p_in_u + p_pic->p->i_visible_lines
295                         * p_pic->p->i_pitch - 8;
296
297     p_out_y = p_outpic->p->p_pixels+i_y_offset;
298     p_out_u = p_outpic->p->p_pixels+i_u_offset;
299     p_out_v = p_outpic->p->p_pixels+i_v_offset;
300
301     /* Create grayscale version of input */
302     vlc_memcpy( p_outpic->p->p_pixels, p_pic->p->p_pixels,
303                 p_pic->p->i_visible_lines * p_pic->p->i_pitch - 8 );
304
305     /*
306      * Do the U and V planes
307      */
308     int i_red = ( i_color & 0xFF0000 ) >> 16;
309     int i_green = ( i_color & 0xFF00 ) >> 8;
310     int i_blue = i_color & 0xFF;
311     int i_u = (int8_t)(( -38 * i_red - 74 * i_green +
312                      112 * i_blue + 128) >> 8) + 128;
313     int i_v = (int8_t)(( 112 * i_red  -  94 * i_green -
314                       18 * i_blue + 128) >> 8) + 128;
315     int refu = i_u - 0x80;         /*bright red*/
316     int refv = i_v - 0x80;
317     int reflength = sqrt(refu*refu+refv*refv);
318
319     while( p_in_u < p_in_end_u ) {
320         /* Length of color vector */
321         int inu = (*p_in_u) - 0x80;
322         int inv = (*p_in_v) - 0x80;
323         int length = sqrt(inu*inu+inv*inv);
324
325         int diffu = refu * length - inu *reflength;
326         int diffv = refv * length - inv *reflength;
327         long long int difflen2=diffu*diffu;
328         difflen2 +=diffv*diffv;
329         long long int thres = length*reflength;
330         thres *= thres;
331         if( length > i_satthres && (difflen2*i_simthres< thres ) ) {
332             *p_out_u = *p_in_u;
333             *p_out_v = *p_in_v;
334 //        fprintf(stderr,"keeping color %d %d\n", length, difflen2);
335         }
336         else
337         {
338             *p_out_u = 0x80;
339             *p_out_v = 0x80;
340         }
341         p_in_u+=4;
342         p_in_v+=4;
343         p_out_u+=4;
344         p_out_v+=4;
345     }
346
347     return CopyInfoAndRelease( p_outpic, p_pic );
348 }
349
350 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
351                             vlc_value_t oldval, vlc_value_t newval, void *p_data )
352 {
353     (void)oldval;    (void)p_data;
354     filter_t *p_filter = (filter_t*)p_this;
355     filter_sys_t *p_sys = p_filter->p_sys;
356
357     if( !strcmp( psz_var, CFG_PREFIX "color" ) )
358     {
359         vlc_mutex_lock( &p_sys->lock );
360         p_sys->i_color = newval.i_int;
361         vlc_mutex_unlock( &p_sys->lock );
362     }
363     else if( !strcmp( psz_var, CFG_PREFIX "similaritythres" ) )
364     {
365         vlc_mutex_lock( &p_sys->lock );
366         p_sys->i_simthres = newval.i_int;
367         vlc_mutex_unlock( &p_sys->lock );
368     }
369     else /* CFG_PREFIX "saturationthres" */
370     {
371         vlc_mutex_lock( &p_sys->lock );
372         p_sys->i_satthres = newval.i_int;
373         vlc_mutex_unlock( &p_sys->lock );
374     }
375
376     return VLC_SUCCESS;
377 }