From 9431911dbef4179663f1fc30efc4b37b696ed7d9 Mon Sep 17 00:00:00 2001
From: breaker27 <mail@uwe-freese.de>
Date: Wed, 26 Dec 2018 14:37:35 +0100
Subject: [PATCH] Add new delogo interpolation mode uglarm.
---
libavfilter/vf_delogo.c | 330 ++++++++++++++++++++++++++++++++++++------------
1 file changed, 247 insertions(+), 83 deletions(-)
@@ -2,6 +2,7 @@
* Copyright (c) 2002 Jindrich Makovicka <makovick@gmail.com>
* Copyright (c) 2011 Stefano Sabatini
* Copyright (c) 2013, 2015 Jean Delvare <jdelvare@suse.com>
+ * Copyright (c) 2019 Uwe Freese <mail@uwe-freese.de>
*
* This file is part of FFmpeg.
*
@@ -25,6 +26,9 @@
* A very simple tv station logo remover
* Originally imported from MPlayer libmpcodecs/vf_delogo.c,
* the algorithm was later improved.
+ * The "UGLARM" mode was first implemented 2001 by Uwe Freese for Virtual
+ * Dub's LogoAway filter (by Krzysztof Wojdon), taken over into ffdshow's
+ * logoaway filter (by Milan Cutka), from where it was ported to ffmpeg.
*/
#include "libavutil/common.h"
@@ -43,27 +47,32 @@
* The algorithm is only applied to the region specified by the logo
* parameters.
*
- * @param w width of the input image
- * @param h height of the input image
- * @param logo_x x coordinate of the top left corner of the logo region
- * @param logo_y y coordinate of the top left corner of the logo region
- * @param logo_w width of the logo
- * @param logo_h height of the logo
- * @param band the size of the band around the processed area
- * @param show show a rectangle around the processed area, useful for
- * parameters tweaking
- * @param direct if non-zero perform in-place processing
+ * @param w width of the input image
+ * @param h height of the input image
+ * @param logo_x x coordinate of the top left corner of the logo region
+ * @param logo_y y coordinate of the top left corner of the logo region
+ * @param logo_w width of the logo
+ * @param logo_h height of the logo
+ * @param band the size of the band around the processed area
+ * @param *uglarmtable pointer to weight table in UGLARM interpolation mode,
+ * zero when x-y mode is used
+ * @param *uglarmweightsum pointer to weight sum table in UGLARM interpolation mode,
+ * zero when x-y mode is used
+ * @param show show a rectangle around the processed area, useful for
+ * parameters tweaking
+ * @param direct if non-zero perform in-place processing
*/
static void apply_delogo(uint8_t *dst, int dst_linesize,
uint8_t *src, int src_linesize,
int w, int h, AVRational sar,
int logo_x, int logo_y, int logo_w, int logo_h,
- unsigned int band, int show, int direct)
+ unsigned int band, double *uglarmtable,
+ double *uglarmweightsum, int show, int direct)
{
int x, y;
uint64_t interp, weightl, weightr, weightt, weightb, weight;
uint8_t *xdst, *xsrc;
-
+
uint8_t *topleft, *botleft, *topright;
unsigned int left_sample, right_sample;
int xclipl, xclipr, yclipt, yclipb;
@@ -88,90 +97,204 @@ static void apply_delogo(uint8_t *dst, int dst_linesize,
dst += (logo_y1 + 1) * dst_linesize;
src += (logo_y1 + 1) * src_linesize;
-
- for (y = logo_y1+1; y < logo_y2; y++) {
- left_sample = topleft[src_linesize*(y-logo_y1)] +
- topleft[src_linesize*(y-logo_y1-1)] +
- topleft[src_linesize*(y-logo_y1+1)];
- right_sample = topright[src_linesize*(y-logo_y1)] +
- topright[src_linesize*(y-logo_y1-1)] +
- topright[src_linesize*(y-logo_y1+1)];
-
- for (x = logo_x1+1,
- xdst = dst+logo_x1+1,
- xsrc = src+logo_x1+1; x < logo_x2; x++, xdst++, xsrc++) {
-
- if (show && (y == logo_y1+1 || y == logo_y2-1 ||
- x == logo_x1+1 || x == logo_x2-1)) {
- *xdst = 0;
- continue;
+
+ if (!uglarmtable) {
+ for (y = logo_y1+1; y < logo_y2; y++) {
+ left_sample = topleft[src_linesize*(y-logo_y1)] +
+ topleft[src_linesize*(y-logo_y1-1)] +
+ topleft[src_linesize*(y-logo_y1+1)];
+ right_sample = topright[src_linesize*(y-logo_y1)] +
+ topright[src_linesize*(y-logo_y1-1)] +
+ topright[src_linesize*(y-logo_y1+1)];
+
+ for (x = logo_x1+1,
+ xdst = dst+logo_x1+1,
+ xsrc = src+logo_x1+1; x < logo_x2; x++, xdst++, xsrc++) {
+
+ if (show && (y == logo_y1+1 || y == logo_y2-1 ||
+ x == logo_x1+1 || x == logo_x2-1)) {
+ *xdst = 0;
+ continue;
+ }
+
+ /* Weighted interpolation based on relative distances, taking SAR into account */
+ weightl = (uint64_t) (logo_x2-x) * (y-logo_y1) * (logo_y2-y) * sar.den;
+ weightr = (uint64_t)(x-logo_x1) * (y-logo_y1) * (logo_y2-y) * sar.den;
+ weightt = (uint64_t)(x-logo_x1) * (logo_x2-x) * (logo_y2-y) * sar.num;
+ weightb = (uint64_t)(x-logo_x1) * (logo_x2-x) * (y-logo_y1) * sar.num;
+
+ interp =
+ left_sample * weightl
+ +
+ right_sample * weightr
+ +
+ (topleft[x-logo_x1] +
+ topleft[x-logo_x1-1] +
+ topleft[x-logo_x1+1]) * weightt
+ +
+ (botleft[x-logo_x1] +
+ botleft[x-logo_x1-1] +
+ botleft[x-logo_x1+1]) * weightb;
+ weight = (weightl + weightr + weightt + weightb) * 3U;
+ interp = ROUNDED_DIV(interp, weight);
+
+ if (y >= logo_y+band && y < logo_y+logo_h-band &&
+ x >= logo_x+band && x < logo_x+logo_w-band) {
+ *xdst = interp;
+ } else {
+ unsigned dist = 0;
+
+ if (x < logo_x+band)
+ dist = FFMAX(dist, logo_x-x+band);
+ else if (x >= logo_x+logo_w-band)
+ dist = FFMAX(dist, x-(logo_x+logo_w-1-band));
+
+ if (y < logo_y+band)
+ dist = FFMAX(dist, logo_y-y+band);
+ else if (y >= logo_y+logo_h-band)
+ dist = FFMAX(dist, y-(logo_y+logo_h-1-band));
+
+ *xdst = (*xsrc*dist + interp*(band-dist))/band;
+ }
}
- /* Weighted interpolation based on relative distances, taking SAR into account */
- weightl = (uint64_t) (logo_x2-x) * (y-logo_y1) * (logo_y2-y) * sar.den;
- weightr = (uint64_t)(x-logo_x1) * (y-logo_y1) * (logo_y2-y) * sar.den;
- weightt = (uint64_t)(x-logo_x1) * (logo_x2-x) * (logo_y2-y) * sar.num;
- weightb = (uint64_t)(x-logo_x1) * (logo_x2-x) * (y-logo_y1) * sar.num;
-
- interp =
- left_sample * weightl
- +
- right_sample * weightr
- +
- (topleft[x-logo_x1] +
- topleft[x-logo_x1-1] +
- topleft[x-logo_x1+1]) * weightt
- +
- (botleft[x-logo_x1] +
- botleft[x-logo_x1-1] +
- botleft[x-logo_x1+1]) * weightb;
- weight = (weightl + weightr + weightt + weightb) * 3U;
- interp = ROUNDED_DIV(interp, weight);
-
- if (y >= logo_y+band && y < logo_y+logo_h-band &&
- x >= logo_x+band && x < logo_x+logo_w-band) {
+ dst += dst_linesize;
+ src += src_linesize;
+ }
+ } else {
+ int bx, by;
+ double interpd;
+
+ for (y = logo_y1 + 1; y < logo_y2; y++) {
+ for (x = logo_x1 + 1,
+ xdst = dst + logo_x1 + 1,
+ xsrc = src + logo_x1 + 1; x < logo_x2; x++, xdst++, xsrc++) {
+
+ if (show && (y == logo_y1 + 1 || y == logo_y2 - 1 ||
+ x == logo_x1 + 1 || x == logo_x2 - 1)) {
+ *xdst = 0;
+ continue;
+ }
+
+ interpd = 0;
+
+ for (bx = 0; bx < logo_w; bx++) {
+ interpd += topleft[bx] *
+ uglarmtable[abs(bx - (x - logo_x1)) + (y - logo_y1) * (logo_w - 1)];
+ interpd += botleft[bx] *
+ uglarmtable[abs(bx - (x - logo_x1)) + (logo_h - (y - logo_y1) - 1) * (logo_w - 1)];
+ }
+
+ for (by = 1; by < logo_h - 1; by++) {
+ interpd += topleft[by * src_linesize] *
+ uglarmtable[(x - logo_x1) + abs(by - (y - logo_y1)) * (logo_w - 1)];
+ interpd += topleft[by * src_linesize + (logo_w - 1)] *
+ uglarmtable[logo_w - (x - logo_x1) - 1 + abs(by - (y - logo_y1)) * (logo_w - 1)];
+ }
+
+ interp = (uint64_t)(interpd /
+ uglarmweightsum[(x - logo_x1) - 1 + (y - logo_y1 - 1) * (logo_w - 2)]);
*xdst = interp;
- } else {
- unsigned dist = 0;
-
- if (x < logo_x+band)
- dist = FFMAX(dist, logo_x-x+band);
- else if (x >= logo_x+logo_w-band)
- dist = FFMAX(dist, x-(logo_x+logo_w-1-band));
-
- if (y < logo_y+band)
- dist = FFMAX(dist, logo_y-y+band);
- else if (y >= logo_y+logo_h-band)
- dist = FFMAX(dist, y-(logo_y+logo_h-1-band));
+ }
+
+ dst += dst_linesize;
+ src += src_linesize;
+ }
+ }
+}
- *xdst = (*xsrc*dist + interp*(band-dist))/band;
+/**
+ * Calculate the lookup tables to be used in UGLARM interpolation mode.
+ *
+ * @param *uglarmtable Pointer to table containing weigths for each possible
+ * diagonal distance between a border pixel and an inner
+ * logo pixel.
+ * @param *uglarmweightsum Pointer to a table containing the weight sum to divide
+ * by for each pixel within the logo area.
+ * @param sar The sar to take into account when calculating lookup
+ * tables.
+ * @param logo_w width of the logo
+ * @param logo_h height of the logo
+ * @param power power of uglarm interpolation
+ */
+static void calcUGLARMTables(double *uglarmtable, double *uglarmweightsum,
+ AVRational sar, int logo_w, int logo_h, int power)
+{
+ double e = 0.2 * power;
+ double aspect = (double)sar.num / sar.den;
+ int x, y;
+
+ /* uglarmtable will contain a weigth for each possible diagonal distance
+ * between a border pixel and an inner logo pixel. The maximum distance in
+ * each direction between border and an inner pixel can be logo_w - 1. The
+ * weight of a border pixel which is x,y pixels away is stored at position
+ * x + y * (logo_w - 1). */
+ for (y = 0; y < logo_h - 1; y++)
+ for (x = 0; x < logo_w - 1; x++) {
+ if (x + y != 0) {
+ double d = pow(sqrt((double)(x * x * aspect * aspect + y * y)), e);
+ uglarmtable[x + y * (logo_w - 1)] = 1.0 / d;
+ } else {
+ uglarmtable[x + y * (logo_w - 1)] = 1.0;
}
}
- dst += dst_linesize;
- src += src_linesize;
- }
+ /* uglarmweithsum will conatain the sum of all weigths which is used when
+ * an inner pixel of the logo at position x,y is calculated out of the
+ * border pixels. The aggregated value has to be divided by that. The value
+ * to use for the inner 1-based logo position x,y is stored at
+ * (x - 1) + (y - 1) * (logo_w - 2). */
+ for (y = 1; y < logo_h - 1; y++)
+ for (x = 1; x < logo_w - 1; x++) {
+ double weightsum = 0;
+
+ for (int bx = 0; bx < logo_w; bx++) {
+ /* top border */
+ weightsum += uglarmtable[abs(bx - x) + y * (logo_w - 1)];
+ /* bottom border */
+ weightsum += uglarmtable[abs(bx - x) + (logo_h - y - 1) * (logo_w - 1)];
+ }
+
+ for (int by = 1; by < logo_h - 1; by++) {
+ /* left border */
+ weightsum += uglarmtable[x + abs(by - y) * (logo_w - 1)];
+ /* right border */
+ weightsum += uglarmtable[(logo_w - x - 1) + abs(by - y) * (logo_w - 1)];
+ }
+
+ uglarmweightsum[(x - 1) + (y - 1) * (logo_w - 2)] = weightsum;
+ }
}
+enum mode {
+ MODE_XY,
+ MODE_UGLARM
+};
+
typedef struct DelogoContext {
const AVClass *class;
- int x, y, w, h, band, show;
-} DelogoContext;
+ int x, y, w, h, band, mode, power, show;
+ double *uglarmtable[10], *uglarmweightsum[10];
+} DelogoContext;
#define OFFSET(x) offsetof(DelogoContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption delogo_options[]= {
- { "x", "set logo x position", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
- { "y", "set logo y position", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
- { "w", "set logo width", OFFSET(w), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
- { "h", "set logo height", OFFSET(h), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
+ { "x", "set logo x position", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
+ { "y", "set logo y position", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
+ { "w", "set logo width", OFFSET(w), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
+ { "h", "set logo height", OFFSET(h), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
#if LIBAVFILTER_VERSION_MAJOR < 7
/* Actual default value for band/t is 1, set in init */
- { "band", "set delogo area band size", OFFSET(band), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
- { "t", "set delogo area band size", OFFSET(band), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
+ { "band", "set delogo area band size", OFFSET(band), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
+ { "t", "set delogo area band size", OFFSET(band), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
#endif
- { "show", "show delogo area", OFFSET(show), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, FLAGS },
+ {"mode", "set the interpolation mode", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = MODE_XY}, 0, 1, FLAGS, "mode"},
+ {"xy", "use pixels in straight x any y direction", OFFSET(mode), AV_OPT_TYPE_CONST, { .i64 = MODE_XY}, 0, 0, FLAGS, "mode"},
+ {"uglarm", "UGLARM mode, use full border", OFFSET(mode), AV_OPT_TYPE_CONST, { .i64 = MODE_UGLARM}, 0, 0, FLAGS, "mode"},
+ { "power", "power of UGLARM interpolation", OFFSET(power), AV_OPT_TYPE_INT, { .i64 = 15 }, 0, 30, FLAGS },
+ { "show", "show delogo area", OFFSET(show), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, FLAGS },
{ NULL }
};
@@ -215,8 +338,8 @@ static av_cold int init(AVFilterContext *ctx)
#else
s->band = 1;
#endif
- av_log(ctx, AV_LOG_VERBOSE, "x:%d y:%d, w:%d h:%d band:%d show:%d\n",
- s->x, s->y, s->w, s->h, s->band, s->show);
+ av_log(ctx, AV_LOG_VERBOSE, "x:%d y:%d, w:%d h:%d band:%d mode:%d power:%d show:%d\n",
+ s->x, s->y, s->w, s->h, s->band, s->mode, s->power, s->show);
s->w += s->band*2;
s->h += s->band*2;
@@ -226,6 +349,21 @@ static av_cold int init(AVFilterContext *ctx)
return 0;
}
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ DelogoContext *s = ctx->priv;
+
+ if (s->mode == MODE_UGLARM)
+ {
+ for (int plane = 0; plane < 10; plane++) {
+ if (s->uglarmtable[plane])
+ av_free(s->uglarmtable[plane]);
+ if (s->uglarmweightsum[plane])
+ av_free(s->uglarmweightsum[plane]);
+ }
+ }
+}
+
static int config_input(AVFilterLink *inlink)
{
DelogoContext *s = inlink->dst->priv;
@@ -273,17 +411,42 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
for (plane = 0; plane < desc->nb_components; plane++) {
int hsub = plane == 1 || plane == 2 ? hsub0 : 0;
int vsub = plane == 1 || plane == 2 ? vsub0 : 0;
+
+ /* Up and left borders were rounded down, inject lost bits
+ * into width and height to avoid error accumulation */
+ int logo_w = AV_CEIL_RSHIFT(s->w + (s->x & ((1<<hsub)-1)), hsub);
+ int logo_h = AV_CEIL_RSHIFT(s->h + (s->y & ((1<<vsub)-1)), vsub);
+
+ /* Init lookup tables once */
+ if ((s->mode == MODE_UGLARM) && (!s->uglarmtable[plane])) {
+ s->uglarmtable[plane] =
+ (double*)av_malloc((logo_w - 1) * (logo_h - 1) * sizeof(double));
+
+ if (!s->uglarmtable[plane]) {
+ return AVERROR(ENOMEM);
+ }
+
+ s->uglarmweightsum[plane] =
+ (double*)av_malloc((logo_w - 2) * (logo_h - 2) * sizeof(double));
+
+ if (!s->uglarmweightsum[plane]) {
+ return AVERROR(ENOMEM);
+ }
+
+ calcUGLARMTables(s->uglarmtable[plane],
+ s->uglarmweightsum[plane],
+ sar, logo_w, logo_h, s->power);
+ }
apply_delogo(out->data[plane], out->linesize[plane],
in ->data[plane], in ->linesize[plane],
AV_CEIL_RSHIFT(inlink->w, hsub),
AV_CEIL_RSHIFT(inlink->h, vsub),
sar, s->x>>hsub, s->y>>vsub,
- /* Up and left borders were rounded down, inject lost bits
- * into width and height to avoid error accumulation */
- AV_CEIL_RSHIFT(s->w + (s->x & ((1<<hsub)-1)), hsub),
- AV_CEIL_RSHIFT(s->h + (s->y & ((1<<vsub)-1)), vsub),
+ logo_w, logo_h,
s->band>>FFMIN(hsub, vsub),
+ s->uglarmtable[plane],
+ s->uglarmweightsum[plane],
s->show, direct);
}
@@ -317,6 +480,7 @@ AVFilter ff_vf_delogo = {
.priv_size = sizeof(DelogoContext),
.priv_class = &delogo_class,
.init = init,
+ .uninit = uninit,
.query_formats = query_formats,
.inputs = avfilter_vf_delogo_inputs,
.outputs = avfilter_vf_delogo_outputs,
--
2.11.0