2018-06-13 23:37:12 +02:00
/*
* Copyright ( c ) 2018 Sergey Lavrushkin
*
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser 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
* Filter implementing image super - resolution using deep convolutional networks .
* https : //arxiv.org/abs/1501.00092
* https : //arxiv.org/abs/1609.05158
*/
# include "avfilter.h"
# include "formats.h"
# include "internal.h"
# include "libavutil/opt.h"
2019-06-01 05:20:33 +02:00
# include "libavutil/pixdesc.h"
2018-06-13 23:37:12 +02:00
# include "libavformat/avio.h"
# include "libswscale/swscale.h"
2021-01-26 07:35:30 +02:00
# include "dnn_filter_common.h"
2018-06-13 23:37:12 +02:00
typedef struct SRContext {
const AVClass * class ;
2021-01-26 07:35:30 +02:00
DnnContext dnnctx ;
2018-06-13 23:37:12 +02:00
int scale_factor ;
2020-08-28 06:51:44 +02:00
struct SwsContext * sws_uv_scale ;
int sws_uv_height ;
struct SwsContext * sws_pre_scale ;
2018-06-13 23:37:12 +02:00
} SRContext ;
# define OFFSET(x) offsetof(SRContext, x)
# define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
static const AVOption sr_options [ ] = {
2021-01-26 07:35:30 +02:00
{ " dnn_backend " , " DNN backend used for model execution " , OFFSET ( dnnctx . backend_type ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , 1 , FLAGS , " backend " } ,
2018-06-13 23:37:12 +02:00
{ " native " , " native backend flag " , 0 , AV_OPT_TYPE_CONST , { . i64 = 0 } , 0 , 0 , FLAGS , " backend " } ,
# if (CONFIG_LIBTENSORFLOW == 1)
{ " tensorflow " , " tensorflow backend flag " , 0 , AV_OPT_TYPE_CONST , { . i64 = 1 } , 0 , 0 , FLAGS , " backend " } ,
# endif
2018-09-06 13:33:06 +02:00
{ " scale_factor " , " scale factor for SRCNN model " , OFFSET ( scale_factor ) , AV_OPT_TYPE_INT , { . i64 = 2 } , 2 , 4 , FLAGS } ,
2021-01-26 07:35:30 +02:00
{ " model " , " path to model file specifying network architecture and its parameters " , OFFSET ( dnnctx . model_filename ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 0 , FLAGS } ,
{ " input " , " input name of the model " , OFFSET ( dnnctx . model_inputname ) , AV_OPT_TYPE_STRING , { . str = " x " } , 0 , 0 , FLAGS } ,
{ " output " , " output name of the model " , OFFSET ( dnnctx . model_outputname ) , AV_OPT_TYPE_STRING , { . str = " y " } , 0 , 0 , FLAGS } ,
2018-06-13 23:37:12 +02:00
{ NULL }
} ;
AVFILTER_DEFINE_CLASS ( sr ) ;
2018-07-27 18:34:02 +02:00
static av_cold int init ( AVFilterContext * context )
2018-06-13 23:37:12 +02:00
{
2018-07-27 18:34:02 +02:00
SRContext * sr_context = context - > priv ;
2021-02-07 08:35:22 +02:00
return ff_dnn_init ( & sr_context - > dnnctx , DFT_PROCESS_FRAME , context ) ;
2018-06-13 23:37:12 +02:00
}
2018-07-27 18:34:02 +02:00
static int query_formats ( AVFilterContext * context )
2018-06-13 23:37:12 +02:00
{
const enum AVPixelFormat pixel_formats [ ] = { AV_PIX_FMT_YUV420P , AV_PIX_FMT_YUV422P , AV_PIX_FMT_YUV444P ,
AV_PIX_FMT_YUV410P , AV_PIX_FMT_YUV411P , AV_PIX_FMT_GRAY8 ,
AV_PIX_FMT_NONE } ;
2018-07-27 18:34:02 +02:00
AVFilterFormats * formats_list ;
2018-06-13 23:37:12 +02:00
formats_list = ff_make_format_list ( pixel_formats ) ;
if ( ! formats_list ) {
av_log ( context , AV_LOG_ERROR , " could not create formats list \n " ) ;
return AVERROR ( ENOMEM ) ;
}
2018-07-31 17:58:28 +02:00
2018-06-13 23:37:12 +02:00
return ff_set_common_formats ( context , formats_list ) ;
}
2020-08-28 06:51:44 +02:00
static int config_output ( AVFilterLink * outlink )
2018-06-13 23:37:12 +02:00
{
2020-08-28 06:51:44 +02:00
AVFilterContext * context = outlink - > src ;
SRContext * ctx = context - > priv ;
2018-06-13 23:37:12 +02:00
DNNReturnType result ;
2020-08-28 06:51:44 +02:00
AVFilterLink * inlink = context - > inputs [ 0 ] ;
2020-09-11 16:15:04 +02:00
int out_width , out_height ;
2018-06-13 23:37:12 +02:00
2020-08-28 06:51:44 +02:00
// have a try run in case that the dnn model resize the frame
2021-01-26 07:35:30 +02:00
result = ff_dnn_get_output ( & ctx - > dnnctx , inlink - > w , inlink - > h , & out_width , & out_height ) ;
2020-09-11 16:15:04 +02:00
if ( result ! = DNN_SUCCESS ) {
av_log ( ctx , AV_LOG_ERROR , " could not get output from the model \n " ) ;
2019-04-25 04:14:17 +02:00
return AVERROR ( EIO ) ;
}
2020-09-11 16:15:04 +02:00
if ( inlink - > w ! = out_width | | inlink - > h ! = out_height ) {
2020-08-28 06:51:44 +02:00
//espcn
2020-09-11 16:15:04 +02:00
outlink - > w = out_width ;
outlink - > h = out_height ;
2019-04-25 04:14:01 +02:00
if ( inlink - > format ! = AV_PIX_FMT_GRAY8 ) {
2020-02-21 13:29:12 +02:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( inlink - > format ) ;
2020-08-28 06:51:44 +02:00
int sws_src_h = AV_CEIL_RSHIFT ( inlink - > h , desc - > log2_chroma_h ) ;
int sws_src_w = AV_CEIL_RSHIFT ( inlink - > w , desc - > log2_chroma_w ) ;
int sws_dst_h = AV_CEIL_RSHIFT ( outlink - > h , desc - > log2_chroma_h ) ;
int sws_dst_w = AV_CEIL_RSHIFT ( outlink - > w , desc - > log2_chroma_w ) ;
ctx - > sws_uv_scale = sws_getContext ( sws_src_w , sws_src_h , AV_PIX_FMT_GRAY8 ,
sws_dst_w , sws_dst_h , AV_PIX_FMT_GRAY8 ,
SWS_BICUBIC , NULL , NULL , NULL ) ;
ctx - > sws_uv_height = sws_src_h ;
2018-06-13 23:37:12 +02:00
}
2020-08-28 06:51:44 +02:00
} else {
//srcnn
2020-09-11 16:15:04 +02:00
outlink - > w = out_width * ctx - > scale_factor ;
outlink - > h = out_height * ctx - > scale_factor ;
2020-08-28 06:51:44 +02:00
ctx - > sws_pre_scale = sws_getContext ( inlink - > w , inlink - > h , inlink - > format ,
outlink - > w , outlink - > h , outlink - > format ,
SWS_BICUBIC , NULL , NULL , NULL ) ;
2018-06-13 23:37:12 +02:00
}
2019-04-25 04:14:01 +02:00
return 0 ;
2018-06-13 23:37:12 +02:00
}
2018-07-27 18:34:02 +02:00
static int filter_frame ( AVFilterLink * inlink , AVFrame * in )
2018-06-13 23:37:12 +02:00
{
2018-07-27 18:34:02 +02:00
AVFilterContext * context = inlink - > dst ;
2020-08-28 06:51:44 +02:00
SRContext * ctx = context - > priv ;
2018-07-27 18:34:02 +02:00
AVFilterLink * outlink = context - > outputs [ 0 ] ;
AVFrame * out = ff_get_video_buffer ( outlink , outlink - > w , outlink - > h ) ;
2018-06-13 23:37:12 +02:00
DNNReturnType dnn_result ;
if ( ! out ) {
av_log ( context , AV_LOG_ERROR , " could not allocate memory for output frame \n " ) ;
av_frame_free ( & in ) ;
return AVERROR ( ENOMEM ) ;
}
av_frame_copy_props ( out , in ) ;
2018-07-31 17:58:28 +02:00
2020-08-28 06:51:44 +02:00
if ( ctx - > sws_pre_scale ) {
sws_scale ( ctx - > sws_pre_scale ,
( const uint8_t * * ) in - > data , in - > linesize , 0 , in - > height ,
out - > data , out - > linesize ) ;
2021-01-26 07:35:30 +02:00
dnn_result = ff_dnn_execute_model ( & ctx - > dnnctx , out , out ) ;
2019-04-25 04:14:01 +02:00
} else {
2021-01-26 07:35:30 +02:00
dnn_result = ff_dnn_execute_model ( & ctx - > dnnctx , in , out ) ;
2018-07-31 17:58:28 +02:00
}
2018-06-13 23:37:12 +02:00
if ( dnn_result ! = DNN_SUCCESS ) {
2020-08-28 06:51:44 +02:00
av_log ( ctx , AV_LOG_ERROR , " failed to execute loaded model \n " ) ;
av_frame_free ( & in ) ;
av_frame_free ( & out ) ;
2018-06-13 23:37:12 +02:00
return AVERROR ( EIO ) ;
}
2020-08-28 06:51:44 +02:00
if ( ctx - > sws_uv_scale ) {
sws_scale ( ctx - > sws_uv_scale , ( const uint8_t * * ) ( in - > data + 1 ) , in - > linesize + 1 ,
0 , ctx - > sws_uv_height , out - > data + 1 , out - > linesize + 1 ) ;
sws_scale ( ctx - > sws_uv_scale , ( const uint8_t * * ) ( in - > data + 2 ) , in - > linesize + 2 ,
0 , ctx - > sws_uv_height , out - > data + 2 , out - > linesize + 2 ) ;
}
2018-06-13 23:37:12 +02:00
2020-08-28 06:51:44 +02:00
av_frame_free ( & in ) ;
2018-06-13 23:37:12 +02:00
return ff_filter_frame ( outlink , out ) ;
}
2018-07-27 18:34:02 +02:00
static av_cold void uninit ( AVFilterContext * context )
2018-06-13 23:37:12 +02:00
{
2018-07-27 18:34:02 +02:00
SRContext * sr_context = context - > priv ;
2018-06-13 23:37:12 +02:00
2021-01-26 07:35:30 +02:00
ff_dnn_uninit ( & sr_context - > dnnctx ) ;
2020-08-28 06:51:44 +02:00
sws_freeContext ( sr_context - > sws_uv_scale ) ;
sws_freeContext ( sr_context - > sws_pre_scale ) ;
2018-06-13 23:37:12 +02:00
}
static const AVFilterPad sr_inputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
. filter_frame = filter_frame ,
} ,
{ NULL }
} ;
static const AVFilterPad sr_outputs [ ] = {
{
. name = " default " ,
2020-08-28 06:51:44 +02:00
. config_props = config_output ,
2018-06-13 23:37:12 +02:00
. type = AVMEDIA_TYPE_VIDEO ,
} ,
{ NULL }
} ;
2021-04-19 18:33:56 +02:00
const AVFilter ff_vf_sr = {
2018-06-13 23:37:12 +02:00
. name = " sr " ,
. description = NULL_IF_CONFIG_SMALL ( " Apply DNN-based image super resolution to the input. " ) ,
. priv_size = sizeof ( SRContext ) ,
. init = init ,
. uninit = uninit ,
. query_formats = query_formats ,
. inputs = sr_inputs ,
. outputs = sr_outputs ,
. priv_class = & sr_class ,
} ;