Initial import of the motion estimation filter.
[melted] / 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
25 #include <framework/mlt.h>
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <math.h>
30 #include <string.h>
31
32 #define MIN(a,b) ((a) > (b) ? (b) : (a))
33
34 #define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b))
35 #define ABS(a) ((a) >= 0 ? (a) : (-(a)))
36
37 // ffmpeg borrowed
38 static inline int clip(int a, int amin, int amax)
39 {
40 if (a < amin)
41 return amin;
42 else if (a > amax)
43 return amax;
44 else
45 return a;
46 }
47
48
49 /**
50 * draws an line from (ex, ey) -> (sx, sy).
51 * Credits: modified from ffmpeg project
52 * @param ystride stride/linesize of the image
53 * @param xstride stride/element size of the image
54 * @param color color of the arrow
55 */
56 static void draw_line(uint8_t *buf, int sx, int sy, int ex, int ey, int w, int h, int xstride, int ystride, int color){
57 int t, x, y, fr, f;
58
59 // buf[sy*ystride + sx*xstride]= color;
60 buf[sy*ystride + sx]+= color;
61
62 sx= clip(sx, 0, w-1);
63 sy= clip(sy, 0, h-1);
64 ex= clip(ex, 0, w-1);
65 ey= clip(ey, 0, h-1);
66
67 if(ABS(ex - sx) > ABS(ey - sy)){
68 if(sx > ex){
69 t=sx; sx=ex; ex=t;
70 t=sy; sy=ey; ey=t;
71 }
72 buf+= sx*xstride + sy*ystride;
73 ex-= sx;
74 f= ((ey-sy)<<16)/ex;
75 for(x= 0; x <= ex; x++){
76 y = (x*f)>>16;
77 fr= (x*f)&0xFFFF;
78 buf[ y *ystride + x*xstride]= (color*(0x10000-fr))>>16;
79 buf[(y+1)*ystride + x*xstride]= (color* fr )>>16;
80 }
81 }else{
82 if(sy > ey){
83 t=sx; sx=ex; ex=t;
84 t=sy; sy=ey; ey=t;
85 }
86 buf+= sx*xstride + sy*ystride;
87 ey-= sy;
88 if(ey) f= ((ex-sx)<<16)/ey;
89 else f= 0;
90 for(y= 0; y <= ey; y++){
91 x = (y*f)>>16;
92 fr= (y*f)&0xFFFF;
93 buf[y*ystride + x *xstride]= (color*(0x10000-fr))>>16;;
94 buf[y*ystride + (x+1)*xstride]= (color* fr )>>16;;
95 }
96 }
97 }
98
99 /**
100 * draws an arrow from (ex, ey) -> (sx, sy).
101 * Credits: modified from ffmpeg project
102 * @param stride stride/linesize of the image
103 * @param color color of the arrow
104 */
105 static __attribute__((used)) void draw_arrow(uint8_t *buf, int sx, int sy, int ex, int ey, int w, int h, int xstride, int ystride, int color){
106 int dx,dy;
107
108 // sx= clip(sx, -100, w+100);
109 // sy= clip(sy, -100, h+100);
110 // ex= clip(ex, -100, w+100);
111 // ey= clip(ey, -100, h+100);
112
113 dx= ex - sx;
114 dy= ey - sy;
115
116 if(dx*dx + dy*dy > 3*3){
117 int rx= dx + dy;
118 int ry= -dx + dy;
119 int length= sqrt((rx*rx + ry*ry)<<8);
120
121 //FIXME subpixel accuracy
122 rx= ROUNDED_DIV(rx*3<<4, length);
123 ry= ROUNDED_DIV(ry*3<<4, length);
124
125 draw_line(buf, sx, sy, sx + rx, sy + ry, w, h, xstride, ystride, color);
126 draw_line(buf, sx, sy, sx - ry, sy + rx, w, h, xstride, ystride, color);
127 }
128 draw_line(buf, sx, sy, ex, ey, w, h, xstride, ystride, color);
129 }
130
131 void caculate_motion( struct motion_vector_s *vectors,
132 mlt_geometry_item boundry,
133 int macroblock_width,
134 int macroblock_height,
135 int mv_buffer_width,
136 int method )
137 {
138
139
140 // translate pixel units (from bounds) to macroblock units
141 // make sure whole macroblock stay within bounds
142 // I know; it hurts.
143 int left_mb = boundry->x / macroblock_width;
144 left_mb += ( (int)boundry->x % macroblock_width == 0 ) ? 0 : 1 ;
145 int top_mb = boundry->y / macroblock_height;
146 top_mb += ( (int)boundry->y % macroblock_height == 0 ) ? 0 : 1 ;
147
148 int right_mb = (boundry->x + boundry->w + 1) / macroblock_width;
149 right_mb -= ( (int)(boundry->x + boundry->w + 1) % macroblock_width == 0 ) ? 0 : 1 ;
150 int bottom_mb = (boundry->y + boundry->h + 1) / macroblock_height;
151 bottom_mb -= ( (int)(boundry->y + boundry->h + 1) % macroblock_height == 0 ) ? 0 : 1 ;
152
153 int i, j, n = 0;
154
155 int average_x = 0, average_y = 0;
156
157 #define CURRENT ( vectors + j*mv_buffer_width + i )
158
159 for( i = left_mb; i <= right_mb; i++ ){
160 for( j = top_mb; j <= bottom_mb; j++ ){
161
162 n++;
163
164 average_x += CURRENT->dx;
165 average_y += CURRENT->dy;
166 }
167 }
168
169 if ( n == 0 )
170 return;
171
172 average_x /= n;
173 average_y /= n;
174
175 int average2_x = 0, average2_y = 0;
176 for( i = left_mb; i <= right_mb; i++ ){
177 for( j = top_mb; j <= bottom_mb; j++ ){
178
179 if( ABS(CURRENT->dx - average_x) < 5 &&
180 ABS(CURRENT->dy - average_y) < 5 )
181 {
182 average2_x += CURRENT->dx;
183 average2_y += CURRENT->dy;
184 }
185 }
186 }
187
188 boundry->x -= average2_x/n;
189 boundry->y -= average2_y/n;
190
191
192 }
193
194 // Image stack(able) method
195 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
196 {
197
198 // Get the filter object
199 mlt_filter filter = mlt_frame_pop_service( frame );
200
201 // Get the filter's property object
202 mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
203
204 // Get the frame properties
205 mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
206
207 // Get the frame position
208 mlt_position position = mlt_frame_get_position( frame );
209
210 // Get the new image
211 int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
212
213 if( error != 0 )
214 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle", stderr );
215
216 // Get the geometry object
217 mlt_geometry geometry = mlt_properties_get_data(filter_properties, "geometry", NULL);
218
219 // Get the current geometry item
220 struct mlt_geometry_item_s boundry;
221 mlt_geometry_fetch(geometry, &boundry, position);
222 //fprintf(stderr, "process %d\n", position);
223
224 // Get the motion vectors
225 struct motion_vector_s *vectors = mlt_properties_get_data( frame_properties, "motion_est.vectors", NULL );
226
227 // How did the rectangle move?
228 if( vectors != NULL ) {
229
230 int method = mlt_properties_get_int( filter_properties, "method" );
231
232 // Get the size of macroblocks in pixel units
233 int macroblock_height = mlt_properties_get_int( frame_properties, "motion_est.macroblock_height" );
234 int macroblock_width = mlt_properties_get_int( frame_properties, "motion_est.macroblock_width" );
235 int mv_buffer_width = *width / macroblock_width;
236
237 caculate_motion( vectors, &boundry, macroblock_width, macroblock_height, mv_buffer_width, method );
238
239 }
240
241 boundry.key = 1;
242
243 boundry.f[0] = 1;
244 boundry.f[1] = 1;
245 boundry.f[2] = 1;
246 boundry.f[3] = 1;
247 boundry.f[4] = 1;
248
249 // boundry.frame = position;
250
251 mlt_geometry_insert(geometry, &boundry);
252
253
254 if( mlt_properties_get_int( filter_properties, "debug" ) == 1 )
255 {
256 int xstep, ystep;
257
258 // Calculate the size of our steps (the number of bytes that seperate adjacent pixels in X and Y direction)
259 switch( *format ) {
260 case mlt_image_yuv422:
261 xstep = 2;
262 ystep = xstep * *width;
263 break;
264 default:
265 // I don't know
266 return -1;
267 break;
268 }
269
270 draw_line(*image, boundry.x, boundry.y, boundry.x, boundry.y + boundry.h, *width, *height, xstep, ystep, 0xff);
271 draw_line(*image, boundry.x, boundry.y + boundry.h, boundry.x + boundry.w, boundry.y + boundry.h, *width, *height, xstep, ystep, 0xff);
272 draw_line(*image, boundry.x + boundry.w, boundry.y + boundry.h, boundry.x + boundry.w, boundry.y, *width, *height, xstep, ystep, 0xff);
273 draw_line(*image, boundry.x + boundry.w, boundry.y, boundry.x, boundry.y, *width, *height, xstep, ystep, 0xff);
274
275 }
276 return error;
277 }
278
279 static int attach_boundry_to_frame( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
280 {
281 // Get the filter object
282 mlt_filter filter = mlt_frame_pop_service( frame );
283
284 // Get the filter's property object
285 mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
286
287 // Get the frame properties
288 mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
289
290 // Get the frame position
291 mlt_position position = mlt_frame_get_position( frame );
292
293 // gEt the geometry object
294 mlt_geometry geometry = mlt_properties_get_data(filter_properties, "geometry", NULL);
295
296 // Get the current geometry item
297 mlt_geometry_item geometry_item = mlt_pool_alloc( sizeof( struct mlt_geometry_item_s ) );
298 mlt_geometry_fetch(geometry, geometry_item, position);
299 //fprintf(stderr, "attach %d\n", position);
300
301 mlt_properties_set_data( frame_properties, "bounds", geometry_item, sizeof( struct mlt_geometry_item_s ), mlt_pool_release, NULL );
302
303 // Get the new image
304 int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
305
306 if( error != 0 )
307 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle attach_boundry_to_frame", stderr );
308
309 return error;
310 }
311
312 /** Filter processing.
313 */
314
315 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
316 {
317
318 //mlt_properties_debug(MLT_SERVICE_PROPERTIES(mlt_service_consumer(mlt_filter_service(this))), "consumer!", stderr);
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
325
326
327 /* apply the motion estimation filter */
328 mlt_filter motion_est = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_motion_est", NULL );
329 mlt_filter_process( motion_est, frame);
330
331
332
333 /* calculate the new geometry based on the motion */
334 mlt_frame_push_service( frame, this);
335 mlt_frame_push_get_image( frame, filter_get_image );
336
337
338 /* visualize the motion vectors */
339 if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "debug" ) == 1 )
340 {
341 mlt_filter vismv = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_vismv", NULL );
342 if( vismv == NULL ) {
343 vismv = mlt_factory_filter( "vismv", NULL );
344 mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_vismv", vismv, 0, (mlt_destructor)mlt_filter_close, NULL );
345 }
346
347 mlt_filter_process( vismv, frame );
348 }
349
350
351 return frame;
352 }
353
354 /** Constructor for the filter.
355 */
356
357
358 mlt_filter filter_autotrack_rectangle_init( char *arg )
359 {
360 mlt_filter this = mlt_filter_new( );
361 if ( this != NULL )
362 {
363 this->process = filter_process;
364
365
366 mlt_geometry geometry = mlt_geometry_init();
367
368 // Initialize with the supplied geometry
369 if( arg != NULL ) {
370
371 struct mlt_geometry_item_s item;
372
373 mlt_geometry_parse_item( geometry, &item, arg );
374
375 item.frame = 0;
376 item.key = 1;
377 item.mix = 100;
378
379 mlt_geometry_insert( geometry, &item );
380
381 }
382
383 mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "geometry", geometry, 0, (mlt_destructor)mlt_geometry_close, (mlt_serialiser)mlt_geometry_serialise );
384
385 mlt_filter motion_est = mlt_factory_filter("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 //mlt_events_init( this );
394 //mlt_events_listen(mlt_service_consumer(mlt_filter_service(this)
395 }
396
397 return this;
398 }
399
400 /** This source code will self destruct in 5...4...3...
401 */