CPPOBJS = filter_deshake.o
CPPOBJS += filter_detect.o
CPPOBJS += filter_transform.o
+CPPOBJS += filter_vidstab.o
CXXFLAGS += -Wno-deprecated $(CFLAGS)
CXXFLAGS += $(shell pkg-config --cflags vidstab)
}
}
+int get_image_and_detect(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable);
+int get_image_and_transform(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable);
+
#endif /* VIDSTAB_COMMON_H_ */
touch ../disable-vidstab
exit 0
fi
+
+ minver="0.98"
+ modver=$(pkg-config --modversion vidstab)
+ if [ 0 == $(expr $modver \>= $minver) ]
+ then
+ echo "- vid.stab $modver found, but $minver or newer is required: disabling"
+ touch ../disable-opencv
+ exit 0
+ fi
echo > config.mak
case $targetos in
#include <limits.h>
#include <framework/mlt.h>
-// extern mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
extern mlt_filter filter_deshake_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
extern mlt_filter filter_detect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
extern mlt_filter filter_transform_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
{
MLT_REGISTER( filter_type, "vid.stab.deshake", filter_deshake_init );
MLT_REGISTER( filter_type, "vid.stab.detect", filter_detect_init );
MLT_REGISTER( filter_type, "vid.stab.transform", filter_transform_init );
+ MLT_REGISTER( filter_type, "vid.stab", filter_vidstab_init );
MLT_REGISTER_METADATA( filter_type, "vid.stab.deshake", metadata, "filter_deshake.yml" );
MLT_REGISTER_METADATA( filter_type, "vid.stab.detect", metadata, "filter_detect.yml" );
MLT_REGISTER_METADATA( filter_type, "vid.stab.transform", metadata, "filter_transform.yml" );
+ MLT_REGISTER_METADATA( filter_type, "vid.stab", metadata, "filter_vidstab.yml" );
}
VSTransformData td;
VSSlidingAvgTrans avg;
- void *parent;
+ mlt_position lastFrame;
} DeshakeData;
int init_deshake(DeshakeData *data, mlt_properties properties,
data->initialized = false;
}
+ // clear deshake data, when seeking or dropping frames
+ mlt_position pos = mlt_filter_get_position(filter, frame);
+ if(pos != data->lastFrame+1) {
+ clear_deshake(data);
+ data->initialized = false;
+ }
+ data->lastFrame = pos;
+
if (!data->initialized)
{
char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "rescale.interp");
filter->process = process_filter;
filter->close = close_filter;
filter->child = data;
- data->parent = filter;
mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
//properties for stabilize
mlt_properties_set(properties, "mincontrast", "0.3");
//properties for transform
- mlt_properties_set(properties, "smoothing", "10");
+ mlt_properties_set(properties, "smoothing", "15");
mlt_properties_set(properties, "maxshift", "-1");
mlt_properties_set(properties, "maxangle", "-1");
mlt_properties_set(properties, "crop", "0");
required: no
minimum: 0
maximum: 100
- default: 10
+ default: 15
mutable: yes
widget: spinner
#include <assert.h>
#include "common.h"
-#define FILTER_NAME "vid.stab.detect"
-
typedef struct _stab_data
{
- bool initialized;
VSMotionDetect md;
mlt_animation animation;
-
- void *parent;
} StabData;
char* vectors_serializer(mlt_animation animation, int length)
mlt_property_close(item.property);
}
-int init_detect(StabData *data, mlt_properties properties, mlt_image_format *format, int *width, int *height)
+static StabData* init_detect(mlt_properties properties, mlt_image_format *format, int *width, int *height)
{
+ StabData *data = new StabData;
+ memset(data, 0, sizeof(StabData));
+ data->animation = mlt_animation_new();
+
VSPixelFormat pf = convertImageFormat(*format);
VSFrameInfo fi;
vsFrameInfoInit(&fi, *width, *height, pf);
- VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig(FILTER_NAME);
+ const char* filterName = mlt_properties_get(properties, "mlt_service");
+
+ VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig(filterName);
conf.shakiness = mlt_properties_get_int(properties, "shakiness");
conf.accuracy = mlt_properties_get_int(properties, "accuracy");
conf.stepSize = mlt_properties_get_int(properties, "stepsize");
conf.contrastThreshold = mlt_properties_get_double(properties, "mincontrast");
conf.show = mlt_properties_get_int(properties, "show");
conf.virtualTripod = mlt_properties_get_int(properties, "tripod");
-
vsMotionDetectInit(&data->md, &conf, &fi);
- // change name of the filter, so the vid.stab.transform will be used in 2nd-pass.
- mlt_properties_set(properties, "mlt_service", "vid.stab.transform");
- return 0;
+ // add vectors to properties
+ mlt_properties_set_data(properties, "vectors", data->animation, 1, (mlt_destructor) mlt_animation_close,
+ (mlt_serialiser) vectors_serializer);
+ return data;
+}
+
+void destroy_detect(StabData *data)
+{
+ if (data)
+ {
+ vsMotionDetectionCleanup(&data->md);
+ delete data;
+ }
}
-static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
+int get_image_and_detect(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
{
mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame);
mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
*format = mlt_image_yuv420p;
- StabData *data = static_cast<StabData*>(filter->child);
+
+ writable = writable || mlt_properties_get_int(properties, "show") ? 1 : 0;
int error = mlt_frame_get_image(frame, image, format, width, height, writable);
if (!error)
// Service locks are for concurrency control
mlt_service_lock(MLT_FILTER_SERVICE(filter));
- if (!data->initialized)
+ StabData *data = static_cast<StabData*>(mlt_properties_get_data(properties, "_stab_data", NULL));
+ if (!data)
{
- init_detect(data, properties, format, width, height);
- data->initialized = true;
+ data = init_detect(properties, format, width, height);
+ mlt_properties_set_data(properties, "_stab_data", data, 0, (mlt_destructor) destroy_detect, NULL);
}
VSMotionDetect* md = &data->md;
static mlt_frame process_filter(mlt_filter filter, mlt_frame frame)
{
mlt_frame_push_service(frame, filter);
- mlt_frame_push_get_image(frame, get_image);
+ mlt_frame_push_get_image(frame, get_image_and_detect);
return frame;
}
-static void close_filter(mlt_filter filter)
-{
- StabData *data = static_cast<StabData*>(filter->child);
- if (data)
- {
- if (data->initialized)
- {
- vsMotionDetectionCleanup(&data->md);
- }
- delete data;
- filter->child = NULL;
- }
-}
-
extern "C"
{
{
mlt_filter filter = NULL;
- StabData *data = new StabData;
- memset(data, 0, sizeof(StabData));
-
if ((filter = mlt_filter_new()))
{
filter->process = process_filter;
- filter->close = close_filter;
- filter->child = data;
-
- data->animation = mlt_animation_new();
- data->parent = filter;
mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
- mlt_properties_set(properties, "mlt_service", "vid.stab.transform");
//properties for stabilize
mlt_properties_set(properties, "shakiness", "4");
mlt_properties_set(properties, "tripod", "0");
// properties for transform
- mlt_properties_set(properties, "smoothing", "10");
+ mlt_properties_set(properties, "smoothing", "15");
mlt_properties_set(properties, "maxshift", "-1");
mlt_properties_set(properties, "maxangle", "-1");
mlt_properties_set(properties, "crop", "0");
mlt_properties_set(properties, "zoomspeed", "0.25");
mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION);
- mlt_properties_set_data(properties, "vectors", data->animation, 1, (mlt_destructor) mlt_animation_close,
- (mlt_serialiser) vectors_serializer);
return filter;
}
- delete data;
return NULL;
}
required: no
minimum: 0
maximum: 100
- default: 10
+ default: 15
mutable: yes
widget: spinner
#include <sstream>
#include "common.h"
-#define FILTER_NAME "vid.stab.transform"
-
typedef struct
{
- bool initialized;
VSTransformData td;
VSTransformations trans;
-
- void *parent;
} TransformData;
int lm_deserialize(LocalMotions *lms, mlt_property property)
return error;
}
-inline int initialize_transforms(TransformData *data, int *width, int *height, mlt_image_format *format,
- mlt_properties properties, char* interps)
+void destroy_transforms(TransformData *data)
+{
+ if (data)
+ {
+ vsTransformDataCleanup(&data->td);
+ vsTransformationsCleanup(&data->trans);
+ delete data;
+ }
+}
+
+TransformData* initialize_transforms(int *width, int *height, mlt_image_format *format,
+ mlt_properties properties, const char* interps)
{
+ TransformData *data = new TransformData;
+ memset(data, 0, sizeof(TransformData));
+
VSPixelFormat pf = convertImageFormat(*format);
VSFrameInfo fi_src, fi_dst;
vsFrameInfoInit(&fi_src, *width, *height, pf);
vsFrameInfoInit(&fi_dst, *width, *height, pf);
- VSTransformConfig conf = vsTransformGetDefaultConfig(FILTER_NAME);
+ const char* filterName = mlt_properties_get(properties, "mlt_service");
+
+ VSTransformConfig conf = vsTransformGetDefaultConfig(filterName);
conf.smoothing = mlt_properties_get_int(properties, "smoothing");
conf.maxShift = mlt_properties_get_int(properties, "maxshift");
conf.maxAngle = mlt_properties_get_double(properties, "maxangle");
conf.interpolType = VS_Linear;
vsTransformDataInit(&data->td, &conf, &fi_src, &fi_dst);
-
vsTransformationsInit(&data->trans);
// load transformations
if (mlt_animation_parse(animation, strAnim, 0, 0, NULL))
{
mlt_log_warning(NULL, "parse failed\n");
- return 1;
+ mlt_animation_close(animation);
+ destroy_transforms(data);
+ return NULL;
}
VSManyLocalMotions mlms;
if (vectors_deserialize(animation, &mlms))
{
- return 1;
+ mlt_animation_close(animation);
+ destroy_transforms(data);
+ return NULL;
}
mlt_animation_close(animation);
vsLocalmotions2Transforms(&data->td, &mlms, &data->trans);
vsPreprocessTransforms(&data->td, &data->trans);
- return 0;
-}
-
-void clear_transforms(TransformData *data)
-{
- if (data->initialized)
- {
- vsTransformDataCleanup(&data->td);
- vsTransformationsCleanup(&data->trans);
- }
+ return data;
}
-static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
+int get_image_and_transform(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable)
{
int error = 0;
mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame);
mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
*format = mlt_image_yuv420p;
- TransformData *data = static_cast<TransformData*>(filter->child);
error = mlt_frame_get_image(frame, image, format, width, height, 1);
if (!error)
// Service locks are for concurrency control
mlt_service_lock(MLT_FILTER_SERVICE(filter));
+ TransformData *data = static_cast<TransformData*>(mlt_properties_get_data(properties, "_transform_data", NULL));
+
// Handle signal from app to re-init data
if (mlt_properties_get_int(properties, "refresh"))
{
mlt_properties_set(properties, "refresh", NULL);
- clear_transforms(data);
- data->initialized = false;
+ destroy_transforms(data);
+ data = NULL;
}
- if (!data->initialized)
+ if (!data)
{
- char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "rescale.interp");
- initialize_transforms(data, width, height, format, properties, interps);
- data->initialized = true;
+ const char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "rescale.interp");
+ data = initialize_transforms(width, height, format, properties, interps);
+ if(!data) {
+ mlt_service_unlock(MLT_FILTER_SERVICE(filter));
+ return 1; // return error code
+ }
+ mlt_properties_set_data(properties, "_transform_data", data, 0, (mlt_destructor) destroy_transforms, NULL);
}
VSTransformData* td = &data->td;
static mlt_frame process_filter(mlt_filter filter, mlt_frame frame)
{
mlt_frame_push_service(frame, filter);
- mlt_frame_push_get_image(frame, get_image);
+ mlt_frame_push_get_image(frame, get_image_and_transform);
return frame;
}
-static void close_filter(mlt_filter filter)
-{
- TransformData *data = static_cast<TransformData*>(filter->child);
- if (data)
- {
- clear_transforms(data);
- delete data;
- filter->child = NULL;
- }
-}
-
extern "C"
{
{
mlt_filter filter = NULL;
- TransformData *data = new TransformData;
- memset(data, 0, sizeof(TransformData));
-
if ((filter = mlt_filter_new()))
{
filter->process = process_filter;
- filter->close = close_filter;
- filter->child = data;
-
- data->parent = filter;
mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
return filter;
}
- delete data;
return NULL;
}
required: no
minimum: 0
maximum: 100
- default: 10
+ default: 15
mutable: yes
widget: spinner
--- /dev/null
+/*
+ * filter_deshake.cpp
+ * Copyright (C) 2013
+ * Marco Gittler <g.marco@freenet.de>
+ * Jakub Ksiezniak <j.ksiezniak@gmail.com>
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+extern "C"
+{
+#include <vid.stab/libvidstab.h>
+#include <framework/mlt.h>
+}
+
+#include <string.h>
+#include <assert.h>
+#include "common.h"
+
+static mlt_frame process_filter(mlt_filter filter, mlt_frame frame)
+{
+ mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
+ mlt_get_image vidstab_get_image = (mlt_get_image) mlt_properties_get_data( properties, "_vidstab_get_image", NULL );
+
+#if 1
+ mlt_position pos = mlt_filter_get_position(filter, frame);
+ mlt_position length = mlt_filter_get_length2(filter, frame) - 1;
+ if(pos >= length)
+ {
+ mlt_properties_set_data(properties, "_vidstab_get_image", NULL, 0, NULL, NULL);
+ }
+#endif
+
+ if(vidstab_get_image == NULL)
+ {
+ if(mlt_properties_get(properties, "vectors") == NULL)
+ {
+ // vectors are NULL, so use a detect filter
+ vidstab_get_image = get_image_and_detect;
+ } else {
+ // found vectors, so use a transform filter
+ vidstab_get_image = get_image_and_transform;
+ }
+
+ mlt_properties_set_data( properties, "_vidstab_get_image", (void*)vidstab_get_image, 0, NULL, NULL );
+ }
+
+ mlt_frame_push_service(frame, filter);
+ mlt_frame_push_get_image(frame, vidstab_get_image);
+ return frame;
+}
+
+extern "C"
+{
+
+mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type,
+ const char *id, char *arg)
+{
+ mlt_filter filter = NULL;
+
+ if ((filter = mlt_filter_new()))
+ {
+ filter->process = process_filter;
+
+ mlt_properties properties = MLT_FILTER_PROPERTIES(filter);
+
+ //properties for stabilize
+ mlt_properties_set(properties, "shakiness", "4");
+ mlt_properties_set(properties, "accuracy", "4");
+ mlt_properties_set(properties, "stepsize", "6");
+ mlt_properties_set(properties, "algo", "1");
+ mlt_properties_set(properties, "mincontrast", "0.3");
+ mlt_properties_set(properties, "show", "0");
+ mlt_properties_set(properties, "tripod", "0");
+
+ // properties for transform
+ mlt_properties_set(properties, "smoothing", "15");
+ mlt_properties_set(properties, "maxshift", "-1");
+ mlt_properties_set(properties, "maxangle", "-1");
+ mlt_properties_set(properties, "crop", "0");
+ mlt_properties_set(properties, "invert", "0");
+ mlt_properties_set(properties, "relative", "1");
+ mlt_properties_set(properties, "zoom", "0");
+ mlt_properties_set(properties, "optzoom", "1");
+ mlt_properties_set(properties, "zoomspeed", "0.25");
+
+ mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION);
+
+ return filter;
+ }
+
+ return NULL;
+}
+
+}
--- /dev/null
+schema_version: 0.1
+type: filter
+identifier: vid.stab
+title: Vid.Stab Detect and Transform
+copyright: Jakub Ksiezniak
+creator: Marco Gittler <g.marco@freenet.de>
+version: 1
+license: GPL
+language: en
+url: http://public.hronopik.de/vid.stab/
+tags:
+ - Video
+description: Stabilize Video (for wiggly/rolling video)
+notes: >
+ This filter requires two passes. The first pass performs analysis and stores
+ the result in the vectors property. The second pass applies the vectors to
+ the image.
+ To use with melt, use 'melt ... -consumer xml:output.mlt all=1' for the
+ first pass. For the second pass, use output.mlt as the input.
+
+parameters:
+ - identifier: vectors (transform)
+ title: Vectors
+ type: geometry
+ description: >
+ A set of X/Y coordinates by which to adjust the image.
+ When this is not supplied, the filter computes the vectors and stores
+ them in this property when the last frame has been processed.
+
+ - identifier: shakiness
+ title: Shakiness
+ type: integer
+ description: How shaky is the video (analysis)
+ readonly: no
+ required: no
+ minimum: 1
+ maximum: 10
+ default: 4
+ mutable: yes
+ widget: spinner
+
+ - identifier: accuracy
+ title: Accuracy
+ type: integer
+ description: Accuracy of shakiness detection (analysis)
+ readonly: no
+ required: no
+ minimum: 1
+ maximum: 15
+ default: 4
+ mutable: yes
+ widget: spinner
+
+ - identifier: stepsize
+ title: Stepsize
+ type: integer
+ description: Step size of search process (analysis)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 100
+ default: 6
+ mutable: yes
+ widget: spinner
+
+ - identifier: algo
+ title: Algorithm
+ type: integer
+ description: 0 = brute force (translation only), 1 = small measurement fields (analysis)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 1
+ default: 1
+ mutable: yes
+ widget: spinner
+
+ - identifier: mincontrast
+ title: Minimum Contrast
+ type: float
+ description: Below this contrast, a field is discarded (analysis)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 1
+ default: 0.3
+ mutable: yes
+ widget: spinner
+
+ - identifier: show
+ title: Show
+ type: integer
+ description: 0 = draw nothing, 1 or 2 = show fields and transforms (analysis)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 2
+ default: 0
+ mutable: yes
+ widget: spinner
+
+ - identifier: tripod
+ title: Tripod
+ type: integer
+ description: virtual tripod mode (if >0): motion is compared to a reference frame (frame N is the value)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 100000
+ default: 0
+ mutable: yes
+ widget: spinner
+
+ - identifier: smoothing
+ title: Smoothing
+ type: integer
+ description: number of frames for lowpass filtering (2N + 1 frames) (transform)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 100
+ default: 15
+ mutable: yes
+ widget: spinner
+
+ - identifier: maxshift
+ title: Maxshift
+ type: integer
+ description: maximum translation, -1 = no limit (transform)
+ unit: pixels
+ readonly: no
+ required: no
+ minimum: -1
+ maximum: 1000
+ default: -1
+ mutable: yes
+ widget: spinner
+
+ - identifier: maxangle
+ title: Maxangle
+ type: float
+ description: max angle to rotate, -1 = no limit (transform)
+ unit: radians
+ readonly: no
+ required: no
+ minimum: -1
+ maximum: 3.142
+ default: -1
+ mutable: yes
+ widget: spinner
+
+ - identifier: crop
+ title: Crop
+ type: integer
+ description: 0 = keep border, 1 = black background (transform)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 1
+ default: 0
+ mutable: yes
+ widget: spinner
+
+ - identifier: invert
+ title: Invert
+ type: integer
+ description: Invert transforms (transform)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 1
+ default: 0
+ mutable: yes
+ widget: spinner
+
+ - identifier: relative
+ title: Relative Transform
+ type: integer
+ description: 0 = absolute, 1 = relative (transform)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 1
+ default: 1
+ mutable: yes
+ widget: spinner
+
+ - identifier: zoom
+ title: Zoom
+ type: integer
+ description: additional zoom amount (transform)
+ unit: percent
+ readonly: no
+ required: no
+ minimum: -500
+ maximum: 500
+ default: 0
+ mutable: yes
+ widget: spinner
+
+ - identifier: optzoom
+ title: Optimal Zoom
+ type: integer
+ description: automatically determine optimal zoom. 1 - static zoom, 2 - adaptive zoom (transform)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 2
+ default: 1
+ mutable: yes
+ widget: spinner
+
+ - identifier: zoomspeed
+ title: Optimal Zoom Speed
+ type: float
+ description: zoom per frame in percent, (used when optzoom = 2) (transform)
+ readonly: no
+ required: no
+ minimum: 0
+ maximum: 1
+ default: 0.25
+ mutable: yes
+ widget: spinner
+
+ - identifier: refresh
+ description: >
+ Applications should set this when it updates a transform parameter.
+ type: integer
+ minimum: 0
+ maximum: 1