]> git.sesse.net Git - mlt/blob - src/modules/vid.stab/filter_vidstab.cpp
Updates to vid.stab module.
[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         mlt_position last_position;
39 } vs_analyze;
40
41 typedef struct
42 {
43         VSTransformData td;
44         VSTransformConfig conf;
45         VSTransformations trans;
46 } vs_apply;
47
48 typedef struct
49 {
50         vs_analyze* analyze_data;
51         vs_apply* apply_data;
52 } vs_data;
53
54 /** Free all data used by a VSManyLocalMotions instance.
55  */
56
57 static void free_manylocalmotions( VSManyLocalMotions* mlms )
58 {
59         for( int i = 0; i < vs_vector_size( mlms ); i++ )
60         {
61                 LocalMotions* lms = (LocalMotions*)vs_vector_get( mlms, i );
62
63                 if( lms )
64                 {
65                         vs_vector_del( lms );
66                 }
67         }
68         vs_vector_del( mlms );
69 }
70
71 /** Serialize a VSManyLocalMotions instance and store it in the properties.
72  *
73  * Each LocalMotions instance is converted to a string and stored in an animation.
74  * Then, the entire animation is serialized and stored in the properties.
75  * \param properties the filter properties
76  * \param mlms an initialized VSManyLocalMotions instance
77  */
78
79 static void publish_manylocalmotions( mlt_properties properties, VSManyLocalMotions* mlms )
80 {
81         mlt_animation animation = mlt_animation_new();
82         mlt_animation_item_s item;
83
84         // Initialize animation item.
85         item.is_key = 1;
86         item.keyframe_type = mlt_keyframe_discrete;
87         item.property = mlt_property_init();
88
89         // Convert each LocalMotions instance to a string and add it to the animation.
90         for( int i = 0; i < vs_vector_size( mlms ); i++ )
91         {
92                 LocalMotions* lms = (LocalMotions*)vs_vector_get( mlms, i );
93                 item.frame = i;
94                 int size = vs_vector_size( lms );
95
96                 std::ostringstream oss;
97                 for ( int j = 0; j < size; ++j )
98                 {
99                         LocalMotion* lm = (LocalMotion*)vs_vector_get( lms, j );
100                         oss << lm->v.x << ' ';
101                         oss << lm->v.y << ' ';
102                         oss << lm->f.x << ' ';
103                         oss << lm->f.y << ' ';
104                         oss << lm->f.size << ' ';
105                         oss << lm->contrast << ' ';
106                         oss << lm->match << ' ';
107                 }
108                 mlt_property_set_string( item.property, oss.str().c_str() );
109                 mlt_animation_insert( animation, &item );
110         }
111
112         // Serialize and store the animation.
113         char* motion_str = mlt_animation_serialize( animation );
114         mlt_properties_set( properties, "results", motion_str );
115
116         mlt_property_close( item.property );
117         mlt_animation_close( animation );
118         free( motion_str );
119 }
120
121 /** Get the motions data from the properties and convert it to a VSManyLocalMotions.
122  *
123  * Each LocalMotions instance is converted to a string and stored in an animation.
124  * Then, the entire animation is serialized and stored in the properties.
125  * \param properties the filter properties
126  * \param mlms an initialized (but empty) VSManyLocalMotions instance
127  */
128
129 static void read_manylocalmotions( mlt_properties properties, VSManyLocalMotions* mlms )
130 {
131         mlt_animation_item_s item;
132         item.property = mlt_property_init();
133         mlt_animation animation = mlt_animation_new();
134         // Get the results property which represents the VSManyLocalMotions
135         char* motion_str = mlt_properties_get( properties, "results" );
136
137         mlt_animation_parse( animation, motion_str, 0, 0, NULL );
138
139         int length = mlt_animation_get_length( animation );
140
141         for ( int i = 0; i <= length; ++i )
142         {
143                 LocalMotions lms;
144                 vs_vector_init( &lms, 0 );
145
146                 // Get the animation item that represents the LocalMotions
147                 mlt_animation_get_item( animation, &item, i );
148
149                 // Convert the property to a real LocalMotions
150                 std::istringstream iss( mlt_property_get_string( item.property ) );
151                 while ( iss.good() )
152                 {
153                         LocalMotion lm;
154                         iss >> lm.v.x >> lm.v.y >> lm.f.x >> lm.f.y >> lm.f.size >> lm.contrast >> lm.match;
155                         if ( !iss.fail() )
156                         {
157                                 vs_vector_append_dup( &lms, &lm, sizeof(lm) );
158                         }
159                 }
160
161                 // Add the LocalMotions to the ManyLocalMotions
162                 vs_vector_set_dup( mlms, i, &lms, sizeof(LocalMotions) );
163         }
164
165         mlt_property_close( item.property );
166         mlt_animation_close( animation );
167 }
168
169 static void get_transform_config( VSTransformConfig* conf, mlt_filter filter, mlt_frame frame )
170 {
171         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
172         const char* filterName = mlt_properties_get( properties, "mlt_service" );
173
174         *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 bicubic interpolation is selected
192         const char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" );
193         conf->interpolType = VS_BiCubic;
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         else if ( strcmp( interps, "bilinear" ) == 0 )
199                 conf->interpolType = VS_BiLinear;
200 }
201
202 static int check_apply_config( mlt_filter filter, mlt_frame frame )
203 {
204         vs_apply* apply_data = ((vs_data*)filter->child)->apply_data;
205
206         if( apply_data )
207         {
208                 VSTransformConfig new_conf;
209                 memset( &new_conf, 0, sizeof(VSTransformConfig) );
210                 get_transform_config( &new_conf, filter, frame );
211                 return compare_transform_config( &apply_data->conf, &new_conf );
212         }
213
214         return 0;
215 }
216
217 static void init_apply_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height )
218 {
219         vs_data* data = (vs_data*)filter->child;
220         vs_apply* apply_data = (vs_apply*)calloc( 1, sizeof(vs_apply) );
221         memset( apply_data, 0, sizeof( vs_apply ) );
222
223         // Initialize the VSTransformConfig
224         get_transform_config( &apply_data->conf, filter, frame );
225
226         // Initialize VSTransformData
227         VSFrameInfo fi_src, fi_dst;
228         vsFrameInfoInit( &fi_src, width, height, vs_format );
229         vsFrameInfoInit( &fi_dst, width, height, vs_format );
230         vsTransformDataInit( &apply_data->td, &apply_data->conf, &fi_src, &fi_dst );
231
232         // Initialize VSTransformations
233         vsTransformationsInit( &apply_data->trans );
234
235         // Load the motions from the analyze step and convert them to VSTransformations
236         VSManyLocalMotions mlms;
237         vs_vector_init( &mlms, mlt_filter_get_length2( filter, frame ) );
238         read_manylocalmotions( MLT_FILTER_PROPERTIES( filter ), &mlms );
239         vsLocalmotions2Transforms( &apply_data->td, &mlms, &apply_data->trans );
240         vsPreprocessTransforms( &apply_data->td, &apply_data->trans );
241         free_manylocalmotions( &mlms );
242
243         data->apply_data = apply_data;
244 }
245
246 static void destory_apply_data( vs_apply* apply_data )
247 {
248         if ( apply_data )
249         {
250                 vsTransformDataCleanup( &apply_data->td );
251                 vsTransformationsCleanup( &apply_data->trans );
252                 free( apply_data );
253         }
254 }
255
256 static void init_analyze_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height )
257 {
258         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
259         vs_data* data = (vs_data*)filter->child;
260         vs_analyze* analyze_data = (vs_analyze*)calloc( 1, sizeof(vs_analyze) );
261         memset( analyze_data, 0, sizeof(vs_analyze) );
262
263         // Initialize the saved VSManyLocalMotions vector where motion data will be
264         // stored for each frame.
265         vs_vector_init( &analyze_data->mlms, mlt_filter_get_length2( filter, frame ) );
266
267         // Initialize a VSMotionDetectConfig
268         const char* filterName = mlt_properties_get( properties, "mlt_service" );
269         VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig( filterName );
270         conf.shakiness = mlt_properties_get_int( properties, "shakiness" );
271         conf.accuracy = mlt_properties_get_int( properties, "accuracy" );
272         conf.stepSize = mlt_properties_get_int( properties, "stepsize" );
273         conf.contrastThreshold = mlt_properties_get_double( properties, "mincontrast" );
274         conf.show = mlt_properties_get_int( properties, "show" );
275         conf.virtualTripod = mlt_properties_get_int( properties, "tripod" );
276
277         // Initialize a VSFrameInfo
278         VSFrameInfo fi;
279         vsFrameInfoInit( &fi, width, height, vs_format );
280
281         // Initialize the saved VSMotionDetect
282         vsMotionDetectInit( &analyze_data->md, &conf, &fi );
283
284         data->analyze_data = analyze_data;
285 }
286
287 void destory_analyze_data( vs_analyze* analyze_data )
288 {
289         if ( analyze_data )
290         {
291                 vsMotionDetectionCleanup( &analyze_data->md );
292                 free_manylocalmotions( &analyze_data->mlms );
293                 free( analyze_data );
294         }
295 }
296
297 static int apply_results( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height )
298 {
299         int error = 0;
300         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
301         vs_data* data = (vs_data*)filter->child;
302
303         if ( check_apply_config( filter, frame ) ||
304                 mlt_properties_get_int( properties, "reload" ) )
305         {
306                 mlt_properties_set_int( properties, "reload", 0 );
307                 destory_apply_data( data->apply_data );
308                 data->apply_data = NULL;
309         }
310
311         // Init transform data if necessary (first time)
312         if ( !data->apply_data )
313         {
314                 init_apply_data( filter, frame, vs_format, width, height );
315         }
316
317         // Apply transformations to this image
318         VSTransformData* td = &data->apply_data->td;
319         VSTransformations* trans = &data->apply_data->trans;
320         VSFrame vsFrame;
321         vsFrameFillFromBuffer( &vsFrame, vs_image, vsTransformGetSrcFrameInfo( td ) );
322         trans->current = mlt_filter_get_position( filter, frame );
323         vsTransformPrepare( td, &vsFrame, &vsFrame );
324         VSTransform t = vsGetNextTransform( td, trans );
325         vsDoTransform( td, t );
326         vsTransformFinish( td );
327
328         return error;
329 }
330
331 static void analyze_image( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height )
332 {
333         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
334         vs_data* data = (vs_data*)filter->child;
335         mlt_position pos = mlt_filter_get_position( filter, frame );
336
337         // If any frames are skipped, analysis data will be incomplete.
338         if( data->analyze_data && pos != data->analyze_data->last_position + 1 )
339         {
340                 destory_analyze_data( data->analyze_data );
341                 data->analyze_data = NULL;
342         }
343
344         if ( !data->analyze_data && pos == 0 )
345         {
346                 // Analysis must start on the first frame
347                 init_analyze_data( filter, frame, vs_format, width, height );
348         }
349
350         if( data->analyze_data )
351         {
352                 // Initialize the VSFrame to be analyzed.
353                 VSMotionDetect* md = &data->analyze_data->md;
354                 LocalMotions localmotions;
355                 VSFrame vsFrame;
356                 vsFrameFillFromBuffer( &vsFrame, vs_image, &md->fi );
357
358                 // Detect and save motions.
359                 vsMotionDetection( md, &localmotions, &vsFrame );
360                 vs_vector_set_dup( &data->analyze_data->mlms, pos, &localmotions, sizeof(LocalMotions) );
361
362                 // Publish the motions if this is the last frame.
363                 if ( pos + 1 == mlt_filter_get_length2( filter, frame ) )
364                 {
365                         publish_manylocalmotions( properties, &data->analyze_data->mlms );
366                 }
367
368                 data->analyze_data->last_position = pos;
369         }
370 }
371
372 static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
373 {
374         mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame );
375         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
376         uint8_t* vs_image = NULL;
377         VSPixelFormat vs_format = PF_NONE;
378
379         // VS only works on progressive frames
380         mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 );
381
382         *format = validate_format( *format );
383
384         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
385
386         // Convert the received image to a format vid.stab can handle
387         if ( !error )
388         {
389                 vs_format = mltimage_to_vsimage( *format, *width, *height, *image, &vs_image );
390         }
391
392         if( vs_image )
393         {
394                 mlt_service_lock( MLT_FILTER_SERVICE(filter) );
395
396                 char* results = mlt_properties_get( properties, "results" );
397                 if( results && strcmp( results, "" ) )
398                 {
399                         apply_results( filter, frame, vs_image, vs_format, *width, *height );
400                         vsimage_to_mltimage( vs_image, *image, *format, *width, *height );
401                 }
402                 else
403                 {
404                         analyze_image( filter, frame, vs_image, vs_format, *width, *height );
405                         if( mlt_properties_get_int( properties, "show" ) == 1 )
406                         {
407                                 vsimage_to_mltimage( vs_image, *image, *format, *width, *height );
408                         }
409                 }
410
411                 mlt_service_unlock( MLT_FILTER_SERVICE(filter) );
412
413                 free_vsimage( vs_image, vs_format );
414         }
415
416         return error;
417 }
418
419 static mlt_frame process_filter( mlt_filter filter, mlt_frame frame )
420 {
421         mlt_frame_push_service( frame, filter );
422         mlt_frame_push_get_image( frame, get_image );
423         return frame;
424 }
425
426 static void filter_close( mlt_filter filter )
427 {
428         vs_data* data = (vs_data*)filter->child;
429         if ( data )
430         {
431                 if ( data->analyze_data ) destory_analyze_data( data->analyze_data );
432                 if ( data->apply_data ) destory_apply_data( data->apply_data );
433                 free( data );
434         }
435         filter->close = NULL;
436         filter->child = NULL;
437         filter->parent.close = NULL;
438         mlt_service_close( &filter->parent );
439 }
440
441 extern "C"
442 {
443
444 mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
445 {
446         mlt_filter filter = mlt_filter_new();
447         vs_data* data = (vs_data*)calloc( 1, sizeof(vs_data) );
448
449         if ( filter && data )
450         {
451                 data->analyze_data = NULL;
452                 data->apply_data = NULL;
453
454                 filter->close = filter_close;
455                 filter->child = data;
456                 filter->process = process_filter;
457
458                 mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
459
460                 //properties for analyze
461                 mlt_properties_set(properties, "shakiness", "4");
462                 mlt_properties_set(properties, "accuracy", "4");
463                 mlt_properties_set(properties, "stepsize", "6");
464                 mlt_properties_set(properties, "algo", "1");
465                 mlt_properties_set(properties, "mincontrast", "0.3");
466                 mlt_properties_set(properties, "show", "0");
467                 mlt_properties_set(properties, "tripod", "0");
468
469                 // properties for apply
470                 mlt_properties_set(properties, "smoothing", "15");
471                 mlt_properties_set(properties, "maxshift", "-1");
472                 mlt_properties_set(properties, "maxangle", "-1");
473                 mlt_properties_set(properties, "crop", "0");
474                 mlt_properties_set(properties, "invert", "0");
475                 mlt_properties_set(properties, "relative", "1");
476                 mlt_properties_set(properties, "zoom", "0");
477                 mlt_properties_set(properties, "optzoom", "1");
478                 mlt_properties_set(properties, "zoomspeed", "0.25");
479                 mlt_properties_set( properties, "reload", "0" );
480
481                 mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION);
482         }
483         else
484         {
485                 if( filter )
486                 {
487                         mlt_filter_close( filter );
488                 }
489
490                 if( data )
491                 {
492                         free( data );
493                 }
494
495                 filter = NULL;
496         }
497         return filter;
498 }
499
500 }