1

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

Buzz
  • 1,102
  • 1
  • 9
  • 24

1 Answers1

1

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.

Rotem
  • 30,366
  • 4
  • 32
  • 65
  • Thank you for your answer! Since comment would be limited, I've reported in an edit the issue with implementing what you suggested. – Buzz Apr 09 '23 at 21:09
  • `test_raw.rgb` is working in my machine, but you better try `test_raw.yuv`. I am using Windows 10 and OpenCV version `OpenCV 4.7.0-dev` (Version control: `4.7.0-1-g9208dcb07c`). I am using the pre-built version from [GitHub](https://github.com/opencv/opencv/releases/download/4.7.0/opencv-4.7.0-windows.exe). – Rotem Apr 09 '23 at 21:34
  • I am also getting the error message: `OpenCV: FFMPEG: tag 0x00000000/'????' is not supported with codec id 13 and format 'rawvideo / raw video'`, but the `test_raw.yuv` is created (the size is `4,608,000` = 640*480*1.5). Reading the file using `VideoCapture` is a different story... (I never tried that). – Rotem Apr 09 '23 at 21:41
  • I think the meaning of "raw" should be less strict here. OP probably just wants to avoid lossy compression. literal raw bitstreams are of little use to most people that _say_ "raw". nor are most people aware of color spaces, or conversion. – Christoph Rackwitz Apr 09 '23 at 23:31
  • @ChristophRackwitz, actually I'm trying, along with uncompressed raw, also lossy and lossless compression. For the specific application, I might need all the three, depending on a series of factors. I'm actually not interested in how and in what format exactly the raw bitstream is written to file, so long as I'll have a compatible way to read it back from the other end. – Buzz Apr 10 '23 at 07:51
  • @Rotem, I was using VideoCapture because, somehow, I will have the necessity to open the file recorded. However, as far as I need, the raw .avi works so I'm happy; I'll dig into .yuv further to see how to handle the issue. Thanks to both for the support. – Buzz Apr 10 '23 at 07:51
  • @Buzz I added an example for reading raw video frames using cv::VideoCapture (using NUT video container). I didn't emphasize it in my answer, and I hope you are aware to the fact that the conversion from RGB to YUV420 is not lossless, due to color subsampling. – Rotem Apr 10 '23 at 08:45