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>
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.
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.
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.
24 #include <vid.stab/libvidstab.h>
25 #include <framework/mlt.h>
26 #include <framework/mlt_animation.h>
37 VSManyLocalMotions mlms;
38 mlt_position last_position;
44 VSTransformConfig conf;
45 VSTransformations trans;
50 vs_analyze* analyze_data;
54 /** Free all data used by a VSManyLocalMotions instance.
57 static void free_manylocalmotions( VSManyLocalMotions* mlms )
59 for( int i = 0; i < vs_vector_size( mlms ); i++ )
61 LocalMotions* lms = (LocalMotions*)vs_vector_get( mlms, i );
68 vs_vector_del( mlms );
71 /** Serialize a VSManyLocalMotions instance and store it in the properties.
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
79 static void publish_manylocalmotions( mlt_properties properties, VSManyLocalMotions* mlms )
81 mlt_animation animation = mlt_animation_new();
82 mlt_animation_item_s item;
84 // Initialize animation item.
86 item.keyframe_type = mlt_keyframe_discrete;
87 item.property = mlt_property_init();
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++ )
92 LocalMotions* lms = (LocalMotions*)vs_vector_get( mlms, i );
94 int size = vs_vector_size( lms );
96 std::ostringstream oss;
97 for ( int j = 0; j < size; ++j )
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 << ' ';
108 mlt_property_set_string( item.property, oss.str().c_str() );
109 mlt_animation_insert( animation, &item );
112 // Serialize and store the animation.
113 char* motion_str = mlt_animation_serialize( animation );
114 mlt_properties_set( properties, "results", motion_str );
116 mlt_property_close( item.property );
117 mlt_animation_close( animation );
121 /** Get the motions data from the properties and convert it to a VSManyLocalMotions.
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
129 static void read_manylocalmotions( mlt_properties properties, VSManyLocalMotions* mlms )
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" );
137 mlt_animation_parse( animation, motion_str, 0, 0, NULL );
139 int length = mlt_animation_get_length( animation );
141 for ( int i = 0; i <= length; ++i )
144 vs_vector_init( &lms, 0 );
146 // Get the animation item that represents the LocalMotions
147 mlt_animation_get_item( animation, &item, i );
149 // Convert the property to a real LocalMotions
150 std::istringstream iss( mlt_property_get_string( item.property ) );
154 iss >> lm.v.x >> lm.v.y >> lm.f.x >> lm.f.y >> lm.f.size >> lm.contrast >> lm.match;
157 vs_vector_append_dup( &lms, &lm, sizeof(lm) );
161 // Add the LocalMotions to the ManyLocalMotions
162 vs_vector_set_dup( mlms, i, &lms, sizeof(LocalMotions) );
165 mlt_property_close( item.property );
166 mlt_animation_close( animation );
169 static void get_transform_config( VSTransformConfig* conf, mlt_filter filter, mlt_frame frame )
171 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
172 const char* filterName = mlt_properties_get( properties, "mlt_service" );
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 )
186 // Virtual tripod mode: relative=False, smoothing=0
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;
202 static int check_apply_config( mlt_filter filter, mlt_frame frame )
204 vs_apply* apply_data = ((vs_data*)filter->child)->apply_data;
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 );
217 static void init_apply_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height )
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 ) );
223 // Initialize the VSTransformConfig
224 get_transform_config( &apply_data->conf, filter, frame );
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 );
232 // Initialize VSTransformations
233 vsTransformationsInit( &apply_data->trans );
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 );
243 data->apply_data = apply_data;
246 static void destory_apply_data( vs_apply* apply_data )
250 vsTransformDataCleanup( &apply_data->td );
251 vsTransformationsCleanup( &apply_data->trans );
256 static void init_analyze_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height )
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) );
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 ) );
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" );
277 // Initialize a VSFrameInfo
279 vsFrameInfoInit( &fi, width, height, vs_format );
281 // Initialize the saved VSMotionDetect
282 vsMotionDetectInit( &analyze_data->md, &conf, &fi );
284 data->analyze_data = analyze_data;
287 void destory_analyze_data( vs_analyze* analyze_data )
291 vsMotionDetectionCleanup( &analyze_data->md );
292 free_manylocalmotions( &analyze_data->mlms );
293 free( analyze_data );
297 static int apply_results( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height )
300 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
301 vs_data* data = (vs_data*)filter->child;
303 if ( check_apply_config( filter, frame ) ||
304 mlt_properties_get_int( properties, "reload" ) )
306 mlt_properties_set_int( properties, "reload", 0 );
307 destory_apply_data( data->apply_data );
308 data->apply_data = NULL;
311 // Init transform data if necessary (first time)
312 if ( !data->apply_data )
314 init_apply_data( filter, frame, vs_format, width, height );
317 // Apply transformations to this image
318 VSTransformData* td = &data->apply_data->td;
319 VSTransformations* trans = &data->apply_data->trans;
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 );
331 static void analyze_image( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height )
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 );
337 // If any frames are skipped, analysis data will be incomplete.
338 if( data->analyze_data && pos != data->analyze_data->last_position + 1 )
340 destory_analyze_data( data->analyze_data );
341 data->analyze_data = NULL;
344 if ( !data->analyze_data && pos == 0 )
346 // Analysis must start on the first frame
347 init_analyze_data( filter, frame, vs_format, width, height );
350 if( data->analyze_data )
352 // Initialize the VSFrame to be analyzed.
353 VSMotionDetect* md = &data->analyze_data->md;
354 LocalMotions localmotions;
356 vsFrameFillFromBuffer( &vsFrame, vs_image, &md->fi );
358 // Detect and save motions.
359 vsMotionDetection( md, &localmotions, &vsFrame );
360 vs_vector_set_dup( &data->analyze_data->mlms, pos, &localmotions, sizeof(LocalMotions) );
362 // Publish the motions if this is the last frame.
363 if ( pos + 1 == mlt_filter_get_length2( filter, frame ) )
365 publish_manylocalmotions( properties, &data->analyze_data->mlms );
368 data->analyze_data->last_position = pos;
372 static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
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;
379 // VS only works on progressive frames
380 mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 );
382 *format = validate_format( *format );
384 int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
386 // Convert the received image to a format vid.stab can handle
389 vs_format = mltimage_to_vsimage( *format, *width, *height, *image, &vs_image );
394 mlt_service_lock( MLT_FILTER_SERVICE(filter) );
396 char* results = mlt_properties_get( properties, "results" );
397 if( results && strcmp( results, "" ) )
399 apply_results( filter, frame, vs_image, vs_format, *width, *height );
400 vsimage_to_mltimage( vs_image, *image, *format, *width, *height );
404 analyze_image( filter, frame, vs_image, vs_format, *width, *height );
405 if( mlt_properties_get_int( properties, "show" ) == 1 )
407 vsimage_to_mltimage( vs_image, *image, *format, *width, *height );
411 mlt_service_unlock( MLT_FILTER_SERVICE(filter) );
413 free_vsimage( vs_image, vs_format );
419 static mlt_frame process_filter( mlt_filter filter, mlt_frame frame )
421 mlt_frame_push_service( frame, filter );
422 mlt_frame_push_get_image( frame, get_image );
426 static void filter_close( mlt_filter filter )
428 vs_data* data = (vs_data*)filter->child;
431 if ( data->analyze_data ) destory_analyze_data( data->analyze_data );
432 if ( data->apply_data ) destory_apply_data( data->apply_data );
435 filter->close = NULL;
436 filter->child = NULL;
437 filter->parent.close = NULL;
438 mlt_service_close( &filter->parent );
444 mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
446 mlt_filter filter = mlt_filter_new();
447 vs_data* data = (vs_data*)calloc( 1, sizeof(vs_data) );
449 if ( filter && data )
451 data->analyze_data = NULL;
452 data->apply_data = NULL;
454 filter->close = filter_close;
455 filter->child = data;
456 filter->process = process_filter;
458 mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
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");
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" );
481 mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION);
487 mlt_filter_close( filter );