Initial import of the motion estimation filter.
[melted] / src / modules / motion_est / filter_crop_detect.c
1 /**
2 * /brief Crop Detection filter
3 *
4 * /author Zachary Drew, Copyright 2005
5 *
6 * inspired by mplayer's cropdetect filter
7 *
8 * Note: The goemetry generated is zero-indexed and is inclusive of the end values
9 *
10 * Options:
11 * -filter crop_detect debug=1 // Visualize crop
12 * -filter crop_detect frequency=25 // Detect the crop once a second
13 * -filter crop_detect frequency=0 // Never detect unless the producer changes
14 * -filter crop_detect thresh=100 // Changes the threshold (default = 25)
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software Foundation,
28 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 */
30
31 #define DEBUG
32 #define DEFAULT_THRESH 20
33
34 #include <framework/mlt.h>
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <math.h>
39 #include <string.h>
40
41 #define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b))
42 #define ABS(a) ((a) >= 0 ? (a) : (-(a)))
43
44 #ifdef DEBUG
45 // ffmpeg borrowed
46 static inline int clip(int a, int amin, int amax)
47 {
48 if (a < amin)
49 return amin;
50 else if (a > amax)
51 return amax;
52 else
53 return a;
54 }
55
56
57 /**
58 * draws an line from (ex, ey) -> (sx, sy).
59 * Credits: modified from ffmpeg project
60 * @param ystride stride/linesize of the image
61 * @param xstride stride/element size of the image
62 * @param color color of the arrow
63 */
64 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){
65 int t, x, y, fr, f;
66
67 // buf[sy*ystride + sx*xstride]= color;
68 buf[sy*ystride + sx]+= color;
69
70 sx= clip(sx, 0, w-1);
71 sy= clip(sy, 0, h-1);
72 ex= clip(ex, 0, w-1);
73 ey= clip(ey, 0, h-1);
74
75 if(ABS(ex - sx) > ABS(ey - sy)){
76 if(sx > ex){
77 t=sx; sx=ex; ex=t;
78 t=sy; sy=ey; ey=t;
79 }
80 buf+= sx*xstride + sy*ystride;
81 ex-= sx;
82 f= ((ey-sy)<<16)/ex;
83 for(x= 0; x <= ex; x++){
84 y = (x*f)>>16;
85 fr= (x*f)&0xFFFF;
86 buf[ y *ystride + x*xstride]= (color*(0x10000-fr))>>16;
87 buf[(y+1)*ystride + x*xstride]= (color* fr )>>16;
88 }
89 }else{
90 if(sy > ey){
91 t=sx; sx=ex; ex=t;
92 t=sy; sy=ey; ey=t;
93 }
94 buf+= sx*xstride + sy*ystride;
95 ey-= sy;
96 if(ey) f= ((ex-sx)<<16)/ey;
97 else f= 0;
98 for(y= 0; y <= ey; y++){
99 x = (y*f)>>16;
100 fr= (y*f)&0xFFFF;
101 buf[y*ystride + x *xstride]= (color*(0x10000-fr))>>16;;
102 buf[y*ystride + (x+1)*xstride]= (color* fr )>>16;;
103 }
104 }
105 }
106
107 /**
108 * draws an arrow from (ex, ey) -> (sx, sy).
109 * Credits: modified from ffmpeg project
110 * @param stride stride/linesize of the image
111 * @param color color of the arrow
112 */
113 static void draw_arrow(uint8_t *buf, int sx, int sy, int ex, int ey, int w, int h, int xstride, int ystride, int color){
114 int dx,dy;
115
116 dx= ex - sx;
117 dy= ey - sy;
118
119 if(dx*dx + dy*dy > 3*3){
120 int rx= dx + dy;
121 int ry= -dx + dy;
122 int length= sqrt((rx*rx + ry*ry)<<4);
123
124 //FIXME subpixel accuracy
125 rx= ROUNDED_DIV(rx*3<<4, length);
126 ry= ROUNDED_DIV(ry*3<<4, length);
127
128 draw_line(buf, sx, sy, sx + rx, sy + ry, w, h, xstride, ystride, color);
129 draw_line(buf, sx, sy, sx - ry, sy + rx, w, h, xstride, ystride, color);
130 }
131 draw_line(buf, sx, sy, ex, ey, w, h, xstride, ystride, color);
132 }
133 #endif
134
135 // Image stack(able) method
136 static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
137 {
138
139 // Get the filter object and properties
140 mlt_filter filter = mlt_frame_pop_service( this );
141 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
142
143 // Get the new image
144 int error = mlt_frame_get_image( this, image, format, width, height, 1 );
145
146 if( error != 0 ) {
147 mlt_properties_debug( MLT_FRAME_PROPERTIES(this), "error after mlt_frame_get_image()", stderr );
148 return error;
149 }
150
151 // Parameter that describes how often to check for the crop
152 int frequency = mlt_properties_get_int( properties, "frequency");
153
154 // Producers may start with blank footage, by default we will skip, oh, 5 frames unless overridden
155 int skip = mlt_properties_get_int( properties, "skip");
156
157 // The result
158 mlt_geometry_item bounds = mlt_properties_get_data( properties, "bounds", NULL );
159
160 // Initialize if needed
161 if( bounds == NULL ) {
162 bounds = calloc( 1, sizeof( struct mlt_geometry_item_s ) );
163 bounds->w = *width;
164 bounds->h = *height;
165 mlt_properties_set_data( properties, "bounds", bounds, sizeof( struct mlt_geometry_item_s ), free, NULL );
166 }
167
168 // mlt_properties first = (mlt_properties) mlt_deque_peek_front( MLT_FRAME_SERVICE_STACK(this) );
169 // int current_producer_id = mlt_properties_get_int( first, "_unique_id");
170 // int former_producer_id = mlt_properties_get_int(properties, "_former_unique_id");
171 // mlt_properties_set_int(properties, "_former_unique_id", current_producer_id);
172
173 // For periodic detection (with offset of 'skip')
174 if( frequency == 0 || (mlt_frame_get_position(this)+skip) % frequency != 0)
175 {
176
177 // Inject in stream
178 mlt_properties_set_data( MLT_FRAME_PROPERTIES(this), "bounds", bounds, sizeof( struct mlt_geometry_item_s ), NULL, NULL );
179
180 return 0;
181 }
182
183
184 // There is no way to detect a crop for sure, so make up an arbitrary one
185 int thresh = mlt_properties_get_int( properties, "thresh" );
186
187 int xstride, ystride;
188
189 switch( *format ) {
190 case mlt_image_yuv422:
191 xstride = 2;
192 ystride = 2 * *width;
193 break;
194 default:
195 fprintf(stderr, "image format not supported by filter_crop_detect\n");
196 return -1;
197 }
198
199 int x, y, average_brightness, deviation; // Scratch variables
200
201 // Top crop
202 for( y = 0; y < *height/2; y++ ) {
203 average_brightness = 0;
204 deviation = 0;
205 bounds->y = y;
206 for( x = 0; x < *width; x++ )
207 average_brightness += *(*image + y*ystride + x*xstride);
208
209 average_brightness /= *width;
210
211 for( x = 0; x < *width; x++ )
212 deviation += abs(average_brightness - *(*image + y*ystride + x*xstride));
213
214 if( deviation >= thresh )
215 break;
216 }
217
218 // Bottom crop
219 for( y = *height - 1; y >= *height/2; y-- ) {
220 average_brightness = 0;
221 deviation = 0;
222 bounds->h = y;
223 for( x = 0; x < *width; x++ )
224 average_brightness += *(*image + y*ystride + x*xstride);
225
226 average_brightness /= *width;
227
228 for( x = 0; x < *width; x++ )
229 deviation += abs(average_brightness - *(*image + y*ystride + x*xstride));
230
231 if( deviation >= thresh )
232 break;
233 }
234
235 // Left crop
236 for( x = 0; x < *width/2; x++ ) {
237 average_brightness = 0;
238 deviation = 0;
239 bounds->x = x;
240 for( y = 0; y < *height; y++ )
241 average_brightness += *(*image + y*ystride + x*xstride);
242
243 average_brightness /= *height;
244
245 for( y = 0; y < *height; y++ )
246 deviation += abs(average_brightness - *(*image + y*ystride + x*xstride));
247
248 if( deviation >= thresh )
249 break;
250 }
251
252 // Right crop
253 for( x = *width - 1; x >= *width/2; x-- ) {
254 average_brightness = 0;
255 deviation = 0;
256 bounds->w = x;
257 for( y = 0; y < *height; y++ )
258 average_brightness += *(*image + y*ystride + x*xstride);
259
260 average_brightness /= *height;
261
262 for( y = 0; y < *height; y++ )
263 deviation += abs(average_brightness - *(*image + y*ystride + x*xstride));
264
265 if( deviation >= thresh )
266 break;
267 }
268
269 /* Debug: Draw arrows to show crop */
270 if( mlt_properties_get_int( properties, "debug") == 1 )
271 {
272 draw_arrow(*image, bounds->x, *height/2, bounds->x+40, *height/2, *width, *height, xstride, ystride, 0xff);
273 draw_arrow(*image, *width/2, bounds->y, *width/2, bounds->y+40, *width, *height, xstride, ystride, 0xff);
274 draw_arrow(*image, bounds->w, *height/2, bounds->w-40, *height/2, *width, *height, xstride, ystride, 0xff);
275 draw_arrow(*image, *width/2, bounds->h, *width/2, bounds->h-40, *width, *height, xstride, ystride, 0xff);
276
277 fprintf(stderr, "Top:%f Left:%f Right:%f Bottom:%f\n", bounds->y, bounds->x, bounds->w, bounds->h);
278 }
279
280 bounds->w -= bounds->x;
281 bounds->h -= bounds->y;
282
283 /* inject into frame */
284 mlt_properties_set_data( MLT_FRAME_PROPERTIES(this), "bounds", bounds, sizeof( struct mlt_geometry_item_s ), NULL, NULL );
285
286
287 return error;
288 }
289
290
291
292 /** Filter processing.
293 */
294
295 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
296 {
297
298 // Put the filter object somewhere we can find it
299 mlt_frame_push_service( frame, this);
300
301 // Push the frame filter
302 mlt_frame_push_get_image( frame, filter_get_image );
303
304 return frame;
305 }
306
307 /** Constructor for the filter.
308 */
309 mlt_filter filter_crop_detect_init( char *arg )
310 {
311 mlt_filter this = mlt_filter_new( );
312 if ( this != NULL )
313 {
314 this->process = filter_process;
315
316 /* defaults */
317 mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "frequency", 1);
318 mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "thresh", 25);
319 mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "clip", 5);
320 mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "former_producer_id", -1);
321
322 }
323
324 return this;
325 }
326
327 /** This source code will self destruct in 5...4...3...
328 */
329