]> git.sesse.net Git - vlc/blob - modules/video_filter/colorthres.c
c49ea7dc2bf44800ed1d91b334a37e08c1ebbaf2
[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 /*****************************************************************************
179  * Render: displays previously rendered output
180  *****************************************************************************
181  * This function send the currently rendered image to adjust modified image,
182  * waits until it is displayed and switch the two rendering buffers, preparing
183  * next frame.
184  *****************************************************************************/
185 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
186 {
187     picture_t *p_outpic;
188     filter_sys_t *p_sys = p_filter->p_sys;
189
190     vlc_mutex_lock( &p_sys->lock );
191     int i_simthres = p_sys->i_simthres;
192     int i_satthres = p_sys->i_satthres;
193     int i_color = p_sys->i_color;
194     vlc_mutex_unlock( &p_sys->lock );
195
196     if( !p_pic ) return NULL;
197
198     p_outpic = filter_NewPicture( p_filter );
199     if( !p_outpic )
200     {
201         picture_Release( p_pic );
202         return NULL;
203     }
204
205     /* Copy the Y plane */
206     plane_CopyPixels( &p_outpic->p[Y_PLANE], &p_pic->p[Y_PLANE] );
207
208     /*
209      * Do the U and V planes
210      */
211     int i_red = ( i_color & 0xFF0000 ) >> 16;
212     int i_green = ( i_color & 0xFF00 ) >> 8;
213     int i_blue = i_color & 0xFF;
214     int i_u = (int8_t)(( -38 * i_red - 74 * i_green +
215                      112 * i_blue + 128) >> 8) + 128;
216     int i_v = (int8_t)(( 112 * i_red  -  94 * i_green -
217                       18 * i_blue + 128) >> 8) + 128;
218
219     int refu = i_u - 0x80;         /*bright red*/
220     int refv = i_v - 0x80;
221     int reflength = sqrt(refu*refu+refv*refv);
222
223     for( int y = 0; y < p_pic->p[U_PLANE].i_visible_lines; y++ )
224     {
225         uint8_t *p_src_u = &p_pic->p[U_PLANE].p_pixels[y * p_pic->p[U_PLANE].i_pitch];
226         uint8_t *p_src_v = &p_pic->p[V_PLANE].p_pixels[y * p_pic->p[V_PLANE].i_pitch];
227         uint8_t *p_dst_u = &p_outpic->p[U_PLANE].p_pixels[y * p_outpic->p[U_PLANE].i_pitch];
228         uint8_t *p_dst_v = &p_outpic->p[V_PLANE].p_pixels[y * p_outpic->p[V_PLANE].i_pitch];
229
230         for( int x = 0; x < p_pic->p[U_PLANE].i_visible_pitch; x++ )
231         {
232             /* Length of color vector */
233             int inu = *p_src_u - 0x80;
234             int inv = *p_src_v - 0x80;
235             int length = sqrt(inu*inu+inv*inv);
236
237             int diffu = refu * length - inu *reflength;
238             int diffv = refv * length - inv *reflength;
239             long long int difflen2=diffu*diffu;
240             difflen2 +=diffv*diffv;
241             long long int thres = length*reflength;
242             thres *= thres;
243             if( length > i_satthres && (difflen2*i_simthres< thres ) )
244             {
245                 *p_dst_u++ = *p_src_u;
246                 *p_dst_v++ = *p_src_v;
247             }
248             else
249             {
250                 *p_dst_u++ = 0x80;
251                 *p_dst_v++ = 0x80;
252             }
253             p_src_u++;
254             p_src_v++;
255         }
256     }
257
258     return CopyInfoAndRelease( p_outpic, p_pic );
259 }
260
261 static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_pic )
262 {
263     picture_t *p_outpic;
264     filter_sys_t *p_sys = p_filter->p_sys;
265
266     vlc_mutex_lock( &p_sys->lock );
267     int i_simthres = p_sys->i_simthres;
268     int i_satthres = p_sys->i_satthres;
269     int i_color = p_sys->i_color;
270     vlc_mutex_unlock( &p_sys->lock );
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     GetPackedYuvOffsets( p_filter->fmt_in.video.i_chroma,
283                          &i_y_offset, &i_u_offset, &i_v_offset );
284
285     /*
286      * Copy Y and do the U and V planes
287      */
288     int i_red = ( i_color & 0xFF0000 ) >> 16;
289     int i_green = ( i_color & 0xFF00 ) >> 8;
290     int i_blue = i_color & 0xFF;
291     int i_u = (int8_t)(( -38 * i_red - 74 * i_green +
292                      112 * i_blue + 128) >> 8) + 128;
293     int i_v = (int8_t)(( 112 * i_red  -  94 * i_green -
294                       18 * i_blue + 128) >> 8) + 128;
295     int refu = i_u - 0x80;         /*bright red*/
296     int refv = i_v - 0x80;
297     int reflength = sqrt(refu*refu+refv*refv);
298
299     for( int y = 0; y < p_pic->p->i_visible_lines; y++ )
300     {
301         uint8_t *p_src = &p_pic->p->p_pixels[y * p_pic->p->i_pitch];
302         uint8_t *p_dst = &p_outpic->p->p_pixels[y * p_outpic->p->i_pitch];
303
304         for( int x = 0; x < p_pic->p->i_visible_pitch / 4; x++ )
305         {
306             p_dst[i_y_offset + 0] = p_src[i_y_offset + 0];
307             p_dst[i_y_offset + 2] = p_src[i_y_offset + 2];
308
309             /* Length of color vector */
310             int inu = p_src[i_u_offset] - 0x80;
311             int inv = p_src[i_v_offset] - 0x80;
312             int length = sqrt(inu*inu+inv*inv);
313
314             int diffu = refu * length - inu *reflength;
315             int diffv = refv * length - inv *reflength;
316             long long int difflen2=diffu*diffu;
317             difflen2 +=diffv*diffv;
318             long long int thres = length*reflength;
319             thres *= thres;
320             if( length > i_satthres && (difflen2*i_simthres< thres ) )
321             {
322                 p_dst[i_u_offset] = p_src[i_u_offset];
323                 p_dst[i_v_offset] = p_src[i_v_offset];
324             }
325             else
326             {
327                 p_dst[i_u_offset] = 0x80;
328                 p_dst[i_v_offset] = 0x80;
329             }
330
331             p_dst += 4;
332             p_src += 4;
333         }
334     }
335
336     return CopyInfoAndRelease( p_outpic, p_pic );
337 }
338
339 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
340                             vlc_value_t oldval, vlc_value_t newval, void *p_data )
341 {
342     (void)oldval;    (void)p_data;
343     filter_t *p_filter = (filter_t*)p_this;
344     filter_sys_t *p_sys = p_filter->p_sys;
345
346     if( !strcmp( psz_var, CFG_PREFIX "color" ) )
347     {
348         vlc_mutex_lock( &p_sys->lock );
349         p_sys->i_color = newval.i_int;
350         vlc_mutex_unlock( &p_sys->lock );
351     }
352     else if( !strcmp( psz_var, CFG_PREFIX "similaritythres" ) )
353     {
354         vlc_mutex_lock( &p_sys->lock );
355         p_sys->i_simthres = newval.i_int;
356         vlc_mutex_unlock( &p_sys->lock );
357     }
358     else /* CFG_PREFIX "saturationthres" */
359     {
360         vlc_mutex_lock( &p_sys->lock );
361         p_sys->i_satthres = newval.i_int;
362         vlc_mutex_unlock( &p_sys->lock );
363     }
364
365     return VLC_SUCCESS;
366 }