diff mbox

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

Message ID 1573654625-30635-2-git-send-email-mypopydev@gmail.com
State New
Headers show

Commit Message

Jun Zhao Nov. 13, 2019, 2:17 p.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 |  274 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 291 insertions(+), 0 deletions(-)
 create mode 100644 libavfilter/vf_superfastblur.c
diff mbox

Patch

diff --git a/doc/filters.texi b/doc/filters.texi
index e48f9c9..23a3ded 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -17460,6 +17460,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..6d45ff0
--- /dev/null
+++ b/libavfilter/vf_superfastblur.c
@@ -0,0 +1,274 @@ 
+/*
+ * 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[rsum];
+            pix[yi * nb_comps + 1] = dv[gsum];
+            pix[yi * nb_comps + 2] = dv[bsum];
+
+            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,
+};