[FFmpeg-devel] [PATCH] avfilter/vf_guided: support single input
Steven Liu
lingjiujianke at gmail.com
Tue Jun 29 11:16:36 EEST 2021
Steven Liu <lq at chinaffmpeg.org> 于2021年6月14日周一 下午6:14写道:
>
>
>
> > 在 2021年6月14日,10:08,Xuewei Meng <928826483 at qq.com> 写道:
> >
> > From: Xuewei Meng <xwmeng96 at 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 at 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.
> >
> > + at 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 at ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
> >
>
> looks ok for me. Waiting for more comments.
applied
Thanks
Steven
More information about the ffmpeg-devel
mailing list