I'm using FFMPEG to extract thumbnails from arbitrary user-submitted videos to jpg and the outputs are noticeably darker than the input video.
Here is an image captured of a MP4 input (SMPTE Color Bars) using Mac OS's built in screen capture utility (which seems to have a reasonably accurate color representation. Red value on this image is R: 191, G: 0, B: 1.
Here is the same image captured using the FFMPEG script below. Red value on this image is R: 176, G: 0, B: 2.
Here is the ffmpeg options I'm using to extract these images:
ffmpeg -i input-video.mp4 \
-vframes 1 \
-filter:v scale=600:-1 \
-qscale:v 90 \
-f singlejpeg \
thumb.jpg
Any thoughts on how to solve this?
(Note that while there are related topics on Stack Overflow, all of them are specific to known input video types. I'm looking for a solution that will work across arbitrary inputs.)
There is a bug in FFmpeg that also applies the latest stable current release (version 5.1.2).
I was able to reproduce the issue using Windows 10, so the bug is not specific to MacOS.
[Here is a question from 2016, describing the same issue].
The solution I found is appending out_color_matrix=bt601:out_range=pc
to the scale filter:
ffmpeg -y -i color_bars.mp4 -vframes 1 -filter:v scale=600:-1:out_color_matrix=bt601:out_range=pc -qscale:v 0 thumb.jpg
out_color_matrix=bt601
- applies BT.601 as output color space.out_range=pc
- applies full range of YUV (full range is [0, 255], and in "TV range" / "limited range", the range of Y is [16, 235]).According Wikipedia JPEG color conversion applies BT.601 "full range" YUV format, and the arguments out_color_matrix=bt601:out_range=pc
were selected accordingly.
Adding out_color_matrix=bt601:out_range=pc
, forces FFmpeg to apply color format conversion that matches JPEG standard.
The bug is that by default FFmpeg doesn't apply color format conversion, and keeps the original color format, which is presumably "limited range" BT.709. Keeping the original color format (without conversion) results a "color shift".
Reproducing the issue:
color_bars.png
.ffmpeg -y -i color_bars.png -loop 100 -vcodec libx264 -vf scale=out_color_matrix=bt709:out_range=tv -crf 10 -pix_fmt yuv444p -bsf:v h264_metadata=video_full_range_flag=0:colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 color_bars.mp4
ffmpeg -y -i color_bars.mp4 -vframes 1 -filter:v scale=600:-1 -qscale:v 90 thumb.jpg
ffmpeg -y -i color_bars.mp4 -vframes 1 -filter:v scale=600:-1:out_color_matrix=bt601:out_range=pc -qscale:v 0 thumb2.jpg
Results:
thumb.jpg
- without color conversion:
The RGB values of the red pixels are [176, 0, 2].
thumb2.jpg
- with color conversion:
The RGB values of the red pixels are [190, 0, 0] (there is an acceptable rounding error).