ffmpeg

FFmpeg, low quality merge using overlay filter


I am currently building a script, part of its functionality is to generate a cover art. I want to limit the scripts dependency to ffmpeg only so please don't suggest to use any other software to do the same.

For this, I am using ffmpeg to generate separate components for the coverart, code is given below:

# Draws transparent circle in correct background
ffmpeg -y -f lavfi -i "color=c=0x20292FFF:s=4000x4000,format=rgba" -filter_complex "geq=r='32':g='41':b='47':a='255*(1-between(hypot(X-2000,Y-2000),0,1150))'" -frames:v 1 -update 1 background.png

# Draws a circle gradient
ffmpeg -y -f lavfi -i "gradients=s=4000x4000:c0=0x33517e:c1=0x645098:c2=0xa53f97:c3=0xdf1177:c4=0xff033e:c5=0x2f4858:n=5:y0=750:x0=750:y1=3250:x1=3250:t=linear,format=rgba" -filter_complex "geq=g='g(X,Y)':a='255*between(hypot(X-2000,Y-2000),0,1200)'" -frames:v 1 -update 1 gradcir.png

# Draws the inner symbol
ffmpeg -y -f lavfi -i "color=c=0x20292FFF:s=4000x4000,format=rgba" -vf "geq=a='255*max(lte((X-2000+(530/3)+25)+sqrt(3)*abs(Y-2000),530)*gte(X-2000+(530/3)+25,0),between(hypot(X-2000,Y-2000),570,630))':r='255*max(lte((X-2000+(530/3)+25)+sqrt(3)*abs(Y-2000),530)*gte(X-2000+(530/3)+25,0),between(hypot(X-2000,Y-2000),570,630))':g='255*max(lte((X-2000+(530/3)+25)+sqrt(3)*abs(Y-2000),530)*gte(X-2000+(530/3)+25,0),between(hypot(X-2000,Y-2000),570,630))':b='255*max(lte((X-2000+(530/3)+25)+sqrt(3)*abs(Y-2000),530)*gte(X-2000+(530/3)+25,0),between(hypot(X-2000,Y-2000),570,630))'" -frames:v 1 shape.png

Now to merge all three pngs, I am using the following command:

ffmpeg -y -i background.png -i gradcir.png -i shape.png -filter_complex "[0][1]overlay[tmp];[tmp][2]overlay" cover.png

All the code works but the problem is that the output is severely low quality with banding and improper merges in the borders of each image.

I believe png is lossless so there shouldn't be any issue with artifacts but still I experimented with different levels of png compression but to no avail. I even tried enforcing rgba format for all three images prior to merging but to no avail.

For now I alleviated the problem by generating all images in 4000x4000 resolution, applying a deband filter and then resizing it to 1000x1000.

ffmpeg -y -i background.png -i gradcir.png -i shape.png -filter_complex "[0][1]overlay[tmp1];[tmp1][2]overlay[tmp2];[tmp2]format=rgb24,scale=2000:2000:flags=spline,deband,scale=1000:1000:flags=lanczos" -frames:v 1 -update 1 cover.png

But the above method is expensive, which is problematic for low performance systems. Can anyone find out what seems to be the issue or suggest some better way to merge it?


Solution

  • The overlay filter, by default, outputs in yuv420p pixel format, as that is the most compatible format for video players. Keep rgba using the format option of the filter.

    ffmpeg -y -i background.png -i gradcir.png -i shape.png -filter_complex "[0][1]overlay=format=auto,format=rgba[tmp];[tmp][2]overlay=format=auto,format=rgba" cover.png