OpenCv read / write video color differenceOpenCv读/写视频色差
感谢博主:
有没有办法让 OpenCV 使用正确的转换??
是的,使用 GStreamer 后端而不是 FFmpeg 后端,颜色看起来很完美。默认情况下,OpenCV 不是使用 GStreamer 构建的(至少在 Windows 中不是)。我使用 GStreamer 从源代码构建 OpenCV(在下载并安装 GStreamer 之后)...
【解决方案1】:
使用 FFmpeg 后端读取视频帧时,OpenCV VideoCapture
中存在错误。
当 H.264 视频流标记为BT.709 颜色标准时,该错误会导致“颜色偏移”。
主题太重要了,无法回答...
这篇文章的重要部分是重现问题,并证明问题是真实的。
我找到的解决方案是选择 GStreamer 后端而不是 FFmpeg 后端。 建议的解决方案有缺点(例如需要构建支持 GStreamer 的 OpenCV)。
注意:
构建合成视频模式以重现问题:
我们可以使用FFmpeg 命令行工具创建合成视频作为输入。
以下命令生成一个带有 H.264 编解码器和 BT.709 颜色标准的 MP4 视频文件:
ffmpeg -y -f lavfi -src_range 1 -color_primaries bt709 -color_trc bt709 -colorspace bt709 -i testsrc=size=192x108:rate=1:duration=5 -vcodec libx264 -crf 17 -pix_fmt yuv444p -dst_range 1 -color_primaries bt709 -color_trc bt709 -colorspace bt709 -bsf:v h264_metadata=video_full_range_flag=1:colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 bt709_full_range.mp4
-bsf:v h264_metadata=video_full_range_flag=1:colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1
使用Bitstream Filter 将H.264 流标记为“全范围”BT.709。使用MediaInfo工具,我们可以查看以下颜色特征:
- colour_range: Full
- colour_primaries: BT.709
- transfer_characteristics: BT.709
- matrix_coefficients: BT.709
使用 OpenCV 捕获视频:
以下 C++ 代码抓取第一帧,并将其保存到1.png
图像文件:
- #include "opencv2/opencv.hpp"
-
- void main()
- {
- cv::VideoCapture cap("bt709_full_range.mp4");
-
- cv::Mat frame;
- cap >> frame;
-
- cv::imwrite("1.png", frame);
-
- cap.release();
- }
我们也可以使用以下 Python 代码:
- import cv2
-
- cap = cv2.VideoCapture('bt709_full_range.mp4')
- _, frame = cap.read()
- cv2.imwrite('1.png', frame)
- cap.release()
使用 FFmpeg 将bt709_full_range.mp4
转换为图像序列:
ffmpeg -i bt709_full_range.mp4 -pix_fmt rgb24 %03d.png
第一个“提取”帧的文件名为001.png
。
比较结果:
1.png
(OpenCV的结果)001.png
(FFmpeg命令行工具的结果)如您所见,颜色不同。
[232, 0, 3]
。[254, 0, 0]
.[255, 0, 0]
(由于颜色转换,值是 254)。如您所见,OpenCV 颜色是错误的!
解决方案 - 选择 GStreamer 后端而不是 FFmpeg 后端:
默认的 OpenCV 版本不包括 GStreamer 支持(至少在 Windows 中)。
您可以使用以下instruction 使用 GStreamer 构建 OpenCV。
这是一个使用 GStreamer 后端抓取第一帧的 C++ 代码示例:
- void main()
- {
- cv::VideoCapture cap("filesrc location=bt709_full_range.mp4 ! decodebin ! videoconvert ! appsink", cv::CAP_GSTREAMER);
-
- cv::Mat frame;
- cap >> frame;
-
- cv::imwrite("1g.png", frame);
-
- cap.release();
- }
结果:
1g.png
(OpenCV使用GStreamer的结果)001.png
(FFmpeg命令行工具的结果)使用 GStreamer 的 OpenCV 红色像素的值是 RGB = [254, 0, 1]
。 (由于颜色转换,蓝色为 1 而不是零)。
结论:
【讨论】:
很好,效果很好!顺便说一句,GStreamer 并不真正适合未流式传输的视频,因为它很难在给定位置检索帧。但是,出于与您介绍的相同原因,使用 MicrosoftMedia Foundation 作为后端也可以很好地工作。 cv::VideoCapture cap(video_path, cv::CAP_MSMF);