我正在使用50 fps的摄像头(在Ubuntu环境和Qt框架中),每隔20 ms,我就会得到一个帧来处理。
我写了一个代码来从相机中读取图像,然后将它们存储在硬盘中。
while(3.14)
{
cv::Mat Camera_Image = Capture_Image();
double T1 = (double)cv::getTickCount();
cv::imwrite (STORE_ADDRESS,Camera_Image);
T1 = (((double)cv::getTickCount() -T1)*1000)/cv::getTickFrequency();
print(T1);
}
当我看到输出时,对于2048 * 1080图像尺寸,将单个图像写入硬盘的时间约为30毫秒。每个图像都是单通道(灰度),但我在硬盘中以.jpg格式编写它们。硬盘中每个图像的大小约为500K字节。
由于我在大约20毫秒内捕获一帧,我无法将它们全部写入硬盘实时。我已经使用Qthread编写了我的代码并创建了一个队列来查看是否有任何改进,但结果是相同的,它只是一个内存开销。
是否可以更改此情况,或使用其他库将这些图像更快地写入硬盘?如果可以的话,我也更喜欢Qt解决方案...
另外我需要将每一帧写入硬盘,所以请不要使用Motion压缩算法,因为它们不适用于我的情况....
代码: Mainwindow.cpp
Qlist<cv::Mat> FINAL_IM_VEC;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
IMREAD *IMR = new IMREAD(this); // an instance of IMREAD Class which reads camera frames
IMWRITE *IMW = new IMWRITE(this); // an instance of IMWRITE Class which Writes camera frames into hard disk
QThread *IMAGE_READ_Thread = new QThread(this);
QThread *Image_Store_Thread = new QThread(this);
connect(IMAGE_READ_Thread,SIGNAL(started()),IMR,SLOT(IMREAD_Process()));
connect(Image_Store_Thread,SIGNAL(started()),IMW,SLOT(IMWrite_Process()));
IMR.moveToThread(IMAGE_READ_Thread);
IMW.moveToThread(Image_Store_Thread);
IMAGE_READ_Thread->start();
Image_Store_Thread->start();
}
imread.hpp
class IMREAD : public QObject
{
Q_OBJECT
public:
explicit IMREAD(QObject *parent = 0);
signals:
public slots:
void IMREAD_Process();
private:
bool Stop;
};
imread.cpp
IMREAD::IMREAD(QObject *parent) :
QObject(parent)
{
this->Stop = false;
}
void IMREAD::IMREAD_Process()
{
while(!Stop)
{
cv::Mat Image = CAM::Campture_F(25);//wait a maximum of 25 milisecond to grab a new frame
if(Image.data())
{
FINAL_IM_VEC.push_back(Image);
}
}
}
}
imwrite.hpp
#ifndef IMWRITE_H
#define IMWRITE_H
#pragma once
#include <QObject>
class IMWRITE : public QObject
{
Q_OBJECT
public:
explicit IMWRITE(QObject *parent = 0);
signals:
public slots:
void IMWrite_Process();
private:
bool Stop;
};
imwrite.cpp
IMWRITE::IMWRITE(QObject *parent) :
QObject(parent)
{
this->Stop =false;
}
void IMWRITE::IMWrite_Process()
{
static int counter = 0;
while(!Stop)
{
for(int i = 0 ; i < FINAL_IM_VEC.size() ; i++)
{
QString address = "/home/Provisioner/ThreadT/Results/" + QString::number(counter++) + ".jpg";
cv::imwrite(address.toUtf8().constData(),FINAL_IM_VEC[i]);
FINAL_IM_VEC.erase(FINAL_IM_VEC.begin() + i);
i--;
}
}
}
由于这只是整个项目的一部分,我已经删除了一些不相关的部分......但它显示了我如何在一张大图中编写我的多线程代码......所以如果有任何问题请告知我
提前致谢。
答案 0 :(得分:3)
让我们看看:2048 * 1080 * 3(通道数)* 50 fps~ = 316MB / s,如果你用raw写的图像。如果您正在使用JPEG,根据压缩参数可能会大幅减少,但如果它是1/5,您仍然会在硬盘上写入大量数据,特别是如果您在笔记本电脑上使用5400rpm
你可以做的事情:
答案 1 :(得分:3)
通常有多种解决方案,但是您需要指定图像的格式 - 灰度是什么? 8位? 12位? 16位?
大多数其他答案完全忽略了你想要做的事情的物理现实:带宽,无论是在I / O和处理方面,都是最重要的。
您是否在实际情况下验证了系统上可用的存储带宽?将此流存储在您的操作系统所在的同一驱动器上通常是一个坏主意,因为寻求到期对其他应用程序的要求会占用你的带宽。请记住,在现代50 + Mbyte / s硬盘驱动器上寻求5ms的速度,一次搜索需要花费0.25MB的带宽,而且相当乐观,因为现代的“磨机运行”硬盘读取速度更快,平均寻求更慢。我说每次搜索损失1MByte是对昔日消费者驱动器的保守估计。
如果您需要编写原始帧并且不想以无损方式压缩它们,那么您需要一个可以支持必要带宽的存储系统。假设8位灰度,你将倾斜2Mbytes /帧,50Hz,即100Mbytes / s。由两个现代现成驱动器组成的条带RAID 0阵列应该能够毫无问题地应对它。
如果你可以刻录一些严重的CPU或GPU进行压缩,但仍然想要无损存储,那么JPEG2000是默认选择。如果你使用GPU implementation,它将使你的CPU独自用于其他事情。我认为预期的带宽减少是2倍,因此您的RAID 0将拥有足够的带宽。这将是使用它的首选方式 - 它将非常强大,无论系统正在做什么,你都不会丢失任何帧(理所当然)。
如果您对有损压缩感觉不错,那么现成的jpeg库就可以解决这个问题。您可能希望减小4倍的大小,并且可以通过操作系统所在的硬盘驱动器生成12.5Mbytes / s的数据流。
至于实现:如果没有压缩,两个线程就足够了。一个线程捕获图像,另一个线程将它们转储到驱动器。如果您发现与单个线程相比没有任何改进,那么完全是由于驱动器的带宽限制。如果使用GPU进行压缩,那么一个处理压缩的线程就足够了。如果使用CPU进行压缩,则需要与核心一样多的线程。
存储图像差异完全没有问题,事实上JPEG2k喜欢这个,如果你很幸运,我可以获得总体2倍压缩效果(总因数为4倍)。您所做的是为完整存储的每个参考帧存储一组差异帧。该比率完全基于之后完成的处理需求 - 您可以减少数据丢失的弹性和交互式处理延迟,从而减少存储时间带宽。
我会说1:5和1:50之间的比例是合理的。对于后者,参考帧的丢失会产生1s的数据,随机搜索数据中的任何位置都需要平均读取参考帧和24个delta帧,再加上解压缩25帧的成本。
答案 2 :(得分:3)
压缩是关键所在。
对于JPEG,它可以是从0到0的质量(CV_IMWRITE_JPEG_QUALITY) 100(越高越好)。默认值为95.
对于PNG,它可以是压缩级别(CV_IMWRITE_PNG_COMPRESSION) 从0到9 值越高意味着尺寸越小,压缩时间越长。默认 价值是3.
对于PPM,PGM或PBM,它可以是二进制格式标志( CV_IMWRITE_PXM_BINARY),0或1.默认值为1。
对于.bmp格式压缩,因为它直接写入位图。
总结:图像写入时间png&gt; jpg&gt; BMP 强>
如果你不关心磁盘大小,我会说使用.bmp格式,这比写png快10倍,比写jpg快6倍。
答案 3 :(得分:0)
您应该有一个要处理的图像队列。您应该有一个捕获线程来捕获图像并将它们放在队列中。你应该有一些压缩/写入线程可以将图像从队列中取出并压缩/写入它们。
现在CPU有多个核心的原因 - 所以你不必在开始下一个之前完成一件事。
如果您认为这就是您正在做的事情并且您仍然看到同样的问题,请告诉我们您的代码。你最有可能做错了。
更新:正如我所怀疑的那样,您使用线程的方式并没有达到首先使用线程的目的。整点是一次压缩多个图像,因为我们知道压缩图像需要30毫秒,我们知道每30毫秒压缩一个图像是不够的。您使用线程的方式,您仍然只尝试一次压缩一个图像。因此压缩/写入图像30毫秒仍然太长。该队列没有用处,因为只有一个线程从中读取。
答案 4 :(得分:0)
我建议您查看QtMultimedia
模块,如果您处理的是流而不是图像,请尝试将代码转换为MPEG。
这将避免一直处理每个像素,因为只处理像素差异。这可能会提高处理性能。
你可以肯定地看一下更为强大的压缩算法,但这超出了Qt的范围,Qt协议可能只是连接算法。