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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b))
34 #define ABS(a) ((a) >= 0 ? (a) : (-(a)))
35
caculate_motion(struct motion_vector_s * vectors,mlt_geometry_item boundry,int macroblock_width,int macroblock_height,int mv_buffer_width,int method,int width,int height)36 void caculate_motion( struct motion_vector_s *vectors,
37 mlt_geometry_item boundry,
38 int macroblock_width,
39 int macroblock_height,
40 int mv_buffer_width,
41 int method,
42 int width,
43 int height )
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 if ( boundry->x < 0 )
95 boundry->x = 0;
96
97 if ( boundry->y < 0 )
98 boundry->y = 0;
99
100 if ( boundry->x + boundry->w > width )
101 boundry->x = width - boundry->w;
102
103 if ( boundry->y + boundry->h > height )
104 boundry->y = height - boundry->h;
105 }
106
107 // Image stack(able) method
filter_get_image(mlt_frame frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int writable)108 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
109 {
110
111 // Get the filter object
112 mlt_filter filter = mlt_frame_pop_service( frame );
113
114 // Get the filter's property object
115 mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
116
117 // Get the frame properties
118 mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
119
120 // Get the frame position
121 mlt_position position = mlt_filter_get_position( filter, frame );
122
123 mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter));
124
125 // Disable consumer scaling
126 if (profile && profile->width && profile->height) {
127 *width = profile->width;
128 *height = profile->height;
129 }
130
131 // Get the new image
132 int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
133
134 if( error != 0 )
135 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle", stderr );
136
137 mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
138
139 // Get the geometry object
140 mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
141
142 // Get the current geometry item
143 struct mlt_geometry_item_s boundry;
144 mlt_geometry_fetch(geometry, &boundry, position);
145
146 // Get the motion vectors
147 struct motion_vector_s *vectors = mlt_properties_get_data( frame_properties, "motion_est.vectors", NULL );
148
149 // Cleanse the geometry item
150 boundry.w = boundry.x < 0 ? boundry.w + boundry.x : boundry.w;
151 boundry.h = boundry.y < 0 ? boundry.h + boundry.y : boundry.h;
152 boundry.x = boundry.x < 0 ? 0 : boundry.x;
153 boundry.y = boundry.y < 0 ? 0 : boundry.y;
154 boundry.w = boundry.w < 0 ? 0 : boundry.w;
155 boundry.h = boundry.h < 0 ? 0 : boundry.h;
156
157 // How did the rectangle move?
158 if( vectors != NULL &&
159 boundry.key != 1 ) // Paused?
160 {
161
162 int method = mlt_properties_get_int( filter_properties, "method" );
163
164 // Get the size of macroblocks in pixel units
165 int macroblock_height = mlt_properties_get_int( frame_properties, "motion_est.macroblock_height" );
166 int macroblock_width = mlt_properties_get_int( frame_properties, "motion_est.macroblock_width" );
167 int mv_buffer_width = *width / macroblock_width;
168
169 caculate_motion( vectors, &boundry, macroblock_width, macroblock_height, mv_buffer_width, method, *width, *height );
170
171
172 // Make the geometry object a real boy
173 boundry.key = 1;
174 boundry.f[0] = 1;
175 boundry.f[1] = 1;
176 boundry.f[2] = 1;
177 boundry.f[3] = 1;
178 boundry.f[4] = 1;
179 mlt_geometry_insert(geometry, &boundry);
180 mlt_geometry_interpolate(geometry);
181 }
182
183 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
184
185 if( mlt_properties_get_int( filter_properties, "debug" ) == 1 )
186 {
187 init_arrows( format, *width, *height );
188 draw_rectangle_outline(*image, boundry.x, boundry.y, boundry.w, boundry.h, 100);
189 }
190
191 if( mlt_properties_get_int( filter_properties, "_serialize" ) == 1 )
192 {
193 // Add the vector change to the list
194 mlt_geometry key_frames = mlt_properties_get_data( filter_properties, "motion_vector_list", NULL );
195 if ( !key_frames )
196 {
197 key_frames = mlt_geometry_init();
198 mlt_properties_set_data( filter_properties, "motion_vector_list", key_frames, 0,
199 (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise );
200 if ( key_frames )
201 mlt_geometry_set_length( key_frames, mlt_filter_get_length2( filter, frame ) );
202 }
203 if ( key_frames )
204 {
205 struct mlt_geometry_item_s item;
206 item.frame = (int) mlt_frame_get_position( frame );
207 item.key = 1;
208 item.x = boundry.x;
209 item.y = boundry.y;
210 item.w = boundry.w;
211 item.h = boundry.h;
212 item.mix = 0;
213 item.f[0] = item.f[1] = item.f[2] = item.f[3] = 1;
214 item.f[4] = 0;
215 mlt_geometry_insert( key_frames, &item );
216 }
217 }
218
219 if( mlt_properties_get_int( filter_properties, "obscure" ) == 1 )
220 {
221 mlt_filter obscure = mlt_properties_get_data( filter_properties, "_obscure", NULL );
222
223 mlt_properties_pass_list( MLT_FILTER_PROPERTIES(obscure), filter_properties, "in, out");
224
225 // Because filter_obscure needs to be rewritten to use mlt_geometry
226 char geom[100];
227 sprintf( geom, "%d/%d:%dx%d", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
228 mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "start", geom );
229 mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "end", geom );
230 }
231
232 if( mlt_properties_get_int( filter_properties, "collect" ) == 1 )
233 {
234 fprintf( stderr, "%d,%d,%d,%d\n", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
235 fflush( stdout );
236 }
237
238 return error;
239 }
240
attach_boundry_to_frame(mlt_frame frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int writable)241 static int attach_boundry_to_frame( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
242 {
243 // Get the filter object
244 mlt_filter filter = mlt_frame_pop_service( frame );
245
246 // Get the filter's property object
247 mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
248
249 // Get the frame properties
250 mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
251
252 // Get the frame position
253 mlt_position position = mlt_filter_get_position( filter, frame );
254
255 mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter));
256
257 // Disable consumer scaling
258 if (profile && profile->width && profile->height) {
259 *width = profile->width;
260 *height = profile->height;
261 }
262
263 mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
264
265 // Get the geometry object
266 mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
267 if (geometry == NULL) {
268 mlt_geometry geom = mlt_geometry_init();
269 char *arg = mlt_properties_get(filter_properties, "geometry");
270
271 // Parse the geometry if we have one
272 mlt_position length = mlt_filter_get_length2( filter, frame );
273 mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
274 mlt_geometry_parse( geom, arg, length, profile->width, profile->height );
275
276 // Initialize with the supplied geometry
277 struct mlt_geometry_item_s item;
278 mlt_geometry_parse_item( geom, &item, arg );
279
280 item.frame = 0;
281 item.key = 1;
282 item.mix = 100;
283
284 mlt_geometry_insert( geom, &item );
285 mlt_geometry_interpolate( geom );
286 mlt_properties_set_data( filter_properties, "filter_geometry", geom, 0, (mlt_destructor)mlt_geometry_close, (mlt_serialiser)mlt_geometry_serialise );
287 geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
288 }
289
290 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
291
292 // Get the current geometry item
293 mlt_geometry_item geometry_item = mlt_pool_alloc( sizeof( struct mlt_geometry_item_s ) );
294 mlt_geometry_fetch(geometry, geometry_item, position);
295
296 // Cleanse the geometry item
297 geometry_item->w = geometry_item->x < 0 ? geometry_item->w + geometry_item->x : geometry_item->w;
298 geometry_item->h = geometry_item->y < 0 ? geometry_item->h + geometry_item->y : geometry_item->h;
299 geometry_item->x = geometry_item->x < 0 ? 0 : geometry_item->x;
300 geometry_item->y = geometry_item->y < 0 ? 0 : geometry_item->y;
301 geometry_item->w = geometry_item->w < 0 ? 0 : geometry_item->w;
302 geometry_item->h = geometry_item->h < 0 ? 0 : geometry_item->h;
303
304 mlt_properties_set_data( frame_properties, "bounds", geometry_item, sizeof( struct mlt_geometry_item_s ), mlt_pool_release, NULL );
305
306 // Get the new image
307 int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
308
309 if( error != 0 )
310 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle attach_boundry_to_frame", stderr );
311
312 return error;
313 }
314
315 /** Filter processing.
316 */
317
filter_process(mlt_filter this,mlt_frame frame)318 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
319 {
320
321 /* modify the frame with the current geometry */
322 mlt_frame_push_service( frame, this);
323 mlt_frame_push_get_image( frame, attach_boundry_to_frame );
324 mlt_properties properties = MLT_FILTER_PROPERTIES( this );
325
326 /* apply the motion estimation filter */
327 mlt_filter motion_est = mlt_properties_get_data( properties, "_motion_est", NULL );
328 /* Pass motion_est properties */
329 mlt_properties_pass( MLT_FILTER_PROPERTIES( motion_est ), properties, "motion_est." );
330 mlt_filter_process( motion_est, frame);
331
332 /* calculate the new geometry based on the motion */
333 mlt_frame_push_service( frame, this);
334 mlt_frame_push_get_image( frame, filter_get_image );
335
336
337 /* visualize the motion vectors */
338 if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "debug" ) == 1 )
339 {
340 mlt_filter vismv = mlt_properties_get_data( properties, "_vismv", NULL );
341 if( vismv == NULL )
342 {
343 mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
344 vismv = mlt_factory_filter( profile, "vismv", NULL );
345 mlt_properties_set_data( properties, "_vismv", vismv, 0, (mlt_destructor)mlt_filter_close, NULL );
346 }
347
348 mlt_filter_process( vismv, frame );
349 }
350
351 if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "obscure" ) == 1 )
352 {
353 mlt_filter obscure = mlt_properties_get_data( properties, "_obscure", NULL );
354 if( obscure == NULL )
355 {
356 mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
357 obscure = mlt_factory_filter( profile, "obscure", NULL );
358 mlt_properties_set_data( properties, "_obscure", obscure, 0, (mlt_destructor)mlt_filter_close, NULL );
359 }
360
361 mlt_filter_process( obscure, frame );
362 }
363
364 return frame;
365 }
366
367 /** Constructor for the filter.
368 */
369
370
filter_autotrack_rectangle_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)371 mlt_filter filter_autotrack_rectangle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
372 {
373 mlt_filter this = mlt_filter_new( );
374 if ( this != NULL )
375 {
376 this->process = filter_process;
377
378 // Initialize with the supplied geometry if there is one
379 if( arg != NULL )
380 mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", arg );
381 else
382 mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", "100/100:100x100" );
383
384 // create an instance of the motion_est and obscure filter
385 mlt_filter motion_est = mlt_factory_filter( profile, "motion_est", NULL );
386 if( motion_est != NULL )
387 mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_motion_est", motion_est, 0, (mlt_destructor)mlt_filter_close, NULL );
388 else {
389 mlt_filter_close( this );
390 return NULL;
391 }
392
393
394 }
395
396 return this;
397 }
398
399 /** This source code will self destruct in 5...4...3...
400 */
401