diff mbox

[FFmpeg-devel] delogo filter: new "uglarm" interpolation mode added

Message ID 5f86c630-efe4-0b8b-9a68-8c624e522573@gmx.de
State Superseded
Headers show

Commit Message

Uwe Freese Dec. 26, 2018, 2:23 p.m. UTC
Hello,

so now I've taken the time to integrate the alternative interpolation 
algorithm 'uglarm' into the delogo filter.

I took the code from ffdshow's logoaway, which took over the code from 
LogoAway for VirtualDub, where I originally added it 2001. :)

I tested the code with several different options and had temporarily 
added many debug outputs (to the console) to see the internally 
calculated values. So I'm pretty confident about the correct functioning 
of the code. I also made some improvements compared to the logoaway 
filter to minimize the lookup tables (which was also a reason for me to 
test it thoroughly).

Of course, it would be nice if the addition will now also find it's way 
into the normal development branch.

The current "xy" mode tends to flicker and show horizontal and vertical 
lines, which sometimes produces an "eye catcher" at the logo area. In 
contrast, the new UGLARM mode IMHO shows a much more non-distracting 
blurred area while keeping the same simple usage (only entering the 
coordinates of the logo).

Can someone please review and add the code? Who would be primarily 
responsible to add this to libavfilter?

You can test the filter (after compiling with the attached patch for 
vf_delogo.c) with the following command to show the old xy mode and the 
new uglarm mode next to each other by:

./ffmpeg -i "yourtestvideo.mkv" -map 0:v:0 -vframes 100 -filter_complex 
"crop=200:200:100:100,split=2[L][R];[L]delogo=x=50:y=50:w=100:h=100[L2];[R]delogo=x=50:y=50:w=100:h=100:mode=uglarm:power=15[R2];[L2][R2]hstack=inputs=2" 
-c:v huffyuv delogotest.mkv && ffplay delogotest.mkv

(power can be from 0 to 30, 15 is default and need not be specified)

Here are some details of what I added and how / why:

- a new parameter "mode" is used to select the mode
   Note: It defaults to the current xy mode.
   Note: I preferred using a string / enum parameter instead of a 
boolean so the parameter could stay backwards compatible in case another 
mode would be added some day.
- a "power" value can be used to set the strength of the effect
   Note: Named "power" (and not "strength") because the distance between 
logo pixels to the border is calculated by using the power of the 
diagonal distance.
   Note: The default value results in exponent 3 (cubic), which 
corresponds to how e.g. a magnetic field is behaving and what I used all 
the years for logoaway.
- Two lookup tables (per plane) are generated at startup (if the new 
mode is set).
- Memory is cleaned up in the new "uninit" function.
- The SAR (sample aspect ratio) is considered to calculate the 
interpolated inner pixels of the logo area.
- As I saw, the filter is applied to three planes, I think it's for Y U 
V, because of the YUV color model.
   Question: How many planes can there be? I used an array of pointers 
to 10 possible lookup tables (one per plane).
   Can there be more? Are 3 enough? Is there a constant to get the max. 
number of planes at compile time?
- Only a band / border of 1 pixel is used for interpolation. This is 
because the "band" option in the delogo filter is planned to be removed 
anyway. It's also not necessary for the new mode.
- I added comments and description as I thought would be useful to 
understand the code. I hope it's ok as it's done.
- I tried to use the same formatting etc. as in the original source 
code. Is there anything I have to change?

I would be really glad for any feedback, comments, improvement 
ideas,..., and of course a review and integration of the code. :)

Of course, I would support adding documentation about the filter as 
well. Just let me know what's needed.

Best Regards,
Uwe

