[FFmpeg-devel] [PATCH] lavfi: add smartblur filter

Stefano Sabatini stefasab at gmail.com
Thu Aug 23 11:29:43 CEST 2012


On date Wednesday 2012-08-22 22:23:34 +0200, Jérémy Tran encoded:
> This is a port of the MPlayer smartblur filter

Note: add a mention of the original contributor, and that he was OK
with the relicensing of the code.

> This is still work in progress, the checksum of the port and
> the original filter aren't the same.

How are they different?

> ---
>  doc/filters.texi           |  26 ++++
>  libavfilter/Makefile       |   1 +
>  libavfilter/allfilters.c   |   1 +
>  libavfilter/vf_smartblur.c | 320 +++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 348 insertions(+)
>  create mode 100644 libavfilter/vf_smartblur.c
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 6e7646f..d888670 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -3353,6 +3353,32 @@ not specified it will use the default value of 16.
>  Adding this in the beginning of filter chains should make filtering
>  faster due to better use of the memory cache.
>  
> + at section smartblur
> +

> +Blur the input video without impacting the outlines.


> +
> +The filter accepts the following parameters:
> + at var{luma_radius}:@var{luma_strength}:@var{luma_threshold}[:@var{chroma_radius}:@var{chroma_strength}:@var{chroma_threshold}]
> +
> +Parameters prefixed by @var{luma} indicates that it works on the luminance of the
> +pixels whereas parameters prefixed by @var{chroma} refer to the chrominance of
> +the pixels.
> +
> +If the chroma parameters are not set, the luma parameters are used for
> +either the luminance and the chrominance of the pixels.
> +

> + at var{xxx_radius} must be a float number in the range [0.1,5.0] that specifies

Please expand this to "@var{luma_radius} or @var{chroma_radius}" or
"radius value", same below.

> +the blur filter strength (slower if larger).
> +
> + at var{xxx_strength} must be a float number in the range [-1.0,1.0] that
> +configures the blurring. A value included in [0.0,1.0] will blur the image
> +whereas a value included in [-1.0,0.0] will sharpen the image.
> +
> + at var{xxx_threshold} must be an integer in the range [-30,30] that is used as a
> +coefficient to determine whether a pixel should be blurred or not. A value of
> +0 will filter all the image, a value included in [0-30] will filter flat areas
> +and a value included in [-30,0] will filter edges.
> +
>  @section split
>  
>  Split input video into several identical outputs.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index e91141e..35477e1 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -125,6 +125,7 @@ OBJS-$(CONFIG_SETSAR_FILTER)                 += vf_aspect.o
>  OBJS-$(CONFIG_SETTB_FILTER)                  += f_settb.o
>  OBJS-$(CONFIG_SHOWINFO_FILTER)               += vf_showinfo.o
>  OBJS-$(CONFIG_SLICIFY_FILTER)                += vf_slicify.o
> +OBJS-$(CONFIG_SMARTBLUR_FILTER)              += vf_smartblur.o
>  OBJS-$(CONFIG_SPLIT_FILTER)                  += split.o
>  OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
>  OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o

Missing libswscale dependency (check how it is done for the other filters).

> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 0bc5a4a..cd9bef8 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -116,6 +116,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER (SETTB,       settb,       vf);
>      REGISTER_FILTER (SHOWINFO,    showinfo,    vf);
>      REGISTER_FILTER (SLICIFY,     slicify,     vf);
> +    REGISTER_FILTER (SMARTBLUR,   smartblur,   vf);
>      REGISTER_FILTER (SPLIT,       split,       vf);
>      REGISTER_FILTER (SUPER2XSAI,  super2xsai,  vf);
>      REGISTER_FILTER (SWAPUV,      swapuv,      vf);
> diff --git a/libavfilter/vf_smartblur.c b/libavfilter/vf_smartblur.c
> new file mode 100644
> index 0000000..3699ec6
> --- /dev/null
> +++ b/libavfilter/vf_smartblur.c
> @@ -0,0 +1,320 @@
> +/*
> + * Copyright (c) 2002 Michael Niedermayer <michaelni at gmx.at>
> + * Copyright (c) 2012 Jeremy Tran
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg 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.
> + *
> + * FFmpeg 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 FFmpeg; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +

> +/**
> + * @file
> + * Apply a smartblur filter to the input video
> + * Ported from MPlayer libmpcodecs/vf_smartblur.c.
> + */
> +
> +#include "libavutil/pixdesc.h"
> +
> +#include "libswscale/swscale.h"
> +
> +#include "avfilter.h"
> +#include "formats.h"
> +#include "internal.h"
> +

> +#define RAD_MIN 0.1
> +#define RAD_MAX 5.0
> +
> +#define STRGTH_MIN -1.0
> +#define STRGTH_MAX 1.0

