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.