2 * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
3 * Copyright (c) 2011 Stefano Sabatini
5 * This file is part of Libav.
7 * Libav 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 * Libav 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 Libav; 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/pixdesc.h"
37 static const char *const var_names
[] = {
63 FilterParam luma_param
;
64 FilterParam chroma_param
;
65 FilterParam alpha_param
;
66 char luma_radius_expr
[256];
67 char chroma_radius_expr
[256];
68 char alpha_radius_expr
[256];
73 uint8_t *temp
[2]; ///< temporary buffer used in blur_power()
81 static av_cold
int init(AVFilterContext
*ctx
, const char *args
)
83 BoxBlurContext
*boxblur
= ctx
->priv
;
87 av_log(ctx
, AV_LOG_ERROR
,
88 "Filter expects 2 or 4 or 6 arguments, none provided\n");
89 return AVERROR(EINVAL
);
92 e
= sscanf(args
, "%255[^:]:%d:%255[^:]:%d:%255[^:]:%d",
93 boxblur
->luma_radius_expr
, &boxblur
->luma_param
.power
,
94 boxblur
->chroma_radius_expr
, &boxblur
->chroma_param
.power
,
95 boxblur
->alpha_radius_expr
, &boxblur
->alpha_param
.power
);
97 if (e
!= 2 && e
!= 4 && e
!= 6) {
98 av_log(ctx
, AV_LOG_ERROR
,
99 "Filter expects 2 or 4 or 6 params, provided %d\n", e
);
100 return AVERROR(EINVAL
);
104 boxblur
->chroma_param
.power
= boxblur
->luma_param
.power
;
105 av_strlcpy(boxblur
->chroma_radius_expr
, boxblur
->luma_radius_expr
,
106 sizeof(boxblur
->chroma_radius_expr
));
109 boxblur
->alpha_param
.power
= boxblur
->luma_param
.power
;
110 av_strlcpy(boxblur
->alpha_radius_expr
, boxblur
->luma_radius_expr
,
111 sizeof(boxblur
->alpha_radius_expr
));
117 static av_cold
void uninit(AVFilterContext
*ctx
)
119 BoxBlurContext
*boxblur
= ctx
->priv
;
121 av_freep(&boxblur
->temp
[0]);
122 av_freep(&boxblur
->temp
[1]);
125 static int query_formats(AVFilterContext
*ctx
)
127 enum AVPixelFormat pix_fmts
[] = {
128 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV420P
,
129 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUVA420P
,
130 AV_PIX_FMT_YUV440P
, AV_PIX_FMT_GRAY8
,
131 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
136 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
140 static int config_input(AVFilterLink
*inlink
)
142 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
143 AVFilterContext
*ctx
= inlink
->dst
;
144 BoxBlurContext
*boxblur
= ctx
->priv
;
145 int w
= inlink
->w
, h
= inlink
->h
;
147 double var_values
[VARS_NB
], res
;
151 av_freep(&boxblur
->temp
[0]);
152 av_freep(&boxblur
->temp
[1]);
153 if (!(boxblur
->temp
[0] = av_malloc(FFMAX(w
, h
))))
154 return AVERROR(ENOMEM
);
155 if (!(boxblur
->temp
[1] = av_malloc(FFMAX(w
, h
)))) {
156 av_freep(&boxblur
->temp
[0]);
157 return AVERROR(ENOMEM
);
160 boxblur
->hsub
= desc
->log2_chroma_w
;
161 boxblur
->vsub
= desc
->log2_chroma_h
;
163 var_values
[VAR_W
] = inlink
->w
;
164 var_values
[VAR_H
] = inlink
->h
;
165 var_values
[VAR_CW
] = cw
= w
>>boxblur
->hsub
;
166 var_values
[VAR_CH
] = ch
= h
>>boxblur
->vsub
;
167 var_values
[VAR_HSUB
] = 1<<boxblur
->hsub
;
168 var_values
[VAR_VSUB
] = 1<<boxblur
->vsub
;
170 #define EVAL_RADIUS_EXPR(comp) \
171 expr = boxblur->comp##_radius_expr; \
172 ret = av_expr_parse_and_eval(&res, expr, var_names, var_values, \
173 NULL, NULL, NULL, NULL, NULL, 0, ctx); \
174 boxblur->comp##_param.radius = res; \
176 av_log(NULL, AV_LOG_ERROR, \
177 "Error when evaluating " #comp " radius expression '%s'\n", expr); \
180 EVAL_RADIUS_EXPR(luma
);
181 EVAL_RADIUS_EXPR(chroma
);
182 EVAL_RADIUS_EXPR(alpha
);
184 av_log(ctx
, AV_LOG_DEBUG
,
185 "luma_radius:%d luma_power:%d "
186 "chroma_radius:%d chroma_power:%d "
187 "alpha_radius:%d alpha_power:%d "
188 "w:%d chroma_w:%d h:%d chroma_h:%d\n",
189 boxblur
->luma_param
.radius
, boxblur
->luma_param
.power
,
190 boxblur
->chroma_param
.radius
, boxblur
->chroma_param
.power
,
191 boxblur
->alpha_param
.radius
, boxblur
->alpha_param
.power
,
194 #define CHECK_RADIUS_VAL(w_, h_, comp) \
195 if (boxblur->comp##_param.radius < 0 || \
196 2*boxblur->comp##_param.radius > FFMIN(w_, h_)) { \
197 av_log(ctx, AV_LOG_ERROR, \
198 "Invalid " #comp " radius value %d, must be >= 0 and <= %d\n", \
199 boxblur->comp##_param.radius, FFMIN(w_, h_)/2); \
200 return AVERROR(EINVAL); \
202 CHECK_RADIUS_VAL(w
, h
, luma
);
203 CHECK_RADIUS_VAL(cw
, ch
, chroma
);
204 CHECK_RADIUS_VAL(w
, h
, alpha
);
206 boxblur
->radius
[Y
] = boxblur
->luma_param
.radius
;
207 boxblur
->radius
[U
] = boxblur
->radius
[V
] = boxblur
->chroma_param
.radius
;
208 boxblur
->radius
[A
] = boxblur
->alpha_param
.radius
;
210 boxblur
->power
[Y
] = boxblur
->luma_param
.power
;
211 boxblur
->power
[U
] = boxblur
->power
[V
] = boxblur
->chroma_param
.power
;
212 boxblur
->power
[A
] = boxblur
->alpha_param
.power
;
217 static inline void blur(uint8_t *dst
, int dst_step
, const uint8_t *src
, int src_step
,
220 /* Naive boxblur would sum source pixels from x-radius .. x+radius
221 * for destination pixel x. That would be O(radius*width).
222 * If you now look at what source pixels represent 2 consecutive
223 * output pixels, then you see they are almost identical and only
224 * differ by 2 pixels, like:
230 * so when you know one output pixel you can find the next by just adding
231 * and subtracting 1 input pixel.
232 * The following code adopts this faster variant.
234 const int length
= radius
*2 + 1;
235 const int inv
= ((1<<16) + length
/2)/length
;
238 for (x
= 0; x
< radius
; x
++)
239 sum
+= src
[x
*src_step
]<<1;
240 sum
+= src
[radius
*src_step
];
242 for (x
= 0; x
<= radius
; x
++) {
243 sum
+= src
[(radius
+x
)*src_step
] - src
[(radius
-x
)*src_step
];
244 dst
[x
*dst_step
] = (sum
*inv
+ (1<<15))>>16;
247 for (; x
< len
-radius
; x
++) {
248 sum
+= src
[(radius
+x
)*src_step
] - src
[(x
-radius
-1)*src_step
];
249 dst
[x
*dst_step
] = (sum
*inv
+ (1<<15))>>16;
252 for (; x
< len
; x
++) {
253 sum
+= src
[(2*len
-radius
-x
-1)*src_step
] - src
[(x
-radius
-1)*src_step
];
254 dst
[x
*dst_step
] = (sum
*inv
+ (1<<15))>>16;
258 static inline void blur_power(uint8_t *dst
, int dst_step
, const uint8_t *src
, int src_step
,
259 int len
, int radius
, int power
, uint8_t *temp
[2])
261 uint8_t *a
= temp
[0], *b
= temp
[1];
263 if (radius
&& power
) {
264 blur(a
, 1, src
, src_step
, len
, radius
);
265 for (; power
> 2; power
--) {
267 blur(b
, 1, a
, 1, len
, radius
);
271 blur(dst
, dst_step
, a
, 1, len
, radius
);
274 for (i
= 0; i
< len
; i
++)
275 dst
[i
*dst_step
] = a
[i
];
279 for (i
= 0; i
< len
; i
++)
280 dst
[i
*dst_step
] = src
[i
*src_step
];
284 static void hblur(uint8_t *dst
, int dst_linesize
, const uint8_t *src
, int src_linesize
,
285 int w
, int h
, int radius
, int power
, uint8_t *temp
[2])
289 if (radius
== 0 && dst
== src
)
292 for (y
= 0; y
< h
; y
++)
293 blur_power(dst
+ y
*dst_linesize
, 1, src
+ y
*src_linesize
, 1,
294 w
, radius
, power
, temp
);
297 static void vblur(uint8_t *dst
, int dst_linesize
, const uint8_t *src
, int src_linesize
,
298 int w
, int h
, int radius
, int power
, uint8_t *temp
[2])
302 if (radius
== 0 && dst
== src
)
305 for (x
= 0; x
< w
; x
++)
306 blur_power(dst
+ x
, dst_linesize
, src
+ x
, src_linesize
,
307 h
, radius
, power
, temp
);
310 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
312 AVFilterContext
*ctx
= inlink
->dst
;
313 BoxBlurContext
*boxblur
= ctx
->priv
;
314 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
317 int cw
= inlink
->w
>> boxblur
->hsub
, ch
= in
->height
>> boxblur
->vsub
;
318 int w
[4] = { inlink
->w
, cw
, cw
, inlink
->w
};
319 int h
[4] = { in
->height
, ch
, ch
, in
->height
};
321 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
324 return AVERROR(ENOMEM
);
326 av_frame_copy_props(out
, in
);
328 for (plane
= 0; in
->data
[plane
] && plane
< 4; plane
++)
329 hblur(out
->data
[plane
], out
->linesize
[plane
],
330 in
->data
[plane
], in
->linesize
[plane
],
331 w
[plane
], h
[plane
], boxblur
->radius
[plane
], boxblur
->power
[plane
],
334 for (plane
= 0; in
->data
[plane
] && plane
< 4; plane
++)
335 vblur(out
->data
[plane
], out
->linesize
[plane
],
336 out
->data
[plane
], out
->linesize
[plane
],
337 w
[plane
], h
[plane
], boxblur
->radius
[plane
], boxblur
->power
[plane
],
342 return ff_filter_frame(outlink
, out
);
345 static const AVFilterPad avfilter_vf_boxblur_inputs
[] = {
348 .type
= AVMEDIA_TYPE_VIDEO
,
349 .config_props
= config_input
,
350 .filter_frame
= filter_frame
,
355 static const AVFilterPad avfilter_vf_boxblur_outputs
[] = {
358 .type
= AVMEDIA_TYPE_VIDEO
,
363 AVFilter avfilter_vf_boxblur
= {
365 .description
= NULL_IF_CONFIG_SMALL("Blur the input."),
366 .priv_size
= sizeof(BoxBlurContext
),
369 .query_formats
= query_formats
,
371 .inputs
= avfilter_vf_boxblur_inputs
,
372 .outputs
= avfilter_vf_boxblur_outputs
,