]> git.sesse.net Git - mlt/blob - src/modules/vid.stab/filter_vidstab.cpp
5bc44977fd384e4271c5f6a9294b2c6874a17b36
[mlt] / src / modules / vid.stab / filter_vidstab.cpp
1 /*
2  * filter_vidstab.cpp
3  * Copyright (C) 2013
4  * Marco Gittler <g.marco@freenet.de>
5  * Jakub Ksiezniak <j.ksiezniak@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 extern "C"
23 {
24 #include <vid.stab/libvidstab.h>
25 #include <framework/mlt.h>
26 #include <framework/mlt_animation.h>
27 }
28
29 #include <sstream>
30 #include <string.h>
31 #include <assert.h>
32 #include "common.h"
33
34
35 typedef struct _stab_data
36 {
37         VSMotionDetect md;
38         mlt_animation animation;
39 } StabData;
40
41 typedef struct
42 {
43         VSTransformData td;
44         VSTransformations trans;
45 } TransformData;
46
47
48 char* vectors_serializer(mlt_animation animation, int length)
49 {
50         return mlt_animation_serialize(animation);
51 }
52
53 char* lm_serializer(LocalMotions *lms, int length)
54 {
55         std::ostringstream oss;
56         int size = vs_vector_size(lms);
57         for (int i = 0; i < size; ++i)
58         {
59                 LocalMotion* lm = (LocalMotion*) vs_vector_get(lms, i);
60                 oss << lm->v.x << ' ';
61                 oss << lm->v.y << ' ';
62                 oss << lm->f.x << ' ';
63                 oss << lm->f.y << ' ';
64                 oss << lm->f.size << ' ';
65                 oss << lm->contrast << ' ';
66                 oss << lm->match << ' ';
67         }
68         return strdup(oss.str().c_str());
69 }
70
71 int lm_deserialize(LocalMotions *lms, mlt_property property)
72 {
73         std::istringstream iss(mlt_property_get_string(property));
74         vs_vector_init(lms, 0);
75
76         while (iss.good())
77         {
78                 LocalMotion lm;
79                 iss >> lm.v.x >> lm.v.y >> lm.f.x >> lm.f.y >> lm.f.size >> lm.contrast >> lm.match;
80                 if (iss.fail())
81                 {
82                         break;
83                 }
84                 vs_vector_append_dup(lms, &lm, sizeof(lm));
85         }
86         return 0;
87 }
88
89 void lm_destructor(void *lms)
90 {
91         vs_vector_del(static_cast<VSVector*>(lms));
92 }
93
94 static void serialize_localmotions(StabData* data, LocalMotions &vectors, mlt_position pos)
95 {
96         mlt_animation_item_s item;
97
98         // Initialize animation item
99         item.is_key = 1;
100         item.frame = data->md.frameNum;
101         item.keyframe_type = mlt_keyframe_discrete;
102         item.property = mlt_property_init();
103
104         mlt_property_set_data(item.property, &vectors, 1, lm_destructor, (mlt_serialiser) lm_serializer);
105         mlt_animation_insert(data->animation, &item);
106         mlt_property_close(item.property);
107 }
108
109 int vectors_deserialize(mlt_animation anim, VSManyLocalMotions *mlms)
110 {
111         int error = 0;
112         mlt_animation_item_s item;
113         item.property = mlt_property_init();
114
115         vs_vector_init(mlms, 1024); // initial number of frames, but it will be increased
116
117         int length = mlt_animation_get_length(anim);
118         for (int i = 0; i < length; ++i)
119         {
120                 LocalMotions lms;
121
122                 // read lms
123                 mlt_animation_get_item(anim, &item, i + 1);
124                 if ((error = lm_deserialize(&lms, item.property)))
125                 {
126                         break;
127                 }
128
129                 vs_vector_set_dup(mlms, i, &lms, sizeof(LocalMotions));
130         }
131
132         mlt_property_close(item.property);
133         return error;
134 }
135
136 void destroy_transforms(TransformData *data)
137 {
138         if (data)
139         {
140                 vsTransformDataCleanup(&data->td);
141                 vsTransformationsCleanup(&data->trans);
142                 delete data;
143         }
144 }
145
146 TransformData* initialize_transforms(int *width, int *height, mlt_image_format *format,
147                 mlt_properties properties, const char* interps)
148 {
149         TransformData *data = new TransformData;
150         memset(data, 0, sizeof(TransformData));
151
152         VSPixelFormat pf = convertImageFormat(*format);
153         VSFrameInfo fi_src, fi_dst;
154         vsFrameInfoInit(&fi_src, *width, *height, pf);
155         vsFrameInfoInit(&fi_dst, *width, *height, pf);
156
157         const char* filterName = mlt_properties_get(properties, "mlt_service");
158
159         VSTransformConfig conf = vsTransformGetDefaultConfig(filterName);
160         conf.smoothing = mlt_properties_get_int(properties, "smoothing");
161         conf.maxShift = mlt_properties_get_int(properties, "maxshift");
162         conf.maxAngle = mlt_properties_get_double(properties, "maxangle");
163         conf.crop = (VSBorderType) mlt_properties_get_int(properties, "crop");
164         conf.zoom = mlt_properties_get_int(properties, "zoom");
165         conf.optZoom = mlt_properties_get_int(properties, "optzoom");
166         conf.zoomSpeed = mlt_properties_get_double(properties, "zoomspeed");
167         conf.relative = mlt_properties_get_int(properties, "relative");
168         conf.invert = mlt_properties_get_int(properties, "invert");
169         if (mlt_properties_get_int(properties, "tripod") != 0)
170         {
171                 // Virtual tripod mode: relative=False, smoothing=0
172                 conf.relative = 0;
173                 conf.smoothing = 0;
174         }
175
176         // by default a bilinear interpolation is selected
177         conf.interpolType = VS_BiLinear;
178         if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0)
179                 conf.interpolType = VS_Zero;
180         else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0)
181                 conf.interpolType = VS_Linear;
182
183         vsTransformDataInit(&data->td, &conf, &fi_src, &fi_dst);
184         vsTransformationsInit(&data->trans);
185
186         // load transformations
187         mlt_animation animation = mlt_animation_new();
188         char* strAnim = mlt_properties_get(properties, "vectors");
189         if (mlt_animation_parse(animation, strAnim, 0, 0, NULL))
190         {
191                 mlt_log_warning(NULL, "parse failed\n");
192                 mlt_animation_close(animation);
193                 destroy_transforms(data);
194                 return NULL;
195         }
196
197         VSManyLocalMotions mlms;
198         if (vectors_deserialize(animation, &mlms))
199         {
200                 mlt_animation_close(animation);
201                 destroy_transforms(data);
202                 return NULL;
203         }
204
205         mlt_animation_close(animation);
206
207         vsLocalmotions2Transforms(&data->td, &mlms, &data->trans);
208         vsPreprocessTransforms(&data->td, &data->trans);
209         return data;
210 }
211
212 int get_image_and_transform(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
213 {
214         int error = 0;
215         mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame);
216         mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
217
218         *format = mlt_image_yuv420p;
219
220         error = mlt_frame_get_image(frame, image, format, width, height, 1);
221         if (!error)
222         {
223                 // Service locks are for concurrency control
224                 mlt_service_lock(MLT_FILTER_SERVICE(filter));
225
226                 TransformData *data = static_cast<TransformData*>(mlt_properties_get_data(properties, "_transform_data", NULL));
227
228                 // Handle signal from app to re-init data
229                 if (mlt_properties_get_int(properties, "refresh"))
230                 {
231                         mlt_properties_set(properties, "refresh", NULL);
232                         destroy_transforms(data);
233                         data = NULL;
234                 }
235
236                 if (!data)
237                 {
238                         const char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "rescale.interp");
239                         data = initialize_transforms(width, height, format, properties, interps);
240                         if(!data) {
241                                 mlt_service_unlock(MLT_FILTER_SERVICE(filter));
242                                 return 1; // return error code
243                         }
244                         mlt_properties_set_data(properties, "_transform_data", data, 0, (mlt_destructor) destroy_transforms, NULL);
245                 }
246
247                 VSTransformData* td = &data->td;
248                 VSFrame vsFrame;
249                 vsFrameFillFromBuffer(&vsFrame, *image, vsTransformGetSrcFrameInfo(td));
250
251                 // transform frame
252                 data->trans.current = mlt_filter_get_position(filter, frame);
253                 vsTransformPrepare(td, &vsFrame, &vsFrame);
254                 VSTransform t = vsGetNextTransform(td, &data->trans);
255                 vsDoTransform(td, t);
256                 vsTransformFinish(td);
257
258                 mlt_service_unlock(MLT_FILTER_SERVICE(filter));
259         }
260
261         return error;
262 }
263
264 static StabData* init_detect(mlt_properties properties, mlt_image_format *format, int *width, int *height)
265 {
266         StabData *data = new StabData;
267         memset(data, 0, sizeof(StabData));
268         data->animation = mlt_animation_new();
269
270         VSPixelFormat pf = convertImageFormat(*format);
271         VSFrameInfo fi;
272         vsFrameInfoInit(&fi, *width, *height, pf);
273
274         const char* filterName = mlt_properties_get(properties, "mlt_service");
275
276         VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig(filterName);
277         conf.shakiness = mlt_properties_get_int(properties, "shakiness");
278         conf.accuracy = mlt_properties_get_int(properties, "accuracy");
279         conf.stepSize = mlt_properties_get_int(properties, "stepsize");
280         conf.algo = mlt_properties_get_int(properties, "algo");
281         conf.contrastThreshold = mlt_properties_get_double(properties, "mincontrast");
282         conf.show = mlt_properties_get_int(properties, "show");
283         conf.virtualTripod = mlt_properties_get_int(properties, "tripod");
284         vsMotionDetectInit(&data->md, &conf, &fi);
285
286         // add vectors to properties
287         mlt_properties_set_data(properties, "vectors", data->animation, 1, (mlt_destructor) mlt_animation_close,
288                                         (mlt_serialiser) vectors_serializer);
289         return data;
290 }
291
292 void destroy_detect(StabData *data)
293 {
294         if (data)
295         {
296                 vsMotionDetectionCleanup(&data->md);
297                 delete data;
298         }
299 }
300
301 int get_image_and_detect(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
302 {
303         mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame);
304         mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
305
306         *format = mlt_image_yuv420p;
307
308         writable = writable || mlt_properties_get_int(properties, "show") ? 1 : 0;
309
310         int error = mlt_frame_get_image(frame, image, format, width, height, writable);
311         if (!error)
312         {
313                 // Service locks are for concurrency control
314                 mlt_service_lock(MLT_FILTER_SERVICE(filter));
315
316                 StabData *data = static_cast<StabData*>(mlt_properties_get_data(properties, "_stab_data", NULL));
317                 if (!data)
318                 {
319                         data = init_detect(properties, format, width, height);
320                         mlt_properties_set_data(properties, "_stab_data", data, 0, (mlt_destructor) destroy_detect, NULL);
321                 }
322
323                 VSMotionDetect* md = &data->md;
324                 LocalMotions localmotions;
325                 VSFrame vsFrame;
326                 vsFrameFillFromBuffer(&vsFrame, *image, &md->fi);
327
328                 // detect and save motions
329                 vsMotionDetection(md, &localmotions, &vsFrame);
330                 mlt_position pos = mlt_filter_get_position( filter, frame );
331                 serialize_localmotions(data, localmotions, pos);
332
333                 mlt_service_unlock(MLT_FILTER_SERVICE(filter));
334         }
335
336         return error;
337 }
338
339 static mlt_frame process_filter(mlt_filter filter, mlt_frame frame)
340 {
341         mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
342         mlt_get_image vidstab_get_image = (mlt_get_image) mlt_properties_get_data( properties, "_vidstab_get_image", NULL );
343
344 #if 1
345         mlt_position pos = mlt_filter_get_position(filter, frame);
346         mlt_position length = mlt_filter_get_length2(filter, frame) - 1;
347         if(pos >= length)
348         {
349                 mlt_properties_set_data(properties, "_vidstab_get_image", NULL, 0, NULL, NULL);
350         }
351 #endif
352
353         if(vidstab_get_image == NULL)
354         {
355                 if(mlt_properties_get(properties, "vectors") == NULL)
356                 {
357                         // vectors are NULL, so use a detect filter
358                         vidstab_get_image = get_image_and_detect;
359                 } else {
360                         // found vectors, so use a transform filter
361                         vidstab_get_image = get_image_and_transform;
362                 }
363
364                 mlt_properties_set_data( properties, "_vidstab_get_image", (void*)vidstab_get_image, 0, NULL, NULL );
365         }
366
367         mlt_frame_push_service(frame, filter);
368         mlt_frame_push_get_image(frame, vidstab_get_image);
369         return frame;
370 }
371
372 extern "C"
373 {
374
375 mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type,
376                 const char *id, char *arg)
377 {
378         mlt_filter filter = mlt_filter_new();
379
380         if( filter )
381         {
382                 filter->process = process_filter;
383
384                 mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
385
386                 //properties for stabilize
387                 mlt_properties_set(properties, "shakiness", "4");
388                 mlt_properties_set(properties, "accuracy", "4");
389                 mlt_properties_set(properties, "stepsize", "6");
390                 mlt_properties_set(properties, "algo", "1");
391                 mlt_properties_set(properties, "mincontrast", "0.3");
392                 mlt_properties_set(properties, "show", "0");
393                 mlt_properties_set(properties, "tripod", "0");
394
395                 // properties for transform
396                 mlt_properties_set(properties, "smoothing", "15");
397                 mlt_properties_set(properties, "maxshift", "-1");
398                 mlt_properties_set(properties, "maxangle", "-1");
399                 mlt_properties_set(properties, "crop", "0");
400                 mlt_properties_set(properties, "invert", "0");
401                 mlt_properties_set(properties, "relative", "1");
402                 mlt_properties_set(properties, "zoom", "0");
403                 mlt_properties_set(properties, "optzoom", "1");
404                 mlt_properties_set(properties, "zoomspeed", "0.25");
405
406                 mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION);
407         }
408
409         return filter;
410 }
411
412 }