]> git.sesse.net Git - mlt/commitdiff
Created a new module to support vid.stab library.
authorJakub Ksiezniak <jksiezniak@gmail.com>
Tue, 31 Dec 2013 07:20:31 +0000 (08:20 +0100)
committerBrian Matherly <pez4brian@yahoo.com>
Tue, 7 Jan 2014 03:01:31 +0000 (21:01 -0600)
src/modules/vid.stab/Makefile [new file with mode: 0644]
src/modules/vid.stab/common.h [new file with mode: 0644]
src/modules/vid.stab/configure [new file with mode: 0755]
src/modules/vid.stab/factory.c [new file with mode: 0644]
src/modules/vid.stab/filter_deshake.cpp [new file with mode: 0644]
src/modules/vid.stab/filter_deshake.yml [new file with mode: 0644]
src/modules/vid.stab/filter_detect.cpp [new file with mode: 0644]
src/modules/vid.stab/filter_detect.yml [new file with mode: 0644]
src/modules/vid.stab/filter_transform.cpp [new file with mode: 0644]
src/modules/vid.stab/filter_transform.yml [new file with mode: 0644]

diff --git a/src/modules/vid.stab/Makefile b/src/modules/vid.stab/Makefile
new file mode 100644 (file)
index 0000000..24eba4d
--- /dev/null
@@ -0,0 +1,45 @@
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt -lm
+
+include ../../../config.mak
+
+TARGET = ../libmltvidstab$(LIBSUF)
+
+OBJS = factory.o
+
+CPPOBJS = filter_deshake.o
+CPPOBJS += filter_detect.o
+CPPOBJS += filter_transform.o
+
+CXXFLAGS += -Wno-deprecated $(CFLAGS)
+CXXFLAGS += $(shell pkg-config --cflags vidstab)
+
+LDFLAGS += -L../../mlt++ -lmlt++
+LDFLAGS += $(shell pkg-config --libs vidstab)
+
+
+SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS) $(CPPOBJS)
+               $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CXX) -MM $(CXXFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend config.h config.mak
+
+clean: 
+               rm -f $(OBJS) $(TARGET) $(CPPOBJS)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)"
+       install -d "$(DESTDIR)$(mltdatadir)/vid.stab"
+       install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/vid.stab"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/vid.stab/common.h b/src/modules/vid.stab/common.h
new file mode 100644 (file)
index 0000000..b373dbd
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * common.h
+ *
+ *  Created on: 20 gru 2013
+ *      Author: Jakub Ksiezniak <j.ksiezniak@gmail.com>
+ */
+
+#ifndef VIDSTAB_COMMON_H_
+#define VIDSTAB_COMMON_H_
+
+extern "C" {
+#include <vid.stab/libvidstab.h>
+#include <framework/mlt.h>
+}
+
+inline VSPixelFormat convertImageFormat(mlt_image_format &format) {
+       switch (format) {
+       case mlt_image_rgb24:
+               return PF_RGB24;
+       case mlt_image_rgb24a:
+               return PF_RGBA;
+       case mlt_image_yuv420p:
+               return PF_YUV420P;
+       default:
+               return PF_NONE;
+       }
+}
+
+#endif /* VIDSTAB_COMMON_H_ */
diff --git a/src/modules/vid.stab/configure b/src/modules/vid.stab/configure
new file mode 100755 (executable)
index 0000000..7a852a1
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+       if ! $(pkg-config vidstab)
+       then
+               echo "- vid.stab not found: disabling"
+               touch ../disable-vidstab
+               exit 0
+       fi
+
+       echo > config.mak
+       case $targetos in
+       Darwin)
+               ;;
+       MinGW)
+               ;;
+       *)
+               ;;
+       esac
+       exit 0
+fi
diff --git a/src/modules/vid.stab/factory.c b/src/modules/vid.stab/factory.c
new file mode 100644 (file)
index 0000000..967e74c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 Dan Dennedy <dan@dennedy.org>
+ * factory.c -- the factory method interfaces
+ *
+ * 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.
+ */
+
+#include <string.h>
+#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 );
+
+static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
+{
+       char file[ PATH_MAX ];
+       snprintf( file, PATH_MAX, "%s/vid.stab/filter_%s.yml", mlt_environment( "MLT_DATA" ), id );
+       return mlt_properties_parse_yaml( file );
+}
+
+MLT_REPOSITORY
+{
+       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_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" );
+}
diff --git a/src/modules/vid.stab/filter_deshake.cpp b/src/modules/vid.stab/filter_deshake.cpp
new file mode 100644 (file)
index 0000000..b998726
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * 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"
+
+#define FILTER_NAME "vid.stab.deshake"
+
+typedef struct _deshake_data
+{
+       bool initialized;
+       VSMotionDetect md;
+       VSTransformData td;
+       VSSlidingAvgTrans avg;
+
+       void *parent;
+} DeshakeData;
+
+int init_deshake(DeshakeData *data, mlt_properties properties,
+               mlt_image_format *format, int *width, int *height, char* interps)
+{
+       VSPixelFormat pf = convertImageFormat(*format);
+       VSFrameInfo fiIn, fiOut;
+       vsFrameInfoInit(&fiIn, *width, *height, pf);
+       vsFrameInfoInit(&fiOut, *width, *height, pf);
+
+       VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig(FILTER_NAME);
+       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.algo = mlt_properties_get_int(properties, "algo");
+       conf.contrastThreshold = mlt_properties_get_double(properties, "mincontrast");
+       conf.show = 0;
+
+       vsMotionDetectInit(&data->md, &conf, &fiIn);
+
+       VSTransformConfig tdconf = vsTransformGetDefaultConfig(FILTER_NAME);
+       tdconf.smoothing = mlt_properties_get_int(properties, "smoothing");
+       tdconf.maxShift = mlt_properties_get_int(properties, "maxshift");
+       tdconf.maxAngle = mlt_properties_get_double(properties, "maxangle");
+       tdconf.crop = (VSBorderType) mlt_properties_get_int(properties, "crop");
+       tdconf.zoom = mlt_properties_get_int(properties, "zoom");
+       tdconf.optZoom = mlt_properties_get_int(properties, "optzoom");
+       tdconf.relative = 1;
+       tdconf.invert = 0;
+
+       // by default a bilinear interpolation is selected
+       tdconf.interpolType = VS_BiLinear;
+       if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0)
+               tdconf.interpolType = VS_Zero;
+       else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0)
+               tdconf.interpolType = VS_Linear;
+
+       vsTransformDataInit(&data->td, &tdconf, &fiIn, &fiOut);
+
+       data->avg.initialized = 0;
+       return 0;
+}
+
+void clear_deshake(DeshakeData *data)
+{
+       if (data->initialized)
+       {
+               vsMotionDetectionCleanup(&data->md);
+               vsTransformDataCleanup(&data->td);
+       }
+}
+
+static int get_image(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;
+       DeshakeData *data = static_cast<DeshakeData*>(filter->child);
+
+       int 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));
+
+               // Handle signal from app to re-init data
+               if (mlt_properties_get_int(properties, "refresh"))
+               {
+                       mlt_properties_set(properties, "refresh", NULL);
+                       clear_deshake(data);
+                       data->initialized = false;
+               }
+
+               if (!data->initialized)
+               {
+                       char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "rescale.interp");
+                       init_deshake(data, properties, format, width, height,
+                                       interps);
+                       data->initialized = true;
+               }
+
+               VSMotionDetect* md = &data->md;
+               VSTransformData* td = &data->td;
+               LocalMotions localmotions;
+               VSTransform motion;
+               VSFrame vsFrame;
+
+               vsFrameFillFromBuffer(&vsFrame, *image, &md->fi);
+               vsMotionDetection(md, &localmotions, &vsFrame);
+
+               motion = vsSimpleMotionsToTransform(td, &localmotions);
+               vs_vector_del(&localmotions);
+
+               vsTransformPrepare(td, &vsFrame, &vsFrame);
+
+               VSTransform t = vsLowPassTransforms(td, &data->avg, &motion);
+//         mlt_log_warning(filter, "Trans: det: %f %f %f \n\t\t act: %f %f %f %f",
+//                      motion.x, motion.y, motion.alpha,
+//                      t.x, t.y, t.alpha, t.zoom);
+               vsDoTransform(td, t);
+               vsTransformFinish(td);
+
+               mlt_service_unlock(MLT_FILTER_SERVICE(filter));
+       }
+
+       return error;
+}
+
+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);
+       return frame;
+}
+
+static void close_filter(mlt_filter filter)
+{
+       DeshakeData *data = static_cast<DeshakeData*>(filter->child);
+       if (data)
+       {
+               clear_deshake(data);
+               delete data;
+               filter->child = NULL;
+       }
+}
+
+extern "C"
+{
+
+mlt_filter filter_deshake_init(mlt_profile profile, mlt_service_type type,
+               const char *id, char *arg)
+{
+       mlt_filter filter = NULL;
+
+       DeshakeData *data = new DeshakeData;
+       memset(data, 0, sizeof(DeshakeData));
+
+       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);
+               //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");
+
+               //properties for transform
+               mlt_properties_set(properties, "smoothing", "10");
+               mlt_properties_set(properties, "maxshift", "-1");
+               mlt_properties_set(properties, "maxangle", "-1");
+               mlt_properties_set(properties, "crop", "0");
+               mlt_properties_set(properties, "zoom", "0");
+               mlt_properties_set(properties, "optzoom", "1");
+               mlt_properties_set(properties, "sharpen", "0.8");
+
+               return filter;
+       }
+
+       delete data;
+       return NULL;
+}
+
+}
diff --git a/src/modules/vid.stab/filter_deshake.yml b/src/modules/vid.stab/filter_deshake.yml
new file mode 100644 (file)
index 0000000..98c5232
--- /dev/null
@@ -0,0 +1,172 @@
+schema_version: 0.1
+type: filter
+identifier: vid.stab.deshake
+title: Vid.Stab Deshake
+copyright: Jakub Ksiezniak
+creator: Georg Martius
+version: 1
+license: GPLv2
+language: en
+url: http://public.hronopik.de/vid.stab/
+tags:
+  - Video
+description: Stabilize Video (for wiggly/rolling video)
+notes: >
+    Deshakes a video clip by extracting relative transformations
+    of subsequent frames and transforms the high-frequency away.
+    This is a single pass verion of stabilize and transform plugin.
+
+parameters:
+  - identifier: shakiness
+    title: Shakiness
+    type: integer
+    description: How shaky is the video and how quick is the camera? (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, region around minimum is scanned with 1 pixel resolution (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: 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: 10
+    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: 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 (transform)
+    readonly: no
+    required: no
+    minimum: 0
+    maximum: 1
+    default: 1
+    mutable: yes
+    widget: spinner
+
+  - identifier: sharpen
+    title: Sharpen Image
+    type: float
+    description: amount of sharpening (transform)
+    readonly: no
+    required: no
+    minimum: 0
+    maximum: 10
+    default: 0.8
+    mutable: yes
+    widget: spinner
+
+  - identifier: refresh
+    description: >
+      Applications should set this when it updates a transform parameter.
+    type: integer
+    minimum: 0
+    maximum: 1
diff --git a/src/modules/vid.stab/filter_detect.cpp b/src/modules/vid.stab/filter_detect.cpp
new file mode 100644 (file)
index 0000000..c5dc921
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * filter_detect.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 <framework/mlt_animation.h>
+}
+
+#include <string.h>
+#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)
+{
+       return mlt_animation_serialize(animation);
+}
+
+#include <sstream>
+char* lm_serializer(LocalMotions *lms, int length)
+{
+       std::ostringstream oss;
+       int size = vs_vector_size(lms);
+       for (int i = 0; i < size; ++i)
+       {
+               LocalMotion* lm = (LocalMotion*) vs_vector_get(lms, i);
+               oss << lm->v.x << ' ';
+               oss << lm->v.y << ' ';
+               oss << lm->f.x << ' ';
+               oss << lm->f.y << ' ';
+               oss << lm->f.size << ' ';
+               oss << lm->contrast << ' ';
+               oss << lm->match << ' ';
+       }
+       return strdup(oss.str().c_str());
+}
+
+void lm_destructor(void *lms)
+{
+       vs_vector_del(static_cast<VSVector*>(lms));
+}
+
+static void serialize_localmotions(StabData* data, LocalMotions &vectors, mlt_position pos)
+{
+       mlt_animation_item_s item;
+
+       // Initialize animation item
+       item.is_key = 1;
+       item.frame = data->md.frameNum;
+       item.keyframe_type = mlt_keyframe_discrete;
+       item.property = mlt_property_init();
+
+       mlt_property_set_data(item.property, &vectors, 1, lm_destructor, (mlt_serialiser) lm_serializer);
+       mlt_animation_insert(data->animation, &item);
+       mlt_property_close(item.property);
+}
+
+int init_detect(StabData *data, mlt_properties properties, mlt_image_format *format, int *width, int *height)
+{
+       VSPixelFormat pf = convertImageFormat(*format);
+       VSFrameInfo fi;
+       vsFrameInfoInit(&fi, *width, *height, pf);
+
+       VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig(FILTER_NAME);
+       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.algo = mlt_properties_get_int(properties, "algo");
+       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;
+}
+
+static int get_image(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);
+
+       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)
+               {
+                       init_detect(data, properties, format, width, height);
+                       data->initialized = true;
+               }
+
+               VSMotionDetect* md = &data->md;
+               LocalMotions localmotions;
+               VSFrame vsFrame;
+               vsFrameFillFromBuffer(&vsFrame, *image, &md->fi);
+
+               // detect and save motions
+               vsMotionDetection(md, &localmotions, &vsFrame);
+               mlt_position pos = mlt_filter_get_position( filter, frame );
+               serialize_localmotions(data, localmotions, pos);
+
+               mlt_service_unlock(MLT_FILTER_SERVICE(filter));
+       }
+
+       return error;
+}
+
+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);
+       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_detect_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg)
+{
+       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, "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", "10");
+               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, "sharpen", "0.8");
+
+               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;
+}
+
+}
diff --git a/src/modules/vid.stab/filter_detect.yml b/src/modules/vid.stab/filter_detect.yml
new file mode 100644 (file)
index 0000000..11865ef
--- /dev/null
@@ -0,0 +1,218 @@
+schema_version: 0.1
+type: filter
+identifier: vid.stab.detect
+title: Vid.Stab Detect
+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: 10
+    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 (transform)
+    readonly: no
+    required: no
+    minimum: 0
+    maximum: 1
+    default: 1
+    mutable: yes
+    widget: spinner
+
+  - identifier: refresh
+    description: >
+      Applications should set this when it updates a transform parameter.
+    type: integer
+    minimum: 0
+    maximum: 1
diff --git a/src/modules/vid.stab/filter_transform.cpp b/src/modules/vid.stab/filter_transform.cpp
new file mode 100644 (file)
index 0000000..3b23372
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * filter_transform.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 <framework/mlt_animation.h>
+}
+
+#include <string.h>
+#include <assert.h>
+#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)
+{
+       std::istringstream iss(mlt_property_get_string(property));
+       vs_vector_init(lms, 0);
+
+       while (iss.good())
+       {
+               LocalMotion lm;
+               iss >> lm.v.x >> lm.v.y >> lm.f.x >> lm.f.y >> lm.f.size >> lm.contrast >> lm.match;
+               if (iss.fail())
+               {
+                       break;
+               }
+               vs_vector_append_dup(lms, &lm, sizeof(lm));
+       }
+       return 0;
+}
+
+int vectors_deserialize(mlt_animation anim, VSManyLocalMotions *mlms)
+{
+       int error = 0;
+       mlt_animation_item_s item;
+       item.property = mlt_property_init();
+
+       vs_vector_init(mlms, 1024); // initial number of frames, but it will be increased
+
+       int length = mlt_animation_get_length(anim);
+       for (int i = 0; i < length; ++i)
+       {
+               LocalMotions lms;
+
+               // read lms
+               mlt_animation_get_item(anim, &item, i + 1);
+               if ((error = lm_deserialize(&lms, item.property)))
+               {
+                       break;
+               }
+
+               vs_vector_set_dup(mlms, i, &lms, sizeof(LocalMotions));
+       }
+
+       mlt_property_close(item.property);
+       return error;
+}
+
+inline int initialize_transforms(TransformData *data, int *width, int *height, mlt_image_format *format,
+               mlt_properties properties, char* interps)
+{
+       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);
+       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.crop = (VSBorderType) mlt_properties_get_int(properties, "crop");
+       conf.zoom = mlt_properties_get_int(properties, "zoom");
+       conf.optZoom = mlt_properties_get_int(properties, "optzoom");
+       conf.relative = mlt_properties_get_int(properties, "relative");
+       conf.invert = mlt_properties_get_int(properties, "invert");
+       if (mlt_properties_get_int(properties, "tripod") != 0)
+       {
+               // Virtual tripod mode: relative=False, smoothing=0
+               conf.relative = 0;
+               conf.smoothing = 0;
+       }
+
+       // by default a bilinear interpolation is selected
+       conf.interpolType = VS_BiLinear;
+       if (strcmp(interps, "nearest") == 0 || strcmp(interps, "neighbor") == 0)
+               conf.interpolType = VS_Zero;
+       else if (strcmp(interps, "tiles") == 0 || strcmp(interps, "fast_bilinear") == 0)
+               conf.interpolType = VS_Linear;
+
+       vsTransformDataInit(&data->td, &conf, &fi_src, &fi_dst);
+
+       vsTransformationsInit(&data->trans);
+
+       // load transformations
+       mlt_animation animation = mlt_animation_new();
+       char* strAnim = mlt_properties_get(properties, "vectors");
+       if (mlt_animation_parse(animation, strAnim, 0, 0, NULL))
+       {
+               mlt_log_warning(NULL, "parse failed\n");
+               return 1;
+       }
+
+       VSManyLocalMotions mlms;
+       if (vectors_deserialize(animation, &mlms))
+       {
+               return 1;
+       }
+
+       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);
+       }
+}
+
+static int get_image(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));
+
+               // 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;
+               }
+
+               if (!data->initialized)
+               {
+                       char *interps = mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "rescale.interp");
+                       initialize_transforms(data, width, height, format, properties, interps);
+                       data->initialized = true;
+               }
+
+               VSTransformData* td = &data->td;
+               VSFrame vsFrame;
+               vsFrameFillFromBuffer(&vsFrame, *image, vsTransformGetSrcFrameInfo(td));
+
+               // transform frame
+               data->trans.current = mlt_filter_get_position(filter, frame);
+               vsTransformPrepare(td, &vsFrame, &vsFrame);
+               VSTransform t = vsGetNextTransform(td, &data->trans);
+               vsDoTransform(td, t);
+               vsTransformFinish(td);
+
+               mlt_service_unlock(MLT_FILTER_SERVICE(filter));
+       }
+
+       return error;
+}
+
+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);
+       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_transform_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg)
+{
+       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);
+
+               // properties for transform
+               mlt_properties_set(properties, "smoothing", "10");
+               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");
+
+               return filter;
+       }
+
+       delete data;
+       return NULL;
+}
+
+}
diff --git a/src/modules/vid.stab/filter_transform.yml b/src/modules/vid.stab/filter_transform.yml
new file mode 100644 (file)
index 0000000..e8c475a
--- /dev/null
@@ -0,0 +1,206 @@
+schema_version: 0.1
+type: filter
+identifier: videostab2
+title: Videostab2
+copyright: Copyright (C) 2011 Marco Gittler
+creator: Marco Gittler <g.marco@freenet.de>
+version: 0.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: 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: 10
+    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 (transform)
+    readonly: no
+    required: no
+    minimum: 0
+    maximum: 1
+    default: 1
+    mutable: yes
+    widget: spinner
+
+  - identifier: refresh
+    description: >
+      Applications should set this when it updates a transform parameter.
+    type: integer
+    minimum: 0
+    maximum: 1