[FFmpeg-devel] [PATCH V3] lavfi/stackblur: add stackblur filter
Jun Zhao
mypopydev at gmail.com
Tue Nov 26 08:14:01 EET 2019
From: Jun Zhao <barryjzhao at tencent.com>
add stackblur filter
Signed-off-by: Jun Zhao <barryjzhao at tencent.com>
---
doc/filters.texi | 22 +
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_stackblur.c | 1040 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1064 insertions(+), 0 deletions(-)
create mode 100644 libavfilter/vf_stackblur.c
diff --git a/doc/filters.texi b/doc/filters.texi
index 16bf2df..5d0bb31 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -17269,6 +17269,28 @@ ffmpeg -i main.mpg -i ref.mkv -lavfi "[0:v]settb=AVTB,setpts=PTS-STARTPTS[main]
@end example
@end itemize
+ at 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:
+
+ 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 [2, 254] that specifies the blur box size of the stack blur filter
+used to blur the image. Default value is @code{2}.
+ at end table
+
@section stereo3d
Convert between different stereoscopic image formats.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 46e3eec..09cd32d 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -393,6 +393,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 2a69227..8e6e0d4 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -374,6 +374,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..732e9f7
--- /dev/null
+++ b/libavfilter/vf_stackblur.c
@@ -0,0 +1,1040 @@
+/*
+ * 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 <mario at quasimondo.com>
+ *
+ * @see http://quasimondo.com/StackBlurForCanvas/StackBlur.js
+ *
+ * StackBlur - a fast almost Gaussian Blur For Canvas
+ *
+ * Version: 0.5
+ * Author: Mario Klingemann
+ * Contact: mario at quasimondo.com
+ * Website: http://www.quasimondo.com/StackBlurForCanvas
+ * Twitter: @quasimondo
+ *
+ * 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.
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+static uint16_t const stackblur_mul[255] = {
+ 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
+ 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
+ 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
+ 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
+ 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
+ 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
+ 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
+ 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
+ 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
+ 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
+ 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
+ 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
+ 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
+ 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
+ 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
+ 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
+};
+
+static uint8_t const stackblur_shr[255] = {
+ 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
+ 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
+};
+
+typedef struct StackBlurContext {
+ const AVClass *class;
+
+ int nb_planes;
+ int depth;
+ int planewidth[4];
+ int planeheight[4];
+
+ int radius;
+
+ int div; // 2 * s->radius + 1;
+
+ uint8_t *stack;
+
+ void (*horiz_stackblur)(uint8_t *src, ///< input image data
+ int w, ///< image width
+ int linesize, ///< image linesize
+ int h, ///< image height
+ int radius, ///< blur intensity (should be in 2..254 range)
+ uint8_t *stack); ///< stack buffer
+
+ void (*vert_stackblur) (uint8_t *src, ///< input image data
+ int w, ///< image width
+ int linesize, ///< image linesize
+ int h, ///< image height
+ int radius, ///< blur intensity (should be in 2..254 range)
+ uint8_t *stack); ///< stack buffer
+} 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}, 2, 254, FLAGS },
+ { "r", "Radius of the stack blurring box", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 2}, 2, 254, FLAGS },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(stackblur);
+
+static av_cold int init(AVFilterContext *ctx)
+{
+ StackBlurContext *s = ctx->priv;
+
+ s->div = 2 * s->radius + 1;
+
+ return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+ static const enum AVPixelFormat pix_fmts[] = {
+ // RGB 24 bits
+ AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+ // RGB 32 bits
+ AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA,
+ AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA,
+ AV_PIX_FMT_0RGB, AV_PIX_FMT_RGB0,
+ AV_PIX_FMT_0BGR, AV_PIX_FMT_BGR0,
+ // YUV
+ AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
+ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
+ AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
+ AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
+ AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
+ AV_PIX_FMT_YUVJ411P,
+ AV_PIX_FMT_GRAY8, AV_PIX_FMT_GBRP,
+ 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 void hstackblur_rgba(uint8_t *src, ///< input image data
+ int w, ///< image width
+ int linesize, ///< image linesize
+ int h, ///< image height
+ int radius, ///< blur intensity (should be in 2..254 range)
+ uint8_t *stack ///< stack buffer
+ )
+{
+ uint32_t x, y, xp, i;
+ uint32_t sp;
+ uint32_t stack_start;
+ uint8_t *stack_ptr;
+
+ uint8_t *src_ptr;
+ uint8_t *dst_ptr;
+
+ uint64_t sum_r;
+ uint64_t sum_g;
+ uint64_t sum_b;
+ uint64_t sum_a;
+
+ uint64_t sum_in_r;
+ uint64_t sum_in_g;
+ uint64_t sum_in_b;
+ uint64_t sum_in_a;
+
+ uint64_t sum_out_r;
+ uint64_t sum_out_g;
+ uint64_t sum_out_b;
+ uint64_t sum_out_a;
+
+ uint32_t wm = w - 1;
+ uint32_t div = (radius * 2) + 1;
+ uint32_t mul_sum = stackblur_mul[radius];
+ uint8_t shr_sum = stackblur_shr[radius];
+
+ for (y = 0; y < h; y++) {
+ sum_r = sum_g = sum_b = sum_a =
+ sum_in_r = sum_in_g = sum_in_b = sum_in_a =
+ sum_out_r = sum_out_g = sum_out_b = sum_out_a = 0;
+
+ src_ptr = src + linesize * y; // start of line (0, y)
+
+ for(i = 0; i <= radius; i++) {
+ stack_ptr = &stack[ 4 * i ];
+
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+ stack_ptr[3] = src_ptr[3];
+
+ sum_r += src_ptr[0] * (i + 1);
+ sum_g += src_ptr[1] * (i + 1);
+ sum_b += src_ptr[2] * (i + 1);
+ sum_a += src_ptr[3] * (i + 1);
+
+ sum_out_r += src_ptr[0];
+ sum_out_g += src_ptr[1];
+ sum_out_b += src_ptr[2];
+ sum_out_a += src_ptr[3];
+ }
+
+ for (i = 1; i <= radius; i++) {
+ if (i <= wm) src_ptr += 4;
+ stack_ptr = &stack[ 4 * (i + radius) ];
+
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+ stack_ptr[3] = src_ptr[3];
+
+ sum_r += src_ptr[0] * (radius + 1 - i);
+ sum_g += src_ptr[1] * (radius + 1 - i);
+ sum_b += src_ptr[2] * (radius + 1 - i);
+ sum_a += src_ptr[3] * (radius + 1 - i);
+
+ sum_in_r += src_ptr[0];
+ sum_in_g += src_ptr[1];
+ sum_in_b += src_ptr[2];
+ sum_in_a += src_ptr[3];
+ }
+
+ sp = radius;
+ xp = radius;
+ if (xp > wm) xp = wm;
+ src_ptr = src + 4 * xp + y * linesize; // pixel(xp, y);
+ dst_ptr = src + y * linesize; // pixel(0, y);
+ for (x = 0; x < w; x++) {
+ dst_ptr[0] = (sum_r * mul_sum) >> shr_sum;
+ dst_ptr[1] = (sum_g * mul_sum) >> shr_sum;
+ dst_ptr[2] = (sum_b * mul_sum) >> shr_sum;
+ dst_ptr[3] = (sum_a * mul_sum) >> shr_sum;
+ dst_ptr += 4;
+
+ sum_r -= sum_out_r;
+ sum_g -= sum_out_g;
+ sum_b -= sum_out_b;
+ sum_a -= sum_out_a;
+
+ stack_start = sp + div - radius;
+ if (stack_start >= div) stack_start -= div;
+ stack_ptr = &stack[4 * stack_start];
+
+ sum_out_r -= stack_ptr[0];
+ sum_out_g -= stack_ptr[1];
+ sum_out_b -= stack_ptr[2];
+ sum_out_a -= stack_ptr[3];
+
+ if (xp < wm) {
+ src_ptr += 4;
+ ++xp;
+ }
+
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+ stack_ptr[3] = src_ptr[3];
+
+ sum_in_r += src_ptr[0];
+ sum_in_g += src_ptr[1];
+ sum_in_b += src_ptr[2];
+ sum_in_a += src_ptr[3];
+
+ sum_r += sum_in_r;
+ sum_g += sum_in_g;
+ sum_b += sum_in_b;
+ sum_a += sum_in_a;
+
+ ++sp;
+ if (sp >= div) sp = 0;
+ stack_ptr = &stack[sp*4];
+
+ sum_out_r += stack_ptr[0];
+ sum_out_g += stack_ptr[1];
+ sum_out_b += stack_ptr[2];
+ sum_out_a += stack_ptr[3];
+
+ sum_in_r -= stack_ptr[0];
+ sum_in_g -= stack_ptr[1];
+ sum_in_b -= stack_ptr[2];
+ sum_in_a -= stack_ptr[3];
+ }
+ }
+}
+
+static void vstackblur_rgba(uint8_t *src, ///< input image data
+ int w, ///< image width
+ int linesize, ///< image linesize
+ int h, ///< image height
+ int radius, ///< blur intensity (should be in 2..254 range)
+ uint8_t *stack ///< stack buffer
+ )
+{
+ uint32_t x, y, yp, i;
+ uint32_t sp;
+ uint32_t stack_start;
+ uint8_t *stack_ptr;
+
+ uint8_t *src_ptr;
+ uint8_t *dst_ptr;
+
+ uint64_t sum_r;
+ uint64_t sum_g;
+ uint64_t sum_b;
+ uint64_t sum_a;
+
+ uint64_t sum_in_r;
+ uint64_t sum_in_g;
+ uint64_t sum_in_b;
+ uint64_t sum_in_a;
+
+ uint64_t sum_out_r;
+ uint64_t sum_out_g;
+ uint64_t sum_out_b;
+ uint64_t sum_out_a;
+
+ uint32_t hm = h - 1;
+ uint32_t div = (radius * 2) + 1;
+ uint32_t mul_sum = stackblur_mul[radius];
+ uint8_t shr_sum = stackblur_shr[radius];
+
+ for (x = 0; x < w; x++) {
+ sum_r = sum_g = sum_b = sum_a =
+ sum_in_r = sum_in_g = sum_in_b = sum_in_a =
+ sum_out_r = sum_out_g = sum_out_b = sum_out_a = 0;
+
+ src_ptr = src + 4 * x; // pixel(x, 0)
+ for (i = 0; i <= radius; i++) {
+ stack_ptr = &stack[i * 4];
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+ stack_ptr[3] = src_ptr[3];
+
+ sum_r += src_ptr[0] * (i + 1);
+ sum_g += src_ptr[1] * (i + 1);
+ sum_b += src_ptr[2] * (i + 1);
+ sum_a += src_ptr[3] * (i + 1);
+
+ sum_out_r += src_ptr[0];
+ sum_out_g += src_ptr[1];
+ sum_out_b += src_ptr[2];
+ sum_out_a += src_ptr[3];
+ }
+ for (i = 1; i <= radius; i++) {
+ if (i <= hm) src_ptr += linesize; // +stride
+
+ stack_ptr = &stack[4 * (i + radius)];
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+ stack_ptr[3] = src_ptr[3];
+
+ sum_r += src_ptr[0] * (radius + 1 - i);
+ sum_g += src_ptr[1] * (radius + 1 - i);
+ sum_b += src_ptr[2] * (radius + 1 - i);
+ sum_a += src_ptr[3] * (radius + 1 - i);
+
+ sum_in_r += src_ptr[0];
+ sum_in_g += src_ptr[1];
+ sum_in_b += src_ptr[2];
+ sum_in_a += src_ptr[2];
+ }
+
+ sp = radius;
+ yp = radius;
+ if (yp > hm) yp = hm;
+ src_ptr = src + 4 * x + yp * linesize; // pixel(x, yp);
+ dst_ptr = src + 4 * x; // pixel(x, 0);
+ for (y = 0; y < h; y++) {
+ dst_ptr[0] = (sum_r * mul_sum) >> shr_sum;
+ dst_ptr[1] = (sum_g * mul_sum) >> shr_sum;
+ dst_ptr[2] = (sum_b * mul_sum) >> shr_sum;
+ dst_ptr[3] = (sum_a * mul_sum) >> shr_sum;
+ dst_ptr += linesize;
+
+ sum_r -= sum_out_r;
+ sum_g -= sum_out_g;
+ sum_b -= sum_out_b;
+ sum_a -= sum_out_a;
+
+ stack_start = sp + div - radius;
+ if (stack_start >= div) stack_start -= div;
+ stack_ptr = &stack[4 * stack_start];
+
+ sum_out_r -= stack_ptr[0];
+ sum_out_g -= stack_ptr[1];
+ sum_out_b -= stack_ptr[2];
+ sum_out_a -= stack_ptr[3];
+
+ if (yp < hm) {
+ src_ptr += linesize; // +stride
+ ++yp;
+ }
+
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+ stack_ptr[3] = src_ptr[3];
+
+ sum_in_r += src_ptr[0];
+ sum_in_g += src_ptr[1];
+ sum_in_b += src_ptr[2];
+ sum_in_a += src_ptr[3];
+
+ sum_r += sum_in_r;
+ sum_g += sum_in_g;
+ sum_b += sum_in_b;
+ sum_a += sum_in_a;
+
+ ++sp;
+ if (sp >= div) sp = 0;
+ stack_ptr = &stack[sp*4];
+
+ sum_out_r += stack_ptr[0];
+ sum_out_g += stack_ptr[1];
+ sum_out_b += stack_ptr[2];
+ sum_out_a += stack_ptr[3];
+
+ sum_in_r -= stack_ptr[0];
+ sum_in_g -= stack_ptr[1];
+ sum_in_b -= stack_ptr[2];
+ sum_in_a -= stack_ptr[3];
+ }
+ }
+}
+
+static void hstackblur_rgb(uint8_t* src, ///< input image data
+ int w, ///< image width
+ int linesize, ///< image linesize
+ int h, ///< image height
+ int radius, ///< blur intensity (should be in 2..254 range)
+ uint8_t *stack ///< stack buffer
+ )
+{
+ uint32_t x, y, xp, i;
+ uint32_t sp;
+ uint32_t stack_start;
+ uint8_t *stack_ptr;
+
+ uint8_t *src_ptr;
+ uint8_t *dst_ptr;
+
+ uint64_t sum_r;
+ uint64_t sum_g;
+ uint64_t sum_b;
+
+ uint64_t sum_in_r;
+ uint64_t sum_in_g;
+ uint64_t sum_in_b;
+
+ uint64_t sum_out_r;
+ uint64_t sum_out_g;
+ uint64_t sum_out_b;
+
+ uint32_t wm = w - 1;
+ uint32_t div = (radius * 2) + 1;
+ uint32_t mul_sum = stackblur_mul[radius];
+ uint8_t shr_sum = stackblur_shr[radius];
+
+ for (y = 0; y < h; y++) {
+ sum_r = sum_g = sum_b =
+ sum_in_r = sum_in_g = sum_in_b =
+ sum_out_r = sum_out_g = sum_out_b = 0;
+
+ src_ptr = src + linesize * y; // start of line (0, y)
+
+ for(i = 0; i <= radius; i++) {
+ stack_ptr = &stack[ 3 * i ];
+
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+
+ sum_r += src_ptr[0] * (i + 1);
+ sum_g += src_ptr[1] * (i + 1);
+ sum_b += src_ptr[2] * (i + 1);
+
+ sum_out_r += src_ptr[0];
+ sum_out_g += src_ptr[1];
+ sum_out_b += src_ptr[2];
+ }
+
+ for (i = 1; i <= radius; i++) {
+ if (i <= wm) src_ptr += 3;
+ stack_ptr = &stack[ 3 * (i + radius) ];
+
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+
+ sum_r += src_ptr[0] * (radius + 1 - i);
+ sum_g += src_ptr[1] * (radius + 1 - i);
+ sum_b += src_ptr[2] * (radius + 1 - i);
+
+ sum_in_r += src_ptr[0];
+ sum_in_g += src_ptr[1];
+ sum_in_b += src_ptr[2];
+ }
+
+ sp = radius;
+ xp = radius;
+ if (xp > wm) xp = wm;
+ src_ptr = src + 3 * xp + y * linesize; // pixel(xp, y);
+ dst_ptr = src + y * linesize; // pixel(0, y);
+ for (x = 0; x < w; x++) {
+ dst_ptr[0] = (sum_r * mul_sum) >> shr_sum;
+ dst_ptr[1] = (sum_g * mul_sum) >> shr_sum;
+ dst_ptr[2] = (sum_b * mul_sum) >> shr_sum;
+ dst_ptr += 3;
+
+ sum_r -= sum_out_r;
+ sum_g -= sum_out_g;
+ sum_b -= sum_out_b;
+
+ stack_start = sp + div - radius;
+ if (stack_start >= div) stack_start -= div;
+ stack_ptr = &stack[3 * stack_start];
+
+ sum_out_r -= stack_ptr[0];
+ sum_out_g -= stack_ptr[1];
+ sum_out_b -= stack_ptr[2];
+
+ if (xp < wm) {
+ src_ptr += 3;
+ ++xp;
+ }
+
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+
+ sum_in_r += src_ptr[0];
+ sum_in_g += src_ptr[1];
+ sum_in_b += src_ptr[2];
+
+ sum_r += sum_in_r;
+ sum_g += sum_in_g;
+ sum_b += sum_in_b;
+
+ ++sp;
+ if (sp >= div) sp = 0;
+ stack_ptr = &stack[sp*3];
+
+ sum_out_r += stack_ptr[0];
+ sum_out_g += stack_ptr[1];
+ sum_out_b += stack_ptr[2];
+
+ sum_in_r -= stack_ptr[0];
+ sum_in_g -= stack_ptr[1];
+ sum_in_b -= stack_ptr[2];
+ }
+ }
+}
+
+static void vstackblur_rgb(uint8_t *src, ///< input image data
+ int w, ///< image width
+ int linesize, ///< image linesize
+ int h, ///< image height
+ int radius, ///< blur intensity (should be in 2..254 range)
+ uint8_t *stack ///< stack buffer
+ )
+{
+ uint32_t x, y, yp, i;
+ uint32_t sp;
+ uint32_t stack_start;
+ uint8_t *stack_ptr;
+
+ uint8_t *src_ptr;
+ uint8_t *dst_ptr;
+
+ uint64_t sum_r;
+ uint64_t sum_g;
+ uint64_t sum_b;
+
+ uint64_t sum_in_r;
+ uint64_t sum_in_g;
+ uint64_t sum_in_b;
+
+ uint64_t sum_out_r;
+ uint64_t sum_out_g;
+ uint64_t sum_out_b;
+
+ uint32_t hm = h - 1;
+ uint32_t div = (radius * 2) + 1;
+ uint32_t mul_sum = stackblur_mul[radius];
+ uint8_t shr_sum = stackblur_shr[radius];
+
+ for (x = 0; x < w; x++) {
+ sum_r = sum_g = sum_b =
+ sum_in_r = sum_in_g = sum_in_b =
+ sum_out_r = sum_out_g = sum_out_b = 0;
+
+ src_ptr = src + 3 * x; // pixel(x, 0)
+ for (i = 0; i <= radius; i++) {
+ stack_ptr = &stack[i * 3];
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+
+ sum_r += src_ptr[0] * (i + 1);
+ sum_g += src_ptr[1] * (i + 1);
+ sum_b += src_ptr[2] * (i + 1);
+
+ sum_out_r += src_ptr[0];
+ sum_out_g += src_ptr[1];
+ sum_out_b += src_ptr[2];
+ }
+ for (i = 1; i <= radius; i++) {
+ if (i <= hm) src_ptr += linesize; // +stride
+
+ stack_ptr = &stack[3 * (i + radius)];
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+
+ sum_r += src_ptr[0] * (radius + 1 - i);
+ sum_g += src_ptr[1] * (radius + 1 - i);
+ sum_b += src_ptr[2] * (radius + 1 - i);
+
+ sum_in_r += src_ptr[0];
+ sum_in_g += src_ptr[1];
+ sum_in_b += src_ptr[2];
+ }
+
+ sp = radius;
+ yp = radius;
+ if (yp > hm) yp = hm;
+ src_ptr = src + 3 * x + yp * linesize; // pixel(x, yp);
+ dst_ptr = src + 3 * x; // pixel(x, 0);
+ for (y = 0; y < h; y++) {
+ dst_ptr[0] = (sum_r * mul_sum) >> shr_sum;
+ dst_ptr[1] = (sum_g * mul_sum) >> shr_sum;
+ dst_ptr[2] = (sum_b * mul_sum) >> shr_sum;
+ dst_ptr += linesize;
+
+ sum_r -= sum_out_r;
+ sum_g -= sum_out_g;
+ sum_b -= sum_out_b;
+
+ stack_start = sp + div - radius;
+ if (stack_start >= div) stack_start -= div;
+ stack_ptr = &stack[3 * stack_start];
+
+ sum_out_r -= stack_ptr[0];
+ sum_out_g -= stack_ptr[1];
+ sum_out_b -= stack_ptr[2];
+
+ if (yp < hm) {
+ src_ptr += linesize; // +stride
+ ++yp;
+ }
+
+ stack_ptr[0] = src_ptr[0];
+ stack_ptr[1] = src_ptr[1];
+ stack_ptr[2] = src_ptr[2];
+
+ sum_in_r += src_ptr[0];
+ sum_in_g += src_ptr[1];
+ sum_in_b += src_ptr[2];
+
+ sum_r += sum_in_r;
+ sum_g += sum_in_g;
+ sum_b += sum_in_b;
+
+ ++sp;
+ if (sp >= div) sp = 0;
+ stack_ptr = &stack[sp*3];
+
+ sum_out_r += stack_ptr[0];
+ sum_out_g += stack_ptr[1];
+ sum_out_b += stack_ptr[2];
+
+ sum_in_r -= stack_ptr[0];
+ sum_in_g -= stack_ptr[1];
+ sum_in_b -= stack_ptr[2];
+ }
+ }
+}
+
+static void hstackblur_y(uint8_t *src, ///< input image data
+ int w, ///< image width
+ int linesize, ///< image linesize
+ int h, ///< image height
+ int radius, ///< blur intensity (should be in 2..254 range)
+ uint8_t *stack ///< stack buffer
+ )
+{
+ uint32_t x, y, xp, i;
+ uint32_t sp;
+ uint32_t stack_start;
+ uint8_t *stack_ptr;
+
+ uint8_t *src_ptr;
+ uint8_t *dst_ptr;
+
+ uint64_t sum;
+ uint64_t sum_in;
+ uint64_t sum_out;
+
+ uint32_t wm = w - 1;
+ uint32_t div = (radius * 2) + 1;
+ uint32_t mul_sum = stackblur_mul[radius];
+ uint8_t shr_sum = stackblur_shr[radius];
+
+ for (y = 0; y < h; y++) {
+ sum = sum_in = sum_out = 0;
+
+ src_ptr = src + linesize * y; // start of line (0, y)
+
+ for(i = 0; i <= radius; i++) {
+ stack_ptr = &stack[ i ];
+
+ stack_ptr[0] = src_ptr[0];
+
+ sum += src_ptr[0] * (i + 1);
+
+ sum_out += src_ptr[0];
+ }
+
+ for (i = 1; i <= radius; i++) {
+ if (i <= wm) src_ptr += 1;
+ stack_ptr = &stack[ 1 * (i + radius) ];
+
+ stack_ptr[0] = src_ptr[0];
+
+ sum += src_ptr[0] * (radius + 1 - i);
+
+ sum_in += src_ptr[0];
+ }
+
+ sp = radius;
+ xp = radius;
+ if (xp > wm) xp = wm;
+ src_ptr = src + xp + y * linesize; // pixel(xp, y);
+ dst_ptr = src + y * linesize; // pixel(0, y);
+ for (x = 0; x < w; x++) {
+ dst_ptr[0] = (sum * mul_sum) >> shr_sum;
+ dst_ptr += 1;
+
+ sum -= sum_out;
+
+ stack_start = sp + div - radius;
+ if (stack_start >= div) stack_start -= div;
+ stack_ptr = &stack[stack_start];
+
+ sum_out -= stack_ptr[0];
+
+ if (xp < wm) {
+ src_ptr += 1;
+ ++xp;
+ }
+
+ stack_ptr[0] = src_ptr[0];
+
+ sum_in += src_ptr[0];
+
+ sum += sum_in;
+
+ ++sp;
+ if (sp >= div) sp = 0;
+ stack_ptr = &stack[sp];
+
+ sum_out += stack_ptr[0];
+
+ sum_in -= stack_ptr[0];
+ }
+ }
+}
+
+static void vstackblur_y(uint8_t *src, ///< input image data
+ int w, ///< image width
+ int linesize, ///< image linesize
+ int h, ///< image height
+ int radius, ///< blur intensity (should be in 2..254 range)
+ uint8_t *stack ///< stack buffer
+ )
+{
+ uint32_t x, y, yp, i;
+ uint32_t sp;
+ uint32_t stack_start;
+ uint8_t *stack_ptr;
+
+ uint8_t *src_ptr;
+ uint8_t *dst_ptr;
+
+ uint64_t sum;
+ uint64_t sum_in;
+ uint64_t sum_out;
+
+ uint32_t hm = h - 1;
+ uint32_t div = (radius * 2) + 1;
+ uint32_t mul_sum = stackblur_mul[radius];
+ uint8_t shr_sum = stackblur_shr[radius];
+
+ for (x = 0; x < w; x++) {
+ sum = sum_in = sum_out = 0;
+
+ src_ptr = src + x; // pixel (x, 0)
+ for (i = 0; i <= radius; i++) {
+ stack_ptr = &stack[i];
+ stack_ptr[0] = src_ptr[0];
+
+ sum += src_ptr[0] * (i + 1);
+
+ sum_out += src_ptr[0];
+ }
+ for (i = 1; i <= radius; i++) {
+ if (i <= hm) src_ptr += linesize; // +stride
+
+ stack_ptr = &stack[1 * (i + radius)];
+ stack_ptr[0] = src_ptr[0];
+
+ sum += src_ptr[0] * (radius + 1 - i);
+
+ sum_in += src_ptr[0];
+ }
+
+ sp = radius;
+ yp = radius;
+ if (yp > hm) yp = hm;
+ src_ptr = src + x + yp * linesize; // pixel(x, yp);
+ dst_ptr = src + x; // pixel(x, 0);
+ for (y = 0; y < h; y++) {
+ dst_ptr[0] = (sum * mul_sum) >> shr_sum;
+ dst_ptr += linesize;
+
+ sum -= sum_out;
+
+ stack_start = sp + div - radius;
+ if (stack_start >= div) stack_start -= div;
+ stack_ptr = &stack[stack_start];
+
+ sum_out -= stack_ptr[0];
+
+ if (yp < hm) {
+ src_ptr += linesize; // +stride
+ ++yp;
+ }
+
+ stack_ptr[0] = src_ptr[0];
+
+ sum_in += src_ptr[0];
+
+ sum += sum_in;
+
+ ++sp;
+ if (sp >= div) sp = 0;
+ stack_ptr = &stack[sp];
+
+ sum_out += stack_ptr[0];
+
+ sum_in -= stack_ptr[0];
+ }
+ }
+}
+
+static int config_props(AVFilterLink *inlink)
+{
+ AVFilterContext *ctx = inlink->dst;
+ StackBlurContext *s = ctx->priv;
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+ int div = s->div;
+
+ s->nb_planes = av_pix_fmt_count_planes(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;
+
+ switch (inlink->format) {
+ case AV_PIX_FMT_RGB24:
+ case AV_PIX_FMT_BGR24:
+ s->horiz_stackblur = hstackblur_rgb;
+ s->vert_stackblur = vstackblur_rgb;
+ s->stack = av_mallocz(div * 3 * sizeof(*s->stack));
+ if (!s->stack)
+ return AVERROR(ENOMEM);
+ break;
+
+ case AV_PIX_FMT_ARGB:
+ case AV_PIX_FMT_RGBA:
+ case AV_PIX_FMT_ABGR:
+ case AV_PIX_FMT_BGRA:
+ case AV_PIX_FMT_0RGB:
+ case AV_PIX_FMT_RGB0:
+ case AV_PIX_FMT_0BGR:
+ case AV_PIX_FMT_BGR0:
+ s->horiz_stackblur = hstackblur_rgba;
+ s->vert_stackblur = vstackblur_rgba;
+ s->stack = av_mallocz(div * 4 * sizeof(*s->stack));
+ if (!s->stack)
+ return AVERROR(ENOMEM);
+ break;
+
+ case AV_PIX_FMT_YUV410P:
+ case AV_PIX_FMT_YUV411P:
+ case AV_PIX_FMT_YUV420P:
+ case AV_PIX_FMT_YUV422P:
+ case AV_PIX_FMT_YUV440P:
+ case AV_PIX_FMT_YUV444P:
+ case AV_PIX_FMT_YUVJ444P:
+ case AV_PIX_FMT_YUVJ440P:
+ case AV_PIX_FMT_YUVJ422P:
+ case AV_PIX_FMT_YUVJ420P:
+ case AV_PIX_FMT_YUVJ411P:
+ case AV_PIX_FMT_GRAY8:
+ case AV_PIX_FMT_GBRP:
+ s->horiz_stackblur = hstackblur_y;
+ s->vert_stackblur = vstackblur_y;
+ s->stack = av_mallocz(div * sizeof(*s->stack));
+ if (!s->stack)
+ return AVERROR(ENOMEM);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ StackBlurContext *s = ctx->priv;
+ AVFilterLink *outlink = ctx->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];
+
+ 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);
+ }
+
+ s->horiz_stackblur(out->data[0], ///< input image data
+ out->width, ///< image width
+ out->linesize[0],///< image linesize
+ out->height, ///< image height
+ s->radius, ///< blur intensity (should be in 2..254 range)
+ s->stack); ///< stack buffer
+
+ s->vert_stackblur(out->data[0], ///< input image data
+ out->width, ///< image width
+ out->linesize[0], ///< image linesize
+ out->height, ///< image height
+ s->radius, ///< blur intensity (should be in 2..254 range)
+ s->stack); ///< stack buffer
+
+ if (out != in)
+ av_frame_free(&in);
+ return ff_filter_frame(outlink, out);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ StackBlurContext *s = ctx->priv;
+
+ 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,
+};
--
1.7.1
More information about the ffmpeg-devel
mailing list