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
22#include "libavutil/opt.h"22#include "libavutil/opt.h"
23#include "libavutil/pixdesc.h"23#include "libavutil/pixdesc.h"
24#include "avfilter.h"24#include "avfilter.h"
25#include "filters.h"
25#include "formats.h"26#include "formats.h"
26#include "framesync.h"27#include "framesync.h"
27#include "internal.h"28#include "internal.h"
@@ -33,6 +34,12 @@ enum FilterModes { · 5 unmodified lines
33 NB_MODES,34 NB_MODES,
34};35};
35 36
37enum GuidanceModes {
38 OFF,
39 ON,
40 NB_GUIDANCE_MODES,
41};
42
36typedef struct GuidedContext {43typedef struct GuidedContext {
37 const AVClass *class;44 const AVClass *class;
38 FFFrameSync fs;45 FFFrameSync fs;
@@ -41,7 +48,7 @@ typedef struct GuidedContext { · 2 unmodified lines
41 float eps;48 float eps;
42 int mode;49 int mode;
43 int sub;50 int sub;
44 51 int guidance;
45 int planes;52 int planes;
46 53
47 int width;54 int width;
@@ -59,13 +66,16 @@ typedef struct GuidedContext { · 11 unmodified lines
59#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM66#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
60 67
61static const AVOption guided_options[] = {68static const AVOption guided_options[] = {
62 { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, FLAGS },69 { "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 },70 { "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" },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" },
65 { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, FLAGS, "mode" },72 { "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" },73 { "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 },74 { "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 },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 },
69 { NULL }79 { NULL }
70};80};
71 81
@@ -149,16 +159,6 @@ static int config_input(AVFilterLink *inlink) · 77 unmodified lines
149 GuidedContext *s = ctx->priv;159 GuidedContext *s = ctx->priv;
150 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);160 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
151 161
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
162 if (s->mode == BASIC) {162 if (s->mode == BASIC) {
163 s->sub = 1;163 s->sub = 1;
164 }164 }
@@ -230,7 +230,7 @@ static int guided_##name(AVFilterContext *ctx, GuidedContext *s, · 65 unmodified lines
230 meanB = av_calloc(w * h, sizeof(float)); \230 meanB = av_calloc(w * h, sizeof(float)); \
231 \231 \
232 if (!I || !II || !P || !IP || !meanI || !meanII || !meanP || \232 if (!I || !II || !P || !IP || !meanI || !meanII || !meanP || \
233 !meanIP || !A || !B || !meanA || !meanB){ \233 !meanIP || !A || !B || !meanA || !meanB) { \
234 ret = AVERROR(ENOMEM); \234 ret = AVERROR(ENOMEM); \
235 goto end; \235 goto end; \
236 } \236 } \
@@ -304,47 +304,54 @@ end: · 67 unmodified lines
304GUIDED(uint8_t, byte)304GUIDED(uint8_t, byte)
305GUIDED(uint16_t, word)305GUIDED(uint16_t, word)
306 306
307static int process_frame(FFFrameSync *fs)307static int filter_frame(AVFilterContext *ctx, AVFrame **out, AVFrame *in, AVFrame *ref)
308{308{
309 AVFilterContext *ctx = fs->parent;309 GuidedContext *s = ctx->priv;
310 GuidedContext *s = fs->opaque;
311 AVFilterLink *outlink = ctx->outputs[0];310 AVFilterLink *outlink = ctx->outputs[0];
312 AVFrame *out_frame = NULL, *main_frame = NULL, *ref_frame = NULL;311 *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
313 int ret;312 if (!*out)
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);
322 return AVERROR(ENOMEM);313 return AVERROR(ENOMEM);
323 }314 av_frame_copy_props(*out, in);
324 av_frame_copy_props(out_frame, main_frame);
325 315
326 for (int plane = 0; plane < s->nb_planes; plane++) {316 for (int plane = 0; plane < s->nb_planes; plane++) {
327 if (!(s->planes & (1 << plane))) {317 if (!(s->planes & (1 << plane))) {
328 av_image_copy_plane(out_frame->data[plane], out_frame->linesize[plane],318 av_image_copy_plane((*out)->data[plane], (*out)->linesize[plane],
329 main_frame->data[plane], main_frame->linesize[plane],319 in->data[plane], in->linesize[plane],
330 s->planewidth[plane] * ((s->depth + 7) / 8), s->planeheight[plane]);320 s->planewidth[plane] * ((s->depth + 7) / 8), s->planeheight[plane]);
331 continue;321 continue;
332 }322 }
333 if (s->depth <= 8)323 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,
335 s->planewidth[plane], s->planeheight[plane],325 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);
337 else327 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,
339 s->planewidth[plane], s->planeheight[plane],329 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
336static 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;
341 }349 }
342 av_frame_free(&main_frame);350 av_frame_free(&main_frame);
343 351
344 return ff_filter_frame(outlink, out_frame);352 return ff_filter_frame(outlink, out_frame);
345}353}
346 354
347
348static int config_output(AVFilterLink *outlink)355static int config_output(AVFilterLink *outlink)
349{356{
350 AVFilterContext *ctx = outlink->src;357 AVFilterContext *ctx = outlink->src;
@@ -354,12 +361,27 @@ static int config_output(AVFilterLink *outlink) · 3 unmodified lines
354 FFFrameSyncIn *in;361 FFFrameSyncIn *in;
355 int ret;362 int ret;
356 363
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 }
357 375
358 outlink->w = mainlink->w;376 outlink->w = mainlink->w;
359 outlink->h = mainlink->h;377 outlink->h = mainlink->h;
360 outlink->time_base = mainlink->time_base;378 outlink->time_base = mainlink->time_base;
361 outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio;379 outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio;
362 outlink->frame_rate = mainlink->frame_rate;380 outlink->frame_rate = mainlink->frame_rate;
381
382 if (s->guidance == OFF)
383 return 0;
384
363 if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0)385 if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0)
364 return ret;386 return ret;
365 387
@@ -383,22 +405,66 @@ static int config_output(AVFilterLink *outlink) · 17 unmodified lines
383static int activate(AVFilterContext *ctx)405static int activate(AVFilterContext *ctx)
384{406{
385 GuidedContext *s = ctx->priv;407 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;
387}433}
388 434
389static av_cold int init(AVFilterContext *ctx)435static av_cold int init(AVFilterContext *ctx)
390{436{
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
391 return 0;457 return 0;
392}458}
393 459
394static av_cold void uninit(AVFilterContext *ctx)460static av_cold void uninit(AVFilterContext *ctx)
395{461{
396 GuidedContext *s = ctx->priv;462 GuidedContext *s = ctx->priv;
397 ff_framesync_uninit(&s->fs);463 if (s->guidance == ON)
464 ff_framesync_uninit(&s->fs);
398 return;465 return;
399}466}
400 467
401
402static int process_command(AVFilterContext *ctx,468static int process_command(AVFilterContext *ctx,
403 const char *cmd,469 const char *cmd,
404 const char *arg,470 const char *arg,
@@ -414,18 +480,6 @@ static int process_command(AVFilterContext *ctx, · 9 unmodified lines
414 return 0;480 return 0;
415}481}
416 482
417static 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
429static const AVFilterPad guided_outputs[] = {483static const AVFilterPad guided_outputs[] = {
430 {484 {
431 .name = "default",485 .name = "default",
@@ -444,7 +498,7 @@ const AVFilter ff_vf_guided = { · 12 unmodified lines
444 .priv_size = sizeof(GuidedContext),498 .priv_size = sizeof(GuidedContext),
445 .priv_class = &guided_class,499 .priv_class = &guided_class,
446 .activate = activate,500 .activate = activate,
447 .inputs = guided_inputs,501 .inputs = NULL,
448 .outputs = guided_outputs,502 .outputs = guided_outputs,
449 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,503 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
450 .process_command = process_command,504 .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