Steven Liu <l...@chinaffmpeg.org> 于2021年6月14日周一 下午6:14写道: > > > > > 在 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. applied
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".