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