繁体   English   中英

C++ OpenCV VideoWriter 帧率同步

[英]C++ OpenCV VideoWriter frame rate synchronization

我即将从视频采集卡中捕获帧。 这些帧被处理并写入硬盘。 整个设置是在一个多线程环境中,所以抓取器将图像写入队列,在另一个线程中,图像被处理,另一个写入硬盘。 如果根据处理器的定义图像是好的,图像就会被写入硬盘。 如果连续 10 张图像“坏”,则文件完成。 如果有 9 个或更少的“坏”图像,则所有图像都将写入下一个好图像,因此文件编写者会得到通知。

这里的问题是,如果我不这样做,而是在处理后直接写入每个文件,视频文件很好,但是写入了9个“坏”图像。 如果我按照上面描述的方式进行操作,则视频的速度/帧速率不合适。 这个描述有点奇怪,所以这里只是一个简化的例子,这样你就可以看到问题了:

void FrameWriter::writeFrameLoop() {

    string path = getPath();
    cv::Size2i size(1350, 1080);
    cv::VideoWriter videoWriter(path, fourcc, 30, size);

    while (this->isRunning) {

        while (!this->frames.empty()) {
        
            usleep(100000); // this effects the speed/frame
            videoWriter.write(this->pop());
        }
        std::this_thread::sleep_for(10ms);
    }

    videoWriter.release();
}

这个例子很简单,在这里我用睡眠“阻塞”了写入过程,记住这是一个不同的线程。 这意味着捕获停止后,文件写入需要更长的时间。 但我希望这不会影响视频本身,因为帧率为 30 并且图像的顺序仍然相同。 但是对我来说,当我不及时调用“videoWriter.write”时,它似乎会影响视频文件。 在这种情况下,视频比预期的要快得多。 我以为只有配置的 30 帧和写入图像的数量会影响视频速度,但事实并非如此。 任何人都可以帮助我了解这里发生了什么吗?

我在 Ubuntu 18.04 上使用 openCV 4.4.0。 感谢您的帮助。 BR迈克尔

我想我知道快速播放结果视频的原因。

在构造函数cv::VideoWriter videoWriter(path, fourcc, 30, size); 您将生成的视频的帧速率 (FPS) 设置为 30。这意味着 CV 库期望write()函数为生成的视频流的每 1 秒write() 30 帧。

同样对于 CV 库,使用新帧调用write()速度没有区别,您可以每秒调用 5 次或 10 次甚至 1000 次。 唯一重要的是您必须为视频的每一秒提供 30 帧,而提供这 30 帧的速度并不重要。

我的意思是,您的所有sleep(...)功能对于 CV VideoWriter 类都无关紧要。 并且对于所有视频渲染/转换库始终如此。 所以暂停线程根本不会改变任何东西。

但是在您的情况下,您是说您从采集卡视频卡的实时视频数据中每秒抓取 10 帧。 这意味着您的 FPS 真的是每秒 10 帧。 因此,为了正确解决您的任务,接下来应该做的事情是:

  1. 删除所有暂停功能,例如调用sleep() 它根本不需要。 并且不会改变 VideoWriter 的行为。
  2. 然后解决任务的第一种方法是在您的构造函数中更改cv::VideoWriter videoWriter(path, fourcc, 30, size); 值 30 到 10。这已经可以解决您的任务,但您必须确保每秒抓取 10 帧,不多不少。 那么您的视频将是一个正确播放(正确速度)的视频,帧速率为每秒 10 帧。 这是最简单的解决方案。 视频不需要为 30 FPS 以后才能正确播放,10 FPS 的视频以后任何播放器都可以正确播放。
  3. 另一种解决方案,如果您真的希望生成的视频每秒播放 30 帧,不多不少,然后将抓取视频的每一帧复制 3 次,这样您将获得 10 帧抓取视频中的 30 帧。 通过复制,我的意思是你应该用相同的单帧调用videWriter.write(...)三次(在一个小循环中),调用这个write没有任何暂停(如sleep )。 然后,您生成的视频每秒将精确到 30 帧。

我想你只是想念了解CV::VideoWriter是如何工作的。 您认为write()实时渲染生成的视频,这意味着如果您将其提供 10 帧但恰好在一秒内,那么它应该渲染正确的视频速度。 但是这个作者不是实时渲染视频,这意味着它只是假设通过的 10 帧仅构成结果视频的 1/3 秒,因此它期望在 1 秒内写入 30 帧。

如果您的相机无法提供恒定的帧率,比如说 30 帧,您还可以考虑将帧率限制为例如 25,并测量自写入最后一帧以来经过的时间。 您还可以将帧率更改为任意值,只要相机能够提供即可。 一个实现示例:

m_fps = 25;
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point now;

while (1) {

    now = std::chrono::steady_clock::now();
    long duration = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start).count();
    
    if ((double)duration > (1e9 / m_fps)) {

        start = std::chrono::steady_clock::now();
        
        m_duration += duration;

        // Capture frame-by-frame
        if(m_cap->grab()) { m_cap->retrieve(m_frame); }

        // write frame
        m_writer->write(m_frame);
        cv::waitKey(1);

    }
}

我想出了我的问题,是我,而不是 OpenCV。 由于多线程环境,我将图像 (cv::Mat) 写入队列。 由于有很多转换 YUV -> RGB -> BGR -> Crop 等。我期待我放入队列的 cv::Mat 对象是一个深拷贝。 而不是这个, cv::Mat 总是同一个对象。 这意味着,即使我的队列中已经有 20 个 cv::Mat 条目,它们都是相同的,并且当有新图像时它们都“改变”了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM