From d316ee34eff2839ff93f7b1f2599203d3e90fffc Mon Sep 17 00:00:00 2001 From: Brian Matherly Date: Sat, 11 Jan 2014 19:46:46 -0600 Subject: [PATCH] Merge filter_detect and filter_transform into filter_vidstab --- src/modules/vid.stab/Makefile | 2 - src/modules/vid.stab/common.h | 3 - src/modules/vid.stab/factory.c | 14 +- src/modules/vid.stab/filter_detect.cpp | 206 -------------- src/modules/vid.stab/filter_detect.yml | 230 ---------------- src/modules/vid.stab/filter_transform.cpp | 250 ----------------- src/modules/vid.stab/filter_transform.yml | 218 --------------- src/modules/vid.stab/filter_vidstab.cpp | 317 +++++++++++++++++++++- src/modules/vid.stab/filter_vidstab.yml | 2 +- 9 files changed, 316 insertions(+), 926 deletions(-) delete mode 100644 src/modules/vid.stab/filter_detect.cpp delete mode 100644 src/modules/vid.stab/filter_detect.yml delete mode 100644 src/modules/vid.stab/filter_transform.cpp delete mode 100644 src/modules/vid.stab/filter_transform.yml diff --git a/src/modules/vid.stab/Makefile b/src/modules/vid.stab/Makefile index b90cf75f..f9991b23 100644 --- a/src/modules/vid.stab/Makefile +++ b/src/modules/vid.stab/Makefile @@ -9,8 +9,6 @@ TARGET = ../libmltvidstab$(LIBSUF) OBJS = factory.o CPPOBJS = filter_deshake.o -CPPOBJS += filter_detect.o -CPPOBJS += filter_transform.o CPPOBJS += filter_vidstab.o CXXFLAGS += -Wno-deprecated $(CFLAGS) diff --git a/src/modules/vid.stab/common.h b/src/modules/vid.stab/common.h index 96a2c9b0..b373dbdd 100644 --- a/src/modules/vid.stab/common.h +++ b/src/modules/vid.stab/common.h @@ -26,7 +26,4 @@ inline VSPixelFormat convertImageFormat(mlt_image_format &format) { } } -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_ */ diff --git a/src/modules/vid.stab/factory.c b/src/modules/vid.stab/factory.c index 25e5b430..ca416a5a 100644 --- a/src/modules/vid.stab/factory.c +++ b/src/modules/vid.stab/factory.c @@ -22,8 +22,6 @@ #include 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 ) @@ -35,13 +33,9 @@ static mlt_properties metadata( mlt_service_type type, const char *id, void *dat 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( filter_type, "vid.stab", filter_vidstab_init ); + MLT_REGISTER( filter_type, "deshake", filter_deshake_init ); + MLT_REGISTER( filter_type, "vidstab", 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" ); + MLT_REGISTER_METADATA( filter_type, "deshake", metadata, "filter_deshake.yml" ); + MLT_REGISTER_METADATA( filter_type, "vidstab", metadata, "filter_vidstab.yml" ); } diff --git a/src/modules/vid.stab/filter_detect.cpp b/src/modules/vid.stab/filter_detect.cpp deleted file mode 100644 index a38f8bce..00000000 --- a/src/modules/vid.stab/filter_detect.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * filter_detect.cpp - * Copyright (C) 2013 - * Marco Gittler - * Jakub Ksiezniak - * - * 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 -#include -#include -} - -#include -#include -#include "common.h" - -typedef struct _stab_data -{ - VSMotionDetect md; - mlt_animation animation; -} StabData; - -char* vectors_serializer(mlt_animation animation, int length) -{ - return mlt_animation_serialize(animation); -} - -#include -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(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); -} - -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); - - 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.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); - - // 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; - } -} - -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; - - 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)); - - StabData *data = static_cast(mlt_properties_get_data(properties, "_stab_data", NULL)); - if (!data) - { - 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; - 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_and_detect); - return frame; -} - -extern "C" -{ - -mlt_filter filter_detect_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; -} - -} diff --git a/src/modules/vid.stab/filter_detect.yml b/src/modules/vid.stab/filter_detect.yml deleted file mode 100644 index a02da168..00000000 --- a/src/modules/vid.stab/filter_detect.yml +++ /dev/null @@ -1,230 +0,0 @@ -schema_version: 0.1 -type: filter -identifier: vid.stab.detect -title: Vid.Stab Detect -copyright: Jakub Ksiezniak -creator: Marco Gittler -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 diff --git a/src/modules/vid.stab/filter_transform.cpp b/src/modules/vid.stab/filter_transform.cpp deleted file mode 100644 index 66e36435..00000000 --- a/src/modules/vid.stab/filter_transform.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * filter_transform.cpp - * Copyright (C) 2013 - * Marco Gittler - * Jakub Ksiezniak - * - * 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 -#include -#include -} - -#include -#include -#include -#include "common.h" - -typedef struct -{ - VSTransformData td; - VSTransformations trans; -} 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; -} - -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); - - 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.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.zoomSpeed = mlt_properties_get_double(properties, "zoomspeed"); - 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"); - mlt_animation_close(animation); - destroy_transforms(data); - return NULL; - } - - VSManyLocalMotions mlms; - if (vectors_deserialize(animation, &mlms)) - { - 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 data; -} - -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; - - 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(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); - destroy_transforms(data); - data = NULL; - } - - if (!data) - { - 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; - 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_and_transform); - return frame; -} - -extern "C" -{ - -mlt_filter filter_transform_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 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, "zoomspeed", "0.25"); - - return filter; - } - - return NULL; -} - -} diff --git a/src/modules/vid.stab/filter_transform.yml b/src/modules/vid.stab/filter_transform.yml deleted file mode 100644 index 1e33c43d..00000000 --- a/src/modules/vid.stab/filter_transform.yml +++ /dev/null @@ -1,218 +0,0 @@ -schema_version: 0.1 -type: filter -identifier: videostab2 -title: Videostab2 -copyright: Copyright (C) 2011 Marco Gittler -creator: Marco Gittler -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: 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 diff --git a/src/modules/vid.stab/filter_vidstab.cpp b/src/modules/vid.stab/filter_vidstab.cpp index ada37322..5bc44977 100644 --- a/src/modules/vid.stab/filter_vidstab.cpp +++ b/src/modules/vid.stab/filter_vidstab.cpp @@ -1,5 +1,5 @@ /* - * filter_deshake.cpp + * filter_vidstab.cpp * Copyright (C) 2013 * Marco Gittler * Jakub Ksiezniak @@ -23,12 +23,319 @@ extern "C" { #include #include +#include } +#include #include #include #include "common.h" + +typedef struct _stab_data +{ + VSMotionDetect md; + mlt_animation animation; +} StabData; + +typedef struct +{ + VSTransformData td; + VSTransformations trans; +} TransformData; + + +char* vectors_serializer(mlt_animation animation, int length) +{ + return mlt_animation_serialize(animation); +} + +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()); +} + +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; +} + +void lm_destructor(void *lms) +{ + vs_vector_del(static_cast(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 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; +} + +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); + + 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.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.zoomSpeed = mlt_properties_get_double(properties, "zoomspeed"); + 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"); + mlt_animation_close(animation); + destroy_transforms(data); + return NULL; + } + + VSManyLocalMotions mlms; + if (vectors_deserialize(animation, &mlms)) + { + 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 data; +} + +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; + + 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(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); + destroy_transforms(data); + data = NULL; + } + + if (!data) + { + 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; + 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 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); + + 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.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); + + // 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; + } +} + +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; + + 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)); + + StabData *data = static_cast(mlt_properties_get_data(properties, "_stab_data", NULL)); + if (!data) + { + 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; + 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_properties properties = MLT_FILTER_PROPERTIES(filter); @@ -68,9 +375,9 @@ extern "C" mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { - mlt_filter filter = NULL; + mlt_filter filter = mlt_filter_new(); - if ((filter = mlt_filter_new())) + if( filter ) { filter->process = process_filter; @@ -97,11 +404,9 @@ mlt_filter filter_vidstab_init(mlt_profile profile, mlt_service_type type, mlt_properties_set(properties, "zoomspeed", "0.25"); mlt_properties_set(properties, "vid.stab.version", LIBVIDSTAB_VERSION); - - return filter; } - return NULL; + return filter; } } diff --git a/src/modules/vid.stab/filter_vidstab.yml b/src/modules/vid.stab/filter_vidstab.yml index b298cd3a..c0f8c591 100644 --- a/src/modules/vid.stab/filter_vidstab.yml +++ b/src/modules/vid.stab/filter_vidstab.yml @@ -1,6 +1,6 @@ schema_version: 0.1 type: filter -identifier: vid.stab +identifier: vidstab title: Vid.Stab Detect and Transform copyright: Jakub Ksiezniak creator: Marco Gittler -- 2.39.5