Message ID | tencent_EC500CC7B59B77E032B12D882A6F571CDB06@qq.com |
---|---|
State | Accepted |
Headers | show |
Series | [FFmpeg-devel] avfilter/vf_guided: support single input | expand |
Context | Check | Description |
---|---|---|
andriy/x86_make | success | Make finished |
andriy/x86_make_fate | success | Make fate finished |
andriy/PPC64_make | success | Make finished |
andriy/PPC64_make_fate | success | Make fate finished |
> 在 2021年6月14日,10:08,Xuewei Meng <928826483@qq.com> 写道: > > From: Xuewei Meng <xwmeng96@gmail.com> > > Support single input for guided filter by adding guidance mode. > If the guidance mode is off, single input is required. And > edge-preserving smoothing is conducted. If the mode is on, two > inputs are needed. The second input serves as the guidance. For > this mode, more tasks are supported, such as detail enhancement, > dehazing and so on. > > Signed-off-by: Xuewei Meng <xwmeng96@gmail.com> > --- > doc/filters.texi | 12 ++-- > libavfilter/vf_guided.c | 168 ++++++++++++++++++++++++++++++++---------------- > 2 files changed, 119 insertions(+), 61 deletions(-) > > diff --git a/doc/filters.texi b/doc/filters.texi > index 78faf76..5c362c0 100644 > --- a/doc/filters.texi > +++ b/doc/filters.texi > @@ -12975,8 +12975,6 @@ greyedge=difford=1:minknorm=0:sigma=2 > > @section guided > Apply guided filter for edge-preserving smoothing, dehazing and so on. > -This filter requires two inputs of same resolution and pixel format. > -The second input serves as the reference. > > The filter accepts the following options: > @table @option > @@ -12997,6 +12995,12 @@ Set subsampling ratio for @code{fast} mode. > Range is 2 to 64. Default is 4. > No subsampling occurs in @code{basic} mode. > > +@item guidance > +Set guidance mode. Can be @code{off} or @code{on}. Default is @code{off}. > +If @code{off}, single input is required. > +If @code{on}, two inputs of the same resolution and pixel format are required. > +The second input serves as the guidance. > + > @item planes > Set planes to filter. Default is first only. > @end table > @@ -13009,7 +13013,7 @@ This filter supports the all above options as @ref{commands}. > @item > Edge-preserving smoothing with guided filter: > @example > -ffmpeg -i in.png -i in.png -filter_complex guided out.png > +ffmpeg -i in.png -vf guided out.png > @end example > > @item > @@ -13017,7 +13021,7 @@ Dehazing, structure-transferring filtering, detail enhancement with guided filte > For the generation of guidance image, refer to paper "Guided Image Filtering". > See: @url{http://kaiminghe.com/publications/pami12guidedfilter.pdf}. > @example > -ffmpeg -i in.png -i guidance.png -filter_complex guided out.png > +ffmpeg -i in.png -i guidance.png -filter_complex guided=guidance=on out.png > @end example > > @end itemize > diff --git a/libavfilter/vf_guided.c b/libavfilter/vf_guided.c > index ea537e4..739d615 100644 > --- a/libavfilter/vf_guided.c > +++ b/libavfilter/vf_guided.c > @@ -22,6 +22,7 @@ > #include "libavutil/opt.h" > #include "libavutil/pixdesc.h" > #include "avfilter.h" > +#include "filters.h" > #include "formats.h" > #include "framesync.h" > #include "internal.h" > @@ -33,6 +34,12 @@ enum FilterModes { > NB_MODES, > }; > > +enum GuidanceModes { > + OFF, > + ON, > + NB_GUIDANCE_MODES, > +}; > + > typedef struct GuidedContext { > const AVClass *class; > FFFrameSync fs; > @@ -41,7 +48,7 @@ typedef struct GuidedContext { > float eps; > int mode; > int sub; > - > + int guidance; > int planes; > > int width; > @@ -59,13 +66,16 @@ typedef struct GuidedContext { > #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM > > static const AVOption guided_options[] = { > - { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS }, > - { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS }, > - { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, FLAGS, "mode" }, > - { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" }, > - { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, FLAGS, "mode" }, > - { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, FLAGS }, > - { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1 }, 0, 0xF, FLAGS }, > + { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS }, > + { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS }, > + { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, FLAGS, "mode" }, > + { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" }, > + { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, FLAGS, "mode" }, > + { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, FLAGS }, > + { "guidance", "set guidance mode (0: off mode; 1: on mode)", OFFSET(guidance), AV_OPT_TYPE_INT, {.i64 = OFF }, OFF, NB_GUIDANCE_MODES - 1, FLAGS, "guidance" }, > + { "off", "only one input is enabled", 0, AV_OPT_TYPE_CONST, {.i64 = OFF }, 0, 0, FLAGS, "guidance" }, > + { "on", "two inputs are required", 0, AV_OPT_TYPE_CONST, {.i64 = ON }, 0, 0, FLAGS, "guidance" }, > + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 0xF, FLAGS }, > { NULL } > }; > > @@ -149,16 +159,6 @@ static int config_input(AVFilterLink *inlink) > GuidedContext *s = ctx->priv; > const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); > > - if (ctx->inputs[0]->w != ctx->inputs[1]->w || > - ctx->inputs[0]->h != ctx->inputs[1]->h) { > - av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n"); > - return AVERROR(EINVAL); > - } > - if (ctx->inputs[0]->format != ctx->inputs[1]->format) { > - av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n"); > - return AVERROR(EINVAL); > - } > - > if (s->mode == BASIC) { > s->sub = 1; > } > @@ -230,7 +230,7 @@ static int guided_##name(AVFilterContext *ctx, GuidedContext *s, > meanB = av_calloc(w * h, sizeof(float)); \ > \ > if (!I || !II || !P || !IP || !meanI || !meanII || !meanP || \ > - !meanIP || !A || !B || !meanA || !meanB){ \ > + !meanIP || !A || !B || !meanA || !meanB) { \ > ret = AVERROR(ENOMEM); \ > goto end; \ > } \ > @@ -304,47 +304,54 @@ end: > GUIDED(uint8_t, byte) > GUIDED(uint16_t, word) > > -static int process_frame(FFFrameSync *fs) > +static int filter_frame(AVFilterContext *ctx, AVFrame **out, AVFrame *in, AVFrame *ref) > { > - AVFilterContext *ctx = fs->parent; > - GuidedContext *s = fs->opaque; > + GuidedContext *s = ctx->priv; > AVFilterLink *outlink = ctx->outputs[0]; > - AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL; > - int ret; > - > - ret = ff_framesync_dualinput_get(fs, &main_frame, &ref_frame); > - if (ret < 0) > - return ret; > - > - out_frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); > - if (!out_frame) { > - av_frame_free(&main_frame); > + *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); > + if (!*out) > return AVERROR(ENOMEM); > - } > - av_frame_copy_props(out_frame, main_frame); > + av_frame_copy_props(*out, in); > > for (int plane = 0; plane < s->nb_planes; plane++) { > if (!(s->planes & (1 << plane))) { > - av_image_copy_plane(out_frame->data[plane], out_frame->linesize[plane], > - main_frame->data[plane], main_frame->linesize[plane], > + av_image_copy_plane((*out)->data[plane], (*out)->linesize[plane], > + in->data[plane], in->linesize[plane], > s->planewidth[plane] * ((s->depth + 7) / 8), s->planeheight[plane]); > continue; > } > if (s->depth <= 8) > - guided_byte(ctx, s, main_frame->data[plane], ref_frame->data[plane], out_frame->data[plane], s->radius, s->eps, > + guided_byte(ctx, s, in->data[plane], ref->data[plane], (*out)->data[plane], s->radius, s->eps, > s->planewidth[plane], s->planeheight[plane], > - main_frame->linesize[plane], ref_frame->linesize[plane], out_frame->linesize[plane], (1 << s->depth) - 1.f); > + in->linesize[plane], ref->linesize[plane], (*out)->linesize[plane], (1 << s->depth) - 1.f); > else > - guided_word(ctx, s, main_frame->data[plane], ref_frame->data[plane], out_frame->data[plane], s->radius, s->eps, > + guided_word(ctx, s, in->data[plane], ref->data[plane], (*out)->data[plane], s->radius, s->eps, > s->planewidth[plane], s->planeheight[plane], > - main_frame->linesize[plane] / 2, ref_frame->linesize[plane] / 2, out_frame->linesize[plane] / 2, (1 << s->depth) - 1.f); > + in->linesize[plane] / 2, ref->linesize[plane] / 2, (*out)->linesize[plane] / 2, (1 << s->depth) - 1.f); > + } > + > + return 0; > +} > + > +static int process_frame(FFFrameSync *fs) > +{ > + AVFilterContext *ctx = fs->parent; > + AVFilterLink *outlink = ctx->outputs[0]; > + AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL; > + int ret; > + ret = ff_framesync_dualinput_get(fs, &main_frame, &ref_frame); > + if (ret < 0) > + return ret; > + > + ret = filter_frame(ctx, &out_frame, main_frame, ref_frame); > + if(ret < 0) { > + return ret; > } > av_frame_free(&main_frame); > > return ff_filter_frame(outlink, out_frame); > } > > - > static int config_output(AVFilterLink *outlink) > { > AVFilterContext *ctx = outlink->src; > @@ -354,12 +361,27 @@ static int config_output(AVFilterLink *outlink) > FFFrameSyncIn *in; > int ret; > > + if(s->guidance == ON) { > + if (ctx->inputs[0]->w != ctx->inputs[1]->w || > + ctx->inputs[0]->h != ctx->inputs[1]->h) { > + av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n"); > + return AVERROR(EINVAL); > + } > + if (ctx->inputs[0]->format != ctx->inputs[1]->format) { > + av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n"); > + return AVERROR(EINVAL); > + } > + } > > outlink->w = mainlink->w; > outlink->h = mainlink->h; > outlink->time_base = mainlink->time_base; > outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio; > outlink->frame_rate = mainlink->frame_rate; > + > + if (s->guidance == OFF) > + return 0; > + > if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0) > return ret; > > @@ -383,22 +405,66 @@ static int config_output(AVFilterLink *outlink) > static int activate(AVFilterContext *ctx) > { > GuidedContext *s = ctx->priv; > - return ff_framesync_activate(&s->fs); > + AVFrame *frame = NULL; > + AVFrame *out = NULL; > + int ret, status; > + int64_t pts; > + if(s->guidance) > + return ff_framesync_activate(&s->fs); > + > + FF_FILTER_FORWARD_STATUS_BACK(ctx->outputs[0], ctx->inputs[0]); > + > + if ((ret = ff_inlink_consume_frame(ctx->inputs[0], &frame)) > 0) { > + ret = filter_frame(ctx, &out, frame, frame); > + av_frame_free(&frame); > + if (ret < 0) > + return ret; > + ret = ff_filter_frame(ctx->outputs[0], out); > + } > + if (ret < 0) > + return ret; > + if (ff_inlink_acknowledge_status(ctx->inputs[0], &status, &pts)) { > + ff_outlink_set_status(ctx->outputs[0], status, pts); > + return 0; > + } > + if (ff_outlink_frame_wanted(ctx->outputs[0])) > + ff_inlink_request_frame(ctx->inputs[0]); > + return 0; > } > > static av_cold int init(AVFilterContext *ctx) > { > + GuidedContext *s = ctx->priv; > + AVFilterPad pad = { 0 }; > + int ret; > + > + pad.type = AVMEDIA_TYPE_VIDEO; > + pad.name = "source"; > + pad.config_props = config_input; > + > + if ((ret = ff_insert_inpad(ctx, 0, &pad)) < 0) > + return ret; > + > + if (s->guidance == ON) { > + pad.type = AVMEDIA_TYPE_VIDEO; > + pad.name = "guidance"; > + pad.config_props = NULL; > + > + if ((ret = ff_insert_inpad(ctx, 1, &pad)) < 0) > + return ret; > + } > + > return 0; > } > > static av_cold void uninit(AVFilterContext *ctx) > { > GuidedContext *s = ctx->priv; > - ff_framesync_uninit(&s->fs); > + if(s->guidance == ON) > + ff_framesync_uninit(&s->fs); > return; > } > > - > static int process_command(AVFilterContext *ctx, > const char *cmd, > const char *arg, > @@ -414,18 +480,6 @@ static int process_command(AVFilterContext *ctx, > return 0; > } > > -static const AVFilterPad guided_inputs[] = { > - { > - .name = "main", > - .type = AVMEDIA_TYPE_VIDEO, > - },{ > - .name = "reference", > - .type = AVMEDIA_TYPE_VIDEO, > - .config_props = config_input, > - }, > - { NULL } > -}; > - > static const AVFilterPad guided_outputs[] = { > { > .name = "default", > @@ -444,7 +498,7 @@ const AVFilter ff_vf_guided = { > .priv_size = sizeof(GuidedContext), > .priv_class = &guided_class, > .activate = activate, > - .inputs = guided_inputs, > + .inputs = NULL, > .outputs = guided_outputs, > .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, > .process_command = process_command, > -- > 1.9.1 > > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". > looks ok for me. Waiting for more comments. Thanks Steven
Steven Liu <lq@chinaffmpeg.org> 于2021年6月14日周一 下午6:14写道: > > > > > 在 2021年6月14日,10:08,Xuewei Meng <928826483@qq.com> 写道: > > > > From: Xuewei Meng <xwmeng96@gmail.com> > > > > Support single input for guided filter by adding guidance mode. > > If the guidance mode is off, single input is required. And > > edge-preserving smoothing is conducted. If the mode is on, two > > inputs are needed. The second input serves as the guidance. For > > this mode, more tasks are supported, such as detail enhancement, > > dehazing and so on. > > > > Signed-off-by: Xuewei Meng <xwmeng96@gmail.com> > > --- > > doc/filters.texi | 12 ++-- > > libavfilter/vf_guided.c | 168 ++++++++++++++++++++++++++++++++---------------- > > 2 files changed, 119 insertions(+), 61 deletions(-) > > > > diff --git a/doc/filters.texi b/doc/filters.texi > > index 78faf76..5c362c0 100644 > > --- a/doc/filters.texi > > +++ b/doc/filters.texi > > @@ -12975,8 +12975,6 @@ greyedge=difford=1:minknorm=0:sigma=2 > > > > @section guided > > Apply guided filter for edge-preserving smoothing, dehazing and so on. > > -This filter requires two inputs of same resolution and pixel format. > > -The second input serves as the reference. > > > > The filter accepts the following options: > > @table @option > > @@ -12997,6 +12995,12 @@ Set subsampling ratio for @code{fast} mode. > > Range is 2 to 64. Default is 4. > > No subsampling occurs in @code{basic} mode. > > > > +@item guidance > > +Set guidance mode. Can be @code{off} or @code{on}. Default is @code{off}. > > +If @code{off}, single input is required. > > +If @code{on}, two inputs of the same resolution and pixel format are required. > > +The second input serves as the guidance. > > + > > @item planes > > Set planes to filter. Default is first only. > > @end table > > @@ -13009,7 +13013,7 @@ This filter supports the all above options as @ref{commands}. > > @item > > Edge-preserving smoothing with guided filter: > > @example > > -ffmpeg -i in.png -i in.png -filter_complex guided out.png > > +ffmpeg -i in.png -vf guided out.png > > @end example > > > > @item > > @@ -13017,7 +13021,7 @@ Dehazing, structure-transferring filtering, detail enhancement with guided filte > > For the generation of guidance image, refer to paper "Guided Image Filtering". > > See: @url{http://kaiminghe.com/publications/pami12guidedfilter.pdf}. > > @example > > -ffmpeg -i in.png -i guidance.png -filter_complex guided out.png > > +ffmpeg -i in.png -i guidance.png -filter_complex guided=guidance=on out.png > > @end example > > > > @end itemize > > diff --git a/libavfilter/vf_guided.c b/libavfilter/vf_guided.c > > index ea537e4..739d615 100644 > > --- a/libavfilter/vf_guided.c > > +++ b/libavfilter/vf_guided.c > > @@ -22,6 +22,7 @@ > > #include "libavutil/opt.h" > > #include "libavutil/pixdesc.h" > > #include "avfilter.h" > > +#include "filters.h" > > #include "formats.h" > > #include "framesync.h" > > #include "internal.h" > > @@ -33,6 +34,12 @@ enum FilterModes { > > NB_MODES, > > }; > > > > +enum GuidanceModes { > > + OFF, > > + ON, > > + NB_GUIDANCE_MODES, > > +}; > > + > > typedef struct GuidedContext { > > const AVClass *class; > > FFFrameSync fs; > > @@ -41,7 +48,7 @@ typedef struct GuidedContext { > > float eps; > > int mode; > > int sub; > > - > > + int guidance; > > int planes; > > > > int width; > > @@ -59,13 +66,16 @@ typedef struct GuidedContext { > > #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM > > > > static const AVOption guided_options[] = { > > - { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS }, > > - { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS }, > > - { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, FLAGS, "mode" }, > > - { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" }, > > - { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, FLAGS, "mode" }, > > - { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, FLAGS }, > > - { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1 }, 0, 0xF, FLAGS }, > > + { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS }, > > + { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS }, > > + { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, FLAGS, "mode" }, > > + { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" }, > > + { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, FLAGS, "mode" }, > > + { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, FLAGS }, > > + { "guidance", "set guidance mode (0: off mode; 1: on mode)", OFFSET(guidance), AV_OPT_TYPE_INT, {.i64 = OFF }, OFF, NB_GUIDANCE_MODES - 1, FLAGS, "guidance" }, > > + { "off", "only one input is enabled", 0, AV_OPT_TYPE_CONST, {.i64 = OFF }, 0, 0, FLAGS, "guidance" }, > > + { "on", "two inputs are required", 0, AV_OPT_TYPE_CONST, {.i64 = ON }, 0, 0, FLAGS, "guidance" }, > > + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 0xF, FLAGS }, > > { NULL } > > }; > > > > @@ -149,16 +159,6 @@ static int config_input(AVFilterLink *inlink) > > GuidedContext *s = ctx->priv; > > const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); > > > > - if (ctx->inputs[0]->w != ctx->inputs[1]->w || > > - ctx->inputs[0]->h != ctx->inputs[1]->h) { > > - av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n"); > > - return AVERROR(EINVAL); > > - } > > - if (ctx->inputs[0]->format != ctx->inputs[1]->format) { > > - av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n"); > > - return AVERROR(EINVAL); > > - } > > - > > if (s->mode == BASIC) { > > s->sub = 1; > > } > > @@ -230,7 +230,7 @@ static int guided_##name(AVFilterContext *ctx, GuidedContext *s, > > meanB = av_calloc(w * h, sizeof(float)); \ > > \ > > if (!I || !II || !P || !IP || !meanI || !meanII || !meanP || \ > > - !meanIP || !A || !B || !meanA || !meanB){ \ > > + !meanIP || !A || !B || !meanA || !meanB) { \ > > ret = AVERROR(ENOMEM); \ > > goto end; \ > > } \ > > @@ -304,47 +304,54 @@ end: > > GUIDED(uint8_t, byte) > > GUIDED(uint16_t, word) > > > > -static int process_frame(FFFrameSync *fs) > > +static int filter_frame(AVFilterContext *ctx, AVFrame **out, AVFrame *in, AVFrame *ref) > > { > > - AVFilterContext *ctx = fs->parent; > > - GuidedContext *s = fs->opaque; > > + GuidedContext *s = ctx->priv; > > AVFilterLink *outlink = ctx->outputs[0]; > > - AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL; > > - int ret; > > - > > - ret = ff_framesync_dualinput_get(fs, &main_frame, &ref_frame); > > - if (ret < 0) > > - return ret; > > - > > - out_frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); > > - if (!out_frame) { > > - av_frame_free(&main_frame); > > + *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); > > + if (!*out) > > return AVERROR(ENOMEM); > > - } > > - av_frame_copy_props(out_frame, main_frame); > > + av_frame_copy_props(*out, in); > > > > for (int plane = 0; plane < s->nb_planes; plane++) { > > if (!(s->planes & (1 << plane))) { > > - av_image_copy_plane(out_frame->data[plane], out_frame->linesize[plane], > > - main_frame->data[plane], main_frame->linesize[plane], > > + av_image_copy_plane((*out)->data[plane], (*out)->linesize[plane], > > + in->data[plane], in->linesize[plane], > > s->planewidth[plane] * ((s->depth + 7) / 8), s->planeheight[plane]); > > continue; > > } > > if (s->depth <= 8) > > - guided_byte(ctx, s, main_frame->data[plane], ref_frame->data[plane], out_frame->data[plane], s->radius, s->eps, > > + guided_byte(ctx, s, in->data[plane], ref->data[plane], (*out)->data[plane], s->radius, s->eps, > > s->planewidth[plane], s->planeheight[plane], > > - main_frame->linesize[plane], ref_frame->linesize[plane], out_frame->linesize[plane], (1 << s->depth) - 1.f); > > + in->linesize[plane], ref->linesize[plane], (*out)->linesize[plane], (1 << s->depth) - 1.f); > > else > > - guided_word(ctx, s, main_frame->data[plane], ref_frame->data[plane], out_frame->data[plane], s->radius, s->eps, > > + guided_word(ctx, s, in->data[plane], ref->data[plane], (*out)->data[plane], s->radius, s->eps, > > s->planewidth[plane], s->planeheight[plane], > > - main_frame->linesize[plane] / 2, ref_frame->linesize[plane] / 2, out_frame->linesize[plane] / 2, (1 << s->depth) - 1.f); > > + in->linesize[plane] / 2, ref->linesize[plane] / 2, (*out)->linesize[plane] / 2, (1 << s->depth) - 1.f); > > + } > > + > > + return 0; > > +} > > + > > +static int process_frame(FFFrameSync *fs) > > +{ > > + AVFilterContext *ctx = fs->parent; > > + AVFilterLink *outlink = ctx->outputs[0]; > > + AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL; > > + int ret; > > + ret = ff_framesync_dualinput_get(fs, &main_frame, &ref_frame); > > + if (ret < 0) > > + return ret; > > + > > + ret = filter_frame(ctx, &out_frame, main_frame, ref_frame); > > + if(ret < 0) { > > + return ret; > > } > > av_frame_free(&main_frame); > > > > return ff_filter_frame(outlink, out_frame); > > } > > > > - > > static int config_output(AVFilterLink *outlink) > > { > > AVFilterContext *ctx = outlink->src; > > @@ -354,12 +361,27 @@ static int config_output(AVFilterLink *outlink) > > FFFrameSyncIn *in; > > int ret; > > > > + if(s->guidance == ON) { > > + if (ctx->inputs[0]->w != ctx->inputs[1]->w || > > + ctx->inputs[0]->h != ctx->inputs[1]->h) { > > + av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n"); > > + return AVERROR(EINVAL); > > + } > > + if (ctx->inputs[0]->format != ctx->inputs[1]->format) { > > + av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n"); > > + return AVERROR(EINVAL); > > + } > > + } > > > > outlink->w = mainlink->w; > > outlink->h = mainlink->h; > > outlink->time_base = mainlink->time_base; > > outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio; > > outlink->frame_rate = mainlink->frame_rate; > > + > > + if (s->guidance == OFF) > > + return 0; > > + > > if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0) > > return ret; > > > > @@ -383,22 +405,66 @@ static int config_output(AVFilterLink *outlink) > > static int activate(AVFilterContext *ctx) > > { > > GuidedContext *s = ctx->priv; > > - return ff_framesync_activate(&s->fs); > > + AVFrame *frame = NULL; > > + AVFrame *out = NULL; > > + int ret, status; > > + int64_t pts; > > + if(s->guidance) > > + return ff_framesync_activate(&s->fs); > > + > > + FF_FILTER_FORWARD_STATUS_BACK(ctx->outputs[0], ctx->inputs[0]); > > + > > + if ((ret = ff_inlink_consume_frame(ctx->inputs[0], &frame)) > 0) { > > + ret = filter_frame(ctx, &out, frame, frame); > > + av_frame_free(&frame); > > + if (ret < 0) > > + return ret; > > + ret = ff_filter_frame(ctx->outputs[0], out); > > + } > > + if (ret < 0) > > + return ret; > > + if (ff_inlink_acknowledge_status(ctx->inputs[0], &status, &pts)) { > > + ff_outlink_set_status(ctx->outputs[0], status, pts); > > + return 0; > > + } > > + if (ff_outlink_frame_wanted(ctx->outputs[0])) > > + ff_inlink_request_frame(ctx->inputs[0]); > > + return 0; > > } > > > > static av_cold int init(AVFilterContext *ctx) > > { > > + GuidedContext *s = ctx->priv; > > + AVFilterPad pad = { 0 }; > > + int ret; > > + > > + pad.type = AVMEDIA_TYPE_VIDEO; > > + pad.name = "source"; > > + pad.config_props = config_input; > > + > > + if ((ret = ff_insert_inpad(ctx, 0, &pad)) < 0) > > + return ret; > > + > > + if (s->guidance == ON) { > > + pad.type = AVMEDIA_TYPE_VIDEO; > > + pad.name = "guidance"; > > + pad.config_props = NULL; > > + > > + if ((ret = ff_insert_inpad(ctx, 1, &pad)) < 0) > > + return ret; > > + } > > + > > return 0; > > } > > > > static av_cold void uninit(AVFilterContext *ctx) > > { > > GuidedContext *s = ctx->priv; > > - ff_framesync_uninit(&s->fs); > > + if(s->guidance == ON) > > + ff_framesync_uninit(&s->fs); > > return; > > } > > > > - > > static int process_command(AVFilterContext *ctx, > > const char *cmd, > > const char *arg, > > @@ -414,18 +480,6 @@ static int process_command(AVFilterContext *ctx, > > return 0; > > } > > > > -static const AVFilterPad guided_inputs[] = { > > - { > > - .name = "main", > > - .type = AVMEDIA_TYPE_VIDEO, > > - },{ > > - .name = "reference", > > - .type = AVMEDIA_TYPE_VIDEO, > > - .config_props = config_input, > > - }, > > - { NULL } > > -}; > > - > > static const AVFilterPad guided_outputs[] = { > > { > > .name = "default", > > @@ -444,7 +498,7 @@ const AVFilter ff_vf_guided = { > > .priv_size = sizeof(GuidedContext), > > .priv_class = &guided_class, > > .activate = activate, > > - .inputs = guided_inputs, > > + .inputs = NULL, > > .outputs = guided_outputs, > > .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, > > .process_command = process_command, > > -- > > 1.9.1 > > > > > > _______________________________________________ > > ffmpeg-devel mailing list > > ffmpeg-devel@ffmpeg.org > > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > > > To unsubscribe, visit link above, or email > > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". > > > > looks ok for me. Waiting for more comments. applied Thanks Steven
diff --git a/doc/filters.texi b/doc/filters.texi index 78faf76..5c362c0 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -12975,8 +12975,6 @@ greyedge=difford=1:minknorm=0:sigma=2 @section guided Apply guided filter for edge-preserving smoothing, dehazing and so on. -This filter requires two inputs of same resolution and pixel format. -The second input serves as the reference. The filter accepts the following options: @table @option @@ -12997,6 +12995,12 @@ Set subsampling ratio for @code{fast} mode. Range is 2 to 64. Default is 4. No subsampling occurs in @code{basic} mode. +@item guidance +Set guidance mode. Can be @code{off} or @code{on}. Default is @code{off}. +If @code{off}, single input is required. +If @code{on}, two inputs of the same resolution and pixel format are required. +The second input serves as the guidance. + @item planes Set planes to filter. Default is first only. @end table @@ -13009,7 +13013,7 @@ This filter supports the all above options as @ref{commands}. @item Edge-preserving smoothing with guided filter: @example -ffmpeg -i in.png -i in.png -filter_complex guided out.png +ffmpeg -i in.png -vf guided out.png @end example @item @@ -13017,7 +13021,7 @@ Dehazing, structure-transferring filtering, detail enhancement with guided filte For the generation of guidance image, refer to paper "Guided Image Filtering". See: @url{http://kaiminghe.com/publications/pami12guidedfilter.pdf}. @example -ffmpeg -i in.png -i guidance.png -filter_complex guided out.png +ffmpeg -i in.png -i guidance.png -filter_complex guided=guidance=on out.png @end example @end itemize diff --git a/libavfilter/vf_guided.c b/libavfilter/vf_guided.c index ea537e4..739d615 100644 --- a/libavfilter/vf_guided.c +++ b/libavfilter/vf_guided.c @@ -22,6 +22,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "filters.h" #include "formats.h" #include "framesync.h" #include "internal.h" @@ -33,6 +34,12 @@ enum FilterModes { NB_MODES, }; +enum GuidanceModes { + OFF, + ON, + NB_GUIDANCE_MODES, +}; + typedef struct GuidedContext { const AVClass *class; FFFrameSync fs; @@ -41,7 +48,7 @@ typedef struct GuidedContext { float eps; int mode; int sub; - + int guidance; int planes; int width; @@ -59,13 +66,16 @@ typedef struct GuidedContext { #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption guided_options[] = { - { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS }, - { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS }, - { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, FLAGS, "mode" }, - { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" }, - { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, FLAGS, "mode" }, - { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, FLAGS }, - { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1 }, 0, 0xF, FLAGS }, + { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS }, + { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS }, + { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, FLAGS, "mode" }, + { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" }, + { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, FLAGS, "mode" }, + { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, FLAGS }, + { "guidance", "set guidance mode (0: off mode; 1: on mode)", OFFSET(guidance), AV_OPT_TYPE_INT, {.i64 = OFF }, OFF, NB_GUIDANCE_MODES - 1, FLAGS, "guidance" }, + { "off", "only one input is enabled", 0, AV_OPT_TYPE_CONST, {.i64 = OFF }, 0, 0, FLAGS, "guidance" }, + { "on", "two inputs are required", 0, AV_OPT_TYPE_CONST, {.i64 = ON }, 0, 0, FLAGS, "guidance" }, + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 0xF, FLAGS }, { NULL } }; @@ -149,16 +159,6 @@ static int config_input(AVFilterLink *inlink) GuidedContext *s = ctx->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); - if (ctx->inputs[0]->w != ctx->inputs[1]->w || - ctx->inputs[0]->h != ctx->inputs[1]->h) { - av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n"); - return AVERROR(EINVAL); - } - if (ctx->inputs[0]->format != ctx->inputs[1]->format) { - av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n"); - return AVERROR(EINVAL); - } - if (s->mode == BASIC) { s->sub = 1; } @@ -230,7 +230,7 @@ static int guided_##name(AVFilterContext *ctx, GuidedContext *s, meanB = av_calloc(w * h, sizeof(float)); \ \ if (!I || !II || !P || !IP || !meanI || !meanII || !meanP || \ - !meanIP || !A || !B || !meanA || !meanB){ \ + !meanIP || !A || !B || !meanA || !meanB) { \ ret = AVERROR(ENOMEM); \ goto end; \ } \ @@ -304,47 +304,54 @@ end: GUIDED(uint8_t, byte) GUIDED(uint16_t, word) -static int process_frame(FFFrameSync *fs) +static int filter_frame(AVFilterContext *ctx, AVFrame **out, AVFrame *in, AVFrame *ref) { - AVFilterContext *ctx = fs->parent; - GuidedContext *s = fs->opaque; + GuidedContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; - AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL; - int ret; - - ret = ff_framesync_dualinput_get(fs, &main_frame, &ref_frame); - if (ret < 0) - return ret; - - out_frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out_frame) { - av_frame_free(&main_frame); + *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!*out) return AVERROR(ENOMEM); - } - av_frame_copy_props(out_frame, main_frame); + av_frame_copy_props(*out, in); for (int plane = 0; plane < s->nb_planes; plane++) { if (!(s->planes & (1 << plane))) { - av_image_copy_plane(out_frame->data[plane], out_frame->linesize[plane], - main_frame->data[plane], main_frame->linesize[plane], + av_image_copy_plane((*out)->data[plane], (*out)->linesize[plane], + in->data[plane], in->linesize[plane], s->planewidth[plane] * ((s->depth + 7) / 8), s->planeheight[plane]); continue; } if (s->depth <= 8) - guided_byte(ctx, s, main_frame->data[plane], ref_frame->data[plane], out_frame->data[plane], s->radius, s->eps, + guided_byte(ctx, s, in->data[plane], ref->data[plane], (*out)->data[plane], s->radius, s->eps, s->planewidth[plane], s->planeheight[plane], - main_frame->linesize[plane], ref_frame->linesize[plane], out_frame->linesize[plane], (1 << s->depth) - 1.f); + in->linesize[plane], ref->linesize[plane], (*out)->linesize[plane], (1 << s->depth) - 1.f); else - guided_word(ctx, s, main_frame->data[plane], ref_frame->data[plane], out_frame->data[plane], s->radius, s->eps, + guided_word(ctx, s, in->data[plane], ref->data[plane], (*out)->data[plane], s->radius, s->eps, s->planewidth[plane], s->planeheight[plane], - main_frame->linesize[plane] / 2, ref_frame->linesize[plane] / 2, out_frame->linesize[plane] / 2, (1 << s->depth) - 1.f); + in->linesize[plane] / 2, ref->linesize[plane] / 2, (*out)->linesize[plane] / 2, (1 << s->depth) - 1.f); + } + + return 0; +} + +static int process_frame(FFFrameSync *fs) +{ + AVFilterContext *ctx = fs->parent; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL; + int ret; + ret = ff_framesync_dualinput_get(fs, &main_frame, &ref_frame); + if (ret < 0) + return ret; + + ret = filter_frame(ctx, &out_frame, main_frame, ref_frame); + if(ret < 0) { + return ret; } av_frame_free(&main_frame); return ff_filter_frame(outlink, out_frame); } - static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -354,12 +361,27 @@ static int config_output(AVFilterLink *outlink) FFFrameSyncIn *in; int ret; + if(s->guidance == ON) { + if (ctx->inputs[0]->w != ctx->inputs[1]->w || + ctx->inputs[0]->h != ctx->inputs[1]->h) { + av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n"); + return AVERROR(EINVAL); + } + if (ctx->inputs[0]->format != ctx->inputs[1]->format) { + av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n"); + return AVERROR(EINVAL); + } + } outlink->w = mainlink->w; outlink->h = mainlink->h; outlink->time_base = mainlink->time_base; outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio; outlink->frame_rate = mainlink->frame_rate; + + if (s->guidance == OFF) + return 0; + if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0) return ret; @@ -383,22 +405,66 @@ static int config_output(AVFilterLink *outlink) static int activate(AVFilterContext *ctx) { GuidedContext *s = ctx->priv; - return ff_framesync_activate(&s->fs); + AVFrame *frame = NULL; + AVFrame *out = NULL; + int ret, status; + int64_t pts; + if(s->guidance) + return ff_framesync_activate(&s->fs); + + FF_FILTER_FORWARD_STATUS_BACK(ctx->outputs[0], ctx->inputs[0]); + + if ((ret = ff_inlink_consume_frame(ctx->inputs[0], &frame)) > 0) { + ret = filter_frame(ctx, &out, frame, frame); + av_frame_free(&frame); + if (ret < 0) + return ret; + ret = ff_filter_frame(ctx->outputs[0], out); + } + if (ret < 0) + return ret; + if (ff_inlink_acknowledge_status(ctx->inputs[0], &status, &pts)) { + ff_outlink_set_status(ctx->outputs[0], status, pts); + return 0; + } + if (ff_outlink_frame_wanted(ctx->outputs[0])) + ff_inlink_request_frame(ctx->inputs[0]); + return 0; } static av_cold int init(AVFilterContext *ctx) { + GuidedContext *s = ctx->priv; + AVFilterPad pad = { 0 }; + int ret; + + pad.type = AVMEDIA_TYPE_VIDEO; + pad.name = "source"; + pad.config_props = config_input; + + if ((ret = ff_insert_inpad(ctx, 0, &pad)) < 0) + return ret; + + if (s->guidance == ON) { + pad.type = AVMEDIA_TYPE_VIDEO; + pad.name = "guidance"; + pad.config_props = NULL; + + if ((ret = ff_insert_inpad(ctx, 1, &pad)) < 0) + return ret; + } + return 0; } static av_cold void uninit(AVFilterContext *ctx) { GuidedContext *s = ctx->priv; - ff_framesync_uninit(&s->fs); + if(s->guidance == ON) + ff_framesync_uninit(&s->fs); return; } - static int process_command(AVFilterContext *ctx, const char *cmd, const char *arg, @@ -414,18 +480,6 @@ static int process_command(AVFilterContext *ctx, return 0; } -static const AVFilterPad guided_inputs[] = { - { - .name = "main", - .type = AVMEDIA_TYPE_VIDEO, - },{ - .name = "reference", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_input, - }, - { NULL } -}; - static const AVFilterPad guided_outputs[] = { { .name = "default", @@ -444,7 +498,7 @@ const AVFilter ff_vf_guided = { .priv_size = sizeof(GuidedContext), .priv_class = &guided_class, .activate = activate, - .inputs = guided_inputs, + .inputs = NULL, .outputs = guided_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command,