[FFmpeg-devel] [PATCH] video stabilization plugins using vid.stab library
Clément Bœsch
ubitux at gmail.com
Mon Mar 18 02:03:51 CET 2013
On Sun, Mar 17, 2013 at 11:59:17PM +0100, Georg Martius wrote:
> Hi,
>
> here is a patch for adding the filters for video stabilization. They need the
> vid.stab library installed [1]. I decided to develop the library outside of
> ffmpeg and only have the thin interfacing plugins in libavfilter.
> I didn't adapt the configure scripts because I am not familiar with them:
> "-lvidstab" is required for linking.
>
> [1] https://github.com/georgmartius/vid.stab
>
> Best regards,
> Georg
> From e295d7d8b123484a2970b10ebee92faa99d870b6 Mon Sep 17 00:00:00 2001
> From: Georg Martius <martius at mis.mpg.de>
> Date: Sun, 17 Mar 2013 23:36:38 +0100
> Subject: [PATCH] video stabilization plugins using vid.stab library
>
> ---
> libavfilter/Makefile | 4 +-
> libavfilter/allfilters.c | 4 +
> libavfilter/vf_stabilize.c | 367 ++++++++++++++++++++++++++++++++++++++
> libavfilter/vf_transform.c | 423 ++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 797 insertions(+), 1 deletion(-)
> create mode 100644 libavfilter/vf_stabilize.c
> create mode 100644 libavfilter/vf_transform.c
>
[...]
> +static int config_input(AVFilterLink *inlink)
> +{
> + AVFilterContext *ctx = inlink->dst;
> + StabData *sd = ctx->priv;
> +// char* filenamecopy, *filebasename;
> +
> + MotionDetect* md = &(sd->md);
> + VSFrameInfo fi;
> + const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
> +
> + _stab_ctx = ctx; // save context for logging
> +
> + initFrameInfo(&fi,inlink->w, inlink->h, AV2OurPixelFormat(ctx, inlink->format));
> + // check
> + if(fi.bytesPerPixel != av_get_bits_per_pixel(desc)/8)
> + av_log(ctx, AV_LOG_ERROR, "pixel-format error: wrong bits/per/pixel");
> + if(fi.log2ChromaW != desc->log2_chroma_w)
> + av_log(ctx, AV_LOG_ERROR, "pixel-format error: log2_chroma_w");
> + if(fi.log2ChromaH != desc->log2_chroma_h)
> + av_log(ctx, AV_LOG_ERROR, "pixel-format error: log2_chroma_h");
> +
> + if(initMotionDetect(md, &fi, "stabilize") != VS_OK){
> + av_log(ctx, AV_LOG_ERROR, "initialization of Motion Detection failed");
> + return AVERROR(EINVAL);
> + }
> +
> + sd->result = av_malloc(VS_INPUT_MAXLEN);
> + snprintf(sd->result, VS_INPUT_MAXLEN, DEFAULT_TRANS_FILE_NAME);
> +
av_strdup() is your friend
Also make sure you free such strings.
> + {
> + int ret;
> + if ((ret = (av_set_options_string(sd, sd->args, "=", ":"))) < 0)
> + return ret;
> + }
> +
> + // display help
> + /* if(optstr_lookup(sd->options, "help")) { */
> + /* av_log(ctx, AV_LOG_INFO, motiondetect_help); */
> + /* return AVERROR(EINVAL); */
> + /* } */
> +
> + if(configureMotionDetect(md)!= VS_OK){
> + av_log(ctx, AV_LOG_ERROR, "configuration of Motion Detection failed\n");
That hidden tab is going to cause trouble when we'll try to push it; make
sure not to forget it when you'll remove the tab in the other places where
Michael point them out.
> + return AVERROR(EINVAL);
> + }
> +
> + av_log(ctx, AV_LOG_INFO, "Image Stabilization Settings:\n");
> + av_log(ctx, AV_LOG_INFO, " shakiness = %d\n", md->shakiness);
> + av_log(ctx, AV_LOG_INFO, " accuracy = %d\n", md->accuracy);
> + av_log(ctx, AV_LOG_INFO, " stepsize = %d\n", md->stepSize);
> + av_log(ctx, AV_LOG_INFO, " algo = %d\n", md->algo);
> + av_log(ctx, AV_LOG_INFO, " mincontrast = %f\n", md->contrastThreshold);
> + av_log(ctx, AV_LOG_INFO, " show = %d\n", md->show);
> + av_log(ctx, AV_LOG_INFO, " result = %s\n", sd->result);
> +
> + sd->f = fopen(sd->result, "w");
> + if (sd->f == NULL) {
> + av_log(ctx, AV_LOG_ERROR, "cannot open transform file %s!\n", sd->result);
> + return AVERROR(EINVAL);
> + }else{
> + if(prepareFile(md, sd->f) != VS_OK){
> + av_log(ctx, AV_LOG_ERROR, "cannot write to transform file %s!\n", sd->result);
> + return AVERROR(EINVAL);
> + }
> + }
> + return 0;
> +}
> +
> +
> +static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *in)
> +{
We moved to AVFrame since a while now; you'll need to update your code
against a recent version.
[...]
> +AVFilter avfilter_vf_stabilize = {
> + .name = "stabilize",
> + .description = NULL_IF_CONFIG_SMALL("extracts relative transformations of \n\
> + subsequent frames (used for stabilization together with the\n\
> + transform filter in a second pass)."),
> +
> + .priv_size = sizeof(StabData),
> +
> + .init = init,
> + .uninit = uninit,
> + .query_formats = query_formats,
> +
> + .inputs = (const AVFilterPad[]) {{ .name = "default",
> + .type = AVMEDIA_TYPE_VIDEO,
> + .get_video_buffer = ff_null_get_video_buffer,
> + .filter_frame = filter_frame,
> + .config_props = config_input,
> + .min_perms = AV_PERM_READ, },
> + { .name = NULL}},
> + .outputs = (const AVFilterPad[]) {{ .name = "default",
> + .type = AVMEDIA_TYPE_VIDEO, },
> + { .name = NULL}},
Please move these declarations out of here like done in the other filters.
> +};
> +
> +
> +
> +/*************************************************************************/
> +
> +/*
> + * Local variables:
> + * c-file-style: "stroustrup"
> + * c-file-offsets: ((case-label . *) (statement-case-intro . *))
> + * indent-tabs-mode: nil
> + * End:
> + *
> + * vim: expandtab shiftwidth=4:
> + */
You can remove this, there is no reason to make an exception in one of the
file of the code base.
> diff --git a/libavfilter/vf_transform.c b/libavfilter/vf_transform.c
> new file mode 100644
> index 0000000..c409d7f
> --- /dev/null
> +++ b/libavfilter/vf_transform.c
> @@ -0,0 +1,423 @@
> +/*
> + * vf_transform.c
> + *
> + * Copyright (C) Georg Martius - Jan 2012
> + * georg dot martius at web dot de
> + *
> + * This file is part of vid.stab, video deshaking lib
> + *
> + * vid.stab is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2, or (at your option)
> + * any later version.
> + *
> + * vid.stab is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with GNU Make; see the file COPYING. If not, write to
> + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +/* Typical call:
> + * ffmpeg -i inp.mpeg -vf transform,unsharp=5:5:0.8:3:3:0.4 inp_s.mpeg
> + * all parameters are optional
> + */
> +
> +#define DEFAULT_TRANS_FILE_NAME "transforms.trf"
> +#define VS_INPUT_MAXLEN 1024
> +
> +#include "libavutil/common.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/imgutils.h"
> +// #include "libavcodec/dsputil.h"
> +#include "avfilter.h"
> +#include "internal.h"
> +
> +#include <vid.stab/libvidstab.h>
> +
> +/* private date structure of this filter*/
> +typedef struct _filter_data {
> + AVClass* class;
> +
> + TransformData td;
> +
> + Transformations trans; // transformations
> + char* args;
> + char* input; // name of transform file
> + int tripod;
> +} FilterData;
> +
> +/*** some conversions from avlib to vid.stab constants and functions ****/
> +
> +/** convert AV's pixelformat to vid.stab pixelformat */
> +static PixelFormat AV2OurPixelFormat(AVFilterContext *ctx, enum AVPixelFormat pf){
> + switch(pf){
> + case AV_PIX_FMT_YUV420P: return PF_YUV420P;
> + case AV_PIX_FMT_RGB24: return PF_RGB24;
> + case AV_PIX_FMT_BGR24: return PF_BGR24;
> + case AV_PIX_FMT_YUV422P: return PF_YUV422P;
> + case AV_PIX_FMT_YUV444P: return PF_YUV444P;
> + case AV_PIX_FMT_YUV410P: return PF_YUV410P;
> + case AV_PIX_FMT_YUV411P: return PF_YUV411P;
> + case AV_PIX_FMT_GRAY8: return PF_GRAY8;
> + case AV_PIX_FMT_YUVA420P:return PF_YUVA420P;
> + case AV_PIX_FMT_RGBA: return PF_RGBA;
> + default:
> + av_log(ctx, AV_LOG_ERROR, "cannot deal with pixel format %i!\n", pf);
> + return PF_NONE;
> + }
> +}
> +
> +/// pointer to context for logging
> +void *_trf_ctx = 0;
No way to avoid such global?
> +/** wrapper to log vs_log into av_log */
> +static int av_log_wrapper(int type, const char* tag, const char* format, ...){
> + va_list ap;
> + av_log(_trf_ctx, type, "%s: ", tag);
> + va_start (ap, format);
> + av_vlog(_trf_ctx, type, format, ap);
> + va_end (ap);
> + return VS_OK;
> +}
> +
> +/** sets the memory allocation function and logging constants to av versions */
> +static void setMemAndLogFunctions(void){
> + vs_malloc = av_malloc;
> + vs_zalloc = av_mallocz;
> + vs_realloc = av_realloc;
> + vs_free = av_free;
> +
> + VS_ERROR_TYPE = AV_LOG_ERROR;
> + VS_WARN_TYPE = AV_LOG_WARNING;
> + VS_INFO_TYPE = AV_LOG_INFO;
> + VS_MSG_TYPE = AV_LOG_VERBOSE;
> +
> + vs_log = av_log_wrapper;
> +
> + VS_ERROR = 0;
> + VS_OK = 1;
> +}
> +
> +/*** Commandline options ****/
> +
> +#define OFFSET(x) offsetof(FilterData, x)
> +#define OFFSETTD(x) (offsetof(FilterData, td)+offsetof(TransformData, x))
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +
> +static const AVOption transform_options[]= {
> + {"input", "path to the file storing the transforms (def:transforms.trf)",
> + OFFSET(input), AV_OPT_TYPE_STRING },
> + {"smoothing", "number of frames*2 + 1 used for lowpass filtering \n"
> + " used for stabilizing (def: 10)",
> + OFFSETTD(smoothing), AV_OPT_TYPE_INT, {.i64 = 10}, 1, 1000, FLAGS},
> + {"maxshift", "maximal number of pixels to translate image\n"
> + " (def: -1 no limit)",
> + OFFSETTD(maxShift), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 500, FLAGS},
> + {"maxangle", "maximal angle in rad to rotate image (def: -1 no limit)",
> + OFFSETTD(maxAngle), AV_OPT_TYPE_DOUBLE, {.dbl = -1.0}, -1.0, 3.14, FLAGS},
> + {"crop", "keep: (def), black",
> + OFFSETTD(crop), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS, "crop"},
> + { "keep", "Keep bolder",
> + 0, AV_OPT_TYPE_CONST, {.i64 = KeepBorder }, 0, 0, FLAGS, "crop" },
> + { "black", "Black border",
> + 0, AV_OPT_TYPE_CONST, {.i64 = CropBorder }, 0, 0, FLAGS, "crop" },
> + {"invert", " 1: invert transforms(def: 0)",
> + OFFSETTD(invert), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, FLAGS},
> + {"relative", "consider transforms as 0: absolute, 1: relative (def)",
> + OFFSETTD(relative), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS},
> + {"zoom", "percentage to zoom >0: zoom in, <0 zoom out (def: 0)",
> + OFFSETTD(zoom), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, 0, 100, FLAGS},
> + {"optzoom", "0: nothing, 1: determine optimal zoom (def)\n"
> + " i.e. no (or only little) border should be visible.\n"
> + " Note that the value given at 'zoom' is added to the \n"
> + " here calculated one",
> + OFFSETTD(optZoom), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, FLAGS},
> + {"interpol", "type of interpolation, no, linear, bilinear (def) , bicubic",
> + OFFSETTD(interpolType), AV_OPT_TYPE_INT, {.i64 = 2}, 0, 3, FLAGS, "interpol"},
> + { "no", "no interpolation",
> + 0, AV_OPT_TYPE_CONST, {.i64 = Zero }, 0, 0, FLAGS, "interpol" },
> + { "linear", "linear (horizontal)",
> + 0, AV_OPT_TYPE_CONST, {.i64 = Linear }, 0, 0, FLAGS, "interpol" },
> + { "bilinear", "bi-linear",
> + 0, AV_OPT_TYPE_CONST, {.i64 = BiLinear }, 0, 0, FLAGS, "interpol" },
> + { "bicubic", "bi-cubic",
> + 0, AV_OPT_TYPE_CONST, {.i64 = BiCubic }, 0, 0, FLAGS, "interpol" },
> + {"tripod", "if 1: virtual tripod mode (equivalent to relative=0:smoothing=0)",
> + OFFSET(tripod), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS},
> + {NULL},
> +};
> +
> +AVFILTER_DEFINE_CLASS(transform);
> +
> +
> +/*************************************************************************/
> +
> +/* Module interface routines and data. */
> +
> +/*************************************************************************/
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args)
> +{
> +
> + FilterData* fd = ctx->priv;
> + _trf_ctx = ctx;
> +
> + setMemAndLogFunctions();
> +
> + if (!fd) {
> + av_log(ctx, AV_LOG_INFO, "init: out of memory!\n");
> + return AVERROR(EINVAL);
> + }
If you are out of memory, the correct error code is AVERROR(ENOMEM), but
this doesn't seem to be the case here. You can safely remove completely
that check.
> + fd->class = (AVClass*)&transform_class;
Please put the class const and avoid the cast.
> + av_opt_set_defaults(fd); // the default values are overwritten by initMotiondetect later
> +
> + av_log(ctx, AV_LOG_INFO, "transform filter: init %s\n", LIBVIDSTAB_VERSION);
> +
AV_LOG_VERBOSE
> + // save args for later
> + if(args)
> + fd->args=av_strdup(args);
> + else
> + fd->args=0;
> +
This looks a bit suspicious; is it necessary?
Also, setting to 0 has no point at this point; everything in the context is zeroed.
> + return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> + FilterData *fd = ctx->priv;
> + _trf_ctx = ctx;
> +
> + // avfilter_unref_buffer(fd->ref);
> +
> + cleanupTransformData(&fd->td);
> + cleanupTransformations(&fd->trans);
> +
> + if(fd->args) av_free(fd->args);
No need to check for pointer before free.
> +}
> +
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> + static const enum AVPixelFormat pix_fmts[] = {
> + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P,
> + AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUVA420P,
> + AV_PIX_FMT_YUV440P, AV_PIX_FMT_GRAY8,
> + AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, AV_PIX_FMT_RGBA,
> + AV_PIX_FMT_NONE
> + };
> +
> + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
> + return 0;
> +}
> +
> +
> +static int config_input(AVFilterLink *inlink)
> +{
> + AVFilterContext *ctx = inlink->dst;
> + FilterData *fd = ctx->priv;
> + FILE* f;
> +// char* filenamecopy, *filebasename;
> +
> + const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
> +
Not using the function for this will cause compilation problems in some
configurations.
[...]
I'll have some more comments later.
--
Clément B.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20130318/6ee1b797/attachment.asc>
More information about the ffmpeg-devel
mailing list