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"
2013-02-20 17:45:02 +03:00
# include "libavutil/opt.h"
2011-07-09 19:13:10 +03:00
# 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"
2018-06-20 03:33:02 +02:00
# include "boxblur.h"
2011-07-09 19:13:10 +03:00
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 ;
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 )
{
2014-12-21 18:38:17 +02:00
AVFilterFormats * formats = NULL ;
2015-10-05 05:39:25 +02:00
int fmt , ret ;
2014-12-21 18:38:17 +02:00
for ( fmt = 0 ; av_pix_fmt_desc_get ( fmt ) ; fmt + + ) {
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( fmt ) ;
if ( ! ( desc - > flags & ( AV_PIX_FMT_FLAG_HWACCEL | AV_PIX_FMT_FLAG_BITSTREAM | AV_PIX_FMT_FLAG_PAL ) ) & &
( desc - > flags & AV_PIX_FMT_FLAG_PLANAR | | desc - > nb_components = = 1 ) & &
2015-10-05 05:39:25 +02:00
( ! ( desc - > flags & AV_PIX_FMT_FLAG_BE ) = = ! HAVE_BIGENDIAN | | desc - > comp [ 0 ] . depth = = 8 ) & &
( ret = ff_add_format ( & formats , fmt ) ) < 0 )
return ret ;
2014-12-21 18:38:17 +02:00
}
2015-04-03 19:55:18 +02:00
return ff_set_common_formats ( ctx , formats ) ;
2011-07-09 19:13:10 +03:00
}
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 ret ;
2014-12-21 15:59:05 +02:00
if ( ! ( s - > temp [ 0 ] = av_malloc ( 2 * FFMAX ( w , h ) ) ) | |
! ( s - > temp [ 1 ] = av_malloc ( 2 * 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
2018-06-20 03:33:02 +02:00
ret = ff_boxblur_eval_filter_params ( inlink ,
& s - > luma_param ,
& s - > chroma_param ,
& s - > alpha_param ) ;
if ( ret ! = 0 ) {
av_log ( ctx , AV_LOG_ERROR , " Failed to evaluate "
" filter params: %d. \n " , ret ) ;
return ret ;
2011-07-09 19:13:10 +03:00
}
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 ;
}
2015-11-01 20:20:58 +02: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 .
*/
# define BLUR(type, depth) \
static inline void blur # # depth ( type * dst , int dst_step , const type * src , \
int src_step , int len , int radius ) \
{ \
const int length = radius * 2 + 1 ; \
const int inv = ( ( 1 < < 16 ) + length / 2 ) / length ; \
int x , sum = src [ radius * src_step ] ; \
\
for ( x = 0 ; x < radius ; x + + ) \
sum + = src [ x * src_step ] < < 1 ; \
\
sum = sum * inv + ( 1 < < 15 ) ; \
\
for ( x = 0 ; x < = radius ; x + + ) { \
sum + = ( src [ ( radius + x ) * src_step ] - src [ ( radius - x ) * src_step ] ) * inv ; \
dst [ x * dst_step ] = sum > > 16 ; \
} \
\
for ( ; x < len - radius ; x + + ) { \
sum + = ( src [ ( radius + x ) * src_step ] - src [ ( x - radius - 1 ) * src_step ] ) * inv ; \
dst [ x * dst_step ] = sum > > 16 ; \
} \
\
for ( ; x < len ; x + + ) { \
sum + = ( src [ ( 2 * len - radius - x - 1 ) * src_step ] - src [ ( x - radius - 1 ) * src_step ] ) * inv ; \
dst [ x * dst_step ] = sum > > 16 ; \
} \
2011-07-09 19:13:10 +03:00
}
2015-11-01 20:20:58 +02:00
BLUR ( uint8_t , 8 )
BLUR ( uint16_t , 16 )
2014-12-21 15:59:05 +02:00
2015-11-01 20:20:58 +02:00
# undef BLUR
2014-12-21 15:59:05 +02:00
static inline void blur ( uint8_t * dst , int dst_step , const uint8_t * src , int src_step ,
int len , int radius , int pixsize )
{
if ( pixsize = = 1 ) blur8 ( dst , dst_step , src , src_step , len , radius ) ;
else blur16 ( ( uint16_t * ) dst , dst_step > > 1 , ( const uint16_t * ) src , src_step > > 1 , len , radius ) ;
}
2011-07-09 19:13:10 +03:00
static inline void blur_power ( uint8_t * dst , int dst_step , const uint8_t * src , int src_step ,
2014-12-21 15:59:05 +02:00
int len , int radius , int power , uint8_t * temp [ 2 ] , int pixsize )
2011-07-09 19:13:10 +03:00
{
uint8_t * a = temp [ 0 ] , * b = temp [ 1 ] ;
if ( radius & & power ) {
2014-12-21 15:59:05 +02:00
blur ( a , pixsize , src , src_step , len , radius , pixsize ) ;
2011-07-09 19:13:10 +03:00
for ( ; power > 2 ; power - - ) {
uint8_t * c ;
2014-12-21 15:59:05 +02:00
blur ( b , pixsize , a , pixsize , len , radius , pixsize ) ;
2011-07-09 19:13:10 +03:00
c = a ; a = b ; b = c ;
}
if ( power > 1 ) {
2014-12-21 15:59:05 +02:00
blur ( dst , dst_step , a , pixsize , len , radius , pixsize ) ;
2011-07-09 19:13:10 +03:00
} else {
int i ;
2014-12-21 15:59:05 +02:00
if ( pixsize = = 1 ) {
for ( i = 0 ; i < len ; i + + )
dst [ i * dst_step ] = a [ i ] ;
} else
for ( i = 0 ; i < len ; i + + )
* ( uint16_t * ) ( dst + i * dst_step ) = ( ( uint16_t * ) a ) [ i ] ;
2011-07-09 19:13:10 +03:00
}
} else {
int i ;
2014-12-21 15:59:05 +02:00
if ( pixsize = = 1 ) {
for ( i = 0 ; i < len ; i + + )
dst [ i * dst_step ] = src [ i * src_step ] ;
} else
for ( i = 0 ; i < len ; i + + )
* ( uint16_t * ) ( dst + i * dst_step ) = * ( uint16_t * ) ( src + i * src_step ) ;
2011-07-09 19:13:10 +03:00
}
}
static void hblur ( uint8_t * dst , int dst_linesize , const uint8_t * src , int src_linesize ,
2014-12-21 15:59:05 +02:00
int w , int h , int radius , int power , uint8_t * temp [ 2 ] , int pixsize )
2011-07-09 19:13:10 +03:00
{
int y ;
if ( radius = = 0 & & dst = = src )
return ;
for ( y = 0 ; y < h ; y + + )
2014-12-21 15:59:05 +02:00
blur_power ( dst + y * dst_linesize , pixsize , src + y * src_linesize , pixsize ,
w , radius , power , temp , pixsize ) ;
2011-07-09 19:13:10 +03:00
}
static void vblur ( uint8_t * dst , int dst_linesize , const uint8_t * src , int src_linesize ,
2014-12-21 15:59:05 +02:00
int w , int h , int radius , int power , uint8_t * temp [ 2 ] , int pixsize )
2011-07-09 19:13:10 +03:00
{
int x ;
if ( radius = = 0 & & dst = = src )
return ;
for ( x = 0 ; x < w ; x + + )
2014-12-21 15:59:05 +02:00
blur_power ( dst + x * pixsize , dst_linesize , src + x * pixsize , src_linesize ,
h , radius , power , temp , pixsize ) ;
2011-07-09 19:13:10 +03:00
}
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 ;
2016-01-27 17:19:38 +02:00
int cw = AV_CEIL_RSHIFT ( inlink - > w , s - > hsub ) , ch = AV_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 } ;
2014-12-21 15:59:05 +02:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( inlink - > format ) ;
2015-09-08 17:10:48 +02:00
const int depth = desc - > comp [ 0 ] . depth ;
2014-12-21 15:59:05 +02:00
const int pixsize = ( depth + 7 ) / 8 ;
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 ] ,
2014-12-21 15:59:05 +02:00
s - > temp , pixsize ) ;
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 ] ,
2014-12-21 15:59:05 +02:00
s - > temp , pixsize ) ;
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
} ,
} ;
static const AVFilterPad avfilter_vf_boxblur_outputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
} ,
} ;
2021-04-19 18:33:56 +02:00
const 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
. uninit = uninit ,
2021-08-12 13:05:31 +02:00
FILTER_INPUTS ( avfilter_vf_boxblur_inputs ) ,
FILTER_OUTPUTS ( avfilter_vf_boxblur_outputs ) ,
2021-09-27 12:07:35 +02:00
FILTER_QUERY_FUNC ( query_formats ) ,
2013-09-07 15:13:50 +03:00
. flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC ,
2011-10-23 19:57:07 +03:00
} ;