I'm trying to save an uncompressed raw video file given some frames with OpenCV. Going trough the doc, I can read:
If FFMPEG is enabled, using
codec=0
;fps=0
; you can create an uncompressed (raw) video file.
OpenCV seems to have FFMPEG enabled; indeed, cv::getBuildInformation()
gives me the following:
Video I/O:
DC1394: NO
FFMPEG: YES (prebuilt binaries)
avcodec: YES (58.134.100)
avformat: YES (58.76.100)
avutil: YES (56.70.100)
swscale: YES (5.9.100)
avresample: YES (4.0.0)
GStreamer: NO
DirectShow: YES
Media Foundation: YES
DXVA: YES
I'm trying the following:
#include <opencv2/videoio.hpp>
void testLosslessWriter()
{
cv::VideoCapture cap(0, cv::CAP_DSHOW);
int w = int(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int h = int(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
cv::VideoWriter writerUncompressed("test_raw", 0, 0, cv::Size(w, h));
}
I'm getting the following error:
OpenCV(4.7.0-dev) Error: Bad argument (CAP_IMAGES: can't find starting number (in the name of file): test_raw) in cv::icvExtractPattern, file C:\workspace\libs\opencv\modules\videoio\src\cap_images.cpp, line 253
[ERROR:[email protected]] global cap.cpp:597 cv::VideoWriter::open VIDEOIO(CV_IMAGES): raised OpenCV exception:
OpenCV(4.7.0-dev) C:\workspace\libs\opencv\modules\videoio\src\cap_images.cpp:253: error: (-5:Bad argument) CAP_IMAGES: can't find starting number (in the name of file): test_raw in function 'cv::icvExtractPattern'
The same happens if I try, just for example, other formats as "test_raw.avi", "test_raw.mp4" or (made up) "test_raw.raw".
What's the right way to use the documentation hint?
Following Rotem answer, the problem was in fact with fps=0
; the issue above got solved with '.avi' and fourcc "RGBA".
However:
// Error returned:
// OpenCV: FFMPEG: tag 0x00000000/'????' is not supported with codec id 13 and format 'rawvideo / raw video'
cv::VideoWriter writerUncompressed("test_raw.rgb", 0, 1, cv::Size(w, h)); // the same for test_raw.yuv
The output file is written (and it is not zero-sized), but when I try to open it back with VideoCapture
using CAP_FFMPEG
or CAP_ANY
API preferences (actually using python, but that should be irrelevant) I got [IMGUTILS @ 0000003dbc7edaf0] Picture size 0x0 is invalid
and no frame is read
According to FFmpeg conventions, raw video file extension supposed to be .yuv
.
The frame rate supposed to be positive (the exact value doesn't matter for raw video - 0
is probably reserved for images).
Replace cv::VideoWriter writerUncompressed("test_raw", 0, 0, cv::Size(w, h));
with:
cv::VideoWriter writerUncompressed("test_raw.yuv", 0, 1, cv::Size(w, h));
Note that other extensions may also work (like .rgb
), but most file extensions are not going to work.
The raw video "pixel format" is yuv420p
.
Example for converting the test_raw.yuv
to MP4 file using FFmpeg CLI:
ffmpeg -y -f rawvideo -video_size 640x480 -pixel_format yuv420p -r 1 -i test_raw.yuv -vcodec libx264 -pix_fmt yuv420p test_raw.mp4
Code sample that writes 10 synthetic video frames to test_raw.yuv
:
#include <opencv2/opencv.hpp>
#include <opencv2/videoio.hpp>
int main()
{
int width = 640;
int height = 480;
int n_frames = 10;
//For raw video, use yuv file extension
cv::VideoWriter writerUncompressed("test_raw.rgb", 0, 1, cv::Size(width, height)); //Set FPS to 1 (0 may not be supported).
//Generate 10 synthetic video frames:
//////////////////////////////////////////////////////////////////////////
for (int i = 0; i < n_frames; i++)
{
cv::Mat img = cv::Mat(height, width, CV_8UC3);
img = cv::Scalar(60, 60, 60);
cv::putText(img, std::to_string(i + 1), cv::Point(width / 2 - 100 * (int)(std::to_string(i + 1).length()), height / 2 + 100), cv::FONT_HERSHEY_DUPLEX, 10, cv::Scalar(30, 255, 30), 20); // Green number
//cv::imshow("img", img);cv::waitKey(100);
writerUncompressed.write(img);
}
writerUncompressed.release();
return 0;
}
Note:
For uncompressed AVI video, we may use fourcc('R', 'G', 'B', 'A')
:
cv::VideoWriter writerUncompressed("test_uncompressed.avi", cv::VideoWriter::fourcc('R', 'G', 'B', 'A'), 1, cv::Size(width, height));
If you find fourcc
for BGR
pixel format (3 bytes per pixel instead of 4), please let me know.
Reading raw video frames using cv::VideoCapture
:
Reading pure raw video, without video file container is problematic, since the raw video file doesn't have any information about resolution, and pixel format.
We may write the raw video with NUT video container.
Use video file name with .nut
extension.
Code sample for writing and reading with NUT video container:
#include <opencv2/opencv.hpp>
#include <opencv2/videoio.hpp>
int main()
{
int width = 640;
int height = 480;
int n_frames = 10;
//For raw video, use nut file extension (use NUT file container)
cv::VideoWriter writerUncompressed("test_raw.nut", 0, 1, cv::Size(width, height)); //Set FPS to 1
//Generate 10 synthetic video frames - write yuv420p raw video to NUT file container:
//////////////////////////////////////////////////////////////////////////
for (int i = 0; i < n_frames; i++)
{
cv::Mat img = cv::Mat(height, width, CV_8UC3);
img = cv::Scalar(60, 60, 60);
cv::putText(img, std::to_string(i + 1), cv::Point(width / 2 - 100 * (int)(std::to_string(i + 1).length()), height / 2 + 100), cv::FONT_HERSHEY_DUPLEX, 10, cv::Scalar(30, 255, 30), 20); // Green number
writerUncompressed.write(img);
}
writerUncompressed.release();
//////////////////////////////////////////////////////////////////////////
//Read the raw video frames from the test_raw.nut
//////////////////////////////////////////////////////////////////////////
cv::VideoCapture cap("test_raw.nut");
cv::Mat frame;
while (true)
{
cap >> frame; //Read the video frame. OpenCV automatically converts the frame to BGR pixel format.
if (frame.empty())
{
break; //Break the loop when there are no more frames to capture
}
imshow("frame", frame);
cv::waitKey(100);
}
cap.release();
cv::destroyAllWindows();
//////////////////////////////////////////////////////////////////////////
return 0;
}
Note:
There may be a way to read pure raw video file using cv::VideoCapture, but if it's not possible, we may simply open the file as binary file, and use fread
to img.data
.
The pixel format is yuv420p, and cv::Mat size is going to be 640x720
.
Use cv::cvtColor
with cv::COLOR_YUV2BGR_I420
for converting to BGR.