繁体   English   中英

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

[英]Speeding up writing images into hard disk in OpenCV

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

}

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

提前致谢。

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

你可以做的事情:

  1. 正如David Schwartz所说,你应该使用队列和多个线程。
  2. 如果您正在有效地编写图像序列,请改为保存视频。 数据压缩得更多,写入磁盘的速度更快。
  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实现 ,它将使您的CPU独自用于其他事情。 我认为预期的带宽减少是2倍,因此您的RAID 0将拥有足够的带宽。 这将是使用它的首选方式 - 它将非常强大,无论系统正在做什么,你都不会丢失任何帧(理所当然)。

  3. 如果你对有损压缩是好的,那么现成的jpeg库就可以了。 您可能希望减小4倍的大小,并且可以通过操作系统所在的硬盘驱动器生成12.5Mbytes / s的数据流。

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

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

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

压缩是关键。

Imwrite docs

对于JPEG,它可以是从0到100的质量(CV_IMWRITE_JPEG_QUALITY)(越高越好)。 默认值为95。

对于PNG,它可以是从0到9的压缩级别(CV_IMWRITE_PNG_COMPRESSION)。值越大意味着更小的尺寸和更长的压缩时间。 默认值为3。

对于PPM,PGM或PBM,它可以是二进制格式标志(CV_IMWRITE_PXM_BINARY),0或1.默认值为1。

对于.bmp格式,不需要压缩,因为它直接写入位图。

总结:图像写入时间png> jpg> bmp

如果您不关心磁盘大小,我会说使用.bmp格式,这比编写png快10倍,比写jpg快6倍。

您应该有一个要处理的图像队列。 您应该有一个捕获线程来捕获图像并将它们放在队列中。 您应该有一些压缩/写入线程,将图像从队列中取出并压缩/写入它们。

这几天CPU有多个核心是有原因的 - 所以你不必在开始下一个之前完成一件事。

如果您认为这就是您正在做的事情并且您仍然看到相同的问题,请向我们展示您的代码。 你最有可能做错了。

更新 :正如我所怀疑的那样,您正在以一种无法实现首先使用线程的目标的方式使用线程。 整点是一次压缩多个图像,因为我们知道压缩图像需要30毫秒,我们知道每30毫秒压缩一个图像是不够的。 您使用线程的方式,您仍然只尝试一次压缩一个图像。 因此压缩/写入图像30毫秒仍然太长。 该队列没有用处,因为只有一个线程从中读取。

我建议看看QtMultimedia模块,如果你处理的是流而不是图像,请尝试将代码转换为MPEG。

这将避免一直处理每个像素,因为只处理像素差异。 这可能会使处理的性能提高。

当然,你可以看一下更强大的压缩算法,但这超出了Qt的范围,Qt协议可能只是算法的接口。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM