]> git.sesse.net Git - vlc/blob - modules/video_filter/seamcarving.c
Remove unnedeeded msg_Error.
[vlc] / modules / video_filter / seamcarving.c
1 /*****************************************************************************
2  * seamcarving.c: "Seam Carving for Content-Aware Image Resizing"
3  * Based on paper by Shai Avidan and Ariel Shamir.
4  *****************************************************************************
5  * Copyright (C) 2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: 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 <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_sout.h>
36 #include <vlc_vout.h>
37
38 #include "vlc_filter.h"
39 #include "filter_picture.h"
40
41 #include <assert.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 int CropCallback( vlc_object_t *, char const *,
51                          vlc_value_t, vlc_value_t,
52                          void * );
53
54 static void FilterSeamCarving( filter_t *, picture_t *, picture_t * );
55
56 /*****************************************************************************
57  * Module descriptor
58  *****************************************************************************/
59
60 #define FILTER_PREFIX "seamcarving-"
61
62 vlc_module_begin();
63     set_description( N_("Seam Carving video filter") );
64     set_shortname( N_( "Seam Carving" ));
65     set_capability( "video filter2", 0 );
66     set_category( CAT_VIDEO );
67     set_subcategory( SUBCAT_VIDEO_VFILTER );
68
69     set_callbacks( Create, Destroy );
70 vlc_module_end();
71
72 struct filter_sys_t
73 {
74     int *p_energy;
75     int *p_grad;
76
77     int i_crop;
78 };
79
80 static int Create( vlc_object_t *p_this )
81 {
82     filter_t *p_filter = (filter_t *)p_this;
83
84     /* Allocate structure */
85     p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
86     if( p_filter->p_sys == NULL )
87         return VLC_ENOMEM;
88
89     p_filter->pf_video_filter = Filter;
90     p_filter->p_sys->p_energy = NULL;
91     p_filter->p_sys->p_grad = NULL;
92     p_filter->p_sys->i_crop = 0;
93
94     var_Create( p_filter, "crop", VLC_VAR_INTEGER|VLC_VAR_ISCOMMAND );
95     var_AddCallback( p_filter, "crop", CropCallback, p_filter->p_sys );
96
97     return VLC_SUCCESS;
98 }
99
100 static void Destroy( vlc_object_t *p_this )
101 {
102     filter_t *p_filter = (filter_t *)p_this;
103
104     free( p_filter->p_sys->p_energy );
105     free( p_filter->p_sys->p_grad );
106
107     free( p_filter->p_sys );
108 }
109
110 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
111 {
112     picture_t *p_outpic;
113
114     if( !p_pic ) return NULL;
115
116     p_outpic = p_filter->pf_vout_buffer_new( p_filter );
117     if( !p_outpic )
118     {
119         msg_Warn( p_filter, "can't get output picture" );
120         if( p_pic->pf_release )
121             p_pic->pf_release( p_pic );
122         return NULL;
123     }
124
125     FilterSeamCarving( p_filter, p_pic, p_outpic );
126
127     return CopyInfoAndRelease( p_outpic, p_pic );
128 }
129
130 static inline int my_min3( int a, int b, int c );
131 static inline int my_min( int a, int b );
132 static int RemoveVerticalSeam( filter_t *p_filter, picture_t *p_inpic, picture_t *p_outpic, int i_src_visible );
133
134 //#define DRAW_GRADIENT
135 //#define DRAW_ENERGY
136 //#define DRAW_SEAM
137
138 static void FilterSeamCarving( filter_t *p_filter, picture_t *p_inpic,
139                                                 picture_t *p_outpic )
140 {
141     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
142     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
143
144     int i_src_visible = p_inpic->p[Y_PLANE].i_visible_pitch;
145
146     if( !p_filter->p_sys->p_energy )
147         p_filter->p_sys->p_energy = (int*)malloc(i_src_pitch * i_num_lines * sizeof(int));
148     if( !p_filter->p_sys->p_grad )
149         p_filter->p_sys->p_grad = (int*)malloc(i_src_pitch * i_num_lines * sizeof(int));
150
151 //#if defined( DRAW_GRADIENT ) || defined( DRAW_ENERGY ) || defined( DRAW_SEAM )
152     vlc_memcpy( p_outpic->p[Y_PLANE].p_pixels, p_inpic->p[Y_PLANE].p_pixels,
153         p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
154 //#else
155 //    vlc_memset( p_outpix, 0x80,
156 //        p_outpic->p[Y_PLANE].i_lines * p_outpic->p[Y_PLANE].i_pitch );
157 //#endif
158     vlc_memset( p_outpic->p[U_PLANE].p_pixels, 0x80,
159         p_outpic->p[U_PLANE].i_lines * p_outpic->p[U_PLANE].i_pitch );
160     vlc_memset( p_outpic->p[V_PLANE].p_pixels, 0x80,
161         p_outpic->p[V_PLANE].i_lines * p_outpic->p[V_PLANE].i_pitch );
162
163 #if defined( DRAW_GRADIENT ) || defined( DRAW_ENERGY ) || defined( DRAW_SEAM )
164     i_src_visible = RemoveVerticalSeam( p_filter, p_outpic, p_outpic, i_src_visible );
165 #else
166     static int j = 1;
167     static int k = 1;
168     int i;
169     if( p_filter->p_sys->i_crop != 0 )
170         j = p_filter->p_sys->i_crop;
171     for( i = 0; i < j; i++ )
172     i_src_visible = RemoveVerticalSeam( p_filter, p_outpic, p_outpic, i_src_visible );
173     int y;
174     for( y = 0; y < p_outpic->p[Y_PLANE].i_lines; y++ )
175         vlc_memset( p_outpic->p[Y_PLANE].p_pixels + y*p_outpic->p[Y_PLANE].i_pitch + i_src_visible, 0x00, p_outpic->p[Y_PLANE].i_pitch - i_src_visible );
176     j += k;
177     if( j == 100 ) k = -1;
178     if( j == 1 ) k = 1;
179 #endif
180 }
181
182 static int ComputeGradient( filter_t *p_filter, picture_t *p_inpic, int i_src_visible )
183 {
184     int x, y;
185     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
186     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
187
188     const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels;
189     int *p_grad = p_filter->p_sys->p_grad;
190
191     for( y = 1; y < i_num_lines - 1;
192          y++, p_grad += i_src_pitch )
193     {
194         /* Compute line y's gradient */
195 #define GRADx( x ) \
196             ( \
197               abs( \
198                  ( p_inpix[(y-1)*i_src_pitch+x-1] \
199                    - p_inpix[(y+1)*i_src_pitch+x-1] ) \
200                + ( ( p_inpix[(y-1)*i_src_pitch+x] \
201                     - p_inpix[(y+1)*i_src_pitch+x] ) <<1 ) \
202                + ( p_inpix[(y-1)*i_src_pitch+x+1] \
203                    - p_inpix[(y+1)*i_src_pitch+x+1] ) \
204               ) \
205             + \
206               abs( \
207                  ( p_inpix[(y-1)*i_src_pitch+x-1] \
208                    - p_inpix[(y-1)*i_src_pitch+x+1] ) \
209                + ( ( p_inpix[y*i_src_pitch+x-1] \
210                     - p_inpix[y*i_src_pitch+x+1] ) <<1 ) \
211                + ( p_inpix[(y+1)*i_src_pitch+x-1] \
212                    - p_inpix[(y+1)*i_src_pitch+x+1] ) \
213               ) \
214             )
215         for( x = 1; x < i_src_visible - 1; x++ )
216         {
217             p_grad[x] = GRADx( x );
218 #ifdef DRAW_GRADIENT
219             p_outpix[y*i_src_pitch+x] = p_grad[x]>>3;
220 #endif
221         }
222     }
223     return 0;
224 }
225
226 static int RemoveVerticalSeam( filter_t *p_filter, picture_t *p_inpic, picture_t *p_outpic, int i_src_visible )
227 {
228     int x, y;
229     const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch;
230     const int i_num_lines = p_inpic->p[Y_PLANE].i_visible_lines;
231
232     uint8_t *p_outpix = p_outpic->p[Y_PLANE].p_pixels;
233
234     int *p_energy = p_filter->p_sys->p_energy;
235     int *p_grad = p_filter->p_sys->p_grad;
236
237     ComputeGradient( p_filter, p_inpic, i_src_visible );
238
239     /** Compute the image's energy (using a sobel gradient as the base energy
240      ** function) */
241     /* Set the first energy line to 0 */
242     memset( p_energy, 0, i_src_pitch*sizeof(int));
243
244     int *p_energy_prev = p_energy;
245     for( y = 1; y < i_num_lines - 1;
246          y++, p_energy_prev = p_energy, p_energy += i_src_pitch,
247          p_grad += i_src_pitch )
248     {
249         /* Compute line y's minimum energy value for paths ending on
250          * each x */
251         x = 1;
252         p_energy[x] = my_min( p_energy_prev[x  ]+p_grad[x  ],
253                            p_energy_prev[x+1]+p_grad[x+1] );
254         for( x = 2; x < i_src_visible - 2; x++ )
255         {
256             p_energy[x] = my_min3( p_energy_prev[x-1]+p_grad[x-1],
257                                 p_energy_prev[x  ]+p_grad[x  ],
258                                 p_energy_prev[x+1]+p_grad[x+1] );
259         }
260         p_energy[x] = my_min( p_energy_prev[x-1]+p_grad[x-1],
261                            p_energy_prev[x  ]+p_grad[x  ] );
262
263 #ifdef DRAW_ENERGY
264         int max = p_energy[1];
265         for( x = 1; x < i_src_visible - 1; x++ )
266             if( p_energy[x] > max ) max = p_energy[x];
267         for( x = 1; x < i_src_visible - 1; x++ )
268             p_outpix[y*i_src_pitch+x] = p_energy[x]*0xff/max;
269 #endif
270     }
271
272     /* Find the minimum energy point on the last line */
273     y--;
274     p_energy -= i_src_pitch;
275     p_grad -= i_src_pitch;
276
277     int m = p_energy[1];
278     int xmin = 1;
279     for( x = 1; x < i_src_visible - 1; x++ )
280     {
281         if( p_energy[x] < m )
282         {
283             m = p_energy[x];
284             xmin = x;
285         }
286     }
287
288 #ifdef DRAW_SEAM
289     p_outpix[y*i_src_pitch+xmin] = 0xff;
290     p_outpix[(y+1)*i_src_pitch+xmin] = 0xff;
291 #else
292     memmove( p_outpix+y*i_src_pitch+xmin, p_outpix+y*i_src_pitch+xmin+1, i_src_pitch-(xmin+1) );
293     memmove( p_outpix+(y+1)*i_src_pitch+xmin, p_outpix+(y+1)*i_src_pitch+xmin+1, i_src_pitch-(xmin+1) );
294 #endif
295
296     p_energy -= i_src_pitch;
297     for( ; y>1; y--, p_energy -= i_src_pitch, p_grad -= i_src_pitch )
298     {
299         if( m != p_energy[xmin]+p_grad[xmin] )
300         {
301             if( xmin > 1 && m == p_energy[xmin-1]+p_grad[xmin-1] )
302             {
303                 xmin--;
304             }
305             else if( xmin < i_src_visible - 2 && m == p_energy[xmin+1]+p_grad[xmin+1] )
306             {
307                 xmin++;
308             }
309             else
310             {
311                 printf("Alarm! %d\n" ,y);
312                 //assert( 0 );
313             }
314         }
315         m = p_energy[xmin];
316 #ifdef DRAW_SEAM
317         p_outpix[y*i_src_pitch+xmin] = 0xff;
318 #else
319         memmove( p_outpix+y*i_src_pitch+xmin, p_outpix+y*i_src_pitch+xmin+1, i_src_pitch-(xmin+1) );
320 #endif
321     }
322 #ifdef DRAW_SEAM
323     p_outpix[y*i_src_pitch+xmin] = 0xff;
324 #else
325     memmove( p_outpix+y*i_src_pitch+xmin, p_outpix+y*i_src_pitch+xmin+1, i_src_pitch-(xmin+1) );
326 #endif
327     y--;
328 #ifdef DRAW_SEAM
329     p_outpix[y*i_src_pitch+xmin] = 0xff;
330 #else
331     memmove( p_outpix+y*i_src_pitch+xmin, p_outpix+y*i_src_pitch+xmin+1, i_src_pitch-(xmin+1) );
332 #endif
333
334 #if defined( DRAW_SEAM )
335     return i_src_visible;
336 #else
337     return i_src_visible-1;
338 #endif
339 }
340
341 static inline int my_min3( int a, int b, int c )
342 {
343     if( a < b )
344     {
345         if( a < c ) return a;
346         return c;
347     }
348     if( b < c ) return b;
349     return c;
350 }
351 static inline int my_min( int a, int b )
352 {
353     return a < b ? a : b;
354 }
355
356 static int CropCallback( vlc_object_t *p_this, char const *psz_var,
357                                 vlc_value_t oldval, vlc_value_t newval,
358                                 void *p_data )
359 {
360     VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
361     filter_sys_t *p_sys = (filter_sys_t *)p_data;
362     p_sys->i_crop = newval.i_int;
363     return VLC_SUCCESS;
364 }