]> git.sesse.net Git - mlt/blob - src/modules/videostab/filter_videostab.c
f6f860007060bdad51504835eb7c9aa5a20d278d
[mlt] / src / modules / videostab / filter_videostab.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 "stab/vector.h"
33 #include "stab/utils.h"
34 #include "stab/estimate.h"
35 #include "stab/resample.h"
36
37
38 typedef struct {
39         mlt_filter parent;
40         int initialized;
41         int* lanc_kernels;
42         es_ctx *es;
43         vc *pos_i;
44         vc *pos_h;
45         vc *pos_y;
46         rs_ctx *rs;
47 } *videostab;
48
49 static void serialize_vectors( videostab self, mlt_position length )
50 {
51         mlt_geometry g = mlt_geometry_init();
52
53         if ( g )
54         {
55                 struct mlt_geometry_item_s item;
56                 mlt_position i;
57
58                 // Initialize geometry item
59                 item.key = item.f[0] = item.f[1] = 1;
60                 item.f[2] = item.f[3] = item.f[4] = 0;
61
62                 for ( i = 0; i < length; i++ )
63                 {
64                         // Set the geometry item
65                         item.frame = i;
66                         item.x = self->pos_h[i].x;
67                         item.y = self->pos_h[i].y;
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( self->parent ), "vectors", g, 0,
76                         (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise );
77         }
78 }
79
80 static void deserialize_vectors( videostab self, char *vectors, mlt_position length )
81 {
82         mlt_geometry g = mlt_geometry_init();
83
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
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                         self->pos_h[i].x = item.x;
95                         self->pos_h[i].y = item.y;
96                 }
97         }
98         else
99         {
100                 mlt_log_warning( MLT_FILTER_SERVICE(self->parent), "failed to parse vectors\n" );
101         }
102
103         // We are done with this mlt_geometry
104         if ( g ) mlt_geometry_close( g );
105 }
106
107 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
108 {
109         mlt_filter filter = mlt_frame_pop_service( frame );
110         *format = mlt_image_rgb24;
111         mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "consumer_deinterlace", 1 );
112         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
113
114         if ( !error && *image )
115         {
116                 videostab self = filter->child;
117                 mlt_position length = mlt_filter_get_length2( filter, frame );
118                 int h = *height;
119                 int w = *width;
120
121                 // Service locks are for concurrency control
122                 mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
123                 if ( !self->initialized )
124                 {
125                         // Initialize our context
126                         self->initialized = 1;
127                         self->es = es_init( w, h );
128                         self->pos_i = (vc*) malloc( length * sizeof(vc) );
129                         self->pos_h = (vc*) malloc( length * sizeof(vc) );
130                         self->pos_y = (vc*) malloc( h * sizeof(vc) );
131                         self->rs = rs_init( w, h );
132                 }
133                 char *vectors = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "vectors" );
134                 if ( !vectors )
135                 {
136                         // Analyse
137                         mlt_position pos = mlt_filter_get_position( filter, frame );
138                         self->pos_i[pos] = vc_add( pos == 0 ? vc_zero() : self->pos_i[pos - 1], es_estimate( self->es, *image ) );
139
140                         // On last frame
141                         if ( pos == length - 1 )
142                         {
143                                 mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) );
144                                 double fps =  mlt_profile_fps( profile );
145
146                                 // Filter and store the results
147                                 hipass( self->pos_i, self->pos_h, length, fps );
148                                 serialize_vectors( self, length );
149                         }
150                 } else {
151                         // Apply
152                         if ( self->initialized != 2 )
153                         {
154                                 // Load analysis results from property
155                                 self->initialized = 2;
156                                 deserialize_vectors( self, vectors, length );
157                         }
158                         if ( self->initialized == 2 )
159                         {
160                                 // Stabilize
161                                 float shutter_angle = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame) , "shutterangle" );
162                                 float pos = mlt_filter_get_position( filter, frame );
163                                 int i;
164
165                                 for (i = 0; i < h; i ++)
166                                         self->pos_y[i] = interp( self->lanc_kernels,self->pos_h, length, pos + (i - h / 2.0) * shutter_angle / (h * 360.0) );
167                                 rs_resample( self->lanc_kernels,self->rs, *image, self->pos_y );
168                         }
169                 }
170                 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
171         }
172         return error;
173 }
174
175 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
176 {
177         mlt_frame_push_service( frame, filter );
178         mlt_frame_push_get_image( frame, filter_get_image );
179         return frame;
180 }
181
182 void filter_close( mlt_filter parent )
183 {
184         videostab self = parent->child;
185         if ( self->es ) es_free( self->es );
186         if ( self->pos_i ) free( self->pos_i );
187         if ( self->pos_h ) free( self->pos_h );
188         if ( self->pos_y ) free( self->pos_y );
189         if ( self->rs ) rs_free( self->rs );
190         if ( self->lanc_kernels) free_lanc_kernels(self->lanc_kernels);
191         free( self );
192         parent->close = NULL;
193         parent->child = NULL;
194 }
195
196 mlt_filter filter_videostab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
197 {
198         videostab self = calloc( 1, sizeof(*self) );
199         if ( self )
200         {
201                 mlt_filter parent = mlt_filter_new();
202                 if ( !parent )
203                 {
204                         free( self );
205                         return NULL;
206                 }
207                 parent->child = self;
208                 parent->close = filter_close;
209                 parent->process = filter_process;
210                 self->parent = parent;
211                 mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "shutterangle", "0" ); // 0 - 180 , default 0
212                 self->lanc_kernels=prepare_lanc_kernels();
213                 return parent;
214         }
215         return NULL;
216 }