备份 性能优化
This commit is contained in:
+149
-52
@@ -31,65 +31,162 @@ static std::wstring utf8ToWstring(const std::string& utf8Str) {
|
||||
}
|
||||
// 在图像上绘制中文(仅 Windows,使用 GDI)
|
||||
static void putTextZH(cv::Mat& image, const std::string& text, cv::Point org, int fontSize = 20, cv::Scalar color = cv::Scalar(255, 0, 0)) {
|
||||
// 创建兼容的位图
|
||||
cv::Mat img;
|
||||
if (image.channels() == 1)
|
||||
cv::cvtColor(image, img, cv::COLOR_GRAY2BGRA);
|
||||
else if (image.channels() == 3)
|
||||
cv::cvtColor(image, img, cv::COLOR_BGR2BGRA);
|
||||
else if (image.channels() == 4)
|
||||
img = image.clone();
|
||||
else
|
||||
throw std::runtime_error("Unsupported number of channels");
|
||||
if (image.channels() != 3)
|
||||
throw std::runtime_error("Only supports 3-channel images");
|
||||
|
||||
// 创建 Windows 位图信息
|
||||
HDC hdc = GetDC(nullptr);
|
||||
HDC memDC = CreateCompatibleDC(hdc);
|
||||
BITMAPINFO bmi;
|
||||
memset(&bmi, 0, sizeof(BITMAPINFO));
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = img.cols;
|
||||
bmi.bmiHeader.biHeight = -img.rows; // top-down
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
// 转为 BGRA
|
||||
cv::Mat img;
|
||||
cv::cvtColor(image, img, cv::COLOR_BGR2BGRA);
|
||||
|
||||
void* pBits = nullptr;
|
||||
HBITMAP hBitmap = CreateDIBSection(memDC, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
|
||||
SelectObject(memDC, hBitmap);
|
||||
memcpy(pBits, img.data, img.total() * img.elemSize());
|
||||
HDC hdc = GetDC(nullptr);
|
||||
HDC memDC = CreateCompatibleDC(hdc);
|
||||
|
||||
// 设置字体
|
||||
HFONT hFont = CreateFontW(
|
||||
fontSize, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
|
||||
GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_SWISS, L"微软雅黑");
|
||||
SelectObject(memDC, hFont);
|
||||
BITMAPINFO bmi = {};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = img.cols;
|
||||
bmi.bmiHeader.biHeight = -img.rows;
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
// 设置颜色和背景
|
||||
SetBkMode(memDC, TRANSPARENT);
|
||||
SetTextColor(memDC, RGB(color[2], color[1], color[0]));
|
||||
void* pBits = nullptr;
|
||||
HBITMAP hBitmap = CreateDIBSection(memDC, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
|
||||
SelectObject(memDC, hBitmap);
|
||||
memcpy(pBits, img.data, img.total() * img.elemSize());
|
||||
|
||||
// 输出文字
|
||||
std::wstring wtext = utf8ToWstring(text);
|
||||
TextOutW(memDC, org.x, org.y, wtext.c_str(), static_cast<int>(wtext.length()));
|
||||
HFONT hFont = CreateFontW(fontSize, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
|
||||
GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_SWISS, L"微软雅黑");
|
||||
SelectObject(memDC, hFont);
|
||||
|
||||
// 拷贝回来
|
||||
GetDIBits(memDC, hBitmap, 0, img.rows, img.data, &bmi, DIB_RGB_COLORS);
|
||||
SetBkMode(memDC, TRANSPARENT);
|
||||
SetTextColor(memDC, RGB(color[2], color[1], color[0]));
|
||||
|
||||
// 转换回原始图像格式
|
||||
if (image.channels() == 1)
|
||||
cv::cvtColor(img, image, cv::COLOR_BGRA2GRAY);
|
||||
else if (image.channels() == 3)
|
||||
cv::cvtColor(img, image, cv::COLOR_BGRA2BGR);
|
||||
else if (image.channels() == 4)
|
||||
image = img.clone();
|
||||
TextOutW(memDC, org.x, org.y, utf8ToWstring(text).c_str(), static_cast<int>(utf8ToWstring(text).length()));
|
||||
|
||||
// 清理
|
||||
DeleteObject(hFont);
|
||||
DeleteObject(hBitmap);
|
||||
DeleteDC(memDC);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
}
|
||||
GetDIBits(memDC, hBitmap, 0, img.rows, img.data, &bmi, DIB_RGB_COLORS);
|
||||
|
||||
cv::cvtColor(img, image, cv::COLOR_BGRA2BGR);
|
||||
|
||||
DeleteObject(hFont);
|
||||
DeleteObject(hBitmap);
|
||||
DeleteDC(memDC);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
}
|
||||
class TextRendererBase {
|
||||
public:
|
||||
virtual ~TextRendererBase() = default;
|
||||
virtual void putText(cv::Mat& image, const std::string& text, cv::Point org, cv::Scalar color = cv::Scalar(255, 0, 0)) = 0;
|
||||
};
|
||||
|
||||
class TextRendererZH : public TextRendererBase {
|
||||
public:
|
||||
TextRendererZH(int width, int height, int fontSize = 20, const std::wstring& fontName = L"微软雅黑")
|
||||
: width_(width), height_(height), fontSize_(fontSize), fontName_(fontName)
|
||||
{
|
||||
screenDC_ = GetDC(nullptr);
|
||||
createResources();
|
||||
}
|
||||
|
||||
~TextRendererZH() {
|
||||
releaseResources();
|
||||
if (screenDC_) ReleaseDC(nullptr, screenDC_);
|
||||
}
|
||||
|
||||
void putText(cv::Mat& image, const std::string& text, cv::Point org, cv::Scalar color = cv::Scalar(255, 0, 0)) override {
|
||||
if (image.channels() != 3)
|
||||
throw std::runtime_error("TextRendererZH only supports 3-channel BGR images.");
|
||||
|
||||
if (image.cols != width_ || image.rows != height_) {
|
||||
resize(image.cols, image.rows);
|
||||
}
|
||||
|
||||
cv::Mat img;
|
||||
cv::cvtColor(image, img, cv::COLOR_BGR2BGRA);
|
||||
|
||||
memcpy(pBits_, img.data, img.total() * img.elemSize());
|
||||
|
||||
SetBkMode(memDC_, TRANSPARENT);
|
||||
SetTextColor(memDC_, RGB(color[2], color[1], color[0]));
|
||||
|
||||
std::wstring wtext = utf8ToWstring(text);
|
||||
TextOutW(memDC_, org.x, org.y, wtext.c_str(), (int)wtext.length());
|
||||
|
||||
GetDIBits(memDC_, hBitmap_, 0, height_, img.data, &bmi_, DIB_RGB_COLORS);
|
||||
|
||||
cv::cvtColor(img, image, cv::COLOR_BGRA2BGR);
|
||||
}
|
||||
|
||||
void resize(int width, int height) {
|
||||
if (width == width_ && height == height_) return;
|
||||
|
||||
releaseResources();
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
createResources();
|
||||
}
|
||||
|
||||
private:
|
||||
void createResources() {
|
||||
memDC_ = CreateCompatibleDC(screenDC_);
|
||||
|
||||
memset(&bmi_, 0, sizeof(bmi_));
|
||||
bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi_.bmiHeader.biWidth = width_;
|
||||
bmi_.bmiHeader.biHeight = -height_;
|
||||
bmi_.bmiHeader.biPlanes = 1;
|
||||
bmi_.bmiHeader.biBitCount = 32;
|
||||
bmi_.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
hBitmap_ = CreateDIBSection(memDC_, &bmi_, DIB_RGB_COLORS, &pBits_, nullptr, 0);
|
||||
SelectObject(memDC_, hBitmap_);
|
||||
|
||||
hFont_ = CreateFontW(
|
||||
fontSize_, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
|
||||
GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_SWISS, fontName_.c_str());
|
||||
SelectObject(memDC_, hFont_);
|
||||
}
|
||||
|
||||
void releaseResources() {
|
||||
if (hFont_) {
|
||||
DeleteObject(hFont_);
|
||||
hFont_ = nullptr;
|
||||
}
|
||||
if (hBitmap_) {
|
||||
DeleteObject(hBitmap_);
|
||||
hBitmap_ = nullptr;
|
||||
pBits_ = nullptr;
|
||||
}
|
||||
if (memDC_) {
|
||||
DeleteDC(memDC_);
|
||||
memDC_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// UTF8 转宽字符串
|
||||
static std::wstring utf8ToWstring(const std::string& str) {
|
||||
if (str.empty()) return {};
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
std::wstring wstrTo(size_needed, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstrTo[0], size_needed);
|
||||
return wstrTo;
|
||||
}
|
||||
|
||||
private:
|
||||
int width_;
|
||||
int height_;
|
||||
int fontSize_;
|
||||
std::wstring fontName_;
|
||||
|
||||
HDC screenDC_ = nullptr;
|
||||
HDC memDC_ = nullptr;
|
||||
HBITMAP hBitmap_ = nullptr;
|
||||
void* pBits_ = nullptr;
|
||||
BITMAPINFO bmi_{};
|
||||
|
||||
HFONT hFont_ = nullptr;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@ void camera::run()
|
||||
int queueSize = requestQueue.size() ;
|
||||
if (queueSize >0)
|
||||
{
|
||||
mutex.lock();
|
||||
QMutexLocker locker(&mutex);
|
||||
std::shared_ptr <detData>data = requestQueue.dequeue();
|
||||
data->oriImgMutex.lock();
|
||||
QMutexLocker locker2(&data->oriImgMutex);
|
||||
while(1){
|
||||
getCameraImg(data->oriImgs, data->imgAmountMin);
|
||||
if (data->oriImgs.size() >= data->imgAmountMin){
|
||||
@@ -76,8 +76,6 @@ void camera::run()
|
||||
showLost++;
|
||||
qDebug() << cameraModulePtr.dllPath << "Lost show";
|
||||
}
|
||||
data->oriImgMutex.unlock();
|
||||
mutex.unlock();
|
||||
break;
|
||||
}
|
||||
QThread::msleep(1);
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
void getImgQueueSize();
|
||||
void captureImage(std::shared_ptr <detData> dataIn)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
requestQueue.enqueue(dataIn);
|
||||
std::thread([this]() {trigger();}).detach();
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ public:
|
||||
|
||||
if (dataPtr == nullptr)
|
||||
qDebug() << "创建新容器失败";
|
||||
qDebug() << "创建新容器成功";
|
||||
return dataPtr;
|
||||
}
|
||||
std::shared_ptr <detData> getDetData(QString uuid)
|
||||
|
||||
@@ -9,7 +9,6 @@ enum imgSaveThreadType
|
||||
resImg,
|
||||
segImg,
|
||||
clsImg,
|
||||
additionalSaveNgImg,
|
||||
tiffOriImg
|
||||
};
|
||||
class imgSaveThread : public QThread
|
||||
@@ -25,32 +24,36 @@ public:
|
||||
QQueue<std::pair<std::string, cv::Mat>>imgQueue;
|
||||
void run()
|
||||
{
|
||||
while (imgQueue.size() > 0 || priorityQueue.size() > 0)
|
||||
while (true)
|
||||
{
|
||||
if (priorityQueue.size() > 0) {
|
||||
mutex.lock();
|
||||
std::pair<std::string, cv::Mat> data = priorityQueue.dequeue();
|
||||
mutex.unlock();
|
||||
qDebug() << "saving priority img " << QString::fromStdString(data.first);
|
||||
cv::imwrite(data.first, data.second);
|
||||
data.second.release();
|
||||
QQueue<std::pair<std::string, cv::Mat>> localQueue;
|
||||
|
||||
// 优先处理 priorityQueue
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (!priorityQueue.isEmpty()) {
|
||||
localQueue.swap(priorityQueue);
|
||||
}
|
||||
else if (!imgQueue.isEmpty()) {
|
||||
localQueue.swap(imgQueue);
|
||||
}
|
||||
}
|
||||
else if (imgQueue.size() > 0) {
|
||||
mutex.lock();
|
||||
std::pair<std::string, cv::Mat> data = imgQueue.dequeue();
|
||||
mutex.unlock();
|
||||
|
||||
// 没有任务就跳出
|
||||
if (localQueue.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 批量保存
|
||||
while (!localQueue.isEmpty()) {
|
||||
auto data = std::move(localQueue.dequeue());
|
||||
qDebug() << "saving img " << QString::fromStdString(data.first);
|
||||
if (data.first.find(".png") != std::string::npos){
|
||||
cv::resize(data.second, data.second, cv::Size(), 0.25, 0.25);
|
||||
cv::imwrite(data.first, data.second, compressionParams);
|
||||
}
|
||||
else {
|
||||
cv::imwrite(data.first, data.second);
|
||||
}
|
||||
data.second.release();
|
||||
cv::imwrite(data.first, data.second);
|
||||
// 不需要手动 release(), cv::Mat 会自动析构
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addData(std::string str, cv::Mat& mat,type type_ = normal)
|
||||
{
|
||||
try
|
||||
@@ -107,7 +110,8 @@ public:
|
||||
std::string imgTypeDirStr;//二级目录,图片类型决定 sample "Result"
|
||||
std::string dateDirStr;//三级目录,日期决定 sample"2024-6-8"
|
||||
std::string hourDirStr;//四级目录,小时段决定 sample "14H"
|
||||
std::string detailNameStr;//五级目录 主要是SN,如没有sn则可能是相机名称
|
||||
std::string okNgStr;//五级目录,OK/NG决定 sample "OK"
|
||||
std::string detailNameStr;//六级目录 主要是SN,如没有sn则可能是相机名称
|
||||
#pragma region 赋值
|
||||
//imgTypeDirStr
|
||||
switch (type)
|
||||
@@ -118,9 +122,6 @@ public:
|
||||
case resImg:
|
||||
imgTypeDirStr = "Result";
|
||||
break;
|
||||
case additionalSaveNgImg:
|
||||
imgTypeDirStr = "NGIMG";
|
||||
break;
|
||||
default:
|
||||
imgTypeDirStr = "error";
|
||||
}
|
||||
@@ -130,7 +131,10 @@ public:
|
||||
dateDirStr = currentDateTime.toString("yyyy-MM-dd").toStdString();
|
||||
hourDirStr = std::to_string(currentDateTime.time().hour()) + "H//";
|
||||
}
|
||||
|
||||
if(data->aiDetOK == true && data->halconOK == true)
|
||||
okNgStr = "OK";
|
||||
else
|
||||
okNgStr = "NG";
|
||||
//detailNameStr
|
||||
if ((data->SNcode).length() != 0)
|
||||
detailNameStr = data->SNcode;
|
||||
@@ -141,6 +145,7 @@ public:
|
||||
imgTypeDirStr + "/" +
|
||||
dateDirStr + "/" +
|
||||
hourDirStr + "/" +
|
||||
okNgStr + "/" +
|
||||
detailNameStr + "/");
|
||||
}
|
||||
std::vector<std::string> genFileNames(std::shared_ptr <detData> data, imgSaveThreadType type, std::string format = "")
|
||||
@@ -173,22 +178,6 @@ public:
|
||||
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;
|
||||
case additionalSaveNgImg:
|
||||
if (data->aiDetOK == false || data->halconOK == false)
|
||||
{
|
||||
for (int i = 0; i < data->segOriginImgs.size(); i++)
|
||||
resultFileNames.push_back(fileName + "NGIMG_segOriginImgs_" + getCurrentDateTime() + "_" + std::to_string(i) + ".png");
|
||||
for (int i = 0; i < data->oriImgs.size(); i++)
|
||||
{
|
||||
if(data->oriImgs[i].depth() == 2)
|
||||
resultFileNames.push_back(fileName + "NGIMG_oriImgs_" + getCurrentDateTime() + "_" + std::to_string(i) + ".tiff");
|
||||
else
|
||||
resultFileNames.push_back(fileName + "NGIMG_oriImgs_" + getCurrentDateTime() + "_" + std::to_string(i) + ".png");
|
||||
}
|
||||
for (int i = 0; i < data->resultImgs.size(); i++)
|
||||
resultFileNames.push_back(fileName + "NGIMG_resultImgs_" + getCurrentDateTime() + "_" + std::to_string(i) + ".jpg");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return resultFileNames;
|
||||
}
|
||||
@@ -211,44 +200,39 @@ public:
|
||||
//结果图保存
|
||||
std::vector<std::string>resFileNames = genFileNames(data, resImg);
|
||||
for (int index = 0; index < resFileNames.size(); index++)
|
||||
{
|
||||
saveThread.addData(fileSavePath + resFileNames[index], data->resultImgs[index], imgSaveThread::priority);
|
||||
}
|
||||
|
||||
//seg
|
||||
std::string segAndClsPath = fileSavePath + "detImgs//" + getCurrentDateTime() + "//";
|
||||
std::vector<std::string>segFileNames = genFileNames(data, segImg);
|
||||
if (segFileNames.size() > 0)
|
||||
createDirectoryIfNotExists(segAndClsPath);//不存在此文件夹则创建
|
||||
for (int index = 0; index < segFileNames.size(); index++)
|
||||
saveThread.addData(segAndClsPath + segFileNames[index], data->segOriginImgs[index]);
|
||||
saveThread.addData(fileSavePath + segFileNames[index], data->segOriginImgs[index]);
|
||||
|
||||
//cls
|
||||
std::vector<std::string>clsFileNames = genFileNames(data, clsImg);
|
||||
for (int index = 0; index < clsFileNames.size(); index++)
|
||||
saveThread.addData(segAndClsPath + clsFileNames[index], data->clsOriginImgs[index].second);
|
||||
saveThread.addData(fileSavePath + clsFileNames[index], data->clsOriginImgs[index].second);
|
||||
|
||||
//额外储存NG文件夹
|
||||
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++;
|
||||
}
|
||||
//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++;
|
||||
//}
|
||||
}
|
||||
private:
|
||||
std::string path = "";
|
||||
|
||||
@@ -208,7 +208,7 @@ public:
|
||||
{
|
||||
if (checkTrigger(callback.triggerAddress, callback.triggerValue, byteArray) == true)
|
||||
{
|
||||
std::cout << "func:" << callback.funcName << "->triggered" << std::endl;
|
||||
qDebug() << "func:" << QString::fromStdString(callback.funcName) << "->triggered";
|
||||
std::thread([callback, byteArray]() mutable {
|
||||
try {
|
||||
callback.func(byteArray); // 执行回调函数
|
||||
|
||||
@@ -16,11 +16,40 @@ public:
|
||||
detCallbackChain(imgSaveThreadManager* imgSaveThreadManager = nullptr,
|
||||
handleAiModelsTemplate* _handleAiModelsIn = nullptr)
|
||||
: _imgSaveThreadManager(imgSaveThreadManager), _handleAiModels(_handleAiModelsIn) {
|
||||
int threadCount = 2 * QThread::idealThreadCount();
|
||||
QThreadPool::globalInstance()->setMaxThreadCount(threadCount);
|
||||
|
||||
}
|
||||
void ansyncProcess(std::shared_ptr <detData> data) {
|
||||
QtConcurrent::run([this, data]() {
|
||||
#pragma region 方法1 较慢
|
||||
QFuture<void> future = QtConcurrent::run([this, data]() {
|
||||
process(data);
|
||||
});
|
||||
#pragma endregion
|
||||
#pragma region 方法2 较慢 安全
|
||||
//QThread* thread = new QThread;
|
||||
//QObject::connect(thread, &QThread::started, [this, data]() {
|
||||
// process(data);
|
||||
// });
|
||||
//QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||||
//thread->start();
|
||||
//thread->setPriority(QThread::TimeCriticalPriority);
|
||||
#pragma endregion
|
||||
#pragma region 方法3 不安全
|
||||
/* std::async(std::launch::async, [this, data]() {
|
||||
process(data);
|
||||
});*/
|
||||
#pragma endregion
|
||||
#pragma region 方法4
|
||||
//std::thread([this, data]() {
|
||||
// process(data);
|
||||
// }).detach();
|
||||
#pragma endregion
|
||||
#pragma region 方法5
|
||||
//QThreadPool::globalInstance()->start([this, data]() {
|
||||
// process(data);
|
||||
// });
|
||||
#pragma endregion
|
||||
}
|
||||
void process(std::shared_ptr <detData> data) {
|
||||
|
||||
@@ -28,11 +57,13 @@ public:
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
//等待图片满足要求后,储存原图,异步执行算法函数
|
||||
std::async(std::launch::async, &imgSaveThreadManager::saveOri, _imgSaveThreadManager, data);
|
||||
|
||||
algoEvaluate(data, _handleAiModels);
|
||||
//执行完毕后异步获取储存使用结果
|
||||
resultHandle(data);
|
||||
|
||||
// 确保线程安全退出
|
||||
qDebug() << "Processing finished for data with UUID:" << data->uuid;
|
||||
QThread::currentThread()->quit();
|
||||
}
|
||||
|
||||
bool algoEvaluate(std::shared_ptr <detData> data, handleAiModelsTemplate* model) {
|
||||
@@ -67,28 +98,39 @@ public:
|
||||
}
|
||||
|
||||
void resultHandle(std::shared_ptr <detData> data){
|
||||
for (auto func : resultHandleCallbacks)
|
||||
func(data);
|
||||
detectionResultHandle(data);
|
||||
std::shared_ptr <detData> dataPtr = data;
|
||||
detDataManager* dManager = data->dManager;
|
||||
data->dManager->deleteDetData(data->uuid);
|
||||
try {
|
||||
detectionResultHandle(data);
|
||||
for (auto func : resultHandleCallbacks)
|
||||
func(data);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Caught exception: " << e.what() << std::endl;
|
||||
qDebug() << "Caught exception: " << e.what();
|
||||
}
|
||||
catch (...) {
|
||||
qDebug() << "Caught exception: ";
|
||||
}
|
||||
// 删除数据释放资源
|
||||
|
||||
}
|
||||
|
||||
// 通用的检测结果处理
|
||||
void detectionResultHandle(std::shared_ptr<detData> data) {
|
||||
//计算处理总耗时(CT)
|
||||
QString totalCtStr = QString::fromStdString(data->SNcode) + QString::fromStdString(data->cameraKeyName) + "\n";
|
||||
totalCtStr += "algoCT->" + QString::number(data->algoTime) + "ms";
|
||||
QString totalCtStr = "algoCT->" + QString::number(data->algoTime) + "ms";
|
||||
qDebug() << totalCtStr;
|
||||
std::cout << totalCtStr.toStdString() << std::endl;
|
||||
//std::cout << totalCtStr.toStdString() << std::endl;
|
||||
// 将耗时数据保存入数据库或日志
|
||||
// data->sqlVec.push_back({ "SN", data->SNcode });
|
||||
// data->sqlVec.push_back({ "totalCt", std::to_string(totalCt.count()) + "ms" });
|
||||
|
||||
// 异步保存检测图像
|
||||
std::async(std::launch::async, &imgSaveThreadManager::saveOri, _imgSaveThreadManager, data);
|
||||
std::async(std::launch::async, &imgSaveThreadManager::saveRes, _imgSaveThreadManager, data);
|
||||
|
||||
// 删除数据释放资源
|
||||
detDataManager* dManager = data->dManager;
|
||||
data->dManager->deleteDetData(data->uuid);
|
||||
}
|
||||
#pragma region 注册回调函数
|
||||
|
||||
@@ -107,5 +149,6 @@ private:
|
||||
|
||||
std::vector< resultHandleCallback>resultHandleCallbacks;
|
||||
processAlgoCallback _processAlgoCallback;
|
||||
//std::vector<QFuture<void>> futures; // 用于存储异步处理的结果
|
||||
};
|
||||
#pragma once
|
||||
@@ -17,136 +17,129 @@
|
||||
#include <QDate>
|
||||
#include <QString>
|
||||
#include <qline>
|
||||
class logWidget :public QWidget
|
||||
{
|
||||
|
||||
//行数上限
|
||||
const int MAX_LOGWIDGET_LINES = 1000;
|
||||
|
||||
class logWidget : public QWidget {
|
||||
public:
|
||||
enum logType
|
||||
{
|
||||
stdConsle,
|
||||
qtConsle
|
||||
};
|
||||
QVBoxLayout* Layout;
|
||||
QHBoxLayout* btnLayout;
|
||||
std::vector<gradientButton*>btns;
|
||||
QLabel* icon, * Title;
|
||||
QStackedWidget* widgets;
|
||||
QTextEdit stdConsoleOutput;
|
||||
QTextEdit qtConsoleOutput;
|
||||
logWidget()
|
||||
{
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::WindowText, Qt::white);
|
||||
QFont font("微软雅黑", 24, QFont::Bold);
|
||||
enum logType {
|
||||
stdConsle,
|
||||
qtConsle
|
||||
};
|
||||
QVBoxLayout* Layout;
|
||||
QHBoxLayout* btnLayout;
|
||||
std::vector<gradientButton*> btns;
|
||||
QLabel* icon, * Title;
|
||||
QStackedWidget* widgets;
|
||||
QPlainTextEdit stdConsoleOutput;
|
||||
QPlainTextEdit qtConsoleOutput;
|
||||
|
||||
widgets = new QStackedWidget();
|
||||
widgets->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
widgets->setMaximumSize(9999, 9999);
|
||||
logWidget() {
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::WindowText, Qt::white);
|
||||
QFont font("微软雅黑", 24, QFont::Bold);
|
||||
|
||||
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
this->setMaximumSize(9999, 9999);
|
||||
this->setObjectName("configSettingWidget");
|
||||
//this->setStyleSheet(QString("QWidget {background-color: rgb(%1)}").arg(sColor_Gold));
|
||||
//this->showMaximized();
|
||||
Layout = new QVBoxLayout(this);
|
||||
Layout->setContentsMargins(0, 0, 0, 0);
|
||||
Layout->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
widgets = new QStackedWidget();
|
||||
widgets->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
Title = new QLabel(u8"日志");
|
||||
Title->setFont(font);
|
||||
Title->setPalette(palette);
|
||||
Title->setAlignment(Qt::AlignLeft);
|
||||
Layout->addWidget(Title);
|
||||
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
this->setObjectName("configSettingWidget");
|
||||
|
||||
btnLayout = new QHBoxLayout();
|
||||
btnLayout->setContentsMargins(0, 0, 0, 0);
|
||||
btnLayout->setAlignment(Qt::AlignLeft);
|
||||
Layout = new QVBoxLayout(this);
|
||||
Layout->setContentsMargins(0, 0, 0, 0);
|
||||
Layout->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
|
||||
Layout->addLayout(btnLayout);
|
||||
Title = new QLabel(u8"日志");
|
||||
Title->setFont(font);
|
||||
Title->setPalette(palette);
|
||||
Title->setAlignment(Qt::AlignLeft);
|
||||
Layout->addWidget(Title);
|
||||
|
||||
stdConsoleOutput.setReadOnly(true);
|
||||
qtConsoleOutput.setReadOnly(true);
|
||||
addLogWidget(u8"控制台", &stdConsoleOutput, logType::stdConsle);
|
||||
addLogWidget(u8"QT日志", &qtConsoleOutput, logType::qtConsle);
|
||||
btns[0]->pressed();
|
||||
btnLayout = new QHBoxLayout();
|
||||
btnLayout->setContentsMargins(0, 0, 0, 0);
|
||||
btnLayout->setAlignment(Qt::AlignLeft);
|
||||
Layout->addLayout(btnLayout);
|
||||
|
||||
Layout->addWidget(widgets);
|
||||
}
|
||||
void addLogWidget(QString btnText,QTextEdit* textWidget, logType type)
|
||||
{
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::WindowText, Qt::white);
|
||||
QFont font("微软雅黑", 13, QFont::Bold);
|
||||
QFont font2("微软雅黑", 8);
|
||||
QFont font3("微软雅黑", 16, QFont::Bold);
|
||||
stdConsoleOutput.setReadOnly(true);
|
||||
qtConsoleOutput.setReadOnly(true);
|
||||
stdConsoleOutput.setMaximumBlockCount(MAX_LOGWIDGET_LINES);
|
||||
qtConsoleOutput.setMaximumBlockCount(MAX_LOGWIDGET_LINES);
|
||||
stdConsoleOutput.setStyleSheet("background-color: rgb(30,30,30); color: white;");
|
||||
qtConsoleOutput.setStyleSheet("background-color: rgb(30,30,30); color: white;");
|
||||
|
||||
//切换配置页面
|
||||
int index = btns.size();
|
||||
gradientButton* btn = new gradientButton(160, 50, btnText, "");
|
||||
btnLayout->addWidget(btn);
|
||||
btns.push_back(btn);
|
||||
connect(btn, &QPushButton::clicked, [=]()
|
||||
{
|
||||
for (auto btnPtr : btns)
|
||||
btnPtr->toDefault();
|
||||
btn->pressed();
|
||||
widgets->setCurrentIndex(index);
|
||||
});
|
||||
addLogWidget(u8"控制台", &stdConsoleOutput, logType::stdConsle);
|
||||
addLogWidget(u8"QT日志", &qtConsoleOutput, logType::qtConsle);
|
||||
btns[0]->pressed();
|
||||
|
||||
//页面widget
|
||||
QWidget* widget = new QWidget();
|
||||
widgets->addWidget(widget);
|
||||
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
Layout->addWidget(widgets);
|
||||
}
|
||||
|
||||
QVBoxLayout* widgetLayout = new QVBoxLayout(widget);
|
||||
widgetLayout->setContentsMargins(0, 0, 0, 0);
|
||||
widgetLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
void addLogWidget(QString btnText, QPlainTextEdit* textWidget, logType type) {
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::WindowText, Qt::white);
|
||||
QFont font2("微软雅黑", 8);
|
||||
|
||||
QHBoxLayout* toolHLayout = new QHBoxLayout();
|
||||
widgetLayout->addLayout(toolHLayout);
|
||||
int index = btns.size();
|
||||
gradientButton* btn = new gradientButton(160, 50, btnText, "");
|
||||
btnLayout->addWidget(btn);
|
||||
btns.push_back(btn);
|
||||
connect(btn, &QPushButton::clicked, [=]() {
|
||||
for (auto btnPtr : btns)
|
||||
btnPtr->toDefault();
|
||||
btn->pressed();
|
||||
widgets->setCurrentIndex(index);
|
||||
});
|
||||
|
||||
QLabel* toolName = new QLabel(u8"过滤器");
|
||||
toolName->setFont(font2);
|
||||
toolName->setPalette(palette);
|
||||
toolHLayout->addWidget(toolName);
|
||||
QWidget* widget = new QWidget();
|
||||
widgets->addWidget(widget);
|
||||
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
QStringList options = { "Debug","Info","Warning" };
|
||||
QComboBox* filterSelector = new QComboBox();
|
||||
filterSelector->addItems(options);
|
||||
toolHLayout->addWidget(filterSelector);
|
||||
QVBoxLayout* widgetLayout = new QVBoxLayout(widget);
|
||||
widgetLayout->setContentsMargins(0, 0, 0, 0);
|
||||
widgetLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
|
||||
QLabel* toolName2 = new QLabel(u8" 正则匹配");
|
||||
toolName2->setFont(font2);
|
||||
toolName2->setPalette(palette);
|
||||
toolHLayout->addWidget(toolName2);
|
||||
QHBoxLayout* toolHLayout = new QHBoxLayout();
|
||||
widgetLayout->addLayout(toolHLayout);
|
||||
|
||||
QLineEdit* lineedit = new QLineEdit();
|
||||
toolHLayout->addWidget(lineedit);
|
||||
QLabel* toolName = new QLabel(u8"过滤器");
|
||||
toolName->setFont(font2);
|
||||
toolName->setPalette(palette);
|
||||
toolHLayout->addWidget(toolName);
|
||||
|
||||
QStringList options = { "Debug","Info","Warning" };
|
||||
QComboBox* filterSelector = new QComboBox();
|
||||
filterSelector->addItems(options);
|
||||
toolHLayout->addWidget(filterSelector);
|
||||
|
||||
QLabel* toolName2 = new QLabel(u8" 正则匹配");
|
||||
toolName2->setFont(font2);
|
||||
toolName2->setPalette(palette);
|
||||
toolHLayout->addWidget(toolName2);
|
||||
|
||||
QLineEdit* lineedit = new QLineEdit();
|
||||
toolHLayout->addWidget(lineedit);
|
||||
|
||||
widgetLayout->addWidget(textWidget);
|
||||
}
|
||||
|
||||
QString getCurrentDate() {
|
||||
return QDate::currentDate().toString("yyyy-MM-dd");
|
||||
}
|
||||
|
||||
QString getCurrentTime() {
|
||||
return QTime::currentTime().toString("HH:mm:ss.zzz");
|
||||
}
|
||||
|
||||
widgetLayout->addWidget(textWidget);
|
||||
textWidget->setStyleSheet("background-color: rgb(30,30,30);");
|
||||
}
|
||||
QString getCurrentDate() {
|
||||
QDate currentDate = QDate::currentDate();
|
||||
return currentDate.toString("yyyy-MM-dd");
|
||||
}
|
||||
QString getCurrentTime() {
|
||||
QTime currentTime = QTime::currentTime();
|
||||
return currentTime.toString("HH:mm:ss.zzz");
|
||||
}
|
||||
public slots:
|
||||
void recvStdLogMsg(QString logMsg)
|
||||
{
|
||||
QString text = "<span style='color: yellow;'>" + getCurrentDate() + "</span> "
|
||||
"<span style='color: yellow;'>" + getCurrentTime() + "</span> "
|
||||
"<span style='color: white;'>" + logMsg + "</span> ";
|
||||
stdConsoleOutput.append(text);
|
||||
}
|
||||
void recvQtLogMsg(QString logMsg)
|
||||
{
|
||||
QString text = "<span style='color: yellow;'>" + getCurrentDate() + "</span> "
|
||||
"<span style='color: yellow;'>" + getCurrentTime() + "</span> "
|
||||
"<span style='color: white;'>" + logMsg + "</span> ";
|
||||
qtConsoleOutput.append(text);
|
||||
}
|
||||
void recvStdLogMsg(QString logMsg) {
|
||||
QString text = getCurrentDate() + " " + getCurrentTime() + " " + logMsg;
|
||||
stdConsoleOutput.appendPlainText(text);
|
||||
}
|
||||
|
||||
void recvQtLogMsg(QString logMsg) {
|
||||
QString text = getCurrentDate() + " " + getCurrentTime() + " " + logMsg;
|
||||
qtConsoleOutput.appendPlainText(text);
|
||||
}
|
||||
};
|
||||
#pragma once
|
||||
@@ -19,8 +19,8 @@ public:
|
||||
m_plot(new QCustomPlot(this))
|
||||
{
|
||||
QString title = QString::fromStdString(json.value("key", "默认标题"));
|
||||
double limitHigh = json.value("limitHigh", 100.0);
|
||||
double limitLow = json.value("limitLow", 0.0);
|
||||
m_limitHigh = json.value("limitHigh", 100.0);
|
||||
m_limitLow = json.value("limitLow", 0.0);
|
||||
m_maxPoints = json.value("maxPoints", 50);
|
||||
|
||||
QFont font("微软雅黑", 24, QFont::Bold);
|
||||
@@ -40,29 +40,81 @@ public:
|
||||
m_mainLayout->addWidget(m_plot);
|
||||
|
||||
m_plot->setBackground(Qt::transparent);
|
||||
|
||||
// 主蓝色曲线
|
||||
m_plot->addGraph();
|
||||
m_plot->graph(0)->setPen(QPen(Qt::blue, 2));
|
||||
m_plot->graph(0)->setScatterStyle(QCPScatterStyle::ssNone);
|
||||
|
||||
m_plot->yAxis->setRange(-1,3);
|
||||
addLimitLines(limitHigh, limitLow);
|
||||
// 新增红色点图层
|
||||
m_plot->addGraph();
|
||||
m_plot->graph(1)->setLineStyle(QCPGraph::lsNone);
|
||||
m_plot->graph(1)->setScatterStyle(
|
||||
QCPScatterStyle(QCPScatterStyle::ssDisc, QPen(Qt::red), QBrush(Qt::red), 6)
|
||||
);
|
||||
m_plot->xAxis->setBasePen(QPen(Qt::white, 2)); // 坐标轴线
|
||||
m_plot->yAxis->setBasePen(QPen(Qt::white, 2));
|
||||
|
||||
m_plot->xAxis->setTickPen(QPen(Qt::white)); // 刻度线
|
||||
m_plot->yAxis->setTickPen(QPen(Qt::white));
|
||||
|
||||
m_plot->xAxis->setSubTickPen(QPen(Qt::white)); // 次刻度
|
||||
m_plot->yAxis->setSubTickPen(QPen(Qt::white));
|
||||
|
||||
m_plot->xAxis->setTickLabelColor(Qt::white); // 数字标签
|
||||
m_plot->yAxis->setTickLabelColor(Qt::white);
|
||||
|
||||
m_plot->xAxis->setLabelColor(Qt::white); // "时间" / "数值" 文字
|
||||
m_plot->yAxis->setLabelColor(Qt::white);
|
||||
|
||||
m_plot->yAxis->setRange(m_limitLow - 1, m_limitHigh + 1);
|
||||
addLimitLines(m_limitHigh, m_limitLow);
|
||||
}
|
||||
|
||||
void appendData(double value) {
|
||||
m_xData.append(m_timeCounter);
|
||||
m_yData.append(value);
|
||||
|
||||
// 如果超限,添加到红点数据并添加文本
|
||||
if (value > m_limitHigh || value < m_limitLow) {
|
||||
m_xOverLimit.append(m_timeCounter);
|
||||
m_yOverLimit.append(value);
|
||||
|
||||
auto* textItem = new QCPItemText(m_plot);
|
||||
textItem->setPositionAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
textItem->position->setType(QCPItemPosition::ptPlotCoords);
|
||||
textItem->position->setCoords(m_timeCounter, value);
|
||||
textItem->setText(QString::number(value, 'f', 2));
|
||||
textItem->setColor(Qt::yellow);
|
||||
textItem->setFont(QFont("微软雅黑", 8, QFont::Bold));
|
||||
m_textItems.append(textItem);
|
||||
}
|
||||
|
||||
++m_timeCounter;
|
||||
|
||||
// 保持窗口大小
|
||||
if (m_xData.size() > m_maxPoints) {
|
||||
m_xData.removeFirst();
|
||||
m_yData.removeFirst();
|
||||
}
|
||||
if (m_xOverLimit.size() > m_maxPoints) {
|
||||
m_xOverLimit.removeFirst();
|
||||
m_yOverLimit.removeFirst();
|
||||
}
|
||||
|
||||
// 清理超出窗口范围的文本
|
||||
while (!m_textItems.isEmpty() && m_textItems.first()->position->key() < m_timeCounter - m_maxPoints) {
|
||||
m_plot->removeItem(m_textItems.first());
|
||||
m_textItems.removeFirst();
|
||||
}
|
||||
|
||||
m_plot->graph(0)->setData(m_xData, m_yData);
|
||||
m_plot->graph(1)->setData(m_xOverLimit, m_yOverLimit);
|
||||
m_plot->xAxis->setRange(qMax(0.0, m_timeCounter - m_maxPoints), m_timeCounter);
|
||||
m_plot->replot();
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
void contextMenuEvent(QContextMenuEvent* event) override {
|
||||
QMenu menu(this);
|
||||
@@ -84,20 +136,31 @@ private slots:
|
||||
private:
|
||||
void applyJsonParams() {
|
||||
m_maxPoints = m_json.value("maxPoints", m_maxPoints);
|
||||
m_limitHigh = m_json.value("limitHigh", m_limitHigh);
|
||||
m_limitLow = m_json.value("limitLow", m_limitLow);
|
||||
|
||||
m_title->setText(QString::fromStdString(m_json.value("key", m_title->text().toStdString())));
|
||||
m_plot->clearItems();
|
||||
addLimitLines(m_json.value("limitHigh", 100.0), m_json.value("limitLow", 0.0));
|
||||
addLimitLines(m_limitHigh, m_limitLow);
|
||||
|
||||
// 更新坐标范围
|
||||
m_plot->yAxis->setRange(m_limitLow - 2, m_limitHigh + 2);
|
||||
|
||||
// 清空超限数据
|
||||
m_xOverLimit.clear();
|
||||
m_yOverLimit.clear();
|
||||
|
||||
m_plot->replot();
|
||||
}
|
||||
|
||||
void addLimitLines(double high, double low) {
|
||||
auto* line1 = new QCPItemStraightLine(m_plot);
|
||||
line1->setPen(QPen(Qt::red, 3, Qt::DashLine));
|
||||
line1->setPen(QPen(Qt::green, 1, Qt::DashLine));
|
||||
line1->point1->setCoords(0, high);
|
||||
line1->point2->setCoords(1, high);
|
||||
|
||||
auto* line2 = new QCPItemStraightLine(m_plot);
|
||||
line2->setPen(QPen(Qt::red, 3, Qt::DashLine));
|
||||
line2->setPen(QPen(Qt::green, 1, Qt::DashLine));
|
||||
line2->point1->setCoords(0, low);
|
||||
line2->point2->setCoords(1, low);
|
||||
}
|
||||
@@ -106,12 +169,15 @@ private:
|
||||
QVBoxLayout* m_mainLayout;
|
||||
QLabel* m_title;
|
||||
QCustomPlot* m_plot;
|
||||
|
||||
QList<QCPItemText*> m_textItems;
|
||||
QVector<double> m_xData, m_yData;
|
||||
QVector<double> m_xOverLimit, m_yOverLimit; // 新增用于红色点
|
||||
int m_maxPoints = 50;
|
||||
double m_timeCounter = 0;
|
||||
double m_limitHigh = -1, m_limitLow = 2;
|
||||
};
|
||||
|
||||
|
||||
class SummuaryDataPage :public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -35,5 +35,4 @@ logService::~logService()
|
||||
{
|
||||
delete _stdConsoleRedirector;
|
||||
delete _qtRediretor;
|
||||
spdlog::shutdown();
|
||||
}
|
||||
@@ -5,40 +5,36 @@
|
||||
#include <spdlog/async.h>
|
||||
#include <spdlog/async_logger.h>
|
||||
#include <spdlog/sinks/daily_file_sink.h>
|
||||
|
||||
#define LOG_DEBUG(logger, msg) \
|
||||
(logger).LogDebug((msg), __FILE__, __LINE__);
|
||||
|
||||
class SpdLogger {
|
||||
public:
|
||||
SpdLogger(const std::string& file_path, const std::string& file_name) {
|
||||
SpdLogger(std::string& file_path, std::string file_name) {
|
||||
try {
|
||||
// 使用同步的 daily_logger_st
|
||||
logger_ = spdlog::daily_logger_st(
|
||||
file_name, file_path + "/" + file_name + ".log", 0, 0);
|
||||
logger_->set_level(spdlog::level::debug);
|
||||
spdlog::flush_every(std::chrono::seconds(1));
|
||||
spdlog::init_thread_pool(8192, 5); // 初始化线程池
|
||||
logger_ = spdlog::daily_logger_mt<spdlog::async_factory>(
|
||||
file_name, file_path + "/" + file_name + ".log", 0, 0); // 日志按天保存
|
||||
logger_->set_level(spdlog::level::debug); // 设置日志级别为 Debug
|
||||
spdlog::flush_every(std::chrono::seconds(1));//每秒写入一次
|
||||
logger_->set_pattern("[%Y-%m-%d %H:%M:%S] [Thread %t] [%l] %v");
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
std::cout << "Log initialization failed: " << ex.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void LogDebug(const std::string& msg) {
|
||||
if (logger_) {
|
||||
logger_->debug(msg);
|
||||
}
|
||||
}
|
||||
|
||||
~SpdLogger() {
|
||||
// 不再需要显式调用 shutdown(),因为使用的是同步模式
|
||||
~SpdLogger()
|
||||
{
|
||||
spdlog::shutdown();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<spdlog::logger> logger_;
|
||||
std::shared_ptr<spdlog::logger> logger_; // 日志器实例
|
||||
};
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
@@ -42,45 +42,46 @@ public:
|
||||
#include <hdevengine/HDevEngineCpp.h>
|
||||
#include <opencvPaintAlgo.hpp>
|
||||
using namespace HDevEngineCpp;
|
||||
static bool _952_15_H_25D_ALGO_HuaShangAI(cv::Mat& img, handleAIModels* _handleAiModels, cv::Mat& Paint, QMutex& paintMutex) {
|
||||
static bool _952_15_H_25D_ALGO_HuaShangAI(std::shared_ptr <detData> data,cv::Mat& img, handleAIModels* _handleAiModels, cv::Mat& Paint, QMutex& paintMutex) {
|
||||
cv::Mat segImg = ~_handleAiModels->SEG_25D->Evaluate(img);
|
||||
std::vector<std::vector<cv::Point>> contours;
|
||||
cv::findContours(segImg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
||||
bool isNg = false;
|
||||
bool isOK = true;
|
||||
|
||||
for (int i = 0; i < contours.size(); i++) {
|
||||
cv::Rect rect = cv::boundingRect(contours[i]);
|
||||
if (rect.width < _handleAiModels->handleAI_WIDTH || rect.height < _handleAiModels->handleAI_HEIGHT) continue; // 过滤掉小轮廓
|
||||
if (rect.width < _handleAiModels->handleAI_WIDTH && rect.height < _handleAiModels->handleAI_HEIGHT) continue; // 过滤掉小轮廓
|
||||
auto clsImgs = cutImg2ClsVec(rect, img);
|
||||
bool clsIsNg = false;
|
||||
for (auto& clsImg : clsImgs) {
|
||||
int clsResult = _handleAiModels->CLS_25D->Evaluate(clsImg);
|
||||
if (clsResult == 1) { // 假设1表示NG
|
||||
clsIsNg = true;
|
||||
break;
|
||||
data->clsOriginImgs.push_back({ std::to_string(i),clsImg });
|
||||
}
|
||||
}
|
||||
cv::Scalar color = cv::Scalar(0, 255, 0);
|
||||
if (clsIsNg == true) {
|
||||
color = cv::Scalar(0, 0, 255);
|
||||
isNg = true;
|
||||
cv::approxPolyDP(contours[i], contours[i], 3, true); // 多边形拟合
|
||||
QMutexLocker locker(&paintMutex);
|
||||
isOK = true;
|
||||
cv::Mat imgTmp;
|
||||
cv::cvtColor(img(rect), imgTmp, cv::COLOR_GRAY2BGR);
|
||||
imgTmp.copyTo(Paint(rect));
|
||||
QMutexLocker locker(&paintMutex);
|
||||
//imgTmp.copyTo(Paint(rect));
|
||||
cv::drawContours(Paint, contours, i, color, 2);
|
||||
cv::putText(Paint, "NG"+std::to_string(i), cv::Point(rect.x + 10, rect.y - 20), cv::FONT_HERSHEY_SIMPLEX, 0.8, color, 2);
|
||||
}
|
||||
else {
|
||||
QMutexLocker locker(&paintMutex);
|
||||
cv::Mat imgTmp;
|
||||
cv::cvtColor(img(rect), imgTmp, cv::COLOR_GRAY2BGR);
|
||||
QMutexLocker locker(&paintMutex);
|
||||
imgTmp.copyTo(Paint(rect));
|
||||
cv::rectangle(Paint, rect, color, 3);
|
||||
}
|
||||
putTextZH(Paint, "NG", cv::Point(rect.x + 10, rect.y - 20), 25, color);
|
||||
|
||||
//putTextZH(Paint, "NG", cv::Point(rect.x + 10, rect.y - 20), 25, color);
|
||||
}
|
||||
return isNg;
|
||||
return isOK;
|
||||
}
|
||||
|
||||
static bool _952_15_H_Algo(std::shared_ptr <detData> data, handleAIModels* _handleAIModels)
|
||||
@@ -103,63 +104,59 @@ static bool _952_15_H_Algo(std::shared_ptr <detData> data, handleAIModels* _hand
|
||||
cv::Mat src1 = data->oriImgs[1];
|
||||
cv::Mat src2 = data->oriImgs[0];
|
||||
cv::Mat src3 = data->oriImgs[2];
|
||||
data->oriImgs[1].copyTo(Paint);
|
||||
|
||||
if (Paint.channels() != 3)
|
||||
cv::cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR);
|
||||
|
||||
cv::cvtColor(src2, Paint, cv::COLOR_GRAY2BGR);
|
||||
else
|
||||
src2.copyTo(Paint);
|
||||
QMutex paintMutex;
|
||||
std::future <bool> future = std::async(std::launch::async, _952_15_H_25D_ALGO_HuaShangAI, data->oriImgs[2], _handleAIModels, std::ref(Paint), std::ref(paintMutex));
|
||||
|
||||
HObject in_Image1 = MatToHObject(src1);
|
||||
HObject in_Image2 = MatToHObject(src2);
|
||||
HObject in_Image3 = MatToHObject(src3);
|
||||
procedureCall.SetInputIconicParamObject("in_Image1", in_Image1);
|
||||
procedureCall.SetInputIconicParamObject("in_Image2", in_Image2);
|
||||
procedureCall.SetInputIconicParamObject("in_Image3", in_Image3);
|
||||
procedureCall.SetInputCtrlParamTuple("in_xResolution", HTuple(_handleAIModels->in_xResolution));
|
||||
procedureCall.SetInputCtrlParamTuple("in_yResolution", HTuple(_handleAIModels->in_yResolution));
|
||||
procedureCall.Execute();
|
||||
|
||||
std::future <bool> future = std::async(std::launch::async, _952_15_H_25D_ALGO_HuaShangAI, data, data->oriImgs[2], _handleAIModels, std::ref(Paint), std::ref(paintMutex));
|
||||
HTuple out_Distance, out_ResultRows, out_ResultCols, out_Flag, out_LinePonit;
|
||||
{
|
||||
HObject in_Image1 = MatToHObject(src1);
|
||||
HObject in_Image2 = MatToHObject(src2);
|
||||
HObject in_Image3 = MatToHObject(src3);
|
||||
procedureCall.SetInputIconicParamObject("in_Image1", in_Image1);
|
||||
procedureCall.SetInputIconicParamObject("in_Image2", in_Image2);
|
||||
procedureCall.SetInputIconicParamObject("in_Image3", in_Image3);
|
||||
procedureCall.SetInputCtrlParamTuple("in_xResolution", HTuple(_handleAIModels->in_xResolution));
|
||||
procedureCall.SetInputCtrlParamTuple("in_yResolution", HTuple(_handleAIModels->in_yResolution));
|
||||
procedureCall.Execute();
|
||||
|
||||
out_Distance = procedureCall.GetOutputCtrlParamTuple("out_Distance");
|
||||
//out_RightDistance = procedureCall.GetOutputCtrlParamTuple("out_RightDistance");
|
||||
out_ResultRows = procedureCall.GetOutputCtrlParamTuple("out_ResultRows");
|
||||
out_ResultCols = procedureCall.GetOutputCtrlParamTuple("out_ResultCols");
|
||||
out_Distance = procedureCall.GetOutputCtrlParamTuple("out_Distance");
|
||||
//out_RightDistance = procedureCall.GetOutputCtrlParamTuple("out_RightDistance");
|
||||
out_ResultRows = procedureCall.GetOutputCtrlParamTuple("out_ResultRows");
|
||||
out_ResultCols = procedureCall.GetOutputCtrlParamTuple("out_ResultCols");
|
||||
|
||||
//out_Angle = procedureCall.GetOutputCtrlParamTuple("out_Angle");
|
||||
out_LinePonit = procedureCall.GetOutputCtrlParamTuple("out_LinePonit");
|
||||
|
||||
out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag");
|
||||
//out_Angle = procedureCall.GetOutputCtrlParamTuple("out_Angle");
|
||||
out_LinePonit = procedureCall.GetOutputCtrlParamTuple("out_LinePonit");
|
||||
|
||||
out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag");
|
||||
}
|
||||
TextRendererZH textPainter(Paint.cols, Paint.rows, 150);
|
||||
int algoFlag = out_Flag.D();
|
||||
double LT_Distance = -1, RT_Distance = -1, LD_Distance = -1, RD_Distance = -1, L_Duiqidu = -1, R_Duiqidu = -1;
|
||||
if (algoFlag == 1) // 算法OK
|
||||
{
|
||||
QMutexLocker locker(&paintMutex);
|
||||
LT_Distance = out_Distance[0].D() + 0.86;//左上
|
||||
putTextZH(Paint, "左上留白:" + std::to_string(LT_Distance), cv::Point(100, 200), 150, cv::Scalar(0, 255, 0));
|
||||
|
||||
RT_Distance = out_Distance[1].D() + 0.52;//右上
|
||||
putTextZH(Paint, "右上留白:" + std::to_string(RT_Distance), cv::Point(100, 350), 150, cv::Scalar(0, 255, 0));
|
||||
|
||||
LD_Distance = out_Distance[2].D() + 0.86;//左下
|
||||
putTextZH(Paint, "左下留白:" + std::to_string(LD_Distance), cv::Point(100, 500), 150, cv::Scalar(0, 255, 0));
|
||||
|
||||
RD_Distance = out_Distance[3].D() + 0.52;//右下
|
||||
putTextZH(Paint, "右下留白:" + std::to_string(RD_Distance), cv::Point(100, 650), 150, cv::Scalar(0, 255, 0));
|
||||
|
||||
L_Duiqidu = std::abs(LT_Distance - LD_Distance);
|
||||
R_Duiqidu = std::abs(RT_Distance - RD_Distance);
|
||||
textPainter.putText(Paint, "左上留白:" + std::to_string(LT_Distance), cv::Point(0, 200), cv::Scalar(0, 255, 0));
|
||||
textPainter.putText(Paint, "右上留白:" + std::to_string(RT_Distance), cv::Point(0, 350), cv::Scalar(0, 255, 0));
|
||||
textPainter.putText(Paint, "左下留白:" + std::to_string(LD_Distance), cv::Point(0, 500), cv::Scalar(0, 255, 0));
|
||||
textPainter.putText(Paint, "右下留白:" + std::to_string(RD_Distance), cv::Point(0, 650), cv::Scalar(0, 255, 0));
|
||||
textPainter.putText(Paint, "左对齐度:" + std::to_string(L_Duiqidu), cv::Point(0, 800), cv::Scalar(0, 255, 0));
|
||||
textPainter.putText(Paint, "右对齐度:" + std::to_string(R_Duiqidu), cv::Point(0, 950), cv::Scalar(0, 255, 0));
|
||||
int outPointRowsLength = out_ResultRows.Length();
|
||||
for (int i = 0; i < outPointRowsLength; i++)
|
||||
Cross(Paint,
|
||||
cv::Point(out_ResultCols[i].D(), out_ResultRows[i].D()),
|
||||
3, 2, cv::Scalar(0, 0, 255));
|
||||
|
||||
//duiqidu = out_Angle.D();
|
||||
L_Duiqidu = std::abs(LT_Distance - LD_Distance);
|
||||
putTextZH(Paint, "左对齐度:" + std::to_string(L_Duiqidu), cv::Point(100, 800), 150, cv::Scalar(0, 255, 0));
|
||||
R_Duiqidu = std::abs(RT_Distance - RD_Distance);
|
||||
putTextZH(Paint, "右对齐度:" + std::to_string(R_Duiqidu), cv::Point(100, 950), 150, cv::Scalar(0, 255, 0));
|
||||
|
||||
int out_LinePonitLength = out_LinePonit.Length() / 4;
|
||||
for (int i = 0; i < out_LinePonitLength; i++)
|
||||
line(Paint,
|
||||
@@ -171,18 +168,11 @@ static bool _952_15_H_Algo(std::shared_ptr <detData> data, handleAIModels* _hand
|
||||
}
|
||||
else // 算法异常
|
||||
{
|
||||
putTextZH(Paint, "留白算法NG",
|
||||
cv::Point(100, 200), 200, cv::Scalar(0, 0, 255));
|
||||
|
||||
QMutexLocker locker(&paintMutex);
|
||||
textPainter.putText(Paint, "留白算法NG", cv::Point(0, 200), cv::Scalar(0, 0, 255));
|
||||
data->halconOK = false;
|
||||
}
|
||||
data->resultImgs.push_back(Paint);
|
||||
|
||||
|
||||
//data->resultImgs.push_back(Paint);
|
||||
// data->sqlVec.push_back({ "Gluewidth",std::to_string(Gluewidth) });
|
||||
// data->sqlVec.push_back({ "_2dResult",std::to_string(result) });
|
||||
// qDebug() << "[Debug]2D Data:" << "hv_out_Gluewidth ::" << QString::number(Gluewidth) << ",Result" << QString::number(result);
|
||||
data->resultData.insert(std::make_pair("LT_Distance", LT_Distance));
|
||||
data->resultData.insert(std::make_pair("RT_Distance", RT_Distance));
|
||||
data->resultData.insert(std::make_pair("LD_Distance", LD_Distance));
|
||||
@@ -191,13 +181,15 @@ static bool _952_15_H_Algo(std::shared_ptr <detData> data, handleAIModels* _hand
|
||||
data->resultData.insert(std::make_pair("R_Duiqidu", R_Duiqidu));
|
||||
|
||||
bool futureResult = future.get();
|
||||
if (futureResult) {
|
||||
putTextZH(Paint, "模型检测结果: NG", cv::Point(100, 50), 200, cv::Scalar(0, 0, 255));
|
||||
if (futureResult) {
|
||||
textPainter.putText(Paint, "模型检测结果: NG", cv::Point(0, 50), cv::Scalar(0, 0, 255));
|
||||
}
|
||||
else {
|
||||
putTextZH(Paint, "模型检测结果: OK", cv::Point(100, 50), 200, cv::Scalar(0, 255, 0));
|
||||
textPainter.putText(Paint, "模型检测结果: OK", cv::Point(0, 50), cv::Scalar(0, 255, 0));
|
||||
}
|
||||
data->resultData.insert(std::make_pair("aiResultIsNG", futureResult));
|
||||
|
||||
cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); // 缩小结果图
|
||||
data->resultImgs.push_back(Paint);
|
||||
data->aiDetOK = futureResult; // 如果任一检测结果为NG,则整体结果为NG
|
||||
|
||||
@@ -247,11 +239,14 @@ public:
|
||||
|
||||
bridge = bridgeIn;
|
||||
_serviceManager = _serviceManagerIN;
|
||||
parseJson(_missionJson);
|
||||
|
||||
#pragma region 初始化外部参数
|
||||
|
||||
parseJson(_missionJson);
|
||||
_handleAIModels.SEG_25D = _serviceManagerIN->_handleAiModelManager->getSegModel("SEG_25D");
|
||||
_handleAIModels.CLS_25D = _serviceManagerIN->_handleAiModelManager->getClsModel("CLS_25D");
|
||||
HDevEngineCpp::HDevEngine myEngine;
|
||||
myEngine.SetProcedurePath("./halconAlgoFiles/AlgoLibrary_952.hdpl");
|
||||
#pragma endregion
|
||||
|
||||
#pragma region 获取相机
|
||||
_25DCamera_1 = _serviceManager->_cameraManager->getCamera("25D-1");
|
||||
if (_25DCamera_1 == nullptr) {
|
||||
@@ -279,10 +274,7 @@ public:
|
||||
HouDuan12ResultHandle(data);
|
||||
}));
|
||||
#pragma endregion
|
||||
_handleAIModels.SEG_25D = _serviceManagerIN->_handleAiModelManager->getSegModel("SEG_25D");
|
||||
_handleAIModels.CLS_25D = _serviceManagerIN->_handleAiModelManager->getClsModel("CLS_25D");
|
||||
HDevEngineCpp::HDevEngine myEngine;
|
||||
myEngine.SetProcedurePath("./halconAlgoFiles/AlgoLibrary_952.hdpl");
|
||||
|
||||
return true;
|
||||
}
|
||||
//9200 1
|
||||
@@ -295,18 +287,17 @@ public:
|
||||
"2.5D-H-1",sn, 3, _25DCamera_1->config.oriSaveFormat);//创建数据包
|
||||
_25DCamera_1->captureImage(data);//拍照
|
||||
//data->oriImgs.push_back(cv::imread("I:\\952Code\\CODES\\7.07\\7.07\\baoMoHou\\yh\\2025-07-07-1106069567896\\2025-07-07-11060695678962.5D-H-1_10-57-28-51_Count0.bmp", -1));
|
||||
//data->oriImgs.push_back(cv::imread("I:\\952Code\\CODES\\7.07\\7.07\\baoMoHou\\yh\\2025-07-07-1106069567896\\2025-07-07-11060695678962.5D-H-1_10-57-28-51_Count1.bmp", -1));
|
||||
// data->oriImgs.push_back(cv::imread("I:\\952Code\\CODES\\7.07\\7.07\\baoMoHou\\yh\\2025-07-07-1106069567896\\2025-07-07-11060695678962.5D-H-1_10-57-28-51_Count1.bmp", -1));
|
||||
//data->oriImgs.push_back(cv::imread("I:\\952Code\\CODES\\bm\\2025-07-08-09361133675272.5D-H-1_09-27-32-22_Count2.bmp", -1));
|
||||
algo_liubai->ansyncProcess(data);//处理算法
|
||||
|
||||
|
||||
if(houduan1SecondTime == false)
|
||||
data->resultImgShowSet.push_back(new showImgPack("后段1-1", "结果图"));
|
||||
else
|
||||
data->resultImgShowSet.push_back(new showImgPack("后段1-2", "结果图"));
|
||||
houduan1SecondTime = !houduan1SecondTime;
|
||||
|
||||
algo_liubai->ansyncProcess(data);//处理算法
|
||||
qDebug() << "[ Station 10 ] " << QString::fromUtf8("2.5D-1 后段检测开始 uuid :") << data->uuid << "SN" << QString::fromStdString(sn);;
|
||||
std::cout << "通道1 SN:" << sn << "检测开始" << std::endl;
|
||||
}
|
||||
|
||||
//9600 1
|
||||
@@ -319,16 +310,15 @@ public:
|
||||
std::shared_ptr <detData> data = _serviceManager->_detDataManager->createDetData(
|
||||
"2.5D-H-2", sn, 3, _25DCamera_2->config.oriSaveFormat);//创建数据包
|
||||
_25DCamera_2->captureImage(data);//拍照
|
||||
algo_liubai->ansyncProcess(data);//处理算法
|
||||
|
||||
|
||||
if (houduan2SecondTime == false)
|
||||
data->resultImgShowSet.push_back(new showImgPack("后段2-1", "结果图"));
|
||||
else
|
||||
data->resultImgShowSet.push_back(new showImgPack("后段2-2", "结果图"));
|
||||
houduan2SecondTime = !houduan2SecondTime;
|
||||
|
||||
algo_liubai->ansyncProcess(data);//处理算法
|
||||
qDebug() << "[ Station 10 ] " << QString::fromUtf8("2.5D-2 后段检测开始 uuid :") << data->uuid << "SN" << QString::fromStdString(sn);;
|
||||
std::cout << "通道2 SN:" << sn << "检测开始" << std::endl;
|
||||
}
|
||||
|
||||
void HouDuan12ResultHandle(std::shared_ptr <detData>data){
|
||||
@@ -380,7 +370,7 @@ public:
|
||||
else {
|
||||
qDebug() << "[ Station 10 ] " << QString::fromUtf8("后段检测结果数据为空 SN:") << QString::fromStdString(data->SNcode);
|
||||
}
|
||||
std::cout << "SN:" << data->SNcode << "检测结束 结果获取完成" << std::endl;
|
||||
qDebug() << "SN:" << QString::fromStdString(data->SNcode) << "检测结束 结果获取完成" ;
|
||||
}
|
||||
|
||||
//9400 1 Summary1
|
||||
|
||||
@@ -67,10 +67,11 @@ bool Hd_VirualCamera::initParas(QByteArray&, nlohmann::json& json)
|
||||
bool Hd_VirualCamera::run()//Èí´¥·¢
|
||||
{
|
||||
n_mutex->lock();
|
||||
if (cameraName == "156112" || cameraName == "162762") {
|
||||
if (cameraName == "25D-1" || cameraName == "25D-2") {
|
||||
vector<cv::Mat> mats;
|
||||
mats.push_back(cv::imread("H:\\testImg\\2.5D-H-1_16-00-14-38_Count0.bmp", -1));
|
||||
mats.push_back(cv::imread("H:\\testImg\\2.5D-H-1_16-00-14-38_Count1.tiff", -1));
|
||||
mats.push_back(cv::imread("I:\\952Code\\CODES\\16H\\A2250709-162941554918413\\A2250709-1629415549184132.5D-H-2_16-31-32-65_Count0.bmp", -1));
|
||||
mats.push_back(cv::imread("I:\\952Code\\CODES\\16H\\A2250709-162941554918413\\A2250709-1629415549184132.5D-H-2_16-31-32-65_Count1.bmp", -1));
|
||||
mats.push_back(cv::imread("I:\\952Code\\CODES\\16H\\A2250709-162941554918413\\A2250709-1629415549184132.5D-H-2_16-31-32-65_Count2.bmp", -1));
|
||||
queuePic.enqueue(mats);
|
||||
}
|
||||
n_mutex->unlock();
|
||||
|
||||
Reference in New Issue
Block a user