]> git.sesse.net Git - vlc/blob - modules/video_filter/colorthres.c
Use var_InheritString for --decklink-video-connection.
[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 <math.h>
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_sout.h>
38
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_integer( CFG_PREFIX "color", 0x00FF0000, NULL, COLOR_TEXT,
76                  COLOR_LONGTEXT, false )
77         change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
78     add_integer( CFG_PREFIX "saturationthres", 20, NULL,
79                  N_("Saturaton threshold"), "", false )
80     add_integer( CFG_PREFIX "similaritythres", 15, NULL,
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     int i_simthres;
102     int i_satthres;
103     int i_color;
104     vlc_mutex_t lock;
105 };
106
107 /*****************************************************************************
108  * Create: allocates adjust video thread output method
109  *****************************************************************************
110  * This function allocates and initializes a adjust vout method.
111  *****************************************************************************/
112 static int Create( vlc_object_t *p_this )
113 {
114     filter_t *p_filter = (filter_t *)p_this;
115     filter_sys_t *p_sys;
116
117     switch( p_filter->fmt_in.video.i_chroma )
118     {
119         CASE_PLANAR_YUV
120             p_filter->pf_video_filter = Filter;
121             break;
122
123         CASE_PACKED_YUV_422
124             p_filter->pf_video_filter = FilterPacked;
125             break;
126
127         default:
128             msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
129                      (char*)&(p_filter->fmt_in.video.i_chroma) );
130             return VLC_EGENERIC;
131     }
132
133     if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
134     {
135         msg_Err( p_filter, "Input and output chromas don't match" );
136         return VLC_EGENERIC;
137     }
138
139     /* Allocate structure */
140     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
141     if( p_filter->p_sys == NULL )
142         return VLC_ENOMEM;
143
144     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
145                        p_filter->p_cfg );
146     p_sys->i_color = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "color" );
147     p_sys->i_simthres = var_CreateGetIntegerCommand( p_filter,
148                                                      CFG_PREFIX "similaritythres" );
149     p_sys->i_satthres = var_CreateGetIntegerCommand( p_filter,
150                                                      CFG_PREFIX "saturationthres" );
151
152     vlc_mutex_init( &p_sys->lock );
153
154     var_AddCallback( p_filter, CFG_PREFIX "color", FilterCallback, NULL );
155     var_AddCallback( p_filter, CFG_PREFIX "similaritythres", FilterCallback, NULL );
156     var_AddCallback( p_filter, CFG_PREFIX "saturationthres", FilterCallback, NULL );
157
158     return VLC_SUCCESS;
159 }
160
161 /*****************************************************************************
162  * Destroy: destroy adjust video thread output method
163  *****************************************************************************
164  * Terminate an output method created by adjustCreateOutputMethod
165  *****************************************************************************/
166 static void Destroy( vlc_object_t *p_this )
167 {
168     filter_t *p_filter = (filter_t *)p_this;
169
170     var_DelCallback( p_filter, CFG_PREFIX "color", FilterCallback, NULL );
171     var_DelCallback( p_filter, CFG_PREFIX "similaritythres", FilterCallback, NULL );
172     var_DelCallback( p_filter, CFG_PREFIX "saturationthres", FilterCallback, NULL );
173
174     vlc_mutex_destroy( &p_filter->p_sys->lock );
175     free( p_filter->p_sys );
176 }
177
178 static void GetReference( int *refu, int *refv, int *reflength,
179                           uint32_t i_color )
180 {
181     int i_red   = ( i_color & 0xFF0000 ) >> 16;
182     int i_green = ( i_color & 0x00FF00 ) >> 8;
183     int i_blue  = ( i_color & 0x0000FF );
184     int i_u = (int8_t)(( -38 * i_red - 74 * i_green + 112 * i_blue + 128) >> 8) + 128;
185     int i_v = (int8_t)(( 112 * i_red - 94 * i_green -  18 * i_blue + 128) >> 8) + 128;
186     *refu = i_u - 0x80;
187     *refv = i_v - 0x80;
188     *reflength = sqrt(*refu * *refu + *refv * *refv);
189 }
190
191 static bool IsSimilar( int u, int v,
192                        int refu, int refv, int reflength,
193                        int i_satthres, int i_simthres )
194 {
195     int length = sqrt(u * u + v * v);
196
197     int diffu = refu * length - u * reflength;
198     int diffv = refv * length - v * reflength;
199     int64_t difflen2 = diffu * diffu + diffv * diffv;
200     int64_t thres = length * reflength;
201     thres *= thres;
202     return length > i_satthres && (difflen2 * i_simthres < thres);
203 }
204 /*****************************************************************************
205  * Render: displays previously rendered output
206  *****************************************************************************
207  * This function send the currently rendered image to adjust modified image,
208  * waits until it is displayed and switch the two rendering buffers, preparing
209  * next frame.
210  *****************************************************************************/
211 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
212 {
213     picture_t *p_outpic;
214     filter_sys_t *p_sys = p_filter->p_sys;
215
216     vlc_mutex_lock( &p_sys->lock );
217     int i_simthres = p_sys->i_simthres;
218     int i_satthres = p_sys->i_satthres;
219     int i_color = p_sys->i_color;
220     vlc_mutex_unlock( &p_sys->lock );
221
222     if( !p_pic ) return NULL;
223
224     p_outpic = filter_NewPicture( p_filter );
225     if( !p_outpic )
226     {
227         picture_Release( p_pic );
228         return NULL;
229     }
230
231     /* Copy the Y plane */
232     plane_CopyPixels( &p_outpic->p[Y_PLANE], &p_pic->p[Y_PLANE] );
233
234     /*
235      * Do the U and V planes
236      */
237     int refu, refv, reflength;
238     GetReference( &refu, &refv, &reflength, i_color );
239
240     for( int y = 0; y < p_pic->p[U_PLANE].i_visible_lines; y++ )
241     {
242         uint8_t *p_src_u = &p_pic->p[U_PLANE].p_pixels[y * p_pic->p[U_PLANE].i_pitch];
243         uint8_t *p_src_v = &p_pic->p[V_PLANE].p_pixels[y * p_pic->p[V_PLANE].i_pitch];
244         uint8_t *p_dst_u = &p_outpic->p[U_PLANE].p_pixels[y * p_outpic->p[U_PLANE].i_pitch];
245         uint8_t *p_dst_v = &p_outpic->p[V_PLANE].p_pixels[y * p_outpic->p[V_PLANE].i_pitch];
246
247         for( int x = 0; x < p_pic->p[U_PLANE].i_visible_pitch; x++ )
248         {
249             if( IsSimilar( *p_src_u - 0x80, *p_src_v - 0x80,
250                            refu, refv, reflength,
251                            i_satthres, i_simthres ) )
252
253             {
254                 *p_dst_u++ = *p_src_u;
255                 *p_dst_v++ = *p_src_v;
256             }
257             else
258             {
259                 *p_dst_u++ = 0x80;
260                 *p_dst_v++ = 0x80;
261             }
262             p_src_u++;
263             p_src_v++;
264         }
265     }
266
267     return CopyInfoAndRelease( p_outpic, p_pic );
268 }
269
270 static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
271 {
272     picture_t *p_outpic;
273     filter_sys_t *p_sys = p_filter->p_sys;
274
275     vlc_mutex_lock( &p_sys->lock );
276     int i_simthres = p_sys->i_simthres;
277     int i_satthres = p_sys->i_satthres;
278     int i_color = p_sys->i_color;
279     vlc_mutex_unlock( &p_sys->lock );
280
281     if( !p_pic ) return NULL;
282
283     p_outpic = filter_NewPicture( p_filter );
284     if( !p_outpic )
285     {
286         picture_Release( p_pic );
287         return NULL;
288     }
289
290     int i_y_offset, i_u_offset, i_v_offset;
291     int i_ret = GetPackedYuvOffsets( p_filter->fmt_in.video.i_chroma,
292                                      &i_y_offset, &i_u_offset, &i_v_offset );
293     if( i_ret == VLC_EGENERIC )
294     {
295         picture_Release( p_pic );
296         return NULL;
297     }
298
299     /*
300      * Copy Y and do the U and V planes
301      */
302     int refu, refv, reflength;
303     GetReference( &refu, &refv, &reflength, i_color );
304
305     for( int y = 0; y < p_pic->p->i_visible_lines; y++ )
306     {
307         uint8_t *p_src = &p_pic->p->p_pixels[y * p_pic->p->i_pitch];
308         uint8_t *p_dst = &p_outpic->p->p_pixels[y * p_outpic->p->i_pitch];
309
310         for( int x = 0; x < p_pic->p->i_visible_pitch / 4; x++ )
311         {
312             p_dst[i_y_offset + 0] = p_src[i_y_offset + 0];
313             p_dst[i_y_offset + 2] = p_src[i_y_offset + 2];
314
315             if( IsSimilar( p_src[i_u_offset] - 0x80, p_src[i_v_offset] - 0x80,
316                            refu, refv, reflength,
317                            i_satthres, i_simthres ) )
318             {
319                 p_dst[i_u_offset] = p_src[i_u_offset];
320                 p_dst[i_v_offset] = p_src[i_v_offset];
321             }
322             else
323             {
324                 p_dst[i_u_offset] = 0x80;
325                 p_dst[i_v_offset] = 0x80;
326             }
327
328             p_dst += 4;
329             p_src += 4;
330         }
331     }
332
333     return CopyInfoAndRelease( p_outpic, p_pic );
334 }
335
336 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
337                             vlc_value_t oldval, vlc_value_t newval, void *p_data )
338 {
339     (void)oldval;    (void)p_data;
340     filter_t *p_filter = (filter_t*)p_this;
341     filter_sys_t *p_sys = p_filter->p_sys;
342
343     if( !strcmp( psz_var, CFG_PREFIX "color" ) )
344     {
345         vlc_mutex_lock( &p_sys->lock );
346         p_sys->i_color = newval.i_int;
347         vlc_mutex_unlock( &p_sys->lock );
348     }
349     else if( !strcmp( psz_var, CFG_PREFIX "similaritythres" ) )
350     {
351         vlc_mutex_lock( &p_sys->lock );
352         p_sys->i_simthres = newval.i_int;
353         vlc_mutex_unlock( &p_sys->lock );
354     }
355     else /* CFG_PREFIX "saturationthres" */
356     {
357         vlc_mutex_lock( &p_sys->lock );
358         p_sys->i_satthres = newval.i_int;
359         vlc_mutex_unlock( &p_sys->lock );
360     }
361
362     return VLC_SUCCESS;
363 }