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(-)

diff --git a/libavfilter/vf_delogo.c b/libavfilter/vf_delogo.c
index 065d093641..12b4c5d2df 100644
--- a/libavfilter/vf_delogo.c
+++ b/libavfilter/vf_delogo.c
@@ -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

