> 在 2021年6月14日,10:08,Xuewei Meng <928826...@qq.com> 写道: > > From: Xuewei Meng <xwmen...@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 <xwmen...@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-requ...@ffmpeg.org with subject "unsubscribe". >
looks ok for me. Waiting for more comments. Thanks Steven _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".