[FFmpeg-devel] [PATCH V1] lavfi/superfastblur: add superfastblur filter
Paul B Mahol
onemda at gmail.com
Tue Nov 12 11:31:21 EET 2019
Isn't this same as boxblur?
On 11/12/19, Steven Liu <lq at chinaffmpeg.org> wrote:
>
>
>> 在 2019年11月12日,15:51,Jun Zhao <mypopydev at gmail.com> 写道:
>>
>> From: Jun Zhao <barryjzhao at tencent.com>
>>
>> add superfastblur filter
>>
>> Signed-off-by: Jun Zhao <barryjzhao at 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.
>>
>> + at 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:
>> +
>> + at table @option
>> + at item radius
>> + at 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}..
>> + at 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 at ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request at 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 at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
More information about the ffmpeg-devel
mailing list