Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * Copyright (c) 2002 Michael Niedermayer |
||
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 { |
||
49 | VAR_W, |
||
50 | VAR_H, |
||
51 | VAR_CW, |
||
52 | VAR_CH, |
||
53 | VAR_HSUB, |
||
54 | VAR_VSUB, |
||
55 | VARS_NB |
||
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; |
||
66 | FilterParam luma_param; |
||
67 | FilterParam chroma_param; |
||
68 | FilterParam alpha_param; |
||
69 | |||
70 | int hsub, vsub; |
||
71 | int radius[4]; |
||
72 | int power[4]; |
||
73 | uint8_t *temp[2]; ///< temporary buffer used in blur_power() |
||
74 | } BoxBlurContext; |
||
75 | |||
76 | #define Y 0 |
||
77 | #define U 1 |
||
78 | #define V 2 |
||
79 | #define A 3 |
||
80 | |||
81 | static av_cold int init(AVFilterContext *ctx) |
||
82 | { |
||
83 | BoxBlurContext *s = ctx->priv; |
||
84 | |||
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); |
||
88 | } |
||
89 | |||
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); |
||
95 | } |
||
96 | if (s->chroma_param.power < 0) |
||
97 | s->chroma_param.power = s->luma_param.power; |
||
98 | |||
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); |
||
103 | } |
||
104 | if (s->alpha_param.power < 0) |
||
105 | s->alpha_param.power = s->luma_param.power; |
||
106 | |||
107 | return 0; |
||
108 | } |
||
109 | |||
110 | static av_cold void uninit(AVFilterContext *ctx) |
||
111 | { |
||
112 | BoxBlurContext *s = ctx->priv; |
||
113 | |||
114 | av_freep(&s->temp[0]); |
||
115 | av_freep(&s->temp[1]); |
||
116 | } |
||
117 | |||
118 | static int query_formats(AVFilterContext *ctx) |
||
119 | { |
||
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, |
||
125 | AV_PIX_FMT_YUVJ440P, |
||
126 | AV_PIX_FMT_NONE |
||
127 | }; |
||
128 | |||
129 | ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); |
||
130 | return 0; |
||
131 | } |
||
132 | |||
133 | static int config_input(AVFilterLink *inlink) |
||
134 | { |
||
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; |
||
139 | int cw, ch; |
||
140 | double var_values[VARS_NB], res; |
||
141 | char *expr; |
||
142 | int ret; |
||
143 | |||
144 | if (!(s->temp[0] = av_malloc(FFMAX(w, h))) || |
||
145 | !(s->temp[1] = av_malloc(FFMAX(w, h)))) |
||
146 | return AVERROR(ENOMEM); |
||
147 | |||
148 | s->hsub = desc->log2_chroma_w; |
||
149 | s->vsub = desc->log2_chroma_h; |
||
150 | |||
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< |
||
156 | var_values[VAR_VSUB] = 1< |
||
157 | |||
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; \ |
||
163 | if (ret < 0) { \ |
||
164 | av_log(NULL, AV_LOG_ERROR, \ |
||
165 | "Error when evaluating " #comp " radius expression '%s'\n", expr); \ |
||
166 | return ret; \ |
||
167 | } |
||
168 | EVAL_RADIUS_EXPR(luma); |
||
169 | EVAL_RADIUS_EXPR(chroma); |
||
170 | EVAL_RADIUS_EXPR(alpha); |
||
171 | |||
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, |
||
180 | w, cw, h, ch); |
||
181 | |||
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); \ |
||
189 | } |
||
190 | CHECK_RADIUS_VAL(w, h, luma); |
||
191 | CHECK_RADIUS_VAL(cw, ch, chroma); |
||
192 | CHECK_RADIUS_VAL(w, h, alpha); |
||
193 | |||
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; |
||
197 | |||
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; |
||
201 | |||
202 | return 0; |
||
203 | } |
||
204 | |||
205 | static inline void blur(uint8_t *dst, int dst_step, const uint8_t *src, int src_step, |
||
206 | int len, int radius) |
||
207 | { |
||
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: |
||
213 | * src0 111111111 |
||
214 | * dst0 1 |
||
215 | * src1 111111111 |
||
216 | * dst1 1 |
||
217 | * src0-src1 1 -1 |
||
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. |
||
221 | */ |
||
222 | const int length = radius*2 + 1; |
||
223 | const int inv = ((1<<16) + length/2)/length; |
||
224 | int x, sum = 0; |
||
225 | |||
226 | for (x = 0; x < radius; x++) |
||
227 | sum += src[x*src_step]<<1; |
||
228 | sum += src[radius*src_step]; |
||
229 | |||
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; |
||
233 | } |
||
234 | |||
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; |
||
238 | } |
||
239 | |||
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; |
||
243 | } |
||
244 | } |
||
245 | |||
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]) |
||
248 | { |
||
249 | uint8_t *a = temp[0], *b = temp[1]; |
||
250 | |||
251 | if (radius && power) { |
||
252 | blur(a, 1, src, src_step, len, radius); |
||
253 | for (; power > 2; power--) { |
||
254 | uint8_t *c; |
||
255 | blur(b, 1, a, 1, len, radius); |
||
256 | c = a; a = b; b = c; |
||
257 | } |
||
258 | if (power > 1) { |
||
259 | blur(dst, dst_step, a, 1, len, radius); |
||
260 | } else { |
||
261 | int i; |
||
262 | for (i = 0; i < len; i++) |
||
263 | dst[i*dst_step] = a[i]; |
||
264 | } |
||
265 | } else { |
||
266 | int i; |
||
267 | for (i = 0; i < len; i++) |
||
268 | dst[i*dst_step] = src[i*src_step]; |
||
269 | } |
||
270 | } |
||
271 | |||
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]) |
||
274 | { |
||
275 | int y; |
||
276 | |||
277 | if (radius == 0 && dst == src) |
||
278 | return; |
||
279 | |||
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); |
||
283 | } |
||
284 | |||
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]) |
||
287 | { |
||
288 | int x; |
||
289 | |||
290 | if (radius == 0 && dst == src) |
||
291 | return; |
||
292 | |||
293 | for (x = 0; x < w; x++) |
||
294 | blur_power(dst + x, dst_linesize, src + x, src_linesize, |
||
295 | h, radius, power, temp); |
||
296 | } |
||
297 | |||
298 | static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
||
299 | { |
||
300 | AVFilterContext *ctx = inlink->dst; |
||
301 | BoxBlurContext *s = ctx->priv; |
||
302 | AVFilterLink *outlink = inlink->dst->outputs[0]; |
||
303 | AVFrame *out; |
||
304 | int plane; |
||
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 }; |
||
308 | |||
309 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
||
310 | if (!out) { |
||
311 | av_frame_free(&in); |
||
312 | return AVERROR(ENOMEM); |
||
313 | } |
||
314 | av_frame_copy_props(out, in); |
||
315 | |||
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], |
||
320 | s->temp); |
||
321 | |||
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], |
||
326 | s->temp); |
||
327 | |||
328 | av_frame_free(&in); |
||
329 | |||
330 | return ff_filter_frame(outlink, out); |
||
331 | } |
||
332 | |||
333 | #define OFFSET(x) offsetof(BoxBlurContext, x) |
||
334 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
||
335 | |||
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 }, |
||
341 | |||
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 }, |
||
346 | |||
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 }, |
||
351 | |||
352 | { NULL } |
||
353 | }; |
||
354 | |||
355 | AVFILTER_DEFINE_CLASS(boxblur); |
||
356 | |||
357 | static const AVFilterPad avfilter_vf_boxblur_inputs[] = { |
||
358 | { |
||
359 | .name = "default", |
||
360 | .type = AVMEDIA_TYPE_VIDEO, |
||
361 | .config_props = config_input, |
||
362 | .filter_frame = filter_frame, |
||
363 | }, |
||
364 | { NULL } |
||
365 | }; |
||
366 | |||
367 | static const AVFilterPad avfilter_vf_boxblur_outputs[] = { |
||
368 | { |
||
369 | .name = "default", |
||
370 | .type = AVMEDIA_TYPE_VIDEO, |
||
371 | }, |
||
372 | { NULL } |
||
373 | }; |
||
374 | |||
375 | AVFilter avfilter_vf_boxblur = { |
||
376 | .name = "boxblur", |
||
377 | .description = NULL_IF_CONFIG_SMALL("Blur the input."), |
||
378 | .priv_size = sizeof(BoxBlurContext), |
||
379 | .priv_class = &boxblur_class, |
||
380 | .init = init, |
||
381 | .uninit = uninit, |
||
382 | .query_formats = query_formats, |
||
383 | .inputs = avfilter_vf_boxblur_inputs, |
||
384 | .outputs = avfilter_vf_boxblur_outputs, |
||
385 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, |
||
386 | };>>>>>>15))><15))>>15))><15))>>15))><15))>=>1; |