From patchwork Thu Sep 1 13:03:18 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 383 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.134 with SMTP id o128csp897582vsd; Thu, 1 Sep 2016 06:03:34 -0700 (PDT) X-Received: by 10.25.77.197 with SMTP id a188mr4453724lfb.190.1472735014388; Thu, 01 Sep 2016 06:03:34 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id g5si2666177ljg.64.2016.09.01.06.03.31; Thu, 01 Sep 2016 06:03:34 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id C3F59689CE0; Thu, 1 Sep 2016 16:03:21 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f50.google.com (mail-wm0-f50.google.com [74.125.82.50]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3DA5F689B8B for ; Thu, 1 Sep 2016 16:03:15 +0300 (EEST) Received: by mail-wm0-f50.google.com with SMTP id c133so77309845wmd.1 for ; Thu, 01 Sep 2016 06:03:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:from:date:message-id:subject:to; bh=GVYj3XEV4yACJxwcZpGlRVi7HDH9bYppUDGEsIlal0Y=; b=JtVmCxUaYxY/6FXJ+nPvW+l5RyCd8DyLzsx+APcPMwq8DBTWW+iWmzwYoiG2cxSfq0 tJQo7WphiVq9DIadZXtc9IJ4EHWh0nhy2aftholUKNCCGyrUizqeU9dlQosW5H3TlVj6 pixzN4+jTDstlHErJCDxpV4+BV1KLHZTwKdolp6Enqf0qS+80EDT6slEZ1BrfDbpMe/t JgpquAIexHY2R+PCB7kZchbobRrMgjXlu13AAWm480oiZNkj/IAisknKkkEXnQXLu76B I9bkIwlQ0kV2jA+DAAe6eu+ujsLcT33WJOYope0y5SWz0cdfW+k6+QaIaVXuLnM7Ut4T omSA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=GVYj3XEV4yACJxwcZpGlRVi7HDH9bYppUDGEsIlal0Y=; b=R0GKR0Z9/n024MGmDNm/9VxJ05ciAumpttTJfnwlLnQlEI95XraJqc3oEbADRZlwJT czvKU/XmNmRQXsi7d9xk6X96mywDMjPHM5vvJSIocX4PFtmq2/kLWOsl9Esj4L8IOX/m j2c1JnbRK0c6Rrdk2ZXYCO4gfd9Hnt4+6oQvxLsy0XqNzG/ihBJcTtUkaS/QJG9S15db t00rmvIfwRLsCYx7gMUlePpPNetrR+RPxAr72xen0Y3DIp8Q8eDvz5WCCkuEV1MDME6N GHG4AelqfbjqjQ3BZtcfTcLBhg/IjIlXQS0lQ6kG8jaUp5FjqD/I8C4QkSsCGF1oaUWi 5VTA== X-Gm-Message-State: AE9vXwNC1XmCcYCwdy+7LlbTQjlr6hkIY572cZ5UUboP32gqY34Ks0+eCnKhFPkDQvZFH5rQ55tgjeCwnDKmAQ== X-Received: by 10.194.117.33 with SMTP id kb1mr4352520wjb.79.1472735001766; Thu, 01 Sep 2016 06:03:21 -0700 (PDT) MIME-Version: 1.0 Received: by 10.28.229.6 with HTTP; Thu, 1 Sep 2016 06:03:18 -0700 (PDT) From: Paul B Mahol Date: Thu, 1 Sep 2016 15:03:18 +0200 Message-ID: To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] avfilter: add gblur 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Hi, patch attached. From dca28955cfa774c0efed615cb5a0434f663731ec Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Thu, 1 Sep 2016 01:18:45 +0200 Subject: [PATCH] avfilter: add gblur filter Signed-off-by: Paul B Mahol --- doc/filters.texi | 17 +++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_gblur.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+) create mode 100644 libavfilter/vf_gblur.c diff --git a/doc/filters.texi b/doc/filters.texi index 88c6f65..cf43edb 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -8231,6 +8231,23 @@ option may cause flicker since the B-Frames have often larger QP. Default is @end table +@section gblur + +Apply Gaussian blur filter. + +The filter accepts the following options: + +@table @option +@item sigma +Set sigma, standard deviation of Gaussian blur. Default is @code{0.5}. + +@item steps +Set number of steps for Gaussian approximation. Defauls is @code{4}. + +@item planes +Set which planes to filter. By default all planes are filtered. +@end table + @section geq The filter accepts the following options: diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 81b40ac..0074ca3 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -183,6 +183,7 @@ OBJS-$(CONFIG_FRAMESTEP_FILTER) += vf_framestep.o OBJS-$(CONFIG_FREI0R_FILTER) += vf_frei0r.o OBJS-$(CONFIG_FSPP_FILTER) += vf_fspp.o OBJS-$(CONFIG_GEQ_FILTER) += vf_geq.o +OBJS-$(CONFIG_GBLUR_FILTER) += vf_gblur.o OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o OBJS-$(CONFIG_HALDCLUT_FILTER) += vf_lut3d.o dualinput.o framesync.o OBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index ee6f9cb..0234833 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -200,6 +200,7 @@ void avfilter_register_all(void) REGISTER_FILTER(FREI0R, frei0r, vf); REGISTER_FILTER(FSPP, fspp, vf); REGISTER_FILTER(GEQ, geq, vf); + REGISTER_FILTER(GBLUR, gblur, vf); REGISTER_FILTER(GRADFUN, gradfun, vf); REGISTER_FILTER(HALDCLUT, haldclut, vf); REGISTER_FILTER(HFLIP, hflip, vf); diff --git a/libavfilter/vf_gblur.c b/libavfilter/vf_gblur.c new file mode 100644 index 0000000..fc316af --- /dev/null +++ b/libavfilter/vf_gblur.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2011 Pascal Getreuer + * + * 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 + */ + +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef struct GBlurContext { + const AVClass *class; + + float sigma; + int steps; + int planes; + + int depth; + int planewidth[4]; + int planeheight[4]; + float *buffer; + int nb_planes; +} GBlurContext; + +#define OFFSET(x) offsetof(GBlurContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption gblur_options[] = { + { "sigma", "set sigma", OFFSET(sigma), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0.0, 1024, FLAGS }, + { "steps", "set number of steps", OFFSET(steps), AV_OPT_TYPE_INT, {.i64=4}, 1, 24, FLAGS }, + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(gblur); + +static void gaussianiir2d(float *buffer, int width, int height, + float sigma, int steps) +{ + const int numpixels = width * height; + float nu, boundaryscale, postscale; + double lambda, dnu; + int i, x, y, step; + float *ptr; + + if (sigma <= 0 || steps < 0) + return; + + lambda = (sigma * sigma) / (2.0 * steps); + dnu = (1.0 + 2.0*lambda - sqrt(1.0 + 4.0 * lambda)) / (2.0 * lambda); + postscale = pow(dnu / lambda, 2*steps); + boundaryscale = 1.0 / (1.0 - dnu); + nu = (float)dnu; + + /* Filter horizontally along each row */ + for (y = 0; y < height; y++) { + for (step = 0; step < steps; step++) { + ptr = buffer + width*y; + ptr[0] *= boundaryscale; + + /* Filter rightwards */ + for (x = 1; x < width; x++) + ptr[x] += nu*ptr[x - 1]; + + ptr[x = width - 1] *= boundaryscale; + + /* Filter leftwards */ + for (; x > 0; x--) + ptr[x - 1] += nu*ptr[x]; + } + } + + /* Filter vertically along each column */ + for (x = 0; x < width; x++) { + for (step = 0; step < steps; step++) { + ptr = buffer + x; + ptr[0] *= boundaryscale; + + /* Filter downwards */ + for (i = width; i < numpixels; i += width) + ptr[i] += nu*ptr[i - width]; + + ptr[i = numpixels - width] *= boundaryscale; + + /* Filter upwards */ + for (; i > 0; i -= width) + ptr[i - width] += nu*ptr[i]; + } + } + + for (i = 0; i < numpixels; i++) + buffer[i] *= postscale; + + return; +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, + AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, + AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_NONE + }; + + return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); +} + +static int config_input(AVFilterLink *inlink) +{ + GBlurContext *s = inlink->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + + s->depth = desc->comp[0].depth; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + s->planewidth[0] = s->planewidth[3] = inlink->w; + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + s->buffer = av_malloc_array(inlink->w, inlink->h * sizeof(*s->buffer)); + if (!s->buffer) + return AVERROR(ENOMEM); + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + GBlurContext *s = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFrame *out; + int plane; + + if (av_frame_is_writable(in)) { + out = in; + } else { + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + } + + for (plane = 0; plane < s->nb_planes; plane++) { + const int height = s->planeheight[plane]; + const int width = s->planewidth[plane]; + float *bptr = s->buffer; + const uint8_t *src = in->data[plane]; + const uint16_t *src16 = (const uint16_t *)in->data[plane]; + uint8_t *dst = out->data[plane]; + uint16_t *dst16 = (uint16_t *)out->data[plane]; + int y, x; + + if (!s->sigma || !(s->planes & (1 << plane))) { + if (out != in) + av_image_copy_plane(out->data[plane], out->linesize[plane], + in->data[plane], in->linesize[plane], + width * ((s->depth + 7) / 8), height); + continue; + } + + if (s->depth == 8) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + bptr[x] = src[x]; + } + bptr += width; + src += in->linesize[plane]; + } + } else { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + bptr[x] = src16[x]; + } + bptr += width; + src16 += in->linesize[plane] / 2; + } + } + + gaussianiir2d(s->buffer, s->planewidth[plane], s->planeheight[plane], s->sigma, s->steps); + + bptr = s->buffer; + if (s->depth == 8) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + dst[x] = bptr[x]; + } + bptr += width; + dst += out->linesize[plane]; + } + } else { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + dst16[x] = bptr[x]; + } + bptr += width; + dst16 += out->linesize[plane] / 2; + } + } + } + + if (out != in) + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + GBlurContext *s = ctx->priv; + + av_freep(&s->buffer); +} + +static const AVFilterPad gblur_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad gblur_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_gblur = { + .name = "gblur", + .description = NULL_IF_CONFIG_SMALL("Apply Gaussian Blur filter."), + .priv_size = sizeof(GBlurContext), + .priv_class = &gblur_class, + .uninit = uninit, + .query_formats = query_formats, + .inputs = gblur_inputs, + .outputs = gblur_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, +}; -- 2.5.0