diff mbox

[FFmpeg-devel,V1] lavfi/superfastblur: add superfastblur filter

Message ID 1573545091-8319-1-git-send-email-mypopydev@gmail.com
State Superseded
Headers show

Commit Message

Jun Zhao Nov. 12, 2019, 7:51 a.m. UTC
From: Jun Zhao <barryjzhao@tencent.com>

add superfastblur filter

Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
---
 doc/filters.texi               |   15 ++
 libavfilter/Makefile           |    1 +
 libavfilter/allfilters.c       |    1 +
 libavfilter/vf_superfastblur.c |  275 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 292 insertions(+), 0 deletions(-)
 create mode 100644 libavfilter/vf_superfastblur.c

Comments

Liu Steven Nov. 12, 2019, 9:08 a.m. UTC | #1
> 在 2019年11月12日,15:51,Jun Zhao <mypopydev@gmail.com> 写道:
> 
> From: Jun Zhao <barryjzhao@tencent.com>
> 
> add superfastblur filter
> 
> Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
> ---
> doc/filters.texi               |   15 ++
> libavfilter/Makefile           |    1 +
> libavfilter/allfilters.c       |    1 +
> libavfilter/vf_superfastblur.c |  275 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 292 insertions(+), 0 deletions(-)
> create mode 100644 libavfilter/vf_superfastblur.c
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 6800124..c7d1893 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -17453,6 +17453,21 @@ Interpolate) pixel art scaling algorithm.
> 
> Useful for enlarging pixel art images without reducing sharpness.
> 
> +@section superfastblur
> +
> +Blur the input image with super fast blur algorithm, multiple invocations of this
> +filter with a small radius will approximate a gaussian blur quite well.
> +
> +This filter accepts the following options:
> +
> +@table @option
> +@item radius
> +@item r
> +Set the blurring box radius. The option value must be a int number in
> +the range [1, 10] that specifies the blur box size of the superfast blur filter
> +used to blur the image. Default value is @code{2}..
> +@end table
> +
> @section swaprect
> 
> Swap two rectangular objects in video.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index fce9303..db4d5e6 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -396,6 +396,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER)               += vf_stereo3d.o
> OBJS-$(CONFIG_STREAMSELECT_FILTER)           += f_streamselect.o framesync.o
> OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
> OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
> +OBJS-$(CONFIG_SUPEREQUALIZER_FILTER)         += vf_superfastblur.o
> OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
> OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
> OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 7c1e19e..d507bc5 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -377,6 +377,7 @@ extern AVFilter ff_vf_stereo3d;
> extern AVFilter ff_vf_streamselect;
> extern AVFilter ff_vf_subtitles;
> extern AVFilter ff_vf_super2xsai;
> +extern AVFilter ff_vf_superfastblur;
> extern AVFilter ff_vf_swaprect;
> extern AVFilter ff_vf_swapuv;
> extern AVFilter ff_vf_tblend;
> diff --git a/libavfilter/vf_superfastblur.c b/libavfilter/vf_superfastblur.c
> new file mode 100644
> index 0000000..a6428cf
> --- /dev/null
> +++ b/libavfilter/vf_superfastblur.c
> @@ -0,0 +1,275 @@
> +/*
> + * Copyright (c) 2019 Jun Zhao
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser 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
> + * Super fast blur filter
> + *
> + * @see http://incubator.quasimondo.com/processing/superfast_blur.php
> + */
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/opt.h"
> +#include "avfilter.h"
> +#include "formats.h"
> +#include "internal.h"
> +#include "video.h"
> +
> +typedef struct SuperFastBlurContext {
> +    const AVClass *class;
> +
> +    int radius;
> +
> +    uint32_t *vMIN;
> +    uint32_t *vMAX;
> +
> +    uint8_t *r;
> +    uint8_t *g;
> +    uint8_t *b;
> +
> +    uint8_t *dv;
> +} SuperFastBlurContext;
> +
> +#define OFFSET(x) offsetof(SuperFastBlurContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +static const AVOption superfastblur_options[] = {
> +    { "radius", "Radius of the super fast blurring box", OFFSET(radius),  AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
> +    { "r",      "Radius of the super fast blurring box", OFFSET(radius),  AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(superfastblur);
> +
> +static av_cold int init(AVFilterContext *ctx)
> +{
> +    SuperFastBlurContext *s = ctx->priv;
> +
> +    // This line precalculates a lookup table for all the possible
> +    // mean values that can occur. This is to avoid costly division
> +    // in the inner loop. On some systems doing the division directly
> +    // instead of a doing an array lookup might actually be faster
> +    // nowadays.
> +    uint32_t div = 2 * s->radius + 1;
> +    s->dv = av_malloc(sizeof(*s->dv) * 256 * div);
> +    if (!s->dv)
> +        return AVERROR(ENOMEM);
> +    for (int i = 0; i < 256 * div; i++)
> +        s->dv[i] = (i / div);
> +
> +    return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum AVPixelFormat pix_fmts[] = {
> +        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
> +
> +        AV_PIX_FMT_NONE
Empty line?
> +    };
> +
> +    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
> +    if (!fmts_list)
> +        return AVERROR(ENOMEM);
> +    return ff_set_common_formats(ctx, fmts_list);
> +}
> +
> +static int config_props(AVFilterLink *inlink)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    SuperFastBlurContext *s = ctx->priv;
> +
> +    uint32_t wm = inlink->w - 1;
> +    uint32_t wh = inlink->w * inlink->h;
> +
> +    s->vMIN = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
> +    s->vMAX = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
> +    s->r = av_malloc(sizeof(*s->r) * wh);
> +    s->g = av_malloc(sizeof(*s->g) * wh);
> +    s->b = av_malloc(sizeof(*s->b) * wh);
> +
> +    if (!s->vMIN || !s->vMAX || !s->r || !s->g || !s->b)
> +        return AVERROR(ENOMEM);
> +
> +    return 0;
> +}
> +
> +/*
> + * Super Fast Blur v1.1+
> + * by Mario Klingemann <http://incubator.quasimondo.com>
> + * Original address: http://incubator.quasimondo.com/processing/superfastblur.pde
> + *
> + * Tip: Multiple invocations of this filter with a small
> + * radius will approximate a gaussian blur quite well.
> + */
> +static void superfast_blur(SuperFastBlurContext *s, uint8_t *pix, int w, int h, int nb_comps)
> +{
> +    uint32_t wm, hm;
> +    uint32_t *vMIN, *vMAX;
> +    uint8_t *r, *g, *b, *dv;
> +    uint32_t rsum, gsum, bsum;
> +    uint32_t p, p1, p2, yi, yw;
> +
> +    int radius;
> +
> +    int x, y, i, yp;
> +
> +    wm = w - 1;
> +    hm = h - 1;
> +
> +    vMIN = s->vMIN;
> +    vMAX = s->vMAX;
> +    r = s->r;
> +    g = s->g;
> +    b = s->b;
> +
> +    dv = s->dv;
> +
> +    radius = s->radius;
> +
> +    yw = yi = 0;
> +    for (y = 0; y < h; y++) {
> +        rsum = gsum = bsum = 0;
> +        // The reason why this algorithm is fast is that it uses a sliding
> +        // window and thus reduces the number of required pixel lookups.
> +        // The window slides from the left edge to the right (and in the
> +        // second pass from top to bottom) and only adds one pixel at the
> +        // right and removes one from the left. The code above initializes
> +        // the window by prefilling the window with the leftmost edge pixel
> +        // depending on the kernel size.
> +        for (i = -radius; i <= radius; i++) {
> +            p = (yi + FFMIN(wm, FFMAX(i, 0))) * nb_comps;
> +            rsum += pix[p];
> +            gsum += pix[p + 1];
> +            bsum += pix[p + 2];
> +        }
> +
> +        for (x = 0; x < w; x++) {
> +            r[yi] = dv[rsum];
> +            g[yi] = dv[gsum];
> +            b[yi] = dv[bsum];
> +
> +            // adds a new pixel but at the same time handles the border
> +            // conditions (when the window tries to read or remove pixels
> +            // outside the bitmap).
> +            if (y == 0) {
> +                vMIN[x] = FFMIN(x + radius + 1, wm);
> +                vMAX[x] = FFMAX(x - radius, 0);
> +            }
> +            p1 = (yw + vMIN[x]) * nb_comps;
> +            p2 = (yw + vMAX[x]) * nb_comps;
> +            rsum += pix[p1]     - pix[p2];
> +            gsum += pix[p1 + 1] - pix[p2 + 1];
> +            bsum += pix[p1 + 2] - pix[p2 + 2];
> +            yi++;
> +        }
> +        yw += w;
> +    }
> +
> +    for (x = 0; x < w; x++) {
> +        rsum = gsum = bsum = 0;
> +        yp = -radius * w;
> +        for (i = -radius; i <= radius; i++) {
> +            yi = FFMAX(0, yp) + x;
> +            rsum += r[yi];
> +            gsum += g[yi];
> +            bsum += b[yi];
> +            yp += w;
> +        }
> +
> +        yi = x;
> +        for (y = 0; y < h; y++) {
> +            pix[yi * nb_comps]     = dv[bsum];
> +            pix[yi * nb_comps + 1] = dv[gsum];
> +            pix[yi * nb_comps + 2] = dv[rsum];
> +
> +            if (x == 0) {
> +                vMIN[y] = FFMIN(y + radius + 1, hm) * w;
> +                vMAX[y] = FFMAX(y - radius, 0) * w;
> +            }
> +            p1 = x + vMIN[y];
> +            p2 = x + vMAX[y];
> +
> +            // rsum, gsum and bsum is the accumulated sum of pixels inside
> +            // the sliding window. What you see is the new pixel on the
> +            // right side being added to the sum and the leftmost pixel
> +            // i nthe window being removed from the sum.
> +            rsum += r[p1] - r[p2];
> +            gsum += g[p1] - g[p2];
> +            bsum += b[p1] - b[p2];
> +            yi += w;
> +        }
> +    }
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    SuperFastBlurContext *s = ctx->priv;
> +    AVFilterLink *outlink = ctx->outputs[0];
> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
> +
> +    superfast_blur(s, in->data[0], inlink->w, inlink->h, desc->nb_components);
> +
> +    return ff_filter_frame(outlink, in);
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    SuperFastBlurContext *s = ctx->priv;
> +
> +    av_freep(&s->r);
> +    av_freep(&s->g);
> +    av_freep(&s->b);
> +    av_freep(&s->vMIN);
> +    av_freep(&s->vMAX);
> +    av_freep(&s->dv);
> +}
> +
> +static const AVFilterPad superfastblur_inputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_VIDEO,
> +        .config_props = config_props,
> +        .filter_frame = filter_frame,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad superfastblur_outputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_VIDEO,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter ff_vf_superfastblur = {
> +    .name          = "superfastblur",
> +    .description   = NULL_IF_CONFIG_SMALL("Blur the input with super fast blur algorithm."),
> +    .priv_size     = sizeof(SuperFastBlurContext),
> +    .init          = init,
> +    .uninit        = uninit,
> +    .query_formats = query_formats,
> +    .inputs        = superfastblur_inputs,
> +    .outputs       = superfastblur_outputs,
> +    .priv_class    = &superfastblur_class,
> +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
> +};
> -- 
> 1.7.1
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe”.

LGTM
BTW, I saw you usually submit multi thread performance optimization for other filter,
Why don’y do that in this filter one time complete?
I think maybe only one patch complete the whole operation maybe better.

Thanks
Steven
Paul B Mahol Nov. 12, 2019, 9:31 a.m. UTC | #2
Isn't this same as boxblur?

On 11/12/19, Steven Liu <lq@chinaffmpeg.org> wrote:
>
>
>> 在 2019年11月12日,15:51,Jun Zhao <mypopydev@gmail.com> 写道:
>>
>> From: Jun Zhao <barryjzhao@tencent.com>
>>
>> add superfastblur filter
>>
>> Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
>> ---
>> doc/filters.texi               |   15 ++
>> libavfilter/Makefile           |    1 +
>> libavfilter/allfilters.c       |    1 +
>> libavfilter/vf_superfastblur.c |  275
>> ++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 292 insertions(+), 0 deletions(-)
>> create mode 100644 libavfilter/vf_superfastblur.c
>>
>> diff --git a/doc/filters.texi b/doc/filters.texi
>> index 6800124..c7d1893 100644
>> --- a/doc/filters.texi
>> +++ b/doc/filters.texi
>> @@ -17453,6 +17453,21 @@ Interpolate) pixel art scaling algorithm.
>>
>> Useful for enlarging pixel art images without reducing sharpness.
>>
>> +@section superfastblur
>> +
>> +Blur the input image with super fast blur algorithm, multiple invocations
>> of this
>> +filter with a small radius will approximate a gaussian blur quite well.
>> +
>> +This filter accepts the following options:
>> +
>> +@table @option
>> +@item radius
>> +@item r
>> +Set the blurring box radius. The option value must be a int number in
>> +the range [1, 10] that specifies the blur box size of the superfast blur
>> filter
>> +used to blur the image. Default value is @code{2}..
>> +@end table
>> +
>> @section swaprect
>>
>> Swap two rectangular objects in video.
>> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
>> index fce9303..db4d5e6 100644
>> --- a/libavfilter/Makefile
>> +++ b/libavfilter/Makefile
>> @@ -396,6 +396,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER)               +=
>> vf_stereo3d.o
>> OBJS-$(CONFIG_STREAMSELECT_FILTER)           += f_streamselect.o
>> framesync.o
>> OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
>> OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
>> +OBJS-$(CONFIG_SUPEREQUALIZER_FILTER)         += vf_superfastblur.o
>> OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
>> OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
>> OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
>> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
>> index 7c1e19e..d507bc5 100644
>> --- a/libavfilter/allfilters.c
>> +++ b/libavfilter/allfilters.c
>> @@ -377,6 +377,7 @@ extern AVFilter ff_vf_stereo3d;
>> extern AVFilter ff_vf_streamselect;
>> extern AVFilter ff_vf_subtitles;
>> extern AVFilter ff_vf_super2xsai;
>> +extern AVFilter ff_vf_superfastblur;
>> extern AVFilter ff_vf_swaprect;
>> extern AVFilter ff_vf_swapuv;
>> extern AVFilter ff_vf_tblend;
>> diff --git a/libavfilter/vf_superfastblur.c
>> b/libavfilter/vf_superfastblur.c
>> new file mode 100644
>> index 0000000..a6428cf
>> --- /dev/null
>> +++ b/libavfilter/vf_superfastblur.c
>> @@ -0,0 +1,275 @@
>> +/*
>> + * Copyright (c) 2019 Jun Zhao
>> + *
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 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
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser 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
>> + * Super fast blur filter
>> + *
>> + * @see http://incubator.quasimondo.com/processing/superfast_blur.php
>> + */
>> +
>> +#include "libavutil/avassert.h"
>> +#include "libavutil/imgutils.h"
>> +#include "libavutil/opt.h"
>> +#include "avfilter.h"
>> +#include "formats.h"
>> +#include "internal.h"
>> +#include "video.h"
>> +
>> +typedef struct SuperFastBlurContext {
>> +    const AVClass *class;
>> +
>> +    int radius;
>> +
>> +    uint32_t *vMIN;
>> +    uint32_t *vMAX;
>> +
>> +    uint8_t *r;
>> +    uint8_t *g;
>> +    uint8_t *b;
>> +
>> +    uint8_t *dv;
>> +} SuperFastBlurContext;
>> +
>> +#define OFFSET(x) offsetof(SuperFastBlurContext, x)
>> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
>> +static const AVOption superfastblur_options[] = {
>> +    { "radius", "Radius of the super fast blurring box", OFFSET(radius),
>> AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
>> +    { "r",      "Radius of the super fast blurring box", OFFSET(radius),
>> AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
>> +    { NULL }
>> +};
>> +
>> +AVFILTER_DEFINE_CLASS(superfastblur);
>> +
>> +static av_cold int init(AVFilterContext *ctx)
>> +{
>> +    SuperFastBlurContext *s = ctx->priv;
>> +
>> +    // This line precalculates a lookup table for all the possible
>> +    // mean values that can occur. This is to avoid costly division
>> +    // in the inner loop. On some systems doing the division directly
>> +    // instead of a doing an array lookup might actually be faster
>> +    // nowadays.
>> +    uint32_t div = 2 * s->radius + 1;
>> +    s->dv = av_malloc(sizeof(*s->dv) * 256 * div);
>> +    if (!s->dv)
>> +        return AVERROR(ENOMEM);
>> +    for (int i = 0; i < 256 * div; i++)
>> +        s->dv[i] = (i / div);
>> +
>> +    return 0;
>> +}
>> +
>> +static int query_formats(AVFilterContext *ctx)
>> +{
>> +    static const enum AVPixelFormat pix_fmts[] = {
>> +        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
>> +
>> +        AV_PIX_FMT_NONE
> Empty line?
>> +    };
>> +
>> +    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
>> +    if (!fmts_list)
>> +        return AVERROR(ENOMEM);
>> +    return ff_set_common_formats(ctx, fmts_list);
>> +}
>> +
>> +static int config_props(AVFilterLink *inlink)
>> +{
>> +    AVFilterContext *ctx = inlink->dst;
>> +    SuperFastBlurContext *s = ctx->priv;
>> +
>> +    uint32_t wm = inlink->w - 1;
>> +    uint32_t wh = inlink->w * inlink->h;
>> +
>> +    s->vMIN = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
>> +    s->vMAX = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
>> +    s->r = av_malloc(sizeof(*s->r) * wh);
>> +    s->g = av_malloc(sizeof(*s->g) * wh);
>> +    s->b = av_malloc(sizeof(*s->b) * wh);
>> +
>> +    if (!s->vMIN || !s->vMAX || !s->r || !s->g || !s->b)
>> +        return AVERROR(ENOMEM);
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Super Fast Blur v1.1+
>> + * by Mario Klingemann <http://incubator.quasimondo.com>
>> + * Original address:
>> http://incubator.quasimondo.com/processing/superfastblur.pde
>> + *
>> + * Tip: Multiple invocations of this filter with a small
>> + * radius will approximate a gaussian blur quite well.
>> + */
>> +static void superfast_blur(SuperFastBlurContext *s, uint8_t *pix, int w,
>> int h, int nb_comps)
>> +{
>> +    uint32_t wm, hm;
>> +    uint32_t *vMIN, *vMAX;
>> +    uint8_t *r, *g, *b, *dv;
>> +    uint32_t rsum, gsum, bsum;
>> +    uint32_t p, p1, p2, yi, yw;
>> +
>> +    int radius;
>> +
>> +    int x, y, i, yp;
>> +
>> +    wm = w - 1;
>> +    hm = h - 1;
>> +
>> +    vMIN = s->vMIN;
>> +    vMAX = s->vMAX;
>> +    r = s->r;
>> +    g = s->g;
>> +    b = s->b;
>> +
>> +    dv = s->dv;
>> +
>> +    radius = s->radius;
>> +
>> +    yw = yi = 0;
>> +    for (y = 0; y < h; y++) {
>> +        rsum = gsum = bsum = 0;
>> +        // The reason why this algorithm is fast is that it uses a
>> sliding
>> +        // window and thus reduces the number of required pixel lookups.
>> +        // The window slides from the left edge to the right (and in the
>> +        // second pass from top to bottom) and only adds one pixel at the
>> +        // right and removes one from the left. The code above
>> initializes
>> +        // the window by prefilling the window with the leftmost edge
>> pixel
>> +        // depending on the kernel size.
>> +        for (i = -radius; i <= radius; i++) {
>> +            p = (yi + FFMIN(wm, FFMAX(i, 0))) * nb_comps;
>> +            rsum += pix[p];
>> +            gsum += pix[p + 1];
>> +            bsum += pix[p + 2];
>> +        }
>> +
>> +        for (x = 0; x < w; x++) {
>> +            r[yi] = dv[rsum];
>> +            g[yi] = dv[gsum];
>> +            b[yi] = dv[bsum];
>> +
>> +            // adds a new pixel but at the same time handles the border
>> +            // conditions (when the window tries to read or remove pixels
>> +            // outside the bitmap).
>> +            if (y == 0) {
>> +                vMIN[x] = FFMIN(x + radius + 1, wm);
>> +                vMAX[x] = FFMAX(x - radius, 0);
>> +            }
>> +            p1 = (yw + vMIN[x]) * nb_comps;
>> +            p2 = (yw + vMAX[x]) * nb_comps;
>> +            rsum += pix[p1]     - pix[p2];
>> +            gsum += pix[p1 + 1] - pix[p2 + 1];
>> +            bsum += pix[p1 + 2] - pix[p2 + 2];
>> +            yi++;
>> +        }
>> +        yw += w;
>> +    }
>> +
>> +    for (x = 0; x < w; x++) {
>> +        rsum = gsum = bsum = 0;
>> +        yp = -radius * w;
>> +        for (i = -radius; i <= radius; i++) {
>> +            yi = FFMAX(0, yp) + x;
>> +            rsum += r[yi];
>> +            gsum += g[yi];
>> +            bsum += b[yi];
>> +            yp += w;
>> +        }
>> +
>> +        yi = x;
>> +        for (y = 0; y < h; y++) {
>> +            pix[yi * nb_comps]     = dv[bsum];
>> +            pix[yi * nb_comps + 1] = dv[gsum];
>> +            pix[yi * nb_comps + 2] = dv[rsum];
>> +
>> +            if (x == 0) {
>> +                vMIN[y] = FFMIN(y + radius + 1, hm) * w;
>> +                vMAX[y] = FFMAX(y - radius, 0) * w;
>> +            }
>> +            p1 = x + vMIN[y];
>> +            p2 = x + vMAX[y];
>> +
>> +            // rsum, gsum and bsum is the accumulated sum of pixels
>> inside
>> +            // the sliding window. What you see is the new pixel on the
>> +            // right side being added to the sum and the leftmost pixel
>> +            // i nthe window being removed from the sum.
>> +            rsum += r[p1] - r[p2];
>> +            gsum += g[p1] - g[p2];
>> +            bsum += b[p1] - b[p2];
>> +            yi += w;
>> +        }
>> +    }
>> +}
>> +
>> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>> +{
>> +    AVFilterContext *ctx = inlink->dst;
>> +    SuperFastBlurContext *s = ctx->priv;
>> +    AVFilterLink *outlink = ctx->outputs[0];
>> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
>> +
>> +    superfast_blur(s, in->data[0], inlink->w, inlink->h,
>> desc->nb_components);
>> +
>> +    return ff_filter_frame(outlink, in);
>> +}
>> +
>> +static av_cold void uninit(AVFilterContext *ctx)
>> +{
>> +    SuperFastBlurContext *s = ctx->priv;
>> +
>> +    av_freep(&s->r);
>> +    av_freep(&s->g);
>> +    av_freep(&s->b);
>> +    av_freep(&s->vMIN);
>> +    av_freep(&s->vMAX);
>> +    av_freep(&s->dv);
>> +}
>> +
>> +static const AVFilterPad superfastblur_inputs[] = {
>> +    {
>> +        .name         = "default",
>> +        .type         = AVMEDIA_TYPE_VIDEO,
>> +        .config_props = config_props,
>> +        .filter_frame = filter_frame,
>> +    },
>> +    { NULL }
>> +};
>> +
>> +static const AVFilterPad superfastblur_outputs[] = {
>> +    {
>> +        .name = "default",
>> +        .type = AVMEDIA_TYPE_VIDEO,
>> +    },
>> +    { NULL }
>> +};
>> +
>> +AVFilter ff_vf_superfastblur = {
>> +    .name          = "superfastblur",
>> +    .description   = NULL_IF_CONFIG_SMALL("Blur the input with super fast
>> blur algorithm."),
>> +    .priv_size     = sizeof(SuperFastBlurContext),
>> +    .init          = init,
>> +    .uninit        = uninit,
>> +    .query_formats = query_formats,
>> +    .inputs        = superfastblur_inputs,
>> +    .outputs       = superfastblur_outputs,
>> +    .priv_class    = &superfastblur_class,
>> +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
>> +};
>> --
>> 1.7.1
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe”.
>
> LGTM
> BTW, I saw you usually submit multi thread performance optimization for
> other filter,
> Why don’y do that in this filter one time complete?
> I think maybe only one patch complete the whole operation maybe better.
>
> Thanks
> Steven
>
>
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Jun Zhao Nov. 12, 2019, 10:21 a.m. UTC | #3
On Tue, Nov 12, 2019 at 4:14 PM Jun Zhao <mypopydev@gmail.com> wrote:
>
> From: Jun Zhao <barryjzhao@tencent.com>
>
> add superfastblur filter
>
> Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
> ---
>  doc/filters.texi               |   15 ++
>  libavfilter/Makefile           |    1 +
>  libavfilter/allfilters.c       |    1 +
>  libavfilter/vf_superfastblur.c |  275 ++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 292 insertions(+), 0 deletions(-)
>  create mode 100644 libavfilter/vf_superfastblur.c
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 6800124..c7d1893 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -17453,6 +17453,21 @@ Interpolate) pixel art scaling algorithm.
>
>  Useful for enlarging pixel art images without reducing sharpness.
>
> +@section superfastblur
> +
> +Blur the input image with super fast blur algorithm, multiple invocations of this
> +filter with a small radius will approximate a gaussian blur quite well.
> +
> +This filter accepts the following options:
> +
> +@table @option
> +@item radius
> +@item r
> +Set the blurring box radius. The option value must be a int number in
> +the range [1, 10] that specifies the blur box size of the superfast blur filter
> +used to blur the image. Default value is @code{2}..
> +@end table
> +
>  @section swaprect
>
>  Swap two rectangular objects in video.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index fce9303..db4d5e6 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -396,6 +396,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER)               += vf_stereo3d.o
>  OBJS-$(CONFIG_STREAMSELECT_FILTER)           += f_streamselect.o framesync.o
>  OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
>  OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
> +OBJS-$(CONFIG_SUPEREQUALIZER_FILTER)         += vf_superfastblur.o
>  OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
>  OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
>  OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 7c1e19e..d507bc5 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -377,6 +377,7 @@ extern AVFilter ff_vf_stereo3d;
>  extern AVFilter ff_vf_streamselect;
>  extern AVFilter ff_vf_subtitles;
>  extern AVFilter ff_vf_super2xsai;
> +extern AVFilter ff_vf_superfastblur;
>  extern AVFilter ff_vf_swaprect;
>  extern AVFilter ff_vf_swapuv;
>  extern AVFilter ff_vf_tblend;
> diff --git a/libavfilter/vf_superfastblur.c b/libavfilter/vf_superfastblur.c
> new file mode 100644
> index 0000000..a6428cf
> --- /dev/null
> +++ b/libavfilter/vf_superfastblur.c
> @@ -0,0 +1,275 @@
> +/*
> + * Copyright (c) 2019 Jun Zhao
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser 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
> + * Super fast blur filter
> + *
> + * @see http://incubator.quasimondo.com/processing/superfast_blur.php
> + */
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/opt.h"
> +#include "avfilter.h"
> +#include "formats.h"
> +#include "internal.h"
> +#include "video.h"
> +
> +typedef struct SuperFastBlurContext {
> +    const AVClass *class;
> +
> +    int radius;
> +
> +    uint32_t *vMIN;
> +    uint32_t *vMAX;
> +
> +    uint8_t *r;
> +    uint8_t *g;
> +    uint8_t *b;
> +
> +    uint8_t *dv;
> +} SuperFastBlurContext;
> +
> +#define OFFSET(x) offsetof(SuperFastBlurContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +static const AVOption superfastblur_options[] = {
> +    { "radius", "Radius of the super fast blurring box", OFFSET(radius),  AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
> +    { "r",      "Radius of the super fast blurring box", OFFSET(radius),  AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(superfastblur);
> +
> +static av_cold int init(AVFilterContext *ctx)
> +{
> +    SuperFastBlurContext *s = ctx->priv;
> +
> +    // This line precalculates a lookup table for all the possible
> +    // mean values that can occur. This is to avoid costly division
> +    // in the inner loop. On some systems doing the division directly
> +    // instead of a doing an array lookup might actually be faster
> +    // nowadays.
> +    uint32_t div = 2 * s->radius + 1;
> +    s->dv = av_malloc(sizeof(*s->dv) * 256 * div);
> +    if (!s->dv)
> +        return AVERROR(ENOMEM);
> +    for (int i = 0; i < 256 * div; i++)
> +        s->dv[i] = (i / div);
> +
> +    return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum AVPixelFormat pix_fmts[] = {
> +        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
> +
> +        AV_PIX_FMT_NONE
> +    };
> +
> +    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
> +    if (!fmts_list)
> +        return AVERROR(ENOMEM);
> +    return ff_set_common_formats(ctx, fmts_list);
> +}
> +
> +static int config_props(AVFilterLink *inlink)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    SuperFastBlurContext *s = ctx->priv;
> +
> +    uint32_t wm = inlink->w - 1;
> +    uint32_t wh = inlink->w * inlink->h;
> +
> +    s->vMIN = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
> +    s->vMAX = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
> +    s->r = av_malloc(sizeof(*s->r) * wh);
> +    s->g = av_malloc(sizeof(*s->g) * wh);
> +    s->b = av_malloc(sizeof(*s->b) * wh);
> +
> +    if (!s->vMIN || !s->vMAX || !s->r || !s->g || !s->b)
> +        return AVERROR(ENOMEM);
> +
> +    return 0;
> +}
> +
> +/*
> + * Super Fast Blur v1.1+
> + * by Mario Klingemann <http://incubator.quasimondo.com>
> + * Original address: http://incubator.quasimondo.com/processing/superfastblur.pde
> + *
> + * Tip: Multiple invocations of this filter with a small
> + * radius will approximate a gaussian blur quite well.
> + */
> +static void superfast_blur(SuperFastBlurContext *s, uint8_t *pix, int w, int h, int nb_comps)
> +{
> +    uint32_t wm, hm;
> +    uint32_t *vMIN, *vMAX;
> +    uint8_t *r, *g, *b, *dv;
> +    uint32_t rsum, gsum, bsum;
> +    uint32_t p, p1, p2, yi, yw;
> +
> +    int radius;
> +
> +    int x, y, i, yp;
> +
> +    wm = w - 1;
> +    hm = h - 1;
> +
> +    vMIN = s->vMIN;
> +    vMAX = s->vMAX;
> +    r = s->r;
> +    g = s->g;
> +    b = s->b;
> +
> +    dv = s->dv;
> +
> +    radius = s->radius;
> +
> +    yw = yi = 0;
> +    for (y = 0; y < h; y++) {
> +        rsum = gsum = bsum = 0;
> +        // The reason why this algorithm is fast is that it uses a sliding
> +        // window and thus reduces the number of required pixel lookups.
> +        // The window slides from the left edge to the right (and in the
> +        // second pass from top to bottom) and only adds one pixel at the
> +        // right and removes one from the left. The code above initializes
> +        // the window by prefilling the window with the leftmost edge pixel
> +        // depending on the kernel size.
> +        for (i = -radius; i <= radius; i++) {
> +            p = (yi + FFMIN(wm, FFMAX(i, 0))) * nb_comps;
> +            rsum += pix[p];
> +            gsum += pix[p + 1];
> +            bsum += pix[p + 2];
> +        }
> +
> +        for (x = 0; x < w; x++) {
> +            r[yi] = dv[rsum];
> +            g[yi] = dv[gsum];
> +            b[yi] = dv[bsum];
> +
> +            // adds a new pixel but at the same time handles the border
> +            // conditions (when the window tries to read or remove pixels
> +            // outside the bitmap).
> +            if (y == 0) {
> +                vMIN[x] = FFMIN(x + radius + 1, wm);
> +                vMAX[x] = FFMAX(x - radius, 0);
> +            }
> +            p1 = (yw + vMIN[x]) * nb_comps;
> +            p2 = (yw + vMAX[x]) * nb_comps;
> +            rsum += pix[p1]     - pix[p2];
> +            gsum += pix[p1 + 1] - pix[p2 + 1];
> +            bsum += pix[p1 + 2] - pix[p2 + 2];
> +            yi++;
> +        }
> +        yw += w;
> +    }
> +
> +    for (x = 0; x < w; x++) {
> +        rsum = gsum = bsum = 0;
> +        yp = -radius * w;
> +        for (i = -radius; i <= radius; i++) {
> +            yi = FFMAX(0, yp) + x;
> +            rsum += r[yi];
> +            gsum += g[yi];
> +            bsum += b[yi];
> +            yp += w;
> +        }
> +
> +        yi = x;
> +        for (y = 0; y < h; y++) {
> +            pix[yi * nb_comps]     = dv[bsum];
> +            pix[yi * nb_comps + 1] = dv[gsum];
> +            pix[yi * nb_comps + 2] = dv[rsum];
> +
need to swap bsum and rsum, will fix in next version
> +            if (x == 0) {
> +                vMIN[y] = FFMIN(y + radius + 1, hm) * w;
> +                vMAX[y] = FFMAX(y - radius, 0) * w;
> +            }
> +            p1 = x + vMIN[y];
> +            p2 = x + vMAX[y];
> +
> +            // rsum, gsum and bsum is the accumulated sum of pixels inside
> +            // the sliding window. What you see is the new pixel on the
> +            // right side being added to the sum and the leftmost pixel
> +            // i nthe window being removed from the sum.
> +            rsum += r[p1] - r[p2];
> +            gsum += g[p1] - g[p2];
> +            bsum += b[p1] - b[p2];
> +            yi += w;
> +        }
> +    }
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    SuperFastBlurContext *s = ctx->priv;
> +    AVFilterLink *outlink = ctx->outputs[0];
> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
> +
> +    superfast_blur(s, in->data[0], inlink->w, inlink->h, desc->nb_components);
> +
> +    return ff_filter_frame(outlink, in);
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    SuperFastBlurContext *s = ctx->priv;
> +
> +    av_freep(&s->r);
> +    av_freep(&s->g);
> +    av_freep(&s->b);
> +    av_freep(&s->vMIN);
> +    av_freep(&s->vMAX);
> +    av_freep(&s->dv);
> +}
> +
> +static const AVFilterPad superfastblur_inputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_VIDEO,
> +        .config_props = config_props,
> +        .filter_frame = filter_frame,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad superfastblur_outputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_VIDEO,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter ff_vf_superfastblur = {
> +    .name          = "superfastblur",
> +    .description   = NULL_IF_CONFIG_SMALL("Blur the input with super fast blur algorithm."),
> +    .priv_size     = sizeof(SuperFastBlurContext),
> +    .init          = init,
> +    .uninit        = uninit,
> +    .query_formats = query_formats,
> +    .inputs        = superfastblur_inputs,
> +    .outputs       = superfastblur_outputs,
> +    .priv_class    = &superfastblur_class,
> +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
> +};
> --
> 1.7.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Jun Zhao Nov. 12, 2019, 10:33 a.m. UTC | #4
On Tue, Nov 12, 2019 at 5:37 PM Paul B Mahol <onemda@gmail.com> wrote:
>
> Isn't this same as boxblur?
>
After going deep into the boxblur, I think the superfastblur same as
the boxblur, please ignore the patch, thx.
> On 11/12/19, Steven Liu <lq@chinaffmpeg.org> wrote:
> >
> >
> >> 在 2019年11月12日,15:51,Jun Zhao <mypopydev@gmail.com> 写道:
> >>
> >> From: Jun Zhao <barryjzhao@tencent.com>
> >>
> >> add superfastblur filter
> >>
> >> Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
> >> ---
> >> doc/filters.texi               |   15 ++
> >> libavfilter/Makefile           |    1 +
> >> libavfilter/allfilters.c       |    1 +
> >> libavfilter/vf_superfastblur.c |  275
> >> ++++++++++++++++++++++++++++++++++++++++
> >> 4 files changed, 292 insertions(+), 0 deletions(-)
> >> create mode 100644 libavfilter/vf_superfastblur.c
> >>
> >> diff --git a/doc/filters.texi b/doc/filters.texi
> >> index 6800124..c7d1893 100644
> >> --- a/doc/filters.texi
> >> +++ b/doc/filters.texi
> >> @@ -17453,6 +17453,21 @@ Interpolate) pixel art scaling algorithm.
> >>
> >> Useful for enlarging pixel art images without reducing sharpness.
> >>
> >> +@section superfastblur
> >> +
> >> +Blur the input image with super fast blur algorithm, multiple invocations
> >> of this
> >> +filter with a small radius will approximate a gaussian blur quite well.
> >> +
> >> +This filter accepts the following options:
> >> +
> >> +@table @option
> >> +@item radius
> >> +@item r
> >> +Set the blurring box radius. The option value must be a int number in
> >> +the range [1, 10] that specifies the blur box size of the superfast blur
> >> filter
> >> +used to blur the image. Default value is @code{2}..
> >> +@end table
> >> +
> >> @section swaprect
> >>
> >> Swap two rectangular objects in video.
> >> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> >> index fce9303..db4d5e6 100644
> >> --- a/libavfilter/Makefile
> >> +++ b/libavfilter/Makefile
> >> @@ -396,6 +396,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER)               +=
> >> vf_stereo3d.o
> >> OBJS-$(CONFIG_STREAMSELECT_FILTER)           += f_streamselect.o
> >> framesync.o
> >> OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
> >> OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
> >> +OBJS-$(CONFIG_SUPEREQUALIZER_FILTER)         += vf_superfastblur.o
> >> OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
> >> OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
> >> OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
> >> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> >> index 7c1e19e..d507bc5 100644
> >> --- a/libavfilter/allfilters.c
> >> +++ b/libavfilter/allfilters.c
> >> @@ -377,6 +377,7 @@ extern AVFilter ff_vf_stereo3d;
> >> extern AVFilter ff_vf_streamselect;
> >> extern AVFilter ff_vf_subtitles;
> >> extern AVFilter ff_vf_super2xsai;
> >> +extern AVFilter ff_vf_superfastblur;
> >> extern AVFilter ff_vf_swaprect;
> >> extern AVFilter ff_vf_swapuv;
> >> extern AVFilter ff_vf_tblend;
> >> diff --git a/libavfilter/vf_superfastblur.c
> >> b/libavfilter/vf_superfastblur.c
> >> new file mode 100644
> >> index 0000000..a6428cf
> >> --- /dev/null
> >> +++ b/libavfilter/vf_superfastblur.c
> >> @@ -0,0 +1,275 @@
> >> +/*
> >> + * Copyright (c) 2019 Jun Zhao
> >> + *
> >> + * This file is part of FFmpeg.
> >> + *
> >> + * FFmpeg is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2.1 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
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser 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
> >> + * Super fast blur filter
> >> + *
> >> + * @see http://incubator.quasimondo.com/processing/superfast_blur.php
> >> + */
> >> +
> >> +#include "libavutil/avassert.h"
> >> +#include "libavutil/imgutils.h"
> >> +#include "libavutil/opt.h"
> >> +#include "avfilter.h"
> >> +#include "formats.h"
> >> +#include "internal.h"
> >> +#include "video.h"
> >> +
> >> +typedef struct SuperFastBlurContext {
> >> +    const AVClass *class;
> >> +
> >> +    int radius;
> >> +
> >> +    uint32_t *vMIN;
> >> +    uint32_t *vMAX;
> >> +
> >> +    uint8_t *r;
> >> +    uint8_t *g;
> >> +    uint8_t *b;
> >> +
> >> +    uint8_t *dv;
> >> +} SuperFastBlurContext;
> >> +
> >> +#define OFFSET(x) offsetof(SuperFastBlurContext, x)
> >> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> >> +static const AVOption superfastblur_options[] = {
> >> +    { "radius", "Radius of the super fast blurring box", OFFSET(radius),
> >> AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
> >> +    { "r",      "Radius of the super fast blurring box", OFFSET(radius),
> >> AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
> >> +    { NULL }
> >> +};
> >> +
> >> +AVFILTER_DEFINE_CLASS(superfastblur);
> >> +
> >> +static av_cold int init(AVFilterContext *ctx)
> >> +{
> >> +    SuperFastBlurContext *s = ctx->priv;
> >> +
> >> +    // This line precalculates a lookup table for all the possible
> >> +    // mean values that can occur. This is to avoid costly division
> >> +    // in the inner loop. On some systems doing the division directly
> >> +    // instead of a doing an array lookup might actually be faster
> >> +    // nowadays.
> >> +    uint32_t div = 2 * s->radius + 1;
> >> +    s->dv = av_malloc(sizeof(*s->dv) * 256 * div);
> >> +    if (!s->dv)
> >> +        return AVERROR(ENOMEM);
> >> +    for (int i = 0; i < 256 * div; i++)
> >> +        s->dv[i] = (i / div);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int query_formats(AVFilterContext *ctx)
> >> +{
> >> +    static const enum AVPixelFormat pix_fmts[] = {
> >> +        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
> >> +
> >> +        AV_PIX_FMT_NONE
> > Empty line?
> >> +    };
> >> +
> >> +    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
> >> +    if (!fmts_list)
> >> +        return AVERROR(ENOMEM);
> >> +    return ff_set_common_formats(ctx, fmts_list);
> >> +}
> >> +
> >> +static int config_props(AVFilterLink *inlink)
> >> +{
> >> +    AVFilterContext *ctx = inlink->dst;
> >> +    SuperFastBlurContext *s = ctx->priv;
> >> +
> >> +    uint32_t wm = inlink->w - 1;
> >> +    uint32_t wh = inlink->w * inlink->h;
> >> +
> >> +    s->vMIN = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
> >> +    s->vMAX = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
> >> +    s->r = av_malloc(sizeof(*s->r) * wh);
> >> +    s->g = av_malloc(sizeof(*s->g) * wh);
> >> +    s->b = av_malloc(sizeof(*s->b) * wh);
> >> +
> >> +    if (!s->vMIN || !s->vMAX || !s->r || !s->g || !s->b)
> >> +        return AVERROR(ENOMEM);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +/*
> >> + * Super Fast Blur v1.1+
> >> + * by Mario Klingemann <http://incubator.quasimondo.com>
> >> + * Original address:
> >> http://incubator.quasimondo.com/processing/superfastblur.pde
> >> + *
> >> + * Tip: Multiple invocations of this filter with a small
> >> + * radius will approximate a gaussian blur quite well.
> >> + */
> >> +static void superfast_blur(SuperFastBlurContext *s, uint8_t *pix, int w,
> >> int h, int nb_comps)
> >> +{
> >> +    uint32_t wm, hm;
> >> +    uint32_t *vMIN, *vMAX;
> >> +    uint8_t *r, *g, *b, *dv;
> >> +    uint32_t rsum, gsum, bsum;
> >> +    uint32_t p, p1, p2, yi, yw;
> >> +
> >> +    int radius;
> >> +
> >> +    int x, y, i, yp;
> >> +
> >> +    wm = w - 1;
> >> +    hm = h - 1;
> >> +
> >> +    vMIN = s->vMIN;
> >> +    vMAX = s->vMAX;
> >> +    r = s->r;
> >> +    g = s->g;
> >> +    b = s->b;
> >> +
> >> +    dv = s->dv;
> >> +
> >> +    radius = s->radius;
> >> +
> >> +    yw = yi = 0;
> >> +    for (y = 0; y < h; y++) {
> >> +        rsum = gsum = bsum = 0;
> >> +        // The reason why this algorithm is fast is that it uses a
> >> sliding
> >> +        // window and thus reduces the number of required pixel lookups.
> >> +        // The window slides from the left edge to the right (and in the
> >> +        // second pass from top to bottom) and only adds one pixel at the
> >> +        // right and removes one from the left. The code above
> >> initializes
> >> +        // the window by prefilling the window with the leftmost edge
> >> pixel
> >> +        // depending on the kernel size.
> >> +        for (i = -radius; i <= radius; i++) {
> >> +            p = (yi + FFMIN(wm, FFMAX(i, 0))) * nb_comps;
> >> +            rsum += pix[p];
> >> +            gsum += pix[p + 1];
> >> +            bsum += pix[p + 2];
> >> +        }
> >> +
> >> +        for (x = 0; x < w; x++) {
> >> +            r[yi] = dv[rsum];
> >> +            g[yi] = dv[gsum];
> >> +            b[yi] = dv[bsum];
> >> +
> >> +            // adds a new pixel but at the same time handles the border
> >> +            // conditions (when the window tries to read or remove pixels
> >> +            // outside the bitmap).
> >> +            if (y == 0) {
> >> +                vMIN[x] = FFMIN(x + radius + 1, wm);
> >> +                vMAX[x] = FFMAX(x - radius, 0);
> >> +            }
> >> +            p1 = (yw + vMIN[x]) * nb_comps;
> >> +            p2 = (yw + vMAX[x]) * nb_comps;
> >> +            rsum += pix[p1]     - pix[p2];
> >> +            gsum += pix[p1 + 1] - pix[p2 + 1];
> >> +            bsum += pix[p1 + 2] - pix[p2 + 2];
> >> +            yi++;
> >> +        }
> >> +        yw += w;
> >> +    }
> >> +
> >> +    for (x = 0; x < w; x++) {
> >> +        rsum = gsum = bsum = 0;
> >> +        yp = -radius * w;
> >> +        for (i = -radius; i <= radius; i++) {
> >> +            yi = FFMAX(0, yp) + x;
> >> +            rsum += r[yi];
> >> +            gsum += g[yi];
> >> +            bsum += b[yi];
> >> +            yp += w;
> >> +        }
> >> +
> >> +        yi = x;
> >> +        for (y = 0; y < h; y++) {
> >> +            pix[yi * nb_comps]     = dv[bsum];
> >> +            pix[yi * nb_comps + 1] = dv[gsum];
> >> +            pix[yi * nb_comps + 2] = dv[rsum];
> >> +
> >> +            if (x == 0) {
> >> +                vMIN[y] = FFMIN(y + radius + 1, hm) * w;
> >> +                vMAX[y] = FFMAX(y - radius, 0) * w;
> >> +            }
> >> +            p1 = x + vMIN[y];
> >> +            p2 = x + vMAX[y];
> >> +
> >> +            // rsum, gsum and bsum is the accumulated sum of pixels
> >> inside
> >> +            // the sliding window. What you see is the new pixel on the
> >> +            // right side being added to the sum and the leftmost pixel
> >> +            // i nthe window being removed from the sum.
> >> +            rsum += r[p1] - r[p2];
> >> +            gsum += g[p1] - g[p2];
> >> +            bsum += b[p1] - b[p2];
> >> +            yi += w;
> >> +        }
> >> +    }
> >> +}
> >> +
> >> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> >> +{
> >> +    AVFilterContext *ctx = inlink->dst;
> >> +    SuperFastBlurContext *s = ctx->priv;
> >> +    AVFilterLink *outlink = ctx->outputs[0];
> >> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
> >> +
> >> +    superfast_blur(s, in->data[0], inlink->w, inlink->h,
> >> desc->nb_components);
> >> +
> >> +    return ff_filter_frame(outlink, in);
> >> +}
> >> +
> >> +static av_cold void uninit(AVFilterContext *ctx)
> >> +{
> >> +    SuperFastBlurContext *s = ctx->priv;
> >> +
> >> +    av_freep(&s->r);
> >> +    av_freep(&s->g);
> >> +    av_freep(&s->b);
> >> +    av_freep(&s->vMIN);
> >> +    av_freep(&s->vMAX);
> >> +    av_freep(&s->dv);
> >> +}
> >> +
> >> +static const AVFilterPad superfastblur_inputs[] = {
> >> +    {
> >> +        .name         = "default",
> >> +        .type         = AVMEDIA_TYPE_VIDEO,
> >> +        .config_props = config_props,
> >> +        .filter_frame = filter_frame,
> >> +    },
> >> +    { NULL }
> >> +};
> >> +
> >> +static const AVFilterPad superfastblur_outputs[] = {
> >> +    {
> >> +        .name = "default",
> >> +        .type = AVMEDIA_TYPE_VIDEO,
> >> +    },
> >> +    { NULL }
> >> +};
> >> +
> >> +AVFilter ff_vf_superfastblur = {
> >> +    .name          = "superfastblur",
> >> +    .description   = NULL_IF_CONFIG_SMALL("Blur the input with super fast
> >> blur algorithm."),
> >> +    .priv_size     = sizeof(SuperFastBlurContext),
> >> +    .init          = init,
> >> +    .uninit        = uninit,
> >> +    .query_formats = query_formats,
> >> +    .inputs        = superfastblur_inputs,
> >> +    .outputs       = superfastblur_outputs,
> >> +    .priv_class    = &superfastblur_class,
> >> +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
> >> +};
> >> --
> >> 1.7.1
> >>
> >> _______________________________________________
> >> ffmpeg-devel mailing list
> >> ffmpeg-devel@ffmpeg.org
> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>
> >> To unsubscribe, visit link above, or email
> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe”.
> >
> > LGTM
> > BTW, I saw you usually submit multi thread performance optimization for
> > other filter,
> > Why don’y do that in this filter one time complete?
> > I think maybe only one patch complete the whole operation maybe better.
> >
> > Thanks
> > Steven
> >
> >
> >
> >
> >
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol Nov. 12, 2019, 11:48 a.m. UTC | #5
If this filter is same speed or better than boxblur, it should replace
boxblur filter, as boxblur filter is GPL.

On 11/12/19, mypopy@gmail.com <mypopy@gmail.com> wrote:
> On Tue, Nov 12, 2019 at 5:37 PM Paul B Mahol <onemda@gmail.com> wrote:
>>
>> Isn't this same as boxblur?
>>
> After going deep into the boxblur, I think the superfastblur same as
> the boxblur, please ignore the patch, thx.
>> On 11/12/19, Steven Liu <lq@chinaffmpeg.org> wrote:
>> >
>> >
>> >> 在 2019年11月12日,15:51,Jun Zhao <mypopydev@gmail.com> 写道:
>> >>
>> >> From: Jun Zhao <barryjzhao@tencent.com>
>> >>
>> >> add superfastblur filter
>> >>
>> >> Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
>> >> ---
>> >> doc/filters.texi               |   15 ++
>> >> libavfilter/Makefile           |    1 +
>> >> libavfilter/allfilters.c       |    1 +
>> >> libavfilter/vf_superfastblur.c |  275
>> >> ++++++++++++++++++++++++++++++++++++++++
>> >> 4 files changed, 292 insertions(+), 0 deletions(-)
>> >> create mode 100644 libavfilter/vf_superfastblur.c
>> >>
>> >> diff --git a/doc/filters.texi b/doc/filters.texi
>> >> index 6800124..c7d1893 100644
>> >> --- a/doc/filters.texi
>> >> +++ b/doc/filters.texi
>> >> @@ -17453,6 +17453,21 @@ Interpolate) pixel art scaling algorithm.
>> >>
>> >> Useful for enlarging pixel art images without reducing sharpness.
>> >>
>> >> +@section superfastblur
>> >> +
>> >> +Blur the input image with super fast blur algorithm, multiple
>> >> invocations
>> >> of this
>> >> +filter with a small radius will approximate a gaussian blur quite
>> >> well.
>> >> +
>> >> +This filter accepts the following options:
>> >> +
>> >> +@table @option
>> >> +@item radius
>> >> +@item r
>> >> +Set the blurring box radius. The option value must be a int number in
>> >> +the range [1, 10] that specifies the blur box size of the superfast
>> >> blur
>> >> filter
>> >> +used to blur the image. Default value is @code{2}..
>> >> +@end table
>> >> +
>> >> @section swaprect
>> >>
>> >> Swap two rectangular objects in video.
>> >> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
>> >> index fce9303..db4d5e6 100644
>> >> --- a/libavfilter/Makefile
>> >> +++ b/libavfilter/Makefile
>> >> @@ -396,6 +396,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER)               +=
>> >> vf_stereo3d.o
>> >> OBJS-$(CONFIG_STREAMSELECT_FILTER)           += f_streamselect.o
>> >> framesync.o
>> >> OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
>> >> OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
>> >> +OBJS-$(CONFIG_SUPEREQUALIZER_FILTER)         += vf_superfastblur.o
>> >> OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
>> >> OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
>> >> OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
>> >> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
>> >> index 7c1e19e..d507bc5 100644
>> >> --- a/libavfilter/allfilters.c
>> >> +++ b/libavfilter/allfilters.c
>> >> @@ -377,6 +377,7 @@ extern AVFilter ff_vf_stereo3d;
>> >> extern AVFilter ff_vf_streamselect;
>> >> extern AVFilter ff_vf_subtitles;
>> >> extern AVFilter ff_vf_super2xsai;
>> >> +extern AVFilter ff_vf_superfastblur;
>> >> extern AVFilter ff_vf_swaprect;
>> >> extern AVFilter ff_vf_swapuv;
>> >> extern AVFilter ff_vf_tblend;
>> >> diff --git a/libavfilter/vf_superfastblur.c
>> >> b/libavfilter/vf_superfastblur.c
>> >> new file mode 100644
>> >> index 0000000..a6428cf
>> >> --- /dev/null
>> >> +++ b/libavfilter/vf_superfastblur.c
>> >> @@ -0,0 +1,275 @@
>> >> +/*
>> >> + * Copyright (c) 2019 Jun Zhao
>> >> + *
>> >> + * This file is part of FFmpeg.
>> >> + *
>> >> + * FFmpeg is free software; you can redistribute it and/or
>> >> + * modify it under the terms of the GNU Lesser General Public
>> >> + * License as published by the Free Software Foundation; either
>> >> + * version 2.1 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
>> >> + * Lesser General Public License for more details.
>> >> + *
>> >> + * You should have received a copy of the GNU Lesser 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
>> >> + * Super fast blur filter
>> >> + *
>> >> + * @see http://incubator.quasimondo.com/processing/superfast_blur.php
>> >> + */
>> >> +
>> >> +#include "libavutil/avassert.h"
>> >> +#include "libavutil/imgutils.h"
>> >> +#include "libavutil/opt.h"
>> >> +#include "avfilter.h"
>> >> +#include "formats.h"
>> >> +#include "internal.h"
>> >> +#include "video.h"
>> >> +
>> >> +typedef struct SuperFastBlurContext {
>> >> +    const AVClass *class;
>> >> +
>> >> +    int radius;
>> >> +
>> >> +    uint32_t *vMIN;
>> >> +    uint32_t *vMAX;
>> >> +
>> >> +    uint8_t *r;
>> >> +    uint8_t *g;
>> >> +    uint8_t *b;
>> >> +
>> >> +    uint8_t *dv;
>> >> +} SuperFastBlurContext;
>> >> +
>> >> +#define OFFSET(x) offsetof(SuperFastBlurContext, x)
>> >> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
>> >> +static const AVOption superfastblur_options[] = {
>> >> +    { "radius", "Radius of the super fast blurring box",
>> >> OFFSET(radius),
>> >> AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
>> >> +    { "r",      "Radius of the super fast blurring box",
>> >> OFFSET(radius),
>> >> AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
>> >> +    { NULL }
>> >> +};
>> >> +
>> >> +AVFILTER_DEFINE_CLASS(superfastblur);
>> >> +
>> >> +static av_cold int init(AVFilterContext *ctx)
>> >> +{
>> >> +    SuperFastBlurContext *s = ctx->priv;
>> >> +
>> >> +    // This line precalculates a lookup table for all the possible
>> >> +    // mean values that can occur. This is to avoid costly division
>> >> +    // in the inner loop. On some systems doing the division directly
>> >> +    // instead of a doing an array lookup might actually be faster
>> >> +    // nowadays.
>> >> +    uint32_t div = 2 * s->radius + 1;
>> >> +    s->dv = av_malloc(sizeof(*s->dv) * 256 * div);
>> >> +    if (!s->dv)
>> >> +        return AVERROR(ENOMEM);
>> >> +    for (int i = 0; i < 256 * div; i++)
>> >> +        s->dv[i] = (i / div);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +static int query_formats(AVFilterContext *ctx)
>> >> +{
>> >> +    static const enum AVPixelFormat pix_fmts[] = {
>> >> +        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
>> >> +
>> >> +        AV_PIX_FMT_NONE
>> > Empty line?
>> >> +    };
>> >> +
>> >> +    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
>> >> +    if (!fmts_list)
>> >> +        return AVERROR(ENOMEM);
>> >> +    return ff_set_common_formats(ctx, fmts_list);
>> >> +}
>> >> +
>> >> +static int config_props(AVFilterLink *inlink)
>> >> +{
>> >> +    AVFilterContext *ctx = inlink->dst;
>> >> +    SuperFastBlurContext *s = ctx->priv;
>> >> +
>> >> +    uint32_t wm = inlink->w - 1;
>> >> +    uint32_t wh = inlink->w * inlink->h;
>> >> +
>> >> +    s->vMIN = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
>> >> +    s->vMAX = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
>> >> +    s->r = av_malloc(sizeof(*s->r) * wh);
>> >> +    s->g = av_malloc(sizeof(*s->g) * wh);
>> >> +    s->b = av_malloc(sizeof(*s->b) * wh);
>> >> +
>> >> +    if (!s->vMIN || !s->vMAX || !s->r || !s->g || !s->b)
>> >> +        return AVERROR(ENOMEM);
>> >> +
>> >> +    return 0;
>> >> +}
>> >> +
>> >> +/*
>> >> + * Super Fast Blur v1.1+
>> >> + * by Mario Klingemann <http://incubator.quasimondo.com>
>> >> + * Original address:
>> >> http://incubator.quasimondo.com/processing/superfastblur.pde
>> >> + *
>> >> + * Tip: Multiple invocations of this filter with a small
>> >> + * radius will approximate a gaussian blur quite well.
>> >> + */
>> >> +static void superfast_blur(SuperFastBlurContext *s, uint8_t *pix, int
>> >> w,
>> >> int h, int nb_comps)
>> >> +{
>> >> +    uint32_t wm, hm;
>> >> +    uint32_t *vMIN, *vMAX;
>> >> +    uint8_t *r, *g, *b, *dv;
>> >> +    uint32_t rsum, gsum, bsum;
>> >> +    uint32_t p, p1, p2, yi, yw;
>> >> +
>> >> +    int radius;
>> >> +
>> >> +    int x, y, i, yp;
>> >> +
>> >> +    wm = w - 1;
>> >> +    hm = h - 1;
>> >> +
>> >> +    vMIN = s->vMIN;
>> >> +    vMAX = s->vMAX;
>> >> +    r = s->r;
>> >> +    g = s->g;
>> >> +    b = s->b;
>> >> +
>> >> +    dv = s->dv;
>> >> +
>> >> +    radius = s->radius;
>> >> +
>> >> +    yw = yi = 0;
>> >> +    for (y = 0; y < h; y++) {
>> >> +        rsum = gsum = bsum = 0;
>> >> +        // The reason why this algorithm is fast is that it uses a
>> >> sliding
>> >> +        // window and thus reduces the number of required pixel
>> >> lookups.
>> >> +        // The window slides from the left edge to the right (and in
>> >> the
>> >> +        // second pass from top to bottom) and only adds one pixel at
>> >> the
>> >> +        // right and removes one from the left. The code above
>> >> initializes
>> >> +        // the window by prefilling the window with the leftmost edge
>> >> pixel
>> >> +        // depending on the kernel size.
>> >> +        for (i = -radius; i <= radius; i++) {
>> >> +            p = (yi + FFMIN(wm, FFMAX(i, 0))) * nb_comps;
>> >> +            rsum += pix[p];
>> >> +            gsum += pix[p + 1];
>> >> +            bsum += pix[p + 2];
>> >> +        }
>> >> +
>> >> +        for (x = 0; x < w; x++) {
>> >> +            r[yi] = dv[rsum];
>> >> +            g[yi] = dv[gsum];
>> >> +            b[yi] = dv[bsum];
>> >> +
>> >> +            // adds a new pixel but at the same time handles the
>> >> border
>> >> +            // conditions (when the window tries to read or remove
>> >> pixels
>> >> +            // outside the bitmap).
>> >> +            if (y == 0) {
>> >> +                vMIN[x] = FFMIN(x + radius + 1, wm);
>> >> +                vMAX[x] = FFMAX(x - radius, 0);
>> >> +            }
>> >> +            p1 = (yw + vMIN[x]) * nb_comps;
>> >> +            p2 = (yw + vMAX[x]) * nb_comps;
>> >> +            rsum += pix[p1]     - pix[p2];
>> >> +            gsum += pix[p1 + 1] - pix[p2 + 1];
>> >> +            bsum += pix[p1 + 2] - pix[p2 + 2];
>> >> +            yi++;
>> >> +        }
>> >> +        yw += w;
>> >> +    }
>> >> +
>> >> +    for (x = 0; x < w; x++) {
>> >> +        rsum = gsum = bsum = 0;
>> >> +        yp = -radius * w;
>> >> +        for (i = -radius; i <= radius; i++) {
>> >> +            yi = FFMAX(0, yp) + x;
>> >> +            rsum += r[yi];
>> >> +            gsum += g[yi];
>> >> +            bsum += b[yi];
>> >> +            yp += w;
>> >> +        }
>> >> +
>> >> +        yi = x;
>> >> +        for (y = 0; y < h; y++) {
>> >> +            pix[yi * nb_comps]     = dv[bsum];
>> >> +            pix[yi * nb_comps + 1] = dv[gsum];
>> >> +            pix[yi * nb_comps + 2] = dv[rsum];
>> >> +
>> >> +            if (x == 0) {
>> >> +                vMIN[y] = FFMIN(y + radius + 1, hm) * w;
>> >> +                vMAX[y] = FFMAX(y - radius, 0) * w;
>> >> +            }
>> >> +            p1 = x + vMIN[y];
>> >> +            p2 = x + vMAX[y];
>> >> +
>> >> +            // rsum, gsum and bsum is the accumulated sum of pixels
>> >> inside
>> >> +            // the sliding window. What you see is the new pixel on
>> >> the
>> >> +            // right side being added to the sum and the leftmost
>> >> pixel
>> >> +            // i nthe window being removed from the sum.
>> >> +            rsum += r[p1] - r[p2];
>> >> +            gsum += g[p1] - g[p2];
>> >> +            bsum += b[p1] - b[p2];
>> >> +            yi += w;
>> >> +        }
>> >> +    }
>> >> +}
>> >> +
>> >> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>> >> +{
>> >> +    AVFilterContext *ctx = inlink->dst;
>> >> +    SuperFastBlurContext *s = ctx->priv;
>> >> +    AVFilterLink *outlink = ctx->outputs[0];
>> >> +    const AVPixFmtDescriptor *desc =
>> >> av_pix_fmt_desc_get(inlink->format);
>> >> +
>> >> +    superfast_blur(s, in->data[0], inlink->w, inlink->h,
>> >> desc->nb_components);
>> >> +
>> >> +    return ff_filter_frame(outlink, in);
>> >> +}
>> >> +
>> >> +static av_cold void uninit(AVFilterContext *ctx)
>> >> +{
>> >> +    SuperFastBlurContext *s = ctx->priv;
>> >> +
>> >> +    av_freep(&s->r);
>> >> +    av_freep(&s->g);
>> >> +    av_freep(&s->b);
>> >> +    av_freep(&s->vMIN);
>> >> +    av_freep(&s->vMAX);
>> >> +    av_freep(&s->dv);
>> >> +}
>> >> +
>> >> +static const AVFilterPad superfastblur_inputs[] = {
>> >> +    {
>> >> +        .name         = "default",
>> >> +        .type         = AVMEDIA_TYPE_VIDEO,
>> >> +        .config_props = config_props,
>> >> +        .filter_frame = filter_frame,
>> >> +    },
>> >> +    { NULL }
>> >> +};
>> >> +
>> >> +static const AVFilterPad superfastblur_outputs[] = {
>> >> +    {
>> >> +        .name = "default",
>> >> +        .type = AVMEDIA_TYPE_VIDEO,
>> >> +    },
>> >> +    { NULL }
>> >> +};
>> >> +
>> >> +AVFilter ff_vf_superfastblur = {
>> >> +    .name          = "superfastblur",
>> >> +    .description   = NULL_IF_CONFIG_SMALL("Blur the input with super
>> >> fast
>> >> blur algorithm."),
>> >> +    .priv_size     = sizeof(SuperFastBlurContext),
>> >> +    .init          = init,
>> >> +    .uninit        = uninit,
>> >> +    .query_formats = query_formats,
>> >> +    .inputs        = superfastblur_inputs,
>> >> +    .outputs       = superfastblur_outputs,
>> >> +    .priv_class    = &superfastblur_class,
>> >> +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
>> >> +};
>> >> --
>> >> 1.7.1
>> >>
>> >> _______________________________________________
>> >> ffmpeg-devel mailing list
>> >> ffmpeg-devel@ffmpeg.org
>> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> >>
>> >> To unsubscribe, visit link above, or email
>> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe”.
>> >
>> > LGTM
>> > BTW, I saw you usually submit multi thread performance optimization for
>> > other filter,
>> > Why don’y do that in this filter one time complete?
>> > I think maybe only one patch complete the whole operation maybe better.
>> >
>> > Thanks
>> > Steven
>> >
>> >
>> >
>> >
>> >
>> > _______________________________________________
>> > ffmpeg-devel mailing list
>> > ffmpeg-devel@ffmpeg.org
>> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> >
>> > To unsubscribe, visit link above, or email
>> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
>
>
> --
> =======================================
> Jun zhao/赵军
> +++++++++++++++++++++++++++++++++++++++
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Michael Niedermayer Nov. 12, 2019, 11:03 p.m. UTC | #6
On Tue, Nov 12, 2019 at 12:48:09PM +0100, Paul B Mahol wrote:
> If this filter is same speed or better than boxblur, it should replace
> boxblur filter, as boxblur filter is GPL.

relicensing boxblur should not be hard if someone needs the code to be
under lgpl

[...]
Paul B Mahol Nov. 12, 2019, 11:24 p.m. UTC | #7
On 11/13/19, Michael Niedermayer <michael@niedermayer.cc> wrote:
> On Tue, Nov 12, 2019 at 12:48:09PM +0100, Paul B Mahol wrote:
>> If this filter is same speed or better than boxblur, it should replace
>> boxblur filter, as boxblur filter is GPL.
>
> relicensing boxblur should not be hard if someone needs the code to be
> under lgpl

I do not think so, look at geq, lots of devs no longer contactable,

>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> The educated differ from the uneducated as much as the living from the
> dead. -- Aristotle
>
Jun Zhao Nov. 13, 2019, 1:02 a.m. UTC | #8
On Wed, Nov 13, 2019 at 7:24 AM Paul B Mahol <onemda@gmail.com> wrote:
>
> On 11/13/19, Michael Niedermayer <michael@niedermayer.cc> wrote:
> > On Tue, Nov 12, 2019 at 12:48:09PM +0100, Paul B Mahol wrote:
> >> If this filter is same speed or better than boxblur, it should replace
> >> boxblur filter, as boxblur filter is GPL.
> >
> > relicensing boxblur should not be hard if someone needs the code to be
> > under lgpl
>
> I do not think so, look at geq, lots of devs no longer contactable,
>
Maybe we can keep both based on the license choice
Liu Steven Nov. 13, 2019, 2:25 a.m. UTC | #9
> 在 2019年11月13日,09:02,mypopy@gmail.com 写道:
> 
> On Wed, Nov 13, 2019 at 7:24 AM Paul B Mahol <onemda@gmail.com> wrote:
>> 
>> On 11/13/19, Michael Niedermayer <michael@niedermayer.cc> wrote:
>>> On Tue, Nov 12, 2019 at 12:48:09PM +0100, Paul B Mahol wrote:
>>>> If this filter is same speed or better than boxblur, it should replace
>>>> boxblur filter, as boxblur filter is GPL.
>>> 
>>> relicensing boxblur should not be hard if someone needs the code to be
>>> under lgpl
>> 
>> I do not think so, look at geq, lots of devs no longer contactable,
>> 
> Maybe we can keep both based on the license choice
I think your suggestion is better :D
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

Thanks
Steven
Michael Niedermayer Nov. 13, 2019, 5:21 p.m. UTC | #10
On Wed, Nov 13, 2019 at 12:24:34AM +0100, Paul B Mahol wrote:
> On 11/13/19, Michael Niedermayer <michael@niedermayer.cc> wrote:
> > On Tue, Nov 12, 2019 at 12:48:09PM +0100, Paul B Mahol wrote:
> >> If this filter is same speed or better than boxblur, it should replace
> >> boxblur filter, as boxblur filter is GPL.
> >
> > relicensing boxblur should not be hard if someone needs the code to be
> > under lgpl
> 
> I do not think so, look at geq, lots of devs no longer contactable,

which lines of geq are written by a non contactable developer ?
Iam happy to rewrite them
same for boxblur btw

thx

[...]
Michael Niedermayer Nov. 14, 2019, 4:45 p.m. UTC | #11
On Wed, Nov 13, 2019 at 10:25:31AM +0800, Steven Liu wrote:
> 
> 
> > 在 2019年11月13日,09:02,mypopy@gmail.com 写道:
> > 
> > On Wed, Nov 13, 2019 at 7:24 AM Paul B Mahol <onemda@gmail.com> wrote:
> >> 
> >> On 11/13/19, Michael Niedermayer <michael@niedermayer.cc> wrote:
> >>> On Tue, Nov 12, 2019 at 12:48:09PM +0100, Paul B Mahol wrote:
> >>>> If this filter is same speed or better than boxblur, it should replace
> >>>> boxblur filter, as boxblur filter is GPL.
> >>> 
> >>> relicensing boxblur should not be hard if someone needs the code to be
> >>> under lgpl
> >> 
> >> I do not think so, look at geq, lots of devs no longer contactable,
> >> 
> > Maybe we can keep both based on the license choice
> I think your suggestion is better :D

I think the best is to get boxblur relicensed to LGPL and then put any
improvments, if there are improvments into it.
Avoiding a 2nd filter which does the same thing and using the filter
name and syntax users already know

Thanks

[...]
Michael Niedermayer Nov. 14, 2019, 5:26 p.m. UTC | #12
On Wed, Nov 13, 2019 at 12:24:34AM +0100, Paul B Mahol wrote:
> On 11/13/19, Michael Niedermayer <michael@niedermayer.cc> wrote:
> > On Tue, Nov 12, 2019 at 12:48:09PM +0100, Paul B Mahol wrote:
> >> If this filter is same speed or better than boxblur, it should replace
> >> boxblur filter, as boxblur filter is GPL.
> >
> > relicensing boxblur should not be hard if someone needs the code to be
> > under lgpl
> 
> I do not think so, look at geq, lots of devs no longer contactable,

who did you already contact about boxblur relicensing ?
i looked at the log and at the changes and i see noone i think wouldnt
reply whos change couldnt be rewritten in less than 5minutes
maybe someone could be on vacation and not check their email for a month ...

you know, just tell me who is the problem and which lines and ill either
contact them, rewrite the code or just revert their change

Thanks

[...]
Jun Zhao Nov. 15, 2019, 2:32 a.m. UTC | #13
On Fri, Nov 15, 2019 at 1:26 AM Michael Niedermayer
<michael@niedermayer.cc> wrote:
>
> On Wed, Nov 13, 2019 at 12:24:34AM +0100, Paul B Mahol wrote:
> > On 11/13/19, Michael Niedermayer <michael@niedermayer.cc> wrote:
> > > On Tue, Nov 12, 2019 at 12:48:09PM +0100, Paul B Mahol wrote:
> > >> If this filter is same speed or better than boxblur, it should replace
> > >> boxblur filter, as boxblur filter is GPL.
> > >
> > > relicensing boxblur should not be hard if someone needs the code to be
> > > under lgpl
> >
> > I do not think so, look at geq, lots of devs no longer contactable,
>
> who did you already contact about boxblur relicensing ?
> i looked at the log and at the changes and i see noone i think wouldnt
> reply whos change couldnt be rewritten in less than 5minutes
> maybe someone could be on vacation and not check their email for a month ...
>
> you know, just tell me who is the problem and which lines and ill either
> contact them, rewrite the code or just revert their change
>
> Thanks
>
I am OK if we can relicese the boxblur and drop this patch.

BTW: https://patchwork.ffmpeg.org/patch/16246/ is the other blur :)
diff mbox

Patch

diff --git a/doc/filters.texi b/doc/filters.texi
index 6800124..c7d1893 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -17453,6 +17453,21 @@  Interpolate) pixel art scaling algorithm.
 
 Useful for enlarging pixel art images without reducing sharpness.
 
+@section superfastblur
+
+Blur the input image with super fast blur algorithm, multiple invocations of this
+filter with a small radius will approximate a gaussian blur quite well.
+
+This filter accepts the following options:
+
+@table @option
+@item radius
+@item r
+Set the blurring box radius. The option value must be a int number in
+the range [1, 10] that specifies the blur box size of the superfast blur filter
+used to blur the image. Default value is @code{2}..
+@end table
+
 @section swaprect
 
 Swap two rectangular objects in video.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index fce9303..db4d5e6 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -396,6 +396,7 @@  OBJS-$(CONFIG_STEREO3D_FILTER)               += vf_stereo3d.o
 OBJS-$(CONFIG_STREAMSELECT_FILTER)           += f_streamselect.o framesync.o
 OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
 OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
+OBJS-$(CONFIG_SUPEREQUALIZER_FILTER)         += vf_superfastblur.o
 OBJS-$(CONFIG_SWAPRECT_FILTER)               += vf_swaprect.o
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
 OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o framesync.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 7c1e19e..d507bc5 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -377,6 +377,7 @@  extern AVFilter ff_vf_stereo3d;
 extern AVFilter ff_vf_streamselect;
 extern AVFilter ff_vf_subtitles;
 extern AVFilter ff_vf_super2xsai;
+extern AVFilter ff_vf_superfastblur;
 extern AVFilter ff_vf_swaprect;
 extern AVFilter ff_vf_swapuv;
 extern AVFilter ff_vf_tblend;
diff --git a/libavfilter/vf_superfastblur.c b/libavfilter/vf_superfastblur.c
new file mode 100644
index 0000000..a6428cf
--- /dev/null
+++ b/libavfilter/vf_superfastblur.c
@@ -0,0 +1,275 @@ 
+/*
+ * Copyright (c) 2019 Jun Zhao
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ * Super fast blur filter
+ *
+ * @see http://incubator.quasimondo.com/processing/superfast_blur.php
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct SuperFastBlurContext {
+    const AVClass *class;
+
+    int radius;
+
+    uint32_t *vMIN;
+    uint32_t *vMAX;
+
+    uint8_t *r;
+    uint8_t *g;
+    uint8_t *b;
+
+    uint8_t *dv;
+} SuperFastBlurContext;
+
+#define OFFSET(x) offsetof(SuperFastBlurContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+static const AVOption superfastblur_options[] = {
+    { "radius", "Radius of the super fast blurring box", OFFSET(radius),  AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
+    { "r",      "Radius of the super fast blurring box", OFFSET(radius),  AV_OPT_TYPE_INT, {.i64 = 2}, 1, 10, FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(superfastblur);
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    SuperFastBlurContext *s = ctx->priv;
+
+    // This line precalculates a lookup table for all the possible
+    // mean values that can occur. This is to avoid costly division
+    // in the inner loop. On some systems doing the division directly
+    // instead of a doing an array lookup might actually be faster
+    // nowadays.
+    uint32_t div = 2 * s->radius + 1;
+    s->dv = av_malloc(sizeof(*s->dv) * 256 * div);
+    if (!s->dv)
+        return AVERROR(ENOMEM);
+    for (int i = 0; i < 256 * div; i++)
+        s->dv[i] = (i / div);
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    static const enum AVPixelFormat pix_fmts[] = {
+        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+
+        AV_PIX_FMT_NONE
+    };
+
+    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
+    if (!fmts_list)
+        return AVERROR(ENOMEM);
+    return ff_set_common_formats(ctx, fmts_list);
+}
+
+static int config_props(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SuperFastBlurContext *s = ctx->priv;
+
+    uint32_t wm = inlink->w - 1;
+    uint32_t wh = inlink->w * inlink->h;
+
+    s->vMIN = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
+    s->vMAX = av_malloc(sizeof(wm) * FFMAX(inlink->w, inlink->h));
+    s->r = av_malloc(sizeof(*s->r) * wh);
+    s->g = av_malloc(sizeof(*s->g) * wh);
+    s->b = av_malloc(sizeof(*s->b) * wh);
+
+    if (!s->vMIN || !s->vMAX || !s->r || !s->g || !s->b)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+/*
+ * Super Fast Blur v1.1+
+ * by Mario Klingemann <http://incubator.quasimondo.com>
+ * Original address: http://incubator.quasimondo.com/processing/superfastblur.pde
+ *
+ * Tip: Multiple invocations of this filter with a small
+ * radius will approximate a gaussian blur quite well.
+ */
+static void superfast_blur(SuperFastBlurContext *s, uint8_t *pix, int w, int h, int nb_comps)
+{
+    uint32_t wm, hm;
+    uint32_t *vMIN, *vMAX;
+    uint8_t *r, *g, *b, *dv;
+    uint32_t rsum, gsum, bsum;
+    uint32_t p, p1, p2, yi, yw;
+
+    int radius;
+
+    int x, y, i, yp;
+
+    wm = w - 1;
+    hm = h - 1;
+
+    vMIN = s->vMIN;
+    vMAX = s->vMAX;
+    r = s->r;
+    g = s->g;
+    b = s->b;
+
+    dv = s->dv;
+
+    radius = s->radius;
+
+    yw = yi = 0;
+    for (y = 0; y < h; y++) {
+        rsum = gsum = bsum = 0;
+        // The reason why this algorithm is fast is that it uses a sliding
+        // window and thus reduces the number of required pixel lookups.
+        // The window slides from the left edge to the right (and in the
+        // second pass from top to bottom) and only adds one pixel at the
+        // right and removes one from the left. The code above initializes
+        // the window by prefilling the window with the leftmost edge pixel
+        // depending on the kernel size.
+        for (i = -radius; i <= radius; i++) {
+            p = (yi + FFMIN(wm, FFMAX(i, 0))) * nb_comps;
+            rsum += pix[p];
+            gsum += pix[p + 1];
+            bsum += pix[p + 2];
+        }
+
+        for (x = 0; x < w; x++) {
+            r[yi] = dv[rsum];
+            g[yi] = dv[gsum];
+            b[yi] = dv[bsum];
+
+            // adds a new pixel but at the same time handles the border
+            // conditions (when the window tries to read or remove pixels
+            // outside the bitmap).
+            if (y == 0) {
+                vMIN[x] = FFMIN(x + radius + 1, wm);
+                vMAX[x] = FFMAX(x - radius, 0);
+            }
+            p1 = (yw + vMIN[x]) * nb_comps;
+            p2 = (yw + vMAX[x]) * nb_comps;
+            rsum += pix[p1]     - pix[p2];
+            gsum += pix[p1 + 1] - pix[p2 + 1];
+            bsum += pix[p1 + 2] - pix[p2 + 2];
+            yi++;
+        }
+        yw += w;
+    }
+
+    for (x = 0; x < w; x++) {
+        rsum = gsum = bsum = 0;
+        yp = -radius * w;
+        for (i = -radius; i <= radius; i++) {
+            yi = FFMAX(0, yp) + x;
+            rsum += r[yi];
+            gsum += g[yi];
+            bsum += b[yi];
+            yp += w;
+        }
+
+        yi = x;
+        for (y = 0; y < h; y++) {
+            pix[yi * nb_comps]     = dv[bsum];
+            pix[yi * nb_comps + 1] = dv[gsum];
+            pix[yi * nb_comps + 2] = dv[rsum];
+
+            if (x == 0) {
+                vMIN[y] = FFMIN(y + radius + 1, hm) * w;
+                vMAX[y] = FFMAX(y - radius, 0) * w;
+            }
+            p1 = x + vMIN[y];
+            p2 = x + vMAX[y];
+
+            // rsum, gsum and bsum is the accumulated sum of pixels inside
+            // the sliding window. What you see is the new pixel on the
+            // right side being added to the sum and the leftmost pixel
+            // i nthe window being removed from the sum.
+            rsum += r[p1] - r[p2];
+            gsum += g[p1] - g[p2];
+            bsum += b[p1] - b[p2];
+            yi += w;
+        }
+    }
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx = inlink->dst;
+    SuperFastBlurContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+
+    superfast_blur(s, in->data[0], inlink->w, inlink->h, desc->nb_components);
+
+    return ff_filter_frame(outlink, in);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    SuperFastBlurContext *s = ctx->priv;
+
+    av_freep(&s->r);
+    av_freep(&s->g);
+    av_freep(&s->b);
+    av_freep(&s->vMIN);
+    av_freep(&s->vMAX);
+    av_freep(&s->dv);
+}
+
+static const AVFilterPad superfastblur_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_props,
+        .filter_frame = filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad superfastblur_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_superfastblur = {
+    .name          = "superfastblur",
+    .description   = NULL_IF_CONFIG_SMALL("Blur the input with super fast blur algorithm."),
+    .priv_size     = sizeof(SuperFastBlurContext),
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .inputs        = superfastblur_inputs,
+    .outputs       = superfastblur_outputs,
+    .priv_class    = &superfastblur_class,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};