加快在OpenCV中将图像写入硬盘

时间:2014-01-05 08:19:18

标签: c++ qt opencv qthread qtcore

我正在使用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--;
            }
      }

}

由于这只是整个项目的一部分,我已经删除了一些不相关的部分......但它显示了我如何在一张大图中编写我的多线程代码......所以如果有任何问题请告知我

提前致谢。

5 个答案:

答案 0 :(得分:3)

让我们看看:2048 * 1080 * 3(通道数)* 50 fps~ = 316MB / s,如果你用raw写的图像。如果您正在使用JPEG,根据压缩参数可能会大幅减少,但如果它是1/5,您仍然会在硬盘上写入大量数据,特别是如果您在笔记本电脑上使用5400rpm

你可以做的事情:

  1. 正如David Schwartz建议的那样,你应该使用队列和多个线程。
  2. 如果您正在有效地编写图像序列,请改为保存视频。数据压缩得更多,写入磁盘的速度更快。
  3. 检查当前设备的斑点,并估算可以写入图像的最大图像大小。选择压缩参数以适合该大小约束。

答案 1 :(得分:3)

通常有多种解决方案,但是您需要指定图像的格式 - 灰度是什么? 8位? 12位? 16位?

大多数其他答案完全忽略了你想要做的事情的物理现实:带宽,无论是在I / O和处理方面,都是最重要的。

您是否在实际情况下验证了系统上可用的存储带宽?将此流存储在您的操作系统所在的同一驱动器上通常是一个坏主意,因为寻求到期对其他应用程序的要求会占用你的带宽。请记住,在现代50 + Mbyte / s硬盘驱动器上寻求5ms的速度,一次搜索需要花费0.25MB的带宽,而且相当乐观,因为现代的“磨机运行”硬盘读取速度更快,平均寻求更慢。我说每次搜索损失1MByte是对昔日消费者驱动器的保守估计。

  1. 如果您需要编写原始帧并且不想以无损方式压缩它们,那么您需要一个可以支持必要带宽的存储系统。假设8位灰度,你将倾斜2Mbytes /帧,50Hz,即100Mbytes / s。由两个现代现成驱动器组成的条带RAID 0阵列应该能够毫无问题地应对它。

  2. 如果你可以刻录一些严重的CPU或GPU进行压缩,但仍然想要无损存储,那么JPEG2000是默认选择。如果你使用GPU implementation,它将使你的CPU独自用于其他事情。我认为预期的带宽减少是2倍,因此您的RAID 0将拥有足够的带宽。这将是使用它的首选方式 - 它将非常强大,无论系统正在做什么,你都不会丢失任何帧(理所当然)。

  3. 如果您对有损压缩感觉不错,那么现成的jpeg库就可以解决这个问题。您可能希望减小4倍的大小,并且可以通过操作系统所在的硬盘驱动器生成12.5Mbytes / s的数据流。

  4. 至于实现:如果没有压缩,两个线程就足够了。一个线程捕获图像,另一个线程将它们转储到驱动器。如果您发现与单个线程相比没有任何改进,那么完全是由于驱动器的带宽限制。如果使用GPU进行压缩,那么一个处理压缩的线程就足够了。如果使用CPU进行压缩,则需要与核心一样多的线程。

    存储图像差异完全没有问题,事实上JPEG2k喜欢这个,如果你很幸运,我可以获得总体2倍压缩效果(总因数为4倍)。您所做的是为完整存储的每个参考帧存储一组差异帧。该比率完全基于之后完成的处理需求 - 您可以减少数据丢失的弹性和交互式处理延迟,从而减少存储时间带宽。

    我会说1:5和1:50之间的比例是合理的。对于后者,参考帧的丢失会产生1s的数据,随机搜索数据中的任何位置都需要平均读取参考帧和24个delta帧,再加上解压缩25帧的成本。

答案 2 :(得分:3)

压缩是关键所在。

Imwrite docs

  

对于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协议可能只是连接算法。