【问题标题】:Why is ffmpeg's conversion to YUV420 so poor?为什么ffmpeg转YUV420这么差?
【发布时间】:2020-11-08 10:55:07
【问题描述】:

我一直在使用ffmpeg 和其他压缩工具来比较 YUV420 重采样视频的速率失真曲线。 在这些比较中,ffmpeg 的结果始终较差,PSNR 值低 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!

这带来了两个问题:

  1. 为什么ffmpeg 默认不实现与yuv420p 的更好转换?
  2. 有没有更简单的方法来获得或表达这种改进的转换?

【问题讨论】:

  • RGB 8 位 -> YUV420 -> RGB 8 位根本不无损,RGB 8 位 -> YUV420 10 位 -> RGB 8 位。

标签: ffmpeg


【解决方案1】:

经过实验,我确实找到了两种解决方法:

(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->yuv420pyuv420p->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

不幸的是,这种行为不是默认的,但至少有一种半方便的方式来访问它。

【讨论】:

  • 在尝试从一批 png 文件中输出 yuv420p 像素格式的 h264 视频时,有没有办法应用这些变通方法?示例命令:ffmpeg -y -framerate 30 -i '%03d.png' -c:v libx264 -preset veryslow -vf format=yuv420p -crf 1 output.mp4
猜你喜欢
  • 2021-11-17
  • 2014-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-20
  • 2020-12-18
  • 2013-05-20
相关资源
最近更新 更多