为什么ffmpeg';s转换到YUV420这么差?
我一直在使用为什么ffmpeg';s转换到YUV420这么差?,ffmpeg,Ffmpeg,我一直在使用ffmpeg和其他压缩工具来比较YUV420重采样视频的率失真曲线。 在这些比较中,来自ffmpeg的结果总是更差,峰值信噪比值低0.5-1.0 dB 我跟踪了问题到ffmpeg在RGB和YUV420之间的转换。 为了简化,让我们假设“无损压缩”,因此只考虑RGB-> YUV420-> RGB。 此外,我们对单个PNG图像帧进行操作 # Use some default options. ffmpeg="ffmpeg -nostdin -hide_banner -v err
ffmpeg
和其他压缩工具来比较YUV420重采样视频的率失真曲线。
在这些比较中,来自ffmpeg
的结果总是更差,峰值信噪比值低0.5-1.0 dB
我跟踪了问题到ffmpeg
在RGB和YUV420之间的转换。
为了简化,让我们假设“无损压缩”,因此只考虑RGB-> YUV420-> RGB。
此外,我们对单个PNG图像帧进行操作
# Use some default options.
ffmpeg="ffmpeg -nostdin -hide_banner -v error"
# Obtain a source image.
wget -nv -O original.png https://i.stack.imgur.com/8J1qY.png
size="256x256"
# Compare it with itself to verify that we get an infinite average PSNR.
$ffmpeg -v info -i original.png -i original.png -lavfi psnr -f null - |& grep PSNR
# average:inf
# Convert the image to YUV420, and convert back to RGB.
$ffmpeg -i original.png -pix_fmt yuv420p -f rawvideo -y temp1.yuv420
$ffmpeg -f rawvideo -s $size -pix_fmt yuv420p -i temp1.yuv420 -y result1.png
# Compare it with the original image to measure the PSNR (in dB).
$ffmpeg -v info -i result1.png -i original.png -lavfi psnr -f null - |& grep PSNR
# average:36.894551
现在,作为替代方案,我们手动执行RGB YUV420色度重采样:
yuv444_to_yuv420="extractplanes=y+u+v[y][u][v];\
[u]scale=w=iw/2:h=ih/2:flags=area[u];\
[v]scale=w=iw/2:h=ih/2:flags=area[v];\
[y][u][v]mergeplanes=0x001020:yuv420p"
yuv420_to_rgb="extractplanes=y+u+v[y][u][v];\
[u]scale=w=iw*2:h=ih*2:flags=neighbor[u];\
[v]scale=w=iw*2:h=ih*2:flags=neighbor[v];\
[y][u][v]mergeplanes=0x001020:yuv444p,format=rgb24"
$ffmpeg -i original.png -pix_fmt yuv444p -f rawvideo - | \
$ffmpeg -f rawvideo -pix_fmt yuv444p -s $size -i - \
-lavfi "$yuv444_to_yuv420" -f rawvideo -y temp2.yuv420
$ffmpeg -f rawvideo -pix_fmt yuv420p -s $size -i temp2.yuv420 \
-lavfi "$yuv420_to_rgb" -y result2.png
# Measure PSNR by comparing with the original image.
$ffmpeg -v info -i result2.png -i original.png -lavfi psnr -f null - |& grep PSNR
# average:37.536444
# This is an improvement of 0.64 dB!
这带来了两个问题:
ffmpeg
不实现与yuv420p
的更好转换经过实验,我确实找到了两个解决办法: (1) 参数
-sws_flags
设置过滤器图中所有隐式引入的swscale
过滤器的参数。
这些过滤器似乎也负责YUV420转换。
使用精心选择的过滤器标志,它可以:
$ffmpeg -i original.png -sws_flags 'area+accurate_rnd+full_chroma_int' \
-pix_fmt yuv420p -f rawvideo -y temp1.yuv420
$ffmpeg -f rawvideo -s $size -pix_fmt yuv420p -i temp1.yuv420 \
-sws_flags 'neighbor+accurate_rnd+full_chroma_int' -y result1.png
$ffmpeg -v info -i result1.png -i original.png -lavfi psnr -f null - |& grep PSNR
# average:37.567842
(2) 还可以将过滤选项指定为scale
过滤器(默认为100%比例)的参数,该过滤器根据下一个图形节点请求的输入格式执行rgb24->yuv420p
或yuv420p->rgb24
格式转换:
$ffmpeg -v info -i original.png \
-lavfi 'scale=flags=area+accurate_rnd+full_chroma_int,format=yuv420p' \
-f rawvideo -y temp1.yuv420
$ffmpeg -v info -f rawvideo -s $size -pix_fmt yuv420p -i temp1.yuv420 \
-lavfi 'scale=flags=neighbor+accurate_rnd+full_chroma_int' -y result1.png
$ffmpeg -v info -i result1.png -i original.png -lavfi psnr -f null - |& grep PSNR
# average:37.567842
不幸的是,这种行为不是默认的,但至少有一种半方便的方式来访问它