]> git.sesse.net Git - mlt/blob - src/modules/videostab/filter_videostab2.c
f78d38e39bad22c229b5253c545f9367ae785e67
[mlt] / src / modules / videostab / filter_videostab2.c
1 /*
2  * filter_imagestab.c -- video stabilization with code from http://vstab.sourceforge.net/
3  * Copyright (c) 2011 Marco Gittler <g.marco@freenet.de>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <framework/mlt_filter.h>
21 #include <framework/mlt_frame.h>
22 #include <framework/mlt_log.h>
23 #include <framework/mlt_producer.h>
24 #include <framework/mlt_geometry.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <math.h>
29 #include <sys/stat.h>
30 #include <string.h>
31
32 #include "stabilize.h"
33 #include "transform_image.h"
34
35 typedef struct {
36         StabData* stab;
37         TransformData* trans;
38         int initialized;
39         void* parent;
40 } videostab2_data;
41
42 static void serialize_vectors( videostab2_data* self, mlt_position length )
43 {
44         mlt_geometry g = mlt_geometry_init();
45         if ( g )
46         {
47                 struct mlt_geometry_item_s item;
48                 mlt_position i;
49
50                 // Initialize geometry item
51                 item.key = item.f[0] = item.f[1] = item.f[2] = item.f[3] = 1;
52                 item.f[4] = 0;
53
54                 tlist* transform_data =self->stab->transs;
55                 for ( i = 0; i < length; i++ )
56                 {
57                         // Set the geometry item
58                         item.frame = i;
59                         if (transform_data){
60                                 if ( transform_data->data){
61                                         Transform* t=transform_data->data;
62                                         item.x=t->x;
63                                         item.y=t->y;
64                                         item.w=t->alpha;
65                                         item.h=t->zoom;
66                                         transform_data=transform_data->next;
67                                 }
68                         }
69                         // Add the geometry item
70                         mlt_geometry_insert( g, &item );
71                 }
72
73                 // Put the analysis results in a property
74                 mlt_geometry_set_length( g, length );
75                 mlt_properties_set_data( MLT_FILTER_PROPERTIES( (mlt_filter) self->parent ), "vectors", g, 0,
76                         (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise );
77         }
78 }
79 // scale zoom implements the factor that the vetcors must be scaled since the vector is calulated for real with, now we need it for (scaled)width
80 Transform* deserialize_vectors( char *vectors, mlt_position length ,float scale_zoom )
81 {
82         mlt_geometry g = mlt_geometry_init();
83         Transform* tx=NULL;
84         // Parse the property as a geometry
85         if ( g && !mlt_geometry_parse( g, vectors, length, -1, -1 ) )
86         {
87                 struct mlt_geometry_item_s item;
88                 int i;
89                 tx=calloc(1,sizeof(Transform)*length);
90                 // Copy the geometry items to a vc array for interp()
91                 for ( i = 0; i < length; i++ )
92                 {
93                         mlt_geometry_fetch( g, &item, i );
94                         Transform t;
95                         t.x=scale_zoom*item.x;
96                         t.y=scale_zoom*item.y;
97                         t.alpha=item.w;
98                         t.zoom=scale_zoom*item.h;
99                         t.extra=0;
100                         tx[i]=t;
101                 }
102
103         }
104         else
105         {
106                 //mlt_log_warning( NULL, "failed to parse vectors\n" );
107         }
108
109         // We are done with this mlt_geometry
110         if ( g ) mlt_geometry_close( g );
111         return tx;
112 }
113
114 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
115 {
116         mlt_filter filter = mlt_frame_pop_service( frame );
117         char *vectors = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "vectors" );
118         *format = mlt_image_yuv422;
119         if (vectors)
120                 *format= mlt_image_rgb24;
121         mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "consumer_deinterlace", 1 );
122         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
123
124         if ( !error && *image )
125         {
126                 videostab2_data* data = filter->child;
127                 if ( data==NULL ) { // big error, abort
128                         return 1;
129                 }
130                 mlt_position length = mlt_filter_get_length2( filter, frame );
131                 int h = *height;
132                 int w = *width;
133
134                 // Service locks are for concurrency control
135                 mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
136
137                 if ( !vectors) {
138                         if ( !data->initialized )
139                         {
140                                 // Initialize our context
141                                 data->initialized = 1;
142                                 data->stab->width=w;
143                                 data->stab->height=h;
144                                 if (*format==mlt_image_yuv420p) data->stab->framesize=w*h* 3/2;//( mlt_image_format_size ( *format, w,h , 0) ; // 3/2 =1 too small
145                                 if (*format==mlt_image_yuv422) data->stab->framesize=w*h;
146                                 data->stab->shakiness = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "shakiness" );
147                                 data->stab->accuracy = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "accuracy" );
148                                 data->stab->stepsize = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "stepsize" );
149                                 data->stab->algo = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "algo" );
150                                 data->stab->show = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "show" );
151                                 data->stab->contrast_threshold = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter) , "mincontrast" );
152                                 stabilize_configure(data->stab);
153                         }
154                                 // Analyse
155                                 mlt_position pos = mlt_filter_get_position( filter, frame );
156                                 stabilize_filter_video ( data->stab , *image, *format );
157
158                                 // On last frame
159                                 if ( pos == length - 1 )
160                                 {
161                                         serialize_vectors( data , length );
162                                 }
163                 }
164                 else
165                 {
166                         if ( data->initialized!=1  )
167                         {
168                                 char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" );
169
170                                 if ( data->initialized != 2 )
171                                 {
172                                         // Load analysis results from property
173                                         data->initialized = 2;
174
175                                         int interp = 2;
176                                         float scale_zoom=1.0;
177                                         if ( *width != mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.media.width" ) )
178                                                 scale_zoom = (float) *width / (float) mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.media.width" );
179                                         if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 )
180                                                 interp = 0;
181                                         else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
182                                                 interp = 1;
183                                         else if ( strcmp( interps, "bilinear" ) == 0 )
184                                                 interp = 2;
185                                         else if ( strcmp( interps, "bicubic" ) == 0 )
186                                                 interp = 3;
187                                         else if ( strcmp( interps, "bicublin" ) == 0 )
188                                                 interp = 4;
189
190                                         data->trans->interpoltype = interp;
191                                         data->trans->smoothing = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "smoothing" );
192                                         data->trans->maxshift = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "maxshift" );
193                                         data->trans->maxangle = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter), "maxangle" );
194                                         data->trans->crop = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "crop" );
195                                         data->trans->invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "invert" );
196                                         data->trans->relative = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "relative" );
197                                         data->trans->zoom = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "zoom" );
198                                         data->trans->optzoom = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "optzoom" );
199                                         data->trans->sharpen = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter), "sharpen" );
200
201                                         transform_configure(data->trans,w,h,*format ,*image, deserialize_vectors(  vectors, length , scale_zoom ),length);
202
203                                 }
204                                 if ( data->initialized == 2 )
205                                 {
206                                         // Stabilize
207                                         float pos = mlt_filter_get_position( filter, frame );
208                                         data->trans->current_trans=pos;
209                                         transform_filter_video(data->trans, *image, *format );
210
211                                 }
212                 }
213                 }
214                 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
215         }
216         return error;
217 }
218
219 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
220 {
221         mlt_frame_push_service( frame, filter );
222         mlt_frame_push_get_image( frame, filter_get_image );
223         return frame;
224 }
225
226 static void filter_close( mlt_filter parent )
227 {
228         videostab2_data* data = parent->child;
229         if (data){
230                 if (data->stab) stabilize_stop(data->stab);
231                 if (data->trans){
232                         if (data->trans->src) free(data->trans->src);
233                         free (data->trans);
234                 }
235                 free( data );
236         }
237         parent->close = NULL;
238         parent->child = NULL;
239 }
240
241 mlt_filter filter_videostab2_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
242 {
243         videostab2_data* data= calloc( 1, sizeof(videostab2_data));
244         if ( data )
245         {
246                 data->stab = calloc( 1, sizeof(StabData) );
247                 if ( !data->stab )
248                 {
249                         free( data );
250                         return NULL;
251                 }
252
253                 data->trans = calloc( 1, sizeof (TransformData) ) ;
254                 if ( !data->trans )
255                 {
256                         free( data->stab );
257                         free( data );
258                         return NULL;
259                 }
260
261                 mlt_filter parent = mlt_filter_new();
262                 if ( !parent )
263                 {
264                         free( data->trans );
265                         free( data->stab );
266                         free( data );
267                         return NULL;
268                 }
269
270                 parent->child = data;
271                 parent->close = filter_close;
272                 parent->process = filter_process;
273                 data->parent = parent;
274                 //properties for stabilize
275                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "shakiness", "4" );
276                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "accuracy", "4" );
277                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "stepsize", "6" );
278                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "algo", "1" );
279                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "mincontrast", "0.3" );
280                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "show", "0" );
281
282                 //properties for transform
283                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "smoothing", "10" );
284                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "maxshift", "-1" );
285                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "maxangle", "-1" );
286                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "crop", "0" );
287                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "invert", "0" );
288                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "relative", "1" );
289                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "zoom", "0" );
290                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "optzoom", "1" );
291                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "sharpen", "0.8" );
292                 return parent;
293         }
294         return NULL;
295 }