2025-03-21 18:25:54 +08:00
|
|
|
#include <qthread.h>
|
|
|
|
|
#include <opencv.hpp>
|
|
|
|
|
#include <qqueue.h>
|
|
|
|
|
#include <qdatetime.h>
|
|
|
|
|
#include <backStruct.h>
|
2025-08-15 16:36:54 +08:00
|
|
|
#include <detData.h>
|
2025-08-20 16:57:03 +08:00
|
|
|
|
2025-10-14 01:27:20 +08:00
|
|
|
enum imgSaveThreadType {
|
2025-03-21 18:25:54 +08:00
|
|
|
oriImg,
|
|
|
|
|
resImg,
|
|
|
|
|
segImg,
|
|
|
|
|
clsImg,
|
|
|
|
|
tiffOriImg
|
|
|
|
|
};
|
2025-10-14 01:27:20 +08:00
|
|
|
struct saveJob {
|
|
|
|
|
saveJob(std::string jobNameIn) :jobName(jobNameIn) {}
|
|
|
|
|
std::string jobName;
|
|
|
|
|
QQueue<std::pair<std::string, cv::Mat>> imgQueue;
|
|
|
|
|
};
|
|
|
|
|
class imgSaveThread : public QThread {
|
|
|
|
|
|
2025-03-21 18:25:54 +08:00
|
|
|
public:
|
2025-10-14 01:27:20 +08:00
|
|
|
enum type {
|
2025-03-21 18:25:54 +08:00
|
|
|
normal,
|
|
|
|
|
priority
|
|
|
|
|
};
|
|
|
|
|
QMutex mutex;
|
2025-10-14 01:27:20 +08:00
|
|
|
std::vector<int> compressionParams{ cv::IMWRITE_PNG_COMPRESSION ,9 };
|
|
|
|
|
QQueue< std::shared_ptr<saveJob>>priorityQueue;
|
|
|
|
|
QQueue< std::shared_ptr<saveJob>>defaultQueue;
|
|
|
|
|
int imgRemainCount = 0;
|
|
|
|
|
void run() {
|
2025-07-11 02:02:09 +08:00
|
|
|
|
2025-10-14 01:27:20 +08:00
|
|
|
while (true) {
|
|
|
|
|
QQueue<std::pair<std::string, cv::Mat>> localQueue;
|
|
|
|
|
std::shared_ptr<saveJob> job = nullptr;
|
2025-07-11 02:02:09 +08:00
|
|
|
// 优先处理 priorityQueue
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&mutex);
|
|
|
|
|
if (!priorityQueue.isEmpty()) {
|
2025-10-14 01:27:20 +08:00
|
|
|
job = priorityQueue.dequeue();
|
2025-07-11 02:02:09 +08:00
|
|
|
}
|
2025-10-14 01:27:20 +08:00
|
|
|
else if (!defaultQueue.isEmpty()) {
|
|
|
|
|
job = defaultQueue.dequeue();
|
2025-07-11 02:02:09 +08:00
|
|
|
}
|
2025-03-21 18:25:54 +08:00
|
|
|
}
|
2025-07-11 02:02:09 +08:00
|
|
|
|
|
|
|
|
// 没有任务就跳出
|
2025-10-14 01:27:20 +08:00
|
|
|
if (job == nullptr) {
|
2025-07-11 02:02:09 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-14 01:27:20 +08:00
|
|
|
qDebug() << "saveJob start" << QString::fromStdString(job->jobName) << "remain" << imgRemainCount;
|
|
|
|
|
localQueue.swap(job->imgQueue);
|
|
|
|
|
imgRemainCount -= job->imgQueue.size();
|
2025-07-11 02:02:09 +08:00
|
|
|
// 批量保存
|
|
|
|
|
while (!localQueue.isEmpty()) {
|
|
|
|
|
auto data = std::move(localQueue.dequeue());
|
2025-07-16 16:43:20 +08:00
|
|
|
#if saveImgEnable
|
2025-07-11 02:02:09 +08:00
|
|
|
cv::imwrite(data.first, data.second);
|
2025-07-16 16:43:20 +08:00
|
|
|
#endif
|
2025-03-21 18:25:54 +08:00
|
|
|
}
|
2025-10-14 01:27:20 +08:00
|
|
|
|
|
|
|
|
qDebug() << "saveJob end " << QString::fromStdString(job->jobName);
|
2025-03-21 18:25:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-11 02:02:09 +08:00
|
|
|
|
2025-10-14 01:27:20 +08:00
|
|
|
void addJob(std::shared_ptr<saveJob> job, type type_ = normal) {
|
|
|
|
|
|
|
|
|
|
QMutexLocker locker(&mutex);
|
2025-03-21 18:25:54 +08:00
|
|
|
switch (type_) {
|
|
|
|
|
case normal:
|
2025-10-14 01:27:20 +08:00
|
|
|
defaultQueue.append(job);
|
2025-03-21 18:25:54 +08:00
|
|
|
break;
|
|
|
|
|
case priority:
|
2025-10-14 01:27:20 +08:00
|
|
|
priorityQueue.append(job);
|
2025-03-21 18:25:54 +08:00
|
|
|
break;
|
|
|
|
|
}
|
2025-10-14 01:27:20 +08:00
|
|
|
imgRemainCount += job->imgQueue.size();
|
|
|
|
|
qDebug() << "savejob enqueue" << QString::fromStdString(job->jobName) << "queue size" << priorityQueue.size() << "|" << defaultQueue.size() << " imgRemain" << imgRemainCount;
|
2025-03-21 18:25:54 +08:00
|
|
|
this->start();
|
|
|
|
|
}
|
2025-10-14 01:27:20 +08:00
|
|
|
|
|
|
|
|
int queueSize() {
|
|
|
|
|
QMutexLocker locker(&mutex);
|
|
|
|
|
int size = defaultQueue.size();
|
2025-03-21 18:25:54 +08:00
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-10-14 01:27:20 +08:00
|
|
|
class imgSaveThreadManager {
|
2025-03-21 18:25:54 +08:00
|
|
|
public:
|
2025-06-13 01:55:47 +08:00
|
|
|
imgSaveThreadManager(std::string pathIn) {
|
2025-07-16 16:43:20 +08:00
|
|
|
path = QString::fromStdString(pathIn);
|
2025-06-13 01:55:47 +08:00
|
|
|
}
|
|
|
|
|
~imgSaveThreadManager() {};
|
2025-10-14 01:27:20 +08:00
|
|
|
void setPath(QString pathIn) {
|
2025-06-11 10:00:09 +08:00
|
|
|
path = pathIn;
|
|
|
|
|
}
|
2025-03-21 18:25:54 +08:00
|
|
|
imgSaveThread saveThread;
|
2025-10-14 01:27:20 +08:00
|
|
|
QString genDirName(std::shared_ptr<detData> data, imgSaveThreadType type, saveMode mode) {
|
2025-08-20 16:57:03 +08:00
|
|
|
// 一级目录(根路径)
|
|
|
|
|
const QString basePath = path;
|
|
|
|
|
|
|
|
|
|
// 二级目录(图片类型)
|
|
|
|
|
static const QMap<imgSaveThreadType, QString> typeMap{
|
|
|
|
|
{ oriImg, "原图" },
|
|
|
|
|
{ resImg, "结果图" }
|
|
|
|
|
};
|
|
|
|
|
QString imgTypeDir = typeMap.value(type, "error");
|
|
|
|
|
// 日期与小时
|
|
|
|
|
QDateTime now = QDateTime::currentDateTime();
|
|
|
|
|
QString dateDir = now.toString("yyyy-MM-dd");
|
|
|
|
|
QString hourDir = QString("%1H").arg(now.time().hour());
|
|
|
|
|
|
|
|
|
|
// 检测结果
|
|
|
|
|
QString resultDir;
|
|
|
|
|
if (!data->aiDetOK && !data->halconOK) resultDir = "NG";
|
|
|
|
|
else if (!data->aiDetOK) resultDir = "aiNG";
|
|
|
|
|
else if (!data->halconOK) resultDir = "halconNG";
|
|
|
|
|
else resultDir = "OK";
|
|
|
|
|
|
|
|
|
|
// SN 或相机名
|
|
|
|
|
QString detailName = (data->SNcode.size() > 0 && data->SNcode.size() < 50)
|
|
|
|
|
? QString::fromStdString(data->SNcode)
|
|
|
|
|
: QString::fromStdString(data->cameraKeyName);
|
|
|
|
|
|
|
|
|
|
// 拼接路径
|
|
|
|
|
QDir dir(basePath);
|
|
|
|
|
dir = QDir(dir.filePath(imgTypeDir));
|
2025-10-14 01:27:20 +08:00
|
|
|
if (data->saveObjectName != "")
|
2025-08-31 16:51:18 +08:00
|
|
|
dir = QDir(dir.filePath(data->saveObjectName));
|
2025-08-20 16:57:03 +08:00
|
|
|
dir = QDir(dir.filePath(dateDir));
|
|
|
|
|
dir = QDir(dir.filePath(hourDir));
|
|
|
|
|
dir = QDir(dir.filePath(resultDir));
|
2025-10-14 01:27:20 +08:00
|
|
|
if (mode != oneFile)
|
2025-08-31 16:51:18 +08:00
|
|
|
dir = QDir(dir.filePath(detailName));
|
2025-08-20 16:57:03 +08:00
|
|
|
return dir.path() + "/";
|
2025-03-21 18:25:54 +08:00
|
|
|
}
|
2025-08-20 16:57:03 +08:00
|
|
|
|
2025-10-14 01:27:20 +08:00
|
|
|
std::vector<std::string> genFileNames(std::shared_ptr <detData> data, imgSaveThreadType type, std::string format = "") {
|
|
|
|
|
|
2025-03-21 18:25:54 +08:00
|
|
|
std::vector<std::string> resultFileNames;
|
|
|
|
|
std::string fileName;
|
2025-07-25 00:29:52 +08:00
|
|
|
if (data->SNcode.length() != 0 && data->SNcode.length() < 50)
|
|
|
|
|
fileName += data->SNcode;
|
|
|
|
|
else
|
|
|
|
|
fileName += data->cameraKeyName;
|
2025-06-15 03:06:01 +08:00
|
|
|
for (auto str : data->imgSaveNameParameter)
|
2025-03-21 18:25:54 +08:00
|
|
|
fileName += str + "_";
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case oriImg:
|
|
|
|
|
for (int i = 0; i < data->oriImgs.size(); i++)
|
|
|
|
|
{
|
2025-10-14 01:27:20 +08:00
|
|
|
if (i < data->oriFormat.size())
|
2025-06-15 03:06:01 +08:00
|
|
|
resultFileNames.push_back(fileName + getCurrentDateTime() + "_Count" + std::to_string(i) + "." + data->oriFormat[i]);
|
2025-03-21 18:25:54 +08:00
|
|
|
else
|
|
|
|
|
resultFileNames.push_back(fileName + getCurrentDateTime() + "_Count" + std::to_string(i) + ".bmp");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case resImg:
|
|
|
|
|
for (int i = 0; i < data->resultImgs.size(); i++)
|
|
|
|
|
resultFileNames.push_back(fileName + getCurrentDateTime() + "_Count" + std::to_string(i) + ".jpg");
|
|
|
|
|
break;
|
|
|
|
|
case segImg:
|
|
|
|
|
for (int i = 0; i < data->segOriginImgs.size(); i++)
|
|
|
|
|
resultFileNames.push_back(fileName + "SEG_" + getCurrentDateTime() + "_" + std::to_string(i) + ".png");
|
|
|
|
|
break;
|
|
|
|
|
case clsImg:
|
|
|
|
|
for (int i = 0; i < data->clsOriginImgs.size(); i++)
|
|
|
|
|
resultFileNames.push_back(fileName + "CLS_RES" + getCurrentDateTime() + "_" + data->clsOriginImgs[i].first + "_Count" + std::to_string(i) + ".bmp");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return resultFileNames;
|
|
|
|
|
}
|
2025-10-14 01:27:20 +08:00
|
|
|
void packageData(QQueue<std::pair<std::string, cv::Mat>>& jobQueue, std::vector<std::string> fileNames, std::vector<cv::Mat>& mats) {
|
|
|
|
|
if (fileNames.size() != mats.size())
|
|
|
|
|
qWarning() << "检测到存图文件名数量与图片数量不符";
|
2025-03-21 18:25:54 +08:00
|
|
|
|
2025-10-14 01:27:20 +08:00
|
|
|
for (int index = 0; index < fileNames.size() && index < mats.size(); index++) {
|
|
|
|
|
try {
|
2025-07-16 16:43:20 +08:00
|
|
|
|
2025-10-14 01:27:20 +08:00
|
|
|
if (mats[index].data == nullptr || &mats[index] == nullptr) {
|
|
|
|
|
qDebug() << "在存图线程中发现无效图像";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (...) {
|
|
|
|
|
qDebug() << "在存图线程中发现无效图像";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv::Mat img;
|
|
|
|
|
mats[index].copyTo(img);
|
|
|
|
|
jobQueue.enqueue(std::make_pair(fileNames[index], img));
|
|
|
|
|
}
|
2025-03-21 18:25:54 +08:00
|
|
|
}
|
2025-10-14 01:27:20 +08:00
|
|
|
void packageData(QQueue<std::pair<std::string, cv::Mat>>& jobQueue, std::vector<std::string> fileNames, std::vector<std::pair<std::string, cv::Mat>>& mats) {
|
|
|
|
|
if (fileNames.size() != mats.size())
|
|
|
|
|
qWarning() << "检测到存图文件名数量与图片数量不符";
|
|
|
|
|
|
|
|
|
|
for (int index = 0; index < fileNames.size() && index < mats.size(); index++) {
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
if (mats[index].second.data == nullptr || &mats[index].second == nullptr) {
|
|
|
|
|
qDebug() << "在存图线程中发现无效图像";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (...) {
|
|
|
|
|
qDebug() << "在存图线程中发现无效图像";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv::Mat img;
|
|
|
|
|
mats[index].second.copyTo(img);
|
|
|
|
|
jobQueue.enqueue(std::make_pair(fileNames[index], img));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
void saveOri(std::shared_ptr <detData> data) {
|
|
|
|
|
|
|
|
|
|
if (data->oriImgs.size() != 0) {
|
|
|
|
|
|
|
|
|
|
QString fileSavePath = genDirName(data, oriImg, data->_saveMode);//获取文件夹名
|
|
|
|
|
createDirectoryIfNotExists(fileSavePath);//不存在此文件夹则创建
|
|
|
|
|
std::vector<std::string>fileNames = genFileNames(data, oriImg);//按规则为文件创建命名
|
|
|
|
|
std::shared_ptr<saveJob> job (new saveJob("[ORI_JOB]+ data->SNcode"));
|
|
|
|
|
packageData(job->imgQueue, fileNames, data->oriImgs);//打包数据
|
|
|
|
|
saveThread.addJob(job);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void saveRes(std::shared_ptr <detData> data) {
|
|
|
|
|
|
|
|
|
|
QString fileSavePath = genDirName(data, resImg, data->_saveMode);//获取文件夹名
|
2025-03-21 18:25:54 +08:00
|
|
|
createDirectoryIfNotExists(fileSavePath);//不存在此文件夹则创建
|
2025-10-14 01:27:20 +08:00
|
|
|
std::shared_ptr<saveJob> job(new saveJob("[RES_JOB]"+ data->SNcode));
|
2025-03-21 18:25:54 +08:00
|
|
|
//结果图保存
|
2025-10-14 01:27:20 +08:00
|
|
|
if (data->resultImgs.size() != 0) {
|
|
|
|
|
std::vector<std::string>fileNames = genFileNames(data, resImg);//按规则为文件创建命名
|
|
|
|
|
packageData(job->imgQueue, fileNames, data->resultImgs);//打包数据
|
|
|
|
|
}
|
|
|
|
|
//SEG原图
|
|
|
|
|
if (data->segOriginImgs.size() != 0) {
|
|
|
|
|
std::vector<std::string>fileNames = genFileNames(data, segImg);//按规则为文件创建命名
|
|
|
|
|
packageData(job->imgQueue, fileNames, data->segOriginImgs);//打包数据
|
|
|
|
|
}
|
|
|
|
|
//CLS原图
|
|
|
|
|
if (data->clsOriginImgs.size() != 0) {
|
|
|
|
|
std::vector<std::string>fileNames = genFileNames(data, clsImg);//按规则为文件创建命名
|
|
|
|
|
packageData(job->imgQueue, fileNames, data->clsOriginImgs);//打包数据
|
|
|
|
|
}
|
|
|
|
|
saveThread.addJob(job, imgSaveThread::priority);
|
2025-03-21 18:25:54 +08:00
|
|
|
//额外储存NG文件夹
|
2025-07-11 02:02:09 +08:00
|
|
|
//std::string additionalSaveNgImgPath = genDirName(data, additionalSaveNgImg);
|
|
|
|
|
//std::vector<std::string>additionalSaveNgImgNames = genFileNames(data, additionalSaveNgImg);
|
|
|
|
|
//if (additionalSaveNgImgNames.size() > 0)
|
|
|
|
|
// createDirectoryIfNotExists(additionalSaveNgImgPath);//不存在此文件夹则创建
|
|
|
|
|
//int additionalSaveNgImgNamesIndex = 0;
|
|
|
|
|
//for (int index = 0; index < data->segOriginImgs.size()&& additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++)
|
|
|
|
|
//{
|
|
|
|
|
// saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->segOriginImgs[index]);
|
|
|
|
|
// additionalSaveNgImgNamesIndex++;
|
|
|
|
|
//}
|
|
|
|
|
//for (int index = 0; index < data->oriImgs.size() && additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++)
|
|
|
|
|
//{
|
|
|
|
|
// saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->oriImgs[index]);
|
|
|
|
|
// additionalSaveNgImgNamesIndex++;
|
|
|
|
|
//}
|
|
|
|
|
//for (int index = 0; index < data->resultImgs.size() && additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++)
|
|
|
|
|
//{
|
|
|
|
|
// saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->resultImgs[index]);
|
|
|
|
|
// additionalSaveNgImgNamesIndex++;
|
|
|
|
|
//}
|
2025-03-21 18:25:54 +08:00
|
|
|
}
|
|
|
|
|
private:
|
2025-07-16 16:43:20 +08:00
|
|
|
QString path = "";
|
2025-10-14 01:27:20 +08:00
|
|
|
|
2025-03-21 18:25:54 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#pragma once
|