Am 13.12.18 um 07:55 schrieb uwe.freese@gmx.de:
> Hello Kyle,
>> You should also try vf_removelogo. It is supposedly better than the
>> more simple vf_delogo.
>
> I tried (with a box in a PNG mask as logo cover).
>
> That is no alternative both quality wise and from usability point of 
> view:
>
> 1. It gave me blurry borders with several pixels widths from outside 
> to the inner. They were very symmetrical and showed a 45 degree 
> "lines" from the edges to the middle. Hardly the nice blurred box I 
> wanted.
>
> 2. If you want to cover the logo by simple coordinates (a box), it's 
> unnecessary effort to create a PNG mask with that box in it instead of 
> giving the coordinates as parameter.
>
>
> Because removelogo can handle arbitrary masks and the calculation I 
> posted is using a simple rectangular box (and this is my typical use 
> case), I suggested to add it to the "delogo" filter.
>
> So my offer stands, would be nice if someone can add this additional 
> mode.
> [...]

Comments

Carl Eugen Hoyos Dec. 26, 2018, 3:05 p.m. UTC | #1
> Am 26.12.2018 um 15:23 schrieb Uwe Freese <uwe.freese@gmx.de>:

> so now I've taken the time to integrate the alternative interpolation algorithm 'uglarm' into the delogo filter.

Please do not re-indent existing code in this patch as this makes reviewing the changes more difficult and please do not add trailing white space as it cannot be committed to our git repository.

(I did not check if you are possibly changing the license of code you did not write yourself, please be extra careful to make sure this does not happen.)

Thank you, Carl Eugen
Uwe Freese Dec. 26, 2018, 3:48 p.m. UTC | #2
Hello,
>> so now I've taken the time to integrate the alternative interpolation algorithm 'uglarm' into the delogo filter.
> Please do not re-indent existing code in this patch as this makes reviewing the changes more difficult

Parts of the previous code are now in an "if" statement block (function 
apply_delogo), so indentation is correct. Even it would maybe be better 
to read the diff, it wouldn't be correctly indented.

In the parameter lists, spaces are added to have the same layout of the 
descriptions in the right part for all lines.

So what should I do in this case?

I could create a diff of course with "wrong" indentation (not correctly 
indented and not good to read in the final file, but the diff is better 
to read...). Would this help for the first review?

>   and please do not add trailing white space as it cannot be committed to our git repository.

OK, I'll remove them, no problem. (I'll send a new patch to this list 
after I know how to handle also the first point.)

> (I did not check if you are possibly changing the license of code you did not write yourself, please be extra careful to make sure this does not happen.)

The TimgFilterLogoaway.cpp from ffdshow also has GPL 2 and up, the same 
as vf_delogo.c from ffmpeg. -> OK.

https://sourceforge.net/p/ffdshow-tryout/code/HEAD/tree/trunk/src/imgFilters/TimgFilterLogoaway.cpp

Regards,
Uwe
Nicolas George Dec. 26, 2018, 3:56 p.m. UTC | #3
Uwe Freese (2018-12-26):
> Parts of the previous code are now in an "if" statement block (function
> apply_delogo), so indentation is correct. Even it would maybe be better to
> read the diff, it wouldn't be correctly indented.
> 
> In the parameter lists, spaces are added to have the same layout of the
> descriptions in the right part for all lines.
> 
> So what should I do in this case?
> 
> I could create a diff of course with "wrong" indentation (not correctly
> indented and not good to read in the final file, but the diff is better to
> read...). Would this help for the first review?

The practice in FFmpeg for this kind of situation is to first make a
commit without re-indenting (hence having the "wrong" indentation) and
then make a second commit with only the re-indent.

Since working on the real patch while juggling with the indentation
commit can be annoying, you can wait until the patch is in its final
form to even bother about the re-indent. You can flag the places where
it is needed with /* TODO reindent */ if you find it useful.

Regards,
Moritz Barsnick Dec. 26, 2018, 5:02 p.m. UTC | #4
On Wed, Dec 26, 2018 at 15:23:58 +0100, Uwe Freese wrote:

Just some initial remarks (no full review):

> Can someone please review and add the code? Who would be primarily 
> responsible to add this to libavfilter?

> ./ffmpeg -i "yourtestvideo.mkv" -map 0:v:0 -vframes 100 -filter_complex 
> "crop=200:200:100:100,split=2[L][R];[L]delogo=x=50:y=50:w=100:h=100[L2];[R]delogo=x=50:y=50:w=100:h=100:mode=uglarm:power=15[R2];[L2][R2]hstack=inputs=2" 
> -c:v huffyuv delogotest.mkv && ffplay delogotest.mkv

