2 * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
3 * Copyright (c) 2011 Stefano Sabatini
5 * This file is part of FFmpeg.
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.
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.
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.
24 * Apply a boxblur filter to the input video.
25 * Ported from MPlayer libmpcodecs/vf_boxblur.c.
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"
38 static const char *const var_names
[] = {
58 typedef struct FilterParam
{
64 typedef struct BoxBlurContext
{
66 FilterParam luma_param
;
67 FilterParam chroma_param
;
68 FilterParam alpha_param
;
73 uint8_t *temp
[2]; ///< temporary buffer used in blur_power()
81 static av_cold
int init(AVFilterContext
*ctx
)
83 BoxBlurContext
*s
= ctx
->priv
;
85 if (!s
->luma_param
.radius_expr
) {
86 av_log(ctx
, AV_LOG_ERROR
, "Luma radius expression is not set.\n");
87 return AVERROR(EINVAL
);
90 /* fill missing params */
91 if (!s
->chroma_param
.radius_expr
) {
92 s
->chroma_param
.radius_expr
= av_strdup(s
->luma_param
.radius_expr
);
93 if (!s
->chroma_param
.radius_expr
)
94 return AVERROR(ENOMEM
);
96 if (s
->chroma_param
.power
< 0)
97 s
->chroma_param
.power
= s
->luma_param
.power
;
99 if (!s
->alpha_param
.radius_expr
) {
100 s
->alpha_param
.radius_expr
= av_strdup(s
->luma_param
.radius_expr
);
101 if (!s
->alpha_param
.radius_expr
)
102 return AVERROR(ENOMEM
);
104 if (s
->alpha_param
.power
< 0)
105 s
->alpha_param
.power
= s
->luma_param
.power
;
110 static av_cold
void uninit(AVFilterContext
*ctx
)
112 BoxBlurContext
*s
= ctx
->priv
;
114 av_freep(&s
->temp
[0]);
115 av_freep(&s
->temp
[1]);
118 static int query_formats(AVFilterContext
*ctx
)
120 static const enum AVPixelFormat pix_fmts
[] = {
121 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV420P
,
122 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUVA420P
,
123 AV_PIX_FMT_YUV440P
, AV_PIX_FMT_GRAY8
,
124 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
129 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
133 static int config_input(AVFilterLink
*inlink
)
135 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
136 AVFilterContext
*ctx
= inlink
->dst
;
137 BoxBlurContext
*s
= ctx
->priv
;
138 int w
= inlink
->w
, h
= inlink
->h
;
140 double var_values
[VARS_NB
], res
;
144 if (!(s
->temp
[0] = av_malloc(FFMAX(w
, h
))) ||
145 !(s
->temp
[1] = av_malloc(FFMAX(w
, h
))))
146 return AVERROR(ENOMEM
);
148 s
->hsub
= desc
->log2_chroma_w
;
149 s
->vsub
= desc
->log2_chroma_h
;
151 var_values
[VAR_W
] = inlink
->w
;
152 var_values
[VAR_H
] = inlink
->h
;
153 var_values
[VAR_CW
] = cw
= w
>>s
->hsub
;
154 var_values
[VAR_CH
] = ch
= h
>>s
->vsub
;
155 var_values
[VAR_HSUB
] = 1<<s
->hsub
;
156 var_values
[VAR_VSUB
] = 1<<s
->vsub
;
158 #define EVAL_RADIUS_EXPR(comp) \
159 expr = s->comp##_param.radius_expr; \
160 ret = av_expr_parse_and_eval(&res, expr, var_names, var_values, \
161 NULL, NULL, NULL, NULL, NULL, 0, ctx); \
162 s->comp##_param.radius = res; \
164 av_log(NULL, AV_LOG_ERROR, \
165 "Error when evaluating " #comp " radius expression '%s'\n", expr); \
168 EVAL_RADIUS_EXPR(luma
);
169 EVAL_RADIUS_EXPR(chroma
);
170 EVAL_RADIUS_EXPR(alpha
);
172 av_log(ctx
, AV_LOG_VERBOSE
,
173 "luma_radius:%d luma_power:%d "
174 "chroma_radius:%d chroma_power:%d "
175 "alpha_radius:%d alpha_power:%d "
176 "w:%d chroma_w:%d h:%d chroma_h:%d\n",
177 s
->luma_param
.radius
, s
->luma_param
.power
,
178 s
->chroma_param
.radius
, s
->chroma_param
.power
,
179 s
->alpha_param
.radius
, s
->alpha_param
.power
,
182 #define CHECK_RADIUS_VAL(w_, h_, comp) \
183 if (s->comp##_param.radius < 0 || \
184 2*s->comp##_param.radius > FFMIN(w_, h_)) { \
185 av_log(ctx, AV_LOG_ERROR, \
186 "Invalid " #comp " radius value %d, must be >= 0 and <= %d\n", \
187 s->comp##_param.radius, FFMIN(w_, h_)/2); \
188 return AVERROR(EINVAL); \
190 CHECK_RADIUS_VAL(w
, h
, luma
);
191 CHECK_RADIUS_VAL(cw
, ch
, chroma
);
192 CHECK_RADIUS_VAL(w
, h
, alpha
);
194 s
->radius
[Y
] = s
->luma_param
.radius
;
195 s
->radius
[U
] = s
->radius
[V
] = s
->chroma_param
.radius
;
196 s
->radius
[A
] = s
->alpha_param
.radius
;
198 s
->power
[Y
] = s
->luma_param
.power
;
199 s
->power
[U
] = s
->power
[V
] = s
->chroma_param
.power
;
200 s
->power
[A
] = s
->alpha_param
.power
;
205 static inline void blur(uint8_t *dst
, int dst_step
, const uint8_t *src
, int src_step
,
208 /* Naive boxblur would sum source pixels from x-radius .. x+radius
209 * for destination pixel x. That would be O(radius*width).
210 * If you now look at what source pixels represent 2 consecutive
211 * output pixels, then you see they are almost identical and only
212 * differ by 2 pixels, like:
218 * so when you know one output pixel you can find the next by just adding
219 * and subtracting 1 input pixel.
220 * The following code adopts this faster variant.
222 const int length
= radius
*2 + 1;
223 const int inv
= ((1<<16) + length
/2)/length
;
226 for (x
= 0; x
< radius
; x
++)
227 sum
+= src
[x
*src_step
]<<1;
228 sum
+= src
[radius
*src_step
];
230 for (x
= 0; x
<= radius
; x
++) {
231 sum
+= src
[(radius
+x
)*src_step
] - src
[(radius
-x
)*src_step
];
232 dst
[x
*dst_step
] = (sum
*inv
+ (1<<15))>>16;
235 for (; x
< len
-radius
; x
++) {
236 sum
+= src
[(radius
+x
)*src_step
] - src
[(x
-radius
-1)*src_step
];
237 dst
[x
*dst_step
] = (sum
*inv
+ (1<<15))>>16;
240 for (; x
< len
; x
++) {
241 sum
+= src
[(2*len
-radius
-x
-1)*src_step
] - src
[(x
-radius
-1)*src_step
];
242 dst
[x
*dst_step
] = (sum
*inv
+ (1<<15))>>16;
246 static inline void blur_power(uint8_t *dst
, int dst_step
, const uint8_t *src
, int src_step
,
247 int len
, int radius
, int power
, uint8_t *temp
[2])
249 uint8_t *a
= temp
[0], *b
= temp
[1];
251 if (radius
&& power
) {
252 blur(a
, 1, src
, src_step
, len
, radius
);
253 for (; power
> 2; power
--) {
255 blur(b
, 1, a
, 1, len
, radius
);
259 blur(dst
, dst_step
, a
, 1, len
, radius
);
262 for (i
= 0; i
< len
; i
++)
263 dst
[i
*dst_step
] = a
[i
];
267 for (i
= 0; i
< len
; i
++)
268 dst
[i
*dst_step
] = src
[i
*src_step
];
272 static void hblur(uint8_t *dst
, int dst_linesize
, const uint8_t *src
, int src_linesize
,
273 int w
, int h
, int radius
, int power
, uint8_t *temp
[2])
277 if (radius
== 0 && dst
== src
)
280 for (y
= 0; y
< h
; y
++)
281 blur_power(dst
+ y
*dst_linesize
, 1, src
+ y
*src_linesize
, 1,
282 w
, radius
, power
, temp
);
285 static void vblur(uint8_t *dst
, int dst_linesize
, const uint8_t *src
, int src_linesize
,
286 int w
, int h
, int radius
, int power
, uint8_t *temp
[2])
290 if (radius
== 0 && dst
== src
)
293 for (x
= 0; x
< w
; x
++)
294 blur_power(dst
+ x
, dst_linesize
, src
+ x
, src_linesize
,
295 h
, radius
, power
, temp
);
298 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
300 AVFilterContext
*ctx
= inlink
->dst
;
301 BoxBlurContext
*s
= ctx
->priv
;
302 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
305 int cw
= FF_CEIL_RSHIFT(inlink
->w
, s
->hsub
), ch
= FF_CEIL_RSHIFT(in
->height
, s
->vsub
);
306 int w
[4] = { inlink
->w
, cw
, cw
, inlink
->w
};
307 int h
[4] = { in
->height
, ch
, ch
, in
->height
};
309 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
312 return AVERROR(ENOMEM
);
314 av_frame_copy_props(out
, in
);
316 for (plane
= 0; plane
< 4 && in
->data
[plane
] && in
->linesize
[plane
]; plane
++)
317 hblur(out
->data
[plane
], out
->linesize
[plane
],
318 in
->data
[plane
], in
->linesize
[plane
],
319 w
[plane
], h
[plane
], s
->radius
[plane
], s
->power
[plane
],
322 for (plane
= 0; plane
< 4 && in
->data
[plane
] && in
->linesize
[plane
]; plane
++)
323 vblur(out
->data
[plane
], out
->linesize
[plane
],
324 out
->data
[plane
], out
->linesize
[plane
],
325 w
[plane
], h
[plane
], s
->radius
[plane
], s
->power
[plane
],
330 return ff_filter_frame(outlink
, out
);
333 #define OFFSET(x) offsetof(BoxBlurContext, x)
334 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
336 static const AVOption boxblur_options
[] = {
337 { "luma_radius", "Radius of the luma blurring box", OFFSET(luma_param
.radius_expr
), AV_OPT_TYPE_STRING
, {.str
="2"}, .flags
= FLAGS
},
338 { "lr", "Radius of the luma blurring box", OFFSET(luma_param
.radius_expr
), AV_OPT_TYPE_STRING
, {.str
="2"}, .flags
= FLAGS
},
339 { "luma_power", "How many times should the boxblur be applied to luma", OFFSET(luma_param
.power
), AV_OPT_TYPE_INT
, {.i64
=2}, 0, INT_MAX
, .flags
= FLAGS
},
340 { "lp", "How many times should the boxblur be applied to luma", OFFSET(luma_param
.power
), AV_OPT_TYPE_INT
, {.i64
=2}, 0, INT_MAX
, .flags
= FLAGS
},
342 { "chroma_radius", "Radius of the chroma blurring box", OFFSET(chroma_param
.radius_expr
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, .flags
= FLAGS
},
343 { "cr", "Radius of the chroma blurring box", OFFSET(chroma_param
.radius_expr
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, .flags
= FLAGS
},
344 { "chroma_power", "How many times should the boxblur be applied to chroma", OFFSET(chroma_param
.power
), AV_OPT_TYPE_INT
, {.i64
=-1}, -1, INT_MAX
, .flags
= FLAGS
},
345 { "cp", "How many times should the boxblur be applied to chroma", OFFSET(chroma_param
.power
), AV_OPT_TYPE_INT
, {.i64
=-1}, -1, INT_MAX
, .flags
= FLAGS
},
347 { "alpha_radius", "Radius of the alpha blurring box", OFFSET(alpha_param
.radius_expr
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, .flags
= FLAGS
},
348 { "ar", "Radius of the alpha blurring box", OFFSET(alpha_param
.radius_expr
), AV_OPT_TYPE_STRING
, {.str
=NULL
}, .flags
= FLAGS
},
349 { "alpha_power", "How many times should the boxblur be applied to alpha", OFFSET(alpha_param
.power
), AV_OPT_TYPE_INT
, {.i64
=-1}, -1, INT_MAX
, .flags
= FLAGS
},
350 { "ap", "How many times should the boxblur be applied to alpha", OFFSET(alpha_param
.power
), AV_OPT_TYPE_INT
, {.i64
=-1}, -1, INT_MAX
, .flags
= FLAGS
},
355 AVFILTER_DEFINE_CLASS(boxblur
);
357 static const AVFilterPad avfilter_vf_boxblur_inputs
[] = {
360 .type
= AVMEDIA_TYPE_VIDEO
,
361 .config_props
= config_input
,
362 .filter_frame
= filter_frame
,
367 static const AVFilterPad avfilter_vf_boxblur_outputs
[] = {
370 .type
= AVMEDIA_TYPE_VIDEO
,
375 AVFilter ff_vf_boxblur
= {
377 .description
= NULL_IF_CONFIG_SMALL("Blur the input."),
378 .priv_size
= sizeof(BoxBlurContext
),
379 .priv_class
= &boxblur_class
,
382 .query_formats
= query_formats
,
383 .inputs
= avfilter_vf_boxblur_inputs
,
384 .outputs
= avfilter_vf_boxblur_outputs
,
385 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,