please avoid contractions when they obfuscate the code (especially if
you don't do it consistently), in this case STRENGTH is much better
than STRGTH and only two chars longer

> +
> +#define THRES_MIN -30
> +#define THRES_MAX 30

> +
> +typedef struct {
> +    float              radius;
> +    float              strength;
> +    int                threshold;
> +    float              quality;
> +    struct SwsContext *filter_context;
> +} FilterParam;
> +
> +typedef struct {
> +    FilterParam luma;
> +    FilterParam chroma;
> +    int         hsub;
> +    int         vsub;
> +} SmartblurContext;
> +
> +static inline int validate_input(AVFilterContext *ctx,
> +                                 float lradius,    float cradius,
> +                                 float lstrength,  float cstrength,
> +                                 int   lthreshold, int   cthreshold)
> +{
> +    int ret = 0;
> +
> +    if (lradius < RAD_MIN || lradius > RAD_MAX) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Invalid value for luma radius %0.1f: "
> +               "must be included between range %0.1f and %0.1f\n",
> +               lradius, RAD_MIN, RAD_MAX);
> +        ret = AVERROR(EINVAL);
> +    }
> +    if (cradius < RAD_MIN || cradius > RAD_MAX) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Invalid value for chroma radius %0.1f: "
> +               "must be included between range %0.1f and %0.1f\n",
> +               cradius, RAD_MIN, RAD_MAX);
> +        ret = AVERROR(EINVAL);
> +    }
> +    if (lstrength < STRGTH_MIN || lstrength > STRGTH_MAX) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Invalid value for luma strength %0.1f: "
> +               "must be included between range %0.1f and %0.1f\n",
> +               lstrength, STRGTH_MIN, STRGTH_MAX);
> +        ret = AVERROR(EINVAL);
> +    }
> +    if (cstrength < STRGTH_MIN || cstrength > STRGTH_MAX) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Invalid value for chroma strength %0.1f: "
> +               "must be included between range %0.1f and %0.1f\n",
> +               cstrength, STRGTH_MIN, STRGTH_MAX);
> +        ret = AVERROR(EINVAL);
> +    }
> +    if (lthreshold < THRES_MIN || lthreshold > THRES_MAX) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Invalid value for luma threshold %d: "
> +               "must be included between range %d and %d\n",
> +               lthreshold, THRES_MIN, THRES_MAX);
> +        ret = AVERROR(EINVAL);
> +    }
> +    if (cthreshold < THRES_MIN || cthreshold > THRES_MAX) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Invalid value for luma threshold %d: "
> +               "must be included between range %d and %d\n",
> +               cthreshold, THRES_MIN, THRES_MAX);
> +        ret = AVERROR(EINVAL);
> +    }

This can be factorized at the pre-processing stage with a macro, see
boxblur.

> +
> +    return ret;
> +}
> +

> +static av_cold int init(AVFilterContext *ctx, const char *args)
> +{
> +    SmartblurContext *sblur = ctx->priv;
> +    int n = 0;
> +    float lradius, lstrength, cradius, cstrength;
> +    int lthreshold, cthreshold;
> +
> +    if (args)
> +        n = sscanf(args, "%f:%f:%d:%f:%f:%d",
> +                   &lradius,
> +                   &lstrength,
> +                   &lthreshold,
> +                   &cradius,
> +                   &cstrength,
> +                   &cthreshold);
> +
> +    if (n != 3 || n != 6) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Incorrect number of parameters: "
> +               "must be luma_radius:luma_strength:luma_threshold"
> +               "[:chroma_radius:chroma_strength:chroma_threshold]\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    sblur->luma.radius      = lradius;
> +    sblur->luma.strength    = lstrength;
> +    sblur->luma.threshold   = lthreshold;
> +
> +    if (n == 3) {
> +        sblur->chroma.radius    = sblur->luma.radius;
> +        sblur->chroma.strength  = sblur->luma.strength;
> +        sblur->chroma.threshold = sblur->luma.threshold;
> +    } else {
> +        sblur->chroma.radius    = cradius;
> +        sblur->chroma.strength  = cstrength;
> +        sblur->chroma.threshold = cthreshold;
> +    }
> +
> +    sblur->luma.quality = sblur->chroma.quality = 3.0;
> +
> +    return validate_input(ctx,
> +                          sblur->luma.radius,    sblur->chroma.radius,
> +                          sblur->luma.strength,  sblur->chroma.strength,
> +                          sblur->luma.threshold, sblur->chroma.threshold);
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    SmartblurContext *sblur = ctx->priv;
> +
> +    sws_freeContext(sblur->luma.filter_context);
> +    sws_freeContext(sblur->chroma.filter_context);
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum PixelFormat pix_fmts[] = {
> +        PIX_FMT_YUV444P,      PIX_FMT_YUV422P,
> +        PIX_FMT_YUV420P,      PIX_FMT_YUV411P,
> +        PIX_FMT_YUV410P,      PIX_FMT_YUV440P,

> +        PIX_FMT_YUVA420P,

Dunno if in this case we should blur the alpha plane as well (just
remove it when in doubt), since it is not supported in the ported
filter.

> +        PIX_FMT_NONE
> +    };
> +
> +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
> +
> +    return 0;
> +}
> +

> +static int allocStuff(FilterParam *f, int width, int height)

Please use a sane name here.

