]> git.sesse.net Git - mlt/blob - src/modules/motion_est/filter_autotrack_rectangle.c
This is a significant rewrite.
[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 {
45
46
47         // translate pixel units (from bounds) to macroblock units
48         // make sure whole macroblock stay within bounds
49         int left_mb = ( boundry->x + macroblock_width - 1 ) / macroblock_width;
50         int top_mb = ( boundry->y + macroblock_height - 1 ) / macroblock_height;
51         int right_mb = ( boundry->x + boundry->w ) / macroblock_width - 1;
52         int bottom_mb = ( boundry->y + boundry->h ) / macroblock_height - 1;
53
54         int i, j, n = 0;
55
56         int average_x = 0, average_y = 0;
57
58         #define CURRENT         ( vectors + j*mv_buffer_width + i )
59
60         for( i = left_mb; i <= right_mb; i++ ){
61                 for( j = top_mb; j <= bottom_mb; j++ )
62                 {
63                         n++;
64                         average_x += CURRENT->dx;
65                         average_y += CURRENT->dy;
66                 }
67         }
68
69         if ( n == 0 ) return;
70
71         average_x /= n;
72         average_y /= n;
73
74         n = 0;
75         int average2_x = 0, average2_y = 0;
76         for( i = left_mb; i <= right_mb; i++ ){
77                 for( j = top_mb; j <= bottom_mb; j++ ){
78
79                         if( ABS(CURRENT->dx - average_x) < 3 &&
80                             ABS(CURRENT->dy - average_y) < 3 )
81                         {
82                                 n++;
83                                 average2_x += CURRENT->dx;
84                                 average2_y += CURRENT->dy;
85                         }
86                 }
87         }
88
89         if ( n == 0 ) return;
90
91         boundry->x -= (double)average2_x / (double)n;
92         boundry->y -= (double)average2_y / (double)n;
93 }
94
95 // Image stack(able) method
96 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
97 {
98
99         // Get the filter object
100         mlt_filter filter = mlt_frame_pop_service( frame );
101
102         // Get the filter's property object
103         mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
104
105         // Get the frame properties
106         mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
107
108         // Get the frame position
109         mlt_position position = mlt_frame_get_position( frame );
110
111         // Get the new image
112         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
113
114         if( error != 0 )
115                 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle", stderr );
116
117         // Get the geometry object
118         mlt_geometry geometry = mlt_properties_get_data(filter_properties, "geometry", NULL);
119
120         // Get the current geometry item
121         struct mlt_geometry_item_s boundry;
122         mlt_geometry_fetch(geometry, &boundry, position);
123
124         // Get the motion vectors
125         struct motion_vector_s *vectors = mlt_properties_get_data( frame_properties, "motion_est.vectors", NULL );
126
127         // How did the rectangle move?
128         if( vectors != NULL &&
129             boundry.key != 1 ) // Paused?
130         {
131
132                 int method = mlt_properties_get_int( filter_properties, "method" );
133
134                 // Get the size of macroblocks in pixel units
135                 int macroblock_height = mlt_properties_get_int( frame_properties, "motion_est.macroblock_height" );
136                 int macroblock_width = mlt_properties_get_int( frame_properties, "motion_est.macroblock_width" );
137                 int mv_buffer_width = *width / macroblock_width;
138
139                 caculate_motion( vectors, &boundry, macroblock_width, macroblock_height, mv_buffer_width, method );
140
141
142                 // Make the geometry object a real boy
143                 boundry.key = 1;
144                 boundry.f[0] = 1;
145                 boundry.f[1] = 1;
146                 boundry.f[2] = 1;
147                 boundry.f[3] = 1;
148                 boundry.f[4] = 1;
149                 mlt_geometry_insert(geometry, &boundry);
150         }
151
152         if( mlt_properties_get_int( filter_properties, "debug" ) == 1 )
153         {
154                 init_arrows( format, *width, *height );
155                 draw_rectangle_outline(*image, boundry.x, boundry.y, boundry.w, boundry.h, 100);
156         }        
157
158         return error;
159 }
160
161 static int attach_boundry_to_frame( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
162 {
163         // Get the filter object
164         mlt_filter filter = mlt_frame_pop_service( frame );
165
166         // Get the filter's property object
167         mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
168
169         // Get the frame properties
170         mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
171
172         // Get the frame position
173         mlt_position position = mlt_frame_get_position( frame );
174
175         // Get the geometry object
176         mlt_geometry geometry = mlt_properties_get_data(filter_properties, "geometry", NULL);
177
178         // Get the current geometry item
179         mlt_geometry_item geometry_item = mlt_pool_alloc( sizeof( struct mlt_geometry_item_s ) );
180         mlt_geometry_fetch(geometry, geometry_item, position);
181
182         mlt_properties_set_data( frame_properties, "bounds", geometry_item, sizeof( struct mlt_geometry_item_s ), mlt_pool_release, NULL );
183
184         // Get the new image
185         int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
186
187         if( error != 0 )
188                 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle attach_boundry_to_frame", stderr );
189
190         return error;
191 }
192
193 /** Filter processing.
194 */
195
196 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
197 {
198
199         /* modify the frame with the current geometry */
200         mlt_frame_push_service( frame, this);
201         mlt_frame_push_get_image( frame, attach_boundry_to_frame );
202
203
204
205         /* apply the motion estimation filter */
206         mlt_filter motion_est = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_motion_est", NULL ); 
207         mlt_filter_process( motion_est, frame);
208
209
210
211         /* calculate the new geometry based on the motion */
212         mlt_frame_push_service( frame, this);
213         mlt_frame_push_get_image( frame, filter_get_image );
214
215
216         /* visualize the motion vectors */
217         if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "debug" ) == 1 )
218         {
219                 mlt_filter vismv = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_vismv", NULL );
220                 if( vismv == NULL ) {
221                         vismv = mlt_factory_filter( "vismv", NULL );
222                         mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_vismv", vismv, 0, (mlt_destructor)mlt_filter_close, NULL );
223                 }
224
225                 mlt_filter_process( vismv, frame );
226         }
227
228
229         return frame;
230 }
231
232 /** Constructor for the filter.
233 */
234
235
236 mlt_filter filter_autotrack_rectangle_init( char *arg )
237 {
238         mlt_filter this = mlt_filter_new( );
239         if ( this != NULL )
240         {
241                 this->process = filter_process;
242
243
244                 mlt_geometry geometry = mlt_geometry_init();
245
246                 // Initialize with the supplied geometry
247                 if( arg != NULL ) {
248
249                         struct mlt_geometry_item_s item;
250
251                         mlt_geometry_parse_item( geometry, &item, arg  );
252
253                         item.frame = 0;
254                         item.key = 1;
255                         item.mix = 100;
256
257                         mlt_geometry_insert( geometry, &item );
258
259                 }
260
261                 // ... and attach it to the frame
262                 mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "geometry", geometry, 0, (mlt_destructor)mlt_geometry_close, (mlt_serialiser)mlt_geometry_serialise );
263
264                 // create an instance of the motion_est filter
265                 mlt_filter motion_est = mlt_factory_filter("motion_est", NULL);
266                 if( motion_est != NULL )
267                         mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_motion_est", motion_est, 0, (mlt_destructor)mlt_filter_close, NULL );
268                 else {
269                         mlt_filter_close( this );
270                         return NULL;
271                 }
272
273
274         }
275
276         return this;
277 }
278
279 /** This source code will self destruct in 5...4...3...
280 */