]> git.sesse.net Git - mlt/blob - src/modules/vid.stab/filter_vidstab.cpp
Add support for more image formats to vid.stab
[mlt] / src / modules / vid.stab / filter_vidstab.cpp
1 /*
2  * filter_vidstab.cpp
3  * Copyright (C) 2013 Marco Gittler <g.marco@freenet.de>
4  * Copyright (C) 2013 Jakub Ksiezniak <j.ksiezniak@gmail.com>
5  * Copyright (C) 2014 Brian Matherly <pez4brian@yahoo.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 extern "C"
23 {
24 #include <vid.stab/libvidstab.h>
25 #include <framework/mlt.h>
26 #include <framework/mlt_animation.h>
27 #include "common.h"
28 }
29
30 #include <sstream>
31 #include <string.h>
32 #include <assert.h>
33
34 typedef struct
35 {
36         VSMotionDetect md;
37         VSManyLocalMotions mlms;
38 } vs_analyze;
39
40 typedef struct
41 {
42         VSTransformData td;
43         VSTransformations trans;
44 } vs_apply;
45
46 typedef struct
47 {
48         vs_analyze* analyze_data;
49         vs_apply* apply_data;
50 } vs_data;
51
52 /** Free all data used by a VSManyLocalMotions instance.
53  */
54
55 static void free_manylocalmotions( VSManyLocalMotions* mlms )
56 {
57         for( int i = 0; i < vs_vector_size( mlms ); i++ )
58         {
59                 LocalMotions* lms = (LocalMotions*)vs_vector_get( mlms, i );
60
61                 if( lms )
62                 {
63                         vs_vector_del( lms );
64                 }
65         }
66         vs_vector_del( mlms );
67 }
68
69 /** Serialize a VSManyLocalMotions instance and store it in the properties.
70  *
71  * Each LocalMotions instance is converted to a string and stored in an animation.
72  * Then, the entire animation is serialized and stored in the properties.
73  * \param properties the filter properties
74  * \param mlms an initialized VSManyLocalMotions instance
75  */
76
77 static void publish_manylocalmotions( mlt_properties properties, VSManyLocalMotions* mlms )
78 {
79         mlt_animation animation = mlt_animation_new();
80         mlt_animation_item_s item;
81
82         // Initialize animation item.
83         item.is_key = 1;
84         item.keyframe_type = mlt_keyframe_discrete;
85         item.property = mlt_property_init();
86
87         // Convert each LocalMotions instance to a string and add it to the animation.
88         for( int i = 0; i < vs_vector_size( mlms ); i++ )
89         {
90                 LocalMotions* lms = (LocalMotions*)vs_vector_get( mlms, i );
91                 item.frame = i;
92                 int size = vs_vector_size( lms );
93
94                 std::ostringstream oss;
95                 for ( int j = 0; j < size; ++j )
96                 {
97                         LocalMotion* lm = (LocalMotion*)vs_vector_get( lms, j );
98                         oss << lm->v.x << ' ';
99                         oss << lm->v.y << ' ';
100                         oss << lm->f.x << ' ';
101                         oss << lm->f.y << ' ';
102                         oss << lm->f.size << ' ';
103                         oss << lm->contrast << ' ';
104                         oss << lm->match << ' ';
105                 }
106                 mlt_property_set_string( item.property, oss.str().c_str() );
107                 mlt_animation_insert( animation, &item );
108         }
109
110         // Serialize and store the animation.
111         char* motion_str = mlt_animation_serialize( animation );
112         mlt_properties_set( properties, "results", motion_str );
113
114         mlt_property_close( item.property );
115         mlt_animation_close( animation );
116         free( motion_str );
117 }
118
119 /** Get the motions data from the properties and convert it to a VSManyLocalMotions.
120  *
121  * Each LocalMotions instance is converted to a string and stored in an animation.
122  * Then, the entire animation is serialized and stored in the properties.
123  * \param properties the filter properties
124  * \param mlms an initialized (but empty) VSManyLocalMotions instance
125  */
126
127 static void read_manylocalmotions( mlt_properties properties, VSManyLocalMotions* mlms )
128 {
129         mlt_animation_item_s item;
130         item.property = mlt_property_init();
131         mlt_animation animation = mlt_animation_new();
132         // Get the results property which represents the VSManyLocalMotions
133         char* motion_str = mlt_properties_get( properties, "results" );
134
135         mlt_animation_parse( animation, motion_str, 0, 0, NULL );
136
137         int length = mlt_animation_get_length( animation );
138
139         for ( int i = 0; i <= length; ++i )
140         {
141                 LocalMotions lms;
142                 vs_vector_init( &lms, 0 );
143
144                 // Get the animation item that represents the LocalMotions
145                 mlt_animation_get_item( animation, &item, i );
146
147                 // Convert the property to a real LocalMotions
148                 std::istringstream iss( mlt_property_get_string( item.property ) );
149                 while ( iss.good() )
150                 {
151                         LocalMotion lm;
152                         iss >> lm.v.x >> lm.v.y >> lm.f.x >> lm.f.y >> lm.f.size >> lm.contrast >> lm.match;
153                         if ( !iss.fail() )
154                         {
155                                 vs_vector_append_dup( &lms, &lm, sizeof(lm) );
156                         }
157                 }
158
159                 // Add the LocalMotions to the ManyLocalMotions
160                 vs_vector_set_dup( mlms, i, &lms, sizeof(LocalMotions) );
161         }
162
163         mlt_property_close( item.property );
164         mlt_animation_close( animation );
165 }
166
167 static vs_apply* init_apply_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height )
168 {
169         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
170         vs_apply* apply_data = (vs_apply*)calloc( 1, sizeof(vs_apply) );
171         memset( apply_data, 0, sizeof( vs_apply ) );
172
173         const char* filterName = mlt_properties_get( properties, "mlt_service" );
174         VSTransformConfig conf = vsTransformGetDefaultConfig( filterName );
175         conf.smoothing = mlt_properties_get_int( properties, "smoothing" );
176         conf.maxShift = mlt_properties_get_int( properties, "maxshift" );
177         conf.maxAngle = mlt_properties_get_double( properties, "maxangle" );
178         conf.crop = (VSBorderType)mlt_properties_get_int( properties, "crop" );
179         conf.zoom = mlt_properties_get_int( properties, "zoom" );
180         conf.optZoom = mlt_properties_get_int( properties, "optzoom" );
181         conf.zoomSpeed = mlt_properties_get_double( properties, "zoomspeed" );
182         conf.relative = mlt_properties_get_int( properties, "relative" );
183         conf.invert = mlt_properties_get_int( properties, "invert" );
184         if ( mlt_properties_get_int( properties, "tripod" ) != 0 )
185         {
186                 // Virtual tripod mode: relative=False, smoothing=0
187                 conf.relative = 0;
188                 conf.smoothing = 0;
189         }
190
191         // by default a bilinear interpolation is selected
192         const char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" );
193         conf.interpolType = VS_BiLinear;
194         if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0)
195                 conf.interpolType = VS_Zero;
196         else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0)
197                 conf.interpolType = VS_Linear;
198
199         // load motions
200         VSManyLocalMotions mlms;
201         vs_vector_init( &mlms, mlt_filter_get_length2( filter, frame ) );
202         read_manylocalmotions( properties, &mlms );
203
204         // Convert motions to VSTransformations
205         VSTransformData* td = &apply_data->td;
206         VSTransformations* trans = &apply_data->trans;
207         VSFrameInfo fi_src, fi_dst;
208         vsFrameInfoInit( &fi_src, width, height, vs_format );
209         vsFrameInfoInit( &fi_dst, width, height, vs_format );
210         vsTransformDataInit( td, &conf, &fi_src, &fi_dst );
211         vsTransformationsInit( trans );
212         vsLocalmotions2Transforms( td, &mlms, trans );
213         vsPreprocessTransforms( td, trans );
214
215         free_manylocalmotions( &mlms );
216
217         return apply_data;
218 }
219
220 static void destory_apply_data( vs_apply* apply_data )
221 {
222         if ( apply_data )
223         {
224                 vsTransformDataCleanup( &apply_data->td );
225                 vsTransformationsCleanup( &apply_data->trans );
226                 free( apply_data );
227         }
228 }
229
230 static vs_analyze* init_analyze_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height )
231 {
232         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
233         vs_analyze* analyze_data = (vs_analyze*)calloc( 1, sizeof(vs_analyze) );
234         memset( analyze_data, 0, sizeof(vs_analyze) );
235
236         // Initialize the VSManyLocalMotions vector where motion data will be
237         // stored for each frame.
238         vs_vector_init( &analyze_data->mlms, mlt_filter_get_length2( filter, frame ) );
239
240         // Initialize a VSFrameInfo to be used below
241         VSFrameInfo fi;
242         vsFrameInfoInit( &fi, width, height, vs_format );
243
244         // Initialize a VSMotionDetect
245         const char* filterName = mlt_properties_get( properties, "mlt_service" );
246         VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig( filterName );
247         conf.shakiness = mlt_properties_get_int( properties, "shakiness" );
248         conf.accuracy = mlt_properties_get_int( properties, "accuracy" );
249         conf.stepSize = mlt_properties_get_int( properties, "stepsize" );
250         conf.algo = mlt_properties_get_int( properties, "algo" );
251         conf.contrastThreshold = mlt_properties_get_double( properties, "mincontrast" );
252         conf.show = mlt_properties_get_int( properties, "show" );
253         conf.virtualTripod = mlt_properties_get_int( properties, "tripod" );
254         vsMotionDetectInit( &analyze_data->md, &conf, &fi );
255
256         return analyze_data;
257 }
258
259 void destory_analyze_data( vs_analyze* analyze_data )
260 {
261         if ( analyze_data )
262         {
263                 vsMotionDetectionCleanup( &analyze_data->md );
264                 free_manylocalmotions( &analyze_data->mlms );
265                 free( analyze_data );
266         }
267 }
268
269 static int transform_image( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height )
270 {
271         int error = 0;
272         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
273         vs_data* data = (vs_data*)filter->child;
274
275         // Handle signal from app to re-init data
276         if ( mlt_properties_get_int(properties, "refresh") )
277         {
278                 mlt_properties_set(properties, "refresh", NULL);
279                 destory_apply_data( data->apply_data );
280                 data->apply_data = NULL;
281         }
282
283         // Init transform data if necessary (first time)
284         if ( !data->apply_data )
285         {
286                 data->apply_data = init_apply_data( filter, frame, vs_format, width, height );
287         }
288
289         // Apply transformations to this image
290         VSTransformData* td = &data->apply_data->td;
291         VSTransformations* trans = &data->apply_data->trans;
292         VSFrame vsFrame;
293         vsFrameFillFromBuffer( &vsFrame, vs_image, vsTransformGetSrcFrameInfo( td ) );
294         trans->current = mlt_filter_get_position( filter, frame );
295         vsTransformPrepare( td, &vsFrame, &vsFrame );
296         VSTransform t = vsGetNextTransform( td, trans );
297         vsDoTransform( td, t );
298         vsTransformFinish( td );
299
300         return error;
301 }
302
303 static void analyze_image( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height )
304 {
305         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
306         vs_data* data = (vs_data*)filter->child;
307         mlt_position pos = mlt_filter_get_position( filter, frame );
308
309         if ( !data->analyze_data )
310         {
311                 data->analyze_data = init_analyze_data( filter, frame, vs_format, width, height );
312         }
313
314         // Initialize the VSFrame to be analyzed.
315         VSMotionDetect* md = &data->analyze_data->md;
316         LocalMotions localmotions;
317         VSFrame vsFrame;
318         vsFrameFillFromBuffer( &vsFrame, vs_image, &md->fi );
319
320         // Detect and save motions.
321         vsMotionDetection( md, &localmotions, &vsFrame );
322         vs_vector_set_dup( &data->analyze_data->mlms, pos, &localmotions, sizeof(LocalMotions) );
323
324         // Publish the motions if this is the last frame.
325         if ( pos + 1 == mlt_filter_get_length2( filter, frame ) )
326         {
327                 publish_manylocalmotions( properties, &data->analyze_data->mlms );
328         }
329 }
330
331 static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
332 {
333         mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame );
334         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
335         uint8_t* vs_image = NULL;
336         VSPixelFormat vs_format = PF_NONE;
337
338         // VS only works on progressive frames
339         mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 );
340
341         *format = validate_format( *format );
342
343         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
344
345         // Convert the received image to a format vid.stab can handle
346         if ( !error )
347         {
348                 vs_format = mltimage_to_vsimage( *format, *width, *height, *image, &vs_image );
349         }
350
351         if( vs_image )
352         {
353                 mlt_service_lock( MLT_FILTER_SERVICE(filter) );
354
355                 if( mlt_properties_get( properties, "results" ) )
356                 {
357                         transform_image( filter, frame, vs_image, vs_format, *width, *height );
358                         vsimage_to_mltimage( vs_image, *image, *format, *width, *height );
359                 }
360                 else
361                 {
362                         analyze_image( filter, frame, vs_image, vs_format, *width, *height );
363                         if( mlt_properties_get_int( properties, "show" ) == 1 )
364                         {
365                                 vsimage_to_mltimage( vs_image, *image, *format, *width, *height );
366                         }
367                 }
368
369                 mlt_service_unlock( MLT_FILTER_SERVICE(filter) );
370
371                 free_vsimage( vs_image, vs_format );
372         }
373
374         return error;
375 }
376
377 static mlt_frame process_filter( mlt_filter filter, mlt_frame frame )
378 {
379         mlt_frame_push_service( frame, filter );
380         mlt_frame_push_get_image( frame, get_image );
381         return frame;
382 }
383
384 static void filter_close( mlt_filter filter )
385 {
386         vs_data* data = (vs_data*)filter->child;
387         if ( data )
388         {
389                 if ( data->analyze_data ) destory_analyze_data( data->analyze_data );
390                 if ( data->apply_data ) destory_apply_data( data->apply_data );
391                 free( data );
392         }
393         filter->close = NULL;
394         filter->child = NULL;
395         filter->parent.close = NULL;
396         mlt_service_close( &filter->parent );
397 }
398
399 extern "C"
400 {
401
402 mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
403 {
404         mlt_filter filter = mlt_filter_new();
405         vs_data* data = (vs_data*)calloc( 1, sizeof(vs_data) );
406
407         if ( filter && data )
408         {
409                 data->analyze_data = NULL;
410                 data->apply_data = NULL;
411
412                 filter->close = filter_close;
413                 filter->child = data;
414                 filter->process = process_filter;
415
416                 mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
417
418                 //properties for analyze
419                 mlt_properties_set(properties, "shakiness", "4");
420                 mlt_properties_set(properties, "accuracy", "4");
421                 mlt_properties_set(properties, "stepsize", "6");
422                 mlt_properties_set(properties, "algo", "1");
423                 mlt_properties_set(properties, "mincontrast", "0.3");
424                 mlt_properties_set(properties, "show", "0");
425                 mlt_properties_set(properties, "tripod", "0");
426
427                 // properties for apply
428                 mlt_properties_set(properties, "smoothing", "15");
429                 mlt_properties_set(properties, "maxshift", "-1");
430                 mlt_properties_set(properties, "maxangle", "-1");
431                 mlt_properties_set(properties, "crop", "0");
432                 mlt_properties_set(properties, "invert", "0");
433                 mlt_properties_set(properties, "relative", "1");
434                 mlt_properties_set(properties, "zoom", "0");
435                 mlt_properties_set(properties, "optzoom", "1");
436                 mlt_properties_set(properties, "zoomspeed", "0.25");
437
438                 mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION);
439         }
440         else
441         {
442                 if( filter )
443                 {
444                         mlt_filter_close( filter );
445                 }
446
447                 if( data )
448                 {
449                         free( data );
450                 }
451
452                 filter = NULL;
453         }
454         return filter;
455 }
456
457 }