]> git.sesse.net Git - mlt/blob - src/modules/videostab/transform_image.c
Merge branch 'frei0r-metadata'
[mlt] / src / modules / videostab / transform_image.c
1 /*
2  *  filter_transform.c
3  *
4  *  Copyright (C) Georg Martius - June 2007
5  *   georg dot martius at web dot de
6  *
7  *  This file is part of transcode, a video stream processing tool
8  *
9  *  transcode is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2, or (at your option)
12  *  any later version.
13  *
14  *  transcode is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with GNU Make; see the file COPYING.  If not, write to
21  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * Typical call:
24  * transcode -J transform -i inp.mpeg -y xdiv,tcaud inp_stab.avi
25  */
26 #include "transform_image.h"
27 #include <string.h>
28 #include <stdlib.h>
29 #include <framework/mlt_log.h>
30
31 #define MAX (a,b) (((a)>(b)?(a):(b)))
32 #define MIN (a,b) (((a)<(b)?(a):(b)))
33 #define CLAMP(a,x,y) MIN( MAX( (a),(x) ,y))
34
35 #define TC_MAX(a, b)            (((a) > (b)) ?(a) :(b))
36 #define TC_MIN(a, b)            (((a) < (b)) ?(a) :(b))
37 /* clamp x between a and b */
38 #define TC_CLAMP(x, a, b)       TC_MIN(TC_MAX((a), (x)), (b))
39
40
41 static const char* interpoltypes[5] = {"No (0)", "Linear (1)", "Bi-Linear (2)",
42                                        "Quadratic (3)", "Bi-Cubic (4)"};
43
44 void (*interpolate)(unsigned char *rv, float x, float y,
45                     unsigned char* img, int width, int height,
46                     unsigned char def,unsigned char N, unsigned char channel) = 0;
47
48 /** interpolateBiLinBorder: bi-linear interpolation function that also works at the border.
49     This is used by many other interpolation methods at and outsize the border, see interpolate */
50 void interpolateBiLinBorder(unsigned char *rv, float x, float y,
51                             unsigned char* img, int width, int height,
52                             unsigned char def,unsigned char N, unsigned char channel)
53 {
54     int x_f = myfloor(x);
55     int x_c = x_f+1;
56     int y_f = myfloor(y);
57     int y_c = y_f+1;
58     short v1 = PIXELN(img, x_c, y_c, width, height, N, channel, def);
59     short v2 = PIXELN(img, x_c, y_f, width, height, N, channel, def);
60     short v3 = PIXELN(img, x_f, y_c, width, height, N, channel, def);
61     short v4 = PIXELN(img, x_f, y_f, width, height, N, channel, def);
62     float s  = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
63         (v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
64     *rv = (unsigned char)s;
65 }
66
67 /* taken from http://en.wikipedia.org/wiki/Bicubic_interpolation for alpha=-0.5
68    in matrix notation:
69    a0-a3 are the neigthboring points where the target point is between a1 and a2
70    t is the point of interpolation (position between a1 and a2) value between 0 and 1
71                  | 0, 2, 0, 0 |  |a0|
72                  |-1, 0, 1, 0 |  |a1|
73    (1,t,t^2,t^3) | 2,-5, 4,-1 |  |a2|
74                  |-1, 3,-3, 1 |  |a3|
75 */
76 static short bicub_kernel(float t, short a0, short a1, short a2, short a3){
77     return (2*a1 + t*((-a0+a2) + t*((2*a0-5*a1+4*a2-a3) + t*(-a0+3*a1-3*a2+a3) )) ) / 2;
78 }
79
80 /** interpolateBiCub: bi-cubic interpolation function using 4x4 pixel, see interpolate */
81 void interpolateBiCub(unsigned char *rv, float x, float y,
82                       unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel)
83 {
84     // do a simple linear interpolation at the border
85     if (x < 1 || x >= width-2 || y < 1 || y >= height - 2) {
86         interpolateBiLinBorder(rv, x,y,img,width,height,def,N,channel);
87     } else {
88         int x_f = myfloor(x);
89         int y_f = myfloor(y);
90         float tx = x-x_f;
91         short v1 = bicub_kernel(tx,
92                                 PIXN(img, x_f-1, y_f-1, width, height, N, channel),
93                                 PIXN(img, x_f,   y_f-1, width, height, N, channel),
94                                 PIXN(img, x_f+1, y_f-1, width, height, N, channel),
95                                 PIXN(img, x_f+2, y_f-1, width, height, N, channel));
96         short v2 = bicub_kernel(tx,
97                                 PIXN(img, x_f-1, y_f, width, height, N, channel),
98                                 PIXN(img, x_f,   y_f, width, height, N, channel),
99                                 PIXN(img, x_f+1, y_f, width, height, N, channel),
100                                 PIXN(img, x_f+2, y_f, width, height, N, channel));
101         short v3 = bicub_kernel(tx,
102                                 PIXN(img, x_f-1, y_f+1, width, height, N, channel),
103                                 PIXN(img, x_f,   y_f+1, width, height, N, channel),
104                                 PIXN(img, x_f+1, y_f+1, width, height, N, channel),
105                                 PIXN(img, x_f+2, y_f+1, width, height, N, channel));
106         short v4 = bicub_kernel(tx,
107                                 PIXN(img, x_f-1, y_f+2, width, height, N, channel),
108                                 PIXN(img, x_f,   y_f+2, width, height, N, channel),
109                                 PIXN(img, x_f+1, y_f+2, width, height, N, channel),
110                                 PIXN(img, x_f+2, y_f+2, width, height, N, channel));
111         *rv = (unsigned char)bicub_kernel(y-y_f, v1, v2, v3, v4);
112     }
113 }
114
115 /** interpolateSqr: bi-quatratic interpolation function, see interpolate */
116 void interpolateSqr(unsigned char *rv, float x, float y,
117                     unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel)
118 {
119     if (x < 0 || x >= width-1 || y < 0 || y >= height-1) {
120         interpolateBiLinBorder(rv, x, y, img, width, height, def,N,channel);
121     } else {
122         int x_f = myfloor(x);
123         int x_c = x_f+1;
124         int y_f = myfloor(y);
125         int y_c = y_f+1;
126         short v1 = PIXN(img, x_c, y_c, width, height, N, channel);
127         short v2 = PIXN(img, x_c, y_f, width, height, N, channel);
128         short v3 = PIXN(img, x_f, y_c, width, height, N, channel);
129         short v4 = PIXN(img, x_f, y_f, width, height, N, channel);
130         float f1 = 1 - sqrt((x_c - x) * (y_c - y));
131         float f2 = 1 - sqrt((x_c - x) * (y - y_f));
132         float f3 = 1 - sqrt((x - x_f) * (y_c - y));
133         float f4 = 1 - sqrt((x - x_f) * (y - y_f));
134         float s  = (v1*f1 + v2*f2 + v3*f3+ v4*f4)/(f1 + f2 + f3 + f4);
135         *rv = (unsigned char)s;
136     }
137 }
138
139 /** interpolateBiLin: bi-linear interpolation function, see interpolate */
140 void interpolateBiLin(unsigned char *rv, float x, float y,
141                       unsigned char* img, int width, int height,
142                       unsigned char def,unsigned char N, unsigned char channel)
143 {
144     if (x < 0 || x >= width-1 || y < 0 || y >= height - 1) {
145         interpolateBiLinBorder(rv, x, y, img, width, height, def,N,channel);
146     } else {
147         int x_f = myfloor(x);
148         int x_c = x_f+1;
149         int y_f = myfloor(y);
150         int y_c = y_f+1;
151         short v1 = PIXN(img, x_c, y_c, width, height, N, channel);
152         short v2 = PIXN(img, x_c, y_f, width, height, N, channel);
153         short v3 = PIXN(img, x_f, y_c, width, height, N, channel);
154         short v4 = PIXN(img, x_f, y_f, width, height, N, channel);
155         float s  = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
156             (v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
157         *rv = (unsigned char)s;
158     }
159 }
160
161
162 /** interpolateLin: linear (only x) interpolation function, see interpolate */
163 void interpolateLin(unsigned char *rv, float x, float y,
164                     unsigned char* img, int width, int height,
165                     unsigned char def,unsigned char N, unsigned char channel)
166 {
167     int x_f = myfloor(x);
168     int x_c = x_f+1;
169     int y_n = myround(y);
170     float v1 = PIXELN(img, x_c, y_n, width, height, N, channel,def);
171     float v2 = PIXELN(img, x_f, y_n, width, height, N, channel,def);
172     float s  = v1*(x - x_f) + v2*(x_c - x);
173     *rv = (unsigned char)s;
174 }
175
176 /** interpolateZero: nearest neighbor interpolation function, see interpolate */
177 void interpolateZero(unsigned char *rv, float x, float y,
178                    unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel)
179 {
180     int x_n = myround(x);
181     int y_n = myround(y);
182     *rv = (unsigned char) PIXELN(img, x_n, y_n, width, height, N,channel,def);
183 }
184
185
186 /**
187  * interpolateN: Bi-linear interpolation function for N channel image.
188  *
189  * Parameters:
190  *             rv: destination pixel (call by reference)
191  *            x,y: the source coordinates in the image img. Note this
192  *                 are real-value coordinates, that's why we interpolate
193  *            img: source image
194  *   width,height: dimension of image
195  *              N: number of channels
196  *        channel: channel number (0..N-1)
197  *            def: default value if coordinates are out of range
198  * Return value:  None
199  */
200 void interpolateN(unsigned char *rv, float x, float y,
201                   unsigned char* img, int width, int height,
202                   unsigned char N, unsigned char channel,
203                   unsigned char def)
204 {
205     if (x < - 1 || x > width || y < -1 || y > height) {
206         *rv = def;
207     } else {
208         int x_f = myfloor(x);
209         int x_c = x_f+1;
210         int y_f = myfloor(y);
211         int y_c = y_f+1;
212         short v1 = PIXELN(img, x_c, y_c, width, height, N, channel, def);
213         short v2 = PIXELN(img, x_c, y_f, width, height, N, channel, def);
214         short v3 = PIXELN(img, x_f, y_c, width, height, N, channel, def);
215         short v4 = PIXELN(img, x_f, y_f, width, height, N, channel, def);
216         float s  = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
217             (v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
218         *rv = (unsigned char)s;
219     }
220 }
221
222
223 /**
224  * transformRGB: applies current transformation to frame
225  * Parameters:
226  *         td: private data structure of this filter
227  * Return value:
228  *         0 for failture, 1 for success
229  * Preconditions:
230  *  The frame must be in RGB format
231  */
232 int transformRGB(TransformData* td)
233 {
234     Transform t;
235     int x = 0, y = 0, z = 0;
236     unsigned char *D_1, *D_2;
237     t = td->trans[td->current_trans];
238
239     D_1  = td->src;
240     D_2  = td->dest;
241                 float zm = 1.0-t.zoom/100;
242                 float zcos_a = zm*cos(-t.alpha); // scaled cos
243     float zsin_a = zm*sin(-t.alpha); // scaled sin
244     float c_s_x = td->width_src/2.0;
245     float c_s_y = td->height_src/2.0;
246     float c_d_x = td->width_dest/2.0;
247     float c_d_y = td->height_dest/2.0;
248
249     /* for each pixel in the destination image we calc the source
250      * coordinate and make an interpolation:
251      *      p_d = c_d + M(p_s - c_s) + t
252      * where p are the points, c the center coordinate,
253      *  _s source and _d destination,
254      *  t the translation, and M the rotation matrix
255      *      p_s = M^{-1}(p_d - c_d - t) + c_s
256      */
257     /* All 3 channels */
258     if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) {
259         for (x = 0; x < td->width_dest; x++) {
260             for (y = 0; y < td->height_dest; y++) {
261                 float x_d1 = (x - c_d_x);
262                 float y_d1 = (y - c_d_y);
263                 float x_s  =  zcos_a * x_d1
264                     + zsin_a * y_d1 + c_s_x -t.x;
265                 float y_s  = -zsin_a * x_d1
266                     + zcos_a * y_d1 + c_s_y -t.y;
267                 for (z = 0; z < 3; z++) { // iterate over colors
268                     unsigned char* dest = &D_2[(x + y * td->width_dest)*3+z];
269                     interpolate(dest, myfloor(x_s), myfloor(y_s), D_1,
270                                  td->width_src, td->height_src,
271                                  td->crop ? 16 : *dest,3,z);
272                 }
273             }
274         }
275      }else {
276         /* no rotation, just translation
277          *(also no interpolation, since no size change (so far)
278          */
279         int round_tx = myround(t.x);
280         int round_ty = myround(t.y);
281         for (x = 0; x < td->width_dest; x++) {
282             for (y = 0; y < td->height_dest; y++) {
283                 for (z = 0; z < 3; z++) { // iterate over colors
284                     short p = PIXELN(D_1, x - round_tx, y - round_ty,
285                                      td->width_src, td->height_src, 3, z, -1);
286                     if (p == -1) {
287                         if (td->crop == 1)
288                             D_2[(x + y * td->width_dest)*3+z] = 16;
289                     } else {
290                         D_2[(x + y * td->width_dest)*3+z] = (unsigned char)p;
291                     }
292                 }
293             }
294         }
295     }
296     return 1;
297 }
298
299 /**
300  * transformYUV: applies current transformation to frame
301  *
302  * Parameters:
303  *         td: private data structure of this filter
304  * Return value:
305  *         0 for failture, 1 for success
306  * Preconditions:
307  *  The frame must be in YUV format
308  */
309 int transformYUV(TransformData* td)
310 {
311     Transform t;
312     int x = 0, y = 0;
313     unsigned char *Y_1, *Y_2, *Cb_1, *Cb_2, *Cr_1, *Cr_2;
314     t = td->trans[td->current_trans];
315
316     Y_1  = td->src;
317     Y_2  = td->dest;
318     Cb_1 = td->src + td->width_src * td->height_src;
319     Cb_2 = td->dest + td->width_dest * td->height_dest;
320     Cr_1 = td->src + 5*td->width_src * td->height_src/4;
321     Cr_2 = td->dest + 5*td->width_dest * td->height_dest/4;
322     float c_s_x = td->width_src/2.0;
323     float c_s_y = td->height_src/2.0;
324     float c_d_x = td->width_dest/2.0;
325     float c_d_y = td->height_dest/2.0;
326
327     float z = 1.0-t.zoom/100;
328     float zcos_a = z*cos(-t.alpha); // scaled cos
329     float zsin_a = z*sin(-t.alpha); // scaled sin
330
331     /* for each pixel in the destination image we calc the source
332      * coordinate and make an interpolation:
333      *      p_d = c_d + M(p_s - c_s) + t
334      * where p are the points, c the center coordinate,
335      *  _s source and _d destination,
336      *  t the translation, and M the rotation and scaling matrix
337      *      p_s = M^{-1}(p_d - c_d - t) + c_s
338      */
339     /* Luminance channel */
340     if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) {
341         for (x = 0; x < td->width_dest; x++) {
342             for (y = 0; y < td->height_dest; y++) {
343                 float x_d1 = (x - c_d_x);
344                 float y_d1 = (y - c_d_y);
345                 float x_s  =  zcos_a * x_d1
346                     + zsin_a * y_d1 + c_s_x -t.x;
347                 float y_s  = -zsin_a * x_d1
348                     + zcos_a * y_d1 + c_s_y -t.y;
349                 unsigned char* dest = &Y_2[x + y * td->width_dest];
350                 interpolate(dest, x_s, y_s, Y_1,
351                             td->width_src, td->height_src,
352                             td->crop ? 16 : *dest,1,0);
353             }
354         }
355      }else {
356         /* no rotation, no zooming, just translation
357          *(also no interpolation, since no size change)
358          */
359         int round_tx = myround(t.x);
360         int round_ty = myround(t.y);
361         for (x = 0; x < td->width_dest; x++) {
362             for (y = 0; y < td->height_dest; y++) {
363                 short p = PIXEL(Y_1, x - round_tx, y - round_ty,
364                                 td->width_src, td->height_src, -1);
365                 if (p == -1) {
366                     if (td->crop == 1)
367                         Y_2[x + y * td->width_dest] = 16;
368                 } else {
369                     Y_2[x + y * td->width_dest] = (unsigned char)p;
370                 }
371             }
372         }
373     }
374
375     /* Color channels */
376     int ws2 = td->width_src/2;
377     int wd2 = td->width_dest/2;
378     int hs2 = td->height_src/2;
379     int hd2 = td->height_dest/2;
380     if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) {
381         for (x = 0; x < wd2; x++) {
382             for (y = 0; y < hd2; y++) {
383                 float x_d1 = x - (c_d_x)/2;
384                 float y_d1 = y - (c_d_y)/2;
385                 float x_s  =  zcos_a * x_d1
386                     + zsin_a * y_d1 + (c_s_x -t.x)/2;
387                 float y_s  = -zsin_a * x_d1
388                     + zcos_a * y_d1 + (c_s_y -t.y)/2;
389                 unsigned char* dest = &Cr_2[x + y * wd2];
390                 interpolate(dest, x_s, y_s, Cr_1, ws2, hs2,
391                             td->crop ? 128 : *dest,1,0);
392                 dest = &Cb_2[x + y * wd2];
393                 interpolate(dest, x_s, y_s, Cb_1, ws2, hs2,
394                             td->crop ? 128 : *dest,1,0);
395             }
396         }
397     } else { // no rotation, no zoom, no interpolation, just translation
398         int round_tx2 = myround(t.x/2.0);
399         int round_ty2 = myround(t.y/2.0);
400         for (x = 0; x < wd2; x++) {
401             for (y = 0; y < hd2; y++) {
402                 short cr = PIXEL(Cr_1, x - round_tx2, y - round_ty2,
403                                  wd2, hd2, -1);
404                 short cb = PIXEL(Cb_1, x - round_tx2, y - round_ty2,
405                                  wd2, hd2, -1);
406                 if (cr == -1) {
407                     if (td->crop==1) {
408                         Cr_2[x + y * wd2] = 128;
409                         Cb_2[x + y * wd2] = 128;
410                     }
411                 } else {
412                     Cr_2[x + y * wd2] = (unsigned char)cr;
413                     Cb_2[x + y * wd2] = (unsigned char)cb;
414                 }
415             }
416         }
417     }
418     return 1;
419 }
420
421
422 /**
423  * preprocess_transforms: does smoothing, relative to absolute conversion,
424  *  and cropping of too large transforms.
425  *  This is actually the core algorithm for canceling the jiggle in the
426  *  movie. We perform a low-pass filter in terms of transformation size.
427  *  This enables still camera movement, but in a smooth fasion.
428  *
429  * Parameters:
430  *            td: tranform private data structure
431  * Return value:
432  *     1 for success and 0 for failture
433  * Preconditions:
434  *     None
435  * Side effects:
436  *     td->trans will be modified
437  */
438 int preprocess_transforms(TransformData* td)
439 {
440     Transform* ts = td->trans;
441     int i;
442
443     if (td->trans_len < 1)
444         return 0;
445     if (0) {
446         mlt_log_debug(NULL,"Preprocess transforms:");
447     }
448     if (td->smoothing>0) {
449         /* smoothing */
450         Transform* ts2 = malloc(sizeof(Transform) * td->trans_len);
451         memcpy(ts2, ts, sizeof(Transform) * td->trans_len);
452
453         /*  we will do a sliding average with minimal update
454          *   \hat x_{n/2} = x_1+x_2 + .. + x_n
455          *   \hat x_{n/2+1} = x_2+x_3 + .. + x_{n+1} = x_{n/2} - x_1 + x_{n+1}
456          *   avg = \hat x / n
457          */
458         int s = td->smoothing * 2 + 1;
459         Transform null = null_transform();
460         /* avg is the average over [-smoothing, smoothing] transforms
461            around the current point */
462         Transform avg;
463         /* avg2 is a sliding average over the filtered signal! (only to past)
464          *  with smoothing * 10 horizont to kill offsets */
465         Transform avg2 = null_transform();
466         double tau = 1.0/(3 * s);
467         /* initialise sliding sum with hypothetic sum centered around
468          * -1st element. We have two choices:
469          * a) assume the camera is not moving at the beginning
470          * b) assume that the camera moves and we use the first transforms
471          */
472         Transform s_sum = null;
473         for (i = 0; i < td->smoothing; i++){
474             s_sum = add_transforms(&s_sum, i < td->trans_len ? &ts2[i]:&null);
475         }
476         mult_transform(&s_sum, 2); // choice b (comment out for choice a)
477
478         for (i = 0; i < td->trans_len; i++) {
479             Transform* old = ((i - td->smoothing - 1) < 0)
480                 ? &null : &ts2[(i - td->smoothing - 1)];
481             Transform* new = ((i + td->smoothing) >= td->trans_len)
482                 ? &null : &ts2[(i + td->smoothing)];
483             s_sum = sub_transforms(&s_sum, old);
484             s_sum = add_transforms(&s_sum, new);
485
486             avg = mult_transform(&s_sum, 1.0/s);
487
488             /* lowpass filter:
489              * meaning high frequency must be transformed away
490              */
491             ts[i] = sub_transforms(&ts2[i], &avg);
492             /* kill accumulating offset in the filtered signal*/
493             avg2 = add_transforms_(mult_transform(&avg2, 1 - tau),
494                                    mult_transform(&ts[i], tau));
495             ts[i] = sub_transforms(&ts[i], &avg2);
496
497             if (0 /*verbose*/ ) {
498                 mlt_log_warning(NULL,"s_sum: %5lf %5lf %5lf, ts: %5lf, %5lf, %5lf\n",
499                            s_sum.x, s_sum.y, s_sum.alpha,
500                            ts[i].x, ts[i].y, ts[i].alpha);
501                 mlt_log_warning(NULL,
502                            "  avg: %5lf, %5lf, %5lf avg2: %5lf, %5lf, %5lf",
503                            avg.x, avg.y, avg.alpha,
504                            avg2.x, avg2.y, avg2.alpha);
505             }
506         }
507         free(ts2);
508     }
509
510
511     /*  invert? */
512     if (td->invert) {
513         for (i = 0; i < td->trans_len; i++) {
514             ts[i] = mult_transform(&ts[i], -1);
515         }
516     }
517
518     /* relative to absolute */
519     if (td->relative) {
520         Transform t = ts[0];
521         for (i = 1; i < td->trans_len; i++) {
522             if (0/*verbose*/ ) {
523                 mlt_log_warning(NULL, "shift: %5lf   %5lf   %lf \n",
524                            t.x, t.y, t.alpha *180/M_PI);
525             }
526             ts[i] = add_transforms(&ts[i], &t);
527             t = ts[i];
528         }
529     }
530     /* crop at maximal shift */
531     if (td->maxshift != -1)
532         for (i = 0; i < td->trans_len; i++) {
533             ts[i].x     = TC_CLAMP(ts[i].x, -td->maxshift, td->maxshift);
534             ts[i].y     = TC_CLAMP(ts[i].y, -td->maxshift, td->maxshift);
535         }
536     if (td->maxangle != - 1.0)
537         for (i = 0; i < td->trans_len; i++)
538             ts[i].alpha = TC_CLAMP(ts[i].alpha, -td->maxangle, td->maxangle);
539
540     /* Calc optimal zoom
541      *  cheap algo is to only consider transformations
542      *  uses cleaned max and min
543      */
544     if (td->optzoom != 0 && td->trans_len > 1){
545         Transform min_t, max_t;
546         cleanmaxmin_xy_transform(ts, td->trans_len, 10, &min_t, &max_t);
547         // the zoom value only for x
548         double zx = 2*TC_MAX(max_t.x,fabs(min_t.x))/td->width_src;
549         // the zoom value only for y
550         double zy = 2*TC_MAX(max_t.y,fabs(min_t.y))/td->height_src;
551         td->zoom += 100* TC_MAX(zx,zy); // use maximum
552         mlt_log_debug(NULL,"Final zoom: %lf\n", td->zoom);
553     }
554
555     /* apply global zoom */
556     if (td->zoom != 0){
557         for (i = 0; i < td->trans_len; i++)
558             ts[i].zoom += td->zoom;
559     }
560
561     return 1;
562 }
563
564 /**
565  * transform_init:  Initialize this instance of the module.  See
566  * tcmodule-data.h for function details.
567  */
568
569 #if 0
570
571 static int transform_init(TCModuleInstance *self, uint32_t features)
572 {
573
574     TransformData* td = NULL;
575     TC_MODULE_SELF_CHECK(self, "init");
576     TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
577
578     td = tc_zalloc(sizeof(TransformData));
579     if (td == NULL) {
580         tc_log_error(MOD_NAME, "init: out of memory!");
581         return TC_ERROR;
582     }
583     self->userdata = td;
584     if (verbose) {
585         tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
586     }
587
588     return T;
589 }
590 #endif
591
592 /**
593  * transform_configure:  Configure this instance of the module.  See
594  * tcmodule-data.h for function details.
595  */
596 int transform_configure(TransformData *self,int width,int height, mlt_image_format  pixelformat, unsigned char* image ,Transform* tx,int trans_len)
597 {
598     TransformData *td = self;
599
600     /**** Initialise private data structure */
601
602     /* td->framesize = td->vob->im_v_width *
603      *  MAX_PLANES * sizeof(char) * 2 * td->vob->im_v_height * 2;
604      */
605         // rgb24 = w*h*3 , yuv420p = w* h* 3/2
606     td->framesize_src = width*height*(pixelformat==mlt_image_rgb24 ? 3 : (3.0/2.0));
607     td->src = malloc(td->framesize_src); /* FIXME */
608     if (td->src == NULL) {
609         mlt_log_error(NULL,"tc_malloc failed\n");
610         return -1;
611     }
612
613     td->width_src  = width;
614     td->height_src = height;
615
616     /* Todo: in case we can scale the images, calc new size later */
617     td->width_dest  = width;
618     td->height_dest = height;
619     td->framesize_dest = td->framesize_src;
620     td->dest = 0;
621
622     td->trans = tx;
623     td->trans_len = trans_len;
624     td->current_trans = 0;
625     td->warned_transform_end = 0;
626
627     /* Options */
628     // set from  filter td->maxshift = -1;
629     // set from  filter td->maxangle = -1;
630
631
632     // set from  filter td->crop = 0;
633     // set from  filter td->relative = 1;
634     // set from  filter td->invert = 0;
635     // set from  filter td->smoothing = 10;
636
637     td->rotation_threshhold = 0.25/(180/M_PI);
638
639     // set from  filter td->zoom    = 0;
640     // set from  filter td->optzoom = 1;
641     // set from  filter td->interpoltype = 2; // bi-linear
642     // set from  filter td->sharpen = 0.8;
643
644     td->interpoltype = TC_MIN(td->interpoltype,4);
645     if (1) {
646         mlt_log_debug(NULL, "Image Transformation/Stabilization Settings:\n");
647         mlt_log_debug(NULL, "    smoothing = %d\n", td->smoothing);
648         mlt_log_debug(NULL, "    maxshift  = %d\n", td->maxshift);
649         mlt_log_debug(NULL, "    maxangle  = %f\n", td->maxangle);
650         mlt_log_debug(NULL, "    crop      = %s\n",
651                         td->crop ? "Black" : "Keep");
652         mlt_log_debug(NULL, "    relative  = %s\n",
653                     td->relative ? "True": "False");
654         mlt_log_debug(NULL, "    invert    = %s\n",
655                     td->invert ? "True" : "False");
656         mlt_log_debug(NULL, "    zoom      = %f\n", td->zoom);
657         mlt_log_debug(NULL, "    optzoom   = %s\n",
658                     td->optzoom ? "On" : "Off");
659         mlt_log_debug(NULL, "    interpol  = %s\n",
660                     interpoltypes[td->interpoltype]);
661         mlt_log_debug(NULL, "    sharpen   = %f\n", td->sharpen);
662     }
663
664     if (td->maxshift > td->width_dest/2
665         ) td->maxshift = td->width_dest/2;
666     if (td->maxshift > td->height_dest/2)
667         td->maxshift = td->height_dest/2;
668
669     if (!preprocess_transforms(td)) {
670         mlt_log_error(NULL,"error while preprocessing transforms!");
671         return -1;
672     }
673
674     switch(td->interpoltype){
675       case 0:  interpolate = &interpolateZero; break;
676       case 1:  interpolate = &interpolateLin; break;
677       case 2:  interpolate = &interpolateBiLin; break;
678       case 3:  interpolate = &interpolateSqr; break;
679       case 4:  interpolate = &interpolateBiCub; break;
680       default: interpolate = &interpolateBiLin;
681     }
682
683     return 0;
684 }
685
686
687 /**
688  * transform_filter_video: performs the transformation of frames
689  * See tcmodule-data.h for function details.
690  */
691 int transform_filter_video(TransformData *self,
692                                   unsigned char *frame,mlt_image_format pixelformat)
693 {
694     TransformData *td = self;
695
696
697     td->dest = frame;
698     memcpy(td->src, frame, td->framesize_src);
699     if (td->current_trans >= td->trans_len) {
700         td->current_trans = td->trans_len-1;
701         if(!td->warned_transform_end)
702             mlt_log_warning(NULL,"not enough transforms found, use last transformation!\n");
703         td->warned_transform_end = 1;
704     }
705
706     if (pixelformat == mlt_image_rgb24 ) {
707         transformRGB(td);
708     } else if (pixelformat == mlt_image_yuv420p) {
709         transformYUV(td);
710     } else {
711         mlt_log_error(NULL,"unsupported Codec: %i\n", pixelformat);
712         return 1;
713     }
714     td->current_trans++;
715     return 0;
716 }
717
718