1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-01-24 13:56:33 +02:00

GSoC: Support fast guided filter.

Two modes are supported in guided filter, basic mode and fast mode.
Basic mode is the initial pushed guided filter without optimization.
Fast mode is implemented based on the basic one by sub-sampling method.
The sub-sampling ratio which can be defined by users controls the
algorithm complexity. The larger the sub-sampling ratio, the lower
the algorithm complexity.

Signed-off-by: Xuewei Meng <xwmeng96@gmail.com>
Reviewed-by: Steven Liu <liuqi05@kuaishou.com>
This commit is contained in:
Xuewei Meng 2021-05-10 21:42:31 +08:00 committed by Steven Liu
parent b5ea0980c5
commit 43d70feb78
2 changed files with 85 additions and 39 deletions

View File

@ -12963,12 +12963,22 @@ Apply guided filter for edge-preserving smoothing, dehazing and so on.
The filter accepts the following options: The filter accepts the following options:
@table @option @table @option
@item radius @item radius
Set the radius in pixels. Set the box radius in pixels.
Allowed range is 1 to 20. Default is 3. Allowed range is 1 to 20. Default is 3.
@item eps @item eps
Set regularization parameter. Set regularization parameter (with square).
Allowed range is 0 to 1. Default is 0.1. Allowed range is 0 to 1. Default is 0.01.
@item mode
Set filter mode. Can be @code{basic} or @code{fast}.
Default is @code{basic}.
@item sub
Set subsampling ratio.
Allowed range is 1 to 64.
Default is always 1 for @code{basic} value of @var{mode} option,
and 4 for @code{fast} value of @var{mode} option.
@item planes @item planes
Set planes to filter. Default is first only. Set planes to filter. Default is first only.
@ -12987,8 +12997,8 @@ ffmpeg -i in.png -i in.png -filter_complex guided out.png
@item @item
Dehazing, structure-transferring filtering, detail enhancement with guided filter. Dehazing, structure-transferring filtering, detail enhancement with guided filter.
For the generation of guidance image, For the generation of guidance image, refer to paper "Guided Image Filtering".
see @url{http://kaiminghe.com/publications/pami12guidedfilter.pdf}. See: @url{http://kaiminghe.com/publications/pami12guidedfilter.pdf}.
@example @example
ffmpeg -i in.png -i guidance.png -filter_complex guided out.png ffmpeg -i in.png -i guidance.png -filter_complex guided out.png
@end example @end example

View File

@ -27,12 +27,20 @@
#include "internal.h" #include "internal.h"
#include "video.h" #include "video.h"
enum FilterModes {
BASIC,
FAST,
NB_MODES,
};
typedef struct GuidedContext { typedef struct GuidedContext {
const AVClass *class; const AVClass *class;
FFFrameSync fs; FFFrameSync fs;
int radius; int radius;
float eps; float eps;
int mode;
int sub;
int planes; int planes;
@ -51,9 +59,13 @@ typedef struct GuidedContext {
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
static const AVOption guided_options[] = { static const AVOption guided_options[] = {
{ "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64=3 }, 1, 20, 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 }, { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, FLAGS },
{ "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1 }, 0, 0xF, FLAGS }, { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, 0, 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", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 1 }, 1, 64, FLAGS },
{ "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1 }, 0, 0xF, FLAGS },
{ NULL } { NULL }
}; };
@ -147,6 +159,26 @@ static int config_input(AVFilterLink *inlink)
return AVERROR(EINVAL); return AVERROR(EINVAL);
} }
if (s->mode == BASIC) {
if (s->sub != 1) {
av_log(ctx, AV_LOG_WARNING, "Subsampling ratio is 1 in basic mode.\n");
s->sub = 1;
}
}
else if (s->mode == FAST) {
if (s->sub == 1) {
av_log(ctx, AV_LOG_WARNING, "Subsampling ratio is larger than 1 in fast mode.\n");
s->sub = 4;
}
if (s->radius >= s->sub)
s->radius = s->radius / s->sub;
else {
s->radius = 1;
}
}
else {
return AVERROR_BUG;
}
s->depth = desc->comp[0].depth; s->depth = desc->comp[0].depth;
s->width = ctx->inputs[0]->w; s->width = ctx->inputs[0]->w;
@ -174,6 +206,10 @@ static int guided_##name(AVFilterContext *ctx, GuidedContext *s,
const type *src = (const type *)ssrc; \ const type *src = (const type *)ssrc; \
const type *srcRef = (const type *)ssrcRef; \ const type *srcRef = (const type *)ssrcRef; \
\ \
int sub = s->sub; \
int h = (height % sub) == 0 ? height / sub : height / sub + 1; \
int w = (width % sub) == 0 ? width / sub : width / sub + 1; \
\
ThreadData t; \ ThreadData t; \
const int nb_threads = ff_filter_get_nb_threads(ctx); \ const int nb_threads = ff_filter_get_nb_threads(ctx); \
float *I; \ float *I; \
@ -189,55 +225,55 @@ static int guided_##name(AVFilterContext *ctx, GuidedContext *s,
float *meanA; \ float *meanA; \
float *meanB; \ float *meanB; \
\ \
I = av_calloc(width * height, sizeof(float)); \ I = av_calloc(w * h, sizeof(float)); \
II = av_calloc(width * height, sizeof(float)); \ II = av_calloc(w * h, sizeof(float)); \
P = av_calloc(width * height, sizeof(float)); \ P = av_calloc(w * h, sizeof(float)); \
IP = av_calloc(width * height, sizeof(float)); \ IP = av_calloc(w * h, sizeof(float)); \
meanI = av_calloc(width * height, sizeof(float)); \ meanI = av_calloc(w * h, sizeof(float)); \
meanII = av_calloc(width * height, sizeof(float)); \ meanII = av_calloc(w * h, sizeof(float)); \
meanP = av_calloc(width * height, sizeof(float)); \ meanP = av_calloc(w * h, sizeof(float)); \
meanIP = av_calloc(width * height, sizeof(float)); \ meanIP = av_calloc(w * h, sizeof(float)); \
\ \
A = av_calloc(width * height, sizeof(float)); \ A = av_calloc(w * h, sizeof(float)); \
B = av_calloc(width * height, sizeof(float)); \ B = av_calloc(w * h, sizeof(float)); \
meanA = av_calloc(width * height, sizeof(float)); \ meanA = av_calloc(w * h, sizeof(float)); \
meanB = av_calloc(width * height, sizeof(float)); \ meanB = av_calloc(w * h, sizeof(float)); \
\ \
if (!I || !II || !P || !IP || !meanI || !meanII || !meanP || \ if (!I || !II || !P || !IP || !meanI || !meanII || !meanP || \
!meanIP || !A || !B || !meanA || !meanB){ \ !meanIP || !A || !B || !meanA || !meanB){ \
ret = AVERROR(ENOMEM); \ ret = AVERROR(ENOMEM); \
goto end; \ goto end; \
} \ } \
for (int i = 0;i < height;i++) { \ for (int i = 0;i < h;i++) { \
for (int j = 0;j < width;j++) { \ for (int j = 0;j < w;j++) { \
int x = i * width + j; \ int x = i * w + j; \
I[x] = src[i * src_stride + j] / maxval; \ I[x] = src[(i * src_stride + j) * sub] / maxval; \
II[x] = I[x] * I[x]; \ II[x] = I[x] * I[x]; \
P[x] = srcRef[i * src_ref_stride + j] / maxval; \ P[x] = srcRef[(i * src_ref_stride + j) * sub] / maxval; \
IP[x] = I[x] * P[x]; \ IP[x] = I[x] * P[x]; \
} \ } \
} \ } \
\ \
t.width = width; \ t.width = w; \
t.height = height; \ t.height = h; \
t.srcStride = width; \ t.srcStride = w; \
t.dstStride = width; \ t.dstStride = w; \
t.src = I; \ t.src = I; \
t.dst = meanI; \ t.dst = meanI; \
ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(height, nb_threads)); \ ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(h, nb_threads)); \
t.src = II; \ t.src = II; \
t.dst = meanII; \ t.dst = meanII; \
ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(height, nb_threads)); \ ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(h, nb_threads)); \
t.src = P; \ t.src = P; \
t.dst = meanP; \ t.dst = meanP; \
ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(height, nb_threads)); \ ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(h, nb_threads)); \
t.src = IP; \ t.src = IP; \
t.dst = meanIP; \ t.dst = meanIP; \
ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(height, nb_threads)); \ ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(h, nb_threads)); \
\ \
for (int i = 0;i < height;i++) { \ for (int i = 0;i < h;i++) { \
for (int j = 0;j < width;j++) { \ for (int j = 0;j < w;j++) { \
int x = i * width + j; \ int x = i * w + j; \
float varI = meanII[x] - (meanI[x] * meanI[x]); \ float varI = meanII[x] - (meanI[x] * meanI[x]); \
float covIP = meanIP[x] - (meanI[x] * meanP[x]); \ float covIP = meanIP[x] - (meanI[x] * meanP[x]); \
A[x] = covIP / (varI + eps); \ A[x] = covIP / (varI + eps); \
@ -247,14 +283,14 @@ static int guided_##name(AVFilterContext *ctx, GuidedContext *s,
\ \
t.src = A; \ t.src = A; \
t.dst = meanA; \ t.dst = meanA; \
ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(height, nb_threads)); \ ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(h, nb_threads)); \
t.src = B; \ t.src = B; \
t.dst = meanB; \ t.dst = meanB; \
ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(height, nb_threads)); \ ctx->internal->execute(ctx, s->box_slice, &t, NULL, FFMIN(h, nb_threads)); \
\ \
for (int i = 0;i < height;i++) { \ for (int i = 0;i < height;i++) { \
for (int j = 0;j < width;j++) { \ for (int j = 0;j < width;j++) { \
int x = i * width + j; \ int x = i / sub * w + j / sub; \
dst[i * dst_stride + j] = meanA[x] * src[i * src_stride + j] + \ dst[i * dst_stride + j] = meanA[x] * src[i * src_stride + j] + \
meanB[x] * maxval; \ meanB[x] * maxval; \
} \ } \