From patchwork Wed Nov 13 14:17:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jun Zhao X-Patchwork-Id: 16247 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 4181A44979D for ; Wed, 13 Nov 2019 16:22:57 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1C00468A5FA; Wed, 13 Nov 2019 16:22:57 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f196.google.com (mail-pf1-f196.google.com [209.85.210.196]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7E72068A274 for ; Wed, 13 Nov 2019 16:22:50 +0200 (EET) Received: by mail-pf1-f196.google.com with SMTP id z4so1749108pfn.12 for ; Wed, 13 Nov 2019 06:22:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=mtLDo31CU0DwEFEnBshqUYkjtE8QLyfd1sbpqmoAzKk=; b=sqtTL+H4ieFRaGCQA9rQwTmtjFhANNjUKbx+4dYI0/ZqTEiafPKbX9hD1I4oNEX5PZ EG3G3rT2NeahTSMjEJJhcDLD+z75NCjRWt4cdItl9+PDvop7fa4j9KUGiMEJARL9OpQk QquWP/RTVpmEYiZiTSBSEfMpW83P5L9f/D+mQatyBfRhoRveMdpZsLERC8RFBc2wbSXv iVU0p26DqYnO0BZfYVJKmMCXwMckCShPDrBZ9wScA9OslRRhZGpv0HXcNUlVzZRxhaus LsqIbyattuL4y89i+tudDQQ0LsVbLE0PsUQB65I+SDFWdaoQzuc9fX33uVTyHvvprCGo eqwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=mtLDo31CU0DwEFEnBshqUYkjtE8QLyfd1sbpqmoAzKk=; b=hxSrHBpr7n+cyjwmle897pkGDTLn41rjNFomoFtqSZsFeOI6kT+3hTunq4AIERBRzx VMx+6Dll64WQa4L0g3oEiwoNKo1VufCYsnqWBaIEVipUW09N9+LX8P16OPBc8KzKEuXq M1PbsMlzciRmysipg10TjqWgpZlM+/U68hQlAhJ/ZYLCaEBt5jcKelqF5hC4hW1EZ8TK xEdFw+VkmQR65rDOXldqmWqf3wledMshuvG20CvvaCG0PpUEmdIJP1af9nfnkJkCtZTI hlPaBVUpNmHn/41hjww1u69mk70GXR6IbytZQQArs0SxVCuKhiEJ3998alYhJO3ZPrzW pUHg== X-Gm-Message-State: APjAAAUQwSzLETzFLKf2rpLjMV12VcMGjehgEOXgEi26Z27Z2e+ujgJ7 IK9L3vh7khsuwlCk2eM1boKwO9pH X-Google-Smtp-Source: APXvYqxlRRW4BvxysLZzaFxpvkBu3rviFSQnq1+tMq2eDHfsoym6K1TgTICdAiPqRAFFddNGWvQaEw== X-Received: by 2002:a63:fe06:: with SMTP id p6mr3841611pgh.245.1573654633319; Wed, 13 Nov 2019 06:17:13 -0800 (PST) Received: from localhost.localdomain ([47.90.47.25]) by smtp.gmail.com with ESMTPSA id 13sm2843488pgu.53.2019.11.13.06.17.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 13 Nov 2019 06:17:12 -0800 (PST) From: Jun Zhao To: ffmpeg-devel@ffmpeg.org Date: Wed, 13 Nov 2019 22:17:04 +0800 Message-Id: <1573654625-30635-2-git-send-email-mypopydev@gmail.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1573654625-30635-1-git-send-email-mypopydev@gmail.com> References: <1573654625-30635-1-git-send-email-mypopydev@gmail.com> Subject: [FFmpeg-devel] [PATCH 1/2] lavfi/superfastblur: add superfastblur filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Jun Zhao MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Jun Zhao add superfastblur filter Signed-off-by: Jun Zhao --- 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 --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 + * 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, +};