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