1

I'm facing what I consider a fairly odd behaviour when writin OpenCV frames to disk: I can't write to disk faster that ~20 fps, independently if I do it on my SSD or my HDD. But, and here's the thing: if I use one thread to write the first half of the data and another to write the second half, then I can write at double the speed (~40 fps).

I'm testing using the code below: two std::vectors are filled with 1920x1080 frames from my webcam, and then sent to two different threads to be written to disk. If, for example, I write 2 vectors of size 50 to disk, I can do it at an overall speed of ~40 fps. But if I only use one vector of size 100, that drops to half. How can it be? I thought I would be limited by the disk throughput, that is sufficient to write at least 30 fps, but I'm missing something and I don't know what. Is there other limit (apart from cpu) that I'm not taking into account?

#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;
}

Edit: just in case it is not very clear, I'm looking for the way to achieve at least 30 fps on write speed. Disks can handle that (we wouldn't be able to record video at 30fps if that wouln't be the case), so my limitations come from my code or from something I'm missing.

dvilela
  • 1,200
  • 12
  • 29

2 Answers2

1

Because 2 thread is reaching same function at the same time and it seems it is faster than one thread. Your threads' joins are in the same place. If you use them like this, you will get the same fps like one thread:

    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();

You can also check here to have more idea.

Yunus Temurlenk
  • 4,085
  • 4
  • 18
  • 39
  • 1
    Yes, the point of having the 2 threads it to be faster, but the disk is still one (both threads can't access at the same time, right?). What I don't get is why I can't write ~30 fps on one thread, because an SSD disk is able to handle much more than that. – dvilela Jul 20 '20 at 13:25
  • Both thread reaching the function at the same time this makes it faster. Thats what I see – Yunus Temurlenk Jul 20 '20 at 13:31
  • 1
    Ok, and I appreciate your help, but the question was "is mono-thread write speed limited by anything other than disk throughput?". I know multithreading usually speeds things up, but that, as far as I know, should not happen when there is hardware limitation. I.e: I surely can't get double the frames just using multithread for reading the camera. – dvilela Jul 20 '20 at 14:57
1

If, for example, I write 2 vectors of size 50 to disk, I can do it at an overall speed of ~40 fps. But if I only use one vector of size 100, that drops to [~20 fps]. How can it be?

in imwrite you are encoding/compressing the frames as well. so more work is being done than simply writing to the disk. that could potentially explain the speedup from using multiple threads.