]> git.sesse.net Git - vlc/blob - modules/video_filter/colorthres.c
decoder: do not mix and match condidition variable and mutex pairings
[vlc] / modules / video_filter / colorthres.c
1 /*****************************************************************************
2  * colorthres.c: Threshold color based on similarity to reference color
3  *****************************************************************************
4  * Copyright (C) 2000-2009 VLC authors and VideoLAN
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 it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <math.h>
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_sout.h>
38 #include <vlc_atomic.h>
39 #include <vlc_filter.h>
40 #include "filter_picture.h"
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static int  Create    ( vlc_object_t * );
46 static void Destroy   ( vlc_object_t * );
47
48 static picture_t *Filter( filter_t *, picture_t * );
49 static picture_t *FilterPacked( filter_t *, picture_t * );
50
51 /*****************************************************************************
52  * Module descriptor
53  *****************************************************************************/
54 #define COLOR_TEXT N_("Color")
55 #define COLOR_LONGTEXT N_("Colors similar to this will be kept, others will be "\
56     "grayscaled. This must be an hexadecimal (like HTML colors). The first two "\
57     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
58     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
59 #define COLOR_HELP N_("Select one color in the video")
60 static const int pi_color_values[] = {
61   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x0000FF00, 0x000000FF, 0x0000FFFF };
62
63 static const char *const ppsz_color_descriptions[] = {
64   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Lime"), N_("Blue"), N_("Aqua") };
65
66 #define CFG_PREFIX "colorthres-"
67
68 vlc_module_begin ()
69     set_description( N_("Color threshold filter") )
70     set_shortname( N_("Color threshold" ))
71     set_help(COLOR_HELP)
72     set_category( CAT_VIDEO )
73     set_subcategory( SUBCAT_VIDEO_VFILTER )
74     set_capability( "video filter2", 0 )
75     add_rgb( CFG_PREFIX "color", 0x00FF0000, COLOR_TEXT,
76                  COLOR_LONGTEXT, false )
77         change_integer_list( pi_color_values, ppsz_color_descriptions )
78     add_integer( CFG_PREFIX "saturationthres", 20,
79                  N_("Saturation threshold"), "", false )
80     add_integer( CFG_PREFIX "similaritythres", 15,
81                  N_("Similarity threshold"), "", false )
82     set_callbacks( Create, Destroy )
83 vlc_module_end ()
84
85 static const char *const ppsz_filter_options[] = {
86     "color", "saturationthres", "similaritythres", NULL
87 };
88
89 /*****************************************************************************
90  * callback prototypes
91  *****************************************************************************/
92 static int FilterCallback( vlc_object_t *, char const *,
93                            vlc_value_t, vlc_value_t, void * );
94
95
96 /*****************************************************************************
97  * filter_sys_t: adjust filter method descriptor
98  *****************************************************************************/
99 struct filter_sys_t
100 {
101     atomic_int i_simthres;
102     atomic_int i_satthres;
103     atomic_int i_color;
104 };
105
106 /*****************************************************************************
107  * Create: allocates adjust video thread output method
108  *****************************************************************************
109  * This function allocates and initializes a adjust vout method.
110  *****************************************************************************/
111 static int Create( vlc_object_t *p_this )
112 {
113     filter_t *p_filter = (filter_t *)p_this;
114     filter_sys_t *p_sys;
115
116     switch( p_filter->fmt_in.video.i_chroma )
117     {
118         CASE_PLANAR_YUV
119             p_filter->pf_video_filter = Filter;
120             break;
121
122         CASE_PACKED_YUV_422
123             p_filter->pf_video_filter = FilterPacked;
124             break;
125
126         default:
127             msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
128                      (char*)&(p_filter->fmt_in.video.i_chroma) );
129             return VLC_EGENERIC;
130     }
131
132     if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
133     {
134         msg_Err( p_filter, "Input and output chromas don't match" );
135         return VLC_EGENERIC;
136     }
137
138     /* Allocate structure */
139     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
140     if( p_filter->p_sys == NULL )
141         return VLC_ENOMEM;
142
143     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
144                        p_filter->p_cfg );
145     atomic_init( &p_sys->i_color,
146                  var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "color" ) );
147     atomic_init( &p_sys->i_simthres,
148        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "similaritythres" ) );
149     atomic_init( &p_sys->i_satthres,
150        var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "saturationthres" ) );
151
152     var_AddCallback( p_filter, CFG_PREFIX "color", FilterCallback, p_sys );
153     var_AddCallback( p_filter, CFG_PREFIX "similaritythres", FilterCallback, p_sys );
154     var_AddCallback( p_filter, CFG_PREFIX "saturationthres", FilterCallback, p_sys );
155
156     return VLC_SUCCESS;
157 }
158
159 /*****************************************************************************
160  * Destroy: destroy adjust video thread output method
161  *****************************************************************************
162  * Terminate an output method created by adjustCreateOutputMethod
163  *****************************************************************************/
164 static void Destroy( vlc_object_t *p_this )
165 {
166     filter_t *p_filter = (filter_t *)p_this;
167     filter_sys_t *p_sys = p_filter->p_sys;
168
169     var_DelCallback( p_filter, CFG_PREFIX "color", FilterCallback, p_sys );
170     var_DelCallback( p_filter, CFG_PREFIX "similaritythres", FilterCallback, p_sys );
171     var_DelCallback( p_filter, CFG_PREFIX "saturationthres", FilterCallback, p_sys );
172     free( p_sys );
173 }
174
175 static void GetReference( int *refu, int *refv, int *reflength,
176                           uint32_t i_color )
177 {
178     int i_red   = ( i_color & 0xFF0000 ) >> 16;
179     int i_green = ( i_color & 0x00FF00 ) >> 8;
180     int i_blue  = ( i_color & 0x0000FF );
181     int i_u = (int8_t)(( -38 * i_red - 74 * i_green + 112 * i_blue + 128) >> 8) + 128;
182     int i_v = (int8_t)(( 112 * i_red - 94 * i_green -  18 * i_blue + 128) >> 8) + 128;
183     *refu = i_u - 0x80;
184     *refv = i_v - 0x80;
185     *reflength = sqrt(*refu * *refu + *refv * *refv);
186 }
187
188 static bool IsSimilar( int u, int v,
189                        int refu, int refv, int reflength,
190                        int i_satthres, int i_simthres )
191 {
192     int length = sqrt(u * u + v * v);
193
194     int diffu = refu * length - u * reflength;
195     int diffv = refv * length - v * reflength;
196     int64_t difflen2 = diffu * diffu + diffv * diffv;
197     int64_t thres = length * reflength;
198     thres *= thres;
199     return length > i_satthres && (difflen2 * i_simthres < thres);
200 }
201 /*****************************************************************************
202  * Render: displays previously rendered output
203  *****************************************************************************
204  * This function send the currently rendered image to adjust modified image,
205  * waits until it is displayed and switch the two rendering buffers, preparing
206  * next frame.
207  *****************************************************************************/
208 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
209 {
210     picture_t *p_outpic;
211     filter_sys_t *p_sys = p_filter->p_sys;
212     int i_simthres = atomic_load( &p_sys->i_simthres );
213     int i_satthres = atomic_load( &p_sys->i_satthres );
214     int i_color = atomic_load( &p_sys->i_color );
215
216     if( !p_pic ) return NULL;
217
218     p_outpic = filter_NewPicture( p_filter );
219     if( !p_outpic )
220     {
221         picture_Release( p_pic );
222         return NULL;
223     }
224
225     /* Copy the Y plane */
226     plane_CopyPixels( &p_outpic->p[Y_PLANE], &p_pic->p[Y_PLANE] );
227
228     /*
229      * Do the U and V planes
230      */
231     int refu, refv, reflength;
232     GetReference( &refu, &refv, &reflength, i_color );
233
234     for( int y = 0; y < p_pic->p[U_PLANE].i_visible_lines; y++ )
235     {
236         uint8_t *p_src_u = &p_pic->p[U_PLANE].p_pixels[y * p_pic->p[U_PLANE].i_pitch];
237         uint8_t *p_src_v = &p_pic->p[V_PLANE].p_pixels[y * p_pic->p[V_PLANE].i_pitch];
238         uint8_t *p_dst_u = &p_outpic->p[U_PLANE].p_pixels[y * p_outpic->p[U_PLANE].i_pitch];
239         uint8_t *p_dst_v = &p_outpic->p[V_PLANE].p_pixels[y * p_outpic->p[V_PLANE].i_pitch];
240
241         for( int x = 0; x < p_pic->p[U_PLANE].i_visible_pitch; x++ )
242         {
243             if( IsSimilar( *p_src_u - 0x80, *p_src_v - 0x80,
244                            refu, refv, reflength,
245                            i_satthres, i_simthres ) )
246
247             {
248                 *p_dst_u++ = *p_src_u;
249                 *p_dst_v++ = *p_src_v;
250             }
251             else
252             {
253                 *p_dst_u++ = 0x80;
254                 *p_dst_v++ = 0x80;
255             }
256             p_src_u++;
257             p_src_v++;
258         }
259     }
260
261     return CopyInfoAndRelease( p_outpic, p_pic );
262 }
263
264 static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
265 {
266     picture_t *p_outpic;
267     filter_sys_t *p_sys = p_filter->p_sys;
268     int i_simthres = atomic_load( &p_sys->i_simthres );
269     int i_satthres = atomic_load( &p_sys->i_satthres );
270     int i_color = atomic_load( &p_sys->i_color );
271
272     if( !p_pic ) return NULL;
273
274     p_outpic = filter_NewPicture( p_filter );
275     if( !p_outpic )
276     {
277         picture_Release( p_pic );
278         return NULL;
279     }
280
281     int i_y_offset, i_u_offset, i_v_offset;
282     int i_ret = GetPackedYuvOffsets( p_filter->fmt_in.video.i_chroma,
283                                      &i_y_offset, &i_u_offset, &i_v_offset );
284     if( i_ret == VLC_EGENERIC )
285     {
286         picture_Release( p_pic );
287         return NULL;
288     }
289
290     /*
291      * Copy Y and do the U and V planes
292      */
293     int refu, refv, reflength;
294     GetReference( &refu, &refv, &reflength, i_color );
295
296     for( int y = 0; y < p_pic->p->i_visible_lines; y++ )
297     {
298         uint8_t *p_src = &p_pic->p->p_pixels[y * p_pic->p->i_pitch];
299         uint8_t *p_dst = &p_outpic->p->p_pixels[y * p_outpic->p->i_pitch];
300
301         for( int x = 0; x < p_pic->p->i_visible_pitch / 4; x++ )
302         {
303             p_dst[i_y_offset + 0] = p_src[i_y_offset + 0];
304             p_dst[i_y_offset + 2] = p_src[i_y_offset + 2];
305
306             if( IsSimilar( p_src[i_u_offset] - 0x80, p_src[i_v_offset] - 0x80,
307                            refu, refv, reflength,
308                            i_satthres, i_simthres ) )
309             {
310                 p_dst[i_u_offset] = p_src[i_u_offset];
311                 p_dst[i_v_offset] = p_src[i_v_offset];
312             }
313             else
314             {
315                 p_dst[i_u_offset] = 0x80;
316                 p_dst[i_v_offset] = 0x80;
317             }
318
319             p_dst += 4;
320             p_src += 4;
321         }
322     }
323
324     return CopyInfoAndRelease( p_outpic, p_pic );
325 }
326
327 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
328                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
329 {
330     filter_sys_t *p_sys = p_data;
331
332     if( !strcmp( psz_var, CFG_PREFIX "color" ) )
333         atomic_store( &p_sys->i_color, newval.i_int );
334     else if( !strcmp( psz_var, CFG_PREFIX "similaritythres" ) )
335         atomic_store( &p_sys->i_simthres, newval.i_int );
336     else /* CFG_PREFIX "saturationthres" */
337         atomic_store( &p_sys->i_satthres, newval.i_int );
338
339     (void)p_this; (void)oldval;
340     return VLC_SUCCESS;
341 }