c++opencvvideoffmpegraw

OpenCV uncompressed raw video file - what format?


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:0@0.325] 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?

Edit 1

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


Solution

  • 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.


    Update:

    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.