]> git.sesse.net Git - mlt/blob - src/modules/vid.stab/filter_transform.cpp
Added a fourth filter, that combines both detect and transform passes.
[mlt] / src / modules / vid.stab / filter_transform.cpp
1 /*
2  * filter_transform.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 <string.h>
30 #include <assert.h>
31 #include <sstream>
32 #include "common.h"
33
34 typedef struct
35 {
36         VSTransformData td;
37         VSTransformations trans;
38 } TransformData;
39
40 int lm_deserialize(LocalMotions *lms, mlt_property property)
41 {
42         std::istringstream iss(mlt_property_get_string(property));
43         vs_vector_init(lms, 0);
44
45         while (iss.good())
46         {
47                 LocalMotion lm;
48                 iss >> lm.v.x >> lm.v.y >> lm.f.x >> lm.f.y >> lm.f.size >> lm.contrast >> lm.match;
49                 if (iss.fail())
50                 {
51                         break;
52                 }
53                 vs_vector_append_dup(lms, &lm, sizeof(lm));
54         }
55         return 0;
56 }
57
58 int vectors_deserialize(mlt_animation anim, VSManyLocalMotions *mlms)
59 {
60         int error = 0;
61         mlt_animation_item_s item;
62         item.property = mlt_property_init();
63
64         vs_vector_init(mlms, 1024); // initial number of frames, but it will be increased
65
66         int length = mlt_animation_get_length(anim);
67         for (int i = 0; i < length; ++i)
68         {
69                 LocalMotions lms;
70
71                 // read lms
72                 mlt_animation_get_item(anim, &item, i + 1);
73                 if ((error = lm_deserialize(&lms, item.property)))
74                 {
75                         break;
76                 }
77
78                 vs_vector_set_dup(mlms, i, &lms, sizeof(LocalMotions));
79         }
80
81         mlt_property_close(item.property);
82         return error;
83 }
84
85 void destroy_transforms(TransformData *data)
86 {
87         if (data)
88         {
89                 vsTransformDataCleanup(&data->td);
90                 vsTransformationsCleanup(&data->trans);
91                 delete data;
92         }
93 }
94
95 TransformData* initialize_transforms(int *width, int *height, mlt_image_format *format,
96                 mlt_properties properties, const char* interps)
97 {
98         TransformData *data = new TransformData;
99         memset(data, 0, sizeof(TransformData));
100
101         VSPixelFormat pf = convertImageFormat(*format);
102         VSFrameInfo fi_src, fi_dst;
103         vsFrameInfoInit(&fi_src, *width, *height, pf);
104         vsFrameInfoInit(&fi_dst, *width, *height, pf);
105
106         const char* filterName = mlt_properties_get(properties, "mlt_service");
107
108         VSTransformConfig conf = vsTransformGetDefaultConfig(filterName);
109         conf.smoothing = mlt_properties_get_int(properties, "smoothing");
110         conf.maxShift = mlt_properties_get_int(properties, "maxshift");
111         conf.maxAngle = mlt_properties_get_double(properties, "maxangle");
112         conf.crop = (VSBorderType) mlt_properties_get_int(properties, "crop");
113         conf.zoom = mlt_properties_get_int(properties, "zoom");
114         conf.optZoom = mlt_properties_get_int(properties, "optzoom");
115         conf.zoomSpeed = mlt_properties_get_double(properties, "zoomspeed");
116         conf.relative = mlt_properties_get_int(properties, "relative");
117         conf.invert = mlt_properties_get_int(properties, "invert");
118         if (mlt_properties_get_int(properties, "tripod") != 0)
119         {
120                 // Virtual tripod mode: relative=False, smoothing=0
121                 conf.relative = 0;
122                 conf.smoothing = 0;
123         }
124
125         // by default a bilinear interpolation is selected
126         conf.interpolType = VS_BiLinear;
127         if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0)
128                 conf.interpolType = VS_Zero;
129         else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0)
130                 conf.interpolType = VS_Linear;
131
132         vsTransformDataInit(&data->td, &conf, &fi_src, &fi_dst);
133         vsTransformationsInit(&data->trans);
134
135         // load transformations
136         mlt_animation animation = mlt_animation_new();
137         char* strAnim = mlt_properties_get(properties, "vectors");
138         if (mlt_animation_parse(animation, strAnim, 0, 0, NULL))
139         {
140                 mlt_log_warning(NULL, "parse failed\n");
141                 mlt_animation_close(animation);
142                 destroy_transforms(data);
143                 return NULL;
144         }
145
146         VSManyLocalMotions mlms;
147         if (vectors_deserialize(animation, &mlms))
148         {
149                 mlt_animation_close(animation);
150                 destroy_transforms(data);
151                 return NULL;
152         }
153
154         mlt_animation_close(animation);
155
156         vsLocalmotions2Transforms(&data->td, &mlms, &data->trans);
157         vsPreprocessTransforms(&data->td, &data->trans);
158         return data;
159 }
160
161 int get_image_and_transform(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
162 {
163         int error = 0;
164         mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame);
165         mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
166
167         *format = mlt_image_yuv420p;
168
169         error = mlt_frame_get_image(frame, image, format, width, height, 1);
170         if (!error)
171         {
172                 // Service locks are for concurrency control
173                 mlt_service_lock(MLT_FILTER_SERVICE(filter));
174
175                 TransformData *data = static_cast<TransformData*>(mlt_properties_get_data(properties, "_transform_data", NULL));
176
177                 // Handle signal from app to re-init data
178                 if (mlt_properties_get_int(properties, "refresh"))
179                 {
180                         mlt_properties_set(properties, "refresh", NULL);
181                         destroy_transforms(data);
182                         data = NULL;
183                 }
184
185                 if (!data)
186                 {
187                         const char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "rescale.interp");
188                         data = initialize_transforms(width, height, format, properties, interps);
189                         if(!data) {
190                                 mlt_service_unlock(MLT_FILTER_SERVICE(filter));
191                                 return 1; // return error code
192                         }
193                         mlt_properties_set_data(properties, "_transform_data", data, 0, (mlt_destructor) destroy_transforms, NULL);
194                 }
195
196                 VSTransformData* td = &data->td;
197                 VSFrame vsFrame;
198                 vsFrameFillFromBuffer(&vsFrame, *image, vsTransformGetSrcFrameInfo(td));
199
200                 // transform frame
201                 data->trans.current = mlt_filter_get_position(filter, frame);
202                 vsTransformPrepare(td, &vsFrame, &vsFrame);
203                 VSTransform t = vsGetNextTransform(td, &data->trans);
204                 vsDoTransform(td, t);
205                 vsTransformFinish(td);
206
207                 mlt_service_unlock(MLT_FILTER_SERVICE(filter));
208         }
209
210         return error;
211 }
212
213 static mlt_frame process_filter(mlt_filter filter, mlt_frame frame)
214 {
215         mlt_frame_push_service(frame, filter);
216         mlt_frame_push_get_image(frame, get_image_and_transform);
217         return frame;
218 }
219
220 extern "C"
221 {
222
223 mlt_filter filter_transform_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg)
224 {
225         mlt_filter filter = NULL;
226
227         if ((filter = mlt_filter_new()))
228         {
229                 filter->process = process_filter;
230
231                 mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
232
233                 // properties for transform
234                 mlt_properties_set(properties, "smoothing", "10");
235                 mlt_properties_set(properties, "maxshift", "-1");
236                 mlt_properties_set(properties, "maxangle", "-1");
237                 mlt_properties_set(properties, "crop", "0");
238                 mlt_properties_set(properties, "invert", "0");
239                 mlt_properties_set(properties, "relative", "1");
240                 mlt_properties_set(properties, "zoom", "0");
241                 mlt_properties_set(properties, "optzoom", "1");
242                 mlt_properties_set(properties, "zoomspeed", "0.25");
243
244                 return filter;
245         }
246
247         return NULL;
248 }
249
250 }