#include "stabilize.h"
#include "transform_image.h"
+typedef struct {
+ StabData* stab;
+ TransformData* trans;
+ int initialized;
+ void* parent;
+} videostab2_data;
-static void serialize_vectors( StabData* self, mlt_position length )
+static void serialize_vectors( videostab2_data* self, mlt_position length )
{
mlt_geometry g = mlt_geometry_init();
-
if ( g )
{
struct mlt_geometry_item_s item;
item.key = item.f[0] = item.f[1] = 1;
item.f[2] = item.f[3] = item.f[4] = 1;
- tlist* transform_data =self->transs;
+ tlist* transform_data =self->stab->transs;
for ( i = 0; i < length; i++ )
{
// Set the geometry item
{
struct mlt_geometry_item_s item;
int i;
- tx=malloc(sizeof(Transform)*length);
- memset(tx,sizeof(Transform)*length,0);
+ tx=calloc(1,sizeof(Transform)*length);
// Copy the geometry items to a vc array for interp()
for ( i = 0; i < length; i++ )
{
t.extra=0;
tx[i]=t;
}
-
+
}
else
{
static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
{
mlt_filter filter = mlt_frame_pop_service( frame );
- if (*format != mlt_image_yuv420p){
- return 1;
- }
+ char *vectors = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "vectors" );
+ *format = mlt_image_yuv422;
+ if (vectors)
+ *format= mlt_image_rgb24;
mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "consumer_deinterlace", 1 );
int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
if ( !error && *image )
{
- StabData* self = filter->child;
+ videostab2_data* data = filter->child;
+ if ( data==NULL ) { // big error, abort
+ return 1;
+ }
mlt_position length = mlt_filter_get_length2( filter, frame );
int h = *height;
int w = *width;
// Service locks are for concurrency control
mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
- if ( !self->initialized )
- {
- // Initialize our context
- self->initialized = 1;
- self->width=w;
- self->height=h;
- self->framesize=w*h* 3/2;//( mlt_image_format_size ( *format, w,h , 0) ; // 3/2 =1 too small
- self->shakiness = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "shakiness" );
- self->accuracy = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "accuracy" );
- self->stepsize = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "stepsize" );
- self->algo = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "algo" );
- self->show = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "show" );
- self->contrast_threshold = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter) , "mincontrast" );
- stabilize_configure(self);
- }
- char *vectors = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "vectors" );
- if ( !vectors )
- {
- // Analyse
- mlt_position pos = mlt_filter_get_position( filter, frame );
- stabilize_filter_video ( self, *image, *format );
-
- // On last frame
- if ( pos == length - 1 )
+ if ( !vectors) {
+ if ( !data->initialized )
{
- serialize_vectors( self, length );
+ // Initialize our context
+ data->initialized = 1;
+ data->stab->width=w;
+ data->stab->height=h;
+ if (*format==mlt_image_yuv420p) data->stab->framesize=w*h* 3/2;//( mlt_image_format_size ( *format, w,h , 0) ; // 3/2 =1 too small
+ if (*format==mlt_image_yuv422) data->stab->framesize=w*h;
+ data->stab->shakiness = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "shakiness" );
+ data->stab->accuracy = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "accuracy" );
+ data->stab->stepsize = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "stepsize" );
+ data->stab->algo = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "algo" );
+ data->stab->show = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "show" );
+ data->stab->contrast_threshold = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter) , "mincontrast" );
+ stabilize_configure(data->stab);
}
+ // Analyse
+ mlt_position pos = mlt_filter_get_position( filter, frame );
+ stabilize_filter_video ( data->stab , *image, *format );
+
+ // On last frame
+ if ( pos == length - 1 )
+ {
+ serialize_vectors( data , length );
+ }
}
- if ( vectors )
+ else
{
- // Apply
- TransformData* tf=mlt_properties_get_data( MLT_FILTER_PROPERTIES(filter), "_transformdata", NULL);
- char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" );
-
- if (!tf){
- tf=mlt_pool_alloc(sizeof(TransformData));
- mlt_properties_set_data( MLT_FILTER_PROPERTIES(filter), "_transformdata", tf, 0, ( mlt_destructor )mlt_pool_release, NULL );
- }
- if ( self->initialized != 2 )
+ if ( data->initialized!=1 )
{
- // Load analysis results from property
- self->initialized = 2;
+ char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" );
- int interp = 2;
- if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 )
- interp = 0;
- else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
- interp = 1;
- else if ( strcmp( interps, "bilinear" ) == 0 )
- interp = 2;
- else if ( strcmp( interps, "bicubic" ) == 0 )
- interp = 3;
- else if ( strcmp( interps, "bicublin" ) == 0 )
- interp = 4;
+ if ( data->initialized != 2 )
+ {
+ // Load analysis results from property
+ data->initialized = 2;
- tf->interpoltype = interp;
- tf->smoothing = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "smoothing" );
- tf->maxshift = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "maxshift" );
- tf->maxangle = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "maxangle" );
- tf->crop = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "crop" );
- tf->invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "invert" );
- tf->relative = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "relative" );
- tf->zoom = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "zoom" );
- tf->optzoom = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "optzoom" );
- tf->sharpen = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter), "sharpen" );
+ int interp = 2;
+ if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 )
+ interp = 0;
+ else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
+ interp = 1;
+ else if ( strcmp( interps, "bilinear" ) == 0 )
+ interp = 2;
+ else if ( strcmp( interps, "bicubic" ) == 0 )
+ interp = 3;
+ else if ( strcmp( interps, "bicublin" ) == 0 )
+ interp = 4;
- transform_configure(tf,w,h,*format ,*image, deserialize_vectors( vectors, length ),length);
-
- }
- if ( self->initialized == 2 )
- {
- // Stabilize
- float pos = mlt_filter_get_position( filter, frame );
- tf->current_trans=pos;
- transform_filter_video(tf, *image, *format );
- }
+ data->trans->interpoltype = interp;
+ data->trans->smoothing = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "smoothing" );
+ data->trans->maxshift = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "maxshift" );
+ data->trans->maxangle = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "maxangle" );
+ data->trans->crop = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "crop" );
+ data->trans->invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "invert" );
+ data->trans->relative = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "relative" );
+ data->trans->zoom = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "zoom" );
+ data->trans->optzoom = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "optzoom" );
+ data->trans->sharpen = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter), "sharpen" );
+
+ transform_configure(data->trans,w,h,*format ,*image, deserialize_vectors( vectors, length ),length);
+
+ }
+ if ( data->initialized == 2 )
+ {
+ // Stabilize
+ float pos = mlt_filter_get_position( filter, frame );
+ data->trans->current_trans=pos;
+ transform_filter_video(data->trans, *image, *format );
+
+ }
+ }
}
mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
}
static void filter_close( mlt_filter parent )
{
- StabData* self = parent->child;
- stabilize_stop(self);
- free( self );
+ videostab2_data* data = parent->child;
+ if (data){
+ if (data->stab) stabilize_stop(data->stab);
+ if (data->trans){
+ if (data->trans->src) free(data->trans->src);
+ free (data->trans);
+ }
+ free( data );
+ }
parent->close = NULL;
parent->child = NULL;
}
mlt_filter filter_videostab2_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
{
- StabData* self = calloc( 1, sizeof(StabData) );
- if ( self )
+ videostab2_data* data= calloc( 1, sizeof(videostab2_data));
+ data->stab = calloc( 1, sizeof(StabData) );
+ data->trans = calloc( 1, sizeof (TransformData) ) ;
+ if ( data )
{
mlt_filter parent = mlt_filter_new();
- parent->child = self;
+
+ parent->child = data;
parent->close = filter_close;
parent->process = filter_process;
- self->parent = parent;
+ data->parent = parent;
//properties for stabilize
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "shakiness", "4" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "accuracy", "4" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "stepsize", "6" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "algo", "1" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "mincontrast", "0.3" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "show", "0" );
-
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "shakiness", "4" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "accuracy", "4" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "stepsize", "6" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "algo", "1" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "mincontrast", "0.3" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "show", "0" );
+
//properties for transform
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "smoothing", "10" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "maxshift", "-1" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "maxangle", "-1" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "crop", "0" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "invert", "0" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "relative", "1" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "zoom", "0" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "optzoom", "1" );
- mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "sharpen", "0.8" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "smoothing", "10" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "maxshift", "-1" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "maxangle", "-1" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "crop", "0" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "invert", "0" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "relative", "1" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "zoom", "0" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "optzoom", "1" );
+ mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "sharpen", "0.8" );
return parent;
}
return NULL;
* mlt adaption by Marco Gittler marco at gitma dot de 2011
*
* This file is part of transcode, a video stream processing tool
- *
+ *
* transcode is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
- *
+ *
* transcode is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* Typical call:
- * transcode -V -J stabilize=shakiness=5:show=1,preview
+ * transcode -V -J stabilize=shakiness=5:show=1,preview
* -i inp.mpeg -y null,null -o dummy
* all parameters are optional
*/
TC_MODULE_FLAG_RECONFIGURABLE | TC_MODULE_FLAG_DELAY
#define MAX(a,b) ((a < b) ? (b) : (a))
#define MIN(a,b) ((a < b) ? (a) : (b))
-#include "stabilize.h"
+#include "stabilize.h"
#include <stdlib.h>
#include <string.h>
#include <framework/mlt_types.h>
/** initialise measurement fields on the frame.
- The size of the fields and the maxshift is used to
+ The size of the fields and the maxshift is used to
calculate an optimal distribution in the frame.
*/
int initFields(StabData* sd)
// make sure that the remaining rows have the same length
sd->field_num = rows*cols;
sd->field_rows = rows;
- mlt_log_debug (NULL,"field setup: rows: %i cols: %i Total: %i fields",
+ mlt_log_debug (NULL,"field setup: rows: %i cols: %i Total: %i fields",
rows, cols, sd->field_num);
if (!(sd->fields = malloc(sizeof(Field) * sd->field_num))) {
\param d_x shift in x direction
\param d_y shift in y direction
*/
-double compareImg(unsigned char* I1, unsigned char* I2,
+double compareImg(unsigned char* I1, unsigned char* I2,
int width, int height, int bytesPerPixel, int d_x, int d_y)
{
int i, j;
unsigned char* p1 = NULL;
unsigned char* p2 = NULL;
- long int sum = 0;
+ long int sum = 0;
int effectWidth = width - abs(d_x);
int effectHeight = height - abs(d_y);
for (i = 0; i < effectHeight; i++) {
p1 = I1;
p2 = I2;
- if (d_y > 0 ){
+ if (d_y > 0 ){
p1 += (i + d_y) * width * bytesPerPixel;
p2 += i * width * bytesPerPixel;
} else {
p1 += i * width * bytesPerPixel;
p2 += (i - d_y) * width * bytesPerPixel;
}
- if (d_x > 0) {
+ if (d_x > 0) {
p1 += d_x * bytesPerPixel;
} else {
- p2 -= d_x * bytesPerPixel;
+ p2 -= d_x * bytesPerPixel;
}
#ifdef USE_SSE2
__m128i A,B,C,D,E;
for (j = 0; j < effectWidth * bytesPerPixel - 16; j++) {
#else
for (j = 0; j < effectWidth * bytesPerPixel; j++) {
-#endif
+#endif
/* fwrite(p1,1,1,pic1);fwrite(p1,1,1,pic1);fwrite(p1,1,1,pic1);
- fwrite(p2,1,1,pic2);fwrite(p2,1,1,pic2);fwrite(p2,1,1,pic2);
+ fwrite(p2,1,1,pic2);fwrite(p2,1,1,pic2);fwrite(p2,1,1,pic2);
*/
#ifdef USE_SSE2
A= _mm_loadu_si128((__m128i*)p1); //load unaligned data
D = _mm_srli_si128(C, 8); // shift first 64 byte value to align at the same as C
E = _mm_add_epi32(C, D); // add the 2 values (sum of all diffs)
sum+= _mm_cvtsi128_si32(E); //convert _m128i to int
-
+
p1+=16;
- p2+=16;
+ p2+=16;
j+=15;
#else
sum += abs((int)*p1 - (int)*p2);
p1++;
- p2++;
+ p2++;
#endif
}
}
/* fclose(pic1);
- fclose(pic2);
+ fclose(pic2);
*/
return sum/((double) effectWidth * effectHeight * bytesPerPixel);
}
/**
- compares a small part of two given images
+ compares a small part of two given images
and returns the average absolute difference.
- Field center, size and shift have to be choosen,
+ Field center, size and shift have to be choosen,
so that no clipping is required
-
- \param field Field specifies position(center) and size of subimage
+
+ \param field Field specifies position(center) and size of subimage
\param d_x shift in x direction
- \param d_y shift in y direction
+ \param d_y shift in y direction
*/
-double compareSubImg(unsigned char* const I1, unsigned char* const I2,
- const Field* field,
+double compareSubImg(unsigned char* const I1, unsigned char* const I2,
+ const Field* field,
int width, int height, int bytesPerPixel, int d_x, int d_y)
{
int k, j;
// TODO: use some mmx or sse stuff here
#ifdef USE_SSE2
__m128i A,B,C,D,E;
-#endif
+#endif
for (j = 0; j < field->size; j++){
#ifdef USE_SSE2
for (k = 0; k < (field->size * bytesPerPixel) - 16 ; k++) {
D = _mm_srli_si128(C, 8); // shift value 8 byte right
E = _mm_add_epi32(C, D); // add the 2 values (sum of all diffs)
sum+= _mm_cvtsi128_si32(E); //convert _m128i to int
-
+
p1+=16;
- p2+=16;
+ p2+=16;
k+=15;
#else
for (k = 0; k < (field->size * bytesPerPixel); k++) {
sum += abs((int)*p1 - (int)*p2);
p1++;
- p2++;
+ p2++;
#endif
}
p1 += ((width - field->size) * bytesPerPixel);
return contrastSubImg(sd->curr,field,sd->width,sd->height,1);
}
-/**
- \see contrastSubImg three times called with bytesPerPixel=3
- for all channels
+/**
+ \see contrastSubImg three times called with bytesPerPixel=3
+ for all channels
*/
double contrastSubImgRGB(StabData* sd, const Field* field){
unsigned char* const I = sd->curr;
- return ( contrastSubImg(I, field,sd->width,sd->height,3)
+ return ( contrastSubImg(I, field,sd->width,sd->height,3)
+ contrastSubImg(I+1,field,sd->width,sd->height,3)
+ contrastSubImg(I+2,field,sd->width,sd->height,3))/3;
}
/**
calculates Michelson-contrast in the given small part of the given image
-
- \param I pointer to framebuffer
- \param field Field specifies position(center) and size of subimage
+
+ \param I pointer to framebuffer
+ \param field Field specifies position(center) and size of subimage
\param width width of frame
\param height height of frame
\param bytesPerPixel calc contrast for only for first channel
*/
-double contrastSubImg(unsigned char* const I, const Field* field,
+double contrastSubImg(unsigned char* const I, const Field* field,
int width, int height, int bytesPerPixel)
{
#if USE_SSE2
{
int x = 0, y = 0;
int i, j;
- double minerror = 1e20;
+ double minerror = 1e20;
for (i = -sd->maxshift; i <= sd->maxshift; i++) {
for (j = -sd->maxshift; j <= sd->maxshift; j++) {
- double error = compareImg(sd->curr, sd->prev,
+ double error = compareImg(sd->curr, sd->prev,
sd->width, sd->height, 3, i, j);
if (error < minerror) {
minerror = error;
x = i;
y = j;
- }
+ }
}
- }
+ }
return new_transform(x, y, 0, 0, 0);
}
-/** tries to register current frame onto previous frame.
+/** tries to register current frame onto previous frame.
(only the luminance is used)
This is the most simple algorithm:
shift images to all possible positions and calc summed error
#endif
// we only use the luminance part of the image
- Y_c = sd->curr;
+ Y_c = sd->curr;
// Cb_c = sd->curr + sd->width*sd->height;
//Cr_c = sd->curr + 5*sd->width*sd->height/4;
- Y_p = sd->prev;
+ Y_p = sd->prev;
//Cb_p = sd->prev + sd->width*sd->height;
//Cr_p = sd->prev + 5*sd->width*sd->height/4;
- double minerror = 1e20;
+ double minerror = 1e20;
for (i = -sd->maxshift; i <= sd->maxshift; i++) {
for (j = -sd->maxshift; j <= sd->maxshift; j++) {
- double error = compareImg(Y_c, Y_p,
+ double error = compareImg(Y_c, Y_p,
sd->width, sd->height, 1, i, j);
#ifdef STABVERBOSE
fprintf(f, "%i %i %f\n", i, j, error);
minerror = error;
x = i;
y = j;
- }
+ }
}
- }
+ }
#ifdef STABVERBOSE
fclose(f);
tc_log_msg(MOD_NAME, "Minerror: %f\n", minerror);
-/* calculates rotation angle for the given transform and
+/* calculates rotation angle for the given transform and
* field with respect to the given center-point
*/
-double calcAngle(StabData* sd, Field* field, Transform* t,
+double calcAngle(StabData* sd, Field* field, Transform* t,
int center_x, int center_y)
{
- // we better ignore fields that are to close to the rotation center
+ // we better ignore fields that are to close to the rotation center
if (abs(field->x - center_x) + abs(field->y - center_y) < sd->maxshift) {
return 0;
} else {
- // double r = sqrt(field->x*field->x + field->y*field->y);
+ // double r = sqrt(field->x*field->x + field->y*field->y);
double a1 = atan2(field->y - center_y, field->x - center_x);
- double a2 = atan2(field->y - center_y + t->y,
+ double a2 = atan2(field->y - center_y + t->y,
field->x - center_x + t->x);
double diff = a2 - a1;
- return (diff>M_PI) ? diff - 2*M_PI
- : ( (diff<-M_PI) ? diff + 2*M_PI : diff);
+ return (diff>M_PI) ? diff - 2*M_PI
+ : ( (diff<-M_PI) ? diff + 2*M_PI : diff);
}
}
/* return t; */
/* } */
#ifdef STABVERBOSE
- // printf("%i %i %f\n", sd->t, fieldnum, contr);
+ // printf("%i %i %f\n", sd->t, fieldnum, contr);
FILE *f = NULL;
char buffer[32];
snprintf(buffer, sizeof(buffer), "f%04i_%02i.dat", sd->t, fieldnum);
f = fopen(buffer, "w");
fprintf(f, "# splot \"%s\"\n", buffer);
-#endif
+#endif
- double minerror = 1e10;
+ double minerror = 1e10;
double error = 1e10;
for (i = -sd->maxshift; i <= sd->maxshift; i += sd->stepsize) {
for (j = -sd->maxshift; j <= sd->maxshift; j += sd->stepsize) {
- error = compareSubImg(Y_c, Y_p, field,
+ error = compareSubImg(Y_c, Y_p, field,
sd->width, sd->height, 1, i, j);
#ifdef STABVERBOSE
fprintf(f, "%i %i %f\n", i, j, error);
-#endif
+#endif
if (error < minerror) {
minerror = error;
t.x = i;
t.y = j;
- }
+ }
}
}
int r = sd->stepsize - 1;
for (i = t.x - r; i <= t.x + r; i += 1) {
for (j = -t.y - r; j <= t.y + r; j += 1) {
- if (i == t.x && j == t.y)
+ if (i == t.x && j == t.y)
continue; //no need to check this since already done
- error = compareSubImg(Y_c, Y_p, field,
+ error = compareSubImg(Y_c, Y_p, field,
sd->width, sd->height, 1, i, j);
#ifdef STABVERBOSE
fprintf(f, "%i %i %f\n", i, j, error);
-#endif
+#endif
if (error < minerror){
minerror = error;
t.x = i;
t.y = j;
- }
+ }
}
}
}
-#ifdef STABVERBOSE
- fclose(f);
+#ifdef STABVERBOSE
+ fclose(f);
mlt_log_debug ( "Minerror: %f\n", minerror);
#endif
if (!sd->allowmax && fabs(t.x) == sd->maxshift) {
-#ifdef STABVERBOSE
+#ifdef STABVERBOSE
mlt_log_debug ( "maximal x shift ");
#endif
t.x = 0;
}
if (!sd->allowmax && fabs(t.y) == sd->maxshift) {
-#ifdef STABVERBOSE
+#ifdef STABVERBOSE
mlt_log_debug ("maximal y shift ");
#endif
t.y = 0;
return t;
}
-/* calculates the optimal transformation for one field in RGB
+/* calculates the optimal transformation for one field in RGB
* slower than the YUV version because it uses all three color channels
*/
Transform calcFieldTransRGB(StabData* sd, const Field* field, int fieldnum)
Transform t = null_transform();
unsigned char *I_c = sd->curr, *I_p = sd->prev;
int i, j;
-
- double minerror = 1e20;
+
+ double minerror = 1e20;
for (i = -sd->maxshift; i <= sd->maxshift; i += 2) {
- for (j=-sd->maxshift; j <= sd->maxshift; j += 2) {
- double error = compareSubImg(I_c, I_p, field,
+ for (j=-sd->maxshift; j <= sd->maxshift; j += 2) {
+ double error = compareSubImg(I_c, I_p, field,
sd->width, sd->height, 3, i, j);
if (error < minerror) {
minerror = error;
t.x = i;
t.y = j;
- }
+ }
}
}
for (i = t.x - 1; i <= t.x + 1; i += 2) {
for (j = -t.y - 1; j <= t.y + 1; j += 2) {
- double error = compareSubImg(I_c, I_p, field,
+ double error = compareSubImg(I_c, I_p, field,
sd->width, sd->height, 3, i, j);
if (error < minerror) {
minerror = error;
t.x = i;
t.y = j;
- }
+ }
}
}
if (!sd->allowmax && fabs(t.x) == sd->maxshift) {
return t;
}
-/* compares contrast_idx structures respect to the contrast
- (for sort function)
+/* compares contrast_idx structures respect to the contrast
+ (for sort function)
*/
int cmp_contrast_idx(const void *ci1, const void* ci2)
{
int i,j;
tlist* goodflds = tlist_new(0);
contrast_idx *ci = malloc(sizeof(contrast_idx) * sd->field_num);
-
+
// we split all fields into row+1 segments and take from each segment
// the best fields
int numsegms = (sd->field_rows+1);
contrast_idx *ci_segms = malloc(sizeof(contrast_idx) * sd->field_num);
int remaining = 0;
// calculate contrast for each field
- for (i = 0; i < sd->field_num; i++) {
+ for (i = 0; i < sd->field_num; i++) {
ci[i].contrast = contrastfunc(sd, &sd->fields[i]);
ci[i].index=i;
if(ci[i].contrast < sd->contrast_threshold) ci[i].contrast = 0;
// else printf("%i %lf\n", ci[i].index, ci[i].contrast);
- }
+ }
memcpy(ci_segms, ci, sizeof(contrast_idx) * sd->field_num);
// get best fields from each segment
//printf("Segment: %i: %i-%i\n", i, startindex, endindex);
// sort within segment
- qsort(ci_segms+startindex, endindex-startindex,
- sizeof(contrast_idx), cmp_contrast_idx);
+ qsort(ci_segms+startindex, endindex-startindex,
+ sizeof(contrast_idx), cmp_contrast_idx);
// take maxfields/numsegms
for(j=0; j<sd->maxfields/numsegms; j++){
if(startindex+j >= endindex) continue;
- // printf("%i %lf\n", ci_segms[startindex+j].index,
+ // printf("%i %lf\n", ci_segms[startindex+j].index,
// ci_segms[startindex+j].contrast);
- if(ci_segms[startindex+j].contrast > 0){
+ if(ci_segms[startindex+j].contrast > 0){
tlist_append(goodflds, &ci[ci_segms[startindex+j].index],sizeof(contrast_idx));
// don't consider them in the later selection process
- ci_segms[startindex+j].contrast=0;
- }
+ ci_segms[startindex+j].contrast=0;
+ }
}
}
// check whether enough fields are selected
// printf("Phase2: %i\n", tc_list_size(goodflds));
- remaining = sd->maxfields - tlist_size(goodflds);
+ remaining = sd->maxfields - tlist_size(goodflds);
if(remaining > 0){
// take the remaining from the leftovers
- qsort(ci_segms, sd->field_num,
+ qsort(ci_segms, sd->field_num,
sizeof(contrast_idx), cmp_contrast_idx);
for(j=0; j < remaining; j++){
if(ci_segms[j].contrast > 0){
- tlist_append(goodflds, &ci_segms[j], sizeof(contrast_idx));
- }
+ tlist_append(goodflds, &ci_segms[j], sizeof(contrast_idx));
+ }
}
- }
+ }
// printf("Ende: %i\n", tc_list_size(goodflds));
free(ci);
free(ci_segms);
-/* tries to register current frame onto previous frame.
+/* tries to register current frame onto previous frame.
* Algorithm:
* check all fields for vertical and horizontal transformation
* use minimal difference of all possible positions
- * discards fields with low contrast
+ * discards fields with low contrast
* select maxfields field according to their contrast
* calculate shift as cleaned mean of all remaining fields
* calculate rotation angle of each field in respect to center of fields
f = fopen(buffer, "w");
fprintf(f, "# plot \"%s\" w l, \"\" every 2:1:0\n", buffer);
#endif
-
+
tlist* goodflds = selectfields(sd, contrastfunc);
- // use all "good" fields and calculate optimal match to previous frame
+ // use all "good" fields and calculate optimal match to previous frame
contrast_idx* f;
while((f = (contrast_idx*)tlist_pop(goodflds,0) ) != 0){
int i = f->index;
t = fieldfunc(sd, &sd->fields[i], i); // e.g. calcFieldTransYUV
#ifdef STABVERBOSE
- fprintf(f, "%i %i\n%f %f %i\n \n\n", sd->fields[i].x, sd->fields[i].y,
+ fprintf(f, "%i %i\n%f %f %i\n \n\n", sd->fields[i].x, sd->fields[i].y,
sd->fields[i].x + t.x, sd->fields[i].y + t.y, t.extra);
#endif
if (t.extra != -1){ // ignore if extra == -1 (unused at the moment)
tlist_fini(goodflds);
t = null_transform();
- num_trans = index; // amount of transforms we actually have
+ num_trans = index; // amount of transforms we actually have
if (num_trans < 1) {
printf( "too low contrast! No field remains.\n \
(no translations are detected in frame %i)", sd->t);
return t;
}
-
+
int center_x = 0;
int center_y = 0;
// calc center point of all remaining fields
for (i = 0; i < num_trans; i++) {
center_x += fs[i]->x;
- center_y += fs[i]->y;
- }
+ center_y += fs[i]->y;
+ }
center_x /= num_trans;
- center_y /= num_trans;
-
+ center_y /= num_trans;
+
if (sd->show){ // draw fields and transforms into frame.
- // this has to be done one after another to handle possible overlap
+ // this has to be done one after another to handle possible overlap
if (sd->show > 1) {
for (i = 0; i < num_trans; i++)
- drawFieldScanArea(sd, fs[i], &ts[i]);
+ drawFieldScanArea(sd, fs[i], &ts[i]);
}
for (i = 0; i < num_trans; i++)
- drawField(sd, fs[i], &ts[i]);
+ drawField(sd, fs[i], &ts[i]);
for (i = 0; i < num_trans; i++)
- drawFieldTrans(sd, fs[i], &ts[i]);
- }
+ drawFieldTrans(sd, fs[i], &ts[i]);
+ }
/* median over all transforms
t= median_xy_transform(ts, sd->field_num);*/
- // cleaned mean
+ // cleaned mean
t = cleanmean_xy_transform(ts, num_trans);
// substract avg
// figure out angle
if (sd->field_num < 6) {
// the angle calculation is inaccurate for 5 and less fields
- t.alpha = 0;
- } else {
+ t.alpha = 0;
+ } else {
for (i = 0; i < num_trans; i++) {
angles[i] = calcAngle(sd, fs[i], &ts[i], center_x, center_y);
}
t.alpha = -cleanmean(angles, num_trans, &min, &max);
if(max-min>sd->maxanglevariation){
t.alpha=0;
- printf( "too large variation in angle(%f)\n",
+ printf( "too large variation in angle(%f)\n",
max-min);
}
}
double p_x = (center_x - sd->width/2);
double p_y = (center_y - sd->height/2);
t.x += (cos(t.alpha)-1)*p_x - sin(t.alpha)*p_y;
- t.y += sin(t.alpha)*p_x + (cos(t.alpha)-1)*p_y;
-
+ t.y += sin(t.alpha)*p_x + (cos(t.alpha)-1)*p_y;
+
#ifdef STABVERBOSE
fclose(f);
#endif
mlt_log_warning (NULL, "format not usable\n");
return;
}
- drawBox(sd->curr, sd->width, sd->height, 1, field->x, field->y,
- field->size+2*sd->maxshift, field->size+2*sd->maxshift, 80);
+ drawBox(sd->curr, sd->width, sd->height, 1, field->x, field->y,
+ field->size+2*sd->maxshift, field->size+2*sd->maxshift, 80);
}
/** draws the field */
mlt_log_warning (NULL, "format not usable\n");
return;
}
- drawBox(sd->curr, sd->width, sd->height, 1, field->x, field->y,
+ drawBox(sd->curr, sd->width, sd->height, 1, field->x, field->y,
field->size, field->size, t->extra == -1 ? 100 : 40);
}
mlt_log_warning (NULL, "format not usable\n");
return;
}
- drawBox(sd->curr, sd->width, sd->height, 1,
+ drawBox(sd->curr, sd->width, sd->height, 1,
field->x, field->y, 5, 5, 128); // draw center
- drawBox(sd->curr, sd->width, sd->height, 1,
+ drawBox(sd->curr, sd->width, sd->height, 1,
field->x + t->x, field->y + t->y, 8, 8, 250); // draw translation
}
/**
* draws a box at the given position x,y (center) in the given color
- (the same for all channels)
+ (the same for all channels)
*/
-void drawBox(unsigned char* I, int width, int height, int bytesPerPixel,
+void drawBox(unsigned char* I, int width, int height, int bytesPerPixel,
int x, int y, int sizex, int sizey, unsigned char color){
-
- unsigned char* p = NULL;
+
+ unsigned char* p = NULL;
int j,k;
p = I + ((x - sizex/2) + (y - sizey/2)*width)*bytesPerPixel;
for (j = 0; j < sizey; j++){
int stabilize_init(StabData* instance)
{
- instance = malloc(sizeof(StabData)); // allocation with zero values
- memset(instance,sizeof(StabData),0);
+ instance = calloc(1,sizeof(StabData)); // allocation with zero values
if (!instance) {
return -1;
}
/*TCModuleExtraData *xdata[]*/)
{
StabData *sd = instance;
- /* sd->framesize = sd->vob->im_v_width * MAX_PLANES *
+ /* sd->framesize = sd->vob->im_v_width * MAX_PLANES *
sizeof(char) * 2 * sd->vob->im_v_height * 2; */
/*TODO sd->framesize = sd->vob->im_v_size; */
- sd->prev = calloc(1,sd->framesize);
- if (!sd->prev) {
+ sd->prev = calloc(1,sd->framesize);
+ sd->grayimage = calloc(1,sd->width*sd->height);
+
+ if (!sd->prev || !sd->grayimage) {
printf( "malloc failed");
return -1;
}
sd->allowmax = 0;
sd->field_size = MIN(sd->width, sd->height)/12;
sd->maxanglevariation = 1;
-
+
sd->shakiness = MIN(10,MAX(1,sd->shakiness));
sd->accuracy = MAX(sd->shakiness,MIN(15,MAX(1,sd->accuracy)));
if (1) {
// shift and size: shakiness 1: height/40; 10: height/4
sd->maxshift = MIN(sd->width, sd->height)*sd->shakiness/40;
sd->field_size = MIN(sd->width, sd->height)*sd->shakiness/40;
-
- mlt_log_debug ( NULL, "Fieldsize: %i, Maximal translation: %i pixel\n",
+
+ mlt_log_debug ( NULL, "Fieldsize: %i, Maximal translation: %i pixel\n",
sd->field_size, sd->maxshift);
- if (sd->algo==1) {
+ if (sd->algo==1) {
// initialize measurement fields. field_num is set here.
if (!initFields(sd)) {
return -1;
sd->maxfields, sd->field_num);
}
if (sd->show){
- sd->currcopy = malloc(sd->framesize);
- memset ( sd->currcopy, sd->framesize, 0 );
+ sd->currcopy = calloc(1,sd->framesize);
}
/* load unsharp filter to smooth the frames. This allows larger stepsize.*/
char unsharp_param[128];
int masksize = MIN(13,sd->stepsize*1.8); // only works up to 13.
- sprintf(unsharp_param,"luma=-1:luma_matrix=%ix%i:pre=1",
+ sprintf(unsharp_param,"luma=-1:luma_matrix=%ix%i:pre=1",
masksize, masksize);
return 0;
}
* See tcmodule-data.h for function details.
*/
-int stabilize_filter_video(StabData* instance,
+int stabilize_filter_video(StabData* instance,
unsigned char *frame,mlt_image_format pixelformat)
{
StabData *sd = instance;
- sd->pixelformat=pixelformat;
-
- if(sd->show) // save the buffer to restore at the end for prev
- memcpy(sd->currcopy, frame, sd->framesize);
+ sd->pixelformat=pixelformat;
+ int l=sd->width*sd->height;
+ unsigned char* tmpgray=sd->grayimage;
+ if (pixelformat == mlt_image_yuv422){
+ while(l--){
+ *tmpgray++=*frame++;
+ frame++;
+ };
+ }
+
+ if(sd->show) { // save the buffer to restore at the end for prev
+ if (pixelformat == mlt_image_yuv420p){
+ memcpy(sd->currcopy, sd->grayimage, sd->framesize);
+ }
+ }
if (sd->hasSeenOneFrame) {
- sd->curr = frame;
+ sd->curr = sd->grayimage;
if (pixelformat == mlt_image_rgb24) {
if (sd->algo == 0)
addTrans(sd, calcShiftRGBSimple(sd));
else if (sd->algo == 1)
addTrans(sd, calcTransFields(sd, calcFieldTransYUV,
contrastSubImgYUV));
+ } else if (pixelformat == mlt_image_yuv422 ) {
+ if (sd->algo == 0)
+ addTrans(sd, calcShiftYUVSimple(sd));
+ else if (sd->algo == 1)
+ addTrans(sd, calcTransFields(sd, calcFieldTransYUV,
+ contrastSubImgYUV));
} else {
mlt_log_warning (NULL,"unsupported Codec: %i\n",
pixelformat);
sd->hasSeenOneFrame = 1;
addTrans(sd, null_transform());
}
-
+
if(!sd->show) { // copy current frame to prev for next frame comparison
- memcpy(sd->prev, frame, sd->framesize);
+ memcpy(sd->prev, sd->grayimage, sd->framesize);
} else { // use the copy because we changed the original frame
memcpy(sd->prev, sd->currcopy, sd->framesize);
}
free(sd->prev);
sd->prev = NULL;
}
+ if (sd->grayimage){
+ free(sd->grayimage);
+ sd->grayimage=NULL;
+
+ }
return 0;
}
unsigned char* curr; // current frame buffer (only pointer)
unsigned char* currcopy; // copy of the current frame needed for drawing
unsigned char* prev; // frame buffer for last frame (copied)
+ unsigned char* grayimage; // frame buffer for last frame (copied)
short hasSeenOneFrame; // true if we have a valid previous frame
int width, height;
/* list of transforms*/
//TCList* transs;
- void* parent;
tlist* transs;
Field* fields;
int t;
char conf_str[1024];
- int initialized;
} StabData;
/* type for a function that calculates the transformation of a certain field
* filter_transform.c
*
* Copyright (C) Georg Martius - June 2007
- * georg dot martius at web dot de
+ * georg dot martius at web dot de
*
* This file is part of transcode, a video stream processing tool
- *
+ *
* transcode is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
- *
+ *
* transcode is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Typical call:
* transcode -J transform -i inp.mpeg -y xdiv,tcaud inp_stab.avi
static const char* interpoltypes[5] = {"No (0)", "Linear (1)", "Bi-Linear (2)",
"Quadratic (3)", "Bi-Cubic (4)"};
-void (*interpolate)(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height,
- unsigned char def) = 0;
+void (*interpolate)(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height,
+ unsigned char def,unsigned char N, unsigned char channel) = 0;
/** interpolateBiLinBorder: bi-linear interpolation function that also works at the border.
This is used by many other interpolation methods at and outsize the border, see interpolate */
-void interpolateBiLinBorder(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height,
- unsigned char def)
+void interpolateBiLinBorder(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height,
+ unsigned char def,unsigned char N, unsigned char channel)
{
int x_f = myfloor(x);
int x_c = x_f+1;
int y_f = myfloor(y);
int y_c = y_f+1;
- short v1 = PIXEL(img, x_c, y_c, width, height, def);
- short v2 = PIXEL(img, x_c, y_f, width, height, def);
- short v3 = PIXEL(img, x_f, y_c, width, height, def);
- short v4 = PIXEL(img, x_f, y_f, width, height, def);
- float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
+ short v1 = PIXELN(img, x_c, y_c, width, height, N, channel, def);
+ short v2 = PIXELN(img, x_c, y_f, width, height, N, channel, def);
+ short v3 = PIXELN(img, x_f, y_c, width, height, N, channel, def);
+ short v4 = PIXELN(img, x_f, y_f, width, height, N, channel, def);
+ float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
(v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
*rv = (unsigned char)s;
}
/* taken from http://en.wikipedia.org/wiki/Bicubic_interpolation for alpha=-0.5
- in matrix notation:
+ in matrix notation:
a0-a3 are the neigthboring points where the target point is between a1 and a2
t is the point of interpolation (position between a1 and a2) value between 0 and 1
| 0, 2, 0, 0 | |a0|
|-1, 0, 1, 0 | |a1|
(1,t,t^2,t^3) | 2,-5, 4,-1 | |a2|
- |-1, 3,-3, 1 | |a3|
+ |-1, 3,-3, 1 | |a3|
*/
-static short bicub_kernel(float t, short a0, short a1, short a2, short a3){
+static short bicub_kernel(float t, short a0, short a1, short a2, short a3){
return (2*a1 + t*((-a0+a2) + t*((2*a0-5*a1+4*a2-a3) + t*(-a0+3*a1-3*a2+a3) )) ) / 2;
}
/** interpolateBiCub: bi-cubic interpolation function using 4x4 pixel, see interpolate */
-void interpolateBiCub(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height, unsigned char def)
+void interpolateBiCub(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel)
{
// do a simple linear interpolation at the border
- if (x < 1 || x > width-2 || y < 1 || y > height - 2) {
- interpolateBiLinBorder(rv, x,y,img,width,height,def);
+ if (x < 1 || x > width-2 || y < 1 || y > height - 2) {
+ interpolateBiLinBorder(rv, x,y,img,width,height,def,N,channel);
} else {
int x_f = myfloor(x);
int y_f = myfloor(y);
float tx = x-x_f;
short v1 = bicub_kernel(tx,
- PIX(img, x_f-1, y_f-1, width, height),
- PIX(img, x_f, y_f-1, width, height),
- PIX(img, x_f+1, y_f-1, width, height),
- PIX(img, x_f+2, y_f-1, width, height));
+ PIXN(img, x_f-1, y_f-1, width, height, N, channel),
+ PIXN(img, x_f, y_f-1, width, height, N, channel),
+ PIXN(img, x_f+1, y_f-1, width, height, N, channel),
+ PIXN(img, x_f+2, y_f-1, width, height, N, channel));
short v2 = bicub_kernel(tx,
- PIX(img, x_f-1, y_f, width, height),
- PIX(img, x_f, y_f, width, height),
- PIX(img, x_f+1, y_f, width, height),
- PIX(img, x_f+2, y_f, width, height));
+ PIXN(img, x_f-1, y_f, width, height, N, channel),
+ PIXN(img, x_f, y_f, width, height, N, channel),
+ PIXN(img, x_f+1, y_f, width, height, N, channel),
+ PIXN(img, x_f+2, y_f, width, height, N, channel));
short v3 = bicub_kernel(tx,
- PIX(img, x_f-1, y_f+1, width, height),
- PIX(img, x_f, y_f+1, width, height),
- PIX(img, x_f+1, y_f+1, width, height),
- PIX(img, x_f+2, y_f+1, width, height));
+ PIXN(img, x_f-1, y_f+1, width, height, N, channel),
+ PIXN(img, x_f, y_f+1, width, height, N, channel),
+ PIXN(img, x_f+1, y_f+1, width, height, N, channel),
+ PIXN(img, x_f+2, y_f+1, width, height, N, channel));
short v4 = bicub_kernel(tx,
- PIX(img, x_f-1, y_f+2, width, height),
- PIX(img, x_f, y_f+2, width, height),
- PIX(img, x_f+1, y_f+2, width, height),
- PIX(img, x_f+2, y_f+2, width, height));
+ PIXN(img, x_f-1, y_f+2, width, height, N, channel),
+ PIXN(img, x_f, y_f+2, width, height, N, channel),
+ PIXN(img, x_f+1, y_f+2, width, height, N, channel),
+ PIXN(img, x_f+2, y_f+2, width, height, N, channel));
*rv = (unsigned char)bicub_kernel(y-y_f, v1, v2, v3, v4);
}
}
/** interpolateSqr: bi-quatratic interpolation function, see interpolate */
-void interpolateSqr(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height, unsigned char def)
+void interpolateSqr(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel)
{
- if (x < 0 || x > width-1 || y < 0 || y > height - 1) {
- interpolateBiLinBorder(rv, x, y, img, width, height, def);
+ if (x < 0 || x > width-1 || y < 0 || y > height - 1) {
+ interpolateBiLinBorder(rv, x, y, img, width, height, def,N,channel);
} else {
int x_f = myfloor(x);
int x_c = x_f+1;
int y_f = myfloor(y);
int y_c = y_f+1;
- short v1 = PIX(img, x_c, y_c, width, height);
- short v2 = PIX(img, x_c, y_f, width, height);
- short v3 = PIX(img, x_f, y_c, width, height);
- short v4 = PIX(img, x_f, y_f, width, height);
+ short v1 = PIXN(img, x_c, y_c, width, height, N, channel);
+ short v2 = PIXN(img, x_c, y_f, width, height, N, channel);
+ short v3 = PIXN(img, x_f, y_c, width, height, N, channel);
+ short v4 = PIXN(img, x_f, y_f, width, height, N, channel);
float f1 = 1 - sqrt((x_c - x) * (y_c - y));
float f2 = 1 - sqrt((x_c - x) * (y - y_f));
float f3 = 1 - sqrt((x - x_f) * (y_c - y));
float f4 = 1 - sqrt((x - x_f) * (y - y_f));
float s = (v1*f1 + v2*f2 + v3*f3+ v4*f4)/(f1 + f2 + f3 + f4);
- *rv = (unsigned char)s;
+ *rv = (unsigned char)s;
}
}
/** interpolateBiLin: bi-linear interpolation function, see interpolate */
-void interpolateBiLin(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height,
- unsigned char def)
+void interpolateBiLin(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height,
+ unsigned char def,unsigned char N, unsigned char channel)
{
- if (x < 0 || x > width-1 || y < 0 || y > height - 1) {
- interpolateBiLinBorder(rv, x, y, img, width, height, def);
+ if (x < 0 || x > width-1 || y < 0 || y > height - 1) {
+ interpolateBiLinBorder(rv, x, y, img, width, height, def,N,channel);
} else {
int x_f = myfloor(x);
int x_c = x_f+1;
int y_f = myfloor(y);
int y_c = y_f+1;
- short v1 = PIX(img, x_c, y_c, width, height);
- short v2 = PIX(img, x_c, y_f, width, height);
- short v3 = PIX(img, x_f, y_c, width, height);
- short v4 = PIX(img, x_f, y_f, width, height);
- float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
+ short v1 = PIXN(img, x_c, y_c, width, height, N, channel);
+ short v2 = PIXN(img, x_c, y_f, width, height, N, channel);
+ short v3 = PIXN(img, x_f, y_c, width, height, N, channel);
+ short v4 = PIXN(img, x_f, y_f, width, height, N, channel);
+ float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
(v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
*rv = (unsigned char)s;
}
/** interpolateLin: linear (only x) interpolation function, see interpolate */
-void interpolateLin(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height,
- unsigned char def)
+void interpolateLin(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height,
+ unsigned char def,unsigned char N, unsigned char channel)
{
int x_f = myfloor(x);
int x_c = x_f+1;
int y_n = myround(y);
- float v1 = PIXEL(img, x_c, y_n, width, height, def);
- float v2 = PIXEL(img, x_f, y_n, width, height, def);
+ float v1 = PIXELN(img, x_c, y_n, width, height, def, N, channel);
+ float v2 = PIXELN(img, x_f, y_n, width, height, def, N, channel);
float s = v1*(x - x_f) + v2*(x_c - x);
*rv = (unsigned char)s;
}
/** interpolateZero: nearest neighbor interpolation function, see interpolate */
-void interpolateZero(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height, unsigned char def)
+void interpolateZero(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel)
{
int x_n = myround(x);
int y_n = myround(y);
- *rv = (unsigned char) PIXEL(img, x_n, y_n, width, height, def);
+ *rv = (unsigned char) PIXELN(img, x_n, y_n, width, height, def,N,channel);
}
-/**
- * interpolateN: Bi-linear interpolation function for N channel image.
+/**
+ * interpolateN: Bi-linear interpolation function for N channel image.
*
* Parameters:
* rv: destination pixel (call by reference)
- * x,y: the source coordinates in the image img. Note this
+ * x,y: the source coordinates in the image img. Note this
* are real-value coordinates, that's why we interpolate
* img: source image
* width,height: dimension of image
* def: default value if coordinates are out of range
* Return value: None
*/
-void interpolateN(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height,
+void interpolateN(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height,
unsigned char N, unsigned char channel,
unsigned char def)
{
if (x < - 1 || x > width || y < -1 || y > height) {
- *rv = def;
+ *rv = def;
} else {
int x_f = myfloor(x);
int x_c = x_f+1;
short v1 = PIXELN(img, x_c, y_c, width, height, N, channel, def);
short v2 = PIXELN(img, x_c, y_f, width, height, N, channel, def);
short v3 = PIXELN(img, x_f, y_c, width, height, N, channel, def);
- short v4 = PIXELN(img, x_f, y_f, width, height, N, channel, def);
- float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
+ short v4 = PIXELN(img, x_f, y_f, width, height, N, channel, def);
+ float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) +
(v2*(x - x_f) + v4*(x_c - x))*(y_c - y);
- *rv = (unsigned char)s;
+ *rv = (unsigned char)s;
}
}
-/**
+/**
* transformRGB: applies current transformation to frame
* Parameters:
* td: private data structure of this filter
- * Return value:
+ * Return value:
* 0 for failture, 1 for success
* Preconditions:
* The frame must be in RGB format
int x = 0, y = 0, z = 0;
unsigned char *D_1, *D_2;
t = td->trans[td->current_trans];
-
- D_1 = td->src;
- D_2 = td->dest;
+
+ D_1 = td->src;
+ D_2 = td->dest;
+ float zm = 1.0-t.zoom/100;
+ float zcos_a = zm*cos(-t.alpha); // scaled cos
+ float zsin_a = zm*sin(-t.alpha); // scaled sin
float c_s_x = td->width_src/2.0;
float c_s_y = td->height_src/2.0;
float c_d_x = td->width_dest/2.0;
- float c_d_y = td->height_dest/2.0;
+ float c_d_y = td->height_dest/2.0;
/* for each pixel in the destination image we calc the source
- * coordinate and make an interpolation:
- * p_d = c_d + M(p_s - c_s) + t
- * where p are the points, c the center coordinate,
- * _s source and _d destination,
+ * coordinate and make an interpolation:
+ * p_d = c_d + M(p_s - c_s) + t
+ * where p are the points, c the center coordinate,
+ * _s source and _d destination,
* t the translation, and M the rotation matrix
* p_s = M^{-1}(p_d - c_d - t) + c_s
*/
/* All 3 channels */
- if (fabs(t.alpha) > td->rotation_threshhold) {
+ if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) {
for (x = 0; x < td->width_dest; x++) {
for (y = 0; y < td->height_dest; y++) {
float x_d1 = (x - c_d_x);
float y_d1 = (y - c_d_y);
- float x_s = cos(-t.alpha) * x_d1
- + sin(-t.alpha) * y_d1 + c_s_x -t.x;
- float y_s = -sin(-t.alpha) * x_d1
- + cos(-t.alpha) * y_d1 + c_s_y -t.y;
- for (z = 0; z < 3; z++) { // iterate over colors
+ float x_s = zcos_a * x_d1
+ + zsin_a * y_d1 + c_s_x -t.x;
+ float y_s = -zsin_a * x_d1
+ + zcos_a * y_d1 + c_s_y -t.y;
+ for (z = 0; z < 3; z++) { // iterate over colors
unsigned char* dest = &D_2[(x + y * td->width_dest)*3+z];
- interpolateN(dest, x_s, y_s, D_1,
- td->width_src, td->height_src,
+ interpolateN(dest, myfloor(x_s), myfloor(y_s), D_1,
+ td->width_src, td->height_src,
3, z, td->crop ? 16 : *dest);
}
}
}
- }else {
- /* no rotation, just translation
- *(also no interpolation, since no size change (so far)
+ }else {
+ /* no rotation, just translation
+ *(also no interpolation, since no size change (so far)
*/
int round_tx = myround(t.x);
int round_ty = myround(t.y);
for (x = 0; x < td->width_dest; x++) {
for (y = 0; y < td->height_dest; y++) {
for (z = 0; z < 3; z++) { // iterate over colors
- short p = PIXELN(D_1, x - round_tx, y - round_ty,
+ short p = PIXELN(D_1, x - round_tx, y - round_ty,
td->width_src, td->height_src, 3, z, -1);
if (p == -1) {
if (td->crop == 1)
return 1;
}
-/**
+/**
* transformYUV: applies current transformation to frame
*
* Parameters:
* td: private data structure of this filter
- * Return value:
+ * Return value:
* 0 for failture, 1 for success
* Preconditions:
* The frame must be in YUV format
int x = 0, y = 0;
unsigned char *Y_1, *Y_2, *Cb_1, *Cb_2, *Cr_1, *Cr_2;
t = td->trans[td->current_trans];
-
- Y_1 = td->src;
- Y_2 = td->dest;
+
+ Y_1 = td->src;
+ Y_2 = td->dest;
Cb_1 = td->src + td->width_src * td->height_src;
Cb_2 = td->dest + td->width_dest * td->height_dest;
Cr_1 = td->src + 5*td->width_src * td->height_src/4;
float c_s_x = td->width_src/2.0;
float c_s_y = td->height_src/2.0;
float c_d_x = td->width_dest/2.0;
- float c_d_y = td->height_dest/2.0;
-
+ float c_d_y = td->height_dest/2.0;
+
float z = 1.0-t.zoom/100;
float zcos_a = z*cos(-t.alpha); // scaled cos
float zsin_a = z*sin(-t.alpha); // scaled sin
/* for each pixel in the destination image we calc the source
- * coordinate and make an interpolation:
- * p_d = c_d + M(p_s - c_s) + t
- * where p are the points, c the center coordinate,
- * _s source and _d destination,
+ * coordinate and make an interpolation:
+ * p_d = c_d + M(p_s - c_s) + t
+ * where p are the points, c the center coordinate,
+ * _s source and _d destination,
* t the translation, and M the rotation and scaling matrix
* p_s = M^{-1}(p_d - c_d - t) + c_s
*/
for (y = 0; y < td->height_dest; y++) {
float x_d1 = (x - c_d_x);
float y_d1 = (y - c_d_y);
- float x_s = zcos_a * x_d1
+ float x_s = zcos_a * x_d1
+ zsin_a * y_d1 + c_s_x -t.x;
- float y_s = -zsin_a * x_d1
+ float y_s = -zsin_a * x_d1
+ zcos_a * y_d1 + c_s_y -t.y;
unsigned char* dest = &Y_2[x + y * td->width_dest];
- interpolate(dest, x_s, y_s, Y_1,
- td->width_src, td->height_src,
- td->crop ? 16 : *dest);
+ interpolate(dest, x_s, y_s, Y_1,
+ td->width_src, td->height_src,
+ td->crop ? 16 : *dest,1,0);
}
}
- }else {
- /* no rotation, no zooming, just translation
- *(also no interpolation, since no size change)
+ }else {
+ /* no rotation, no zooming, just translation
+ *(also no interpolation, since no size change)
*/
int round_tx = myround(t.x);
int round_ty = myround(t.y);
for (x = 0; x < td->width_dest; x++) {
for (y = 0; y < td->height_dest; y++) {
- short p = PIXEL(Y_1, x - round_tx, y - round_ty,
+ short p = PIXEL(Y_1, x - round_tx, y - round_ty,
td->width_src, td->height_src, -1);
if (p == -1) {
if (td->crop == 1)
for (y = 0; y < hd2; y++) {
float x_d1 = x - (c_d_x)/2;
float y_d1 = y - (c_d_y)/2;
- float x_s = zcos_a * x_d1
+ float x_s = zcos_a * x_d1
+ zsin_a * y_d1 + (c_s_x -t.x)/2;
- float y_s = -zsin_a * x_d1
+ float y_s = -zsin_a * x_d1
+ zcos_a * y_d1 + (c_s_y -t.y)/2;
unsigned char* dest = &Cr_2[x + y * wd2];
- interpolate(dest, x_s, y_s, Cr_1, ws2, hs2,
- td->crop ? 128 : *dest);
+ interpolate(dest, x_s, y_s, Cr_1, ws2, hs2,
+ td->crop ? 128 : *dest,1,0);
dest = &Cb_2[x + y * wd2];
- interpolate(dest, x_s, y_s, Cb_1, ws2, hs2,
- td->crop ? 128 : *dest);
+ interpolate(dest, x_s, y_s, Cb_1, ws2, hs2,
+ td->crop ? 128 : *dest,1,0);
}
}
- } else { // no rotation, no zoom, no interpolation, just translation
+ } else { // no rotation, no zoom, no interpolation, just translation
int round_tx2 = myround(t.x/2.0);
- int round_ty2 = myround(t.y/2.0);
+ int round_ty2 = myround(t.y/2.0);
for (x = 0; x < wd2; x++) {
for (y = 0; y < hd2; y++) {
- short cr = PIXEL(Cr_1, x - round_tx2, y - round_ty2,
+ short cr = PIXEL(Cr_1, x - round_tx2, y - round_ty2,
wd2, hd2, -1);
- short cb = PIXEL(Cb_1, x - round_tx2, y - round_ty2,
+ short cb = PIXEL(Cb_1, x - round_tx2, y - round_ty2,
wd2, hd2, -1);
if (cr == -1) {
- if (td->crop==1) {
+ if (td->crop==1) {
Cr_2[x + y * wd2] = 128;
Cb_2[x + y * wd2] = 128;
}
/**
* preprocess_transforms: does smoothing, relative to absolute conversion,
* and cropping of too large transforms.
- * This is actually the core algorithm for canceling the jiggle in the
+ * This is actually the core algorithm for canceling the jiggle in the
* movie. We perform a low-pass filter in terms of transformation size.
* This enables still camera movement, but in a smooth fasion.
*
*/
int s = td->smoothing * 2 + 1;
Transform null = null_transform();
- /* avg is the average over [-smoothing, smoothing] transforms
+ /* avg is the average over [-smoothing, smoothing] transforms
around the current point */
Transform avg;
- /* avg2 is a sliding average over the filtered signal! (only to past)
+ /* avg2 is a sliding average over the filtered signal! (only to past)
* with smoothing * 10 horizont to kill offsets */
Transform avg2 = null_transform();
double tau = 1.0/(3 * s);
/* initialise sliding sum with hypothetic sum centered around
* -1st element. We have two choices:
- * a) assume the camera is not moving at the beginning
+ * a) assume the camera is not moving at the beginning
* b) assume that the camera moves and we use the first transforms
*/
- Transform s_sum = null;
+ Transform s_sum = null;
for (i = 0; i < td->smoothing; i++){
s_sum = add_transforms(&s_sum, i < td->trans_len ? &ts2[i]:&null);
}
mult_transform(&s_sum, 2); // choice b (comment out for choice a)
for (i = 0; i < td->trans_len; i++) {
- Transform* old = ((i - td->smoothing - 1) < 0)
+ Transform* old = ((i - td->smoothing - 1) < 0)
? &null : &ts2[(i - td->smoothing - 1)];
- Transform* new = ((i + td->smoothing) >= td->trans_len)
+ Transform* new = ((i + td->smoothing) >= td->trans_len)
? &null : &ts2[(i + td->smoothing)];
s_sum = sub_transforms(&s_sum, old);
s_sum = add_transforms(&s_sum, new);
avg = mult_transform(&s_sum, 1.0/s);
- /* lowpass filter:
+ /* lowpass filter:
* meaning high frequency must be transformed away
*/
ts[i] = sub_transforms(&ts2[i], &avg);
ts[i] = sub_transforms(&ts[i], &avg2);
if (0 /*verbose*/ ) {
- mlt_log_warning(NULL,"s_sum: %5lf %5lf %5lf, ts: %5lf, %5lf, %5lf\n",
- s_sum.x, s_sum.y, s_sum.alpha,
+ mlt_log_warning(NULL,"s_sum: %5lf %5lf %5lf, ts: %5lf, %5lf, %5lf\n",
+ s_sum.x, s_sum.y, s_sum.alpha,
ts[i].x, ts[i].y, ts[i].alpha);
- mlt_log_warning(NULL,
- " avg: %5lf, %5lf, %5lf avg2: %5lf, %5lf, %5lf",
- avg.x, avg.y, avg.alpha,
- avg2.x, avg2.y, avg2.alpha);
+ mlt_log_warning(NULL,
+ " avg: %5lf, %5lf, %5lf avg2: %5lf, %5lf, %5lf",
+ avg.x, avg.y, avg.alpha,
+ avg2.x, avg2.y, avg2.alpha);
}
}
free(ts2);
}
-
-
+
+
/* invert? */
if (td->invert) {
for (i = 0; i < td->trans_len; i++) {
- ts[i] = mult_transform(&ts[i], -1);
+ ts[i] = mult_transform(&ts[i], -1);
}
}
-
+
/* relative to absolute */
if (td->relative) {
Transform t = ts[0];
for (i = 1; i < td->trans_len; i++) {
if (0/*verbose*/ ) {
- mlt_log_warning(NULL, "shift: %5lf %5lf %lf \n",
+ mlt_log_warning(NULL, "shift: %5lf %5lf %lf \n",
t.x, t.y, t.alpha *180/M_PI);
}
- ts[i] = add_transforms(&ts[i], &t);
+ ts[i] = add_transforms(&ts[i], &t);
t = ts[i];
}
}
for (i = 0; i < td->trans_len; i++)
ts[i].alpha = TC_CLAMP(ts[i].alpha, -td->maxangle, td->maxangle);
- /* Calc optimal zoom
+ /* Calc optimal zoom
* cheap algo is to only consider transformations
- * uses cleaned max and min
+ * uses cleaned max and min
*/
- if (td->optzoom != 0 && td->trans_len > 1){
+ if (td->optzoom != 0 && td->trans_len > 1){
Transform min_t, max_t;
- cleanmaxmin_xy_transform(ts, td->trans_len, 10, &min_t, &max_t);
+ cleanmaxmin_xy_transform(ts, td->trans_len, 10, &min_t, &max_t);
// the zoom value only for x
double zx = 2*TC_MAX(max_t.x,fabs(min_t.x))/td->width_src;
// the zoom value only for y
td->zoom += 100* TC_MAX(zx,zy); // use maximum
mlt_log_debug(NULL,"Final zoom: %lf\n", td->zoom);
}
-
+
/* apply global zoom */
if (td->zoom != 0){
for (i = 0; i < td->trans_len; i++)
- ts[i].zoom += td->zoom;
+ ts[i].zoom += td->zoom;
}
return 1;
TransformData* td = NULL;
TC_MODULE_SELF_CHECK(self, "init");
TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
-
+
td = tc_zalloc(sizeof(TransformData));
if (td == NULL) {
tc_log_error(MOD_NAME, "init: out of memory!");
* transform_configure: Configure this instance of the module. See
* tcmodule-data.h for function details.
*/
-int transform_configure(TransformData *self,int width,int height, mlt_image_format pixelformat, unsigned char* image ,Transform* tx,int trans_len)
+int transform_configure(TransformData *self,int width,int height, mlt_image_format pixelformat, unsigned char* image ,Transform* tx,int trans_len)
{
TransformData *td = self;
/**** Initialise private data structure */
/* td->framesize = td->vob->im_v_width *
- * MAX_PLANES * sizeof(char) * 2 * td->vob->im_v_height * 2;
+ * MAX_PLANES * sizeof(char) * 2 * td->vob->im_v_height * 2;
*/
// rgb24 = w*h*3 , yuv420p = w* h* 3/2
- td->framesize_src = width*height*(pixelformat==mlt_image_rgb24 ? 3 : (3.0/2.0));
+ td->framesize_src = width*height*(pixelformat==mlt_image_rgb24 ? 3 : (3.0/2.0));
td->src = malloc(td->framesize_src); /* FIXME */
if (td->src == NULL) {
mlt_log_error(NULL,"tc_malloc failed\n");
return -1;
}
-
+
td->width_src = width;
td->height_src = height;
-
+
/* Todo: in case we can scale the images, calc new size later */
td->width_dest = width;
td->height_dest = height;
td->framesize_dest = td->framesize_src;
td->dest = 0;
-
+
td->trans = tx;
td->trans_len = trans_len;
td->current_trans = 0;
- td->warned_transform_end = 0;
+ td->warned_transform_end = 0;
/* Options */
// set from filter td->maxshift = -1;
// set from filter td->maxangle = -1;
-
+
// set from filter td->crop = 0;
// set from filter td->relative = 1;
// set from filter td->invert = 0;
// set from filter td->smoothing = 10;
-
+
td->rotation_threshhold = 0.25/(180/M_PI);
// set from filter td->zoom = 0;
// set from filter td->optzoom = 1;
// set from filter td->interpoltype = 2; // bi-linear
// set from filter td->sharpen = 0.8;
-
+
td->interpoltype = TC_MIN(td->interpoltype,4);
if (1) {
mlt_log_debug(NULL, "Image Transformation/Stabilization Settings:\n");
mlt_log_debug(NULL, " smoothing = %d\n", td->smoothing);
mlt_log_debug(NULL, " maxshift = %d\n", td->maxshift);
mlt_log_debug(NULL, " maxangle = %f\n", td->maxangle);
- mlt_log_debug(NULL, " crop = %s\n",
+ mlt_log_debug(NULL, " crop = %s\n",
td->crop ? "Black" : "Keep");
- mlt_log_debug(NULL, " relative = %s\n",
+ mlt_log_debug(NULL, " relative = %s\n",
td->relative ? "True": "False");
- mlt_log_debug(NULL, " invert = %s\n",
+ mlt_log_debug(NULL, " invert = %s\n",
td->invert ? "True" : "False");
mlt_log_debug(NULL, " zoom = %f\n", td->zoom);
- mlt_log_debug(NULL, " optzoom = %s\n",
+ mlt_log_debug(NULL, " optzoom = %s\n",
td->optzoom ? "On" : "Off");
- mlt_log_debug(NULL, " interpol = %s\n",
+ mlt_log_debug(NULL, " interpol = %s\n",
interpoltypes[td->interpoltype]);
mlt_log_debug(NULL, " sharpen = %f\n", td->sharpen);
}
-
+
if (td->maxshift > td->width_dest/2
) td->maxshift = td->width_dest/2;
if (td->maxshift > td->height_dest/2)
td->maxshift = td->height_dest/2;
-
+
if (!preprocess_transforms(td)) {
mlt_log_error(NULL,"error while preprocessing transforms!");
- return -1;
- }
+ return -1;
+ }
switch(td->interpoltype){
case 0: interpolate = &interpolateZero; break;
case 4: interpolate = &interpolateBiCub; break;
default: interpolate = &interpolateBiLin;
}
-
+
return 0;
}
* transform_filter_video: performs the transformation of frames
* See tcmodule-data.h for function details.
*/
-int transform_filter_video(TransformData *self,
- unsigned char *frame,mlt_image_format pixelformat)
+int transform_filter_video(TransformData *self,
+ unsigned char *frame,mlt_image_format pixelformat)
{
TransformData *td = self;
-
+
td->dest = frame;
memcpy(td->src, frame, td->framesize_src);
- if (td->current_trans >= td->trans_len) {
+ if (td->current_trans >= td->trans_len) {
td->current_trans = td->trans_len-1;
if(!td->warned_transform_end)
mlt_log_warning(NULL,"not enough transforms found, use last transformation!\n");
- td->warned_transform_end = 1;
+ td->warned_transform_end = 1;
}
-
+
if (pixelformat == mlt_image_rgb24 ) {
transformRGB(td);
} else if (pixelformat == mlt_image_yuv420p) {
* filter_transform.c
*
* Copyright (C) Georg Martius - June 2007
- * georg dot martius at web dot de
+ * georg dot martius at web dot de
*
* This file is part of transcode, a video stream processing tool
- *
+ *
* transcode is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
- *
+ *
* transcode is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Typical call:
* transcode -J transform -i inp.mpeg -y xdiv,tcaud inp_stab.avi
#define DEFAULT_TRANS_FILE_NAME "transforms.dat"
#define PIXEL(img, x, y, w, h, def) ((x) < 0 || (y) < 0) ? def \
- : (((x) >=w || (y) >= h) ? def : img[(x) + (y) * w])
-#define PIX(img, x, y, w, h) (img[(x) + (y) * w])
+ : (((x) >=w || (y) >= h) ? def : img[(x) + (y) * w])
+#define PIX(img, x, y, w, h) (img[(x) + (y) * w])
+#define PIXN(img, x, y, w, h,N,channel) (img[((x) + (y) * w)*N+channel])
// gives Pixel in N-channel image. channel in {0..N-1}
#define PIXELN(img, x, y, w, h, N,channel , def) ((x) < 0 || (y) < 0) ? def \
- : (((x) >=w || (y) >= h) ? def : img[((x) + (y) * w)*N + channel])
+ : (((x) >=w || (y) >= h) ? def : img[((x) + (y) * w)*N + channel])
typedef struct {
int framesize_src; // size of frame buffer in bytes (src)
int current_trans; // index to current transformation
int trans_len; // length of trans array
short warned_transform_end; // whether we warned that there is no transform left
-
+
/* Options */
int maxshift; // maximum number of pixels we will shift
double maxangle; // maximum angle in rad
- /* whether to consider transforms as relative (to previous frame)
- * or absolute transforms
+ /* whether to consider transforms as relative (to previous frame)
+ * or absolute transforms
*/
- int relative;
- /* number of frames (forward and backward)
+ int relative;
+ /* number of frames (forward and backward)
* to use for smoothing transforms */
- int smoothing;
+ int smoothing;
int crop; // 1: black bg, 0: keep border from last frame(s)
int invert; // 1: invert transforms, 0: nothing
/* constants */
/* threshhold below which no rotation is performed */
- double rotation_threshhold;
+ double rotation_threshhold;
double zoom; // percentage to zoom: 0->no zooming 10:zoom in 10%
int optzoom; // 1: determine optimal zoom, 0: nothing
int interpoltype; // type of interpolation: 0->Zero,1->Lin,2->BiLin,3->Sqr
} TransformData;
/* forward declarations, please look below for documentation*/
-void interpolateBiLinBorder(unsigned char *rv, float x, float y,
- unsigned char* img, int w, int h, unsigned char def);
-void interpolateBiCub(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height, unsigned char def);
-void interpolateSqr(unsigned char *rv, float x, float y,
- unsigned char* img, int w, int h, unsigned char def);
-void interpolateBiLin(unsigned char *rv, float x, float y,
- unsigned char* img, int w, int h, unsigned char def);
-void interpolateLin(unsigned char *rv, float x, float y,
- unsigned char* img, int w, int h, unsigned char def);
-void interpolateZero(unsigned char *rv, float x, float y,
- unsigned char* img, int w, int h, unsigned char def);
-void interpolateN(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height,
+void interpolateBiLinBorder(unsigned char *rv, float x, float y,
+ unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel);
+void interpolateBiCub(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel);
+void interpolateSqr(unsigned char *rv, float x, float y,
+ unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel);
+void interpolateBiLin(unsigned char *rv, float x, float y,
+ unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel);
+void interpolateLin(unsigned char *rv, float x, float y,
+ unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel);
+void interpolateZero(unsigned char *rv, float x, float y,
+ unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel);
+void interpolateN(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height,
unsigned char N, unsigned char channel, unsigned char def);
int transformRGB(TransformData* td);
int transformYUV(TransformData* td);
int preprocess_transforms(TransformData* td);
-/**
+/**
* interpolate: general interpolation function pointer for one channel image data
*
* Parameters:
* rv: destination pixel (call by reference)
- * x,y: the source coordinates in the image img. Note this
+ * x,y: the source coordinates in the image img. Note this
* are real-value coordinates, that's why we interpolate
* img: source image
* width,height: dimension of image
* def: default value if coordinates are out of range
* Return value: None
*/
-/*void (*interpolate)(unsigned char *rv, float x, float y,
- unsigned char* img, int width, int height,
+/*void (*interpolate)(unsigned char *rv, float x, float y,
+ unsigned char* img, int width, int height,
unsigned char def) = 0;
*/
/** interpolateBiLinBorder: bi-linear interpolation function that also works at the border.
This is used by many other interpolation methods at and outsize the border, see interpolate */
int transform_configure(TransformData *self,int width,int height, mlt_image_format pixelformat, unsigned char* image,Transform* tx,int trans_len) ;
-int transform_filter_video(TransformData *self,
+int transform_filter_video(TransformData *self,
unsigned char *frame,mlt_image_format pixelformat);