0

Similar question: FFmpeg transparent PNG black outline issue

When using FFmpeg to create a GIF from an mp4, masking the video using alphamerge and a PNG-mask, and overlaying a circle outline, black lines occur on both the video and the circle in the final GIF.

From this question, some suggestions are made to add alpha=premultiplied and format=rgb,format=yuv420p in the overlay-filter. This removes the black line (on the circle), but the masking from alphamerge no longer works.

Filter used which generates black outlines:

[0][1]alphamerge[merged];
[merged][2:v]overlay=
    format=auto:
    alpha=premultiplied[withCircleOverlay];
[withCircleOverlay]scale=${size}x${size}[scaled];
[scaled]split[s0][s1];
[s0]palettegen=reserve_transparent=on:transparency_color=ffffff[p];
[s1][p]paletteuse

Filter used which removes outlines, but also cropping:

[0][1]alphamerge[merged];
[merged][2:v]overlay=
    alpha=premultiplied:
    format=rgb,format=yuv420p[withCircleOverlay];
[withCircleOverlay]scale=${size}x${size}[scaled];
[scaled]split[s0][s1];
[s0]palettegen=reserve_transparent=on:transparency_color=ffffff[p];
[s1][p]paletteuse

Command used:

ffmpeg -i movie.mp4 -i mask.png -i circle.png -filter_complex <filter> output.gif

What I've tried:

  • Changing the format-parameter on overlay to a lot of different values.
  • Changing the order of the filters.
  • Playing around with running multiple ffmpeg-passes: First doing the merging, saving this to a file, and then adding the circle afterwards. I think this in principle should work if I tinker with it enough.

Are there any ways to get around this issue?

Image with black outlines

Image with black outline

Image without outline, but also without alphamerge cropping

Image without outline, but also without alphamerge cropping

trmd
  • 21
  • 1
  • 6

1 Answers1

0

How about unpremultiply filter?

Try adding unpremultiply=inplace=1 somewhere.
Maybe after the alphamerge, or before the split, or... somewhere nice.

AND remove the alpha=premultiplied parameter from overlay.


My test result:

