1/* 2 * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at> 3 * Copyright (c) 2011 Stefano Sabatini 4 * 5 * This file is part of Libav. 6 * 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. 11 * 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. 16 * 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. 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/eval.h" 30#include "libavutil/pixdesc.h" 31#include "avfilter.h" 32 33static const char *var_names[] = { 34 "w", 35 "h", 36 "cw", 37 "ch", 38 "hsub", 39 "vsub", 40 NULL 41}; 42 43enum var_name { 44 VAR_W, 45 VAR_H, 46 VAR_CW, 47 VAR_CH, 48 VAR_HSUB, 49 VAR_VSUB, 50 VARS_NB 51}; 52 53typedef struct { 54 int radius; 55 int power; 56} FilterParam; 57 58typedef struct { 59 FilterParam luma_param; 60 FilterParam chroma_param; 61 FilterParam alpha_param; 62 char luma_radius_expr [256]; 63 char chroma_radius_expr[256]; 64 char alpha_radius_expr [256]; 65 66 int hsub, vsub; 67 int radius[4]; 68 int power[4]; 69 uint8_t *temp[2]; ///< temporary buffer used in blur_power() 70} BoxBlurContext; 71 72#define Y 0 73#define U 1 74#define V 2 75#define A 3 76 77static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) 78{ 79 BoxBlurContext *boxblur = ctx->priv; 80 int e; 81 82 if (!args) { 83 av_log(ctx, AV_LOG_ERROR, 84 "Filter expects 2 or 4 or 6 arguments, none provided\n"); 85 return AVERROR(EINVAL); 86 } 87 88 e = sscanf(args, "%255[^:]:%d:%255[^:]:%d:%255[^:]:%d", 89 boxblur->luma_radius_expr, &boxblur->luma_param .power, 90 boxblur->chroma_radius_expr, &boxblur->chroma_param.power, 91 boxblur->alpha_radius_expr, &boxblur->alpha_param .power); 92 93 if (e != 2 && e != 4 && e != 6) { 94 av_log(ctx, AV_LOG_ERROR, 95 "Filter expects 2 or 4 or 6 params, provided %d\n", e); 96 return AVERROR(EINVAL); 97 } 98 99 if (e < 4) { 100 boxblur->chroma_param.power = boxblur->luma_param.power; 101 av_strlcpy(boxblur->chroma_radius_expr, boxblur->luma_radius_expr, 102 sizeof(boxblur->chroma_radius_expr)); 103 } 104 if (e < 6) { 105 boxblur->alpha_param.power = boxblur->luma_param.power; 106 av_strlcpy(boxblur->alpha_radius_expr, boxblur->luma_radius_expr, 107 sizeof(boxblur->alpha_radius_expr)); 108 } 109 110 return 0; 111} 112 113static av_cold void uninit(AVFilterContext *ctx) 114{ 115 BoxBlurContext *boxblur = ctx->priv; 116 117 av_freep(&boxblur->temp[0]); 118 av_freep(&boxblur->temp[1]); 119} 120 121static int query_formats(AVFilterContext *ctx) 122{ 123 enum PixelFormat pix_fmts[] = { 124 PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV420P, 125 PIX_FMT_YUV411P, PIX_FMT_YUV410P, PIX_FMT_YUVA420P, 126 PIX_FMT_YUV440P, PIX_FMT_GRAY8, 127 PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P, 128 PIX_FMT_YUVJ440P, 129 PIX_FMT_NONE 130 }; 131 132 avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); 133 return 0; 134} 135 136static int config_input(AVFilterLink *inlink) 137{ 138 const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format]; 139 AVFilterContext *ctx = inlink->dst; 140 BoxBlurContext *boxblur = ctx->priv; 141 int w = inlink->w, h = inlink->h; 142 int cw, ch; 143 double var_values[VARS_NB], res; 144 char *expr; 145 int ret; 146 147 av_freep(&boxblur->temp[0]); 148 av_freep(&boxblur->temp[1]); 149 if (!(boxblur->temp[0] = av_malloc(FFMAX(w, h)))) 150 return AVERROR(ENOMEM); 151 if (!(boxblur->temp[1] = av_malloc(FFMAX(w, h)))) { 152 av_freep(&boxblur->temp[0]); 153 return AVERROR(ENOMEM); 154 } 155 156 boxblur->hsub = desc->log2_chroma_w; 157 boxblur->vsub = desc->log2_chroma_h; 158 159 var_values[VAR_W] = inlink->w; 160 var_values[VAR_H] = inlink->h; 161 var_values[VAR_CW] = cw = w>>boxblur->hsub; 162 var_values[VAR_CH] = ch = h>>boxblur->vsub; 163 var_values[VAR_HSUB] = 1<<boxblur->hsub; 164 var_values[VAR_VSUB] = 1<<boxblur->vsub; 165 166#define EVAL_RADIUS_EXPR(comp) \ 167 expr = boxblur->comp##_radius_expr; \ 168 ret = av_expr_parse_and_eval(&res, expr, var_names, var_values, \ 169 NULL, NULL, NULL, NULL, NULL, 0, ctx); \ 170 boxblur->comp##_param.radius = res; \ 171 if (ret < 0) { \ 172 av_log(NULL, AV_LOG_ERROR, \ 173 "Error when evaluating " #comp " radius expression '%s'\n", expr); \ 174 return ret; \ 175 } 176 EVAL_RADIUS_EXPR(luma); 177 EVAL_RADIUS_EXPR(chroma); 178 EVAL_RADIUS_EXPR(alpha); 179 180 av_log(ctx, AV_LOG_DEBUG, 181 "luma_radius:%d luma_power:%d " 182 "chroma_radius:%d chroma_power:%d " 183 "alpha_radius:%d alpha_power:%d " 184 "w:%d chroma_w:%d h:%d chroma_h:%d\n", 185 boxblur->luma_param .radius, boxblur->luma_param .power, 186 boxblur->chroma_param.radius, boxblur->chroma_param.power, 187 boxblur->alpha_param .radius, boxblur->alpha_param .power, 188 w, cw, h, ch); 189 190#define CHECK_RADIUS_VAL(w_, h_, comp) \ 191 if (boxblur->comp##_param.radius < 0 || \ 192 2*boxblur->comp##_param.radius > FFMIN(w_, h_)) { \ 193 av_log(ctx, AV_LOG_ERROR, \ 194 "Invalid " #comp " radius value %d, must be >= 0 and <= %d\n", \ 195 boxblur->comp##_param.radius, FFMIN(w_, h_)/2); \ 196 return AVERROR(EINVAL); \ 197 } 198 CHECK_RADIUS_VAL(w, h, luma); 199 CHECK_RADIUS_VAL(cw, ch, chroma); 200 CHECK_RADIUS_VAL(w, h, alpha); 201 202 boxblur->radius[Y] = boxblur->luma_param.radius; 203 boxblur->radius[U] = boxblur->radius[V] = boxblur->chroma_param.radius; 204 boxblur->radius[A] = boxblur->alpha_param.radius; 205 206 boxblur->power[Y] = boxblur->luma_param.power; 207 boxblur->power[U] = boxblur->power[V] = boxblur->chroma_param.power; 208 boxblur->power[A] = boxblur->alpha_param.power; 209 210 return 0; 211} 212 213static inline void blur(uint8_t *dst, int dst_step, const uint8_t *src, int src_step, 214 int len, int radius) 215{ 216 /* Naive boxblur would sum source pixels from x-radius .. x+radius 217 * for destination pixel x. That would be O(radius*width). 218 * If you now look at what source pixels represent 2 consecutive 219 * output pixels, then you see they are almost identical and only 220 * differ by 2 pixels, like: 221 * src0 111111111 222 * dst0 1 223 * src1 111111111 224 * dst1 1 225 * src0-src1 1 -1 226 * so when you know one output pixel you can find the next by just adding 227 * and subtracting 1 input pixel. 228 * The following code adopts this faster variant. 229 */ 230 const int length = radius*2 + 1; 231 const int inv = ((1<<16) + length/2)/length; 232 int x, sum = 0; 233 234 for (x = 0; x < radius; x++) 235 sum += src[x*src_step]<<1; 236 sum += src[radius*src_step]; 237 238 for (x = 0; x <= radius; x++) { 239 sum += src[(radius+x)*src_step] - src[(radius-x)*src_step]; 240 dst[x*dst_step] = (sum*inv + (1<<15))>>16; 241 } 242 243 for (; x < len-radius; x++) { 244 sum += src[(radius+x)*src_step] - src[(x-radius-1)*src_step]; 245 dst[x*dst_step] = (sum*inv + (1<<15))>>16; 246 } 247 248 for (; x < len; x++) { 249 sum += src[(2*len-radius-x-1)*src_step] - src[(x-radius-1)*src_step]; 250 dst[x*dst_step] = (sum*inv + (1<<15))>>16; 251 } 252} 253 254static inline void blur_power(uint8_t *dst, int dst_step, const uint8_t *src, int src_step, 255 int len, int radius, int power, uint8_t *temp[2]) 256{ 257 uint8_t *a = temp[0], *b = temp[1]; 258 259 if (radius && power) { 260 blur(a, 1, src, src_step, len, radius); 261 for (; power > 2; power--) { 262 uint8_t *c; 263 blur(b, 1, a, 1, len, radius); 264 c = a; a = b; b = c; 265 } 266 if (power > 1) { 267 blur(dst, dst_step, a, 1, len, radius); 268 } else { 269 int i; 270 for (i = 0; i < len; i++) 271 dst[i*dst_step] = a[i]; 272 } 273 } else { 274 int i; 275 for (i = 0; i < len; i++) 276 dst[i*dst_step] = src[i*src_step]; 277 } 278} 279 280static void hblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize, 281 int w, int h, int radius, int power, uint8_t *temp[2]) 282{ 283 int y; 284 285 if (radius == 0 && dst == src) 286 return; 287 288 for (y = 0; y < h; y++) 289 blur_power(dst + y*dst_linesize, 1, src + y*src_linesize, 1, 290 w, radius, power, temp); 291} 292 293static void vblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize, 294 int w, int h, int radius, int power, uint8_t *temp[2]) 295{ 296 int x; 297 298 if (radius == 0 && dst == src) 299 return; 300 301 for (x = 0; x < w; x++) 302 blur_power(dst + x, dst_linesize, src + x, src_linesize, 303 h, radius, power, temp); 304} 305 306static void draw_slice(AVFilterLink *inlink, int y0, int h0, int slice_dir) 307{ 308 AVFilterContext *ctx = inlink->dst; 309 BoxBlurContext *boxblur = ctx->priv; 310 AVFilterLink *outlink = inlink->dst->outputs[0]; 311 AVFilterBufferRef *inpicref = inlink ->cur_buf; 312 AVFilterBufferRef *outpicref = outlink->out_buf; 313 int plane; 314 int cw = inlink->w >> boxblur->hsub, ch = h0 >> boxblur->vsub; 315 int w[4] = { inlink->w, cw, cw, inlink->w }; 316 int h[4] = { h0, ch, ch, h0 }; 317 318 for (plane = 0; inpicref->data[plane] && plane < 4; plane++) 319 hblur(outpicref->data[plane], outpicref->linesize[plane], 320 inpicref ->data[plane], inpicref ->linesize[plane], 321 w[plane], h[plane], boxblur->radius[plane], boxblur->power[plane], 322 boxblur->temp); 323 324 for (plane = 0; inpicref->data[plane] && plane < 4; plane++) 325 vblur(outpicref->data[plane], outpicref->linesize[plane], 326 outpicref->data[plane], outpicref->linesize[plane], 327 w[plane], h[plane], boxblur->radius[plane], boxblur->power[plane], 328 boxblur->temp); 329 330 avfilter_draw_slice(outlink, y0, h0, slice_dir); 331} 332 333AVFilter avfilter_vf_boxblur = { 334 .name = "boxblur", 335 .description = NULL_IF_CONFIG_SMALL("Blur the input."), 336 .priv_size = sizeof(BoxBlurContext), 337 .init = init, 338 .uninit = uninit, 339 .query_formats = query_formats, 340 341 .inputs = (AVFilterPad[]) {{ .name = "default", 342 .type = AVMEDIA_TYPE_VIDEO, 343 .config_props = config_input, 344 .draw_slice = draw_slice, 345 .min_perms = AV_PERM_READ }, 346 { .name = NULL}}, 347 .outputs = (AVFilterPad[]) {{ .name = "default", 348 .type = AVMEDIA_TYPE_VIDEO, }, 349 { .name = NULL}}, 350}; 351