History

avfilter/vf_guided: support single input

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.

Xuewei Meng committed 5 years ago

Commit 9ca88b31 parent 49e3a812 files · +119 −61Browse files

Showing the changes to libavfilter (1 of 2 files). Show all

Unified · Split

libavfilter/vf_guided.c+111−57
@@ -22,6 +22,7 @@ · 21 unmodified lines
2222 #include "libavutil/opt.h"
2323 #include "libavutil/pixdesc.h"
2424 #include "avfilter.h"
25+#include "filters.h"
2526 #include "formats.h"
2627 #include "framesync.h"
2728 #include "internal.h"
@@ -33,6 +34,12 @@ enum FilterModes { · 5 unmodified lines
3334 NB_MODES,
3435 };
3536
37+enum GuidanceModes {
38+ OFF,
39+ ON,
40+ NB_GUIDANCE_MODES,
41+};
42+
3643 typedef struct GuidedContext {
3744 const AVClass *class;
3845 FFFrameSync fs;
@@ -41,7 +48,7 @@ typedef struct GuidedContext { · 2 unmodified lines
4148 float eps;
4249 int mode;
4350 int sub;
44−
51+ int guidance;
4552 int planes;
4653
4754 int width;
@@ -59,13 +66,16 @@ typedef struct GuidedContext { · 11 unmodified lines
5966 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
6067
6168 static const AVOption guided_options[] = {
62− { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS },
63− { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS },
64− { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, FLAGS, "mode" },
65− { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" },
66− { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, FLAGS, "mode" },
67− { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, FLAGS },
68− { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1 }, 0, 0xF, FLAGS },
69+ { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS },
70+ { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS },
71+ { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, FLAGS, "mode" },
72+ { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" },
73+ { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, FLAGS, "mode" },
74+ { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, FLAGS },
75+ { "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" },
76+ { "off", "only one input is enabled", 0, AV_OPT_TYPE_CONST, {.i64 = OFF }, 0, 0, FLAGS, "guidance" },
77+ { "on", "two inputs are required", 0, AV_OPT_TYPE_CONST, {.i64 = ON }, 0, 0, FLAGS, "guidance" },
78+ { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 0xF, FLAGS },
6979 { NULL }
7080 };
7181
@@ -149,16 +159,6 @@ static int config_input(AVFilterLink *inlink) · 77 unmodified lines
149159 GuidedContext *s = ctx->priv;
150160 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
151161
152− if (ctx->inputs[0]->w != ctx->inputs[1]->w ||
153− ctx->inputs[0]->h != ctx->inputs[1]->h) {
154− av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n");
155− return AVERROR(EINVAL);
156− }
157− if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
158− av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n");
159− return AVERROR(EINVAL);
160− }
161−
162162 if (s->mode == BASIC) {
163163 s->sub = 1;
164164 }
@@ -230,7 +230,7 @@ static int guided_##name(AVFilterContext *ctx, GuidedContext *s, · 65 unmodified lines
230230 meanB = av_calloc(w * h, sizeof(float)); \
231231 \
232232 if (!I || !II || !P || !IP || !meanI || !meanII || !meanP || \
233− !meanIP || !A || !B || !meanA || !meanB){ \
233+ !meanIP || !A || !B || !meanA || !meanB) { \
234234 ret = AVERROR(ENOMEM); \
235235 goto end; \
236236 } \
@@ -304,47 +304,54 @@ end: · 67 unmodified lines
304304 GUIDED(uint8_t, byte)
305305 GUIDED(uint16_t, word)
306306
307−static int process_frame(FFFrameSync *fs)
307+static int filter_frame(AVFilterContext *ctx, AVFrame **out, AVFrame *in, AVFrame *ref)
308308 {
309− AVFilterContext *ctx = fs->parent;
310− GuidedContext *s = fs->opaque;
309+ GuidedContext *s = ctx->priv;
311310 AVFilterLink *outlink = ctx->outputs[0];
312− AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL;
313− int ret;
314−
315− ret = ff_framesync_dualinput_get(fs, &main_frame, &ref_frame);
316− if (ret < 0)
317− return ret;
318−
319− out_frame = ff_get_video_buffer(outlink, outlink->w, outlink->h);
320− if (!out_frame) {
321− av_frame_free(&main_frame);
311+ *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
312+ if (!*out)
322313 return AVERROR(ENOMEM);
323− }
324− av_frame_copy_props(out_frame, main_frame);
314+ av_frame_copy_props(*out, in);
325315
326316 for (int plane = 0; plane < s->nb_planes; plane++) {
327317 if (!(s->planes & (1 << plane))) {
328− av_image_copy_plane(out_frame->data[plane], out_frame->linesize[plane],
329− main_frame->data[plane], main_frame->linesize[plane],
318+ av_image_copy_plane((*out)->data[plane], (*out)->linesize[plane],
319+ in->data[plane], in->linesize[plane],
330320 s->planewidth[plane] * ((s->depth + 7) / 8), s->planeheight[plane]);
331321 continue;
332322 }
333323 if (s->depth <= 8)
334− guided_byte(ctx, s, main_frame->data[plane], ref_frame->data[plane], out_frame->data[plane], s->radius, s->eps,
324+ guided_byte(ctx, s, in->data[plane], ref->data[plane], (*out)->data[plane], s->radius, s->eps,
335325 s->planewidth[plane], s->planeheight[plane],
336− main_frame->linesize[plane], ref_frame->linesize[plane], out_frame->linesize[plane], (1 << s->depth) - 1.f);
326+ in->linesize[plane], ref->linesize[plane], (*out)->linesize[plane], (1 << s->depth) - 1.f);
337327 else
338− guided_word(ctx, s, main_frame->data[plane], ref_frame->data[plane], out_frame->data[plane], s->radius, s->eps,
328+ guided_word(ctx, s, in->data[plane], ref->data[plane], (*out)->data[plane], s->radius, s->eps,
339329 s->planewidth[plane], s->planeheight[plane],
340− main_frame->linesize[plane] / 2, ref_frame->linesize[plane] / 2, out_frame->linesize[plane] / 2, (1 << s->depth) - 1.f);
330+ in->linesize[plane] / 2, ref->linesize[plane] / 2, (*out)->linesize[plane] / 2, (1 << s->depth) - 1.f);
331+ }
332+
333+ return 0;
334+}
335+
336+static int process_frame(FFFrameSync *fs)
337+{
338+ AVFilterContext *ctx = fs->parent;
339+ AVFilterLink *outlink = ctx->outputs[0];
340+ AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL;
341+ int ret;
342+ ret = ff_framesync_dualinput_get(fs, &main_frame, &ref_frame);
343+ if (ret < 0)
344+ return ret;
345+
346+ ret = filter_frame(ctx, &out_frame, main_frame, ref_frame);
347+ if (ret < 0) {
348+ return ret;
341349 }
342350 av_frame_free(&main_frame);
343351
344352 return ff_filter_frame(outlink, out_frame);
345353 }
346354
347−
348355 static int config_output(AVFilterLink *outlink)
349356 {
350357 AVFilterContext *ctx = outlink->src;
@@ -354,12 +361,27 @@ static int config_output(AVFilterLink *outlink) · 3 unmodified lines
354361 FFFrameSyncIn *in;
355362 int ret;
356363
364+ if (s->guidance == ON) {
365+ if (ctx->inputs[0]->w != ctx->inputs[1]->w ||
366+ ctx->inputs[0]->h != ctx->inputs[1]->h) {
367+ av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n");
368+ return AVERROR(EINVAL);
369+ }
370+ if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
371+ av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n");
372+ return AVERROR(EINVAL);
373+ }
374+ }
357375
358376 outlink->w = mainlink->w;
359377 outlink->h = mainlink->h;
360378 outlink->time_base = mainlink->time_base;
361379 outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio;
362380 outlink->frame_rate = mainlink->frame_rate;
381+
382+ if (s->guidance == OFF)
383+ return 0;
384+
363385 if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0)
364386 return ret;
365387
@@ -383,22 +405,66 @@ static int config_output(AVFilterLink *outlink) · 17 unmodified lines
383405 static int activate(AVFilterContext *ctx)
384406 {
385407 GuidedContext *s = ctx->priv;
386− return ff_framesync_activate(&s->fs);
408+ AVFrame *frame = NULL;
409+ AVFrame *out = NULL;
410+ int ret, status;
411+ int64_t pts;
412+ if (s->guidance)
413+ return ff_framesync_activate(&s->fs);
414+
415+ FF_FILTER_FORWARD_STATUS_BACK(ctx->outputs[0], ctx->inputs[0]);
416+
417+ if ((ret = ff_inlink_consume_frame(ctx->inputs[0], &frame)) > 0) {
418+ ret = filter_frame(ctx, &out, frame, frame);
419+ av_frame_free(&frame);
420+ if (ret < 0)
421+ return ret;
422+ ret = ff_filter_frame(ctx->outputs[0], out);
423+ }
424+ if (ret < 0)
425+ return ret;
426+ if (ff_inlink_acknowledge_status(ctx->inputs[0], &status, &pts)) {
427+ ff_outlink_set_status(ctx->outputs[0], status, pts);
428+ return 0;
429+ }
430+ if (ff_outlink_frame_wanted(ctx->outputs[0]))
431+ ff_inlink_request_frame(ctx->inputs[0]);
432+ return 0;
387433 }
388434
389435 static av_cold int init(AVFilterContext *ctx)
390436 {
437+ GuidedContext *s = ctx->priv;
438+ AVFilterPad pad = { 0 };
439+ int ret;
440+
441+ pad.type = AVMEDIA_TYPE_VIDEO;
442+ pad.name = "source";
443+ pad.config_props = config_input;
444+
445+ if ((ret = ff_insert_inpad(ctx, 0, &pad)) < 0)
446+ return ret;
447+
448+ if (s->guidance == ON) {
449+ pad.type = AVMEDIA_TYPE_VIDEO;
450+ pad.name = "guidance";
451+ pad.config_props = NULL;
452+
453+ if ((ret = ff_insert_inpad(ctx, 1, &pad)) < 0)
454+ return ret;
455+ }
456+
391457 return 0;
392458 }
393459
394460 static av_cold void uninit(AVFilterContext *ctx)
395461 {
396462 GuidedContext *s = ctx->priv;
397− ff_framesync_uninit(&s->fs);
463+ if (s->guidance == ON)
464+ ff_framesync_uninit(&s->fs);
398465 return;
399466 }
400467
401−
402468 static int process_command(AVFilterContext *ctx,
403469 const char *cmd,
404470 const char *arg,
@@ -414,18 +480,6 @@ static int process_command(AVFilterContext *ctx, · 9 unmodified lines
414480 return 0;
415481 }
416482
417−static const AVFilterPad guided_inputs[] = {
418− {
419− .name = "main",
420− .type = AVMEDIA_TYPE_VIDEO,
421− },{
422− .name = "reference",
423− .type = AVMEDIA_TYPE_VIDEO,
424− .config_props = config_input,
425− },
426− { NULL }
427−};
428−
429483 static const AVFilterPad guided_outputs[] = {
430484 {
431485 .name = "default",
@@ -444,7 +498,7 @@ const AVFilter ff_vf_guided = { · 12 unmodified lines
444498 .priv_size = sizeof(GuidedContext),
445499 .priv_class = &guided_class,
446500 .activate = activate,
447− .inputs = guided_inputs,
501+ .inputs = NULL,
448502 .outputs = guided_outputs,
449503 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
450504 .process_command = process_command,

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t