ffmpeg can display directly to screen as well, BTW.

>    Note: I preferred using a string / enum parameter instead of a 
> boolean so the parameter could stay backwards compatible in case another 
> mode would be added some day.

Perfect.

>    Question: How many planes can there be? I used an array of pointers 
> to 10 possible lookup tables (one per plane).

The "10" should be a #define, so you only need to change it in one
place if you figure out that the correct number of planes is different.
;-) (For your own ease, but also for understanding where the "10" comes
from.)

> - I tried to use the same formatting etc. as in the original source 
> code. Is there anything I have to change?

You almost did. You already heard about indentation and especially the
added whitespace. You became inconsistent in some places, such as here:

>      /* 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 },

Note how the existing original code had '{ "string",...', and you chose
to drop some whitespace. That's just cosmetic, but inconsistent. You
also have mismatching indentation.

> Of course, I would support adding documentation about the filter as 
> well. Just let me know what's needed.

You can start writing it now already, because it needs to go into
doc/filters.texi.

> + * Copyright (c) 2019 Uwe Freese <mail@uwe-freese.de>

Considering you authored it in 2018, this is forward-looking. ;-)

>      uint8_t *xdst, *xsrc;
> -
> +                
>      uint8_t *topleft, *botleft, *topright;

See, this, among others, is what we mean with whitespace. If you had
looked through your patch, you would have noticed this useless change.
(Some editors and some patch viewers point out this danglig
whitespace.)

> +            left_sample = topleft[src_linesize*(y-logo_y1)]   +
> +                        topleft[src_linesize*(y-logo_y1-1)] +
> +                        topleft[src_linesize*(y-logo_y1+1)];

The "topleft"s and the '+'s used to be column-aligned. You should
ensure that stays that way (in the indentation patch of course, if it's
indentation related).

> +            for (x = logo_x1+1,
> +                xdst = dst+logo_x1+1,
> +                xsrc = src+logo_x1+1; x < logo_x2; x++, xdst++, xsrc++) {

Spaces around operators: x = logo_x1 + 1
(Also everywhere else. Unless it's the original code, then leave it be.)

> + * @param *uglarmtable      Pointer to table containing weigths for each possible

weigths -> weights

> +static void calcUGLARMTables(double *uglarmtable, double *uglarmweightsum,
> +                             AVRational sar, int logo_w, int logo_h, int power)

No capital letters or CamelCase in function names.

> +    double e = 0.2 * power;

Could power also be a double instead of an int? Would specifying a
power of e.g. 2.5 make sense?

> +    /* uglarmtable will contain a weigth for each possible diagonal distance

weigth -> weight

> +                double d = pow(sqrt((double)(x * x * aspect * aspect + y * y)), e);

int * int * double * double + int * int
already results in a double, unless I am mistaken. No need to cast the
result.

> +    /* uglarmweithsum will conatain the sum of all weigths which is used when

uglarmweithsum -> uglarmweightsum
conatain -> contain
weigths -> weights

> +    {"mode",    "set the interpolation mode",               OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = MODE_XY},      0, 1, FLAGS, "mode"},

min and max are MODE_XY and MODE_UGLARM (or MODE_NB-1, if you code it
that way to give room for more modes).

> +        {"xy",     "use pixels in straight x any y direction",  OFFSET(mode), AV_OPT_TYPE_CONST, { .i64 = MODE_XY},     0, 0, FLAGS, "mode"},

any -> and

> +        for (int plane = 0; plane < 10; plane++) {

As mentioned, 10 could be a #define.

> +            if (s->uglarmtable[plane])
> +                av_free(s->uglarmtable[plane]);
> +            if (s->uglarmweightsum[plane])
> +                av_free(s->uglarmweightsum[plane]);

Please read the av_free() documentation. No need for a NULL check.
https://www.ffmpeg.org/doxygen/4.1/group__lavu__mem__funcs.html#ga0c9096f498624c525aa2315b8a20c411

Moritz
diff mbox

Patch

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