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.