Off/On alpha=premultiplied * Off/On unpremultiply=inplace=1
(I'm new to this site so not allowed to embed images directly)

  • "Just Overlay": causes black outline (in other words border/boundary/fringe) on all sides of the front square.
  • "Premultiplied Overlay": seems nice on the opaque background, but leaves the black outline on the transparent one, you know.
  • "Unpremultiply Filter": fine.
  • "Unpremultiply + Premultiplied": seems nice on the transparent background, but causes extra white outline on the opaque one.

Its bash script:
(Using -loglevel warning to keep stdout calm for multiple runs of ffmpeg and -update 1 -frames:v 1 to output static images without warnings)

#!/bin/bash

# Prepare two resources
## back.png: left gold, right transparent
image_size=54
ffmpeg -loglevel warning \
  -f lavfi -i "color=color=gold:size=${image_size}x${image_size},
               format=rgba,
               crop=w=in_w/2:x=0,
               pad=w=in_w*2:x=0:color=black@0" \
  -update 1 -frames:v 1 -y back.png
## front.png: a tilted cyan square
ffmpeg -loglevel warning \
  -f lavfi -i "color=color=cyan:size=36x36,
               format=rgba,
               pad=width=${image_size}:height=out_w:x=-1:y=-1:color=black@0,
               rotate=angle=30*PI/180:fillcolor=black@0" \
  -update 1 -frames:v 1 -y front.png

# Overlay in 4 ways
ffmpeg -loglevel warning -i back.png -i front.png \
  -filter_complex "[0] [1] overlay=format=rgb" \
  -update 1 -frames:v 1 -y result0-just-overlay.png
ffmpeg -loglevel warning -i back.png -i front.png \
  -filter_complex "[0] [1] overlay=format=rgb:alpha=premultiplied" \
  -update 1 -frames:v 1 -y result1-pre.png
ffmpeg -loglevel warning -i back.png -i front.png \
  -filter_complex "[1] unpremultiply=inplace=1 [front];
                   [0] [front] overlay=format=rgb" \
  -update 1 -frames:v 1 -y result2-unpre.png
ffmpeg -loglevel warning -i back.png -i front.png \
  -filter_complex "[1] unpremultiply=inplace=1 [front];
                   [0] [front] overlay=format=rgb:alpha=premultiplied" \
  -update 1 -frames:v 1 -y result3-unpre-pre.png

# Enlarge with the Nearest Neighbor method and output combined result
scale=6
text_options="fontfile='C\:/Windows/fonts/arial.ttf':
              fontcolor=black:
              fontsize=28"
lf=$'\n'
ffmpeg -loglevel warning -i result0-just-overlay.png \
                         -i result1-pre.png \
                         -i result2-unpre.png \
                         -i result3-unpre-pre.png \
  -filter_complex "
    [0] [1] [2] [3] xstack=grid=2x2,
                    scale=width=in_w*${scale}:height=-1:flags=neighbor,
                    drawtext=text='Just${lf}Overlay':
                             x=(main_w-text_w)/2-${image_size}*${scale}/2:
                             y=(main_h-text_h)/2-${image_size}*${scale}/2:
                             ${text_options},
                    drawtext=text='Premultiplied${lf}Overlay':
                             x=(main_w-text_w)/2+${image_size}*${scale}/2:
                             y=(main_h-text_h)/2-${image_size}*${scale}/2:
                             ${text_options},
                    drawtext=text='Unpremultiply${lf}Filter':
                             x=(main_w-text_w)/2-${image_size}*${scale}/2:
                             y=(main_h-text_h)/2+${image_size}*${scale}/2:
                             ${text_options},
                    drawtext=text='Unpremultiply${lf}+${lf}Premultiplied':
                             x=(main_w-text_w)/2+${image_size}*${scale}/2:
                             y=(main_h-text_h)/2+${image_size}*${scale}/2:
                             ${text_options}
  " \
  -update 1 -frames:v 1 -y results.png

I'm surprised that it works even though the intermediate file "front.png" has the black outline:

front.png

Why...?

TBH I don't know its detailed reasons/mechanisms.
I just met the same issue and fortunately found this unpremultiply filter.


And apparently, even this filter is not a panacea.

The below script should produce an image using at most 1 color (+ 1 alpha channel) because ffmpeg just draws gold text on the gold background:

color=gold
ffmpeg -loglevel warning \
  -f lavfi -i "color=color=${color}:size=100x100, format=rgba" \
  -f lavfi -i "nullsrc=size=100x100, format=rgba" \
  -filter_complex "[1] drawtext=text='A':
                                fontfile='C\:/Windows/fonts/arial.ttf':
                                fontcolor=${color}:
                                fontsize=90:
                                x=(main_w-text_w)/2:
                                y=(main_h-text_h)/2,
                       unpremultiply=inplace=1 [text];
                   [0] [text] overlay=format=rgb" \
  -update 1 -frames:v 1 -y text.png

text.png

But, it actually results in 6 colors (+ 1 alpha).

Let ffmpeg count the number of colors:

$ ffmpeg -i text.png -vf "palettegen, metadata=mode=print" -f null - 2>&1 \
> | grep '^\[Parsed_palettegen'
[Parsed_palettegen_0 @ 000001a2fb678c00] 6(+1) colors generated out of 6 colors; ratio=1.000000

Or just visualize where the pixels of different colors are, with normalize filter:

ffmpeg -i text.png -vf "normalize" -update 1 -frames:v 1 -y text-normalize.png

text-normalize.png

Why...!?

So, I think there are probably other ways that would be more perfect.
Exparts wanted.

Phroneris
  • 1
  • 1