目录
前言:
本文基于QT框架提供一个简单明了的“多线程存图队列”的模板,我们也可以基于结构体提供更多信息,增加更多对图像的处理操作。咱们还是先上源码再讲设计,建议结合设计再看代码哦。
1. 源码
SaveImageThread.h
// SaveImageThread.h
#pragma once
#include "opencv2/opencv.hpp"
#include <QThread>
#include <QMutex>
#include <QQueue>
#include <QImage>
typedef struct _ImageInfo
{
cv::Mat image;// QImage image;
// double scale;
// QString fileName;
// ...
}ImageInfo;
class SaveImageThread :public QThread
{
public:
SaveImageThread(QObject* parent = nullptr);
~SaveImageThread();
public:
void enQueue(const ImageInfo &info);
void run();
//=====================启动子线程前的操作=================//
// 设置存图路径
void setPath(QString path);
// 清空队列
void reset();
protected:
QMutex m_Mutex;
QQueue<ImageInfo> m_ImageInfoQueue;
QString m_sFilePath;
uint64_t m_nSaveNum;
};
SaveImageThread.cpp
// SaveImageThread.cpp
#include "SaveImageThread.h"
#include <QApplication>
#include <QDebug>
#include <QFile>
using cv::Mat;
SaveImageThread::SaveImageThread(QObject* parent) : QThread(parent)
{
}
SaveImageThread::~SaveImageThread()
{
m_ImageInfoQueue.clear();
quit();
wait();
qDebug() << "SaveImageThread:: Thread Over";
deleteLater();
}
void SaveImageThread::enQueue(const ImageInfo &info)
{
// 只有一个主线程执行入队操作,若多线程操作入队,需加锁
//m_Mutex.lock();
m_ImageInfoQueue.enqueue(info);
//m_Mutex.unlock();
if (!isRunning())
{
start();// 子线程执行run()
}
}
void SaveImageThread::run()
{
cv::Mat mat;
ImageInfo imageInfo;
bool quit = m_ImageInfoQueue.empty();
while (!quit)
{
// 出队操作
imageInfo = m_ImageInfoQueue.dequeue();
mat = imageInfo.image;
// 图像处理、存图
if(m_sFilePath != "")
cv::imwrite((m_sFilePath + QString::number(m_nSaveNum++) + ".bmp").toLocal8Bit().data(), mat);
quit = m_ImageInfoQueue.empty();
// 这里你注意 ❛‿˂̵✧
// 如果quit放在dequeue后面,在开头更新判断为空,而主线程在入队后SaveImage线程在正在处理图像,
// 那么就会丢失最后一张图,所以quit得放最后更新。
}
}
void SaveImageThread::setPath(QString path)
{
if (QFile::exists(path))
m_sFilePath = path + QString("/image");
else
m_sFilePath = "";
}
void SaveImageThread::reset()
{
m_nSaveNum = 0;
m_ImageInfoQueue.clear();
}
2. 结构体设计
入队的数据为包含图像数据的结构体ImageInfo,要处理更多的图像操作,当然要提供更多的数据,例如缩放比例,文件名......
typedef struct _ImageInfo
{
cv::Mat image;// QImage image;
// double scale;
// QString fileName;
// ...
}ImageInfo;
处理图像当然要用cv::Mat了,如果你懒的配置opencv,也可以使用QImage代替,并使用QImage的存图函数。
3. 多线程设计
入队操作enQueue()是在主线程进行的,而存图操作run()则是在SaveImage线程进行。要注意锁,不必要的加锁会成为线程的枷锁。实际上,这里主线程入队操作是很快的,因为并没有拷贝图像,而子线程存图是极为耗时的。
如果对QT多线程QThread操作有疑问,也可以看博主另一篇,包大全套。