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 38static const char *const var_names[] = { 39 "w", 40 "h", 41 "cw", 42 "ch", 43 "hsub", 44 "vsub", 45 NULL 46}; 47 48enum 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 58typedef struct FilterParam { 59 int radius; 60 int power; 61 char *radius_expr; 62} FilterParam; 63 64typedef struct BoxBlurContext { 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 81static 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 110static 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 118static 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 133static 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<<s->hsub; 156 var_values[VAR_VSUB] = 1<<s->vsub; 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 205static 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 246static 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 272static 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 285static 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 298static 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 336static 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 355AVFILTER_DEFINE_CLASS(boxblur); 356 357static 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 367static const AVFilterPad avfilter_vf_boxblur_outputs[] = { 368 { 369 .name = "default", 370 .type = AVMEDIA_TYPE_VIDEO, 371 }, 372 { NULL } 373}; 374 375AVFilter ff_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}; 387