How is PSNR calculated when the reference file is RGB and the encoded file is YUV?
Given a 16 bpc RGB PNG sequence, if I encode it with:
ffmpeg -i regular%04d.png -c:v libx265 -pix_fmt gbrp12le -crf 1 out_gbrp12le.mp4
and then I encode that output into a lossless codec with a bigger pixel format:
ffmpeg -i .\out_gbrp12le.mp4 -c:v ffv1 -pix_fmt rgb48le libx265_gbrp12le_to_rgb48le.mkv
If I create a reference file with:
ffmpeg -i regular%04d.png -vcodec rawvideo -pix_fmt rgb48le lossless.nut
and I compare a plot of the two PSNRs to this reference file , they are extremely close:
This makes sense to me. gbrp12le is basically a subset of rgb48le so the PSNR plots should be identical. They are off slightly here, but it could just be rounding; no big deal.
However, as soon as I move to YUV, something odd happens. Take this example:
ffmpeg -i regular%04d.png -c:v libx265 -pix_fmt yuv444p12le -color_range pc -crf 1 out_yuv444p12le_pc.mp4
and then I encode that output with
ffmpeg -i .\out_yuv444p12le_pc.mp4 -c:v ffv1 -pix_fmt rgb48le libx265_yuv444p12le_pc_to_rgb48le.mkv
I get a notable difference in the PSNR values.
Why is there a difference? In order to calculate the PSNR, both files have to converted to the same pixel format, right? So when the reference is an RGB file and the encoded file is YUV, is ffmetrics converting the reference from RGB to YUV, or is it converting the encoded file from YUV to RGB?
Or is it something else entirely? I don't understand what's happening behind the scenes that causes a gap in the second chart when there is no gap in the first chart.
I did some testing. Running ffmpeg myself, I found I was able to recreate those exact plot values with these commands:
ffmpeg -r 60 -i out_yuv444p12le_pc.mp4 -r 60 -i lossless.nut -lavfi "[0:v]setpts=N[out0];[1:v]setpts=N[out1];[out0][out1]psnr=stats_file={inpt}_psnr_logfile.txt" -f null -
gave me 76.26 for the first frame (matching the red line), and
ffmpeg -r 60 -i out_yuv444p12le_pc.mp4 -r 60 -i lossless.nut -lavfi "[0:v]format=rgb48le,setpts=N[out0];[1:v]setpts=N[out1];[out0][out1]psnr=stats_file={inpt}_psnr_logfile_force_rgb.txt" -f null -
gave me 72.83 for the first frame, matching the pink line.
So I'm guessing ffmpeg is converting the reference RGB file to YUV rather than the other way around.
For me, I have found this confusing and encountered several unintuitive results when using ffmetrics to compare files of different pixel formats. I think I'd rather have the encoded files converted to the same pixel_format format as the reference file, rather than the reference file converted to the pixel format of the encoded file.
Do you think it makes sense to give the user the option to choose how the different pixel format conversions are handled?
My thought is that you could add a simple toggle under the options menu. Leaving it disabled will continue to use the current behavior but enabling it would force the encoded files to match the pixel format of the reference file.
I would find this really helpful. I realize I this may be a little advanced, but it makes sense to me to have this option.
Yep. FFMetrics do not do anything atm in this case. Check limitations section.
This is in my todo@, but no ETA, sorry.
P.S. You can always find out what FFMpeg commands were issued by FFMetrics: turn on option 'Write ffmpeg commands into log' and analyze FFMetrics.log.
Yep. FFMetrics do not do anything atm in this case. Check limitations section.
This is in my todo@, but no ETA, sorry.
P.S. You can always find out what FFMpeg commands were issued by FFMetrics: turn on option 'Write ffmpeg commands into log' and analyze FFMetrics.log.
OK, thanks. You said it's in your todo, so I take it you were already considering this? How were you envisioning it working?
FFMetrics already getting media info for all files (including colour space & range). I thought it will add colour conversion into ffmpeg mertic calculation command to convert distorted back into original colour space (the same logic as with different frame size). At this stage I have not investigated if this can be done.
I think it should be doable. You should be able to get the pixel format of the reference file with:
ffprobe -v error -select_streams v:0 -show_entries stream=pix_fmt -of default=noprint_wrappers=1:nokey=1 reference.mov
and you can convert the distorted file to that pixel format with:
ffmpeg ... -lavfi "[0:v]format=reference_file_pix_fmt,setpts=N[out0];[1:v]setpts=N[out1];[out0][out1]psnr=stats_file=psnr_logfile.txt" -f null -
or
ffmpeg ... -lavfi "[0:v]format=reference_file_pix_fmt,setpts=N[out0];[1:v]setpts=N[out1];[out0][out1]ssim=stats_file=ssim_logfile.txt" -f null -
It's possible I'm overlooking something but this seems to have worked for me so far.
Hi, I noticed this text on the v1.6.0 beta 1 release:
New: Automatic pixel format conversion implemented. A list of currently supported formats: yuv420p (default), yuv422p, yuv444p. If ref's pixel format is not on the supported list, it will be converted to default format. Distorted file will be converted to ref's pixel format.
It seems like you're attempting to address what I brought up here. Am I correct? If so, do you plan to add other pixel formats to this list in the future? I'm particularly interested in support for RGB/BGR pixel formats (at varying bit depths) for the reference file. Thanks.
It seems like you're attempting to address what I brought up here. Am I correct?
The issue that metric calculation can be not correct if files are in different pixel formats is known for me from very beginning. Your case is only one of many possible combinations. So I'm not trying to only fix your issue, but all possible combinations. However, it might give better results for your case as well. As for convertion to RGB -- If I remember correctly, VMAF require YUV, not RGB so at least for this metric it will not be possible to have what you want. I have plans to introduce a list of supported pixel formats per metric. In this case if metric support RGB and the source is in this format, then distorted will be converted to the format. But I will need to investigate first if PSNR and SSIM supports RGB and have the same output. Short term solution to make it better might be: convert RGB to yuv444 instead of yuv420
It seems like you're attempting to address what I brought up here. Am I correct?
The issue that metric calculation can be not correct if files are in different pixel formats is known for me from very beginning. Your case is only one of many possible combinations. So I'm not trying to only fix your issue, but all possible combinations. However, it might give better results for your case as well. As for convertion to RGB -- If I remember correctly, VMAF require YUV, not RGB so at least for this metric it will not be possible to have what you want. I have plans to introduce a list of supported pixel formats per metric. In this case if metric support RGB and the source is in this format, then distorted will be converted to the format. But I will need to investigate first if PSNR and SSIM supports RGB and have the same output. Short term solution to make it better might be: convert RGB to yuv444 instead of yuv420
Have you had a chance to look at adding support for RGB pixel formats? It would be ideal if the reference file isn't converted to YUV. Whenever an RGB reference file is used, VMAF could be disabled.
Sorry, I was distracted by some other stuff (job, personal life and other FFMetrics issues) and do not remember at what stage the colour conversion is atm. As I mentioned earlier, I'm planning to implement automatic colour spaces conversion. Will try to check it out when I have time and report back.