From patchwork Wed Nov 13 14:17:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jun Zhao X-Patchwork-Id: 16246 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 BD807449133 for ; Wed, 13 Nov 2019 16:17:26 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id A0A3E68AC51; Wed, 13 Nov 2019 16:17:26 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f172.google.com (mail-pf1-f172.google.com [209.85.210.172]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 79CB368A486 for ; Wed, 13 Nov 2019 16:17:19 +0200 (EET) Received: by mail-pf1-f172.google.com with SMTP id r4so1758188pfl.7 for ; Wed, 13 Nov 2019 06:17:19 -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=EIIlcwA5GexUpfqU0Nvwx9+j/Kwltt2lbu24b3cyEjU=; b=oNn7mN97dhP9j3ZpetKGsbuZYV9IdAPU8kl5p+a1pqJebmOFACmSabRxUdnVzBfOO9 EsTGwNj77uOPIbSK8g3TGp8eDTzpNpbBjskGFCH0ial4QROvvq1dCfh+EwSlO8bZhU1O rdWE8E1aIonW7W9MF3Ywf4x0/eYBn7lwts0O9OFwURW8lUpOeCwFLOnA4pNZ/+S1v6VU o9T4ZoaqyCW6i0IZV43LtD6Jc15I+RUqLtxFlDzSs5v3izixWesny9rDCLBELF5SLQp+ bmzxQSGRpVZpE5V+2VMPgmLo7BqlgkjlQlj9JUZpBesEBL2YZE/JXkJa1tSnZedMXTCk tP/Q== 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=EIIlcwA5GexUpfqU0Nvwx9+j/Kwltt2lbu24b3cyEjU=; b=mCESZOq22OVqsdbsF2Bb3uW/6VijKKNXeYmZSnb53RDxyLvn4kHEANcL/lvsg236CD F63Z7mEWpimnqopZirrSLkXRI5yrRhvZpH488qYewjBcnnZdlDI0L71fEyWvAbfNuEY6 SDg062cA+j2pt/V+GhYl7F7H5ANS/zmzHKLL3Ng5cp1edxTP689q0oqm+W+LhQAiGssS XHSx796eEcEkawJM1HH23fCxmzWB41jCeo0MJyrUnAsfByYdoRxIZQtql5xZkTzXWz0R 3U6NavDkIb5QZ+ju0qLDXtth1MUE9JRDCd0VftB/zjSp/be57iXT7S38u3xsIfVGLzs6 5SXw== X-Gm-Message-State: APjAAAUPtvpn5dsw5JSaPG2YBdHZRznGz2gT2Z3xCK0sefhDLcAUq5ds 1IPxHhHfDJEOCPqftZpiZuXm2lTB X-Google-Smtp-Source: APXvYqxb65guZBP97uflcLc//PfH49JPY01PeN4XUWdSdvXptO4Wv0Cew1mFByFWJlcMNDGZCF7+Kg== X-Received: by 2002:a63:c150:: with SMTP id p16mr3715712pgi.405.1573654636824; Wed, 13 Nov 2019 06:17:16 -0800 (PST) Received: from localhost.localdomain ([47.90.47.25]) by smtp.gmail.com with ESMTPSA id 13sm2843488pgu.53.2019.11.13.06.17.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 13 Nov 2019 06:17:16 -0800 (PST) From: Jun Zhao To: ffmpeg-devel@ffmpeg.org Date: Wed, 13 Nov 2019 22:17:05 +0800 Message-Id: <1573654625-30635-3-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 2/2] lavfi/stackblur: add stackblur 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 stackblur filter Signed-off-by: Jun Zhao --- doc/filters.texi | 22 +++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_stackblur.c | 362 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+), 0 deletions(-) create mode 100644 libavfilter/vf_stackblur.c diff --git a/doc/filters.texi b/doc/filters.texi index 23a3ded..0c450ff 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -17132,6 +17132,28 @@ ffmpeg -i main.mpg -i ref.mkv -lavfi "[0:v]settb=1/AVTB,setpts=PTS-STARTPTS[mai @end example @end itemize +@section stackblur + +Blur the input image with stack blur algorithm, this is a compromise between Gaussian Blur +and Box blur, It creates much better looking blurs than Box Blur, but is faster +than the Gaussian Blur. + +Called it stack blur because this describes best how this filter works internally: it creates +a kind of moving stack of colors whilst scanning through the image. Thereby it +just has to add one new block of color to the right side of the stack and remove the +leftmost color. The remaining colors on the topmost layer of the stack are either added on +or reduced by one, depending on if they are on the right or on the left side of the stack. + +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, 179] that specifies the blur box size of the stack blur filter +used to blur the image. Default value is @code{2}. +@end table + @section stereo3d Convert between different stereoscopic image formats. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index db4d5e6..b9a4ad0 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -392,6 +392,7 @@ OBJS-$(CONFIG_SPLIT_FILTER) += split.o OBJS-$(CONFIG_SPP_FILTER) += vf_spp.o OBJS-$(CONFIG_SR_FILTER) += vf_sr.o OBJS-$(CONFIG_SSIM_FILTER) += vf_ssim.o framesync.o +OBJS-$(CONFIG_STACKBLUR_FILTER) += vf_stackblur.o OBJS-$(CONFIG_STEREO3D_FILTER) += vf_stereo3d.o OBJS-$(CONFIG_STREAMSELECT_FILTER) += f_streamselect.o framesync.o OBJS-$(CONFIG_SUBTITLES_FILTER) += vf_subtitles.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index d507bc5..4064ba0 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -373,6 +373,7 @@ extern AVFilter ff_vf_split; extern AVFilter ff_vf_spp; extern AVFilter ff_vf_sr; extern AVFilter ff_vf_ssim; +extern AVFilter ff_vf_stackblur; extern AVFilter ff_vf_stereo3d; extern AVFilter ff_vf_streamselect; extern AVFilter ff_vf_subtitles; diff --git a/libavfilter/vf_stackblur.c b/libavfilter/vf_stackblur.c new file mode 100644 index 0000000..f1a6f20 --- /dev/null +++ b/libavfilter/vf_stackblur.c @@ -0,0 +1,362 @@ +/* + * 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 + * Stack blur filter + * + * Stack Blur Algorithm by Mario Klingemann + * + * @see http://incubator.quasimondo.com/processing/stackblur.pde + */ + +#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; + uint8_t *rgb; + uint8_t *dv; + + int *stack; +} StackBlurContext; + +#define OFFSET(x) offsetof(StackBlurContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +static const AVOption stackblur_options[] = { + { "radius", "Radius of the stack blurring box", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 179, FLAGS }, + { "r", "Radius of the stack blurring box", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 179, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(stackblur); + +static av_cold int init(AVFilterContext *ctx) +{ + StackBlurContext *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; + int divsum = (div + 1) >> 1; + divsum *= divsum; + s->dv = av_malloc(256 * divsum * sizeof(*s->dv)); + if (!s->dv) + return AVERROR(ENOMEM); + for (int i = 0; i < 256 * divsum; i++) + s->dv[i] = i / divsum; + + 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; + StackBlurContext *s = ctx->priv; + + uint32_t div = 2 * s->radius + 1; + uint32_t wh = inlink->w * inlink->h; + + s->rgb = av_malloc(sizeof(*s->rgb) * wh * 3); + s->vMIN = av_malloc(FFMAX(inlink->w, inlink->h) * sizeof(*s->vMIN)); + s->stack = av_malloc(div * 3 * sizeof(*s->stack)); + if (!s->vMIN || !s->rgb || !s->stack) + return AVERROR(ENOMEM); + + return 0; +} + +// Stack Blur v1.0 +// +// Author: Mario Klingemann +// http://incubator.quasimondo.com +// created Feburary 29, 2004 +// +// This is a compromise between Gaussian Blur and Box blur +// It creates much better looking blurs than Box Blur, but is faster +// than the Gaussian Blur implementation. +// +// Called it Stack Blur because this describes best how this +// filter works internally: it creates a kind of moving stack +// of colors whilst scanning through the image. Thereby it +// just has to add one new block of color to the right side +// of the stack and remove the leftmost color. The remaining +// colors on the topmost layer of the stack are either added on +// or reduced by one, depending on if they are on the right or +// on the left side of the stack. +// +static void stack_blur(StackBlurContext *s, uint8_t *pix, int w, int h, int nb_comps) +{ + uint32_t wm = w - 1; + uint32_t hm = h - 1; + uint32_t wh = w * h; + + int radius = s->radius; + + uint8_t *rgb = s->rgb; + uint8_t *r = rgb; + uint8_t *g = rgb + wh; + uint8_t *b = rgb + wh * 2; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + + uint32_t stackpointer; + uint32_t stackstart; + int *sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + uint32_t div = 2 * radius + 1; + + int(*stack)[3] = (int(*)[3])(s->stack); + uint32_t *vMIN = s->vMIN; + uint8_t *dv = s->dv; + + yw = yi = 0; + + for (y = 0; y < h; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = yi + (FFMIN(wm, FFMAX(i, 0))); + sir = stack[i + radius]; + sir[0] = pix[(p*nb_comps)]; + sir[1] = pix[(p*nb_comps) + 1]; + sir[2] = pix[(p*nb_comps) + 2]; + + rbs = r1 - FFABS(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) + vMIN[x] = FFMIN(x + radius + 1, wm); + p = yw + vMIN[x]; + + sir[0] = pix[(p*nb_comps)]; + sir[1] = pix[(p*nb_comps) + 1]; + sir[2] = pix[(p*nb_comps) + 2]; + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = FFMAX(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - FFABS(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) + yp += w; + } + yi = x; + stackpointer = radius; + 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]; + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) + vMIN[y] = FFMIN(y + r1, hm) * w; + p = x + vMIN[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + StackBlurContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + + stack_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) +{ + StackBlurContext *s = ctx->priv; + + av_freep(&s->rgb); + av_freep(&s->vMIN); + av_freep(&s->dv); + av_freep(&s->stack); +} + +static const AVFilterPad stackblur_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_props, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad stackblur_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_stackblur = { + .name = "stackblur", + .description = NULL_IF_CONFIG_SMALL("Blur the input with stack algorithm."), + .priv_size = sizeof(StackBlurContext), + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .inputs = stackblur_inputs, + .outputs = stackblur_outputs, + .priv_class = &stackblur_class, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, +};