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