> +{
> +    SwsVector *vec;
> +    SwsFilter sws_filter;
> +
> +    vec = sws_getGaussianVec(f->radius, f->quality);
> +    sws_scaleVec(vec, f->strength);
> +    vec->coeff[vec->length / 2] += 1.0 - f->strength;
> +    sws_filter.lumH = sws_filter.lumV = vec;
> +    sws_filter.chrH = sws_filter.chrV = NULL;
> +    f->filter_context = sws_getContext(width, height, PIX_FMT_GRAY8,
> +                                       width, height, PIX_FMT_GRAY8,
> +                                       SWS_BICUBIC, &sws_filter, NULL, NULL);

Missing failure checks.

Note: maybe scaling flags could be made configurable.

> +
> +    sws_freeVec(vec);
> +
> +    return 0;
> +}
> +
> +static int config_props(AVFilterLink *inlink)
> +{
> +    SmartblurContext *sblur = inlink->dst->priv;
> +    const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
> +
> +    sblur->hsub = desc->log2_chroma_w;
> +    sblur->vsub = desc->log2_chroma_h;
> +
> +    allocStuff(&sblur->luma, inlink->w, inlink->h);
> +    allocStuff(&sblur->chroma, inlink->w >> sblur->hsub, inlink->h >> sblur->vsub);
> +
> +    return 0;
> +}
> +
> +static void blur(uint8_t *dst, int dst_linesize,
> +                 uint8_t *src, int src_linesize,
> +                 int w, int h, int threshold,
> +                 struct SwsContext *filter_context)
> +{
> +    int x, y;
> +    int orig, filtered;
> +    int diff;
> +    const uint8_t* const src_array[1] = {src};
> +    uint8_t *dst_array[1]             = {dst};
> +    int src_linesize_array[1] = {src_linesize};
> +    int dst_linesize_array[1] = {dst_linesize};


> +
> +    sws_scale(filter_context, src_array, src_linesize_array, 0, h, dst_array, dst_linesize_array);
> +
> +    if (threshold > 0) {
> +        for (y = 0; y < h; ++y)
> +            for (x = 0; x < w; ++x) {
> +                orig     = src[x + y * src_linesize];
> +                filtered = dst[x + y * dst_linesize];
> +                diff     = orig - filtered;
> +
> +                if (diff > 0)
> +                    if (diff > 2 * threshold)
> +                        dst[x + y * dst_linesize] = orig;
> +                    else /* add 'diff' and substract 'threshold' from 'filtered' */
> +                        dst[x + y * dst_linesize] = orig - threshold;
> +                else
> +                    if (-diff > 2 * threshold)
> +                        dst[x + y * dst_linesize] = orig;
> +                    else /* add 'diff' and 'threshold' to 'filtered' */
> +                        dst[x + y * dst_linesize] = orig + threshold;
> +            }
> +    } else if (threshold < 0) {
> +        for (y = 0; y < h; ++y)
> +            for (x = 0; x < w; ++x) {
> +                orig = src[x + y * src_linesize];
> +                filtered = dst[x + y * dst_linesize];
> +                diff = orig - filtered;
> +
> +                if (diff > 0)
> +                    if (diff <= -threshold)
> +                        dst[x + y * dst_linesize] = orig;
> +                    else if (diff <= -2 * threshold) /* substract 'diff' and 'threshold' from 'orig' */
> +                        dst[x + y * dst_linesize] = filtered - threshold;
> +                else
> +                    if (diff >= threshold)
> +                        dst[x + y * dst_linesize] = orig;
> +                    else if (diff >= 2 * threshold) /* add 'threshold' and substract 'diff' from 'orig' */
> +                        dst[x + y * dst_linesize] = filtered + threshold;
> +            }
> +    }
> +}
> +
> +static int end_frame(AVFilterLink *inlink)
> +{
> +    SmartblurContext  *sblur  = inlink->dst->priv;
> +    AVFilterBufferRef *inpic  = inlink->cur_buf;
> +    AVFilterBufferRef *outpic = inlink->dst->outputs[0]->out_buf;
> +    int cw = inlink->w >> sblur->hsub;
> +    int ch = inlink->h >> sblur->vsub;
> +

> +    blur(outpic->data[0], outpic->linesize[0],
> +         inpic->data[0],  inpic->linesize[0],
> +         inlink->w, inlink->h, sblur->luma.threshold,
> +         sblur->luma.filter_context);
> +    blur(outpic->data[1], outpic->linesize[1],
> +         inpic->data[1],  inpic->linesize[1],
> +         cw, ch, sblur->chroma.threshold,
> +         sblur->chroma.filter_context);
> +    blur(outpic->data[2], outpic->linesize[2],
> +         inpic->data[2],  inpic->linesize[2],
> +         cw, ch, sblur->chroma.threshold,
> +         sblur->chroma.filter_context);

Adding support to GRAY8 should be very simple.

> +
> +    return ff_end_frame(inlink->dst->outputs[0]);
> +}
> +
[...]
-- 
FFmpeg = Furious & Fanciful Multimedia Political Eretic Gladiator


More information about the ffmpeg-devel mailing list