FFmpeg
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vf_boxblur.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
3  * Copyright (c) 2011 Stefano Sabatini
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 /**
23  * @file
24  * Apply a boxblur filter to the input video.
25  * Ported from MPlayer libmpcodecs/vf_boxblur.c.
26  */
27 
28 #include "libavutil/avstring.h"
29 #include "libavutil/common.h"
30 #include "libavutil/eval.h"
31 #include "libavutil/opt.h"
32 #include "libavutil/pixdesc.h"
33 #include "avfilter.h"
34 #include "formats.h"
35 #include "internal.h"
36 #include "video.h"
37 
38 static const char *const var_names[] = {
39  "w",
40  "h",
41  "cw",
42  "ch",
43  "hsub",
44  "vsub",
45  NULL
46 };
47 
48 enum var_name {
56 };
57 
58 typedef struct {
59  int radius;
60  int power;
61  char *radius_expr;
62 } FilterParam;
63 
64 typedef struct {
65  const AVClass *class;
69 
70  int hsub, vsub;
71  int radius[4];
72  int power[4];
73  uint8_t *temp[2]; ///< temporary buffer used in blur_power()
75 
76 #define OFFSET(x) offsetof(BoxBlurContext, x)
77 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
78 
79 static const AVOption boxblur_options[] = {
80  { "luma_radius", "set luma radius", OFFSET(luma_param.radius_expr), AV_OPT_TYPE_STRING, {.str="2"}, .flags = FLAGS },
81  { "lr", "set luma radius", OFFSET(luma_param.radius_expr), AV_OPT_TYPE_STRING, {.str="2"}, .flags = FLAGS },
82  { "luma_power", "set luma power", OFFSET(luma_param.power), AV_OPT_TYPE_INT, {.i64=2}, 0, INT_MAX, .flags = FLAGS },
83  { "lp", "set luma power", OFFSET(luma_param.power), AV_OPT_TYPE_INT, {.i64=2}, 0, INT_MAX, .flags = FLAGS },
84 
85  { "chroma_radius", "set chroma radius", OFFSET(chroma_param.radius_expr), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
86  { "cr", "set chroma radius", OFFSET(chroma_param.radius_expr), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
87  { "chroma_power", "set chroma power", OFFSET(chroma_param.power), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS },
88  { "cp", "set chroma power", OFFSET(chroma_param.power), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS },
89 
90  { "alpha_radius", "set alpha radius", OFFSET(alpha_param.radius_expr), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
91  { "ar", "set alpha radius", OFFSET(alpha_param.radius_expr), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
92  { "alpha_power", "set alpha power", OFFSET(alpha_param.power), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS },
93  { "ap", "set alpha power", OFFSET(alpha_param.power), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS },
94 
95  { NULL }
96 };
97 
98 AVFILTER_DEFINE_CLASS(boxblur);
99 
100 #define Y 0
101 #define U 1
102 #define V 2
103 #define A 3
104 
105 static av_cold int init(AVFilterContext *ctx, const char *args)
106 {
107  BoxBlurContext *boxblur = ctx->priv;
108  static const char *shorthand[] = {
109  "luma_radius", "luma_power",
110  "chroma_radius", "chroma_power",
111  "alpha_radius", "alpha_power",
112  NULL
113  };
114  int ret;
115 
116  boxblur->class = &boxblur_class;
117  av_opt_set_defaults(boxblur);
118 
119  if ((ret = av_opt_set_from_string(boxblur, args, shorthand, "=", ":")) < 0)
120  return ret;
121 
122  /* fill missing params */
123  if (!boxblur->chroma_param.radius_expr) {
125  if (!boxblur->chroma_param.radius_expr)
126  return AVERROR(ENOMEM);
127  }
128  if (boxblur->chroma_param.power < 0)
129  boxblur->chroma_param.power = boxblur->luma_param.power;
130 
131  if (!boxblur->alpha_param.radius_expr) {
133  if (!boxblur->alpha_param.radius_expr)
134  return AVERROR(ENOMEM);
135  }
136  if (boxblur->alpha_param.power < 0)
137  boxblur->alpha_param.power = boxblur->luma_param.power;
138 
139  return 0;
140 }
141 
142 static av_cold void uninit(AVFilterContext *ctx)
143 {
144  BoxBlurContext *boxblur = ctx->priv;
145 
146  av_freep(&boxblur->temp[0]);
147  av_freep(&boxblur->temp[1]);
148  av_opt_free(boxblur);
149 }
150 
152 {
153  static const enum AVPixelFormat pix_fmts[] = {
160  };
161 
163  return 0;
164 }
165 
166 static int config_input(AVFilterLink *inlink)
167 {
168  const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
169  AVFilterContext *ctx = inlink->dst;
170  BoxBlurContext *boxblur = ctx->priv;
171  int w = inlink->w, h = inlink->h;
172  int cw, ch;
173  double var_values[VARS_NB], res;
174  char *expr;
175  int ret;
176 
177  if (!(boxblur->temp[0] = av_malloc(FFMAX(w, h))) ||
178  !(boxblur->temp[1] = av_malloc(FFMAX(w, h))))
179  return AVERROR(ENOMEM);
180 
181  boxblur->hsub = desc->log2_chroma_w;
182  boxblur->vsub = desc->log2_chroma_h;
183 
184  var_values[VAR_W] = inlink->w;
185  var_values[VAR_H] = inlink->h;
186  var_values[VAR_CW] = cw = w>>boxblur->hsub;
187  var_values[VAR_CH] = ch = h>>boxblur->vsub;
188  var_values[VAR_HSUB] = 1<<boxblur->hsub;
189  var_values[VAR_VSUB] = 1<<boxblur->vsub;
190 
191 #define EVAL_RADIUS_EXPR(comp) \
192  expr = boxblur->comp##_param.radius_expr; \
193  ret = av_expr_parse_and_eval(&res, expr, var_names, var_values, \
194  NULL, NULL, NULL, NULL, NULL, 0, ctx); \
195  boxblur->comp##_param.radius = res; \
196  if (ret < 0) { \
197  av_log(NULL, AV_LOG_ERROR, \
198  "Error when evaluating " #comp " radius expression '%s'\n", expr); \
199  return ret; \
200  }
201  EVAL_RADIUS_EXPR(luma);
202  EVAL_RADIUS_EXPR(chroma);
204 
205  av_log(ctx, AV_LOG_VERBOSE,
206  "luma_radius:%d luma_power:%d "
207  "chroma_radius:%d chroma_power:%d "
208  "alpha_radius:%d alpha_power:%d "
209  "w:%d chroma_w:%d h:%d chroma_h:%d\n",
210  boxblur->luma_param .radius, boxblur->luma_param .power,
211  boxblur->chroma_param.radius, boxblur->chroma_param.power,
212  boxblur->alpha_param .radius, boxblur->alpha_param .power,
213  w, cw, h, ch);
214 
215 #define CHECK_RADIUS_VAL(w_, h_, comp) \
216  if (boxblur->comp##_param.radius < 0 || \
217  2*boxblur->comp##_param.radius > FFMIN(w_, h_)) { \
218  av_log(ctx, AV_LOG_ERROR, \
219  "Invalid " #comp " radius value %d, must be >= 0 and <= %d\n", \
220  boxblur->comp##_param.radius, FFMIN(w_, h_)/2); \
221  return AVERROR(EINVAL); \
222  }
223  CHECK_RADIUS_VAL(w, h, luma);
224  CHECK_RADIUS_VAL(cw, ch, chroma);
225  CHECK_RADIUS_VAL(w, h, alpha);
226 
227  boxblur->radius[Y] = boxblur->luma_param.radius;
228  boxblur->radius[U] = boxblur->radius[V] = boxblur->chroma_param.radius;
229  boxblur->radius[A] = boxblur->alpha_param.radius;
230 
231  boxblur->power[Y] = boxblur->luma_param.power;
232  boxblur->power[U] = boxblur->power[V] = boxblur->chroma_param.power;
233  boxblur->power[A] = boxblur->alpha_param.power;
234 
235  return 0;
236 }
237 
238 static inline void blur(uint8_t *dst, int dst_step, const uint8_t *src, int src_step,
239  int len, int radius)
240 {
241  /* Naive boxblur would sum source pixels from x-radius .. x+radius
242  * for destination pixel x. That would be O(radius*width).
243  * If you now look at what source pixels represent 2 consecutive
244  * output pixels, then you see they are almost identical and only
245  * differ by 2 pixels, like:
246  * src0 111111111
247  * dst0 1
248  * src1 111111111
249  * dst1 1
250  * src0-src1 1 -1
251  * so when you know one output pixel you can find the next by just adding
252  * and subtracting 1 input pixel.
253  * The following code adopts this faster variant.
254  */
255  const int length = radius*2 + 1;
256  const int inv = ((1<<16) + length/2)/length;
257  int x, sum = 0;
258 
259  for (x = 0; x < radius; x++)
260  sum += src[x*src_step]<<1;
261  sum += src[radius*src_step];
262 
263  for (x = 0; x <= radius; x++) {
264  sum += src[(radius+x)*src_step] - src[(radius-x)*src_step];
265  dst[x*dst_step] = (sum*inv + (1<<15))>>16;
266  }
267 
268  for (; x < len-radius; x++) {
269  sum += src[(radius+x)*src_step] - src[(x-radius-1)*src_step];
270  dst[x*dst_step] = (sum*inv + (1<<15))>>16;
271  }
272 
273  for (; x < len; x++) {
274  sum += src[(2*len-radius-x-1)*src_step] - src[(x-radius-1)*src_step];
275  dst[x*dst_step] = (sum*inv + (1<<15))>>16;
276  }
277 }
278 
279 static inline void blur_power(uint8_t *dst, int dst_step, const uint8_t *src, int src_step,
280  int len, int radius, int power, uint8_t *temp[2])
281 {
282  uint8_t *a = temp[0], *b = temp[1];
283 
284  if (radius && power) {
285  blur(a, 1, src, src_step, len, radius);
286  for (; power > 2; power--) {
287  uint8_t *c;
288  blur(b, 1, a, 1, len, radius);
289  c = a; a = b; b = c;
290  }
291  if (power > 1) {
292  blur(dst, dst_step, a, 1, len, radius);
293  } else {
294  int i;
295  for (i = 0; i < len; i++)
296  dst[i*dst_step] = a[i];
297  }
298  } else {
299  int i;
300  for (i = 0; i < len; i++)
301  dst[i*dst_step] = src[i*src_step];
302  }
303 }
304 
305 static void hblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize,
306  int w, int h, int radius, int power, uint8_t *temp[2])
307 {
308  int y;
309 
310  if (radius == 0 && dst == src)
311  return;
312 
313  for (y = 0; y < h; y++)
314  blur_power(dst + y*dst_linesize, 1, src + y*src_linesize, 1,
315  w, radius, power, temp);
316 }
317 
318 static void vblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize,
319  int w, int h, int radius, int power, uint8_t *temp[2])
320 {
321  int x;
322 
323  if (radius == 0 && dst == src)
324  return;
325 
326  for (x = 0; x < w; x++)
327  blur_power(dst + x, dst_linesize, src + x, src_linesize,
328  h, radius, power, temp);
329 }
330 
332 {
333  AVFilterContext *ctx = inlink->dst;
334  BoxBlurContext *boxblur = ctx->priv;
335  AVFilterLink *outlink = inlink->dst->outputs[0];
337  int plane;
338  int cw = inlink->w >> boxblur->hsub, ch = in->video->h >> boxblur->vsub;
339  int w[4] = { inlink->w, cw, cw, inlink->w };
340  int h[4] = { in->video->h, ch, ch, in->video->h };
341 
342  out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
343  if (!out) {
345  return AVERROR(ENOMEM);
346  }
348 
349  for (plane = 0; in->data[plane] && plane < 4; plane++)
350  hblur(out->data[plane], out->linesize[plane],
351  in ->data[plane], in ->linesize[plane],
352  w[plane], h[plane], boxblur->radius[plane], boxblur->power[plane],
353  boxblur->temp);
354 
355  for (plane = 0; in->data[plane] && plane < 4; plane++)
356  vblur(out->data[plane], out->linesize[plane],
357  out->data[plane], out->linesize[plane],
358  w[plane], h[plane], boxblur->radius[plane], boxblur->power[plane],
359  boxblur->temp);
360 
362 
363  return ff_filter_frame(outlink, out);
364 }
365 
367  {
368  .name = "default",
369  .type = AVMEDIA_TYPE_VIDEO,
370  .config_props = config_input,
371  .filter_frame = filter_frame,
372  .min_perms = AV_PERM_READ
373  },
374  { NULL }
375 };
376 
378  {
379  .name = "default",
380  .type = AVMEDIA_TYPE_VIDEO,
381  },
382  { NULL }
383 };
384 
386  .name = "boxblur",
387  .description = NULL_IF_CONFIG_SMALL("Blur the input."),
388  .priv_size = sizeof(BoxBlurContext),
389  .init = init,
390  .uninit = uninit,
392 
393  .inputs = avfilter_vf_boxblur_inputs,
394  .outputs = avfilter_vf_boxblur_outputs,
395 
396  .priv_class = &boxblur_class,
397 };