OpenCv 读/写视频色差
OpenCv read / write video color difference
我正在尝试使用 openCV 简单地打开视频,处理帧并将处理后的帧写入新的视频文件。
我的问题是,即使我根本不处理帧(只是打开视频,使用 VideoCapture 读取帧并使用 VideoWriter 将它们写入新文件),输出文件看起来比输入。
执行此操作的代码可以在任何 openCV 教程中找到,没什么特别的。
我在 Windows 10 上使用 openCV c++ 4.4.0。
我通过 opencv_videoio_ffmpeg440_64.dll 将 openCV 与 ffmpeg 结合使用
输入视频是mp4。
我用 huffyuv 编解码器将输出写为 .avi :
m_video_writer.reset(new cv::VideoWriter(m_save_video_path.toStdString(), cv::VideoWriter::fourcc('H', 'F', 'Y', 'U'), // lossless compression
m_model->getFps(), cv::Size(m_frame_size.width(), m_frame_size.height())));
我尝试了许多其他编解码器,问题仍然存在。
像素差异很小,值不是恒定的,但总是以相同的方式变化:蓝色通道较低,红色和绿色通道较高。
奇怪的事实:当我用 opencv 打开输入或输出视频时,矩阵实际上完全相同。所以我猜问题出在阅读上??
这是每个视频文件的属性,使用 Windows Media Playre (MPC-HC) 导出。
VS
我应该调查什么?
谢谢!!
完整代码在这里(复制我视频的前 100 帧):
VideoCapture original("C:/Users/axelle/Videos/original.MP4");
int frame_height = original.get(CAP_PROP_FRAME_HEIGHT);
int frame_width = original.get(CAP_PROP_FRAME_WIDTH);
int fps = original.get(CAP_PROP_FPS);
VideoWriter output("C:/Users/axelle/Videos/output.avi", VideoWriter::fourcc('H', 'F', 'Y', 'U'),
fps, cv::Size(frame_width, frame_height));
int count = 0;
while (count < 100)
{
count++;
Mat frame;
original >> frame;
if (frame.empty())
{
break;
}
//imshow("test", frame);
//waitKey(0);
output.write(frame);
}
original.release();
output.release();
注意:imshow 中已经可以看出颜色的差异。
使用 FFmpeg 后端读取视频帧时,OpenCV VideoCapture
中存在错误。
当 H.264 视频流被标记为 BT.709 颜色标准时,该错误会导致“颜色偏移”。
这个主题太重要了,不能不回答...
post 的重要部分是重现问题,并证明问题是真实的。
我找到的解决方案是选择 GStreamer 后端而不是 FFmpeg 后端。
建议的解决方案有缺点(比如需要构建支持 GStreamer 的 OpenCV)。
注:
- 在 Windows 10.
下使用 OpenCV 4.53 可重现该问题
该问题在 Ubuntu 18.04 下也可重现(在 Python 中使用 OpenCV)。
该问题适用于BT.709颜色标准的“全范围”和“有限范围”。
构建合成视频模式 以重现问题:
我们可以使用 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
- 以上命令使用 yuv444p 像素格式(而不是 yuv420p)以获得更纯的颜色。
- 参数
-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命令行工具的结果)
如您所见,颜色不同。
- OpenCV的红色像素值是RGB =
[232, 0, 3]
.
- FFmpeg的红色像素值是RGB =
[254, 0, 0]
.
原来的RGB值大概是[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 后端(而不是 FFmpeg)后端似乎可以解决“颜色偏移”问题。
- OpenCV 用户需要注意颜色偏移问题。
- 希望OpenCV开发者(或FFmpeg插件开发者)解决问题。
我正在尝试使用 openCV 简单地打开视频,处理帧并将处理后的帧写入新的视频文件。
我的问题是,即使我根本不处理帧(只是打开视频,使用 VideoCapture 读取帧并使用 VideoWriter 将它们写入新文件),输出文件看起来比输入。
执行此操作的代码可以在任何 openCV 教程中找到,没什么特别的。
我在 Windows 10 上使用 openCV c++ 4.4.0。 我通过 opencv_videoio_ffmpeg440_64.dll 将 openCV 与 ffmpeg 结合使用 输入视频是mp4。 我用 huffyuv 编解码器将输出写为 .avi :
m_video_writer.reset(new cv::VideoWriter(m_save_video_path.toStdString(), cv::VideoWriter::fourcc('H', 'F', 'Y', 'U'), // lossless compression
m_model->getFps(), cv::Size(m_frame_size.width(), m_frame_size.height())));
我尝试了许多其他编解码器,问题仍然存在。
像素差异很小,值不是恒定的,但总是以相同的方式变化:蓝色通道较低,红色和绿色通道较高。
奇怪的事实:当我用 opencv 打开输入或输出视频时,矩阵实际上完全相同。所以我猜问题出在阅读上??
这是每个视频文件的属性,使用 Windows Media Playre (MPC-HC) 导出。
VS
我应该调查什么? 谢谢!!
完整代码在这里(复制我视频的前 100 帧):
VideoCapture original("C:/Users/axelle/Videos/original.MP4");
int frame_height = original.get(CAP_PROP_FRAME_HEIGHT);
int frame_width = original.get(CAP_PROP_FRAME_WIDTH);
int fps = original.get(CAP_PROP_FPS);
VideoWriter output("C:/Users/axelle/Videos/output.avi", VideoWriter::fourcc('H', 'F', 'Y', 'U'),
fps, cv::Size(frame_width, frame_height));
int count = 0;
while (count < 100)
{
count++;
Mat frame;
original >> frame;
if (frame.empty())
{
break;
}
//imshow("test", frame);
//waitKey(0);
output.write(frame);
}
original.release();
output.release();
注意:imshow 中已经可以看出颜色的差异。
使用 FFmpeg 后端读取视频帧时,OpenCV VideoCapture
中存在错误。
当 H.264 视频流被标记为 BT.709 颜色标准时,该错误会导致“颜色偏移”。
这个主题太重要了,不能不回答...
post 的重要部分是重现问题,并证明问题是真实的。
我找到的解决方案是选择 GStreamer 后端而不是 FFmpeg 后端。 建议的解决方案有缺点(比如需要构建支持 GStreamer 的 OpenCV)。
注:
- 在 Windows 10.
下使用 OpenCV 4.53 可重现该问题 该问题在 Ubuntu 18.04 下也可重现(在 Python 中使用 OpenCV)。
该问题适用于BT.709颜色标准的“全范围”和“有限范围”。
构建合成视频模式 以重现问题:
我们可以使用 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
- 以上命令使用 yuv444p 像素格式(而不是 yuv420p)以获得更纯的颜色。
- 参数
-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命令行工具的结果)
如您所见,颜色不同。
- OpenCV的红色像素值是RGB =
[232, 0, 3]
. - FFmpeg的红色像素值是RGB =
[254, 0, 0]
.
原来的RGB值大概是[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 后端(而不是 FFmpeg)后端似乎可以解决“颜色偏移”问题。
- OpenCV 用户需要注意颜色偏移问题。
- 希望OpenCV开发者(或FFmpeg插件开发者)解决问题。