用 C++ 将 OpenCV 帧写入磁盘:单线程写入速度是不是受磁盘吞吐量以外的任何限制?

Posted

技术标签:

【中文标题】用 C++ 将 OpenCV 帧写入磁盘:单线程写入速度是不是受磁盘吞吐量以外的任何限制?【英文标题】:Writing OpenCV frames to disk in C++: is mono-thread write speed limited by anything other than disk throughput?用 C++ 将 OpenCV 帧写入磁盘:单线程写入速度是否受磁盘吞吐量以外的任何限制? 【发布时间】:2020-11-09 16:46:52 【问题描述】:

在将 OpenCV 帧写入磁盘时,我面临着我认为相当奇怪的行为:如果我在 SSD 或 HDD 上独立执行,我无法以大约 20 fps 的速度写入磁盘。但是,事情是这样的:如果我使用一个线程写入数据的前半部分,而另一个线程写入后半部分,那么我可以以双倍的速度(~40 fps)写入。

我正在使用下面的代码进行测试:两个 std::vector 填充了来自我的网络摄像头的 1920x1080 帧,然后发送到两个不同的线程以写入磁盘。例如,如果我将 2 个大小为 50 的向量写入磁盘,我可以以大约 40 fps 的整体速度完成它。但是,如果我只使用一个大小为 100 的向量,则它会下降到一半。怎么会这样?我以为我会受到磁盘吞吐量的限制,这足以写入至少 30 fps,但我错过了一些东西,我不知道是什么。还有其他我没有考虑的限制(除了 cpu)吗?

#include "opencv2/opencv.hpp"
#include "iostream"
#include "thread"
#include <unistd.h>
#include <chrono>
#include <ctime>

cv::VideoCapture camera(0);

void writeFrames(std::vector<cv::Mat> &frames, std::vector<int> &compression_params, std::string dir)

    for(size_t i=0; i<frames.size(); i++)
    
        cv::imwrite(dir + std::to_string(i) + ".jpg",
                    frames[i], compression_params);
    


int main(int argc, char* argv[])

    camera.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
    camera.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);
    camera.set(cv::CAP_PROP_FPS, 30);

    std::vector<int> compression_params;
    compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
    compression_params.push_back(95); // [0 - 100] (100 better), default 95

    size_t vecSizeA = 50;
    size_t vecSizeB = 50;

    std::vector<cv::Mat> framesA, framesB;
    cv::Mat frame;

    std::chrono::system_clock::time_point t0 = std::chrono::system_clock::now();

    for(unsigned int i=0; i<vecSizeA; i++)
    
        camera >> frame;
        framesA.push_back(frame);
    

    for(unsigned int i=0; i<vecSizeB; i++)
    
        camera >> frame;
        framesB.push_back(frame);
    

    std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now();

    std::thread trA(writeFrames, std::ref(framesA), std::ref(compression_params), "/tmp/frames/A/");
    std::thread trB(writeFrames, std::ref(framesB), std::ref(compression_params), "/tmp/frames/B/");

    trA.join();
    trB.join();

    std::chrono::system_clock::time_point t2 = std::chrono::system_clock::now();

    double tr = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count() / 1000.0;
    double tw = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() / 1000.0;

    std::cout << "Read fps: " << (vecSizeA + vecSizeB) / tr << std::endl;
    std::cout << "Write fps: " << (vecSizeA + vecSizeB) / tw << std::endl;

    return 0;

编辑:以防万一不是很清楚,我正在寻找实现至少 30 fps 写入速度的方法。磁盘可以处理(如果不是这种情况,我们将无法以 30fps 录制视频),所以我的限制来自我的代码或我缺少的东西。

【问题讨论】:

【参考方案1】:

因为 2 个线程同时达到相同的功能,而且它似乎比一个线程快。您的线程的连接在同一个地方。如果您像这样使用它们,您将获得与一个线程相同的 fps:

    std::thread trA(writeFrames, std::ref(framesA), std::ref(compression_params), "/tmp/frames/A/");

    trA.join();

    std::thread trB(writeFrames, std::ref(framesB), std::ref(compression_params), "/tmp/frames/A/");

    trB.join();

您也可以查看here 了解更多信息。

【讨论】:

是的,拥有 2 个线程会更快,但磁盘仍然是一个(两个线程不能同时访问,对吗?)。我不明白为什么我不能在一个线程上写入约 30 fps,因为 SSD 磁盘能够处理的远不止这些。 两个线程同时到达函数,这使它更快。这就是我所看到的 好的,感谢您的帮助,但问题是“单线程写入速度是否受磁盘吞吐量以外的任何限制?”。我知道多线程通常会加快速度,但据我所知,当存在硬件限制时不应该发生这种情况。即:仅使用多线程读取相机,我肯定无法将帧数加倍。【参考方案2】:

例如,如果我将 2 个大小为 50 的向量写入磁盘,我可以以大约 40 fps 的整体速度执行此操作。但如果我只使用一个大小为 100 的向量,则会下降到 [~20 fps]。怎么可能?

imwrite 中,您也在编码/压缩帧。所以要做的工作比简单地写入磁盘要多。这可能解释了使用多线程带来的加速。

【讨论】:

以上是关于用 C++ 将 OpenCV 帧写入磁盘:单线程写入速度是不是受磁盘吞吐量以外的任何限制?的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV C++ 多线程提高帧率

将 opencv 帧写入 gstreamer rtsp 服务器管道

使用opencv将视频写入文件时遇到问题

在多线程应用程序中使用 opencv waitKey()

OpenCV:录制扭曲的视频

如何将帧写入视频文件?