]> git.sesse.net Git - mlt/blob - src/modules/motion_est/filter_autotrack_rectangle.c
Initial import of the motion estimation filter.
[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
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 */