2011-07-09 19:13:10 +03:00
/*
* Copyright ( c ) 2002 Michael Niedermayer < michaelni @ gmx . at >
* Copyright ( c ) 2011 Stefano Sabatini
*
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* FFmpeg is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with FFmpeg ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
/**
* @ file
* Apply a boxblur filter to the input video .
* Ported from MPlayer libmpcodecs / vf_boxblur . c .
*/
# include "libavutil/avstring.h"
2012-08-06 16:49:32 +03:00
# include "libavutil/common.h"
2011-07-09 19:13:10 +03:00
# include "libavutil/eval.h"
2013-02-20 17:45:02 +03:00
# include "libavutil/opt.h"
2011-07-09 19:13:10 +03:00
# include "libavutil/pixdesc.h"
# include "avfilter.h"
2012-05-30 11:12:55 +03:00
# include "formats.h"
2012-06-12 21:12:42 +03:00
# include "internal.h"
2012-05-30 12:20:32 +03:00
# include "video.h"
2011-07-09 19:13:10 +03:00
2012-02-20 11:42:33 +03:00
static const char * const var_names [ ] = {
2011-07-09 19:13:10 +03:00
" w " ,
" h " ,
" cw " ,
" ch " ,
" hsub " ,
" vsub " ,
NULL
} ;
enum var_name {
VAR_W ,
VAR_H ,
VAR_CW ,
VAR_CH ,
VAR_HSUB ,
VAR_VSUB ,
VARS_NB
} ;
2014-04-11 12:54:15 +03:00
typedef struct FilterParam {
2011-07-09 19:13:10 +03:00
int radius ;
int power ;
2013-02-20 17:45:02 +03:00
char * radius_expr ;
2011-07-09 19:13:10 +03:00
} FilterParam ;
2014-04-11 12:54:15 +03:00
typedef struct BoxBlurContext {
2013-02-20 17:45:02 +03:00
const AVClass * class ;
2011-07-09 19:13:10 +03:00
FilterParam luma_param ;
FilterParam chroma_param ;
FilterParam alpha_param ;
int hsub , vsub ;
int radius [ 4 ] ;
int power [ 4 ] ;
uint8_t * temp [ 2 ] ; ///< temporary buffer used in blur_power()
} BoxBlurContext ;
# define Y 0
# define U 1
# define V 2
# define A 3
2013-03-13 10:26:39 +03:00
static av_cold int init ( AVFilterContext * ctx )
2011-07-09 19:13:10 +03:00
{
2013-03-18 22:44:36 +03:00
BoxBlurContext * s = ctx - > priv ;
2011-07-09 19:13:10 +03:00
2013-05-16 11:08:17 +03:00
if ( ! s - > luma_param . radius_expr ) {
2013-02-25 23:21:29 +03:00
av_log ( ctx , AV_LOG_ERROR , " Luma radius expression is not set. \n " ) ;
2011-07-09 19:13:10 +03:00
return AVERROR ( EINVAL ) ;
}
2013-02-20 17:45:02 +03:00
/* fill missing params */
2013-05-16 11:08:17 +03:00
if ( ! s - > chroma_param . radius_expr ) {
s - > chroma_param . radius_expr = av_strdup ( s - > luma_param . radius_expr ) ;
if ( ! s - > chroma_param . radius_expr )
2013-02-20 17:45:02 +03:00
return AVERROR ( ENOMEM ) ;
2011-07-09 19:13:10 +03:00
}
2013-05-16 11:08:17 +03:00
if ( s - > chroma_param . power < 0 )
s - > chroma_param . power = s - > luma_param . power ;
2013-02-20 17:45:02 +03:00
2013-05-16 11:08:17 +03:00
if ( ! s - > alpha_param . radius_expr ) {
s - > alpha_param . radius_expr = av_strdup ( s - > luma_param . radius_expr ) ;
if ( ! s - > alpha_param . radius_expr )
2013-02-20 17:45:02 +03:00
return AVERROR ( ENOMEM ) ;
2011-07-09 19:13:10 +03:00
}
2013-05-16 11:08:17 +03:00
if ( s - > alpha_param . power < 0 )
s - > alpha_param . power = s - > luma_param . power ;
2011-07-09 19:13:10 +03:00
return 0 ;
}
static av_cold void uninit ( AVFilterContext * ctx )
{
2013-03-18 22:44:36 +03:00
BoxBlurContext * s = ctx - > priv ;
2011-07-09 19:13:10 +03:00
2013-03-18 22:44:36 +03:00
av_freep ( & s - > temp [ 0 ] ) ;
av_freep ( & s - > temp [ 1 ] ) ;
2011-07-09 19:13:10 +03:00
}
static int query_formats ( AVFilterContext * ctx )
{
2012-12-22 01:38:59 +03:00
static const enum AVPixelFormat pix_fmts [ ] = {
2012-10-06 13:10:34 +03:00
AV_PIX_FMT_YUV444P , AV_PIX_FMT_YUV422P , AV_PIX_FMT_YUV420P ,
AV_PIX_FMT_YUV411P , AV_PIX_FMT_YUV410P , AV_PIX_FMT_YUVA420P ,
AV_PIX_FMT_YUV440P , AV_PIX_FMT_GRAY8 ,
AV_PIX_FMT_YUVJ444P , AV_PIX_FMT_YUVJ422P , AV_PIX_FMT_YUVJ420P ,
AV_PIX_FMT_YUVJ440P ,
AV_PIX_FMT_NONE
2011-07-09 19:13:10 +03:00
} ;
2012-05-30 11:12:55 +03:00
ff_set_common_formats ( ctx , ff_make_format_list ( pix_fmts ) ) ;
2011-07-09 19:13:10 +03:00
return 0 ;
}
static int config_input ( AVFilterLink * inlink )
{
2012-10-06 14:29:37 +03:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( inlink - > format ) ;
2011-07-09 19:13:10 +03:00
AVFilterContext * ctx = inlink - > dst ;
2013-03-18 22:44:36 +03:00
BoxBlurContext * s = ctx - > priv ;
2011-07-09 19:13:10 +03:00
int w = inlink - > w , h = inlink - > h ;
int cw , ch ;
double var_values [ VARS_NB ] , res ;
char * expr ;
int ret ;
2013-05-16 11:08:17 +03:00
if ( ! ( s - > temp [ 0 ] = av_malloc ( FFMAX ( w , h ) ) ) | |
! ( s - > temp [ 1 ] = av_malloc ( FFMAX ( w , h ) ) ) )
2011-07-09 19:13:10 +03:00
return AVERROR ( ENOMEM ) ;
2013-03-18 22:44:36 +03:00
s - > hsub = desc - > log2_chroma_w ;
s - > vsub = desc - > log2_chroma_h ;
2011-07-09 19:13:10 +03:00
2012-11-28 21:12:42 +03:00
var_values [ VAR_W ] = inlink - > w ;
var_values [ VAR_H ] = inlink - > h ;
2013-03-18 22:44:36 +03:00
var_values [ VAR_CW ] = cw = w > > s - > hsub ;
var_values [ VAR_CH ] = ch = h > > s - > vsub ;
var_values [ VAR_HSUB ] = 1 < < s - > hsub ;
var_values [ VAR_VSUB ] = 1 < < s - > vsub ;
2011-07-09 19:13:10 +03:00
# define EVAL_RADIUS_EXPR(comp) \
2013-05-16 11:08:17 +03:00
expr = s - > comp # # _param . radius_expr ; \
2011-07-09 19:13:10 +03:00
ret = av_expr_parse_and_eval ( & res , expr , var_names , var_values , \
NULL , NULL , NULL , NULL , NULL , 0 , ctx ) ; \
2013-03-18 22:44:36 +03:00
s - > comp # # _param . radius = res ; \
2011-07-09 19:13:10 +03:00
if ( ret < 0 ) { \
av_log ( NULL , AV_LOG_ERROR , \
" Error when evaluating " # comp " radius expression '%s' \n " , expr ) ; \
return ret ; \
}
EVAL_RADIUS_EXPR ( luma ) ;
EVAL_RADIUS_EXPR ( chroma ) ;
EVAL_RADIUS_EXPR ( alpha ) ;
2012-07-14 19:26:04 +03:00
av_log ( ctx , AV_LOG_VERBOSE ,
2011-07-09 19:13:10 +03:00
" luma_radius:%d luma_power:%d "
" chroma_radius:%d chroma_power:%d "
" alpha_radius:%d alpha_power:%d "
" w:%d chroma_w:%d h:%d chroma_h:%d \n " ,
2013-03-18 22:44:36 +03:00
s - > luma_param . radius , s - > luma_param . power ,
s - > chroma_param . radius , s - > chroma_param . power ,
s - > alpha_param . radius , s - > alpha_param . power ,
2011-07-09 19:13:10 +03:00
w , cw , h , ch ) ;
# define CHECK_RADIUS_VAL(w_, h_, comp) \
2013-03-18 22:44:36 +03:00
if ( s - > comp # # _param . radius < 0 | | \
2 * s - > comp # # _param . radius > FFMIN ( w_ , h_ ) ) { \
2011-07-09 19:13:10 +03:00
av_log ( ctx , AV_LOG_ERROR , \
" Invalid " # comp " radius value %d, must be >= 0 and <= %d \n " , \
2013-03-18 22:44:36 +03:00
s - > comp # # _param . radius , FFMIN ( w_ , h_ ) / 2 ) ; \
2011-07-09 19:13:10 +03:00
return AVERROR ( EINVAL ) ; \
}
CHECK_RADIUS_VAL ( w , h , luma ) ;
CHECK_RADIUS_VAL ( cw , ch , chroma ) ;
CHECK_RADIUS_VAL ( w , h , alpha ) ;
2013-03-18 22:44:36 +03:00
s - > radius [ Y ] = s - > luma_param . radius ;
s - > radius [ U ] = s - > radius [ V ] = s - > chroma_param . radius ;
s - > radius [ A ] = s - > alpha_param . radius ;
2011-07-09 19:13:10 +03:00
2013-03-18 22:44:36 +03:00
s - > power [ Y ] = s - > luma_param . power ;
s - > power [ U ] = s - > power [ V ] = s - > chroma_param . power ;
s - > power [ A ] = s - > alpha_param . power ;
2011-07-09 19:13:10 +03:00
return 0 ;
}
static inline void blur ( uint8_t * dst , int dst_step , const uint8_t * src , int src_step ,
2011-08-03 00:45:30 +03:00
int len , int radius )
2011-07-09 19:13:10 +03:00
{
/* Naive boxblur would sum source pixels from x-radius .. x+radius
* for destination pixel x . That would be O ( radius * width ) .
* If you now look at what source pixels represent 2 consecutive
* output pixels , then you see they are almost identical and only
* differ by 2 pixels , like :
* src0 111111111
* dst0 1
* src1 111111111
* dst1 1
* src0 - src1 1 - 1
* so when you know one output pixel you can find the next by just adding
* and subtracting 1 input pixel .
* The following code adopts this faster variant .
*/
const int length = radius * 2 + 1 ;
const int inv = ( ( 1 < < 16 ) + length / 2 ) / length ;
2012-11-28 21:12:42 +03:00
int x , sum = 0 ;
2011-07-09 19:13:10 +03:00
for ( x = 0 ; x < radius ; x + + )
sum + = src [ x * src_step ] < < 1 ;
sum + = src [ radius * src_step ] ;
for ( x = 0 ; x < = radius ; x + + ) {
sum + = src [ ( radius + x ) * src_step ] - src [ ( radius - x ) * src_step ] ;
dst [ x * dst_step ] = ( sum * inv + ( 1 < < 15 ) ) > > 16 ;
}
2011-08-03 00:45:30 +03:00
for ( ; x < len - radius ; x + + ) {
2011-07-09 19:13:10 +03:00
sum + = src [ ( radius + x ) * src_step ] - src [ ( x - radius - 1 ) * src_step ] ;
dst [ x * dst_step ] = ( sum * inv + ( 1 < < 15 ) ) > > 16 ;
}
2011-08-03 00:45:30 +03:00
for ( ; x < len ; x + + ) {
sum + = src [ ( 2 * len - radius - x - 1 ) * src_step ] - src [ ( x - radius - 1 ) * src_step ] ;
2011-07-09 19:13:10 +03:00
dst [ x * dst_step ] = ( sum * inv + ( 1 < < 15 ) ) > > 16 ;
}
}
static inline void blur_power ( uint8_t * dst , int dst_step , const uint8_t * src , int src_step ,
2011-08-03 00:45:30 +03:00
int len , int radius , int power , uint8_t * temp [ 2 ] )
2011-07-09 19:13:10 +03:00
{
uint8_t * a = temp [ 0 ] , * b = temp [ 1 ] ;
if ( radius & & power ) {
2011-08-03 00:45:30 +03:00
blur ( a , 1 , src , src_step , len , radius ) ;
2011-07-09 19:13:10 +03:00
for ( ; power > 2 ; power - - ) {
uint8_t * c ;
2011-08-03 00:45:30 +03:00
blur ( b , 1 , a , 1 , len , radius ) ;
2011-07-09 19:13:10 +03:00
c = a ; a = b ; b = c ;
}
if ( power > 1 ) {
2011-08-03 00:45:30 +03:00
blur ( dst , dst_step , a , 1 , len , radius ) ;
2011-07-09 19:13:10 +03:00
} else {
int i ;
2011-08-03 00:45:30 +03:00
for ( i = 0 ; i < len ; i + + )
2011-07-09 19:13:10 +03:00
dst [ i * dst_step ] = a [ i ] ;
}
} else {
int i ;
2011-08-03 00:45:30 +03:00
for ( i = 0 ; i < len ; i + + )
2011-07-09 19:13:10 +03:00
dst [ i * dst_step ] = src [ i * src_step ] ;
}
}
static void hblur ( uint8_t * dst , int dst_linesize , const uint8_t * src , int src_linesize ,
int w , int h , int radius , int power , uint8_t * temp [ 2 ] )
{
int y ;
if ( radius = = 0 & & dst = = src )
return ;
for ( y = 0 ; y < h ; y + + )
blur_power ( dst + y * dst_linesize , 1 , src + y * src_linesize , 1 ,
w , radius , power , temp ) ;
}
static void vblur ( uint8_t * dst , int dst_linesize , const uint8_t * src , int src_linesize ,
int w , int h , int radius , int power , uint8_t * temp [ 2 ] )
{
int x ;
if ( radius = = 0 & & dst = = src )
return ;
for ( x = 0 ; x < w ; x + + )
blur_power ( dst + x , dst_linesize , src + x , src_linesize ,
h , radius , power , temp ) ;
}
2012-11-28 10:41:07 +03:00
static int filter_frame ( AVFilterLink * inlink , AVFrame * in )
2011-07-09 19:13:10 +03:00
{
AVFilterContext * ctx = inlink - > dst ;
2013-03-18 22:44:36 +03:00
BoxBlurContext * s = ctx - > priv ;
2011-07-09 19:13:10 +03:00
AVFilterLink * outlink = inlink - > dst - > outputs [ 0 ] ;
2012-11-28 10:41:07 +03:00
AVFrame * out ;
2011-07-09 19:13:10 +03:00
int plane ;
2013-05-16 11:08:17 +03:00
int cw = FF_CEIL_RSHIFT ( inlink - > w , s - > hsub ) , ch = FF_CEIL_RSHIFT ( in - > height , s - > vsub ) ;
2011-07-09 19:13:10 +03:00
int w [ 4 ] = { inlink - > w , cw , cw , inlink - > w } ;
2012-11-28 10:41:07 +03:00
int h [ 4 ] = { in - > height , ch , ch , in - > height } ;
2011-07-09 19:13:10 +03:00
2012-11-28 10:41:07 +03:00
out = ff_get_video_buffer ( outlink , outlink - > w , outlink - > h ) ;
2012-11-28 19:33:07 +03:00
if ( ! out ) {
2012-11-28 10:41:07 +03:00
av_frame_free ( & in ) ;
2012-11-28 19:33:07 +03:00
return AVERROR ( ENOMEM ) ;
}
2012-11-28 10:41:07 +03:00
av_frame_copy_props ( out , in ) ;
2012-11-28 19:33:07 +03:00
2013-08-03 19:54:43 +03:00
for ( plane = 0 ; plane < 4 & & in - > data [ plane ] & & in - > linesize [ plane ] ; plane + + )
2012-11-28 19:33:07 +03:00
hblur ( out - > data [ plane ] , out - > linesize [ plane ] ,
in - > data [ plane ] , in - > linesize [ plane ] ,
2013-03-18 22:44:36 +03:00
w [ plane ] , h [ plane ] , s - > radius [ plane ] , s - > power [ plane ] ,
s - > temp ) ;
2011-07-09 19:13:10 +03:00
2013-08-03 19:54:43 +03:00
for ( plane = 0 ; plane < 4 & & in - > data [ plane ] & & in - > linesize [ plane ] ; plane + + )
2012-11-28 19:33:07 +03:00
vblur ( out - > data [ plane ] , out - > linesize [ plane ] ,
out - > data [ plane ] , out - > linesize [ plane ] ,
2013-03-18 22:44:36 +03:00
w [ plane ] , h [ plane ] , s - > radius [ plane ] , s - > power [ plane ] ,
s - > temp ) ;
2011-08-03 00:58:19 +03:00
2012-11-28 10:41:07 +03:00
av_frame_free ( & in ) ;
2012-11-28 19:33:07 +03:00
return ff_filter_frame ( outlink , out ) ;
2011-07-09 19:13:10 +03:00
}
2013-02-25 23:21:29 +03:00
# define OFFSET(x) offsetof(BoxBlurContext, x)
2013-04-10 17:28:23 +03:00
# define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption boxblur_options [ ] = {
{ " luma_radius " , " Radius of the luma blurring box " , OFFSET ( luma_param . radius_expr ) , AV_OPT_TYPE_STRING , { . str = " 2 " } , . flags = FLAGS } ,
{ " lr " , " Radius of the luma blurring box " , OFFSET ( luma_param . radius_expr ) , AV_OPT_TYPE_STRING , { . str = " 2 " } , . flags = FLAGS } ,
{ " 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 } ,
{ " 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 } ,
{ " chroma_radius " , " Radius of the chroma blurring box " , OFFSET ( chroma_param . radius_expr ) , AV_OPT_TYPE_STRING , { . str = NULL } , . flags = FLAGS } ,
{ " cr " , " Radius of the chroma blurring box " , OFFSET ( chroma_param . radius_expr ) , AV_OPT_TYPE_STRING , { . str = NULL } , . flags = FLAGS } ,
{ " 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 } ,
{ " 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 } ,
2013-02-25 23:21:29 +03:00
2013-04-10 17:28:23 +03:00
{ " alpha_radius " , " Radius of the alpha blurring box " , OFFSET ( alpha_param . radius_expr ) , AV_OPT_TYPE_STRING , { . str = NULL } , . flags = FLAGS } ,
{ " ar " , " Radius of the alpha blurring box " , OFFSET ( alpha_param . radius_expr ) , AV_OPT_TYPE_STRING , { . str = NULL } , . flags = FLAGS } ,
{ " 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 } ,
{ " 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 } ,
{ NULL }
2013-02-25 23:21:29 +03:00
} ;
2013-04-10 17:28:23 +03:00
AVFILTER_DEFINE_CLASS ( boxblur ) ;
2012-07-24 16:14:01 +03:00
static const AVFilterPad avfilter_vf_boxblur_inputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
. config_props = config_input ,
2012-11-28 19:33:07 +03:00
. filter_frame = filter_frame ,
2012-07-24 16:14:01 +03:00
} ,
{ NULL }
} ;
static const AVFilterPad avfilter_vf_boxblur_outputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
} ,
{ NULL }
} ;
2013-10-28 09:44:24 +03:00
AVFilter ff_vf_boxblur = {
2011-07-09 19:13:10 +03:00
. name = " boxblur " ,
. description = NULL_IF_CONFIG_SMALL ( " Blur the input. " ) ,
. priv_size = sizeof ( BoxBlurContext ) ,
2013-02-25 23:21:29 +03:00
. priv_class = & boxblur_class ,
2011-07-09 19:13:10 +03:00
. init = init ,
. uninit = uninit ,
. query_formats = query_formats ,
2013-09-07 15:13:50 +03:00
. inputs = avfilter_vf_boxblur_inputs ,
. outputs = avfilter_vf_boxblur_outputs ,
. flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC ,
2011-10-23 19:57:07 +03:00
} ;