]> git.sesse.net Git - mlt/blob - src/modules/motion_est/filter_autotrack_rectangle.c
Version modules and data directories, and melt
[mlt] / src / modules / motion_est / filter_autotrack_rectangle.c
1 /*
2  *      filter_autotrack_rectangle.c
3  *
4  *      /brief 
5  *      /author Zachary Drew, Copyright 2005
6  *
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22
23 #include "filter_motion_est.h"
24 #include "arrow_code.h"
25
26 #include <framework/mlt.h>
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <math.h>
31 #include <string.h>
32
33 #define MIN(a,b) ((a) > (b) ? (b) : (a))
34
35 #define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b))
36 #define ABS(a) ((a) >= 0 ? (a) : (-(a)))
37
38 void caculate_motion( struct motion_vector_s *vectors,
39                       mlt_geometry_item boundry,
40                       int macroblock_width,
41                       int macroblock_height,
42                       int mv_buffer_width,
43                       int method,
44                       int width,
45                       int height )
46 {
47
48
49         // translate pixel units (from bounds) to macroblock units
50         // make sure whole macroblock stay within bounds
51         int left_mb = ( boundry->x + macroblock_width - 1 ) / macroblock_width;
52         int top_mb = ( boundry->y + macroblock_height - 1 ) / macroblock_height;
53         int right_mb = ( boundry->x + boundry->w ) / macroblock_width - 1;
54         int bottom_mb = ( boundry->y + boundry->h ) / macroblock_height - 1;
55
56         int i, j, n = 0;
57
58         int average_x = 0, average_y = 0;
59
60         #define CURRENT         ( vectors + j*mv_buffer_width + i )
61
62         for( i = left_mb; i <= right_mb; i++ ){
63                 for( j = top_mb; j <= bottom_mb; j++ )
64                 {
65                         n++;
66                         average_x += CURRENT->dx;
67                         average_y += CURRENT->dy;
68                 }
69         }
70
71         if ( n == 0 ) return;
72
73         average_x /= n;
74         average_y /= n;
75
76         n = 0;
77         int average2_x = 0, average2_y = 0;
78         for( i = left_mb; i <= right_mb; i++ ){
79                 for( j = top_mb; j <= bottom_mb; j++ ){
80
81                         if( ABS(CURRENT->dx - average_x) < 3 &&
82                             ABS(CURRENT->dy - average_y) < 3 )
83                         {
84                                 n++;
85                                 average2_x += CURRENT->dx;
86                                 average2_y += CURRENT->dy;
87                         }
88                 }
89         }
90
91         if ( n == 0 ) return;
92
93         boundry->x -= (double)average2_x / (double)n;
94         boundry->y -= (double)average2_y / (double)n;
95
96         if ( boundry->x < 0 )
97                 boundry->x = 0;
98
99         if ( boundry->y < 0 )
100                 boundry->y = 0;
101
102         if ( boundry->x + boundry->w > width )
103                 boundry->x = width - boundry->w;
104
105         if ( boundry->y + boundry->h > height )
106                 boundry->y = height - boundry->h;
107 }
108
109 // Image stack(able) method
110 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
111 {
112
113         // Get the filter object
114         mlt_filter filter = mlt_frame_pop_service( frame );
115
116         // Get the filter's property object
117         mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
118
119         // Get the frame properties
120         mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
121
122         // Get the frame position
123         mlt_position position = mlt_filter_get_position( filter, frame );
124
125         // Get the new image
126         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
127
128         if( error != 0 )
129                 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle", stderr );
130
131         mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
132
133         // Get the geometry object
134         mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
135
136         // Get the current geometry item
137         struct mlt_geometry_item_s boundry;
138         mlt_geometry_fetch(geometry, &boundry, position);
139
140         // Get the motion vectors
141         struct motion_vector_s *vectors = mlt_properties_get_data( frame_properties, "motion_est.vectors", NULL );
142
143         // Cleanse the geometry item
144         boundry.w = boundry.x < 0 ? boundry.w + boundry.x : boundry.w;
145         boundry.h = boundry.y < 0 ? boundry.h + boundry.y : boundry.h;
146         boundry.x = boundry.x < 0 ? 0 : boundry.x;
147         boundry.y = boundry.y < 0 ? 0 : boundry.y;
148         boundry.w = boundry.w < 0 ? 0 : boundry.w;
149         boundry.h = boundry.h < 0 ? 0 : boundry.h;
150
151         // How did the rectangle move?
152         if( vectors != NULL &&
153             boundry.key != 1 ) // Paused?
154         {
155
156                 int method = mlt_properties_get_int( filter_properties, "method" );
157
158                 // Get the size of macroblocks in pixel units
159                 int macroblock_height = mlt_properties_get_int( frame_properties, "motion_est.macroblock_height" );
160                 int macroblock_width = mlt_properties_get_int( frame_properties, "motion_est.macroblock_width" );
161                 int mv_buffer_width = *width / macroblock_width;
162
163                 caculate_motion( vectors, &boundry, macroblock_width, macroblock_height, mv_buffer_width, method, *width, *height );
164
165
166                 // Make the geometry object a real boy
167                 boundry.key = 1;
168                 boundry.f[0] = 1;
169                 boundry.f[1] = 1;
170                 boundry.f[2] = 1;
171                 boundry.f[3] = 1;
172                 boundry.f[4] = 1;
173                 mlt_geometry_insert(geometry, &boundry);
174                 mlt_geometry_interpolate(geometry);
175         }
176
177         mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
178
179         if( mlt_properties_get_int( filter_properties, "debug" ) == 1 )
180         {
181                 init_arrows( format, *width, *height );
182                 draw_rectangle_outline(*image, boundry.x, boundry.y, boundry.w, boundry.h, 100);
183         }
184
185         if( mlt_properties_get_int( filter_properties, "_serialize" ) == 1 )
186         {
187                 // Add the vector change to the list
188                 mlt_geometry key_frames = mlt_properties_get_data( filter_properties, "motion_vector_list", NULL );
189                 if ( !key_frames )
190                 {
191                         key_frames = mlt_geometry_init();
192                         mlt_properties_set_data( filter_properties, "motion_vector_list", key_frames, 0,
193                                                  (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise );
194                         if ( key_frames )
195                                 mlt_geometry_set_length( key_frames, mlt_filter_get_length2( filter, frame ) );
196                 }
197                 if ( key_frames )
198                 {
199                         struct mlt_geometry_item_s item;
200                         item.frame = (int) mlt_frame_get_position( frame );
201                         item.key = 1;
202                         item.x = boundry.x;
203                         item.y = boundry.y;
204                         item.w = boundry.w;
205                         item.h = boundry.h;
206                         item.mix = 0;
207                         item.f[0] = item.f[1] = item.f[2] = item.f[3] = 1;
208                         item.f[4] = 0;
209                         mlt_geometry_insert( key_frames, &item );
210                 }
211         }
212         
213         if( mlt_properties_get_int( filter_properties, "obscure" ) == 1 )
214         {
215                 mlt_filter obscure = mlt_properties_get_data( filter_properties, "_obscure", NULL );
216
217                 mlt_properties_pass_list( MLT_FILTER_PROPERTIES(obscure), filter_properties, "in, out");
218
219                 // Because filter_obscure needs to be rewritten to use mlt_geometry
220                 char geom[100];
221                 sprintf( geom, "%d/%d:%dx%d", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
222                 mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "start", geom );
223                 mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "end", geom );
224         }
225                 
226         if( mlt_properties_get_int( filter_properties, "collect" ) == 1 )
227         {
228                 fprintf( stderr, "%d,%d,%d,%d\n", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
229                 fflush( stdout );
230         }
231
232         return error;
233 }
234
235 static int attach_boundry_to_frame( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
236 {
237         // Get the filter object
238         mlt_filter filter = mlt_frame_pop_service( frame );
239
240         // Get the filter's property object
241         mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
242
243         // Get the frame properties
244         mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
245
246         // Get the frame position
247         mlt_position position = mlt_filter_get_position( filter, frame );
248         
249         mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
250
251         // Get the geometry object
252         mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
253         if (geometry == NULL) {
254                 mlt_geometry geom = mlt_geometry_init();
255                 char *arg = mlt_properties_get(filter_properties, "geometry");
256
257                 // Initialize with the supplied geometry
258                 struct mlt_geometry_item_s item;
259                 mlt_geometry_parse_item( geom, &item, arg  );
260
261                 item.frame = 0;
262                 item.key = 1;
263                 item.mix = 100;
264
265                 mlt_geometry_insert( geom, &item );
266                 mlt_geometry_interpolate( geom );
267                 mlt_properties_set_data( filter_properties, "filter_geometry", geom, 0, (mlt_destructor)mlt_geometry_close, (mlt_serialiser)mlt_geometry_serialise );
268                 geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
269         }
270
271         mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
272
273         // Get the current geometry item
274         mlt_geometry_item geometry_item = mlt_pool_alloc( sizeof( struct mlt_geometry_item_s ) );
275         mlt_geometry_fetch(geometry, geometry_item, position);
276
277         // Cleanse the geometry item
278         geometry_item->w = geometry_item->x < 0 ? geometry_item->w + geometry_item->x : geometry_item->w;
279         geometry_item->h = geometry_item->y < 0 ? geometry_item->h + geometry_item->y : geometry_item->h;
280         geometry_item->x = geometry_item->x < 0 ? 0 : geometry_item->x;
281         geometry_item->y = geometry_item->y < 0 ? 0 : geometry_item->y;
282         geometry_item->w = geometry_item->w < 0 ? 0 : geometry_item->w;
283         geometry_item->h = geometry_item->h < 0 ? 0 : geometry_item->h;
284
285         mlt_properties_set_data( frame_properties, "bounds", geometry_item, sizeof( struct mlt_geometry_item_s ), mlt_pool_release, NULL );
286
287         // Get the new image
288         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
289
290         if( error != 0 )
291                 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle attach_boundry_to_frame", stderr );
292
293         return error;
294 }
295
296 /** Filter processing.
297 */
298
299 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
300 {
301
302         /* modify the frame with the current geometry */
303         mlt_frame_push_service( frame, this);
304         mlt_frame_push_get_image( frame, attach_boundry_to_frame );
305         mlt_properties properties = MLT_FILTER_PROPERTIES( this );
306
307         /* apply the motion estimation filter */
308         mlt_filter motion_est = mlt_properties_get_data( properties, "_motion_est", NULL ); 
309         /* Pass motion_est properties */
310         mlt_properties_pass( MLT_FILTER_PROPERTIES( motion_est ), properties, "motion_est." );
311         mlt_filter_process( motion_est, frame);
312
313         /* calculate the new geometry based on the motion */
314         mlt_frame_push_service( frame, this);
315         mlt_frame_push_get_image( frame, filter_get_image );
316
317
318         /* visualize the motion vectors */
319         if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "debug" ) == 1 )
320         {
321                 mlt_filter vismv = mlt_properties_get_data( properties, "_vismv", NULL );
322                 if( vismv == NULL )
323                 {
324                         mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
325                         vismv = mlt_factory_filter( profile, "vismv", NULL );
326                         mlt_properties_set_data( properties, "_vismv", vismv, 0, (mlt_destructor)mlt_filter_close, NULL );
327                 }
328
329                 mlt_filter_process( vismv, frame );
330         }
331
332         if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "obscure" ) == 1 )
333         {
334                 mlt_filter obscure = mlt_properties_get_data( properties, "_obscure", NULL );
335                 if( obscure == NULL )
336                 {
337                         mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
338                         obscure = mlt_factory_filter( profile, "obscure", NULL );
339                         mlt_properties_set_data( properties, "_obscure", obscure, 0, (mlt_destructor)mlt_filter_close, NULL );
340                 }
341
342                 mlt_filter_process( obscure, frame );
343         }
344
345         return frame;
346 }
347
348 /** Constructor for the filter.
349 */
350
351
352 mlt_filter filter_autotrack_rectangle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
353 {
354         mlt_filter this = mlt_filter_new( );
355         if ( this != NULL )
356         {
357                 this->process = filter_process;
358
359                 // Initialize with the supplied geometry if ther is one
360                 if( arg != NULL ) 
361                         mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", arg );
362                 else
363                         mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", "100/100:100x100" );
364
365                 // create an instance of the motion_est and obscure filter
366                 mlt_filter motion_est = mlt_factory_filter( profile, "motion_est", NULL );
367                 if( motion_est != NULL )
368                         mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_motion_est", motion_est, 0, (mlt_destructor)mlt_filter_close, NULL );
369                 else {
370                         mlt_filter_close( this );
371                         return NULL;
372                 }
373
374
375         }
376
377         return this;
378 }
379
380 /** This source code will self destruct in 5...4...3...
381 */