a54faa7cb010fdc16fa28f807c35c546017af201
[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 #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 if( mlt_properties_get_int( filter_properties, "obscure" ) == 1 )
159 {
160 mlt_filter obscure = mlt_properties_get_data( filter_properties, "_obscure", NULL );
161
162 mlt_properties_pass_list( MLT_FILTER_PROPERTIES(obscure), filter_properties, "in, out");
163
164 // Because filter_obscure needs to be rewritten to use mlt_geometry
165 char geom[100];
166 sprintf( geom, "%d,%d:%dx%d", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
167 mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "start", geom );
168 mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "end", geom );
169 }
170
171
172 return error;
173 }
174
175 static int attach_boundry_to_frame( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
176 {
177 // Get the filter object
178 mlt_filter filter = mlt_frame_pop_service( frame );
179
180 // Get the filter's property object
181 mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
182
183 // Get the frame properties
184 mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
185
186 // Get the frame position
187 mlt_position position = mlt_frame_get_position( frame );
188
189 // Get the geometry object
190 mlt_geometry geometry = mlt_properties_get_data(filter_properties, "geometry", NULL);
191
192 // Get the current geometry item
193 mlt_geometry_item geometry_item = mlt_pool_alloc( sizeof( struct mlt_geometry_item_s ) );
194 mlt_geometry_fetch(geometry, geometry_item, position);
195
196 mlt_properties_set_data( frame_properties, "bounds", geometry_item, sizeof( struct mlt_geometry_item_s ), mlt_pool_release, NULL );
197
198 // Get the new image
199 int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
200
201 if( error != 0 )
202 mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle attach_boundry_to_frame", stderr );
203
204 return error;
205 }
206
207 /** Filter processing.
208 */
209
210 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
211 {
212
213 /* modify the frame with the current geometry */
214 mlt_frame_push_service( frame, this);
215 mlt_frame_push_get_image( frame, attach_boundry_to_frame );
216
217
218
219 /* apply the motion estimation filter */
220 mlt_filter motion_est = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_motion_est", NULL );
221 mlt_filter_process( motion_est, frame);
222
223
224
225 /* calculate the new geometry based on the motion */
226 mlt_frame_push_service( frame, this);
227 mlt_frame_push_get_image( frame, filter_get_image );
228
229
230 /* visualize the motion vectors */
231 if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "debug" ) == 1 )
232 {
233 mlt_filter vismv = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_vismv", NULL );
234 if( vismv == NULL )
235 {
236 vismv = mlt_factory_filter( "vismv", NULL );
237 mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_vismv", vismv, 0, (mlt_destructor)mlt_filter_close, NULL );
238 }
239
240 mlt_filter_process( vismv, frame );
241 }
242
243 if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "obscure" ) == 1 )
244 {
245 mlt_filter obscure = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_obscure", NULL );
246 if( obscure == NULL )
247 {
248 obscure = mlt_factory_filter( "obscure", NULL );
249 mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_obscure", obscure, 0, (mlt_destructor)mlt_filter_close, NULL );
250 }
251
252 mlt_filter_process( obscure, frame );
253 }
254
255 return frame;
256 }
257
258 /** Constructor for the filter.
259 */
260
261
262 mlt_filter filter_autotrack_rectangle_init( char *arg )
263 {
264 mlt_filter this = mlt_filter_new( );
265 if ( this != NULL )
266 {
267 this->process = filter_process;
268
269
270 mlt_geometry geometry = mlt_geometry_init();
271
272 // Initialize with the supplied geometry
273 if( arg != NULL ) {
274
275 struct mlt_geometry_item_s item;
276
277 mlt_geometry_parse_item( geometry, &item, arg );
278
279 item.frame = 0;
280 item.key = 1;
281 item.mix = 100;
282
283 mlt_geometry_insert( geometry, &item );
284
285 }
286
287 // ... and attach it to the filter
288 mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "geometry", geometry, 0, (mlt_destructor)mlt_geometry_close, (mlt_serialiser)mlt_geometry_serialise );
289
290 // create an instance of the motion_est and obscure filter
291 mlt_filter motion_est = mlt_factory_filter( "motion_est", NULL );
292 if( motion_est != NULL )
293 mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_motion_est", motion_est, 0, (mlt_destructor)mlt_filter_close, NULL );
294 else {
295 mlt_filter_close( this );
296 return NULL;
297 }
298
299
300 }
301
302 return this;
303 }
304
305 /** This source code will self destruct in 5...4...3...
306 */